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>
254 lines
9.4 KiB
Markdown
254 lines
9.4 KiB
Markdown
# Admin Panel: Invite Codes + User Management Enhancement
|
|
|
|
Date: 2026-02-12
|
|
Status: Proposed
|
|
|
|
## Summary
|
|
Enhance admin capabilities to:
|
|
1. Create invite codes tied to plans (`free`, `pro`, `team`) with optional trial durations.
|
|
2. Send invite emails via Resend (best-effort, non-blocking).
|
|
3. Apply invite-assigned plan/trial on registration.
|
|
4. Give admins a detailed user management view with subscription/session/audit context.
|
|
5. Support admin subscription actions (change plan, extend/start trial).
|
|
6. 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 `email` is 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..90` days.
|
|
- 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'` implies `trial_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 `False` if `RESEND_API_KEY` missing.
|
|
- 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] = None`
|
|
- `FROM_EMAIL: str = "ResolutionFlow <invites@resolutionflow.com>"`
|
|
- `email_enabled` property.
|
|
|
|
Update `backend/requirements.txt`:
|
|
- Add `resend` package.
|
|
|
|
## Phase 3: Backend Schemas + Endpoints
|
|
|
|
### Invite code schemas
|
|
Update `backend/app/schemas/invite_code.py`:
|
|
- `InviteCodeCreate` adds:
|
|
- `email: Optional[EmailStr]`
|
|
- `assigned_plan: Literal['free','pro','team'] = 'free'`
|
|
- `trial_duration_days: Optional[int]` (1..90)
|
|
- `InviteCodeResponse` adds:
|
|
- `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 /invites` accepts 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.
|
|
- 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` (fallback `free`).
|
|
- if `trial_duration_days` set:
|
|
- `status='trialing'`
|
|
- `current_period_start=now`
|
|
- `current_period_end=now + trial_duration_days`.
|
|
- else `status='active'`.
|
|
- 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/plan`
|
|
- `PUT /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 `SubscriptionResponse` as needed.
|
|
|
|
Create `backend/app/schemas/user_detail.py`:
|
|
- `AccountSummary`
|
|
- `SessionSummary`
|
|
- `AuditLogSummary`
|
|
- `InviteCodeUsedSummary`
|
|
- `UserDetailResponse` (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:
|
|
- `UserDetail`
|
|
- `SubscriptionDetail`
|
|
- `SessionSummary`
|
|
- `AuditLogSummary`
|
|
- `AccountSummary`.
|
|
|
|
Update `frontend/src/api/admin.ts`:
|
|
- Switch invite endpoints to `/invites`.
|
|
- Enhance `createInviteCode` payload.
|
|
- 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`.
|
|
- `GET /invites`
|
|
- new response fields: `email`, `assigned_plan`, `trial_duration_days`, `email_sent_at`, `has_trial`, `email_sent`.
|
|
- `GET /admin/users/{id}`
|
|
- enriched response with account/subscription/recent activity details.
|
|
|
|
### Added
|
|
- `PUT /admin/users/{id}/subscription/plan`
|
|
- `PUT /admin/users/{id}/subscription/extend-trial`
|
|
|
|
## Test Plan
|
|
|
|
## Backend tests
|
|
1. Invite create with `assigned_plan + trial_duration_days` persists correctly.
|
|
2. Invite create with email:
|
|
- Resend success sets `email_sent_at`.
|
|
- Resend failure still returns 201 and does not set `email_sent_at`.
|
|
3. Registration with invite applies correct subscription plan/status/period fields.
|
|
4. Registration with optional invite (`REQUIRE_INVITE_CODE=false`) still applies plan/trial.
|
|
5. Expired trial auto-downgrades on authenticated request.
|
|
6. Admin plan update endpoint updates subscription + audit logs.
|
|
7. Admin extend-trial endpoint converts/extends correctly + audit logs.
|
|
8. Enriched `GET /admin/users/{id}` returns expected shape and list-size caps.
|
|
|
|
## Frontend verification
|
|
1. Create invite with email + plan + trial from admin UI.
|
|
2. Confirm invite table renders recipient/plan/trial/email-sent.
|
|
3. Open user detail from users table.
|
|
4. Change plan and extend trial from detail page.
|
|
5. Confirm updated values refresh in UI.
|
|
6. `npm run build` passes.
|
|
|
|
## Commands
|
|
- `cd backend && pytest --override-ini="addopts="`
|
|
- `cd frontend && npm run build`
|
|
|
|
## Risks and Mitigations
|
|
- Endpoint drift (`/invite-codes` vs `/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
|
|
1. Deploy migration and backend changes.
|
|
2. Validate admin invite creation and registration path in staging.
|
|
3. Deploy frontend with new invite/user-detail UI.
|
|
4. Monitor audit logs and invite email delivery behavior post-release.
|
|
|
|
## Assumptions
|
|
- Existing admin access control (`require_admin`) remains unchanged.
|
|
- Plan limits for `free/pro/team` are already configured in `plan_limits`.
|
|
- No mandatory template engine addition is required for this email template rendering path.
|