implement calendar event display with day indicators and overlay

- Add ExpandedEvent type to shared package for recurring event instances
- Implement EventController and EventService with full CRUD operations
- Server-side recurring event expansion via recurrenceExpander
- Calendar grid shows orange dot indicator for days with events
- Tap on day opens modal overlay with EventCards
- EventCard component with Feather icons (calendar, clock, repeat, edit, trash)
- EventsStore with Zustand for client-side event state management
- Load events for visible grid range including adjacent month days
- Add textPrimary, borderPrimary, eventIndicator to theme
- Update test responses for multiple events on Saturdays
This commit is contained in:
2026-01-04 17:19:58 +01:00
parent e3f7a778c7
commit 1532acab78
12 changed files with 601 additions and 99 deletions

View File

@@ -16,7 +16,7 @@ import { getWeeksOverview, getMonthOverview } from "../utils/eventFormatters";
type TestResponse = { content: string; proposedChange?: ProposedEventChange };
// Test response index (cycles through responses)
let responseIndex = 8;
let responseIndex = 0;
// Static test responses (event proposals)
const staticResponses: TestResponse[] = [
@@ -109,7 +109,7 @@ const staticResponses: TestResponse[] = [
'• "Verschiebe das Meeting auf Donnerstag"\n\n' +
"Wie kann ich dir helfen?",
},
// Response 9: Phone call - short appointment
// Response 9: Phone call - short appointment (Wednesday, so +2 days = Friday)
{
content:
"Alles klar! Ich habe das Telefonat mit deiner Mutter eingetragen:",
@@ -117,12 +117,12 @@ const staticResponses: TestResponse[] = [
action: "create",
event: {
title: "Telefonat mit Mama",
startTime: getDay("Sunday", 0, 11, 0),
endTime: getDay("Sunday", 0, 11, 30),
startTime: getDay("Wednesday", 1, 11, 0),
endTime: getDay("Wednesday", 1, 11, 30),
},
},
},
// Response 10: Update "Telefonat mit Mama" +2 days (DYNAMIC - placeholder)
// Response 10: Update "Telefonat mit Mama" +3 days (DYNAMIC - placeholder)
{ content: "" },
// Response 11: Birthday party - evening event
{
@@ -137,10 +137,10 @@ const staticResponses: TestResponse[] = [
},
},
},
// Response 12: Language course - limited to 8 weeks
// Response 12: Language course - limited to 8 weeks (Thu + Sat)
{
content:
"Dein Spanischkurs ist eingetragen! Er läuft jeden Donnerstag für die nächsten 8 Wochen:",
"Dein Spanischkurs ist eingetragen! Er läuft jeden Donnerstag und Samstag für die nächsten 8 Wochen:",
proposedChange: {
action: "create",
event: {
@@ -148,7 +148,7 @@ const staticResponses: TestResponse[] = [
startTime: getDay("Thursday", 1, 19, 0),
endTime: getDay("Thursday", 1, 20, 30),
isRecurring: true,
recurrenceRule: "FREQ=WEEKLY;BYDAY=TH;COUNT=8",
recurrenceRule: "FREQ=WEEKLY;BYDAY=TH,SA;COUNT=8",
},
},
},
@@ -191,17 +191,18 @@ async function getTestResponse(
}
if (responseIdx === 10) {
// Update "Telefonat mit Mama" +2 days
// Update "Telefonat mit Mama" +3 days and change time to 13:00
const events = await eventRepo.findByUserId(userId);
const mamaEvent = events.find((e) => e.title === "Telefonat mit Mama");
if (mamaEvent) {
const newStart = new Date(mamaEvent.startTime);
newStart.setDate(newStart.getDate() + 2);
const newEnd = new Date(mamaEvent.endTime);
newEnd.setDate(newEnd.getDate() + 2);
newStart.setDate(newStart.getDate() + 3);
newStart.setHours(13, 0, 0, 0);
const newEnd = new Date(newStart);
newEnd.setMinutes(30);
return {
content:
"Alles klar, ich verschiebe das Telefonat mit Mama um 2 Tage nach hinten:",
"Alles klar, ich verschiebe das Telefonat mit Mama auf Samstag um 13:00 Uhr:",
proposedChange: {
action: "update",
eventId: mamaEvent.id,