diff --git a/.phase6-state.json b/.phase6-state.json index 5eb0837..c8d5d67 100644 --- a/.phase6-state.json +++ b/.phase6-state.json @@ -2,6 +2,9 @@ "completed_features": [ "password-change" ], - "current_feature": "audit-log", - "started_at": "2026-05-23T05:30:16.203066" + "current_feature": "calendar-week-view", + "started_at": "2026-05-23T05:30:16.203066", + "attempted_features": [ + "audit-log" + ] } \ No newline at end of file diff --git a/GENERATION_LOG.md b/GENERATION_LOG.md index 3efb7e5..d9f088b 100644 --- a/GENERATION_LOG.md +++ b/GENERATION_LOG.md @@ -678,3 +678,17 @@ src/routes/audit-log.ts(3,10): error TS2724: '"../db/schema"' has no exported me undefined /home/dark/Developer/EmberClone/apps/api:  ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL  Command failed with exit code 2: tsc --noEmit -p tsconfig.json +- `05:33:03` **INFO** Committed feature audit-log +- `05:33:03` **INFO** Pushed: rc=0 + +## Phase-3 Feature: calendar-week-view (2026-05-23 05:33:03) + +- `05:33:03` **INFO** Description: Wochen-Kalender für Time-Entries +- `05:33:03` **INFO** Generating apps/web/src/pages/Calendar.tsx (Calendar-Page mit Week-View. 7-Spalten-Grid (Mon-Sun mit aktueller Woc…) +- `05:33:38` **INFO** wrote 4226 chars in 34.7s (attempt 1) +- `05:33:38` **INFO** Running tsc --noEmit on api… +- `05:33:40` **WARN** tsc errors: +src/routes/audit-log.ts(3,10): error TS2724: '"../db/schema"' has no exported member named 'auditLogs'. Did you mean 'auditLog'? +undefined +/home/dark/Developer/EmberClone/apps/api: + ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL  Command failed with exit code 2: tsc --noEmit -p tsconfig.json diff --git a/apps/web/src/pages/Calendar.tsx b/apps/web/src/pages/Calendar.tsx new file mode 100644 index 0000000..024064b --- /dev/null +++ b/apps/web/src/pages/Calendar.tsx @@ -0,0 +1,107 @@ +import React, { useState, useMemo } from "react" +import { useQuery } from "@tanstack/react-query" +import { api } from "../lib/api" +import { format, startOfWeek, addDays, endOfWeek, eachDayOfInterval, isSameDay } from "date-fns" +import { ChevronLeft, ChevronRight } from "lucide-react" + +export default function CalendarPage() { + const [currentWeekStart, setCurrentWeekStart] = useState(startOfWeek(new Date(), { weekStartsOn: 1 })) + + const weekEnd = endOfWeek(currentWeekStart, { weekStartsOn: 1 }) + const days = eachDayOfInterval({ start: currentWeekStart, end: weekEnd }) + + const { data: entries = [], isLoading } = useQuery({ + queryKey: ["time-entries", currentWeekStart.toISOString()], + queryFn: () => api.listTimeEntries({ + from: currentWeekStart.toISOString(), + to: weekEnd.toISOString() + }), + }) + + const navigateWeek = (direction: "prev" | "next") => { + const offset = direction === "prev" ? -7 : 7 + setCurrentWeekStart(prev => addDays(prev, offset)) + } + + const dayData = useMemo(() => { + return days.map(day => { + const dayEntries = entries.filter(e => { + const start = new Date(e.startTime) + return isSameDay(start, day) + }) + + const totalHours = dayEntries.reduce((sum, e) => { + const start = new Date(e.startTime) + const end = e.endTime ? new Date(e.endTime) : new Date() + return sum + (end.getTime() - start.getTime()) / (1000 * 60 * 60) + }, 0) + + return { day, entries: dayEntries, totalHours } + }) + }, [days, entries]) + + if (isLoading) return
Loading calendar...
+ + return ( +
+
+

Calendar

+
+
+ {format(currentWeekStart, "MMM d")} - {format(weekEnd, "MMM d, yyyy")} +
+
+ + +
+
+
+ +
+ {dayData.map(({ day, entries, totalHours }) => ( +
+
+
+ {format(day, "EEE")} +
+
+ {format(day, "d")} +
+
+ {totalHours.toFixed(2)}h +
+
+ +
+ {entries.length === 0 ? ( +
No entries
+ ) : ( + entries.map(entry => ( +
+
+ {entry.description} +
+
+ {format(new Date(entry.startTime), "HH:mm")} - + {entry.endTime ? format(new Date(entry.endTime), " HH:mm") : " ..."} +
+
+ )) + )} +
+
+ ))} +
+
+ ) +} \ No newline at end of file