feat(auth): auto-create admin user profiles with highest tier and complimentary flag

- Add `is_complimentary` column to UserProfile model (migration 019)
- Update `_ensure_user_profile` to accept `is_admin` param; admins get
  highest subscription tier, is_complimentary=True, onboarding skipped
- Call `_ensure_user_profile` from all login paths (OAuth, local user, admin creds)
- Add `is_complimentary` to UserProfileUpsert schema, response helpers,
  list_users, get_user, upsert_user_profile in admin API
- Add complimentary toggle to admin users UI with gift badge in table
- Write 18 new tests covering complimentary plan and admin auto-creation
- Update SubscriptionTiers.md documentation

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-07 20:14:34 +00:00
parent a7d428d009
commit 97f85ce74e
8 changed files with 357 additions and 14 deletions
+31 -1
View File
@@ -140,6 +140,14 @@
></i>
<span x-text="user.subscription_tier || 'free'"></span>
</span>
<span
x-show="user.is_complimentary"
class="ml-1 inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold bg-green-100 text-green-700"
title="Complimentary — not billed"
>
<i class="fas fa-gift" aria-hidden="true"></i>
<span class="sr-only">Complimentary plan</span>
</span>
</td>
<!-- Upload limit -->
<td class="px-4 py-3 text-sm text-center text-gray-700">
@@ -375,6 +383,25 @@
</p>
</div>
<!-- Complimentary plan toggle -->
<div class="flex items-center gap-3">
<label class="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
x-model="form.is_complimentary"
class="sr-only peer"
id="modal-complimentary"
role="switch"
:aria-checked="form.is_complimentary"
/>
<div class="w-10 h-6 bg-gray-200 peer-focus:ring-2 peer-focus:ring-blue-400 rounded-full peer peer-checked:bg-green-500 after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:after:translate-x-4"></div>
</label>
<label for="modal-complimentary" class="text-sm font-medium text-gray-700">
Complimentary plan
<span class="text-xs text-gray-400 font-normal">(user keeps tier benefits but is never billed — set automatically for admin accounts)</span>
</label>
</div>
</div>
<!-- Footer -->
@@ -472,6 +499,7 @@ function adminUsersApp() {
subscription_tier: 'free',
subscription_billing_cycle: 'monthly',
subscription_period_start: null,
is_complimentary: false,
},
// Delete modal
@@ -524,7 +552,7 @@ function adminUsersApp() {
openCreateModal() {
this.isCreate = true;
this.modalTitle = 'Add User Profile';
this.form = { user_id: '', display_name: '', daily_upload_limit: null, notes: '', is_blocked: false, subscription_tier: 'free', subscription_billing_cycle: 'monthly', subscription_period_start: null };
this.form = { user_id: '', display_name: '', daily_upload_limit: null, notes: '', is_blocked: false, subscription_tier: 'free', subscription_billing_cycle: 'monthly', subscription_period_start: null, is_complimentary: false };
this.modalOpen = true;
},
@@ -541,6 +569,7 @@ function adminUsersApp() {
subscription_tier: user.subscription_tier || 'free',
subscription_billing_cycle: user.subscription_billing_cycle || 'monthly',
subscription_period_start: user.subscription_period_start ? user.subscription_period_start.substring(0, 10) : null,
is_complimentary: !!user.is_complimentary,
};
this.modalOpen = true;
},
@@ -555,6 +584,7 @@ function adminUsersApp() {
notes: this.form.notes || null,
is_blocked: !!this.form.is_blocked,
subscription_tier: this.form.subscription_tier || 'free',
is_complimentary: !!this.form.is_complimentary,
};
const uid = encodeURIComponent(this.form.user_id);
const resp = await fetch(`/api/admin/users/${uid}`, {