UMBRA/frontend/src/App.tsx
Kyle Pope 3d22568b9c Add user connections, notification centre, and people integration
Implements the full User Connections & Notification Centre feature:

Phase 1 - Database: migrations 039-043 adding umbral_name to users,
profile/social fields to settings, notifications table, connection
request/user_connection tables, and linked_user_id to people.

Phase 2 - Notifications: backend CRUD router + service + 90-day purge,
frontend NotificationsPage with All/Unread filter, bell icon in sidebar
with unread badge polling every 60s.

Phase 3 - Settings: profile fields (phone, mobile, address, company,
job_title), social card with accept_connections toggle and per-field
sharing defaults, umbral name display with CopyableField.

Phase 4 - Connections: timing-safe user search, send/accept/reject flow
with atomic status updates, bidirectional UserConnection + Person records,
in-app + ntfy notifications, per-receiver pending cap, nginx rate limiting.

Phase 5 - People integration: batch-loaded shared profiles (N+1 prevention),
Ghost icon for umbral contacts, Umbral filter pill, split Add Person button,
shared field indicators (synced labels + Lock icons), disabled form inputs
for synced fields on umbral contacts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 02:10:16 +08:00

94 lines
3.1 KiB
TypeScript

import { lazy, Suspense } from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';
import { useAuth } from '@/hooks/useAuth';
import LockScreen from '@/components/auth/LockScreen';
import AppLayout from '@/components/layout/AppLayout';
import DashboardPage from '@/components/dashboard/DashboardPage';
import TodosPage from '@/components/todos/TodosPage';
import CalendarPage from '@/components/calendar/CalendarPage';
import RemindersPage from '@/components/reminders/RemindersPage';
import ProjectsPage from '@/components/projects/ProjectsPage';
import ProjectDetail from '@/components/projects/ProjectDetail';
import PeoplePage from '@/components/people/PeoplePage';
import LocationsPage from '@/components/locations/LocationsPage';
import SettingsPage from '@/components/settings/SettingsPage';
import NotificationsPage from '@/components/notifications/NotificationsPage';
const AdminPortal = lazy(() => import('@/components/admin/AdminPortal'));
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const { authStatus, isLoading } = useAuth();
if (isLoading) {
return (
<div className="flex h-screen items-center justify-center">
<div className="text-muted-foreground">Loading...</div>
</div>
);
}
if (!authStatus?.authenticated) {
return <Navigate to="/login" replace />;
}
return <>{children}</>;
}
function AdminRoute({ children }: { children: React.ReactNode }) {
const { authStatus, isLoading } = useAuth();
if (isLoading) {
return (
<div className="flex h-screen items-center justify-center">
<div className="text-muted-foreground">Loading...</div>
</div>
);
}
if (!authStatus?.authenticated || authStatus?.role !== 'admin') {
return <Navigate to="/dashboard" replace />;
}
return <>{children}</>;
}
function App() {
return (
<Routes>
<Route path="/login" element={<LockScreen />} />
<Route
path="/"
element={
<ProtectedRoute>
<AppLayout />
</ProtectedRoute>
}
>
<Route index element={<Navigate to="/dashboard" replace />} />
<Route path="dashboard" element={<DashboardPage />} />
<Route path="todos" element={<TodosPage />} />
<Route path="calendar" element={<CalendarPage />} />
<Route path="reminders" element={<RemindersPage />} />
<Route path="projects" element={<ProjectsPage />} />
<Route path="projects/:id" element={<ProjectDetail />} />
<Route path="people" element={<PeoplePage />} />
<Route path="locations" element={<LocationsPage />} />
<Route path="notifications" element={<NotificationsPage />} />
<Route path="settings" element={<SettingsPage />} />
<Route
path="admin/*"
element={
<AdminRoute>
<Suspense fallback={<div className="flex h-full items-center justify-center text-muted-foreground">Loading...</div>}>
<AdminPortal />
</Suspense>
</AdminRoute>
}
/>
</Route>
</Routes>
);
}
export default App;