- Tags management page: grid cards, rename/delete/merge modals, color-coded - Onboarding wizard: 4-step full-screen flow for new users (welcome, client, style, tour) - Client list pagination: page controls, page size selector, URL query params - Pipeline view unaffected (shows all clients) - Tags added to sidebar navigation - All components support dark mode
92 lines
4.5 KiB
TypeScript
92 lines
4.5 KiB
TypeScript
import { useEffect, lazy, Suspense } from 'react';
|
|
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
|
|
import { useAuthStore } from '@/stores/auth';
|
|
import Layout from '@/components/Layout';
|
|
import { PageLoader } from '@/components/LoadingSpinner';
|
|
import ErrorBoundary from '@/components/ErrorBoundary';
|
|
import { ToastContainer, toast } from '@/components/Toast';
|
|
import { api } from '@/lib/api';
|
|
|
|
const LoginPage = lazy(() => import('@/pages/LoginPage'));
|
|
const DashboardPage = lazy(() => import('@/pages/DashboardPage'));
|
|
const ClientsPage = lazy(() => import('@/pages/ClientsPage'));
|
|
const ClientDetailPage = lazy(() => import('@/pages/ClientDetailPage'));
|
|
const EventsPage = lazy(() => import('@/pages/EventsPage'));
|
|
const EmailsPage = lazy(() => import('@/pages/EmailsPage'));
|
|
const SettingsPage = lazy(() => import('@/pages/SettingsPage'));
|
|
const AdminPage = lazy(() => import('@/pages/AdminPage'));
|
|
const NetworkPage = lazy(() => import('@/pages/NetworkPage'));
|
|
const ReportsPage = lazy(() => import('@/pages/ReportsPage'));
|
|
const TemplatesPage = lazy(() => import('@/pages/TemplatesPage'));
|
|
const SegmentsPage = lazy(() => import('@/pages/SegmentsPage'));
|
|
const InvitePage = lazy(() => import('@/pages/InvitePage'));
|
|
const ForgotPasswordPage = lazy(() => import('@/pages/ForgotPasswordPage'));
|
|
const ResetPasswordPage = lazy(() => import('@/pages/ResetPasswordPage'));
|
|
const AuditLogPage = lazy(() => import('@/pages/AuditLogPage'));
|
|
const TagsPage = lazy(() => import('@/pages/TagsPage'));
|
|
|
|
function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
|
const { isAuthenticated, isLoading } = useAuthStore();
|
|
if (isLoading) return <PageLoader />;
|
|
if (!isAuthenticated) return <Navigate to="/login" replace />;
|
|
return <>{children}</>;
|
|
}
|
|
|
|
// Setup global API error interceptor
|
|
api.setErrorHandler((status, message) => {
|
|
if (status === 401) {
|
|
toast.error('Session expired. Please log in again.');
|
|
} else if (status === 403) {
|
|
toast.error('Access denied: ' + message);
|
|
} else if (status >= 500) {
|
|
toast.error('Server error: ' + message);
|
|
}
|
|
});
|
|
|
|
function PageErrorBoundary({ children }: { children: React.ReactNode }) {
|
|
return <ErrorBoundary>{children}</ErrorBoundary>;
|
|
}
|
|
|
|
export default function App() {
|
|
const { checkSession, isAuthenticated } = useAuthStore();
|
|
|
|
useEffect(() => {
|
|
checkSession();
|
|
}, [checkSession]);
|
|
|
|
return (
|
|
<BrowserRouter>
|
|
<Suspense fallback={<PageLoader />}>
|
|
<Routes>
|
|
<Route path="/login" element={
|
|
isAuthenticated ? <Navigate to="/" replace /> : <PageErrorBoundary><LoginPage /></PageErrorBoundary>
|
|
} />
|
|
<Route path="/invite/:token" element={<PageErrorBoundary><InvitePage /></PageErrorBoundary>} />
|
|
<Route path="/forgot-password" element={<PageErrorBoundary><ForgotPasswordPage /></PageErrorBoundary>} />
|
|
<Route path="/reset-password/:token" element={<PageErrorBoundary><ResetPasswordPage /></PageErrorBoundary>} />
|
|
<Route path="/" element={
|
|
<ProtectedRoute>
|
|
<Layout />
|
|
</ProtectedRoute>
|
|
}>
|
|
<Route index element={<PageErrorBoundary><DashboardPage /></PageErrorBoundary>} />
|
|
<Route path="clients" element={<PageErrorBoundary><ClientsPage /></PageErrorBoundary>} />
|
|
<Route path="clients/:id" element={<PageErrorBoundary><ClientDetailPage /></PageErrorBoundary>} />
|
|
<Route path="events" element={<PageErrorBoundary><EventsPage /></PageErrorBoundary>} />
|
|
<Route path="emails" element={<PageErrorBoundary><EmailsPage /></PageErrorBoundary>} />
|
|
<Route path="network" element={<PageErrorBoundary><NetworkPage /></PageErrorBoundary>} />
|
|
<Route path="reports" element={<PageErrorBoundary><ReportsPage /></PageErrorBoundary>} />
|
|
<Route path="templates" element={<PageErrorBoundary><TemplatesPage /></PageErrorBoundary>} />
|
|
<Route path="tags" element={<PageErrorBoundary><TagsPage /></PageErrorBoundary>} />
|
|
<Route path="segments" element={<PageErrorBoundary><SegmentsPage /></PageErrorBoundary>} />
|
|
<Route path="settings" element={<PageErrorBoundary><SettingsPage /></PageErrorBoundary>} />
|
|
<Route path="admin" element={<PageErrorBoundary><AdminPage /></PageErrorBoundary>} />
|
|
<Route path="audit-log" element={<PageErrorBoundary><AuditLogPage /></PageErrorBoundary>} />
|
|
</Route>
|
|
</Routes>
|
|
</Suspense>
|
|
<ToastContainer />
|
|
</BrowserRouter>
|
|
);
|
|
}
|