- .gitignore: .claude/docs, rules, commands tracken; settings.local weiter ignorieren - DOCUMENTATION.md: verbindliche Ablage functional/technical/working/issues - .claude/README.md: Agent-Einstieg; GITEA_ISSUES_INDEX aus MCP (Stand 2026-04-08) - Arbeitspapiere von docs/ nach .claude/docs/working/ verschoben - docs/MEMBERSHIP_SYSTEM.md als Stub; kanonisch technical/MEMBERSHIP_SYSTEM.md - CLAUDE.md Pflichtlektüre und Links angepasst; docs/README.md vereinfacht Made-with: Cursor
3.9 KiB
3.9 KiB
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
// ✅ 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:
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) – inApp.jsx(Bottom-Nav) undDesktopSidebar.jsxnutzen. - 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-maininapp.css. - Agent-Doku:
docs/issues/GUI_IA_ADMIN_NAV_2026-04-05.md
Neue Seite hinzufügen
frontend/src/pages/MeineSeite.jsxerstellen- In
App.jsximportieren und Route hinzufügen - Navigation: Eintrag in
config/appNav.js(und ggf. Admin inadminNav.js) – nicht mehr nur inApp.jsxduplizieren
Komponenten-Pattern
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 <div style={{display:'flex',justifyContent:'center',padding:40}}><div className="spinner"/></div>
if (error) return <div style={{color:'var(--danger)',padding:16}}>{error}</div>
return (
<div style={{padding:'0 0 80px'}}>
{/* Inhalt */}
</div>
)
}
CSS-Variablen (Kurzreferenz)
--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
// ❌ 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
// ❌ Falsch:
<Bar fill={(entry) => entry.color}/>
// ✅ Richtig:
<Bar fill="#1D9E75"/>