Raffael SheikhandClaude Opus 4.8 f600c9f187 UI: alle 8 Dialoge auf barrierearme Modal-Basis umgestellt
Bisher waren alle Overlay-Dialoge plain divs ohne role/Fokus-Trap/Escape.
Jetzt setzen sie auf <Modal> auf (role=dialog, aria-modal, Fokus-Trap +
-Rueckgabe, Escape, Body-Scroll-Lock, konsistenter X-Button, Scroll bei langem
Inhalt).

- Modal: X-Schliessen-Button + max-h-[90vh]/overflow-y-auto ergaenzt
- BlockerDialog, ManualDialog (Timeline)
- leads: CSV-Import + Neuer Lead
- bereich: Neues Projekt
- projekt: Gate-Review + Projekt abschliessen
- lead: Zu Deal machen

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 11:49:05 +02:00

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/<slug>) 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)

# Repo & Tooling
pnpm install            # 4 Workspaces installieren
cp .env.example .env    # Defaults reichen für lokale Entwicklung

Dev-Stack starten

# 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

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=<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/<procedurePath> tRPC-Endpunkt (Batch + Stream)
POST /internal/cron/<job> 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=<von Authentik>
    AUTHENTIK_CLIENT_SECRET=<von Authentik>
    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<AppRouter>). 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:

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 <entity>_id, Enums als text + check.
  • tRPC: Procedure-Naming <feature>.<verbNoun> (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.tool-ctrl.de: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/<feature>.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).
S
Description
No description provided
Readme
1.7 MiB
0 Stars 9 Watchers 0 Forks
Languages
TypeScript 85.1%
HTML 11.1%
Python 2%
CSS 0.8%
Dockerfile 0.4%
Other 0.6%