2026-02-20 19:03:15 +00:00
|
|
|
import React, { useState, useEffect, Suspense, lazy } from 'react';
|
2026-02-17 20:49:42 +00:00
|
|
|
import Header from './components/landing/Header';
|
|
|
|
|
import Hero from './components/landing/Hero';
|
|
|
|
|
import CoachHighlight from './components/landing/CoachHighlight';
|
|
|
|
|
import HowItWorks from './components/landing/HowItWorks';
|
|
|
|
|
import Features from './components/landing/Features';
|
|
|
|
|
import Testimonials from './components/landing/Testimonials';
|
|
|
|
|
import Pricing from './components/landing/Pricing';
|
|
|
|
|
import FAQ from './components/landing/FAQ';
|
|
|
|
|
import Footer from './components/landing/Footer';
|
|
|
|
|
import RegistrationModal from './components/modals/RegistrationModal';
|
|
|
|
|
import CalculatorsModal from './components/modals/CalculatorsModal';
|
|
|
|
|
import { LanguageProvider } from './contexts/LanguageContext';
|
2026-02-20 19:03:15 +00:00
|
|
|
import { UserProvider, useUser } from './contexts/UserContext';
|
2026-02-17 20:49:42 +00:00
|
|
|
import { Loader2 } from 'lucide-react';
|
|
|
|
|
|
2026-02-20 19:03:15 +00:00
|
|
|
const Dashboard = lazy(() => import('./pages/Dashboard'));
|
|
|
|
|
const AdminPanel = lazy(() => import('./pages/AdminPanel'));
|
|
|
|
|
const ProfessionalDashboard = lazy(() => import('./pages/ProfessionalDashboard'));
|
|
|
|
|
const FAQPage = lazy(() => import('./pages/FAQPage'));
|
|
|
|
|
const PrivacyPolicy = lazy(() => import('./pages/legal/PrivacyPolicy'));
|
|
|
|
|
const TermsOfService = lazy(() => import('./pages/legal/TermsOfService'));
|
|
|
|
|
const DataDeletion = lazy(() => import('./pages/legal/DataDeletion'));
|
|
|
|
|
|
|
|
|
|
export type ViewState = 'home' | 'faq' | 'privacy' | 'terms' | 'data-deletion';
|
|
|
|
|
|
2026-02-17 20:49:42 +00:00
|
|
|
const AppContent: React.FC = () => {
|
|
|
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
|
|
|
const [isToolsOpen, setIsToolsOpen] = useState(false);
|
|
|
|
|
const [authMode, setAuthMode] = useState<'login' | 'register'>('register');
|
|
|
|
|
const [selectedPlan, setSelectedPlan] = useState('starter');
|
2026-02-20 19:03:15 +00:00
|
|
|
|
|
|
|
|
// Custom simple router state based on URL
|
|
|
|
|
const [currentPath, setCurrentPath] = useState(window.location.pathname);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const handleLocationChange = () => setCurrentPath(window.location.pathname);
|
|
|
|
|
window.addEventListener('popstate', handleLocationChange);
|
|
|
|
|
return () => window.removeEventListener('popstate', handleLocationChange);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// Helper mapping from path to ViewState internally if needed
|
|
|
|
|
const getCurrentView = (): ViewState => {
|
2026-02-20 19:24:49 +00:00
|
|
|
const normalizedPath = currentPath.replace(/\/$/, '') || '/';
|
|
|
|
|
|
|
|
|
|
if (normalizedPath.includes('/faq')) return 'faq';
|
|
|
|
|
if (normalizedPath.includes('/privacidade')) return 'privacy';
|
|
|
|
|
if (normalizedPath.includes('/termos')) return 'terms';
|
|
|
|
|
if (normalizedPath.includes('/exclusao-de-dados')) return 'data-deletion';
|
|
|
|
|
|
|
|
|
|
return 'home';
|
2026-02-20 19:03:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const currentView = getCurrentView();
|
2026-02-17 23:01:09 +00:00
|
|
|
|
|
|
|
|
// Consume UserContext
|
|
|
|
|
const {
|
|
|
|
|
user,
|
|
|
|
|
loading,
|
|
|
|
|
isAdminView,
|
|
|
|
|
isProfessionalView,
|
|
|
|
|
isCompletingProfile,
|
|
|
|
|
toggleAdminView,
|
|
|
|
|
setIsProfessionalView,
|
|
|
|
|
logout,
|
|
|
|
|
refreshProfile
|
|
|
|
|
} = useUser();
|
|
|
|
|
|
|
|
|
|
// Effect to handle "Complete Profile" flow automatically
|
2026-02-17 20:49:42 +00:00
|
|
|
useEffect(() => {
|
2026-02-17 23:01:09 +00:00
|
|
|
if (isCompletingProfile) {
|
|
|
|
|
setAuthMode('register');
|
|
|
|
|
setIsModalOpen(true);
|
2026-02-17 20:49:42 +00:00
|
|
|
}
|
2026-02-17 23:01:09 +00:00
|
|
|
}, [isCompletingProfile]);
|
2026-02-17 20:49:42 +00:00
|
|
|
|
|
|
|
|
const handleOpenRegister = (plan: string = 'starter') => {
|
|
|
|
|
setSelectedPlan(plan);
|
|
|
|
|
setAuthMode('register');
|
|
|
|
|
setIsModalOpen(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleOpenLogin = (context?: 'user' | 'professional') => {
|
|
|
|
|
if (context === 'professional') {
|
|
|
|
|
localStorage.setItem('login_intent', 'professional');
|
|
|
|
|
} else {
|
|
|
|
|
localStorage.setItem('login_intent', 'user');
|
|
|
|
|
}
|
|
|
|
|
setAuthMode('login');
|
|
|
|
|
setIsModalOpen(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleAuthSuccess = async () => {
|
|
|
|
|
setIsModalOpen(false);
|
2026-02-17 23:01:09 +00:00
|
|
|
await refreshProfile();
|
|
|
|
|
// Login intent logic handled inside context or simply by state update
|
|
|
|
|
localStorage.removeItem('login_intent');
|
2026-02-17 20:49:42 +00:00
|
|
|
};
|
|
|
|
|
|
2026-02-20 19:03:15 +00:00
|
|
|
// Helper function for navigating with real URLs
|
|
|
|
|
const handleNavigate = (view: ViewState) => {
|
|
|
|
|
let path = '/';
|
|
|
|
|
if (view === 'faq') path = '/faq';
|
|
|
|
|
if (view === 'privacy') path = '/privacidade';
|
|
|
|
|
if (view === 'terms') path = '/termos';
|
|
|
|
|
if (view === 'data-deletion') path = '/exclusao-de-dados';
|
|
|
|
|
|
|
|
|
|
window.history.pushState({}, '', path);
|
|
|
|
|
setCurrentPath(path);
|
2026-02-17 20:49:42 +00:00
|
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-17 23:01:09 +00:00
|
|
|
if (loading) {
|
2026-02-17 20:49:42 +00:00
|
|
|
return (
|
|
|
|
|
<div className="min-h-screen flex items-center justify-center bg-white">
|
|
|
|
|
<Loader2 className="w-8 h-8 animate-spin text-brand-600" />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rota Admin
|
|
|
|
|
if (user && isAdminView && user.is_admin) {
|
2026-02-20 19:03:15 +00:00
|
|
|
return (
|
|
|
|
|
<Suspense fallback={<div className="min-h-screen flex items-center justify-center bg-white"><Loader2 className="w-8 h-8 animate-spin text-brand-600" /></div>}>
|
|
|
|
|
<AdminPanel user={user} onExitAdmin={toggleAdminView} onLogout={logout} />
|
|
|
|
|
</Suspense>
|
|
|
|
|
);
|
2026-02-17 20:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rota Profissional
|
|
|
|
|
if (user && isProfessionalView) {
|
2026-02-20 19:03:15 +00:00
|
|
|
return (
|
|
|
|
|
<Suspense fallback={<div className="min-h-screen flex items-center justify-center bg-white"><Loader2 className="w-8 h-8 animate-spin text-brand-600" /></div>}>
|
|
|
|
|
<ProfessionalDashboard user={user} onExit={() => setIsProfessionalView(false)} onLogout={logout} />
|
|
|
|
|
</Suspense>
|
|
|
|
|
);
|
2026-02-17 20:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rota Dashboard Usuário
|
|
|
|
|
if (user) {
|
|
|
|
|
return (
|
2026-02-20 19:03:15 +00:00
|
|
|
<Suspense fallback={<div className="min-h-screen flex items-center justify-center bg-gray-50"><Loader2 className="w-8 h-8 animate-spin text-brand-600" /></div>}>
|
|
|
|
|
<Dashboard
|
|
|
|
|
user={user}
|
|
|
|
|
onLogout={logout}
|
|
|
|
|
onOpenAdmin={user.is_admin ? toggleAdminView : undefined}
|
|
|
|
|
onOpenPro={() => setIsProfessionalView(true)}
|
2026-02-26 00:17:54 +00:00
|
|
|
initialTab={currentPath.includes('/meu-plano') ? 'coach' : 'overview'}
|
2026-02-20 19:03:15 +00:00
|
|
|
/>
|
|
|
|
|
</Suspense>
|
2026-02-17 20:49:42 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rota Pública (Landing Page ou FAQ Page)
|
|
|
|
|
return (
|
|
|
|
|
<div className="min-h-screen bg-white text-gray-900 font-sans selection:bg-brand-100 selection:text-brand-900">
|
|
|
|
|
<Header
|
|
|
|
|
onRegister={() => handleOpenRegister('starter')}
|
|
|
|
|
onLogin={handleOpenLogin}
|
|
|
|
|
onOpenTools={() => setIsToolsOpen(true)}
|
2026-02-17 23:01:09 +00:00
|
|
|
onNavigate={handleNavigate}
|
2026-02-17 20:49:42 +00:00
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<main>
|
|
|
|
|
{currentView === 'home' ? (
|
|
|
|
|
<>
|
|
|
|
|
<Hero onRegister={() => handleOpenRegister('starter')} />
|
|
|
|
|
<CoachHighlight onRegister={() => handleOpenRegister('starter')} />
|
|
|
|
|
<HowItWorks />
|
|
|
|
|
<Features />
|
|
|
|
|
<Testimonials />
|
|
|
|
|
<Pricing onRegister={handleOpenRegister} />
|
|
|
|
|
<FAQ />
|
|
|
|
|
</>
|
2026-02-20 19:03:15 +00:00
|
|
|
) : currentView === 'privacy' ? (
|
|
|
|
|
<Suspense fallback={<div className="min-h-screen flex items-center justify-center bg-white"><Loader2 className="w-8 h-8 animate-spin text-brand-600" /></div>}>
|
|
|
|
|
<PrivacyPolicy onBack={() => handleNavigate('home')} />
|
|
|
|
|
</Suspense>
|
|
|
|
|
) : currentView === 'terms' ? (
|
|
|
|
|
<Suspense fallback={<div className="min-h-screen flex items-center justify-center bg-white"><Loader2 className="w-8 h-8 animate-spin text-brand-600" /></div>}>
|
|
|
|
|
<TermsOfService onBack={() => handleNavigate('home')} />
|
|
|
|
|
</Suspense>
|
|
|
|
|
) : currentView === 'data-deletion' ? (
|
|
|
|
|
<Suspense fallback={<div className="min-h-screen flex items-center justify-center bg-white"><Loader2 className="w-8 h-8 animate-spin text-brand-600" /></div>}>
|
|
|
|
|
<DataDeletion onBack={() => handleNavigate('home')} />
|
|
|
|
|
</Suspense>
|
2026-02-17 20:49:42 +00:00
|
|
|
) : (
|
2026-02-20 19:03:15 +00:00
|
|
|
<Suspense fallback={<div className="min-h-screen flex items-center justify-center bg-white"><Loader2 className="w-8 h-8 animate-spin text-brand-600" /></div>}>
|
|
|
|
|
<FAQPage onBack={() => handleNavigate('home')} />
|
|
|
|
|
</Suspense>
|
2026-02-17 20:49:42 +00:00
|
|
|
)}
|
|
|
|
|
</main>
|
|
|
|
|
|
|
|
|
|
<Footer
|
|
|
|
|
onRegister={() => handleOpenRegister('starter')}
|
2026-02-17 23:01:09 +00:00
|
|
|
onNavigate={handleNavigate}
|
2026-02-17 20:49:42 +00:00
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<RegistrationModal
|
|
|
|
|
isOpen={isModalOpen}
|
|
|
|
|
onClose={() => setIsModalOpen(false)}
|
|
|
|
|
plan={selectedPlan}
|
|
|
|
|
mode={authMode}
|
2026-02-17 23:01:09 +00:00
|
|
|
isCompletingProfile={isCompletingProfile}
|
2026-02-17 20:49:42 +00:00
|
|
|
onSuccess={handleAuthSuccess}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<CalculatorsModal
|
|
|
|
|
isOpen={isToolsOpen}
|
|
|
|
|
onClose={() => setIsToolsOpen(false)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const App: React.FC = () => {
|
|
|
|
|
return (
|
2026-02-17 23:01:09 +00:00
|
|
|
<UserProvider>
|
|
|
|
|
<LanguageProvider>
|
|
|
|
|
<AppContent />
|
|
|
|
|
</LanguageProvider>
|
|
|
|
|
</UserProvider>
|
2026-02-17 20:49:42 +00:00
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default App;
|