feat: audit log page, meeting prep modal, communication style, error boundaries + toast
- AuditLogPage: filterable table with expandable details (admin only) - MeetingPrepModal: AI-generated meeting briefs with health score, talking points, conversation starters - Communication Style section in Settings: tone, greeting, signoff, writing samples, avoid words - ErrorBoundary wrapping all page routes with Try Again button - Global toast system with API error interceptor (401/403/500) - ToastContainer with success/error/warning/info variants - Print CSS for meeting prep - Audit Log added to sidebar nav for admins - All 80 frontend tests pass, clean build
This commit is contained in:
51
src/App.tsx
51
src/App.tsx
@@ -3,6 +3,9 @@ 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'));
|
||||
@@ -19,6 +22,7 @@ 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'));
|
||||
|
||||
function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
||||
const { isAuthenticated, isLoading } = useAuthStore();
|
||||
@@ -27,6 +31,21 @@ function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
||||
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();
|
||||
|
||||
@@ -39,30 +58,32 @@ export default function App() {
|
||||
<Suspense fallback={<PageLoader />}>
|
||||
<Routes>
|
||||
<Route path="/login" element={
|
||||
isAuthenticated ? <Navigate to="/" replace /> : <LoginPage />
|
||||
isAuthenticated ? <Navigate to="/" replace /> : <PageErrorBoundary><LoginPage /></PageErrorBoundary>
|
||||
} />
|
||||
<Route path="/invite/:token" element={<InvitePage />} />
|
||||
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
|
||||
<Route path="/reset-password/:token" element={<ResetPasswordPage />} />
|
||||
<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={<DashboardPage />} />
|
||||
<Route path="clients" element={<ClientsPage />} />
|
||||
<Route path="clients/:id" element={<ClientDetailPage />} />
|
||||
<Route path="events" element={<EventsPage />} />
|
||||
<Route path="emails" element={<EmailsPage />} />
|
||||
<Route path="network" element={<NetworkPage />} />
|
||||
<Route path="reports" element={<ReportsPage />} />
|
||||
<Route path="templates" element={<TemplatesPage />} />
|
||||
<Route path="segments" element={<SegmentsPage />} />
|
||||
<Route path="settings" element={<SettingsPage />} />
|
||||
<Route path="admin" element={<AdminPage />} />
|
||||
<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="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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user