Add DEMO feature to the project

This commit is contained in:
Urtzi Alfaro
2025-10-03 14:09:34 +02:00
parent 1243c2ca6d
commit dc8221bd2f
77 changed files with 6251 additions and 1074 deletions

View File

@@ -0,0 +1,256 @@
import React, { useState, useEffect } from 'react';
import { useNavigate, Link } from 'react-router-dom';
import { PublicLayout } from '../../components/layout';
import { Button } from '../../components/ui';
import { getDemoAccounts, createDemoSession, DemoAccount } from '../../api/services/demo';
import { apiClient } from '../../api/client';
import { Check, Clock, Shield, Play, Zap, ArrowRight, Store, Factory } from 'lucide-react';
export const DemoPage: React.FC = () => {
const navigate = useNavigate();
const [demoAccounts, setDemoAccounts] = useState<DemoAccount[]>([]);
const [loading, setLoading] = useState(true);
const [creatingSession, setCreatingSession] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchDemoAccounts = async () => {
try {
const accounts = await getDemoAccounts();
setDemoAccounts(accounts);
} catch (err) {
setError('Error al cargar las cuentas demo');
console.error('Error fetching demo accounts:', err);
} finally {
setLoading(false);
}
};
fetchDemoAccounts();
}, []);
const handleStartDemo = async (accountType: string) => {
setCreatingSession(true);
setError(null);
try {
const session = await createDemoSession({
demo_account_type: accountType as 'individual_bakery' | 'central_baker',
});
// Store session ID in API client
apiClient.setDemoSessionId(session.session_id);
// Store session info in localStorage for UI
localStorage.setItem('demo_mode', 'true');
localStorage.setItem('demo_session_id', session.session_id);
localStorage.setItem('demo_account_type', accountType);
localStorage.setItem('demo_expires_at', session.expires_at);
localStorage.setItem('demo_tenant_id', session.virtual_tenant_id);
// Navigate to dashboard
navigate('/app/dashboard');
} catch (err: any) {
setError(err?.message || 'Error al crear sesión demo');
console.error('Error creating demo session:', err);
} finally {
setCreatingSession(false);
}
};
const getAccountIcon = (accountType: string) => {
return accountType === 'individual_bakery' ? Store : Factory;
};
if (loading) {
return (
<PublicLayout
variant="full-width"
contentPadding="none"
headerProps={{
showThemeToggle: true,
showAuthButtons: true,
showLanguageSelector: true,
}}
>
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[var(--bg-primary)] via-[var(--bg-secondary)] to-[var(--color-primary)]/5">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[var(--color-primary)] mx-auto"></div>
<p className="mt-4 text-[var(--text-secondary)]">Cargando cuentas demo...</p>
</div>
</div>
</PublicLayout>
);
}
return (
<PublicLayout
variant="full-width"
contentPadding="none"
headerProps={{
showThemeToggle: true,
showAuthButtons: true,
showLanguageSelector: true,
}}
>
{/* Hero Section */}
<section className="relative py-20 lg:py-32 bg-gradient-to-br from-[var(--bg-primary)] via-[var(--bg-secondary)] to-[var(--color-primary)]/5">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<div className="mb-6">
<span className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-[var(--color-primary)]/10 text-[var(--color-primary)]">
<Play className="w-4 h-4 mr-2" />
Demo Interactiva
</span>
</div>
<h1 className="text-4xl tracking-tight font-extrabold text-[var(--text-primary)] sm:text-5xl lg:text-6xl">
<span className="block">Prueba BakeryIA</span>
<span className="block text-[var(--color-primary)]">sin compromiso</span>
</h1>
<p className="mt-6 max-w-3xl mx-auto text-lg text-[var(--text-secondary)] sm:text-xl">
Explora nuestro sistema con datos reales de panaderías españolas.
Elige el tipo de negocio que mejor se adapte a tu caso.
</p>
<div className="mt-8 flex items-center justify-center space-x-6 text-sm text-[var(--text-tertiary)]">
<div className="flex items-center">
<Check className="w-4 h-4 text-green-500 mr-2" />
Sin tarjeta de crédito
</div>
<div className="flex items-center">
<Clock className="w-4 h-4 text-green-500 mr-2" />
30 minutos de acceso
</div>
<div className="flex items-center">
<Shield className="w-4 h-4 text-green-500 mr-2" />
Datos aislados y seguros
</div>
</div>
</div>
{error && (
<div className="mb-8 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 text-red-700 dark:text-red-400 px-4 py-3 rounded-lg max-w-2xl mx-auto">
{error}
</div>
)}
{/* Demo Account Cards */}
<div className="grid md:grid-cols-2 gap-8 max-w-6xl mx-auto">
{demoAccounts.map((account) => {
const Icon = getAccountIcon(account.account_type);
return (
<div
key={account.account_type}
className="relative bg-[var(--bg-primary)] rounded-2xl shadow-xl hover:shadow-2xl transition-all duration-300 overflow-hidden border border-[var(--border-default)] group"
>
{/* Gradient overlay */}
<div className="absolute inset-0 bg-gradient-to-br from-[var(--color-primary)]/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div className="relative p-8">
{/* Header */}
<div className="flex items-start justify-between mb-6">
<div className="flex items-center">
<div className="p-3 rounded-xl bg-[var(--color-primary)]/10 text-[var(--color-primary)]">
<Icon className="w-6 h-6" />
</div>
<div className="ml-4">
<h2 className="text-2xl font-bold text-[var(--text-primary)]">
{account.name}
</h2>
<p className="text-sm text-[var(--text-tertiary)] mt-1">
{account.business_model}
</p>
</div>
</div>
<span className="px-3 py-1 bg-[var(--color-primary)]/10 text-[var(--color-primary)] rounded-full text-xs font-semibold">
DEMO
</span>
</div>
{/* Description */}
<p className="text-[var(--text-secondary)] mb-6">
{account.description}
</p>
{/* Features */}
{account.features && account.features.length > 0 && (
<div className="mb-6 space-y-2">
<p className="text-sm font-semibold text-[var(--text-primary)] mb-3">
Funcionalidades incluidas:
</p>
{account.features.map((feature, idx) => (
<div key={idx} className="flex items-center text-sm text-[var(--text-secondary)]">
<Zap className="w-4 h-4 mr-2 text-[var(--color-primary)]" />
{feature}
</div>
))}
</div>
)}
{/* Demo Benefits */}
<div className="space-y-2 mb-8 pt-6 border-t border-[var(--border-default)]">
<div className="flex items-center text-sm text-[var(--text-secondary)]">
<Check className="w-4 h-4 mr-2 text-green-500" />
Datos reales en español
</div>
<div className="flex items-center text-sm text-[var(--text-secondary)]">
<Check className="w-4 h-4 mr-2 text-green-500" />
Sesión aislada de 30 minutos
</div>
<div className="flex items-center text-sm text-[var(--text-secondary)]">
<Check className="w-4 h-4 mr-2 text-green-500" />
Sin necesidad de registro
</div>
</div>
{/* CTA Button */}
<Button
onClick={() => handleStartDemo(account.account_type)}
disabled={creatingSession}
size="lg"
className="w-full bg-[var(--color-primary)] hover:bg-[var(--color-primary-dark)] text-white shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200"
>
{creatingSession ? (
<span className="flex items-center justify-center">
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Creando sesión...
</span>
) : (
<>
<Play className="mr-2 w-5 h-5" />
Probar Demo Ahora
</>
)}
</Button>
</div>
</div>
);
})}
</div>
{/* Footer CTA */}
<div className="mt-16 text-center">
<p className="text-[var(--text-secondary)] mb-4">
¿Ya tienes una cuenta?
</p>
<Link
to="/login"
className="inline-flex items-center text-[var(--color-primary)] hover:text-[var(--color-primary-dark)] font-semibold transition-colors"
>
Inicia sesión aquí
<ArrowRight className="ml-2 w-4 h-4" />
</Link>
</div>
</div>
</section>
</PublicLayout>
);
};
export default DemoPage;