docs: README, Schnellstart, Handbuch, Fahrplan und CHANGELOG
README mit Funktionsumfang, Bildergalerie, allen Umgebungsvariablen als Tabelle und dem Betrieb inklusive Sicherung und Wiederherstellung. Der Schnellstart führt in acht Schritten von git clone zur ersten Anmeldung, jeder Schritt mit erwartetem Ergebnis. Er endet mit dem Hinterlegen eines Passkeys statt beim ersten Kalenderkonto oder der ersten Aufgabe — beides gibt es erst mit M3 und M2. Das ist in ROADMAP und Handbuch so vermerkt, damit niemand danach sucht. Handbuch mit Seiten zu Anmelden, Bereichen und Protokoll. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f91d015cc4
commit
56b5356708
@@ -39,3 +39,4 @@ Thumbs.db
|
|||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
.idea/
|
.idea/
|
||||||
|
screenshot-lauf.log
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# Änderungen
|
||||||
|
|
||||||
|
Alle nennenswerten Änderungen an diesem Projekt stehen hier.
|
||||||
|
Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/),
|
||||||
|
die Versionierung [Semantic Versioning](https://semver.org/lang/de/).
|
||||||
|
|
||||||
|
## [Unveröffentlicht]
|
||||||
|
|
||||||
|
### Hinzugefügt
|
||||||
|
|
||||||
|
- Projektgerüst mit Next 16 (App Router, `output: "standalone"`), React 19,
|
||||||
|
TypeScript strict, Tailwind 4 und shadcn-Primitiven.
|
||||||
|
- `npm run pruefen` bündelt Prettier, ESLint, `tsc --noEmit`, Vitest und
|
||||||
|
ShellCheck. ShellCheck läuft über das Docker-Abbild, wenn es lokal fehlt.
|
||||||
|
- Datenmodell für Benutzer mit Rolle, OIDC-Konten, Passkeys, Bereiche und
|
||||||
|
Audit-Einträge; PostgreSQL 16 über Prisma 7 mit pg-Treiberadapter.
|
||||||
|
- Anmeldung über einen generischen OIDC-Anbieter, über Passkeys
|
||||||
|
(SimpleWebAuthn) und wahlweise über E-Mail und Passwort.
|
||||||
|
- Rollen `BENUTZER` und `ADMIN`. Jede schreibende Aktion prüft die Rolle
|
||||||
|
serverseitig in der Seite **und** noch einmal in der Server Action.
|
||||||
|
- Audit-Protokoll für jede schreibende Aktion, mit Protokollseite für die
|
||||||
|
Verwaltung. Details werden vor dem Speichern gesäubert.
|
||||||
|
- Instanzsteuerung über `INSTANZ=firma|daheim`. Was eine Instanz nicht darf,
|
||||||
|
liefert dort 404 und lädt seine Konfiguration gar nicht erst.
|
||||||
|
- Bereiche mit Farbe, Arbeitsfenster und Tageskapazität je Wochentag.
|
||||||
|
- Zeitrechnung durchgehend in Europe/Berlin, mit Tagesgrenzen, Wochenbeginn
|
||||||
|
am Montag und deutschen Anzeigeformaten.
|
||||||
|
- `/api/health` mit Status, Version, Instanz und Erreichbarkeit der Datenbank.
|
||||||
|
- Hell und Dunkel, Navigation mit Rollenfilter, Oberfläche auf Deutsch.
|
||||||
|
- Dockerfile in drei Stufen und Compose-Stapel mit eigenem Migrationsdienst.
|
||||||
|
- `backup.sh` (pg_dump im Custom-Format, `.env` und Uploads),
|
||||||
|
`update.sh` (mit Rückrollpfad), `scripts/release.sh` und `scripts/zeit.sh`.
|
||||||
|
- Tests: Unit mit Vitest, Integrationstest und Migrationstest gegen eine
|
||||||
|
echte Datenbank, E2E-Rauchtest mit Playwright.
|
||||||
|
- `npm run screenshots` nimmt die Handbuchbilder in einer Wegwerf-Instanz mit
|
||||||
|
ausschließlich erfundenen Daten auf.
|
||||||
|
- README, Schnellstart, Handbuch, ROADMAP und DEVLOG.
|
||||||
|
|
||||||
|
### Sicherheit
|
||||||
|
|
||||||
|
- Anmeldung, Passwort und Passkey antworten bei Fehlschlag immer gleich,
|
||||||
|
damit sich nicht abfragen lässt, wer ein Konto hat.
|
||||||
|
- Sitzungen als JWT, aber Rolle und Sperrstatus werden bei jeder Anfrage
|
||||||
|
frisch aus der Datenbank gelesen — eine Sperre wirkt sofort.
|
||||||
|
- Die WebAuthn-Herausforderung wird nach einem Versuch verworfen, gleich ob
|
||||||
|
er geglückt ist.
|
||||||
|
- Der Demo-Seed verweigert den Dienst ohne `DEMO_MODUS=true`.
|
||||||
|
- `allowDangerousEmailAccountLinking` ist bewusst aktiv: je Instanz gibt es
|
||||||
|
genau einen vertrauenswürdigen OIDC-Anbieter, der die Adresse selbst prüft.
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
# Planer
|
||||||
|
|
||||||
|
Aufgaben, Ideen, Projekte und Zeitblöcke an einem Ort.
|
||||||
|
|
||||||
|
Die Kernidee: Kalender aus Microsoft 365 (Firma, Gewerbe) und Google (privat)
|
||||||
|
lesen und Aufgaben per Ziehen und Ablegen als Zeitblöcke direkt in diese
|
||||||
|
Kalender schreiben — inklusive Rückweg, wenn ein Block im Kalender verschoben
|
||||||
|
wird.
|
||||||
|
|
||||||
|
> **Stand:** Meilenstein 1 von 8. Die Grundlage steht: Anmeldung, Rollen,
|
||||||
|
> Protokoll, Bereiche und der Betrieb. Aufgaben kommen mit M2, das Lesen der
|
||||||
|
> Kalender mit M3, die Wochenplanung mit M4. Der [Fahrplan](ROADMAP.md) sagt,
|
||||||
|
> was wann folgt.
|
||||||
|
|
||||||
|
- **[Schnellstart](docs/SCHNELLSTART.md)** – in zehn Minuten von `git clone`
|
||||||
|
zur ersten Anmeldung
|
||||||
|
- **[Handbuch](docs/handbuch/)** – Anleitung für den Alltag
|
||||||
|
- **[Fahrplan](ROADMAP.md)** – was geplant ist und was bewusst nicht
|
||||||
|
- **[Änderungen](CHANGELOG.md)** – was sich je Version getan hat
|
||||||
|
|
||||||
|
## Funktionsumfang
|
||||||
|
|
||||||
|
### Heute
|
||||||
|
|
||||||
|
- Anmeldung über einen OIDC-Anbieter, über Passkeys oder mit E-Mail und
|
||||||
|
Passwort — je Instanz einzeln zuschaltbar
|
||||||
|
- Rollen `Benutzer` und `Verwaltung`; jede schreibende Aktion wird
|
||||||
|
serverseitig geprüft und hinterlässt einen Protokolleintrag
|
||||||
|
- Bereiche mit Farbe, Arbeitsfenster und Tageskapazität je Wochentag
|
||||||
|
- Hell und Dunkel, Oberfläche durchgehend auf Deutsch
|
||||||
|
- Betrieb über Docker Compose, mit Sicherung, Aktualisierung und Healthcheck
|
||||||
|
|
||||||
|
### Geplant
|
||||||
|
|
||||||
|
Aufgaben und Projekte, Kalender lesen und schreiben, Wochenplanung per Ziehen
|
||||||
|
und Ablegen, Benachrichtigungen, Integrationen, Schnellerfassung vom Handy und
|
||||||
|
ein MCP-Server. Siehe [ROADMAP.md](ROADMAP.md).
|
||||||
|
|
||||||
|
## Bildergalerie
|
||||||
|
|
||||||
|
| | Hell | Dunkel |
|
||||||
|
| ------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------- |
|
||||||
|
| Anmelden |  |  |
|
||||||
|
| Start |  |  |
|
||||||
|
| Bereiche |  |  |
|
||||||
|
| Einstellungen |  |  |
|
||||||
|
| Protokoll |  |  |
|
||||||
|
|
||||||
|
Alle Bilder entstehen mit `npm run screenshots` in einer Wegwerf-Instanz mit
|
||||||
|
ausschließlich erfundenen Daten — niemals aus einer Instanz mit echten Daten.
|
||||||
|
|
||||||
|
## Die zwei Instanzen
|
||||||
|
|
||||||
|
Ein Abbild, zwei Deployments, gesteuert über `INSTANZ`. Die Umschaltung wird
|
||||||
|
**serverseitig erzwungen**: Was eine Instanz nicht darf, liefert dort 404 und
|
||||||
|
lädt seine Konfiguration gar nicht erst.
|
||||||
|
|
||||||
|
| | `firma` | `daheim` |
|
||||||
|
| -------------- | -------------------------------------- | ------------------------------ |
|
||||||
|
| Anmeldung | OIDC Entra ID | OIDC authentik |
|
||||||
|
| Kalenderkonten | Graph (Firmen-Tenant) | Graph (Gewerbe-Tenant), Google |
|
||||||
|
| Mail | Graph Firmenpostfach | Graph Gewerbepostfach, Gmail |
|
||||||
|
| Integrationen | Zammad, Wartungsplaner, Spiegel senden | Telegram, Spiegel empfangen |
|
||||||
|
|
||||||
|
Ein falsch geschriebener Wert bricht den Start ab, statt still auf einen
|
||||||
|
Standard zu fallen — in der Firma dürfen keine Provider anlaufen, die dort
|
||||||
|
nichts zu suchen haben.
|
||||||
|
|
||||||
|
## Umgebungsvariablen
|
||||||
|
|
||||||
|
Alle Werte stehen in `.env`. Vorlage: [`.env.example`](.env.example).
|
||||||
|
`.env` gehört niemals ins Git.
|
||||||
|
|
||||||
|
### Pflicht
|
||||||
|
|
||||||
|
| Variable | Beispiel | Bedeutung |
|
||||||
|
| ----------------- | ---------------------------------------------------- | ---------------------------------------------- |
|
||||||
|
| `INSTANZ` | `daheim` | `firma` oder `daheim` |
|
||||||
|
| `APP_URL` | `https://planer.example.de` | Öffentliche Adresse, ohne Schrägstrich am Ende |
|
||||||
|
| `DATABASE_URL` | `postgresql://planer:…@db:5432/planer?schema=public` | Verbindung zur Datenbank |
|
||||||
|
| `AUTH_SECRET` | _(32 Byte Base64)_ | Signiert Sitzungen. `openssl rand -base64 32` |
|
||||||
|
| `WEBAUTHN_RP_ID` | `planer.example.de` | Nackte Domain, ohne Schema und Port |
|
||||||
|
| `WEBAUTHN_ORIGIN` | `https://planer.example.de` | Exakt die Herkunft im Browser |
|
||||||
|
|
||||||
|
### Datenbank im Compose-Stapel
|
||||||
|
|
||||||
|
| Variable | Standard | Bedeutung |
|
||||||
|
| ------------------- | ---------- | ---------------------------------------------- |
|
||||||
|
| `POSTGRES_USER` | `planer` | Benutzer der Datenbank |
|
||||||
|
| `POSTGRES_PASSWORD` | _(keiner)_ | Pflicht. Muss zu `DATABASE_URL` passen |
|
||||||
|
| `POSTGRES_DB` | `planer` | Name der Datenbank |
|
||||||
|
| `POSTGRES_PORT` | `5432` | Nach außen veröffentlichter Port |
|
||||||
|
| `APP_PORT` | `3000` | Nach außen veröffentlichter Port der Anwendung |
|
||||||
|
|
||||||
|
### Anmeldung
|
||||||
|
|
||||||
|
| Variable | Standard | Bedeutung |
|
||||||
|
| -------------------- | -------- | ------------------------------------------ |
|
||||||
|
| `AUTH_TRUST_HOST` | `false` | Hinter einem Reverse Proxy auf `true` |
|
||||||
|
| `ANMELDUNG_PASSWORT` | `false` | Anmeldung mit E-Mail und Passwort erlauben |
|
||||||
|
| `OIDC_NAME` | _(leer)_ | Beschriftung des Anmeldeknopfs |
|
||||||
|
| `OIDC_ISSUER` | _(leer)_ | Issuer-URL des Anbieters |
|
||||||
|
| `OIDC_CLIENT_ID` | _(leer)_ | Client-ID |
|
||||||
|
| `OIDC_CLIENT_SECRET` | _(leer)_ | Client-Secret |
|
||||||
|
| `WEBAUTHN_RP_NAME` | `Planer` | Name, den das Gerät beim Passkey anzeigt |
|
||||||
|
|
||||||
|
OIDC ist aktiv, sobald Issuer, Client-ID und Secret gesetzt sind. Der
|
||||||
|
Rückleit-URI beim Anbieter lautet `<APP_URL>/api/auth/callback/oidc`.
|
||||||
|
|
||||||
|
### Einrichtung und Sonstiges
|
||||||
|
|
||||||
|
| Variable | Standard | Bedeutung |
|
||||||
|
| ---------------- | --------------- | ---------------------------------------------------------------------------------------- |
|
||||||
|
| `ADMIN_EMAIL` | _(leer)_ | Erstes Verwaltungskonto, wird vom Seed angelegt |
|
||||||
|
| `ADMIN_PASSWORT` | _(leer)_ | Mindestens 12 Zeichen. Ohne den Wert entsteht ein Konto, das nur OIDC oder Passkey kennt |
|
||||||
|
| `TZ` | `Europe/Berlin` | Zeitzone für Anzeigen und Tagesgrenzen |
|
||||||
|
| `DEMO_MODUS` | `false` | Nur für die Screenshot-Instanz. Im Betrieb niemals `true` |
|
||||||
|
|
||||||
|
## Betrieb
|
||||||
|
|
||||||
|
### Starten und stoppen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d # starten
|
||||||
|
docker compose logs -f app # Protokoll mitlesen
|
||||||
|
docker compose down # stoppen, Daten bleiben
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Stapel besteht aus drei Diensten: `db`, ein Einmaldienst `migration`, der
|
||||||
|
die Migrationen einspielt und sich beendet, und `app`.
|
||||||
|
|
||||||
|
### Healthcheck
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://planer.example.de/api/health
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"instanz": "daheim",
|
||||||
|
"datenbank": "ok",
|
||||||
|
"zeit": "2026-03-17T08:20:00.000Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Die Auskunft ist bewusst ohne Anmeldung erreichbar und enthält deshalb keine
|
||||||
|
Zählstände, Namen oder Adressen. Bei `"status": "fehler"` antwortet der
|
||||||
|
Endpunkt mit HTTP 503.
|
||||||
|
|
||||||
|
### Aktualisieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./update.sh # auf den neuesten Tag
|
||||||
|
./update.sh --nur-pruefen # nur zeigen, was passieren würde
|
||||||
|
./update.sh --version v0.2.0 # auf eine bestimmte Version
|
||||||
|
./update.sh --ja # ohne Rückfrage
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Skript prüft die Voraussetzungen, zeigt die Änderungen aus dem CHANGELOG,
|
||||||
|
legt eine Sicherung an, wechselt auf den Ziel-Tag, baut, startet und wartet
|
||||||
|
auf einen gesunden `/api/health` mit der erwarteten Version. Klappt das nicht,
|
||||||
|
rollt es auf die vorherige Version zurück und nennt den Pfad der Sicherung.
|
||||||
|
Jeder Lauf wird nach `logs/update-<Datum>.log` protokolliert.
|
||||||
|
|
||||||
|
Die **Datenbank wird beim Zurückrollen nicht automatisch zurückgespielt** —
|
||||||
|
das wäre bei einem Teilfehler gefährlicher als hilfreich. Das Skript nennt den
|
||||||
|
Befehl dafür.
|
||||||
|
|
||||||
|
### Sicherung und Wiederherstellung
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./backup.sh # Datenbank, .env und Uploads
|
||||||
|
./backup.sh --tage 90 # längere Aufbewahrung
|
||||||
|
./backup.sh --nur-datenbank # nur der Dump
|
||||||
|
```
|
||||||
|
|
||||||
|
Je Lauf entstehen zwei Dateien in `sicherungen/`:
|
||||||
|
ein `pg_dump` im Custom-Format und ein tar.gz mit `.env` und `uploads`.
|
||||||
|
|
||||||
|
Wiederherstellen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Datenbank
|
||||||
|
docker compose exec -T db pg_restore -U planer -d planer --clean --if-exists \
|
||||||
|
< sicherungen/planer-db-2026-03-17_02-00-00.dump
|
||||||
|
|
||||||
|
# Dateien
|
||||||
|
tar -xzf sicherungen/planer-dateien-2026-03-17_02-00-00.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
Die Sicherung enthält `.env` und damit alle Geheimnisse. Sie gehört so
|
||||||
|
geschützt wie die Datenbank selbst.
|
||||||
|
|
||||||
|
Nächtlich per cron:
|
||||||
|
|
||||||
|
```cron
|
||||||
|
0 2 * * * cd /opt/planer && ./backup.sh --tage 30 >> logs/backup.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Entwicklung
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
docker compose up -d db # nur die Datenbank
|
||||||
|
npm run db:migrieren # Migrationen einspielen
|
||||||
|
npm run db:seed # Bereiche und Verwaltungskonto
|
||||||
|
npm run dev # Entwicklungsserver
|
||||||
|
```
|
||||||
|
|
||||||
|
### Prüfungen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run pruefen # Prettier, ESLint, tsc, Vitest, ShellCheck
|
||||||
|
npm run test:integration # gegen eine echte Datenbank
|
||||||
|
npm run test:migration # Migrationen gegen eine Wegwerf-Datenbank
|
||||||
|
npm run test:e2e # Playwright gegen die gebaute Anwendung
|
||||||
|
npm run screenshots # Handbuchbilder neu aufnehmen
|
||||||
|
```
|
||||||
|
|
||||||
|
`npm run pruefen` muss vor jedem Commit grün sein. Fehlt `shellcheck` lokal,
|
||||||
|
wird das Docker-Abbild `koalaman/shellcheck` benutzt.
|
||||||
|
|
||||||
|
### Aufbau
|
||||||
|
|
||||||
|
```
|
||||||
|
prisma/ Schema, Migrationen, Seed, Demo-Seed
|
||||||
|
scripts/ Prüf-, Test- und Betriebsskripte
|
||||||
|
src/app/ Seiten und API-Routen; (app) = angemeldeter Bereich
|
||||||
|
src/components/ Bausteine; ui/ enthält die Primitiven
|
||||||
|
src/lib/ Reine Logik ohne DB und ohne Systemzeit, je Datei ein Test
|
||||||
|
src/server/ DB-nahe Dienste, Server Actions, Provider
|
||||||
|
e2e/ Playwright-Rauchtests
|
||||||
|
screenshots/ Aufnahme der Handbuchbilder
|
||||||
|
docs/ Schnellstart, Handbuch, Bilder
|
||||||
|
devlog/ Entwicklungstagebuch und Zeiterfassung
|
||||||
|
```
|
||||||
|
|
||||||
|
Reine Funktionen bekommen die aktuelle Zeit als Parameter, damit sie ohne
|
||||||
|
Tricks prüfbar bleiben. Die Tests laufen bewusst in UTC — so fällt sofort auf,
|
||||||
|
wenn irgendwo die Ortszeit des Servers durchschlägt statt Europe/Berlin.
|
||||||
|
|
||||||
|
### Bekannte Meldungen
|
||||||
|
|
||||||
|
`npm audit` meldet vier Funde in transitiven Abhängigkeiten der
|
||||||
|
**`prisma`-CLI** (`deepmerge-ts` über `@prisma/config`, `mysql2`). Beide sind
|
||||||
|
Entwicklungsabhängigkeiten und erreichen das Laufzeitabbild nicht; MySQL wird
|
||||||
|
ohnehin nie geladen. Der angebotene Fix wäre ein Rückschritt auf Prisma 6.
|
||||||
|
|
||||||
|
## Sicherheit
|
||||||
|
|
||||||
|
- Anmeldung, Passwort und Passkey antworten bei Fehlschlag immer gleich, damit
|
||||||
|
sich nicht abfragen lässt, wer hier ein Konto hat.
|
||||||
|
- Sitzungen sind JWT, aber Rolle und Sperrstatus werden bei jeder Anfrage
|
||||||
|
frisch aus der Datenbank gelesen — eine Sperre wirkt sofort.
|
||||||
|
- Jede schreibende Aktion prüft die Rolle in der Seite **und** noch einmal in
|
||||||
|
der Server Action. Die Oberfläche blendet Knöpfe nur zusätzlich aus.
|
||||||
|
- Audit-Details werden vor dem Speichern gesäubert: Felder, deren Name nach
|
||||||
|
Passwort, Token, Betreff oder Mailinhalt aussieht, werden ersetzt.
|
||||||
|
- Keine Geheimnisse, Tokens, Mailinhalte oder Termin-Betreffs in Protokollen.
|
||||||
|
|
||||||
|
Gefundene Schwachstellen bitte nicht als Issue anlegen, sondern direkt melden.
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
|
||||||
|
Privates Projekt, keine Lizenz zur Weiterverwendung.
|
||||||
+124
@@ -0,0 +1,124 @@
|
|||||||
|
# Fahrplan
|
||||||
|
|
||||||
|
Acht Meilensteine. Jeder endet mit einer lauffähigen Version auf `main`,
|
||||||
|
grünen Prüfungen, aktueller Dokumentation und einem Eintrag im DEVLOG.
|
||||||
|
|
||||||
|
| Meilenstein | Ziel | Status |
|
||||||
|
| --------------------------------------- | ---------------------------- | --------- |
|
||||||
|
| [M1](#m1--fundament) | Fundament | in Arbeit |
|
||||||
|
| [M2](#m2--aufgaben-und-projekte) | Aufgaben und Projekte | geplant |
|
||||||
|
| [M3](#m3--kalender-lesen) | Kalender lesen | geplant |
|
||||||
|
| [M4](#m4--wochenplanung-und-zeitblöcke) | Wochenplanung und Zeitblöcke | geplant |
|
||||||
|
| [M5](#m5--benachrichtigungen) | Benachrichtigungen | geplant |
|
||||||
|
| [M6](#m6--integrationen) | Integrationen | geplant |
|
||||||
|
| [M7](#m7--schnellerfassung-und-api) | Schnellerfassung und API | geplant |
|
||||||
|
| [M8](#m8--mcp) | MCP | geplant |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M1 – Fundament
|
||||||
|
|
||||||
|
**Ziel:** Ein angemeldeter, betreibbarer Rumpf. Noch keine Aufgaben, kein
|
||||||
|
Kalender — aber alles, worauf die späteren Meilensteine aufsetzen.
|
||||||
|
|
||||||
|
- [x] Projektgerüst: Next 16, React 19, TypeScript strict, Tailwind 4
|
||||||
|
- [x] `npm run pruefen`: Prettier, ESLint, tsc, Vitest, ShellCheck
|
||||||
|
- [x] Datenmodell für Anmeldung, Passkeys, Bereiche und Audit
|
||||||
|
- [x] Anmeldung über OIDC, Passkey und wahlweise Passwort
|
||||||
|
- [x] Rollen `BENUTZER` und `ADMIN`, serverseitig in Seite **und** Server Action
|
||||||
|
- [x] Audit-Eintrag je schreibender Aktion, Details werden gesäubert
|
||||||
|
- [x] Instanzsteuerung `INSTANZ=firma|daheim` mit 404 statt Hinweis
|
||||||
|
- [x] Bereiche verwalten: Farbe, Arbeitsfenster, Tageskapazität
|
||||||
|
- [x] Protokollseite für die Verwaltung
|
||||||
|
- [x] `/api/health` mit Status, Version und Instanz
|
||||||
|
- [x] Dockerfile, Compose-Stapel mit eigenem Migrationsdienst
|
||||||
|
- [x] `backup.sh`, `update.sh`, `scripts/release.sh`, `scripts/zeit.sh`
|
||||||
|
- [x] Unit-, Integrations-, Migrations- und E2E-Tests
|
||||||
|
- [x] Screenshot-Kette mit eigener Wegwerf-Instanz
|
||||||
|
- [x] README, Schnellstart, Handbuch, CHANGELOG, DEVLOG
|
||||||
|
|
||||||
|
**Bewusst verschoben:** `Aufgabe` und `Zeitblock` kommen mit M2 und M4,
|
||||||
|
`Kalenderkonto` mit M3 — jeweils mit den Feldern, die der Provider dann
|
||||||
|
wirklich braucht, statt heute geraten.
|
||||||
|
|
||||||
|
## M2 – Aufgaben und Projekte
|
||||||
|
|
||||||
|
**Ziel:** Alles erfassen und wiederfinden, noch ohne Kalender.
|
||||||
|
|
||||||
|
- [ ] `Aufgabe` mit Typ, Status, Priorität, Aufwand, Fälligkeit, Quelle
|
||||||
|
- [ ] Eindeutigkeit `[quelle, quelleRef]`, Integrationen upserten
|
||||||
|
- [ ] Inbox: schnell erfassen, später einsortieren
|
||||||
|
- [ ] Ideen getrennt von Aufgaben
|
||||||
|
- [ ] Projekte mit Arbeitspaketen und Meilensteinen
|
||||||
|
- [ ] Listen mit Filtern nach Bereich, Status und Fälligkeit
|
||||||
|
- [ ] Handbuchseiten und Bilder
|
||||||
|
|
||||||
|
## M3 – Kalender lesen
|
||||||
|
|
||||||
|
**Ziel:** Termine aus Microsoft 365 und Google sehen.
|
||||||
|
|
||||||
|
- [ ] `Kalenderkonto` mit Anbieter, Tokens und Delta-Verweis
|
||||||
|
- [ ] OAuth-Verbindung je Instanz: Graph immer, Google nur daheim
|
||||||
|
- [ ] Graph-Delta-Sync und Google-Sync über `fetch`, Antworten mit zod geprüft
|
||||||
|
- [ ] Ganztägige Termine zählen nicht zur Belegung
|
||||||
|
- [ ] Anonymisierte Beispielantworten in `test/fixtures/`
|
||||||
|
- [ ] Hintergrundjob mit node-cron
|
||||||
|
|
||||||
|
## M4 – Wochenplanung und Zeitblöcke
|
||||||
|
|
||||||
|
**Ziel:** Aufgaben per Ziehen und Ablegen in den Kalender schreiben — und
|
||||||
|
Verschiebungen von dort zurückholen.
|
||||||
|
|
||||||
|
- [ ] `Zeitblock` an genau einer Aufgabe und einem Kalenderkonto
|
||||||
|
- [ ] Wochenansicht mit `@dnd-kit/core`
|
||||||
|
- [ ] Schreiben in den Kalender, Rückweg beim Verschieben
|
||||||
|
- [ ] Schleifenschutz über `hash = sha256(startISO|endeISO)`
|
||||||
|
- [ ] Konflikte: der Kalender gewinnt (HTTP 412 auf If-Match)
|
||||||
|
- [ ] Belegung gegen die Tageskapazität des Bereichs
|
||||||
|
|
||||||
|
## M5 – Benachrichtigungen
|
||||||
|
|
||||||
|
- [ ] Web-Push mit `web-push`
|
||||||
|
- [ ] Tagesüberblick per E-Mail über nodemailer
|
||||||
|
- [ ] Erinnerungen an Fälligkeiten, Jobs über node-cron
|
||||||
|
- [ ] Je Benutzer einstellbar, mit Ruhezeiten
|
||||||
|
|
||||||
|
## M6 – Integrationen
|
||||||
|
|
||||||
|
- [ ] Zammad und Wartungsplaner-API (nur `firma`)
|
||||||
|
- [ ] Telegram (nur `daheim`)
|
||||||
|
- [ ] Spiegel: `firma` sendet, `daheim` empfängt
|
||||||
|
- [ ] Jede Integration upsertet über `[quelle, quelleRef]`
|
||||||
|
|
||||||
|
## M7 – Schnellerfassung und API
|
||||||
|
|
||||||
|
- [ ] Erfassung vom Handy, auch offline
|
||||||
|
- [ ] Öffentliche API mit Token je Anwendung
|
||||||
|
- [ ] Rechte je Token, jede Nutzung im Protokoll
|
||||||
|
|
||||||
|
## M8 – MCP
|
||||||
|
|
||||||
|
- [ ] MCP-Server über `@modelcontextprotocol/sdk`
|
||||||
|
- [ ] Lesen und Anlegen von Aufgaben und Zeitblöcken
|
||||||
|
- [ ] Rechte wie bei der API, jede Nutzung im Protokoll
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ideen für später
|
||||||
|
|
||||||
|
- Auswertung: wie viel Zeit ging tatsächlich in welchen Bereich
|
||||||
|
- Wiederkehrende Aufgaben mit eigenem Rhythmus
|
||||||
|
- Vorlagen für wiederkehrende Projekte
|
||||||
|
- Verknüpfung von Aufgaben untereinander (blockiert, gehört zu)
|
||||||
|
- Export der eigenen Daten als JSON
|
||||||
|
|
||||||
|
## Bewusst nicht geplant
|
||||||
|
|
||||||
|
- **Wiederkehrende Wartungsaufgaben mit Nachweispflicht.** Die bleiben im
|
||||||
|
Wartungsplaner. Der Planer liest nur Fälligkeiten über dessen API.
|
||||||
|
- **Mehrbenutzerbetrieb mit geteilten Aufgaben.** Der Planer ist für eine
|
||||||
|
Person gedacht; Kollegen melden sich in der Firmeninstanz nur an ihren
|
||||||
|
eigenen Daten an.
|
||||||
|
- **Eigene Kalenderdarstellung als Ersatz für Outlook.** Der Planer schreibt
|
||||||
|
in die vorhandenen Kalender, er ersetzt sie nicht.
|
||||||
|
- **Zeiterfassung zur Abrechnung.** Dafür gibt es andere Werkzeuge.
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
# Schnellstart
|
||||||
|
|
||||||
|
Von `git clone` bis zur ersten Anmeldung in höchstens zehn Minuten.
|
||||||
|
Jeder Befehl ist zum Kopieren, jeder Schritt nennt das erwartete Ergebnis.
|
||||||
|
|
||||||
|
## Voraussetzungen
|
||||||
|
|
||||||
|
| Werkzeug | Fassung | Prüfen mit |
|
||||||
|
| ------------------ | ---------------- | ------------------------ |
|
||||||
|
| Docker mit Compose | 25 oder neuer | `docker compose version` |
|
||||||
|
| Git | 2.40 oder neuer | `git --version` |
|
||||||
|
| Node.js | 22.12 oder neuer | `node -v` |
|
||||||
|
|
||||||
|
Node wird nur für die Entwicklung gebraucht. Für den reinen Betrieb genügen
|
||||||
|
Docker und Git.
|
||||||
|
|
||||||
|
## 1. Holen und Verzeichnis wechseln
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <REMOTE_URL> planer
|
||||||
|
cd planer
|
||||||
|
```
|
||||||
|
|
||||||
|
**Erwartet:** Das Verzeichnis `planer` enthält unter anderem `package.json`,
|
||||||
|
`docker-compose.yml` und `.env.example`.
|
||||||
|
|
||||||
|
## 2. Konfiguration anlegen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Jetzt die Pflichtwerte setzen. Die Geheimnisse werden erzeugt, nicht erfunden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Signiert die Sitzungen. Ohne diesen Wert startet die Anwendung nicht.
|
||||||
|
openssl rand -base64 32
|
||||||
|
|
||||||
|
# Ein Passwort für die Datenbank.
|
||||||
|
openssl rand -hex 16
|
||||||
|
```
|
||||||
|
|
||||||
|
Unter Windows ohne OpenSSL geht auch:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
||||||
|
```
|
||||||
|
|
||||||
|
Diese Werte in `.env` eintragen:
|
||||||
|
|
||||||
|
| Variable | Beispiel | Bedeutung |
|
||||||
|
| ------------------- | -------------------------------------------------------------------- | ------------------------------------------------------- |
|
||||||
|
| `INSTANZ` | `daheim` | `firma` oder `daheim` – bestimmt, was es überhaupt gibt |
|
||||||
|
| `APP_URL` | `http://localhost:3000` | Adresse ohne Schrägstrich am Ende |
|
||||||
|
| `AUTH_SECRET` | _(erzeugt)_ | Signatur der Sitzungen |
|
||||||
|
| `POSTGRES_PASSWORD` | _(erzeugt)_ | Passwort der Datenbank |
|
||||||
|
| `DATABASE_URL` | `postgresql://planer:<Passwort>@localhost:5432/planer?schema=public` | muss dasselbe Passwort enthalten |
|
||||||
|
| `WEBAUTHN_RP_ID` | `localhost` | nackte Domain, ohne Schema und Port |
|
||||||
|
| `WEBAUTHN_ORIGIN` | `http://localhost:3000` | exakt wie im Browser, mit Port |
|
||||||
|
| `ADMIN_EMAIL` | `du@example.de` | erstes Verwaltungskonto |
|
||||||
|
| `ADMIN_PASSWORT` | _(mindestens 12 Zeichen)_ | nur nötig, wenn du dich ohne OIDC anmelden willst |
|
||||||
|
|
||||||
|
> **Sind die Ports 5432 oder 3000 auf deinem Rechner belegt?**
|
||||||
|
> Dann `POSTGRES_PORT` beziehungsweise `APP_PORT` in `.env` auf einen freien
|
||||||
|
> Wert setzen. `DATABASE_URL`, `APP_URL` und `WEBAUTHN_ORIGIN` müssen denselben
|
||||||
|
> Port nennen.
|
||||||
|
|
||||||
|
**Erwartet:** `.env` liegt im Projektverzeichnis. Sie gehört niemals ins Git —
|
||||||
|
`.gitignore` sorgt dafür.
|
||||||
|
|
||||||
|
## 3. Starten
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Beim ersten Mal dauert der Bau einige Minuten. Der Ablauf ist: Datenbank
|
||||||
|
starten, Migrationen einspielen, Anwendung starten.
|
||||||
|
|
||||||
|
**Erwartet:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
zeigt `db` als `healthy` und `app` als `Up`. Der Dienst `migration` taucht
|
||||||
|
nicht mehr auf — er hat seine Arbeit getan und sich beendet.
|
||||||
|
|
||||||
|
## 4. Prüfen, ob alles läuft
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:3000/api/health
|
||||||
|
```
|
||||||
|
|
||||||
|
**Erwartet:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "status": "ok", "version": "0.1.0", "instanz": "daheim", "datenbank": "ok" }
|
||||||
|
```
|
||||||
|
|
||||||
|
Steht dort `"datenbank": "fehler"`, stimmt `DATABASE_URL` nicht mit
|
||||||
|
`POSTGRES_PASSWORD` überein.
|
||||||
|
|
||||||
|
## 5. Erste Bereiche und das Verwaltungskonto anlegen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose run --rm migration node node_modules/prisma/build/index.js db seed
|
||||||
|
```
|
||||||
|
|
||||||
|
**Erwartet:** Die Ausgabe nennt die vier angelegten Bereiche (Firma, Gewerbe,
|
||||||
|
Privat, Homelab) und bestätigt das Verwaltungskonto.
|
||||||
|
|
||||||
|
## 6. Anmelden
|
||||||
|
|
||||||
|
Im Browser <http://localhost:3000> öffnen.
|
||||||
|
|
||||||
|
**Erwartet:** Die Anmeldeseite. Mit `ADMIN_EMAIL` und `ADMIN_PASSWORT`
|
||||||
|
anmelden — danach steht oben „Guten Tag" und darunter die vier Bereiche.
|
||||||
|
|
||||||
|
> Ist OIDC eingerichtet, erscheint zusätzlich ein Knopf mit dem Namen aus
|
||||||
|
> `OIDC_NAME`. Der Rückleit-URI, den du beim Anbieter hinterlegen musst,
|
||||||
|
> lautet `<APP_URL>/api/auth/callback/oidc`.
|
||||||
|
|
||||||
|
## 7. Einen Passkey hinterlegen
|
||||||
|
|
||||||
|
Auf **Einstellungen** gehen, unter _Passkeys_ einen Namen für das Gerät
|
||||||
|
eintragen (etwa „Arbeitsrechner") und auf **Anlegen** klicken. Das Gerät
|
||||||
|
fragt nach Fingerabdruck, Gesicht oder PIN.
|
||||||
|
|
||||||
|
**Erwartet:** Der Passkey steht in der Liste. Ab jetzt genügt zum Anmelden die
|
||||||
|
E-Mail-Adresse und **Mit Passkey anmelden**.
|
||||||
|
|
||||||
|
> Hinterlege am besten zwei Passkeys auf verschiedenen Geräten. Sonst sperrst
|
||||||
|
> du dich aus, wenn ein Gerät verlorengeht.
|
||||||
|
|
||||||
|
## 8. Aufräumen
|
||||||
|
|
||||||
|
Wenn `ADMIN_PASSWORT` nur zur Einrichtung gedient hat, kannst du es jetzt
|
||||||
|
zusammen mit `ADMIN_EMAIL` aus `.env` entfernen. Das Konto bleibt bestehen.
|
||||||
|
|
||||||
|
## Wie es weitergeht
|
||||||
|
|
||||||
|
- [Handbuch](handbuch/) – Anleitung für den Alltag
|
||||||
|
- [README](../README.md) – alle Umgebungsvariablen, Betrieb, Sicherung
|
||||||
|
- [ROADMAP](../ROADMAP.md) – was in den nächsten Meilensteinen kommt
|
||||||
|
|
||||||
|
## Wenn etwas klemmt
|
||||||
|
|
||||||
|
| Symptom | Ursache und Abhilfe |
|
||||||
|
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `INSTANZ fehlt in .env` beim Start | `INSTANZ` ist leer. Erlaubt sind nur `firma` und `daheim`. |
|
||||||
|
| `Die Konfiguration ist unvollständig` | Die Meldung nennt die fehlende Variable. Sie nennt nie den Wert. |
|
||||||
|
| `port is already allocated` | Ein anderer Dienst hält den Port. `APP_PORT` oder `POSTGRES_PORT` ändern. |
|
||||||
|
| Passkey lässt sich nicht anlegen | `WEBAUTHN_ORIGIN` muss exakt der Adresse im Browser entsprechen, inklusive Port. `WEBAUTHN_RP_ID` ist die nackte Domain, ohne `https://`. |
|
||||||
|
| Anmeldung schlägt immer fehl | Bewusst gibt es nur eine Meldung für alle Fälle. Prüfe im Protokoll der Anwendung: `docker compose logs app`. |
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Handbuch
|
||||||
|
|
||||||
|
Anleitung für den Alltag mit dem Planer. Eine Seite je Thema.
|
||||||
|
|
||||||
|
## Erste Schritte
|
||||||
|
|
||||||
|
1. **[Anmelden](anmelden.md)** – die drei Wege hinein, Passkeys einrichten,
|
||||||
|
was tun, wenn nichts mehr geht
|
||||||
|
2. **[Bereiche einrichten](bereiche.md)** – Farbe, Arbeitsfenster und
|
||||||
|
Tageskapazität festlegen
|
||||||
|
|
||||||
|
## Verwaltung
|
||||||
|
|
||||||
|
3. **[Protokoll](protokoll.md)** – nachsehen, wer wann was geändert hat
|
||||||
|
|
||||||
|
## Was noch kommt
|
||||||
|
|
||||||
|
Diese Seiten entstehen mit den nächsten Meilensteinen:
|
||||||
|
|
||||||
|
| Thema | Meilenstein |
|
||||||
|
| ------------------------------- | ----------- |
|
||||||
|
| Aufgaben erfassen, Inbox, Ideen | M2 |
|
||||||
|
| Projekte und Arbeitspakete | M2 |
|
||||||
|
| Kalenderkonten verbinden | M3 |
|
||||||
|
| Wochenplanung und Zeitblöcke | M4 |
|
||||||
|
| Benachrichtigungen | M5 |
|
||||||
|
| Integrationen | M6 |
|
||||||
|
| Schnellerfassung vom Handy, API | M7 |
|
||||||
|
| MCP | M8 |
|
||||||
|
|
||||||
|
Der [Fahrplan](../../ROADMAP.md) sagt, was in welchem Meilenstein steckt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Zur Einrichtung einer neuen Instanz: [Schnellstart](../SCHNELLSTART.md).
|
||||||
|
Für Betrieb, Sicherung und alle Umgebungsvariablen: [README](../../README.md).
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# Anmelden
|
||||||
|
|
||||||
|
Es gibt drei Wege in den Planer. Welche davon zu sehen sind, hängt davon ab,
|
||||||
|
was für deine Instanz eingerichtet wurde.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
| Weg | Wann sinnvoll |
|
||||||
|
| -------------------------------- | --------------------------------------------------------------------------------------------------- |
|
||||||
|
| **Unternehmensanmeldung** (OIDC) | In der Firma der Normalfall. Ein Klick, die Anmeldung übernimmt Entra ID beziehungsweise authentik. |
|
||||||
|
| **Passkey** | Der bequemste Weg am eigenen Gerät: Fingerabdruck, Gesicht oder Geräte-PIN, kein Passwort. |
|
||||||
|
| **E-Mail und Passwort** | Der Rückfallweg. Oft nur daheim freigeschaltet. |
|
||||||
|
|
||||||
|
## Mit Passkey anmelden
|
||||||
|
|
||||||
|
1. E-Mail-Adresse eintragen.
|
||||||
|
2. Auf **Mit Passkey anmelden** klicken.
|
||||||
|
3. Das Gerät fragt nach Fingerabdruck, Gesicht oder PIN.
|
||||||
|
|
||||||
|
Fertig — du landest auf der Startseite.
|
||||||
|
|
||||||
|
> **Warum erst die E-Mail-Adresse?** Damit der Planer weiß, welche Passkeys er
|
||||||
|
> anbieten soll. Er antwortet dabei für unbekannte Adressen genauso wie für
|
||||||
|
> bekannte, damit sich von außen nicht abfragen lässt, wer hier ein Konto hat.
|
||||||
|
|
||||||
|
## Einen Passkey einrichten
|
||||||
|
|
||||||
|
Das geht erst, wenn du angemeldet bist — also beim ersten Mal über OIDC oder
|
||||||
|
über E-Mail und Passwort.
|
||||||
|
|
||||||
|
1. Oben rechts auf **Einstellungen**.
|
||||||
|
2. Im Abschnitt _Passkeys_ einen Namen für das Gerät eintragen, etwa
|
||||||
|
„Arbeitsrechner" oder „Diensthandy".
|
||||||
|
3. Auf **Anlegen** klicken und die Abfrage des Geräts bestätigen.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Der Passkey steht danach in der Liste, mit Anlegedatum und letzter Benutzung.
|
||||||
|
|
||||||
|
> **Lege zwei Passkeys an, auf zwei verschiedenen Geräten.** Sonst sperrst du
|
||||||
|
> dich aus, sobald ein Gerät verlorengeht oder zurückgesetzt wird.
|
||||||
|
|
||||||
|
Unter jedem Eintrag steht, ob der Passkey **geräteübergreifend** ist — dann
|
||||||
|
liegt er im Schlüsselbund von Apple, Google oder deinem Passwortmanager und ist
|
||||||
|
auf deinen anderen Geräten ebenfalls verfügbar. Steht dort **nur dieses
|
||||||
|
Gerät**, ist er an genau dieses Gerät gebunden.
|
||||||
|
|
||||||
|
## Einen Passkey entfernen
|
||||||
|
|
||||||
|
In den Einstellungen auf das Papierkorb-Symbol neben dem Eintrag. Der Passkey
|
||||||
|
ist sofort ungültig; auf dem Gerät selbst bleibt ein Rest zurück, den du dort
|
||||||
|
im Schlüsselbund löschen kannst.
|
||||||
|
|
||||||
|
## Abmelden
|
||||||
|
|
||||||
|
Oben rechts auf das Symbol mit dem Pfeil aus der Tür.
|
||||||
|
|
||||||
|
## Wenn es klemmt
|
||||||
|
|
||||||
|
| Was du siehst | Was dahintersteckt |
|
||||||
|
| -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| „Anmeldung nicht möglich. Bitte Angaben prüfen." | Diese eine Meldung gilt für alle Fälle: falsches Passwort, unbekannte Adresse, gesperrtes Konto. Das ist Absicht — sonst ließe sich abfragen, wer hier ein Konto hat. |
|
||||||
|
| Es passiert nichts nach dem Klick auf **Mit Passkey anmelden** | Du hast den Systemdialog abgebrochen. Einfach noch einmal klicken. |
|
||||||
|
| „Dieses Gerät ist hier schon hinterlegt." | Für dieses Gerät gibt es bereits einen Passkey. Ein zweiter bringt nichts. |
|
||||||
|
| Der Passkey-Knopf tut gar nichts | Der Browser unterstützt WebAuthn nicht, oder die Seite läuft nicht über HTTPS. Passkeys brauchen eine sichere Verbindung — `localhost` ausgenommen. |
|
||||||
|
| Passkey lässt sich nicht anlegen | Meist stimmt die Konfiguration nicht: `WEBAUTHN_ORIGIN` muss exakt der Adresse im Browser entsprechen, inklusive Port. Siehe [README](../../README.md). |
|
||||||
|
|
||||||
|
## Ausgesperrt?
|
||||||
|
|
||||||
|
Wenn kein Weg mehr hineinführt, hilft nur jemand mit Zugriff auf den Server.
|
||||||
|
Dort lässt sich mit `ADMIN_EMAIL` und `ADMIN_PASSWORT` in der `.env` und einem
|
||||||
|
erneuten Seed-Lauf wieder ein Passwort setzen — siehe
|
||||||
|
[Schnellstart, Schritt 5](../SCHNELLSTART.md).
|
||||||
|
|
||||||
|
## Rollen
|
||||||
|
|
||||||
|
Unten in der Fußzeile und in den Einstellungen steht, welche Rolle du hast.
|
||||||
|
|
||||||
|
- **Benutzer** – sieht und plant die eigenen Sachen.
|
||||||
|
- **Verwaltung** – darf zusätzlich Bereiche ändern und das
|
||||||
|
[Protokoll](protokoll.md) einsehen.
|
||||||
|
|
||||||
|
Die Rolle vergibt die Verwaltung; sie lässt sich nicht selbst hochstufen.
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# Bereiche einrichten
|
||||||
|
|
||||||
|
Ein **Bereich** ist ein Lebensbereich: Firma, Gewerbe, Privat, Homelab. Jede
|
||||||
|
Aufgabe und jeder Zeitblock gehört später zu genau einem davon.
|
||||||
|
|
||||||
|
Der Bereich bestimmt drei Dinge:
|
||||||
|
|
||||||
|
- **die Farbe**, an der du im Kalender sofort erkennst, worum es geht,
|
||||||
|
- **das Arbeitsfenster** — die Tageszeit, in der für diesen Bereich überhaupt
|
||||||
|
geplant wird,
|
||||||
|
- **die Tageskapazität** — wie viele Minuten je Wochentag höchstens
|
||||||
|
hineingehen.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> Bereiche ändern darf nur die **Verwaltung**. Als Benutzer siehst du sie,
|
||||||
|
> aber ohne die Formulare.
|
||||||
|
|
||||||
|
## Einen Bereich anlegen
|
||||||
|
|
||||||
|
1. Im Menü auf **Bereiche**.
|
||||||
|
2. Ganz unten unter _Neuen Bereich anlegen_ die Felder ausfüllen.
|
||||||
|
3. Auf **Bereich anlegen** klicken.
|
||||||
|
|
||||||
|
| Feld | Bedeutung |
|
||||||
|
| ---------------------------- | ----------------------------------------------------------------------------------------------- |
|
||||||
|
| **Name** | Zwei bis 40 Zeichen, muss eindeutig sein |
|
||||||
|
| **Farbe** | Wird zum Farbpunkt in Listen und später im Kalender |
|
||||||
|
| **Arbeitsfenster von / bis** | Zwischen diesen Uhrzeiten wird geplant. Muss nach dem Beginn enden |
|
||||||
|
| **Tageskapazität** | Minuten je Wochentag. 0 heißt: an diesem Tag gar nicht |
|
||||||
|
| **In Benutzung** | Abgehakt lassen. Ohne Haken bleibt der Bereich erhalten, taucht aber nicht mehr zur Auswahl auf |
|
||||||
|
| **Reihenfolge** | Kleinere Zahlen stehen weiter oben |
|
||||||
|
|
||||||
|
## Was eine sinnvolle Kapazität ist
|
||||||
|
|
||||||
|
Nicht die Zeit, die der Tag hergibt — die Zeit, die **wirklich für geplante
|
||||||
|
Arbeit** übrig bleibt, nach Besprechungen, Störungen und Pausen.
|
||||||
|
|
||||||
|
Ein brauchbarer Anfang:
|
||||||
|
|
||||||
|
| Bereich | Montag bis Donnerstag | Freitag | Wochenende |
|
||||||
|
| ------- | --------------------- | ------- | --------------- |
|
||||||
|
| Firma | 360 min (6 h) | 240 min | 0 |
|
||||||
|
| Gewerbe | 90 min | 60 min | Samstag 180 min |
|
||||||
|
| Privat | 60 min | 120 min | je 240 min |
|
||||||
|
| Homelab | 0 bis 60 min | 0 | Samstag 120 min |
|
||||||
|
|
||||||
|
Wenn am Ende der Woche regelmäßig etwas liegenbleibt, ist die Kapazität zu
|
||||||
|
hoch angesetzt, nicht der Tag zu kurz.
|
||||||
|
|
||||||
|
> Die Tageskapazität darf nie länger sein als das Arbeitsfenster. Ein Fenster
|
||||||
|
> von 08:00 bis 17:00 fasst höchstens 540 Minuten; trägst du mehr ein, weist
|
||||||
|
> das Formular es ab.
|
||||||
|
|
||||||
|
## Einen Bereich ändern
|
||||||
|
|
||||||
|
Auf **Bearbeiten** unter dem Bereich klicken, ändern, **Speichern**. Mit
|
||||||
|
**Schließen** verwirfst du, was du noch nicht gespeichert hast.
|
||||||
|
|
||||||
|
## Einen Bereich loswerden
|
||||||
|
|
||||||
|
Zwei Wege, und meistens ist der erste der richtige:
|
||||||
|
|
||||||
|
1. **Stilllegen** – den Haken bei _In Benutzung_ entfernen und speichern. Alles
|
||||||
|
Bisherige bleibt erhalten, der Bereich taucht nur nicht mehr zur Auswahl auf.
|
||||||
|
2. **Löschen** – der rote Knopf. Endgültig.
|
||||||
|
|
||||||
|
> Ab M2 hängen Aufgaben an Bereichen. Dann ist Stilllegen fast immer die
|
||||||
|
> richtige Wahl, weil beim Löschen auch die Zuordnung verlorenginge.
|
||||||
|
|
||||||
|
Jede Änderung landet im [Protokoll](protokoll.md).
|
||||||
|
|
||||||
|
## Typische Fehler
|
||||||
|
|
||||||
|
| Meldung | Was zu tun ist |
|
||||||
|
| --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||||
|
| „Diesen Namen gibt es schon." | Namen sind eindeutig. Vielleicht existiert der Bereich stillgelegt schon. |
|
||||||
|
| „Das Arbeitsfenster muss nach seinem Beginn enden." | Von-Zeit liegt hinter der Bis-Zeit. Über Mitternacht hinweg geht nicht — dafür lieber zwei Bereiche. |
|
||||||
|
| „Keine Tageskapazität darf länger sein als das Arbeitsfenster." | Entweder die Kapazität senken oder das Fenster weiten. |
|
||||||
|
| „Dazu fehlt dir die Berechtigung." | Du bist als Benutzer angemeldet, nicht als Verwaltung. |
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# Protokoll
|
||||||
|
|
||||||
|
Jede schreibende Aktion im Planer hinterlässt einen Eintrag: wer, wann, was,
|
||||||
|
und woran. Einträge lassen sich weder ändern noch löschen.
|
||||||
|
|
||||||
|
Die Seite steht nur der **Verwaltung** offen. Als Benutzer siehst du den
|
||||||
|
Menüpunkt gar nicht erst; wer die Adresse direkt aufruft, landet auf einem
|
||||||
|
Hinweis.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Was dort steht
|
||||||
|
|
||||||
|
| Spalte | Bedeutung |
|
||||||
|
| ------------- | --------------------------------------------------------------------- |
|
||||||
|
| **Zeitpunkt** | Datum und Uhrzeit in Europe/Berlin |
|
||||||
|
| **Wer** | Die E-Mail-Adresse zum Zeitpunkt der Aktion |
|
||||||
|
| **Was** | Die Aktion, in der Form `objekt.verb` |
|
||||||
|
| **Objekt** | Die Art des betroffenen Objekts, dahinter die fachlichen Einzelheiten |
|
||||||
|
|
||||||
|
Angezeigt werden die letzten 100 Einträge, die neuesten zuerst.
|
||||||
|
|
||||||
|
## Die Aktionen
|
||||||
|
|
||||||
|
| Aktion | Bedeutung |
|
||||||
|
| ------------------- | --------------------------------------------------- |
|
||||||
|
| `bereich.angelegt` | Ein neuer Bereich ist entstanden |
|
||||||
|
| `bereich.geaendert` | Name, Farbe, Fenster oder Kapazität wurden geändert |
|
||||||
|
| `bereich.geloescht` | Ein Bereich wurde entfernt |
|
||||||
|
| `passkey.angelegt` | Jemand hat ein Gerät hinterlegt |
|
||||||
|
| `passkey.entfernt` | Ein Gerät wurde entfernt |
|
||||||
|
|
||||||
|
Mit jedem Meilenstein kommen Aktionen hinzu.
|
||||||
|
|
||||||
|
## Was dort bewusst **nicht** steht
|
||||||
|
|
||||||
|
- Keine Passwörter, Hashes oder Tokens.
|
||||||
|
- Keine Termin-Betreffs und keine Mailinhalte.
|
||||||
|
- Keine Zugangsdaten der Kalenderkonten.
|
||||||
|
|
||||||
|
Das ist doppelt abgesichert: Solche Felder werden gar nicht erst übergeben,
|
||||||
|
und vor dem Speichern ersetzt der Planer zusätzlich jedes Feld, dessen Name
|
||||||
|
nach einem Geheimnis aussieht, durch `[entfernt]`. Wenn du dort `[entfernt]`
|
||||||
|
liest, hat dieser zweite Riegel gegriffen.
|
||||||
|
|
||||||
|
## Warum die E-Mail-Adresse und nicht nur die Kennung
|
||||||
|
|
||||||
|
Wird ein Benutzer gelöscht, verschwinden seine Passkeys und verknüpften
|
||||||
|
Konten — der Protokolleintrag bleibt. Die Verknüpfung zum Konto wird gelöst,
|
||||||
|
die Adresse bleibt als Nachweis stehen. Sonst wäre das Protokoll nach einer
|
||||||
|
Löschung wertlos.
|
||||||
|
|
||||||
|
## Aufbewahrung
|
||||||
|
|
||||||
|
Einträge werden nicht automatisch entfernt. Die Tabelle wächst langsam — ein
|
||||||
|
Eintrag je Änderung, nicht je Seitenaufruf. Sollte sie einmal stören, gehört
|
||||||
|
das Aufräumen in die Sicherungsroutine und nicht in die Anwendung.
|
||||||
Reference in New Issue
Block a user