feat: add RRULE parsing to shared package and improve ProposedEventCard UI
- Add rrule library to shared package for RRULE string parsing - Add rruleHelpers.ts with parseRRule() returning freq, until, count, interval, byDay - Add formatters.ts with German date/time formatters for client and server - Extend CreateEventDTO with exceptionDates field for proposals - Extend ChatModel schema with exceptionDates, deleteMode, occurrenceDate - Update proposeUpdateEvent tool to support isRecurring and recurrenceRule params - ProposedEventCard now shows green "Neue Ausnahme" and "Neues Ende" text - Add Sport test scenario with dynamic exception and UNTIL responses - Update CLAUDE.md documentation
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
import { CalendarEvent } from "@calchat/shared";
|
||||
import {
|
||||
CalendarEvent,
|
||||
formatDate,
|
||||
formatTime,
|
||||
formatDateTime,
|
||||
} from "@calchat/shared";
|
||||
|
||||
// German date/time formatting helpers
|
||||
export const formatDate = (d: Date) => d.toLocaleDateString("de-DE");
|
||||
export const formatTime = (d: Date) =>
|
||||
d.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" });
|
||||
export const formatDateTime = (d: Date) =>
|
||||
`${formatDate(d)} ${d.toLocaleTimeString("de-DE")}`;
|
||||
// Re-export for backwards compatibility
|
||||
export { formatDate, formatTime, formatDateTime };
|
||||
|
||||
/**
|
||||
* Format a list of events for display in the system prompt.
|
||||
|
||||
@@ -46,13 +46,20 @@ WICHTIG - Wiederkehrende Termine (RRULE):
|
||||
2. "Arbeit" Fr 9:00-13:00 (RRULE mit BYDAY=FR)
|
||||
- Nutze NIEMALS BYHOUR/BYMINUTE in RRULE - diese überschreiben die Startzeit nicht wie erwartet!
|
||||
- Gültige RRULE-Optionen: FREQ (DAILY/WEEKLY/MONTHLY/YEARLY), BYDAY (MO,TU,WE,TH,FR,SA,SU), INTERVAL, COUNT, UNTIL
|
||||
- UNTIL Format: YYYYMMDDTHHMMSSZ (UTC) z.B. UNTIL=20260310T000000Z
|
||||
- WICHTIG: Schreibe die RRULE NIEMALS in das description-Feld! Nutze IMMER das recurrenceRule-Feld!
|
||||
|
||||
WICHTIG - Antwortformat:
|
||||
- Halte deine Textantworten SEHR KURZ (1-2 Sätze maximal)
|
||||
- Die Event-Details (Titel, Datum, Uhrzeit, Beschreibung) werden dem Benutzer automatisch in separaten Karten angezeigt
|
||||
- Wiederhole NIEMALS die Event-Details im Text! Der Benutzer sieht sie bereits in den Karten
|
||||
- Gute Beispiele: "Alles klar!" oder "Hier sind deine Termine:"
|
||||
- Schlechte Beispiele: Lange Listen mit allen Terminen und ihren Details im Text
|
||||
- Verwende kontextbezogene Antworten in der GEGENWARTSFORM je nach Aktion:
|
||||
- Bei Termin-Erstellung: "Ich schlage folgenden Termin vor:" oder "Neuer Termin:"
|
||||
- Bei Termin-Änderung: "Ich schlage folgende Änderung vor:" oder "Änderung:"
|
||||
- Bei Termin-Löschung: "Ich schlage vor, diesen Termin zu löschen:" oder "Löschung:"
|
||||
- Bei Übersichten: "Hier sind deine Termine:"
|
||||
- WICHTIG: Verwende NIEMALS Vergangenheitsform wie "Ich habe ... vorgeschlagen" - immer Gegenwartsform!
|
||||
- Schlechte Beispiele: "Alles klar!" (zu unspezifisch), lange Listen mit Termin-Details im Text
|
||||
- Bei Rückfragen oder wenn keine Termine erstellt werden, kannst du ausführlicher antworten
|
||||
|
||||
Existierende Termine des Benutzers:
|
||||
|
||||
@@ -131,7 +131,16 @@ export const TOOL_DEFINITIONS: ToolDefinition[] = [
|
||||
},
|
||||
description: {
|
||||
type: "string",
|
||||
description: "New description (optional)",
|
||||
description: "New description (optional). NEVER put RRULE here!",
|
||||
},
|
||||
isRecurring: {
|
||||
type: "boolean",
|
||||
description: "Whether this is a recurring event (optional)",
|
||||
},
|
||||
recurrenceRule: {
|
||||
type: "string",
|
||||
description:
|
||||
"RRULE format string (optional). Use to add UNTIL or modify recurrence. Format: FREQ=DAILY;UNTIL=20260310T000000Z",
|
||||
},
|
||||
},
|
||||
required: ["eventId"],
|
||||
|
||||
@@ -88,6 +88,9 @@ export function executeToolCall(
|
||||
updates.startTime = new Date(args.startTime as string);
|
||||
if (args.endTime) updates.endTime = new Date(args.endTime as string);
|
||||
if (args.description) updates.description = args.description;
|
||||
if (args.isRecurring !== undefined)
|
||||
updates.isRecurring = args.isRecurring;
|
||||
if (args.recurrenceRule) updates.recurrenceRule = args.recurrenceRule;
|
||||
|
||||
// Build event object for display (merge existing with updates)
|
||||
const displayEvent = {
|
||||
@@ -96,7 +99,11 @@ export function executeToolCall(
|
||||
endTime: (updates.endTime as Date) || existingEvent.endTime,
|
||||
description:
|
||||
(updates.description as string) || existingEvent.description,
|
||||
isRecurring: existingEvent.isRecurring,
|
||||
isRecurring:
|
||||
(updates.isRecurring as boolean) ?? existingEvent.isRecurring,
|
||||
recurrenceRule:
|
||||
(updates.recurrenceRule as string) || existingEvent.recurrenceRule,
|
||||
exceptionDates: existingEvent.exceptionDates,
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -149,6 +156,8 @@ export function executeToolCall(
|
||||
endTime: existingEvent.endTime,
|
||||
description: existingEvent.description,
|
||||
isRecurring: existingEvent.isRecurring,
|
||||
recurrenceRule: existingEvent.recurrenceRule,
|
||||
exceptionDates: existingEvent.exceptionDates,
|
||||
},
|
||||
deleteMode: existingEvent.isRecurring ? deleteMode : undefined,
|
||||
occurrenceDate: existingEvent.isRecurring
|
||||
|
||||
Reference in New Issue
Block a user