Improve demo tennat and user get
This commit is contained in:
@@ -60,6 +60,37 @@ export interface CloneDataRequest {
|
||||
* Demo session response
|
||||
* Backend: services/demo_session/app/api/schemas.py:18-30 (DemoSessionResponse)
|
||||
*/
|
||||
/**
|
||||
* Demo user data returned in session response
|
||||
* Matches the structure of a real login user response
|
||||
*/
|
||||
export interface DemoUser {
|
||||
id: string;
|
||||
email: string;
|
||||
full_name: string;
|
||||
role: string;
|
||||
is_active: boolean;
|
||||
is_verified: boolean;
|
||||
tenant_id: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Demo tenant data returned in session response
|
||||
* Matches the structure of a real tenant response
|
||||
*/
|
||||
export interface DemoTenant {
|
||||
id: string;
|
||||
name: string;
|
||||
subdomain: string;
|
||||
subscription_tier: string;
|
||||
tenant_type: string;
|
||||
business_type: string;
|
||||
business_model: string;
|
||||
description: string;
|
||||
is_active: boolean;
|
||||
}
|
||||
|
||||
export interface DemoSessionResponse {
|
||||
session_id: string;
|
||||
virtual_tenant_id: string;
|
||||
@@ -69,9 +100,11 @@ export interface DemoSessionResponse {
|
||||
expires_at: string; // ISO datetime
|
||||
demo_config: Record<string, any>;
|
||||
session_token: string;
|
||||
subscription_tier: string; // NEW: Subscription tier from demo session
|
||||
is_enterprise: boolean; // NEW: Whether this is an enterprise demo
|
||||
tenant_name: string; // NEW: Tenant name for display
|
||||
subscription_tier: string;
|
||||
is_enterprise: boolean;
|
||||
// Complete user and tenant data (like a real login response)
|
||||
user: DemoUser;
|
||||
tenant: DemoTenant;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,9 +72,19 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
||||
}
|
||||
}
|
||||
} else if (authStore.isAuthenticated) {
|
||||
// User is marked as authenticated but no tokens, logout
|
||||
console.log('No tokens found but user marked as authenticated, logging out');
|
||||
authStore.logout();
|
||||
// User is marked as authenticated but no tokens
|
||||
// Check if this is a demo session - demo sessions don't use JWT tokens
|
||||
const isDemoMode = localStorage.getItem('demo_mode') === 'true';
|
||||
const demoSessionId = localStorage.getItem('demo_session_id');
|
||||
|
||||
if (isDemoMode && demoSessionId) {
|
||||
// Demo session: authentication is valid via X-Demo-Session-Id header
|
||||
console.log('Demo session detected, maintaining authentication state');
|
||||
} else {
|
||||
// Regular user without tokens - logout
|
||||
console.log('No tokens found but user marked as authenticated, logging out');
|
||||
authStore.logout();
|
||||
}
|
||||
} else {
|
||||
console.log('No stored auth data found');
|
||||
}
|
||||
|
||||
@@ -364,13 +364,14 @@ export function BakeryDashboard({ plan }: { plan?: string }) {
|
||||
</div>
|
||||
|
||||
{/* Setup Flow - Three States */}
|
||||
{loadingOnboarding ? (
|
||||
/* Loading state for onboarding checks */
|
||||
{/* DEMO MODE BYPASS: Demo tenants have pre-configured data, skip onboarding blocker */}
|
||||
{loadingOnboarding && !isDemoMode ? (
|
||||
/* Loading state for onboarding checks (non-demo only) */
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2" style={{ borderColor: 'var(--color-primary)' }}></div>
|
||||
</div>
|
||||
) : progressPercentage < 50 ? (
|
||||
/* STATE 1: Critical Missing (<50%) - Full-page blocker */
|
||||
) : progressPercentage < 50 && !isDemoMode ? (
|
||||
/* STATE 1: Critical Missing (<50%) - Full-page blocker (non-demo only) */
|
||||
<SetupWizardBlocker
|
||||
criticalSections={setupSections}
|
||||
/>
|
||||
|
||||
@@ -198,26 +198,29 @@ const DemoPage = () => {
|
||||
localStorage.setItem('virtual_tenant_id', sessionData.virtual_tenant_id);
|
||||
localStorage.setItem('demo_expires_at', sessionData.expires_at);
|
||||
|
||||
// BUG FIX: Store the session token as access_token for authentication
|
||||
if (sessionData.session_token) {
|
||||
// Store the session token as access_token for authentication
|
||||
if (sessionData.session_token && sessionData.user) {
|
||||
console.log('🔐 [DemoPage] Storing session token:', sessionData.session_token.substring(0, 20) + '...');
|
||||
localStorage.setItem('access_token', sessionData.session_token);
|
||||
|
||||
// CRITICAL FIX: Update Zustand auth store with demo auth
|
||||
// This ensures the token persists across navigation and page reloads
|
||||
// Use complete user data from backend (like a real login response)
|
||||
setDemoAuth(sessionData.session_token, {
|
||||
id: 'demo-user',
|
||||
email: 'demo@bakery.com',
|
||||
full_name: 'Demo User',
|
||||
is_active: true,
|
||||
is_verified: true,
|
||||
created_at: new Date().toISOString(),
|
||||
tenant_id: sessionData.virtual_tenant_id,
|
||||
}, tier); // NEW: Pass subscription tier to setDemoAuth
|
||||
id: sessionData.user.id,
|
||||
email: sessionData.user.email,
|
||||
full_name: sessionData.user.full_name,
|
||||
is_active: sessionData.user.is_active,
|
||||
is_verified: sessionData.user.is_verified,
|
||||
created_at: sessionData.user.created_at,
|
||||
tenant_id: sessionData.user.tenant_id,
|
||||
role: sessionData.user.role as any,
|
||||
}, tier);
|
||||
|
||||
console.log('✅ [DemoPage] Demo auth set in store');
|
||||
// Store tenant data in localStorage for tenant initializer
|
||||
localStorage.setItem('demo_tenant_data', JSON.stringify(sessionData.tenant));
|
||||
|
||||
console.log('✅ [DemoPage] Demo auth set in store with complete user data');
|
||||
} else {
|
||||
console.error('❌ [DemoPage] No session_token in response!', sessionData);
|
||||
console.error('❌ [DemoPage] Missing session_token or user in response!', sessionData);
|
||||
}
|
||||
|
||||
// Now poll for status until ready
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useIsAuthenticated } from './auth.store';
|
||||
import { useIsAuthenticated, useAuthStore } from './auth.store';
|
||||
import { useTenantActions, useAvailableTenants, useCurrentTenant, useParentTenant } from './tenant.store';
|
||||
import { useIsDemoMode, useDemoSessionId, useDemoAccountType } from '../hooks/useAccessControl';
|
||||
import { useSubscription } from '../api/hooks/subscription';
|
||||
@@ -19,29 +19,19 @@ const getDemoTierForAccountType = (accountType: string | null): SubscriptionTier
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines appropriate tenant characteristics based on account type
|
||||
* Gets tenant data from localStorage (set by DemoPage from backend response)
|
||||
* Returns null if not available - caller must handle this case
|
||||
*/
|
||||
const getTenantDetailsForAccountType = (accountType: string | null) => {
|
||||
// These details match the fixture files:
|
||||
// professional: shared/demo/fixtures/professional/01-tenant.json
|
||||
// enterprise: shared/demo/fixtures/enterprise/parent/01-tenant.json
|
||||
const details = {
|
||||
professional: {
|
||||
name: 'Panadería Artesana Madrid - Demo',
|
||||
business_type: 'bakery',
|
||||
business_model: 'professional',
|
||||
description: 'Professional tier demo tenant for bakery operations'
|
||||
},
|
||||
enterprise: {
|
||||
name: 'Panadería Artesana España - Central',
|
||||
business_type: 'bakery',
|
||||
business_model: 'enterprise',
|
||||
description: 'Central production facility and parent tenant for Panadería Artesana España multi-location bakery chain'
|
||||
const getTenantDataFromStorage = (): Record<string, any> | null => {
|
||||
const storedTenantData = localStorage.getItem('demo_tenant_data');
|
||||
if (storedTenantData) {
|
||||
try {
|
||||
return JSON.parse(storedTenantData);
|
||||
} catch (e) {
|
||||
console.error('Failed to parse stored tenant data:', e);
|
||||
}
|
||||
};
|
||||
|
||||
const defaultDetails = details.professional;
|
||||
return details[accountType as keyof typeof details] || defaultDetails;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -58,6 +48,8 @@ export const useTenantInitializer = () => {
|
||||
const parentTenant = useParentTenant(); // Track if we're viewing a child tenant
|
||||
const { loadUserTenants, loadChildTenants, setCurrentTenant, setAvailableTenants } = useTenantActions();
|
||||
const { subscriptionInfo } = useSubscription();
|
||||
// Use reactive hook for user to ensure re-run when auth store rehydrates
|
||||
const authUser = useAuthStore((state) => state.user);
|
||||
|
||||
// Load tenants for authenticated users (but not demo users - they have special initialization below)
|
||||
useEffect(() => {
|
||||
@@ -101,30 +93,40 @@ export const useTenantInitializer = () => {
|
||||
typeof currentTenant === 'object' &&
|
||||
currentTenant.id === virtualTenantId;
|
||||
|
||||
// Determine the appropriate subscription tier based on stored value or account type
|
||||
const subscriptionTier = storedTier as SubscriptionTier || getDemoTierForAccountType(demoAccountType);
|
||||
// Get tenant data from localStorage (set by DemoPage from backend)
|
||||
const tenantData = getTenantDataFromStorage();
|
||||
// Use reactive authUser from hook instead of getState() to ensure re-run when user is available
|
||||
const userId = authUser?.id;
|
||||
|
||||
// Get appropriate tenant details based on account type
|
||||
const tenantDetails = getTenantDetailsForAccountType(demoAccountType);
|
||||
// Guard: If no tenant data or user is available, skip tenant setup
|
||||
// This means the demo session wasn't properly initialized or auth store hasn't rehydrated yet
|
||||
if (!tenantData || !userId) {
|
||||
console.log('[useTenantInitializer] Waiting for tenant data or user - tenantData:', !!tenantData, 'userId:', userId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a complete tenant object matching TenantResponse structure
|
||||
// Determine the appropriate subscription tier
|
||||
const subscriptionTier = storedTier as SubscriptionTier || tenantData.subscription_tier || getDemoTierForAccountType(demoAccountType);
|
||||
|
||||
// Create a complete tenant object using data from backend response
|
||||
const mockTenant = {
|
||||
id: virtualTenantId,
|
||||
name: tenantDetails.name,
|
||||
subdomain: `demo-${demoSessionId.slice(0, 8)}`,
|
||||
business_type: tenantDetails.business_type,
|
||||
business_model: tenantDetails.business_model,
|
||||
description: tenantDetails.description,
|
||||
id: tenantData.id || virtualTenantId,
|
||||
name: tenantData.name,
|
||||
subdomain: tenantData.subdomain,
|
||||
business_type: tenantData.business_type,
|
||||
business_model: tenantData.business_model,
|
||||
description: tenantData.description,
|
||||
address: 'Demo Address',
|
||||
city: 'Madrid',
|
||||
postal_code: '28001',
|
||||
phone: null,
|
||||
is_active: true,
|
||||
is_active: tenantData.is_active,
|
||||
subscription_plan: subscriptionTier,
|
||||
subscription_tier: subscriptionTier,
|
||||
tenant_type: tenantData.tenant_type,
|
||||
ml_model_trained: false,
|
||||
last_training_date: null,
|
||||
owner_id: 'demo-user',
|
||||
owner_id: userId,
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
|
||||
@@ -179,5 +181,5 @@ export const useTenantInitializer = () => {
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [isDemoMode, demoSessionId, demoAccountType, currentTenant, availableTenants, parentTenant, setCurrentTenant, setAvailableTenants]);
|
||||
}, [isDemoMode, demoSessionId, demoAccountType, currentTenant, availableTenants, parentTenant, setCurrentTenant, setAvailableTenants, authUser]);
|
||||
};
|
||||
Reference in New Issue
Block a user