naechsterSchrittDatum wurde erfasst, aber nie als faellige Liste geb√ºndelt —
warme Leads gingen unter. Neue Query lead.dueFollowUps (aktive Leads mit
faelligem/ueberfaelligem naechsten Schritt) + "Faellige Wiedervorlagen"-Block
oben auf /leads.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
time_entry erfasst bezugKind='project'+bezugId, aber niemand sah je, wie viel
Zeit ein Projekt frisst. Neue Query time.byProject (Gesamtsumme + pro Person);
neue "Getrackte Zeit"-Sektion im Projekt-Detail (blendet sich aus, wenn nichts
getrackt wurde).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
withAudit war als tRPC-Wrapper definiert, aber an keine Procedure gehaengt
(audit_event blieb leer). Jetzt eine echte tRPC-v11-Middleware-Procedure
(auditedProcedure), die nach erfolgreicher Mutation best-effort einen
audit_event schreibt (Fehler beim Audit kippen die Mutation nicht).
Verdrahtet an die folgenreichen Zustandsuebergaenge:
- blocker.resolve
- project.archive, project.setPhase, project.addPhaseGate
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bisher reine uuid-Spalten ohne referenzielle Integritaet -> Orphan-Risiko, wenn
das Ziel geloescht wird. Jetzt echte FKs mit ON DELETE SET NULL:
- day_entry.blocker_id -> blocker
- blocker.area_id -> area
- blocker.project_id -> project
Migration bereinigt vorab verwaiste IDs (UPDATE ... SET NULL), damit ADD
CONSTRAINT auf Altdaten nicht bricht. Verifiziert: alle 18 Migrationen laufen
gegen frische Postgres 16 durch, FKs angelegt.
(todo.bezug_id / time_entry.bezug_id bleiben FK-frei: echt polymorph ueber
bezug_kind project/deal/campaign — kein einzelner FK moeglich.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Edit/Delete/Start/Zeit-Buttons waren nur per group-hover sichtbar (opacity-0)
und damit auf Touch-Geraeten unerreichbar. Jetzt mobil dauerhaft sichtbar, ab
sm (Desktop) weiterhin Hover-Reveal.
- TimelineSection (Bearbeiten/Loeschen einer Zeit-Zeile)
- heute (Task starten/fortsetzen, Zeit nachtragen)
- reflexion (ToDo-Titel bearbeiten)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Vorbereitung der Modal-Umstellung: TimelineSection/leads/bereich (Alt-Bestand
nie formatiert) + kleiner B3-Nachzug in projekt/lead. Reine Formatierung, damit
die folgenden Modal-Commits nur echte Aenderungen zeigen.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dashboard/Projekt/Lead/Team/Historie rendern bei fehlgeschlagener Query nicht
mehr ein stilles `return null` (leere weisse Seite), sondern einen Hinweis mit
"Neu laden"-Button.
- Neue Komponente ErrorState (Meldung + optionaler Retry)
- 5 Routen: if (q.isError || !q.data) -> ErrorState mit q.refetch()
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diese drei Dateien waren nicht prettier-konform (nie formatiert). Isolierter
Format-Lauf, damit der folgende Logik-Commit nur die echten Aenderungen zeigt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fundament für Paket B (UI/UX-Härtung):
- Modal: barrierearmer Basis-Dialog (role/aria-modal, Fokus-Trap, Escape,
Fokus-Rückgabe, Body-Scroll-Lock, Portal). Basis für alle App-Dialoge.
- ConfirmProvider/useConfirm: imperative Bestaetigung (await confirm({...}))
statt window.confirm(); wird als Naechstes an die Loeschpfade gehaengt.
- Feierabend-Dialog: Tagesabschluss mit Zusammenfassung (erledigt/offen/
verworfen) + Hinweis auf offene/unreflektierte ToDos und fehlende Reflexion.
Check-Out feuert erst nach Bestaetigung; bleibt reversibel.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nachtarbeit über Mitternacht brach bisher den Check-Out ("Du bist heute
noch nicht locked in") und sperrte nach versehentlichem Feierabend den Tag.
- Tag = Kalendertag des Check-ins; eine über Mitternacht laufende Session
zaehlt zum Starttag, bis 06:00 des Folgetags (isWorkdaySealed)
- checkOut/today finden den offenen Eintrag statt stur "heute"
- reopen ("Arbeit fortsetzen") hebt einen Feierabend wieder auf
- applyWorkdayBoundary: nahtloser 6-Uhr-Schnitt (Auto-Checkout + Folge-Tag,
offene ToDos wandern mit)
- Reflexion/ToDo-Edit folgen der 6-Uhr-Grenze statt Mitternacht
- Cron apply-workday-boundaries fuer Team-Dashboard-Konsistenz
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Der aktuelle Projektstatus hing allein an der statusHistory-Query. Kam
die beim Laden mal leer oder fehlerhaft zurück, stand fälschlich "Noch
kein Status gesetzt", obwohl der statusCache (aus demselben byId-Load)
den Status hatte. Jetzt: Verlauf vorhanden -> volle Anzeige; sonst
Fallback auf statusCache-Badge; "Noch kein Status" nur wenn die Query
erfolgreich leer ist. Zusätzlich Lade-/Fehlerzustand.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Label + Platzhalter angepasst: jeder fasst im Fazit zusammen, was er
gemacht hat. Damit ergibt auch der Text in "Gestern im Team" Sinn.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deadline lässt sich jetzt direkt im Projekt-Header bearbeiten (Inline-
Datepicker mit Freitag/Wochenende-Warnung), via project.update. Der
Arbeitsmodus (Kanban/Cycle) ist in ebw bedeutungslos und fliegt aus der
Projekterstellung und der Header-Zeile raus. DB-Spalte bleibt ungenutzt
bestehen (keine destruktive Migration nötig).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Logo erscheint jetzt zusätzlich bei "Anstehende Meilensteine" (neben dem
Projekt-Link) und bei "Pflicht-Minimum offen" (vor dem Namen). Dashboard-
Queries liefern dafür project.logoUrl mit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Logo kann bei der Projekterstellung und nachträglich im Projekt-Header
hochgeladen werden. Client-seitiger Center-Crop auf 256px WebP, gespeichert
als Data-URL in neuer Spalte project.logo_url (Migration 0016). Anzeige in
der Bereichs-Projektliste (Card) und im Detail-Header, mit Initialen-
Platzhalter wenn kein Logo gesetzt ist.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Im Dashboard-Feed wird je User nur noch die aktuellste Reflexion als
eine Card gezeigt (Dedup über userId, feedRows ist bereits nach Datum
absteigend sortiert). Ältere Einträge sieht man weiterhin unter
"Ganze Historie". Angezeigter Text ist der Freitext "Sonst noch was?"
(reflectionText).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Die Bereiche (Entwicklung/Vertrieb/Marketing/Allgemein) raus aus dem
Dashboard. AmpelDot + Ampel-Typ wurden dadurch ungenutzt, ebenfalls
entfernt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Projektübergreifende Liste offener/überfälliger Meilensteine mit Datum,
sortiert nach Fälligkeit. Überfällige rot markiert, relative Tagesangabe
("in 3 Tagen" / "überfällig"), direkt verlinkt aufs Projekt. Sitzt oben
zwischen Showstoppern und kritischen Modi.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Risikoliste und Lessons Learned konnten bisher nur beim Anlegen bzw.
Abschluss gesetzt werden. Jetzt ist die ganze Verlinkungen-Karte über
"Bearbeiten" editierbar (Outline, Plane, Risikoliste, Lessons Learned).
Leeres Feld entfernt den Link, URLs werden serverseitig validiert.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
User-sichtbare Hinweise (Meilenstein-Mindestanzahl, Stakeholder-Empty,
Freitags-Warnung) ohne Quellenangabe umformuliert; Dev-Kommentare von
"Buch Kap. X" auf "Leitfaden" geändert.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Daten-Migration entfernt alle alten 'break'-Zeiteinträge (das Pause-Feature
wurde abgeschafft). Sie erschienen sonst als graue Blöcke in der Team-Timeline.
Läuft beim Deploy automatisch über runMigrations.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pause-Feature entfernt: kein Pause-Button, kein break-Buchen mehr. Man bucht
nur Tasks; stoppt man eine Task und es läuft keine andere, ist die Lücke
automatisch eine Pause — im Kalender/Tagesansicht schlicht leer. Tasks bleiben
pausierbar (Stop = Pause, erneut Start = Fortsetzen → mehrere Segmente auf
dieselbe Task, Lücke dazwischen leer). Wall-Clock zählt Lücken ohnehin nicht.
- time.pause-Endpoint entfernt; 'break' aus der angebotenen Kategorienliste
raus (bleibt im Enum für Altdaten).
- TimerPill: Pause-Button + Pause-Panel-Grund entfernt; Stop/Start je Task bleibt
(= pausieren/fortsetzen). Heute-ToDo-Zeile entsprechend beschriftet.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Arbeitsantritt = bestehender Lock-in (setzt checkedInAt); neu dazu:
- Check-Out / Feierabend: dayEntry.checkedOutAt + Endpoint dayEntry.checkOut;
Heute-Seite zeigt „Arbeitsantritt HH:MM" und einen „Feierabend / Check-Out"-
Button, danach Antritt/Feierabend/Dauer.
- Kimai-Anbindung (config-gated, best-effort, nicht blockierend): lib/kimai.ts
mit clockIn/clockOut. Check-In startet ein Kimai-Timesheet, Check-Out stoppt
es; ID + Sync-Status auf dem dayEntry (kimaiTimesheetId, kimaiSynced).
No-Op solange KIMAI_BASE_URL/API_TOKEN/PROJECT_ID/ACTIVITY_ID nicht gesetzt.
Migration 0014 (checkedOutAt, kimaiTimesheetId, kimaiSynced). Env-Vars als
optional ergänzt + in .env.example dokumentiert.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mehrere Tasks gleichzeitig tracken. Unique-Index "ein aktiver Timer pro User"
entfernt (Migration 0013); time.start stoppt den laufenden nicht mehr, sondern
läuft parallel.
- time.now liefert jetzt ALLE laufenden Timer (Array) inkl. ToDo-Titel.
- time.stop({id}) stoppt einen bestimmten Timer; time.stopAll stoppt alle.
- time.pause stoppt ALLE laufenden Tasks und startet eine Pause (passt zum
Pause-Ticket „alle laufenden Tasks pausiert").
- TimerPill: zeigt Anzahl laufender Timer; Panel listet alle mit Live-Zähler +
einzelnem Stop, „Weiteren Task starten", Grund-Nachtrag für Pausen.
- Heute-ToDo-Zeile: Start/Stop pro ToDo unabhängig (Array-basiert).
Zusammenspiel: Team-Timeline zeigt parallele Stränge, „Gesamt"-Zeit zählt
Überlappung weiterhin nur einmal (Wall-Clock).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drive-by-Aufgaben ("btw, Yevgen-Security-Fix hab ich auch noch gemacht")
strukturiert erfassen statt nur im Freitext. Neues Feld todo.unplanned +
Endpoint dayEntry.addUnplanned: legt sofort ein erledigtes, als „ungeplant"
markiertes ToDo an.
- Schnell-Eingabe „Ungeplant erledigt" auf Heute (Mid-Day) und in der
Reflexion (End-of-Day-Recall).
- "ungeplant"-Badge in Heute, Reflexion und Historie.
- historyForDate liefert das unplanned-Flag mit.
- Reflexions-Freitext bleibt für die Tages-Story.
Migration 0012 (todo.unplanned).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Neue Seite /team (Tab „Team", g t): horizontale Tages-Timeline mit einer Spur
pro Person. Zeit-Blöcke werden nach Start/Ende auf einer Stundenachse
positioniert, eingefärbt nach Kategorie; überlappende Blöcke landen auf
parallelen Sub-Spuren ("parallele Zeitstränge"). Laufende Blöcke (heute) ticken
live und sind rot umrandet. Datums-Navigation (Tag vor/zurück, Picker, „Heute"),
Hover-Tooltip mit Label/Zeitraum/Dauer, Kategorie-Legende.
Endpoint time.teamTimeline({date?}) liefert pro Person die Roh-Einträge
(Start/Ende/Kategorie/Label); Achse & Sub-Spuren rechnet das Frontend.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Neue Seite /historie: pro vergangenem Tag sieht man, was jede/r gemacht hat —
Reflexion (Ampel/Trend/Text) + ToDos (erledigt/offen/verworfen), plus Liste
„ohne Eintrag". Datums-Navigation (vorheriger/nächster Werktag, Datum-Picker);
Default = vorheriger Werktag. Heutige Reflexionen bleiben privat (nur ToDos).
- Endpoint dayEntry.historyForDate({date?}) liefert Einträge + prev/next-Werktag.
- TopNav-Tab „Historie" (g i), Link „Ganze Historie →" im Dashboard-Feed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Der Vortags-Feed verlangte published_at IS NOT NULL, gesetzt nur vom Cron
publish-day-entries. Auf Prod ist dieser Cron nicht eingeplant (keine crontab/
kein Scheduler) → published_at blieb bei allen echten Reflexionen null → Feed
immer leer (auf Dev sah's gut aus, weil der Seed published_at setzt).
Feed jetzt vom Cron entkoppelt: zeigt volle Reflexionen mit Datum < heute
(heutige bleiben privat bis morgen), sortiert nach Datum/Reflexionszeit.
Gilt für dashboard.overview.feed und dayEntry.feed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ein um 23:00 gestarteter, noch laufender Timer hatte Start-Datum = gestern und
fiel nach Mitternacht aus listToday/teamToday (Filter nach Start-Datum=heute) —
nur die TimerPill zeigte ihn noch.
- listToday/teamToday: Filter „overlapsToday" = heute gestartet ODER heute
beendet (Mitternacht überschritten) ODER laufend → Eintrag bleibt sichtbar.
- Tages-Summen clampen den Anteil auf den heutigen Tag (berlinDayStartMs,
DST-korrekt) → über Mitternacht gelaufene Blöcke werden nicht falsch zugezählt.
- ToDo-Titel werden tagesübergreifend serverseitig aufgelöst (time.now +
listToday) → Task bleibt benannt, auch wenn das ToDo von gestern ist.
- Timeline zeigt das Start-Datum, wenn ein Eintrag nicht heute begonnen hat.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
"Failed to fetch dynamically imported module" trat auf, wenn ein offener Tab
nach einem Deploy auf alte (gehashte, inzwischen gelöschte) Route-Chunks zeigt.
Jetzt:
- lazyWithReload() lädt bei fehlgeschlagenem Lazy-Import einmal automatisch neu
(holt frische index.html); sessionStorage-Guard verhindert Reload-Schleifen.
- vite:preloadError-Event wird ebenfalls abgefangen (modulepreload-Fehler).
- errorElement (RouteError) ersetzt die Dev-Default-Fehlerseite durch eine
saubere "Neu laden"-Ansicht als Fallback.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pause-Button in der Timer-Pill (sichtbar im Leerlauf und während ein Task
läuft): ein Klick stoppt den laufenden Task und startet sofort eine Pause
(break-Eintrag) — bis man wieder eine Aufgabe aktiviert. Läuft eine Pause,
zeigt die Pill sie mit Live-Zähler; ein Panel erlaubt optional einen Grund
nachzutragen (z. B. Bürofahrt, Essen), „Weiter mit Aufgabe" oder „Pause
beenden". Grund ist optional — Pause kann einfach Pause sein.
Reine Frontend-Änderung (nutzt time.start mit category 'break'), keine Migration.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Backend:
- Vortags-Feed (dashboard.overview + dayEntry.feed) sortierte aufsteigend
mit limit(50) → zeigte die ÄLTESTEN statt neuesten Einträge. Jetzt desc.
- deal.updateStage: wonValueEur wurde beim Gewinnen mit der Wahrscheinlichkeit
gewichtet → zu niedriger Won-Umsatz. Jetzt voller Auftragswert.
- Pflicht-Minimum „Status aktuell" wurde global (letzte Reflexion irgendwo)
statt pro Projekt berechnet. Jetzt je Projekt aus dem jüngsten Statusbericht.
- time.listToday/teamToday nutzten fixen +02:00-Offset → im Winter (CET) um
1h verschobene Tagesgrenze. Jetzt DST-sicher via AT TIME ZONE 'Europe/Berlin'.
Frontend:
- lead.tsx + projekt.tsx: Datumsanzeigen nutzten UTC (toISOString) → abends
Off-by-one. Jetzt Berlin-lokal formatiert.
- reflexion.tsx: Submit mappte nur den initialen ToDo-Snapshot → mitten am Tag
ergänzte ToDos verloren ihren Status. Jetzt über die aktuelle Liste.
- leads.tsx: CSV-Zeilenzähler konnte „-1" zeigen (Math.max).
- TimerPill: Label ohne Control (a11y-Lint-Error, war repo-weit rot) + unused
clsx-Import behoben → pnpm lint jetzt 0 Errors.
DB: seed.ts unbedingten updatedAt-Bump aller User entfernt (Debug-Rest).
README: vollständig auf aktuellen Stand gebracht (Leads/CRM, CSV-Import,
PM-Tiefe, Phasen/Status/Risiken/Stakeholder/Änderungen, Tabellen bis 0011,
tRPC-Router, staleTime 0, Auto-Deploy-Runner + Startup-Migrationen).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ein Projekt lebt in genau einem Heimat-Bereich und wird einmal angelegt;
Leads bekommen eine optionale Verknüpfung dorthin (lead.projectId), egal in
welchem Bereich das Projekt wohnt — kein redundantes Anlegen mehr.
- Migration 0011: lead.project_id (FK → project, ON DELETE SET NULL).
- Lead anlegen / CSV-Import / Lead-Detail: Projekt zuordnen; CSV ordnet alle
importierten Leads einem Projekt zu.
- Projektseite: neue "Leads"-Sektion (Vertriebs-Sicht des Projekts).
- Vertrieb: "Projekte mit Vertriebsaktivität" — bereichsübergreifend alle
Projekte mit verknüpften Leads (so erscheint ein Entwicklungsprojekt hier,
WEIL es Leads hat). Rein interne Projekte ohne Leads tauchen nicht auf.
- Lead-Liste/Detail zeigen das verknüpfte Projekt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nach dem Lock-in konnte man ToDos nur bearbeiten/abhaken, aber keine neuen
anlegen. Neuer Endpoint dayEntry.addTodo hängt ein einzelnes ToDo an den
heutigen Tageseintrag an (erlaubt solange der Eintrag vom heutigen
Berlin-Tag ist, auch nach der Reflexion). Inline-Feld „Neues ToDo
hinzufügen" in der EinstempeltView; aktualisiert Heute + Dashboard-Sichten.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Feiertage: isWerktag berücksichtigt jetzt die 9 bundesweiten gesetzlichen
Feiertage (Oster-Computus + feste Tage) → ein Feiertag bricht die Streak
nicht mehr (zählt wie ein Wochenende neutral). Landesspezifische Feiertage
bewusst (noch) nicht enthalten.
- Wochenend-Edge: Reflexion an Sa/So setzte die angezeigte Streak auf 0
(Schleife brach am Nicht-Werktag ab). Jetzt wird übersprungen statt gebrochen.
- Lookback von 120 auf 400 Tage erhöht, damit lange Streaks nicht
abgeschnitten werden.
- Streak-Zähllogik in reine, getestete countStreakDays() ausgelagert
(8 Streak- + 5 Feiertags-Tests).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
staleTime war 30 s → nach „Lock in" zeigte das Dashboard bis zu 30 s alte
Cache-Stände, erst ein Browser-Refresh half. Jetzt staleTime 0: Seiten
holen beim Aufrufen/Tab-Fokus frische Daten (kein Polling). Zusätzlich
invalidiert checkIn jetzt dashboard.overview, dayEntry.todayTeam und
todayPresence, damit ein offenes Dashboard sofort mitzieht.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
"Zeit im Team heute" (und "Zeit heute") summierten alle Task-Dauern —
wer 5 Tasks parallel trackt, hatte 10h statt 2h Anwesenheit. Gesamt wird
jetzt als Vereinigung überlappender Intervalle berechnet (unionSeconds).
Pro-Aufgabe/Kategorie bleibt separat als Summe erhalten und wird als
"getrackt X" angezeigt; der Verteilungsbalken nutzt diese Summe als Nenner.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Zwei Ursachen behoben:
- Frontend: areaId/Default wurden nur beim Mount aus dem (noch ladenden)
areas-Prop berechnet → kein Default, keine Platzhalter-Option. Jetzt via
useEffect Default setzen sobald Areas geladen sind + Platzhalter-Option.
- Prod: Deploy seedet nicht → frische DB hatte keine Areas. Neue
ensureBaseAreas() legt die Basis-Bereiche idempotent beim API-Start an
(nach den Migrationen), analog zu runMigrations.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Runner kam nach dem letzten Push online; löst die deploy.yml-Pipeline für
den aktuellen origin/main aus (Lead-CRM, PM-Tiefe, CSV-Import). API wendet
Migrationen 0008–0010 beim Start an.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Das web-Runtime-Image (caddy:2-alpine) hatte keine eigene Config, daher
lieferte Caddy seine Default-Welcome-Seite aus /usr/share/caddy statt der
gebauten SPA aus /srv ("Caddy works!"). Eigene Caddyfile (root /srv,
try_files SPA-Fallback) wird jetzt nach /etc/caddy/Caddyfile kopiert.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- .gitea/workflows/deploy.yml: deployt bei Push auf main via Host-Runner
(git reset --hard origin/main in /opt/build/cockpit, docker compose up
-d --build in /opt/stacks/cockpit, Healthcheck auf cockpit-api).
- Auto-Migrate: runMigrations() (drizzle-orm Migrator) wird beim API-Start
aufgerufen; Schema wird vor dem Listen idempotent aktualisiert.
- API-Dockerfile: Workspace-Libs (@cockpit/db, @cockpit/shared) werden jetzt
mit Emit kompiliert und main/exports im Image auf dist gebogen. Vorher war
das Prod-Image nicht reproduzierbar baubar (Node konnte die TS-Quelle der
Libs nicht laden → ERR_MODULE_NOT_FOUND). Dev-Modus und Web-/Vite-Build
bleiben unberührt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
"CSV importieren"-Dialog auf /leads. Serverseitiger lead.importCsv:
eigener CSV-Parser (Quotes/Kommata/Zeilenumbrüche, mit Tests),
header-basiertes Mapping (umlaut-tolerant). Pro Zeile entsteht ein Lead
(Adresse → standorte) + primärer Kontakt; Notizen werden als erster
Austausch-Log-Eintrag gespeichert. Dedup über Firmenname.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>