Files
everythingbutwork/packages/shared/src/schemas/time-entry.ts
T
dejanandClaude Opus 4.7 95beb99cd4
ci / quality (push) Has been cancelled
ci / build (push) Has been cancelled
Zeiterfassung + Visual-Polish (Keycap-Buttons, Space Grotesk)
Zeiterfassung
- Neue Tabelle time_entry mit started_at/ended_at/duration_seconds,
  optional todo_id/bezug, category-Enum (focus/meeting/break/admin/learning/other),
  description, day_entry_id-Verknüpfung. Partial-Unique-Index erzwingt
  max. einen aktiven Timer pro User.
- tRPC time-Router: now, start (stoppt aktiven automatisch), stop,
  listToday (Liste + Tagessumme), update, upsertManual (retroaktiv),
  delete, teamToday (personenübergreifend mit byCategory + aktivem
  Timer pro User).
- TopNav: persistente TimerPill links neben Streak. Quick-Picker mit
  heutigen ToDos, Kategorie-Auswahl und Freitext-Start. Live-Sekundenzähler.
- /heute: TimelineSection mit Edit/Delete pro Eintrag, ToDo-Bindung
  nachträglich änderbar, Block-nachtragen-Dialog. Pro ToDo-Zeile
  ▶-Start- und 🕐-Nachtrag-Button.
- /puls: TeamTimeSection mit aktiver Timer-Anzeige + Tagessumme +
  Kategorie-Breakdown pro User (refresh alle 30s, Live-Tick jede Sekunde).

Visual
- Space Grotesk als Hausschrift (selbst-gehostet, latin + latin-ext)
- Keycap-Button-Stil (Gradient + Druckeffekt) für primary/secondary/destructive
- Neue Helper-Module: format-duration, time-categories

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 17:27:40 +02:00

47 lines
1.5 KiB
TypeScript

import { z } from 'zod';
export const timeCategorySchema = z.enum([
'focus',
'meeting',
'break',
'admin',
'learning',
'other',
]);
export type TimeCategory = z.infer<typeof timeCategorySchema>;
export const TIME_CATEGORIES: { value: TimeCategory; label: string }[] = [
{ value: 'focus', label: 'Fokus' },
{ value: 'meeting', label: 'Meeting' },
{ value: 'admin', label: 'Admin' },
{ value: 'learning', label: 'Lernen' },
{ value: 'break', label: 'Pause' },
{ value: 'other', label: 'Sonstiges' },
];
export const startTimeEntryInput = z.object({
todoId: z.string().uuid().optional(),
category: timeCategorySchema.default('focus'),
description: z.string().max(200).optional(),
});
export type StartTimeEntryInput = z.infer<typeof startTimeEntryInput>;
export const updateTimeEntryInput = z.object({
id: z.string().uuid(),
startedAt: z.string().datetime().optional(),
endedAt: z.string().datetime().nullable().optional(),
todoId: z.string().uuid().nullable().optional(),
category: timeCategorySchema.optional(),
description: z.string().max(200).nullable().optional(),
});
export type UpdateTimeEntryInput = z.infer<typeof updateTimeEntryInput>;
export const manualTimeEntryInput = z.object({
startedAt: z.string().datetime(),
endedAt: z.string().datetime(),
todoId: z.string().uuid().optional(),
category: timeCategorySchema.default('focus'),
description: z.string().max(200).optional(),
});
export type ManualTimeEntryInput = z.infer<typeof manualTimeEntryInput>;