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:
@@ -34,3 +34,15 @@ export function getDay(
|
||||
result.setHours(hour, minute, 0, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an event spans multiple days.
|
||||
* Compares dates at midnight to determine if start and end are on different calendar days.
|
||||
*/
|
||||
export function isMultiDayEvent(start: Date, end: Date): boolean {
|
||||
const startDate = new Date(start);
|
||||
const endDate = new Date(end);
|
||||
startDate.setHours(0, 0, 0, 0);
|
||||
endDate.setHours(0, 0, 0, 0);
|
||||
return startDate.getTime() !== endDate.getTime();
|
||||
}
|
||||
|
||||
@@ -46,3 +46,26 @@ export function formatDateWithWeekday(date: Date): string {
|
||||
year: "numeric",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Format date as DD.MM. (short, without year)
|
||||
*/
|
||||
export function formatDateShort(date: Date): string {
|
||||
const d = new Date(date);
|
||||
return d.toLocaleDateString("de-DE", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Format date with weekday short as "Mo., DD.MM."
|
||||
*/
|
||||
export function formatDateWithWeekdayShort(date: Date): string {
|
||||
const d = new Date(date);
|
||||
return d.toLocaleDateString("de-DE", {
|
||||
weekday: "short",
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from "./dateHelpers";
|
||||
export * from "./rruleHelpers";
|
||||
export * from "./formatters";
|
||||
export * from "./rruleHelpers";
|
||||
|
||||
@@ -1,49 +1,29 @@
|
||||
import { rrulestr, Frequency } from "rrule";
|
||||
/**
|
||||
* RRULE building and parsing helpers.
|
||||
*/
|
||||
|
||||
export interface ParsedRRule {
|
||||
freq: string; // "YEARLY", "MONTHLY", "WEEKLY", "DAILY", etc.
|
||||
until?: Date;
|
||||
count?: number;
|
||||
interval?: number;
|
||||
byDay?: string[]; // ["MO", "WE", "FR"]
|
||||
}
|
||||
export type RepeatType = "Tag" | "Woche" | "Monat" | "Jahr";
|
||||
|
||||
const FREQ_NAMES: Record<Frequency, string> = {
|
||||
[Frequency.YEARLY]: "YEARLY",
|
||||
[Frequency.MONTHLY]: "MONTHLY",
|
||||
[Frequency.WEEKLY]: "WEEKLY",
|
||||
[Frequency.DAILY]: "DAILY",
|
||||
[Frequency.HOURLY]: "HOURLY",
|
||||
[Frequency.MINUTELY]: "MINUTELY",
|
||||
[Frequency.SECONDLY]: "SECONDLY",
|
||||
const REPEAT_TYPE_TO_FREQ: Record<RepeatType, string> = {
|
||||
Tag: "DAILY",
|
||||
Woche: "WEEKLY",
|
||||
Monat: "MONTHLY",
|
||||
Jahr: "YEARLY",
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses an RRULE string and extracts the relevant fields.
|
||||
* Handles both with and without "RRULE:" prefix.
|
||||
* Build an RRULE string from repeat count and type.
|
||||
*
|
||||
* @param repeatType - The type of repetition (Tag, Woche, Monat, Jahr)
|
||||
* @param interval - The interval between repetitions (default: 1)
|
||||
* @returns RRULE string like "FREQ=WEEKLY;INTERVAL=2"
|
||||
*/
|
||||
export function parseRRule(ruleString: string): ParsedRRule | null {
|
||||
if (!ruleString) {
|
||||
return null;
|
||||
export function buildRRule(repeatType: RepeatType, interval: number = 1): string {
|
||||
const freq = REPEAT_TYPE_TO_FREQ[repeatType];
|
||||
|
||||
if (interval <= 1) {
|
||||
return `FREQ=${freq}`;
|
||||
}
|
||||
|
||||
try {
|
||||
// Ensure RRULE: prefix is present
|
||||
const normalized = ruleString.startsWith("RRULE:")
|
||||
? ruleString
|
||||
: `RRULE:${ruleString}`;
|
||||
|
||||
const rule = rrulestr(normalized);
|
||||
const options = rule.options;
|
||||
|
||||
return {
|
||||
freq: FREQ_NAMES[options.freq] || "UNKNOWN",
|
||||
until: options.until || undefined,
|
||||
count: options.count || undefined,
|
||||
interval: options.interval > 1 ? options.interval : undefined,
|
||||
byDay: options.byweekday?.map((d) => d.toString()) || undefined,
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return `FREQ=${freq};INTERVAL=${interval}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user