UI/Routing - Tab + Route: /puls → /dashboard, Heading „Unternehmens-Puls" → „Dashboard" - Shortcut: g p → g d (Cheatsheet entsprechend) - Breadcrumbs, 404-Hinweistext, alle internen Links angepasst Code-Layout - apps/web/src/routes/puls.tsx → dashboard.tsx (Komponente DashboardRoute) - apps/api/src/routers/puls.ts → dashboard.ts (dashboardRouter) - tRPC-Aufrufe: trpc.puls.* / utils.puls.* → trpc.dashboard.* / utils.dashboard.* - Auth-Redirects (/api/auth/login, /callback, dev-login) zeigen jetzt auf /dashboard Projekt-Beschreibung - Migration 0007_project_description: project.description text (nullable) - Shared Zod: ProjectSchemas.createProjectInput.description (max 2000, optional) - Backend: project.create mappt input.description - Frontend Dialog „Neues Projekt": Textarea (3 Zeilen, optional) - Projekt-Detail: zeigt Beschreibung als whitespace-pre-wrap unter Subtitle README - Routen-Tabelle, Shortcut-Liste, Verzeichnis-Baum, DB-Tabellen, Migration-Range, Frontend-Routenliste aktualisiert Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
434 lines
21 KiB
Markdown
434 lines
21 KiB
Markdown
# 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, „Heute fehlt"-Liste, bereichsübergreifende Blocker, Pflicht-Minimum-Übersicht, Jourfixe-Hinweis, „Heute im Team" (ToDos), „Zeit im Team heute" (Live-Tracking), Vortags-Feed |
|
|
| **Bereich** (`/bereich`, `/bereich/<slug>`) | Tab-Switcher über Entwicklung / Vertrieb / Marketing — pro Bereich Projekt-Liste (Kundenprojekte / Interne Projekte) mit Pflicht-Minimum-Indicator |
|
|
| **Projekt** (`/projekt/:id`) | Detail: Verlinkungen (Outline-Steckbrief, Plane-Board, Risikoliste), Pflicht-Minimum-Status, Meilenstein-Checkliste, DoD, Projekt-Abschluss (P1) |
|
|
| **Heute** (`/heute`) | Tagesrhythmus: Lock-in mit ToDo-Liste, Auto-Carryover offener ToDos vom Vortag, ToDo-Editor, Zeiterfassungs-Timeline mit Edit/Delete/Nachtrag |
|
|
| **Reflexion** (`/reflexion`) | Abend-Reflexion: ToDos abhaken, Freitext, Ampel + Trend, optional Blocker. Editierbar bis 00:00 Berlin |
|
|
| **Archiv** (`/archiv`) | Abgeschlossene Projekte, gruppiert nach Bereich |
|
|
|
|
**Querschnittsfunktionen**
|
|
|
|
- **Zeiterfassung**: Persistente Timer-Pill in der TopNav (Live-Sekundenzähler), Quick-Picker mit ToDos + Kategorien + Freitext, retroaktiver Nachtrag-Dialog, Bindung an ToDo optional. Personenübergreifend sichtbar im Dashboard.
|
|
- **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, einer davon (Status-Ampel aktuell) wird stündlich per Cron aus den Reflexions-Zeitstempeln aktualisiert.
|
|
- **Streak**: aufeinanderfolgende Werktage mit voller Reflexion. 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
|
|
│ │ │ ├── routes/ dashboard, bereich, projekt, heute, reflexion,
|
|
│ │ │ │ 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), ampel, pflicht-minimum, logger
|
|
│ ├── routers/ _app, user, area, project, deal, gtm,
|
|
│ │ 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}.ts
|
|
│ │ ├── migrations/ 0000_init … 0007_project_description
|
|
│ │ └── drizzle.config.ts
|
|
│ └── shared/ Zod-Schemas + Konstanten + Types
|
|
│ └── src/schemas/ day-entry, project, deal, campaign, time-entry
|
|
├── _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=<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 0007)
|
|
|
|
| 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 für jede mutierende Operation |
|
|
| `area` | Bereich (entwicklung / vertrieb / marketing / allgemein) |
|
|
| `project` | Projekt: Name, optionale `description`, Outline-/Plane-URLs, Arbeitsmodus (cycle/kanban), `pflicht_minimum_state` JSONB, Status-/Trend-Cache, Archiv-Felder |
|
|
| `milestone` | Cockpit-Sicht-Meilenstein pro Projekt |
|
|
| `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 |
|
|
| `deal`, `gtm_step`, `campaign`, `lead_handover` | Im Schema vorhanden (Phase-2-Vorbereitung), **kein UI** mehr |
|
|
| `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 (mit area), create, archive,
|
|
setMilestoneStatus, listArchived
|
|
deal list, kpis, create, updateStage (kein UI, Phase 2)
|
|
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, 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` (30 s staleTime, ein Retry, zentrales onError → Toast).
|
|
- **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
|
|
|
|
`.gitea/workflows/ci.yml`:
|
|
|
|
```
|
|
quality → install, typecheck, lint, format:check, test
|
|
build → install, web build, api build (needs quality)
|
|
```
|
|
|
|
---
|
|
|
|
## 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 / Lead-Funnel-UI** — Schemas existieren, aber UI raus
|
|
- **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).
|