Kansho/frontend/src/App.jsx

161 lines
5.8 KiB
JavaScript

import { NavLink, Navigate, Outlet, Route, Routes, useLocation } from 'react-router-dom'
import { LogOut } from 'lucide-react'
import { useAuth } from './context/AuthContext.jsx'
import { getMainNavItems } from './config/appNav.js'
import LoginScreen from './pages/LoginScreen.jsx'
import SetupScreen from './pages/SetupScreen.jsx'
import HomePage from './pages/HomePage.jsx'
import DialoguePage from './pages/DialoguePage.jsx'
import JournalSpacesPage from './pages/JournalSpacesPage.jsx'
import JournalSpacePage from './pages/JournalSpacePage.jsx'
import JournalTrashPage from './pages/JournalTrashPage.jsx'
import JournalDayPage from './pages/JournalDayPage.jsx'
import JournalEditorPage from './pages/JournalEditorPage.jsx'
import JournalSourcePage from './pages/JournalSourcePage.jsx'
import SettingsPage from './pages/SettingsPage.jsx'
import AdminHomePage from './pages/AdminHomePage.jsx'
import AdminUsersPage from './pages/AdminUsersPage.jsx'
import AdminPromptsPage from './pages/AdminPromptsPage.jsx'
import AdminPlaceholdersPage from './pages/AdminPlaceholdersPage.jsx'
import AdminFeaturesPage from './pages/AdminFeaturesPage.jsx'
import AdminDialoguePage from './pages/AdminDialoguePage.jsx'
import AdminProvidersPage from './pages/AdminProvidersPage.jsx'
import AdminIdentitiesPage from './pages/AdminIdentitiesPage.jsx'
import RequireAdmin from './layouts/RequireAdmin.jsx'
import AdminShell from './layouts/AdminShell.jsx'
import { UnsavedProvider, useUnsavedChanges } from './context/UnsavedChanges.jsx'
function navItemActive(pathname, item, routerIsActive) {
if (item.to.startsWith('/admin')) return pathname.startsWith('/admin')
if (item.to === '/journal') return pathname.startsWith('/journal')
return routerIsActive
}
function BottomNav({ isAdmin }) {
const items = getMainNavItems(isAdmin)
const loc = useLocation()
return (
<nav className="bottom-nav">
{items.map((item) => {
const Icon = item.icon
return (
<NavLink
key={item.to}
to={item.to}
className={({ isActive }) =>
navItemActive(loc.pathname, item, isActive) ? 'nav-item active' : 'nav-item'
}
end={item.to === '/'}
>
<Icon size={20} />
<span>{item.label}</span>
</NavLink>
)
})}
</nav>
)
}
function DesktopSidebar({ isAdmin, onLogout, name }) {
const items = getMainNavItems(isAdmin)
const loc = useLocation()
return (
<aside className="desktop-sidebar">
<div className="brand">Kanshō</div>
<nav>
{items.map((item) => {
const Icon = item.icon
return (
<NavLink
key={item.to}
to={item.to}
className={({ isActive }) =>
navItemActive(loc.pathname, item, isActive) ? 'nav-item active' : 'nav-item'
}
end={item.to === '/'}
>
<Icon size={18} />
<span>{item.label}</span>
</NavLink>
)
})}
</nav>
<div className="sidebar-footer">
<span className="user-name">{name}</span>
<button type="button" className="ghost" onClick={onLogout}>
<LogOut size={16} /> Abmelden
</button>
</div>
</aside>
)
}
function AppShell() {
const { session, isAdmin, logout } = useAuth()
const { confirmLeave } = useUnsavedChanges()
const onLogout = () => {
if (!confirmLeave()) return
logout()
}
return (
<div className="app-shell">
<DesktopSidebar isAdmin={isAdmin} onLogout={onLogout} name={session?.profile?.name} />
<div className="app-main">
<header className="mobile-header">
<strong>Kanshō</strong>
<button type="button" className="ghost" onClick={onLogout}>Abmelden</button>
</header>
<main className="page">
<Outlet />
</main>
<BottomNav isAdmin={isAdmin} />
</div>
</div>
)
}
export default function App() {
const { loading, needsSetup, session } = useAuth()
if (loading) {
return <div className="centered">Lädt </div>
}
if (needsSetup) {
return <SetupScreen />
}
if (!session) {
return <LoginScreen />
}
return (
<UnsavedProvider>
<Routes>
<Route element={<AppShell />}>
<Route path="/" element={<HomePage />} />
<Route path="/journal" element={<JournalSpacesPage />} />
<Route path="/journal/:spaceId" element={<JournalSpacePage />} />
<Route path="/journal/:spaceId/trash" element={<JournalTrashPage />} />
<Route path="/journal/:spaceId/:dayId" element={<JournalDayPage />} />
<Route path="/journal/:spaceId/:dayId/entry" element={<JournalEditorPage />} />
<Route path="/journal/:spaceId/:dayId/source/:conversationId" element={<JournalSourcePage />} />
<Route path="/settings" element={<SettingsPage />} />
<Route element={<RequireAdmin />}>
<Route path="/dialog" element={<DialoguePage />} />
<Route path="/admin" element={<AdminShell />}>
<Route index element={<AdminHomePage />} />
<Route path="users" element={<AdminUsersPage />} />
<Route path="prompts" element={<AdminPromptsPage />} />
<Route path="placeholders" element={<AdminPlaceholdersPage />} />
<Route path="features" element={<AdminFeaturesPage />} />
<Route path="providers" element={<AdminProvidersPage />} />
<Route path="identities" element={<AdminIdentitiesPage />} />
<Route path="dialogue" element={<AdminDialoguePage />} />
</Route>
</Route>
</Route>
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</UnsavedProvider>
)
}