# Frontend-Architektur ## Struktur ``` frontend/src/ ├── App.jsx # Root: Auth-Gates, Navigation, Routing ├── app.css # CSS-Variablen + globale Klassen ├── main.jsx # Vite Entry Point ├── context/ │ ├── AuthContext.jsx # Session, Login, Logout, getToken() │ └── ProfileContext.jsx # Aktives Profil, Profile-Liste ├── pages/ # Eine Datei pro Screen ├── utils/ │ ├── api.js # Alle API-Calls (Token automatisch injiziert) │ ├── calc.js # Körperfett-Formeln (Jackson/Pollock etc.) │ ├── interpret.js # Regelbasierte Auswertungen │ ├── Markdown.jsx # Eigener Markdown-Renderer │ └── guideData.js # Messanleitungen (statisch) └── public/ # Icons (Jinkendo Ensō-Logo) ``` ## API-Calls – IMMER über api.js ```javascript // ✅ Richtig – Token wird automatisch injiziert: import { api } from '../utils/api' const data = await api.listWeight() await api.upsertWeight(date, weight, note) // ❌ Falsch – kein Token, gibt 401: const r = await fetch('/api/weight') ``` ## Neue API-Methode hinzufügen In `frontend/src/utils/api.js`: ```javascript export const api = { // ...bestehende Methoden... meinEndpoint: (param) => req(`/mein-endpoint?p=${param}`), createItem: (data) => req('/items', json(data)), updateItem: (id, d) => req(`/items/${id}`, jput(d)), deleteItem: (id) => req(`/items/${id}`, {method:'DELETE'}), } ``` ## Navigation (Haupt-App & Admin) - **Hauptmenü (Mobile + Desktop):** `frontend/src/config/appNav.js` (`getMainNavItems`) – in `App.jsx` (Bottom-Nav) und `DesktopSidebar.jsx` nutzen. - **Admin-Bereich:** `frontend/src/config/adminNav.js` + `layouts/AdminShell.jsx` + `layouts/RequireAdmin.jsx`; Shell wie Analyse (`.analysis-split*`). - **Bottom-Nav / Safe Area (PWA):** `--nav-h`, `.bottom-nav`, `.app-main` in `app.css`. - **Agent-Doku:** `docs/issues/GUI_IA_ADMIN_NAV_2026-04-05.md` ## Neue Seite hinzufügen 1. `frontend/src/pages/MeineSeite.jsx` erstellen 2. In `App.jsx` importieren und Route hinzufügen 3. Navigation: Eintrag in **`config/appNav.js`** (und ggf. Admin in **`adminNav.js`**) – nicht mehr nur in `App.jsx` duplizieren ## Komponenten-Pattern ```jsx export default function MeineSeite() { const [data, setData] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) useEffect(() => { load() }, []) const load = async () => { try { setLoading(true) setData(await api.meinEndpoint()) } catch(e) { setError(e.message) } finally { setLoading(false) } } if (loading) return
if (error) return
{error}
return (
{/* Inhalt */}
) } ``` ## CSS-Variablen (Kurzreferenz) ```css --accent: #1D9E75 --accent-dark: #085041 --accent-light: #E1F5EE --danger: #D85A30 --bg · --surface · --surface2 · --border · --text1 · --text2 · --text3 ``` ## CSS-Klassen ``` .card Weißer Container, border-radius 12px .btn Basis-Button .btn-primary Grüner Button .btn-secondary Grauer Button .btn-full 100% Breite .form-input Eingabefeld .form-label Label (klein, uppercase) .spinner Ladekreis ``` ## Bekannte Fallstricke ### dayjs.week() – NIEMALS verwenden ```javascript // ❌ Falsch: dayjs(date).week() // ✅ Richtig (ISO 8601): const weekNum = (() => { const dt = new Date(date) dt.setHours(0,0,0,0) dt.setDate(dt.getDate()+4-(dt.getDay()||7)) return Math.ceil(((dt-new Date(dt.getFullYear(),0,1))/86400000+1)/7) })() ``` ### Recharts Bar fill ```jsx // ❌ Falsch: entry.color}/> // ✅ Richtig: ```