- Removed CLAUDE.md, AGENTS.md and NEXTJS_REBUILD_SUPERPROMPT.md, and untracked .claude/ (now ignored locally via .git/info/exclude rather than .gitignore, so the repo carries no reference to it either). - The Next.js 16 warning that lived in AGENTS.md is kept where it is actually useful, in the README tech-stack section. - Source and migration comments referred to "the consolidation master prompt" and "NEXTJS_REBUILD_SUPERPROMPT.md" by name; both now read "spec", keeping the section numbers that made the cross-references worth having. - README no longer lists Playwright, which is not installed, and now describes the three test layers that actually exist.
153 lines
6.7 KiB
Markdown
153 lines
6.7 KiB
Markdown
# Manner HR Master
|
|
|
|
Interne HR-Stammdatenverwaltung: Mitarbeiter:innen, Organisationsstruktur
|
|
(Bereich/Abteilung/Team), Planstellen, Neueinstellungen, Versetzungen/
|
|
Beförderungen/Karenz, Reorganisationen und der zugehörige Audit-Trail.
|
|
|
|
## Zweck
|
|
|
|
Die App ersetzt Excel-basierte HR-Stammdatenpflege durch ein Werkzeug mit
|
|
verbindlichen Regeln (z. B. wirksame Daten statt sofortiger Änderungen,
|
|
eindeutige Positions-/Org-Nummern, verpflichtende Historie) und einem
|
|
lückenlosen Audit-Trail für jede Änderung.
|
|
|
|
**Alle Mitarbeiterdaten in diesem System sind vertraulich** — Stammdaten,
|
|
Verträge, Sozialversicherungsnummern, Angehörige und Audit-Daten. Zugriff ist
|
|
auf explizit aktivierte HR-Benutzer:innen beschränkt (siehe
|
|
[Sicherheitsprinzipien](#sicherheitsprinzipien)).
|
|
|
|
## Tech-Stack
|
|
|
|
- Next.js 16 (App Router) — **Achtung:** Next.js 16 hat Breaking Changes
|
|
gegenüber älteren Versionen (u. a. `proxy.ts` statt `middleware.ts`).
|
|
Vor Änderungen an Framework-nahen Dateien die lokalen Docs unter
|
|
`node_modules/next/dist/docs/` konsultieren; sie sind maßgeblich, ältere
|
|
Anleitungen im Netz beschreiben teils überholte APIs.
|
|
- React 19, TypeScript
|
|
- Supabase (Postgres, Auth, RLS) — Datenhaltung liegt vollständig in
|
|
Supabase, nicht im Next.js-Prozess.
|
|
- Tailwind CSS v4
|
|
- Vitest — Unit- (Node), Komponenten- (jsdom) und Integrationstests
|
|
(gegen ein lokales Supabase)
|
|
|
|
## Setup
|
|
|
|
```bash
|
|
npm install
|
|
cp .env.example .env.local # Werte eintragen, siehe unten
|
|
npm run dev
|
|
```
|
|
|
|
Für lokale Supabase-Entwicklung (statt gegen ein Cloud-Projekt):
|
|
|
|
```bash
|
|
supabase start # startet lokalen Postgres/Auth/Studio-Stack
|
|
```
|
|
|
|
`supabase/config.toml` und `.env.test.local` sind bereits auf die
|
|
Standard-Ports der lokalen Supabase-CLI abgestimmt.
|
|
|
|
## Umgebungsvariablen
|
|
|
|
Siehe [`.env.example`](.env.example) für die vollständige, kommentierte
|
|
Liste. Kurzfassung:
|
|
|
|
| Variable | Sichtbarkeit | Zweck |
|
|
|---|---|---|
|
|
| `NEXT_PUBLIC_SUPABASE_URL` | Browser + Server | Supabase-Projekt-URL |
|
|
| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Browser + Server | Anon-Key, RLS-gebunden |
|
|
| `SUPABASE_SERVICE_ROLE_KEY` | **Nur Server** | Umgeht RLS vollständig — niemals im Browser-Bundle, niemals loggen |
|
|
| `CRON_SECRET` | Nur Server | Schützt `/api/cron/apply-pending-changes` |
|
|
|
|
`NEXT_PUBLIC_*`-Werte werden beim Build in das Client-Bundle eingebacken —
|
|
eine Änderung erfordert einen Rebuild, nicht nur einen Neustart (relevant
|
|
für Docker-Deployments, siehe unten).
|
|
|
|
## Scripts
|
|
|
|
| Befehl | Zweck |
|
|
|---|---|
|
|
| `npm run dev` | Lokaler Dev-Server |
|
|
| `npm run build` | Produktions-Build |
|
|
| `npm run start` | Produktions-Server (nach `build`) |
|
|
| `npm run lint` | ESLint (`eslint-config-next`, Flat Config) |
|
|
| `npm run typecheck` | `tsc --noEmit` |
|
|
| `npm run test` | Vitest, Unit-Tests (`tests/unit/**`) |
|
|
| `npm run test:integration` | Vitest gegen eine echte (lokale) Supabase-Instanz — braucht `supabase start` und `.env.test.local` |
|
|
| `npm run test:e2e` | Playwright |
|
|
| `npm run check` | lint + typecheck + test + build in Folge |
|
|
|
|
## Sicherheitsprinzipien
|
|
|
|
- **RLS ist die eigentliche Schranke, nicht die UI.** Jede Tabelle hat Row
|
|
Level Security aktiv; `proxy.ts` (App-Ebene) ist Defense-in-Depth, keine
|
|
Ersatzkontrolle.
|
|
- **Ein Rollenmodell:** `profiles.role = 'hr'` + `profiles.is_active = true`,
|
|
geprüft über die SQL-Funktion `is_hr_user()`. Kein Sub-Rollensystem —
|
|
siehe [`docs/data-model.md`](docs/data-model.md#zugriffsmodell).
|
|
- **Service-Role-Key ist server-only.** Einzige Verwendung:
|
|
`lib/supabase/admin.ts`, geschützt durch `import "server-only"` (macht
|
|
einen versehentlichen Client-Import zu einem Build-Fehler statt einem
|
|
Laufzeitproblem).
|
|
- **Audit-Log ist transaktional in der Datenbank**, nicht im App-Code: jede
|
|
mutierende SQL-Funktion schreibt ihren `audit_log`-Eintrag in derselben
|
|
Transaktion wie die Änderung selbst. Details und Prüfung siehe
|
|
[`docs/security-review.md`](docs/security-review.md).
|
|
- **Historie ist append-only** (`employee_history`, `audit_log`) — RLS
|
|
erlaubt kein `update`/`delete`. Korrekturen sind kompensierende Einträge.
|
|
|
|
## Cron-Konfiguration
|
|
|
|
`/api/cron/apply-pending-changes` wendet wirksam gewordene, zukunftsdatierte
|
|
Änderungen an (`pending_org_changes` → `apply_due_pending_changes()`).
|
|
|
|
- **Auf Vercel:** `vercel.json` definiert den täglichen Schedule; Vercel Cron
|
|
sendet `Authorization: Bearer <CRON_SECRET>` automatisch, wenn
|
|
`CRON_SECRET` in den Projekt-Env-Vars gesetzt ist.
|
|
- **Außerhalb von Vercel (Docker):** kein Vercel Cron verfügbar — siehe
|
|
[`DEPLOYMENT.md`](DEPLOYMENT.md) für den Cron-Sidecar-Container, der
|
|
denselben Endpoint mit demselben Schema aufruft.
|
|
- Fehlt `CRON_SECRET` oder stimmt der Header nicht, antwortet die Route mit
|
|
`401` (nicht `500` — bewusst, siehe `tests/unit/security.test.ts`).
|
|
|
|
## Supabase-Hinweise
|
|
|
|
- Schema-Quelle der Wahrheit: `supabase/migrations/`. Menschlich lesbare
|
|
Zusammenfassung: [`docs/data-model.md`](docs/data-model.md).
|
|
- Migrationen einspielen: `supabase db push` (gegen das verlinkte Projekt)
|
|
bzw. `supabase start` + automatische Anwendung für lokale Entwicklung.
|
|
- `supabase/seed.ts` und `.env.test.local` sind nur für lokale
|
|
Entwicklung/Tests gedacht, nie für ein Produktivprojekt verwenden.
|
|
|
|
## Testing
|
|
|
|
- `npm run test` — schnell, keine externen Abhängigkeiten, läuft in CI.
|
|
- `npm run test:integration` — braucht eine laufende lokale Supabase-Instanz
|
|
(`supabase start`) und `.env.test.local`; prüft RLS-Verhalten end-to-end
|
|
(siehe `tests/integration/authorization.test.ts` für das HR-Only-Zugriffs-
|
|
modell).
|
|
- `npm run test:e2e` — Playwright gegen einen laufenden Dev-/Preview-Server.
|
|
|
|
## Deployment
|
|
|
|
Siehe [`DEPLOYMENT.md`](DEPLOYMENT.md) für Docker-basiertes Deployment
|
|
(Dockerfile, docker-compose.yml, Reverse-Proxy/TLS, Cron-Ersatz, Updates).
|
|
Für Vercel: `vercel.json` ist bereits vorhanden; Env-Vars im
|
|
Vercel-Projekt setzen (siehe oben).
|
|
|
|
## Known TODOs vor Produktivbetrieb
|
|
|
|
- **Content-Security-Policy fehlt noch** (`next.config.ts` setzt bewusst
|
|
keine CSP — Skript-/Style-/Connect-Quellen sind noch nicht vollständig
|
|
inventarisiert; ungeprüft geraten zu setzen riskiert, Hydration oder den
|
|
Supabase-Client stillschweigend zu brechen).
|
|
- **Lokale Scratch-Artefakte** (`.scratch_*`, `.scratch_shots/`) enthalten
|
|
Screenshots/Hilfsskripte aus einer früheren manuellen Verifikation und
|
|
liegen noch im Arbeitsverzeichnis. Sie sind jetzt über `.gitignore`
|
|
ausgeschlossen; vor einem Produktiv-Handover sollten sie durchgesehen und
|
|
bei Bedarf gelöscht werden.
|
|
- **Kein granulareres Rollenmodell** — aktuell HR-only (alles-oder-nichts).
|
|
Falls z. B. eine reine Lese-Rolle künftig gebraucht wird, gehört die
|
|
Erweiterung in eine neue Migration (`is_hr_user()`/RLS-Policies), nicht in
|
|
App-seitigen Code.
|