Add new Frontend
This commit is contained in:
246
frontend/src/App.tsx
Normal file
246
frontend/src/App.tsx
Normal file
@@ -0,0 +1,246 @@
|
||||
const [appState, setAppState] = useState<AppState>({
|
||||
isAuthenticated: false,
|
||||
isLoading: true,
|
||||
user: null,
|
||||
currentPage: 'landing' // 👈 Startimport React, { useState, useEffect } from 'react';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
|
||||
// Components
|
||||
import LoadingSpinner from './components/ui/LoadingSpinner';
|
||||
import ErrorBoundary from './components/ErrorBoundary';
|
||||
import LandingPage from './pages/landing/LandingPage';
|
||||
import LoginPage from './pages/auth/LoginPage';
|
||||
import RegisterPage from './pages/auth/RegisterPage';
|
||||
import OnboardingPage from './pages/onboarding/OnboardingPage';
|
||||
import DashboardPage from './pages/dashboard/DashboardPage';
|
||||
import ForecastPage from './pages/forecast/ForecastPage';
|
||||
import OrdersPage from './pages/orders/OrdersPage';
|
||||
import SettingsPage from './pages/settings/SettingsPage';
|
||||
import Layout from './components/layout/Layout';
|
||||
|
||||
// Store and types
|
||||
import { store } from './store';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
// i18n
|
||||
import './i18n';
|
||||
|
||||
// Global styles
|
||||
import './styles/globals.css';
|
||||
|
||||
type CurrentPage = 'landing' | 'login' | 'register' | 'onboarding' | 'dashboard' | 'forecast' | 'orders' | 'settings';
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
email: string;
|
||||
fullName: string;
|
||||
role: string;
|
||||
isOnboardingComplete: boolean;
|
||||
}
|
||||
|
||||
interface AppState {
|
||||
isAuthenticated: boolean;
|
||||
isLoading: boolean;
|
||||
user: User | null;
|
||||
currentPage: CurrentPage;
|
||||
}
|
||||
|
||||
const LoadingFallback = () => (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
||||
<div className="text-center">
|
||||
<LoadingSpinner size="lg" />
|
||||
<p className="mt-4 text-gray-600">Cargando PanIA...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [appState, setAppState] = useState<AppState>({
|
||||
isAuthenticated: false,
|
||||
isLoading: true,
|
||||
user: null,
|
||||
currentPage: 'landing' // 👈 Start with landing page
|
||||
});
|
||||
|
||||
// Initialize app and check authentication
|
||||
useEffect(() => {
|
||||
const initializeApp = async () => {
|
||||
try {
|
||||
// Check for stored auth token
|
||||
const token = localStorage.getItem('auth_token');
|
||||
const userData = localStorage.getItem('user_data');
|
||||
|
||||
if (token && userData) {
|
||||
const user = JSON.parse(userData);
|
||||
setAppState({
|
||||
isAuthenticated: true,
|
||||
isLoading: false,
|
||||
user,
|
||||
currentPage: user.isOnboardingComplete ? 'dashboard' : 'onboarding'
|
||||
});
|
||||
} else {
|
||||
setAppState(prev => ({
|
||||
...prev,
|
||||
isLoading: false,
|
||||
currentPage: 'landing' // 👈 Show landing page for non-authenticated users
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('App initialization error:', error);
|
||||
setAppState(prev => ({
|
||||
...prev,
|
||||
isLoading: false,
|
||||
currentPage: 'landing' // 👈 Fallback to landing page
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
initializeApp();
|
||||
}, []);
|
||||
|
||||
const handleLogin = (user: User, token: string) => {
|
||||
localStorage.setItem('auth_token', token);
|
||||
localStorage.setItem('user_data', JSON.stringify(user));
|
||||
|
||||
setAppState({
|
||||
isAuthenticated: true,
|
||||
isLoading: false,
|
||||
user,
|
||||
currentPage: user.isOnboardingComplete ? 'dashboard' : 'onboarding'
|
||||
});
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
localStorage.removeItem('auth_token');
|
||||
localStorage.removeItem('user_data');
|
||||
|
||||
setAppState({
|
||||
isAuthenticated: false,
|
||||
isLoading: false,
|
||||
user: null,
|
||||
currentPage: 'landing' // 👈 Return to landing page after logout
|
||||
});
|
||||
};
|
||||
|
||||
const handleOnboardingComplete = () => {
|
||||
const updatedUser = { ...appState.user!, isOnboardingComplete: true };
|
||||
localStorage.setItem('user_data', JSON.stringify(updatedUser));
|
||||
|
||||
setAppState(prev => ({
|
||||
...prev,
|
||||
user: updatedUser,
|
||||
currentPage: 'dashboard'
|
||||
}));
|
||||
};
|
||||
|
||||
const navigateTo = (page: CurrentPage) => {
|
||||
setAppState(prev => ({ ...prev, currentPage: page }));
|
||||
};
|
||||
|
||||
if (appState.isLoading) {
|
||||
return <LoadingFallback />;
|
||||
}
|
||||
|
||||
const renderCurrentPage = () => {
|
||||
// Public pages (non-authenticated)
|
||||
if (!appState.isAuthenticated) {
|
||||
switch (appState.currentPage) {
|
||||
case 'login':
|
||||
return (
|
||||
<LoginPage
|
||||
onLogin={handleLogin}
|
||||
onNavigateToRegister={() => navigateTo('register')}
|
||||
/>
|
||||
);
|
||||
case 'register':
|
||||
return (
|
||||
<RegisterPage
|
||||
onLogin={handleLogin}
|
||||
onNavigateToLogin={() => navigateTo('login')}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<LandingPage
|
||||
onNavigateToLogin={() => navigateTo('login')}
|
||||
onNavigateToRegister={() => navigateTo('register')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Authenticated pages
|
||||
if (!appState.user?.isOnboardingComplete && appState.currentPage !== 'settings') {
|
||||
return (
|
||||
<OnboardingPage
|
||||
user={appState.user!}
|
||||
onComplete={handleOnboardingComplete}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Main app pages with layout
|
||||
const pageComponent = () => {
|
||||
switch (appState.currentPage) {
|
||||
case 'forecast':
|
||||
return <ForecastPage />;
|
||||
case 'orders':
|
||||
return <OrdersPage />;
|
||||
case 'settings':
|
||||
return <SettingsPage user={appState.user!} onLogout={handleLogout} />;
|
||||
default:
|
||||
return <DashboardPage user={appState.user!} />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout
|
||||
user={appState.user!}
|
||||
currentPage={appState.currentPage}
|
||||
onNavigate={navigateTo}
|
||||
onLogout={handleLogout}
|
||||
>
|
||||
{pageComponent()}
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<ErrorBoundary>
|
||||
<div className="App min-h-screen bg-gray-50">
|
||||
{renderCurrentPage()}
|
||||
|
||||
{/* Global Toast Notifications */}
|
||||
<Toaster
|
||||
position="top-right"
|
||||
toastOptions={{
|
||||
duration: 4000,
|
||||
style: {
|
||||
background: '#fff',
|
||||
color: '#333',
|
||||
boxShadow: '0 4px 25px -5px rgba(0, 0, 0, 0.1)',
|
||||
borderRadius: '12px',
|
||||
padding: '16px',
|
||||
},
|
||||
success: {
|
||||
iconTheme: {
|
||||
primary: '#22c55e',
|
||||
secondary: '#fff',
|
||||
},
|
||||
},
|
||||
error: {
|
||||
iconTheme: {
|
||||
primary: '#ef4444',
|
||||
secondary: '#fff',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
Reference in New Issue
Block a user