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

@@ -1,25 +1,31 @@
import { View, Text, Pressable } from "react-native";
import { Feather } from "@expo/vector-icons";
import { ProposedEventChange, parseRRule, formatDate } from "@calchat/shared";
import { ProposedEventChange, formatDate } from "@calchat/shared";
import { rrulestr } from "rrule";
import { useThemeStore } from "../stores/ThemeStore";
import { EventCardBase } from "./EventCardBase";
type ProposedEventCardProps = {
proposedChange: ProposedEventChange;
onConfirm: () => void;
onConfirm: (proposal: ProposedEventChange) => void;
onReject: () => void;
onEdit?: (proposal: ProposedEventChange) => void;
};
const ConfirmRejectButtons = ({
const ActionButtons = ({
isDisabled,
respondedAction,
showEdit,
onConfirm,
onReject,
onEdit,
}: {
isDisabled: boolean;
respondedAction?: "confirm" | "reject";
showEdit: boolean;
onConfirm: () => void;
onReject: () => void;
onEdit?: () => void;
}) => {
const { theme } = useThemeStore();
return (
@@ -56,6 +62,19 @@ const ConfirmRejectButtons = ({
Ablehnen
</Text>
</Pressable>
{showEdit && onEdit && (
<Pressable
onPress={onEdit}
className="py-2 px-3 rounded-lg items-center"
style={{
backgroundColor: theme.secondaryBg,
borderWidth: 1,
borderColor: theme.borderPrimary,
}}
>
<Feather name="edit-2" size={18} color={theme.textPrimary} />
</Pressable>
)}
</View>
);
};
@@ -64,6 +83,7 @@ export const ProposedEventCard = ({
proposedChange,
onConfirm,
onReject,
onEdit,
}: ProposedEventCardProps) => {
const { theme } = useThemeStore();
const event = proposedChange.event;
@@ -79,7 +99,7 @@ export const ProposedEventCard = ({
const newUntilDate =
proposedChange.action === "update" &&
event?.recurrenceRule &&
parseRRule(event.recurrenceRule)?.until;
rrulestr(event.recurrenceRule).options.until;
if (!event) {
return null;
@@ -93,7 +113,7 @@ export const ProposedEventCard = ({
startTime={event.startTime}
endTime={event.endTime}
description={event.description}
isRecurring={event.isRecurring}
isRecurring={!!event.recurrenceRule}
>
{/* Show new exception date for delete/single actions */}
{newExceptionDate && (
@@ -123,11 +143,13 @@ export const ProposedEventCard = ({
</Text>
</View>
)}
<ConfirmRejectButtons
<ActionButtons
isDisabled={isDisabled}
respondedAction={proposedChange.respondedAction}
onConfirm={onConfirm}
showEdit={proposedChange.action !== "delete" && !isDisabled}
onConfirm={() => onConfirm(proposedChange)}
onReject={onReject}
onEdit={onEdit ? () => onEdit(proposedChange) : undefined}
/>
</EventCardBase>
</View>