Der Forums-Abgleich las von 25 Archivseiten nur zwei. WoltLab verlinkt in der Blätterleiste nur die Nachbarseiten und führt die Gesamtzahl in einem eigenen Element; ausgewertet wurden aber die Links. Alles jenseits von Seite zwei war damit unsichtbar - etwa ein Antrag auf Seite vier, der deshalb nie ankam. Die Rollen verlaufen jetzt zwischen erfassen und gewähren statt zwischen Alltag und Verwaltung: Editoren erfassen Käufe und führen Datei-Import, GExtension- und Forums-Abgleich samt Entscheidung über Anträge durch. Kulanz zu vergeben bleibt Administratoren, ebenso das Auflösen unklarer Pakete, weil dabei Kulanz entstehen kann. Dazu lassen sich frühere Steam- und Forumskonten am Spieler hinterlegen. Abgleich und Import prüfen sie mit, sodass ein Kontowechsel die Zuordnung nicht abreißen lässt. Steam-Kennungen werden dabei auf dieselbe Form gebracht wie die Hauptangabe, und eine Kennung kann nicht zwei Spielern gehören.
110 lines
2.5 KiB
TypeScript
110 lines
2.5 KiB
TypeScript
import { Navigate, Route, Routes } from 'react-router-dom';
|
|
import Layout from './components/Layout';
|
|
import { useSession } from './lib/session';
|
|
import type { Role } from './lib/types';
|
|
import Account from './pages/Account';
|
|
import Admin from './pages/Admin';
|
|
import Dashboard from './pages/Dashboard';
|
|
import Forum from './pages/Forum';
|
|
import Import from './pages/Import';
|
|
import Login from './pages/Login';
|
|
import PlayerDetail from './pages/PlayerDetail';
|
|
|
|
function Protected({ children, minimum }: { children: React.ReactNode; minimum?: Role }) {
|
|
const { user, loading, can } = useSession();
|
|
|
|
if (loading) return <div className="boot">Lade …</div>;
|
|
if (!user) return <Navigate to="/login" replace />;
|
|
|
|
// Ein frisch angelegtes Konto muss zuerst sein Passwort wechseln.
|
|
if (user.mustChangePassword) return <Navigate to="/account" replace />;
|
|
if (minimum && !can(minimum)) return <Navigate to="/" replace />;
|
|
|
|
return <>{children}</>;
|
|
}
|
|
|
|
export default function App() {
|
|
const { user, loading } = useSession();
|
|
|
|
return (
|
|
<Routes>
|
|
<Route
|
|
path="/login"
|
|
element={loading ? <div className="boot">Lade …</div> : user ? <Navigate to="/" replace /> : <Login />}
|
|
/>
|
|
|
|
<Route
|
|
path="/account"
|
|
element={
|
|
loading ? (
|
|
<div className="boot">Lade …</div>
|
|
) : !user ? (
|
|
<Navigate to="/login" replace />
|
|
) : (
|
|
<Layout>
|
|
<Account />
|
|
</Layout>
|
|
)
|
|
}
|
|
/>
|
|
|
|
<Route
|
|
path="/"
|
|
element={
|
|
<Protected>
|
|
<Layout>
|
|
<Dashboard />
|
|
</Layout>
|
|
</Protected>
|
|
}
|
|
/>
|
|
|
|
<Route
|
|
path="/players/:id"
|
|
element={
|
|
<Protected>
|
|
<Layout>
|
|
<PlayerDetail />
|
|
</Layout>
|
|
</Protected>
|
|
}
|
|
/>
|
|
|
|
<Route
|
|
path="/forum"
|
|
element={
|
|
<Protected>
|
|
<Layout>
|
|
<Forum />
|
|
</Layout>
|
|
</Protected>
|
|
}
|
|
/>
|
|
|
|
<Route
|
|
path="/import"
|
|
element={
|
|
<Protected minimum="EDITOR">
|
|
<Layout>
|
|
<Import />
|
|
</Layout>
|
|
</Protected>
|
|
}
|
|
/>
|
|
|
|
<Route
|
|
path="/admin"
|
|
element={
|
|
<Protected minimum="ADMIN">
|
|
<Layout>
|
|
<Admin />
|
|
</Layout>
|
|
</Protected>
|
|
}
|
|
/>
|
|
|
|
<Route path="*" element={<Navigate to="/" replace />} />
|
|
</Routes>
|
|
);
|
|
}
|