Auto-Tage vom 6-Uhr-Schnitt haben keinen echten Lock-in — statt die
Anwesenheit ganz auszublenden, ankert sie jetzt am ersten Zeiteintrag
des Tages. teamToday/listToday liefern presenceStartedAt/presenceEndedAt
(echter Check-in hat Vorrang); Team-Karte, Heute-Balken und "Zeit heute"
nutzen die neuen Felder.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Der 6-Uhr-Schnitt legt Folgetage mit checkedInAt=06:00 an — das ist kein
echter Lock-in, die Anwesenheitsanzeige lief dadurch fälschlich seit
06:00 mit (z. B. "6h 6m" mittags trotz spätem Arbeitsbeginn). Neues Flag
day_entry.auto_checked_in (Backfill: bestehende 06:00:00-Check-ins);
teamToday, Heute-Balken und "Zeit heute" zeigen Anwesenheit nur noch bei
echtem Check-in.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
teamToday liefert pro Person checkedInAt/checkedOutAt aus dem heutigen
day_entry mit; die Team-Karte zeigt daneben "Anwesend: …" (live, bzw.
eingefroren nach Feierabend). Reine Anzeige, kein Eingriff ins Tracking.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Neben der getrackten Task-Zeit zeigt der "Zeit heute"-Header jetzt
"Getrackt · Anwesend"; der Check-In/Out-Balken zeigt die Anwesenheit
live ("anwesend seit …") und nach Check-Out die finale Dauer. Kein
Eingriff ins Task-Tracking — Wert wird nur aus checkedInAt/checkedOutAt
berechnet.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Behebt die rote CI (format:check schlug an 36 Alt-Bestand-Dateien fehl:
migrations/meta-Snapshots, seed.ts, schemas/lead.ts, README u. a.) und erzwingt
nebenbei einen frischen web-Build beim Auto-Deploy (apps/web-Dateien geaendert
-> COPY-Layer invalidiert -> Vite baut neu).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reflexion/ToDo-Bearbeitung war nur bis 06:00 des Folgetags moeglich. Jetzt
3-Tage-Nachfrist (isEditableWithinGrace) und ein Bearbeiten-Weg aus der Historie:
- werktag: isEditableWithinGrace(entryDate, now, 3) + Tests
- submitReflection/editTodoTitle: Sperre auf die 3-Tage-Nachfrist gelockert
- neue Query dayEntry.ownForDate(date): eigener Tageseintrag + editable-Flag
- reflexion.tsx liest ?date= und laedt vergangene Tage; Felder werden mit der
bestehenden Reflexion vorbefuellt
- historie.tsx: "Bearbeiten"-Button am eigenen Eintrag (wenn <= 3 Tage)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Notizfeld pro Task in der Reflexion (optional, jeder Status) — nutzt das
bestehende dropReason-Feld (Limit 200->500). Wird in der Historie bei JEDEM
Status angezeigt (bisher nur bei "verworfen").
- Optionaler Tages-Link wird in der Historie jetzt angezeigt.
- Historie-Pfeile springen zum naechsten/vorherigen Tag MIT Eintraegen statt
starr ueber Werktage — Wochenend-Arbeit (z. B. Sa 6.6.) ist nun erreichbar.
Default-Tag = juengster Tag vor heute mit Eintrag.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Der Abschluss verlangte eine URL + freie Vermerk-Textarea, half aber nicht beim
Strukturieren der Retro. Jetzt gefuehrte Felder (Ziel erreicht? / Aufwand vs.
Plan / was lief gut / was nicht), gespeichert als JSONB project.lessons_learned
(Migration 0018). Anzeige im Projekt-Detail als "Retrospektive"; Altprojekte
fallen auf den freien Abschlussvermerk zurueck.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Alle Bausteine waren da (Ampel/Trend, ToDo-Status, Anwesenheit), aber niemand
sah je seinen eigenen Verlauf. Neue Query dayEntry.myWeek + Route /meine-woche
(pro Tag Ampel, Antritt→Feierabend, ToDo-Zähler). Nav-Tab „Woche" + Shortcut g w.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Alt-Bestand nie formatiert — isolierter Format-Lauf vor der myWeek-Query und
dem Woche-Nav-Tab.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Keine Sprung-zu-Suche bei wachsender Projekt-/Lead-Zahl. Neuer search-Router
(ILIKE ueber Projekte, Leads, offene Blocker) + Command-Palette ueber die
Modal-Basis, getriggert per Cmd/Ctrl+K oder „/". Klick springt zum Ziel.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Das vollstaendige Deal-Backend (list/kpis/updateStage) lag ohne UI brach;
Lead->Deal-Promotion war eine Sackgasse. Neue Route /pipeline: KPI-Header
(gewichtete Pipeline, offene Deals, gewonnen/Quartal) + Spalten je Stufe
(lead/erstgespraech/demo/angebot/abschluss), Stufenwechsel per Select
(deal.updateStage). Verlinkt von /leads. Kein Drag-Kanban (CONVENTIONS).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
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>
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>