# everythingbutwork Internes Web-Tool von **ToolCTRL** für den täglichen Überblick über alle Projekte und Bereiche. Es ist das dritte Werkzeug neben **Plane** (Tickets) und **Outline** (Doku) und operationalisiert den ToolCTRL Projektmanagement-Leitfaden v3.0. > Falls du in dieses Repo neu reinrutschst (neue Session, anderes Modell): lies **`CONVENTIONS.md`** vor jedem Edit, und schau in **`_bmad-output/planning-artifacts/`** für den vollen PRD/Architektur/UX-/Stories-Kontext. --- ## Was die App tut | Bereich | Feature | | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Dashboard** (`/dashboard`) | Unternehmens-Übersicht: Bereichs-Ampelstatus, **Showstopper** (offene Showstopper-Risiken), **Projekte in Eskalation/Taskforce**, „Heute fehlt"-Liste, bereichsübergreifende Blocker, Pflicht-Minimum-Übersicht, Jourfixe-Hinweis, „Heute im Team" (ToDos), „Zeit im Team heute" (Live-Tracking, Wall-Clock), Vortags-Feed | | **Bereich** (`/bereich`, `/bereich/`) | Tab-Switcher Entwicklung / Vertrieb / Marketing — Projekt-Liste (Kundenprojekte / Interne Projekte) mit Pflicht-Minimum- & Modus-Indicator. **Vertrieb** zusätzlich: Lead-Teaser + „Projekte mit Vertriebsaktivität" (bereichsübergreifend) | | **Projekt** (`/projekt/:id`) | Detail: Ziel/Maxime/Nicht-Ziele/Liefergegenstände + Zieldreieck, **Phasen-Lebenszyklus mit Gate-Reviews**, **Statusbericht** (Ampel/Trend + Verlauf), **Stakeholder-Matrix**, **Risiko- & Chancen-Register**, **verknüpfte Leads**, **Änderungs-Log**, Verlinkungen, Pflicht-Minimum, Meilensteine (mit Ergebnis/Kundennutzen + Datums-Warnung), DoD, Projekt-Abschluss (Checkliste + Vermerk, P1), Projekt-Modus | | **Leads** (`/leads`, `/lead/:id`) | Vertriebs-CRM: Lead-Liste nach Status (Cold/Warm/Hot/Won/Lost), Dossier (Stammdaten, Pain & Bedarf, Soft Factors, Strategie), Kontaktpersonen, Austausch-Log, **CSV-Import**, Lead→Deal-Promotion, **Projekt-Verknüpfung** | | **Heute** (`/heute`) | Tagesrhythmus: Lock-in mit ToDo-Liste, Auto-Carryover offener ToDos vom Vortag, **ToDos mitten am Tag hinzufügen**, Timer pro ToDo, Zeiterfassungs-Timeline mit Edit/Delete/Nachtrag | | **Reflexion** (`/reflexion`) | Abend-Reflexion: ToDos abhaken + **Titel inline korrigieren**, Freitext, Ampel + Trend, optional Blocker. Editierbar bis 00:00 Berlin | | **Archiv** (`/archiv`) | Abgeschlossene Projekte, gruppiert nach Bereich | **Querschnittsfunktionen** - **Leads / CRM**: Qualitative Lead-Dossiers im Vertrieb, **CSV-Import** (header-basiert, Dedup über Firmenname, Adresse → Standort, Notizen → Austausch-Log), optionale **Verknüpfung zu einem Projekt** (egal welcher Bereich) → Projekte mit Leads erscheinen in der Vertriebs-Sicht, ohne redundant angelegt zu werden. - **Projektmanagement-Tiefe** (operationalisiert Leitfaden v3.0): Projekt-Modus (Relax/Standard/Eskalation/Taskforce), 4-Phasen-Lebenszyklus mit Gate-Reviews, Statusbericht (Ampel/Trend/Text + Verlauf), Stakeholder-Analyse mit Macht/Interesse-Matrix, Risiko- & Chancen-Register (W×A, Showstopper, Owner, Strategie), Änderungs-Log (Scope-Creep), Zieldreieck, strukturierter Abschluss. - **Zeiterfassung**: Persistente Timer-Pill in der TopNav (Live-Sekundenzähler), Quick-Picker mit ToDos + Kategorien + Freitext, retroaktiver Nachtrag. „Gesamt" pro Person ist **Wall-Clock** (überlappende parallele Tasks zählen einmal); pro Aufgabe wird die Summe getrennt ausgewiesen („getrackt"). - **Blocker**: Inline in der Reflexion oder über `n`-Shortcut bzw. den globalen „Neuer Blocker"-Dialog. Auflösung nur durch den Projekt-P1. - **Pflicht-Minimum-Check** (Leitfaden §9): 6-Punkte-Checkliste pro Projekt; Punkt „Status-Ampel aktuell (≤ 7 Werktage)" wird per Cron **pro Projekt** aus dem jüngsten Statusbericht berechnet. - **Streak**: aufeinanderfolgende Werktage mit voller Reflexion. Wochenenden, **bundesweite Feiertage** und gemeldete Abwesenheiten sind neutral (brechen nicht). Flammen-Pill rechts oben. - **Keyboard-Shortcuts**: `g d/b/h/a` Navigation, `g e/v/m` direkt in Entwicklung/Vertrieb/Marketing, `n` Neuer Blocker, `?` Cheatsheet. --- ## Tech Stack | Layer | Wahl | | ------------- | ------------------------------------------------------------------------------------------------------------------------- | | Sprache | **TypeScript** strict, ESM überall | | Repo-Layout | **pnpm**-Monorepo (`pnpm-workspace.yaml`) | | Node | **22+** | | Frontend | **Vite 5 + React 18 + Tailwind 4 + React Router 7 + React Query 5 + Sonner** (Toasts) + **lucide-react** (Icons) | | Backend | **Fastify 5 + tRPC v11** (`@trpc/server/adapters/fastify`) | | ORM | **Drizzle 0.38** + **drizzle-kit** | | Datenbank | **Postgres 16** | | Auth | **Authentik OIDC** via `openid-client v6` (Auth Code + PKCE), Session-Cookies HttpOnly+SameSite=Lax, Sessions in Postgres | | Validation | **Zod 3** (geteilt zwischen FE und BE via `@cockpit/shared`) | | Logging | **pino** (Fastify-built-in) | | Styling | Tailwind 4 + CSS-Custom-Properties · Hausschrift **Space Grotesk** (selbst gehostet) · Keycap-Button-Stil | | Tests | **Vitest** (Unit + tRPC) | | Reverse-Proxy | **Caddy 2** (siehe `Caddyfile`) | | CI | **Gitea Actions** (`.gitea/workflows/ci.yml`) | | Deployment | **Docker Compose** (`docker-compose.yml`) auf ToolCTRL-interner Infra | ⚠️ **Namespace-Hinweis**: Die internen pnpm-Packages heißen `@cockpit/*` (Historie), die App selbst heißt **everythingbutwork**. Nicht umbenennen — das würde unzählige Imports brechen ohne Mehrwert. --- ## Verzeichnisstruktur ``` . ├── apps/ │ ├── web/ Vite + React SPA (Port 7173) │ │ ├── public/ Brand-Assets (Logo SVG, Fonts, Favicon) │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── blocker/ BlockerDialog │ │ │ │ ├── layout/ AppShell, TopNav │ │ │ │ ├── time/ TimerPill, TimelineSection, TeamTimeSection │ │ │ │ └── ui/ Button, Card, KeyCaret, SkeletonCard, ToolCtrlLogo │ │ │ ├── hooks/ useCurrentUser │ │ │ ├── lib/ router, trpc, trpc-provider, voice, │ │ │ │ format-duration, time-categories, │ │ │ │ lead-meta, project-meta │ │ │ ├── routes/ dashboard, bereich, projekt, heute, reflexion, │ │ │ │ leads, lead, archiv, login, not-found │ │ │ └── styles/globals.css │ │ ├── index.html │ │ └── vite.config.ts │ └── api/ Fastify Backend (Port 7100) │ ├── crontab System-Cron-Definitionen (Containerseitig) │ └── src/ │ ├── auth/ oidc, routes (login/callback/logout/dev-login), │ │ session, user, middleware │ ├── cron/routes.ts /internal/cron/* (Bearer-Token geschützt) │ ├── lib/ werktag (Mo-Fr Berlin + Feiertage), holidays, │ │ streak, csv, ampel, pflicht-minimum, logger │ ├── routers/ _app, user, area, project, deal, gtm, lead, │ │ risk, stakeholder, change, campaign, blocker, │ │ day-entry, time, dashboard │ ├── env.ts Zod-validierte Env-Vars │ ├── db.ts Singleton-Drizzle-Client │ ├── trpc.ts Context, protectedProcedure, withAudit │ ├── types.ts Fastify-Augmentation für req.cockpitUser │ └── server.ts Fastify-Bootstrap + Healthcheck ├── packages/ │ ├── db/ Drizzle-Schema + Migrationen │ │ ├── src/{schema,client,seed,index,migrate,base-data}.ts │ │ ├── migrations/ 0000_init … 0011_lead_project_link │ │ └── drizzle.config.ts │ └── shared/ Zod-Schemas + Konstanten + Types │ └── src/schemas/ day-entry, project, deal, campaign, time-entry, │ lead, risk, stakeholder, change ├── _bmad-output/planning-artifacts/ PRD, Architektur, UX-Spec, Epics, Decision-Log ├── docs/operations.md Ops-Guide (Authentik-Setup, Backup, Restore) ├── scripts/backup.sh pg_dump-Skript (im Postgres-Container) ├── docker-compose.yml Produktion (web + api + postgres + caddy) ├── docker-compose.dev.yml Nur Postgres für lokale Entwicklung ├── Caddyfile Reverse-Proxy-Config ├── CONVENTIONS.md Bindende Code-Konventionen ├── .gitea/workflows/ci.yml Gitea Actions ├── .env.example Env-Template ├── eslint.config.mjs Flat-Config (ESLint 9) ├── tsconfig.base.json Shared TS-Config └── pnpm-workspace.yaml ``` --- ## Lokal loslegen ### Vorbereitung (einmalig) ```bash # Repo & Tooling pnpm install # 4 Workspaces installieren cp .env.example .env # Defaults reichen für lokale Entwicklung ``` ### Dev-Stack starten ```bash # 1) Postgres im Docker-Container (Port 55432, NICHT 5432!) docker compose -f docker-compose.dev.yml up -d postgres # 2) Migration anwenden (Schema initialisieren) DATABASE_URL='postgres://cockpit:cockpit_dev@localhost:55432/cockpit' \ pnpm --filter @cockpit/db migrate:up # 3) Optional: Seed mit Test-Areas (Entwicklung/Vertrieb/Marketing/Allgemein) DATABASE_URL='postgres://cockpit:cockpit_dev@localhost:55432/cockpit' \ pnpm --filter @cockpit/db run seed # 4) API + Web parallel starten (zwei Terminals) pnpm --filter @cockpit/api dev # → http://localhost:7100 pnpm --filter web dev # → http://localhost:7173 ``` ### Browser öffnen **http://localhost:7173** Im Dev-Modus läuft ein **Auto-Login** als Admin-User `dejan@tool-ctrl.de`. Authentik ist nicht erforderlich; der Redirect zu `/api/auth/dev-login` setzt sofort eine Session. ### Wichtige Ports | Service | Port | Quelle | | --------------- | --------- | ------------------------------------------------------------------------- | | Vite Dev-Server | **7173** | `apps/web/vite.config.ts` | | Fastify API | **7100** | `.env` (`PORT`) | | Postgres (Dev) | **55432** | `docker-compose.dev.yml` (5432 würde mit Mac-System-Postgres kollidieren) | ### Häufig genutzte Scripts ```bash pnpm typecheck # alle Workspaces parallel pnpm lint # ESLint (flat config) pnpm format # Prettier write pnpm test # Vitest (--passWithNoTests für leere) pnpm --filter @cockpit/db studio # Drizzle Studio (Browser-DB-Inspector) pnpm --filter @cockpit/db migrate:generate --name= # neue Migration ``` --- ## Datenbank Postgres 16, Drizzle als ORM und Migrator. **Schema-Single-Source-of-Truth**: `packages/db/src/schema.ts`. ### Tabellen (aktueller Stand nach Migration 0011) | Tabelle | Zweck | | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `user` | Mitarbeiter (`authentik_sub` als Identitäts-Anker), `is_admin`-Flag | | `session` | Server-Side Sessions (Postgres-persistent), Browser kennt nur Cookie-`id` | | `audit_event` | Audit-Trail (Tabelle vorhanden; `withAudit`-Wrapper aktuell nicht verdrahtet) | | `area` | Bereich (entwicklung / vertrieb / marketing / allgemein) | | `project` | Projekt: Name, `description`, `maxime`, `ziel`/`nicht_ziele`/`liefergegenstaende`, `zieldreieck_flex`, `modus`, `phase`, Outline-/Plane-URLs, Arbeitsmodus, `pflicht_minimum_state` JSONB, Status-/Trend-Cache, `abschlussvermerk`, Archiv-Felder | | `milestone` | Cockpit-Sicht-Meilenstein pro Projekt (+ `ergebnis`, `kundennutzen`) | | `project_status` | Statusbericht-Historie pro Projekt (Ampel/Trend/Text, extern-Flag) | | `phase_gate` | Gate-Review am Phasenende (Ergebnis erreicht? + Notiz) | | `change_entry` | Änderungs-Log pro Projekt (Scope-Creep) | | `stakeholder` | Stakeholder-Analyse pro Projekt (Interesse × Einfluss, Haltung) | | `risk` | Risiko- & Chancen-Register pro Projekt (W×A, Showstopper, Owner, Strategie) | | `day_entry` | Tageseintrag (begun / full / absent), `published_at` für Vortrags-Veröffentlichung | | `todo` | Tagesplan-Item, optional an Bezug gebunden, `carried_over`-Flag | | `absence_day` | Urlaub/Krank/Sonstiges — zählt nicht gegen Streak | | `blocker` | Bereichsübergreifendes Hindernis | | `lead` | Vertriebs-Lead-Dossier; optionale `project_id`-Verknüpfung zum (universellen) Projekt | | `lead_contact` | Kontaktperson(en) eines Leads | | `lead_touchpoint` | Austausch-Log eines Leads (Kanal, Outcome, Follow-up) | | `deal` | Pipeline-Deal; wird per Lead→Deal-Promotion erzeugt | | `gtm_step`, `campaign`, `lead_handover` | Im Schema vorhanden (Phase-2-Vorbereitung), kein UI | | `time_entry` | Zeiterfassungs-Block, optional an ToDo gebunden, Partial-Unique-Index erzwingt max. einen aktiven Timer pro User | Migrationen liegen in `packages/db/migrations/*.sql` und werden über `drizzle-kit migrate` angewendet. Schema-Änderungen brauchen **immer eine neue Migration** — kein Schema-Drift. --- ## API-Surface ### REST-Routen (Fastify) | Methode | Pfad | Zweck | | ----------- | ----------------------- | ----------------------------------------------------------------------------------------------------------- | | `GET` | `/api/health` | DB- und Authentik-Reachability-Check | | `GET` | `/api/me` | Eingeloggter User (oder 401) | | `GET` | `/api/auth/login` | Startet OIDC-Flow (Redirect zu Authentik) | | `GET` | `/api/auth/callback` | Code-Exchange + Session-Create | | `POST` | `/api/auth/logout` | Session löschen + Cookie clearen | | `GET\|POST` | `/api/auth/dev-login` | **Nur Dev-Modus**: Auto-Login als Dejan | | `POST` | `/trpc/` | tRPC-Endpunkt (Batch + Stream) | | `POST` | `/internal/cron/` | Bearer-Token-geschützt: publish-day-entries, recompute-streaks, recompute-pflicht-minimum-5, pg-dump-backup | ### tRPC-Router (`apps/api/src/routers/_app.ts`) ``` health publicProcedure user me, list, markAbsent area list project list, byAreaSlug, byId, create, update, archive, setMilestoneStatus, addMilestone, updateMilestone, removeMilestone, setModus, setPhase, addPhaseGate, phaseGates, addStatus, statusHistory, listArchived lead list, byId, forProject, create, update, addContact, removeContact, addTouchpoint, removeTouchpoint, promoteToDeal, importCsv risk forProject, create, update, remove stakeholder forProject, create, update, remove change forProject, create, update, remove deal list, kpis, create, updateStage gtm list, create, setStatus (kein UI, Phase 2) campaign list, create, funnel, crosslink (kein UI, Phase 2) blocker listOpen, byProject, create, resolve dayEntry today, todayTeam, pendingCarryOver, checkIn, updateTodos, editTodoTitle, addTodo, submitReflection, feed, todayPresence time now, start, stop, listToday, update, upsertManual, delete, teamToday dashboard overview ``` Frontend importiert den Typ via `import type { AppRouter } from '@cockpit/api/router'` (siehe `apps/web/src/lib/trpc.ts`). ### Procedure-Typen - `publicProcedure` — kein Auth-Check - `protectedProcedure` — wirft `UNAUTHORIZED` wenn `ctx.user === null` Hinweis: `dayEntryVisibleProcedure` und `projectOwnerProcedure` waren ursprünglich geplant (siehe Architektur-Spec), sind aber implizit in den Routern erfüllt (z. B. `blocker.resolve` prüft P1 manuell). --- ## Authentication ### Produktion: Authentik OIDC Der Code ist vollständig vorhanden — was fehlt sind die Authentik-seitigen Daten: 1. Authentik-Admin legt Provider + Application an (siehe `docs/operations.md` § Authentik-Setup). 2. Client-ID/Secret/Issuer-URL fließen in `.env.production`: ``` AUTHENTIK_ISSUER=https://auth.tool-ctrl.de/application/o/everythingbutwork/ AUTHENTIK_CLIENT_ID= AUTHENTIK_CLIENT_SECRET= AUTHENTIK_REDIRECT_URI=https://everythingbutwork.tool-ctrl.de/api/auth/callback ``` 3. Flow: `openid-client v6`, Authorization Code + PKCE, Discovery via `/.well-known/openid-configuration`. Code: `apps/api/src/auth/oidc.ts`. **Bekannte offene Stelle (OQ-1)**: Der `groups`-Claim wird aus dem Token gelesen, aber **noch nicht** auf `user.default_area_id` gemappt. Heißt: neue User landen ohne Default-Bereich. Mapping kann später in `auth/user.ts:upsertUserFromOidc` ergänzt werden. ### Dev: Auto-Login Im `NODE_ENV=development` ist `/api/auth/dev-login` aktiv. Die Frontend-Login-Route (`/login`) redirected dort automatisch hin, sodass man nie eine Login-Seite sieht. Logik: - Sucht den User mit `email = 'dejan@tool-ctrl.de'`. - Existiert er nicht, wird er via `upsertUserFromOidc` mit `sub='local-dejan'` angelegt. - Session wird gesetzt, Redirect auf `/dashboard`. In Production ist diese Route nicht registriert, weil die `if (env.NODE_ENV === 'development')`-Guard in `apps/api/src/auth/routes.ts` dann false ist. ### Session-Modell - Postgres-Tabelle `session` (id, user_id, expires_at, ip_address, user_agent, created_at) - Cookie: `cockpit_session`, HttpOnly + SameSite=Lax + (`Secure` in Prod) - TTL: 24h (Env: `SESSION_TTL_HOURS`) - Stop-condition: jedes Request läuft durch `auth/middleware.ts` → `getSessionWithUser(sessionId)` → setzt `req.cockpitUser` --- ## Frontend-Architektur ### Routing React Router v7 (`createBrowserRouter`) — alle Routen sind **Lazy** (Code-Split per Route). Definition in `apps/web/src/lib/router.tsx`. Login-Route ist außerhalb der `AppShell` (`/login`). Alle übrigen Routen unter `/` rendern die `AppShell` (TopNav + Main + Outlet + BlockerDialog). ### State - **Server-State**: ausschließlich React Query via tRPC (`createTRPCReact`). Konfig in `apps/web/src/lib/trpc-provider.tsx` (**staleTime 0** → Daten werden bei Mount/Tab-Fokus neu geholt, kein Polling; ein Retry; zentrales onError → Toast). Mutationen invalidieren zusätzlich gezielt die gleichzeitig sichtbaren Sichten. - **Auth-State**: React Query `user.me` (Hook `useCurrentUser`). - **Lokaler UI-State**: `useState` in der Komponente. Kein globaler Store. ### Brand & Voice - **Logo**: ToolCTRL-Emblem (`apps/web/public/toolctrl-emblem.svg`) + Wordmark `everythingbutwork`. - **Hausschrift**: Space Grotesk (selbst gehostet in `apps/web/public/fonts/`). - **Buttons**: Keycap-Stil (Gradient + Druckeffekt + Tastenkante) — siehe `apps/web/src/styles/globals.css` § Keycap-Buttons. - **Mikrocopy**: zentral in `apps/web/src/lib/voice.ts`. Tonalität locker/direkt/deutsch (mit zwei englischen Lehnwörtern: „Lock in" und „Locked in" statt „Einstempeln/Eingestempelt"). - **Ampel-Farben**: `green=#15803d/#dcfce7`, `amber=#a16207/#fef3c7`, `red=#b91c1c/#fee2e2` (UX-Spec § Visual Design Foundation). ### Keyboard-Shortcuts In `apps/web/src/components/layout/AppShell.tsx`: ``` g d Dashboard g b Bereich (Default Entwicklung) g e Entwicklung g v Vertrieb g m Marketing g h Heute g a Archiv n Neuer Blocker ? Hilfe-Cheatsheet ``` --- ## Geteilte Logik (`packages/shared`) Zod-Schemas + zugehörige TS-Types werden über `@cockpit/shared` re-exportiert. Beispiele: ```ts import { DayEntrySchemas, TimeEntrySchemas, ProjectSchemas } from '@cockpit/shared'; // Im Backend (tRPC): checkIn: protectedProcedure.input(DayEntrySchemas.checkInInput).mutation(...) // Im Frontend (React Hook Form): const resolver = zodResolver(TimeEntrySchemas.manualTimeEntryInput); ``` Konstanten (`MAX_TODO_TEXT_LENGTH`, `WERKTAGE`, `JOURFIXE_DAY`, `REFLEXION_REMINDER_HOUR`) in `packages/shared/src/constants.ts`. --- ## Produktion / Deployment ### Docker Compose `docker-compose.yml` definiert vier Services: | Service | Image | Rolle | | ---------- | ------------------------------- | ----------------------------------------------------------------------------- | | `postgres` | `postgres:16-alpine` | DB, persistentes Volume `cockpit_postgres_data`, Backups in `cockpit_backups` | | `api` | Build aus `apps/api/Dockerfile` | Fastify auf 3000, hängt an Postgres-Health | | `web` | Build aus `apps/web/Dockerfile` | Vite-Build wird in `cockpit_web_dist`-Volume gelegt | | `caddy` | `caddy:2-alpine` | Reverse-Proxy (80+443), TLS via Let's Encrypt, Caddyfile mountet | Env-Variablen werden über `.env.production` injiziert. Required: `POSTGRES_PASSWORD`, `SESSION_COOKIE_SECRET` (mind. 32 Z.), `CRON_BEARER_TOKEN`, `AUTHENTIK_*`. ### Caddyfile - `cockpit.tool-ctrl.de` als Host (per `${COCKPIT_HOST}` overridbar) — wird beim nächsten Deploy auf `everythingbutwork.tool-ctrl.de` gesetzt - `/internal/*` wird **extern geblockt** (403) — Cron-Endpunkte nur internes Netz - `/api/*` und `/trpc/*` reverse-proxy zu `api:3000` - Statischer Frontend-Content aus `/srv` - Standard-Security-Header (HSTS, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) ### Cron im Postgres-Container `scripts/backup.sh` läuft als System-Cron im Postgres-Container: tägliches `pg_dump` mit 14-Tage-Retention + Heartbeat an `/internal/cron/pg-dump-backup` für Audit. ### CI & Auto-Deploy `.gitea/workflows/ci.yml`: ``` quality → install, typecheck, lint, format:check, test build → install, web build, api build (needs quality) ``` `.gitea/workflows/deploy.yml` (Runner `runs-on: host` auf dem Homeserver): **bei jedem Push auf `main`** → `git reset --hard origin/main`, `docker compose up -d --build`, dann Warten bis `cockpit-api` healthy. Es gibt **keinen manuellen Trigger** — ein (ggf. leerer) Push löst den Deploy aus. **Startup-Verhalten der API** (`apps/api/src/server.ts`): vor dem Listen wird 1. `runMigrations()` ausgeführt (Drizzle-Migrationen idempotent; schlägt eine fehl, startet die API nicht), dann 2. `ensureBaseAreas()` (legt die 4 Basis-Bereiche idempotent an, damit z. B. der „Neues Projekt"-Dropdown nie leer ist — auch auf einer nicht geseedeten Prod-DB). --- ## Konventionen — Pflicht-Lesen vor Code-Edits **`CONVENTIONS.md`** im Repo-Root. Zusammengefasst die wichtigsten Punkte: - **DB**: Singular `snake_case` Tabellen, `id` als UUID, FK `_id`, Enums als `text` + check. - **tRPC**: Procedure-Naming `.` (camelCase). Inputs **immer** Zod aus `@cockpit/shared`, niemals inline. - **Errors**: `TRPCError` mit **deutschen** menschenlesbaren Messages. Frontend zeigt sie via zentralem onError-Toast. - **State**: Server-State ausschließlich React Query — niemals `useState` für Server-Loading. - **Werktage/Zeit**: Alle Datumslogik im Backend (`apps/api/src/lib/werktag.ts`). Server-Zeit `Europe/Berlin`. - **Sichtbarkeit**: Heutige ToDos und Zeit sind **teamweit sichtbar** (Änderung gegenüber Original-PRD). Vortags-Reflexionen werden via Cron publiziert. - **No-Gos**: kein Kanban, kein Steckbrief-Editor, kein Plane-Issue-Spiegel — die Wahrheit dieser Dinge lebt in Plane/Outline. --- ## Was bewusst NICHT gebaut ist (Phase 2) - **Plane-/Outline-API-Sync** — Cockpit verlinkt nur, hängt sich nichts ab - **Kanban-Pipeline / Deal-Funnel-UI** — `gtm`/`campaign` ohne UI (Lead-CRM ist gebaut) - **Reportings / Wochen-/Monatsauswertungen** — bewusst minimal - **Reaktionen / Kommentare** auf Tagesupdates - **Mobile schreibbar** — Desktop-first, mobil nur lesbar - **Volltextsuche** über Updates - **Kimai-Sync** — Kimai läuft parallel ohne Bezug (eigener Pfad) - **Loki/Grafana, Sentry** — pino → stdout reicht erstmal - **Authentik-Group → Bereich-Mapping** (OQ-1, siehe Auth-Abschnitt) --- ## Planungs-Artefakte Vollständige PRD/Architektur/UX/Stories im `_bmad-output/planning-artifacts/`-Ordner. Diese Artefakte sind teilweise historisch (z. B. Kanban war ursprünglich geplant, ist aber inzwischen aus dem UI raus) — der Code ist die aktuelle Quelle der Wahrheit. Die Artefakte sind nützlich für: - Hintergrund zur Methodik (ToolCTRL-Leitfaden v3.0) - Entscheidungs-Log (`prds/prd-projectmanagement-2026-05-25/.decision-log.md`) - Ursprüngliche User Journeys - Anti-Ziele und Roadmap-Hinweise | Datei | Inhalt | | ---------------------------------------------- | --------------------------------------- | | `prds/prd-projectmanagement-2026-05-25/prd.md` | PRD (49 FRs, 16 NFRs) | | `architecture.md` | Stack-Entscheidungen, Patterns | | `ux-design-specification.md` | Brand-Tokens, Komponenten, Voice & Tone | | `epics.md` | 6 Epics, 32 Stories | --- ## Repo & Remote - **Default-Branch**: `main` - **Remote**: `ssh://git@git.hg-int.dev:2222/toolctrl-labs/everythingbutwork.git` (Gitea, non-standard SSH-Port) - **Gitignore**: `.env`, `node_modules`, `dist`, `_bmad/`-Tools (Bmad-Framework), `.claude/`, `ToolCTRL Brand Identity/` (215 MB Raw-Assets) --- ## Quick-Wins fürs nächste Modell / die nächste Session 1. **Sanity-Check vor jedem Edit**: `git status`, dann lies `CONVENTIONS.md`. 2. **Schema ändern?** → neue Drizzle-Migration generieren (`pnpm --filter @cockpit/db migrate:generate --name=<...>`) und applien. NIEMALS direkt am DB-Snapshot rumdoktern. 3. **Neuer tRPC-Endpoint?** → Zod-Schema in `packages/shared/src/schemas/.ts`, dann Procedure im passenden Router. Frontend importiert den Typ automatisch über `AppRouter`. 4. **Englisch-Verbot in UI-Strings** — Ausnahmen: „Lock in", „Locked in" (vom Owner so gewollt), Fachbegriffe wie „Kanban" (im Leitfaden gesetzt). 5. **Postgres-Port ist 55432, nicht 5432**. Ports sind aus Kollisionsgründen so gewählt: API 7100, Web 7173, DB 55432. 6. **Dev-User**: `dejan@tool-ctrl.de` (Admin). Wird im Auto-Login angelegt falls nicht vorhanden. 7. **Beim Bauen neuer Features**: schau wie ähnliche Features umgesetzt sind (z. B. Time-Tracking als Vorbild für strukturähnliche Domain-Slices: Schema → Shared-Zod → Router → Frontend-Komponente).