Adds a new "procedural" tree type for linear step-by-step project workflows (domain controller setup, M365 onboarding, VPN config, etc). Includes intake form builder, two-panel step navigation, variable resolution, procedural exports, 3 seed templates, and UI rename from "Trees" to "Flows". Also archives 19 implemented plan docs and creates deferred features backlog. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9.4 KiB
Admin Panel: Invite Codes + User Management Enhancement
Date: 2026-02-12
Status: Proposed
Summary
Enhance admin capabilities to:
- Create invite codes tied to plans (
free,pro,team) with optional trial durations. - Send invite emails via Resend (best-effort, non-blocking).
- Apply invite-assigned plan/trial on registration.
- Give admins a detailed user management view with subscription/session/audit context.
- Support admin subscription actions (change plan, extend/start trial).
- Auto-downgrade expired trials during authenticated access checks.
Goals
- Remove manual invite-code sharing workflow.
- Support controlled beta onboarding with plan + trial at invite level.
- Enable operational admin workflows for account/subscription lifecycle.
- Keep backward compatibility where practical and avoid unsafe breaking changes.
Non-Goals
- Stripe billing workflow redesign.
- Full historical pagination for user-detail sessions/audits in this iteration.
- Rework of account invite (
/accounts/me/invites) flow.
Key Decisions Locked
- Invite API path standardization: use
/invites(frontend and backend aligned). - User detail endpoint: enrich existing
GET /admin/users/{id}. - Invite
emailis advisory only (no strict email-match enforcement at registration). - Invite plan/trial applies whenever a valid invite code is provided, even if
REQUIRE_INVITE_CODE=false. - Trial duration bounds:
1..90days. - Extend trial endpoint may convert non-trialing subscriptions to
trialing. - User detail payload includes recent summaries (latest 10 sessions + latest 10 audit logs) plus total counts.
Scope by Phase
Phase 1: Database Migration (030)
Create backend/alembic/versions/030_enhance_invite_codes.py (down revision 029).
Add to invite_codes:
email:String(255), nullable, indexed.assigned_plan:String(50), non-null, server default'free'.trial_duration_days:Integer, nullable.email_sent_at:DateTime(timezone=True), nullable.
Constraints:
assigned_plan IN ('free','pro','team').trial_duration_days IS NULL OR trial_duration_days BETWEEN 1 AND 90.- Optional consistency guard:
assigned_plan='free'impliestrial_duration_days IS NULL.
Update model backend/app/models/invite_code.py:
- Add mapped columns above.
- Add computed properties:
has_trial: bool(trial_duration_days is not None and > 0)email_sent: bool(email_sent_at is not None)
Phase 2: Resend Email Integration
Create backend/app/core/email.py:
EmailService.send_invite_email(to_email, code, plan, trial_days, signup_url) -> bool.- Returns
FalseifRESEND_API_KEYmissing. - Catches provider failures and returns
False(logs warning/error). - Never blocks invite creation.
Create backend/app/templates/invite_email.html:
- Monochrome branded HTML.
- Invite code, plan, optional trial text, signup CTA button.
Update backend/app/core/config.py:
RESEND_API_KEY: Optional[str] = NoneFROM_EMAIL: str = "ResolutionFlow <invites@resolutionflow.com>"email_enabledproperty.
Update backend/requirements.txt:
- Add
resendpackage.
Phase 3: Backend Schemas + Endpoints
Invite code schemas
Update backend/app/schemas/invite_code.py:
InviteCodeCreateadds:email: Optional[EmailStr]assigned_plan: Literal['free','pro','team'] = 'free'trial_duration_days: Optional[int](1..90)
InviteCodeResponseadds:email,assigned_plan,trial_duration_days,email_sent_at- computed flags
has_trial,email_sent.
Invite endpoints
Update backend/app/api/endpoints/invite.py:
POST /invitesaccepts new fields.- Creates invite with plan/trial/email metadata.
- If email provided, attempts send:
- on success: set
email_sent_at. - on failure: invite still returns 201.
- on success: set
- Add audit log for invite creation with delivery result.
- Keep
GET /invites,DELETE /invites/{code},GET /invites/validate/{code}behavior compatible.
Registration plan assignment
Update backend/app/api/endpoints/auth.py:
- If invite code is supplied and valid, load it and apply invite plan/trial regardless of
REQUIRE_INVITE_CODE. - For non-account-invite registrations:
- create subscription
plan=invite_code.assigned_plan(fallbackfree). - if
trial_duration_daysset:status='trialing'current_period_start=nowcurrent_period_end=now + trial_duration_days.
- else
status='active'.
- create subscription
- Preserve account-invite join flow behavior.
- Mark invite as used post user creation.
Admin subscription + detail endpoints
Update backend/app/api/endpoints/admin.py:
- Enrich
GET /admin/users/{id}response:- base user fields
- account summary
- subscription summary
- recent sessions (10) + total count
- recent audit logs (10) + total count
- invite code used summary
- Add:
PUT /admin/users/{id}/subscription/planPUT /admin/users/{id}/subscription/extend-trial
Trial expiry check
Update backend/app/api/deps.py:
- In
get_current_active_user, check account subscription. - If
status='trialing'and expired, auto-downgrade:plan='free',status='active'- clear/normalize trial period fields
- commit before returning user.
Phase 4: Backend Schema Additions
Use existing file backend/app/schemas/subscription.py (do not duplicate):
- Add
SubscriptionPlanUpdate. - Add
ExtendTrialRequest. - Keep/extend
SubscriptionResponseas needed.
Create backend/app/schemas/user_detail.py:
AccountSummarySessionSummaryAuditLogSummaryInviteCodeUsedSummaryUserDetailResponse(superset for enriched/admin/users/{id}).
Phase 5: Frontend Types + API Client
Update frontend/src/types/admin.ts:
- Invite response fields for email/plan/trial/email-sent metadata.
- New detail types:
UserDetailSubscriptionDetailSessionSummaryAuditLogSummaryAccountSummary.
Update frontend/src/api/admin.ts:
- Switch invite endpoints to
/invites. - Enhance
createInviteCodepayload. - Add:
getUserDetail(userId)updateUserSubscriptionPlan(userId, plan)extendUserTrial(userId, days).
Phase 6: Frontend Invite Codes Page
Update frontend/src/pages/admin/InviteCodesPage.tsx:
- Create form fields:
- optional email
- plan selector (Free/Pro/Team)
- trial days input when plan != free
- Table additions:
- recipient
- plan badge
- trial column
- email sent indicator
- Preserve existing create/copy/delete actions and status badges.
Phase 7: Frontend User Detail Page
Create frontend/src/pages/admin/UserDetailPage.tsx:
- Header: name/email/role/active.
- Account & subscription card.
- Admin actions:
- change role
- change plan
- extend/start trial
- activate/deactivate
- Tabs:
- recent sessions
- audit logs
- Invite code card:
- code, assigned plan, creator.
Update frontend/src/router.tsx:
- Add route
admin/users/:userId.
Update frontend/src/pages/admin/UsersPage.tsx:
- Make rows navigate to detail.
- Ensure action menu clicks do not trigger row navigation.
API / Interface Changes
Modified
POST /invites- new request fields:
email,assigned_plan,trial_duration_days.
- new request fields:
GET /invites- new response fields:
email,assigned_plan,trial_duration_days,email_sent_at,has_trial,email_sent.
- new response fields:
GET /admin/users/{id}- enriched response with account/subscription/recent activity details.
Added
PUT /admin/users/{id}/subscription/planPUT /admin/users/{id}/subscription/extend-trial
Test Plan
Backend tests
- Invite create with
assigned_plan + trial_duration_dayspersists correctly. - Invite create with email:
- Resend success sets
email_sent_at. - Resend failure still returns 201 and does not set
email_sent_at.
- Registration with invite applies correct subscription plan/status/period fields.
- Registration with optional invite (
REQUIRE_INVITE_CODE=false) still applies plan/trial. - Expired trial auto-downgrades on authenticated request.
- Admin plan update endpoint updates subscription + audit logs.
- Admin extend-trial endpoint converts/extends correctly + audit logs.
- Enriched
GET /admin/users/{id}returns expected shape and list-size caps.
Frontend verification
- Create invite with email + plan + trial from admin UI.
- Confirm invite table renders recipient/plan/trial/email-sent.
- Open user detail from users table.
- Change plan and extend trial from detail page.
- Confirm updated values refresh in UI.
npm run buildpasses.
Commands
cd backend && pytest --override-ini="addopts="cd frontend && npm run build
Risks and Mitigations
- Endpoint drift (
/invite-codesvs/invites): update admin API client and validate all admin invite calls. - Subscription side-effects in auth/deps: centralize trial-expiry logic and cover with tests.
- Payload growth for user detail: cap related arrays at 10 and include totals.
- Email provider outages: best-effort send with logging, no invite creation failure.
Rollout
- Deploy migration and backend changes.
- Validate admin invite creation and registration path in staging.
- Deploy frontend with new invite/user-detail UI.
- Monitor audit logs and invite email delivery behavior post-release.
Assumptions
- Existing admin access control (
require_admin) remains unchanged. - Plan limits for
free/pro/teamare already configured inplan_limits. - No mandatory template engine addition is required for this email template rendering path.