feat: admin invite codes with plan assignment + user detail page

- Migration 030: add email, assigned_plan, trial_duration_days, email_sent_at
  to invite_codes with CHECK constraints
- Resend email integration (graceful degradation when API key not set)
- Invite codes now support plan assignment (free/pro/team) and trial duration (1-90 days)
- Registration applies invite code plan/trial to new subscription
- Auto-downgrade expired trials on authenticated access
- Enriched GET /admin/users/{id} with account, subscription, sessions, audit logs
- New endpoints: PUT /admin/users/{id}/subscription/plan and extend-trial
- Frontend: enhanced invite codes page with email, plan, trial fields
- Frontend: new user detail page at /admin/users/:userId
- Fixed API path drift: /invite-codes -> /invites
- 11 new backend tests, 416 total passing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-02-11 21:42:58 -05:00
parent a466400c5b
commit 50cb0fc7f0
24 changed files with 2522 additions and 1121 deletions

View File

@@ -128,3 +128,89 @@ export interface GlobalCategoryCreate {
slug: string
description?: string | null
}
// Invite code types (enhanced)
export interface InviteCodeResponse {
id: string
code: string
created_by_id: string
used_by_id: string | null
expires_at: string | null
note: string | null
created_at: string
used_at: string | null
is_used: boolean
is_expired: boolean
is_valid: boolean
email: string | null
assigned_plan: string
trial_duration_days: number | null
email_sent_at: string | null
has_trial: boolean
email_sent: boolean
}
export interface InviteCodeCreateRequest {
expires_at?: string | null
note?: string | null
email?: string | null
assigned_plan?: 'free' | 'pro' | 'team'
trial_duration_days?: number | null
}
// User detail types
export interface AccountSummary {
id: string
name: string
display_code: string | null
}
export interface SubscriptionSummary {
id: string
plan: string
status: string
current_period_start: string | null
current_period_end: string | null
}
export interface SessionSummary {
id: string
tree_name: string | null
started_at: string
completed_at: string | null
outcome: string | null
}
export interface AuditLogSummary {
id: string
action: string
resource_type: string | null
resource_id: string | null
created_at: string
details: Record<string, unknown> | null
}
export interface InviteCodeUsedSummary {
code: string
assigned_plan: string
trial_duration_days: number | null
created_by_email: string | null
}
export interface UserDetailResponse {
id: string
email: string
full_name: string | null
role: string
is_active: boolean
is_super_admin: boolean
is_team_admin: boolean
created_at: string
account: AccountSummary | null
subscription: SubscriptionSummary | null
invite_code_used: InviteCodeUsedSummary | null
recent_sessions: SessionSummary[]
total_sessions: number
recent_audit_logs: AuditLogSummary[]
total_audit_logs: number
}