feat: Implement tenant invitation system with onboarding wizard

Backend Implementation:
- Add TenantInvitation model with lifecycle management (PENDING/ACCEPTED/EXPIRED/CANCELLED)
- Create platform admin API endpoints for invitation CRUD operations
- Add public token-based endpoints for invitation retrieval and acceptance
- Implement schema_context wrappers to ensure tenant operations run in public schema
- Add tenant permissions: can_manage_oauth_credentials, can_accept_payments, can_use_custom_domain, can_white_label, can_api_access
- Fix tenant update/create serializers to handle multi-schema environment
- Add migrations for tenant permissions and invitation system

Frontend Implementation:
- Create TenantInviteModal with comprehensive invitation form (350 lines)
  - Email, business name, subscription tier configuration
  - Custom user/resource limits
  - Platform permissions toggles
  - Future feature flags (video conferencing, event types, calendars, 2FA, logs, data deletion, POS, mobile app)
- Build TenantOnboardPage with 4-step wizard for invitation acceptance
  - Step 1: Account setup (email, password, name)
  - Step 2: Business details (name, subdomain, contact)
  - Step 3: Payment setup (conditional based on permissions)
  - Step 4: Success confirmation with redirect
- Extract BusinessCreateModal and BusinessEditModal into separate components
- Refactor PlatformBusinesses from 1080 lines to 220 lines (80% reduction)
- Add inactive businesses dropdown section (similar to staff page pattern)
- Update masquerade button styling to match Users page
- Remove deprecated "Add New Tenant" functionality in favor of invitation flow
- Add /tenant-onboard route for public access

API Integration:
- Add platform.ts API functions for tenant invitations
- Create React Query hooks in usePlatform.ts for invitation management
- Implement proper error handling and success states
- Add TypeScript interfaces for invitation types

Testing:
- Verified end-to-end invitation flow from creation to acceptance
- Confirmed tenant, domain, and owner user creation
- Validated schema context fixes for multi-tenant environment
- Tested active/inactive business filtering

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
poduck
2025-11-28 03:55:07 -05:00
parent 83815fcb34
commit d158c1ddb0
32 changed files with 3715 additions and 201 deletions

View File

@@ -61,6 +61,8 @@ export interface Business {
isTrialExpired?: boolean;
daysLeftInTrial?: number;
resourceTypes?: ResourceTypeDefinition[]; // Custom resource types
// Platform-controlled permissions
canManageOAuthCredentials?: boolean;
}
export type UserRole = 'superuser' | 'platform_manager' | 'platform_support' | 'owner' | 'manager' | 'staff' | 'resource' | 'customer';
@@ -196,34 +198,34 @@ export interface PlatformMetric {
// --- OAuth Settings Types ---
export interface OAuthProvider {
id: string;
name: string;
icon: string;
description: string;
}
export interface BusinessOAuthSettings {
enabledProviders: string[];
allowRegistration: boolean;
autoLinkByEmail: boolean;
useCustomCredentials: boolean;
}
export interface BusinessOAuthSettingsResponse {
businessSettings: BusinessOAuthSettings;
availableProviders: string[];
settings: BusinessOAuthSettings;
availableProviders: OAuthProvider[];
}
// --- OAuth Credentials Types ---
export interface OAuthProviderCredentials {
export interface OAuthProviderCredential {
client_id: string;
client_secret: string;
team_id?: string; // Apple only
key_id?: string; // Apple only
tenant_id?: string; // Microsoft only
has_secret: boolean;
}
export interface BusinessOAuthCredentials {
use_custom_credentials: boolean;
google?: OAuthProviderCredentials;
apple?: OAuthProviderCredentials;
facebook?: OAuthProviderCredentials;
linkedin?: OAuthProviderCredentials;
microsoft?: OAuthProviderCredentials;
twitter?: OAuthProviderCredentials;
twitch?: OAuthProviderCredentials;
export interface BusinessOAuthCredentialsResponse {
credentials: Record<string, OAuthProviderCredential>;
useCustomCredentials: boolean;
}