feat: add EditEventScreen with calendar and chat mode support

Add a unified event editor that works in two modes:
- Calendar mode: Create/edit events directly via EventService API
- Chat mode: Edit AI-proposed events before confirming them

The chat mode allows users to modify proposed events (title, time,
recurrence) and persists changes both locally and to the server.

New components: DateTimePicker, ScrollableDropdown, useDropdownPosition
New API: PUT /api/chat/messages/:messageId/proposal
This commit is contained in:
2026-01-31 18:46:31 +01:00
parent 617543a603
commit 6f0d172bf2
33 changed files with 1394 additions and 289 deletions

View File

@@ -44,9 +44,13 @@ export function expandRecurringEvents(
const endTime = new Date(event.endTime);
const duration = endTime.getTime() - startTime.getTime();
// For multi-day events: adjust range start back by event duration
// to find events that start before rangeStart but extend into the range
const adjustedRangeStart = new Date(rangeStart.getTime() - duration);
if (!event.isRecurring || !event.recurrenceRule) {
// Non-recurring event: add as-is if within range
if (startTime >= rangeStart && startTime <= rangeEnd) {
// Non-recurring event: add if it overlaps with the range
if (endTime >= rangeStart && startTime <= rangeEnd) {
expanded.push({
...event,
occurrenceStart: startTime,
@@ -64,9 +68,11 @@ export function expandRecurringEvents(
`DTSTART:${formatRRuleDateString(startTime)}\nRRULE:${ruleString}`,
);
// Get occurrences within the range (using fake UTC dates)
// Get occurrences within the adjusted range (using fake UTC dates)
// Use adjustedRangeStart to catch multi-day events that start before
// rangeStart but still extend into the range
const occurrences = rule.between(
toRRuleDate(rangeStart),
toRRuleDate(adjustedRangeStart),
toRRuleDate(rangeEnd),
true, // inclusive
);
@@ -78,6 +84,11 @@ export function expandRecurringEvents(
const occurrenceStart = fromRRuleDate(occurrence);
const occurrenceEnd = new Date(occurrenceStart.getTime() + duration);
// Only include if occurrence actually overlaps with the original range
if (occurrenceEnd < rangeStart || occurrenceStart > rangeEnd) {
continue;
}
// Skip if this occurrence is in the exception dates
const dateKey = formatDateKey(occurrenceStart);
if (exceptionSet.has(dateKey)) {