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,34 +1,15 @@
|
||||
import { View, Text, Pressable } from "react-native";
|
||||
import { ProposedEventChange, RecurringDeleteMode } from "@calchat/shared";
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import { ProposedEventChange, parseRRule, formatDate } from "@calchat/shared";
|
||||
import { useThemeStore } from "../stores/ThemeStore";
|
||||
import { EventCardBase } from "./EventCardBase";
|
||||
|
||||
const DELETE_MODE_LABELS: Record<RecurringDeleteMode, string> = {
|
||||
single: "Nur dieses Vorkommen",
|
||||
future: "Dieses & zukuenftige",
|
||||
all: "Alle Vorkommen",
|
||||
};
|
||||
|
||||
type ProposedEventCardProps = {
|
||||
proposedChange: ProposedEventChange;
|
||||
onConfirm: () => void;
|
||||
onReject: () => void;
|
||||
};
|
||||
|
||||
const DeleteModeBadge = ({ mode }: { mode: RecurringDeleteMode }) => {
|
||||
const { theme } = useThemeStore();
|
||||
return (
|
||||
<View
|
||||
className="self-start px-2 py-1 rounded-md mb-2"
|
||||
style={{ backgroundColor: theme.rejectButton }}
|
||||
>
|
||||
<Text style={{ color: theme.buttonText }} className="text-xs font-medium">
|
||||
{DELETE_MODE_LABELS[mode]}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfirmRejectButtons = ({
|
||||
isDisabled,
|
||||
respondedAction,
|
||||
@@ -84,15 +65,21 @@ export const ProposedEventCard = ({
|
||||
onConfirm,
|
||||
onReject,
|
||||
}: ProposedEventCardProps) => {
|
||||
const { theme } = useThemeStore();
|
||||
const event = proposedChange.event;
|
||||
// respondedAction is now part of the proposedChange
|
||||
const isDisabled = !!proposedChange.respondedAction;
|
||||
|
||||
// Show delete mode badge for delete actions on recurring events
|
||||
const showDeleteModeBadge =
|
||||
// For delete/single action, the occurrenceDate becomes a new exception
|
||||
const newExceptionDate =
|
||||
proposedChange.action === "delete" &&
|
||||
event?.isRecurring &&
|
||||
proposedChange.deleteMode;
|
||||
proposedChange.deleteMode === "single" &&
|
||||
proposedChange.occurrenceDate;
|
||||
|
||||
// For update actions, check if a new UNTIL date is being set
|
||||
const newUntilDate =
|
||||
proposedChange.action === "update" &&
|
||||
event?.recurrenceRule &&
|
||||
parseRRule(event.recurrenceRule)?.until;
|
||||
|
||||
if (!event) {
|
||||
return null;
|
||||
@@ -108,8 +95,33 @@ export const ProposedEventCard = ({
|
||||
description={event.description}
|
||||
isRecurring={event.isRecurring}
|
||||
>
|
||||
{showDeleteModeBadge && (
|
||||
<DeleteModeBadge mode={proposedChange.deleteMode!} />
|
||||
{/* Show new exception date for delete/single actions */}
|
||||
{newExceptionDate && (
|
||||
<View className="flex-row items-center mb-2">
|
||||
<Feather
|
||||
name="plus-circle"
|
||||
size={16}
|
||||
color={theme.confirmButton}
|
||||
style={{ marginRight: 8 }}
|
||||
/>
|
||||
<Text style={{ color: theme.confirmButton }} className="font-medium">
|
||||
Neue Ausnahme: {formatDate(new Date(proposedChange.occurrenceDate!))}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
{/* Show new UNTIL date for update actions */}
|
||||
{newUntilDate && (
|
||||
<View className="flex-row items-center mb-2">
|
||||
<Feather
|
||||
name="plus-circle"
|
||||
size={16}
|
||||
color={theme.confirmButton}
|
||||
style={{ marginRight: 8 }}
|
||||
/>
|
||||
<Text style={{ color: theme.confirmButton }} className="font-medium">
|
||||
Neues Ende: {formatDate(newUntilDate)}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
<ConfirmRejectButtons
|
||||
isDisabled={isDisabled}
|
||||
|
||||
Reference in New Issue
Block a user