refactor: extract shared EventCardBase component

- Create EventCardBase with common layout, icons (calendar, clock, repeat), and formatting functions
- Refactor EventCard and ProposedEventCard to use EventCardBase
- Add event details to delete action responses for better UX
- Include event title in delete confirmation message
This commit is contained in:
2026-01-05 19:27:33 +01:00
parent 24ab6f0420
commit 8e58ab4249
5 changed files with 235 additions and 192 deletions

View File

@@ -1,6 +1,7 @@
import { View, Text, Pressable } from "react-native";
import { ProposedEventChange } from "@caldav/shared";
import currentTheme from "../Themes";
import { EventCardBase } from "./EventCardBase";
type ProposedEventCardProps = {
proposedChange: ProposedEventChange;
@@ -9,17 +10,52 @@ type ProposedEventCardProps = {
onReject: () => void;
};
function formatDateTime(date?: Date): string {
if (!date) return "";
const d = new Date(date);
return d.toLocaleDateString("de-DE", {
weekday: "long",
day: "2-digit",
month: "2-digit",
hour: "2-digit",
minute: "2-digit",
});
}
const ConfirmRejectButtons = ({
isDisabled,
respondedAction,
onConfirm,
onReject,
}: {
isDisabled: boolean;
respondedAction?: "confirm" | "reject";
onConfirm: () => void;
onReject: () => void;
}) => (
<View className="flex-row mt-3 gap-2">
<Pressable
onPress={onConfirm}
disabled={isDisabled}
className="flex-1 py-2 rounded-lg items-center"
style={{
backgroundColor: isDisabled
? currentTheme.disabledButton
: currentTheme.confirmButton,
borderWidth: respondedAction === "confirm" ? 2 : 0,
borderColor: currentTheme.confirmButton,
}}
>
<Text style={{ color: currentTheme.buttonText }} className="font-medium">
Annehmen
</Text>
</Pressable>
<Pressable
onPress={onReject}
disabled={isDisabled}
className="flex-1 py-2 rounded-lg items-center"
style={{
backgroundColor: isDisabled
? currentTheme.disabledButton
: currentTheme.rejectButton,
borderWidth: respondedAction === "reject" ? 2 : 0,
borderColor: currentTheme.rejectButton,
}}
>
<Text style={{ color: currentTheme.buttonText }} className="font-medium">
Ablehnen
</Text>
</Pressable>
</View>
);
export const ProposedEventCard = ({
proposedChange,
@@ -30,71 +66,27 @@ export const ProposedEventCard = ({
const event = proposedChange.event;
const isDisabled = !!respondedAction;
return (
<View
className="border-t p-2 mt-2"
style={{ borderTopColor: currentTheme.placeholderBg }}
>
{/* Event Details */}
<Text className="font-bold text-base">{event?.title}</Text>
<Text style={{ color: currentTheme.textSecondary }}>
{formatDateTime(event?.startTime)}
</Text>
{event?.description && (
<Text
style={{ color: currentTheme.textSecondary }}
className="text-sm mt-1"
>
{event.description}
</Text>
)}
{event?.isRecurring && (
<Text style={{ color: currentTheme.textMuted }} className="text-sm">
Wiederkehrend
</Text>
)}
if (!event) {
return null;
}
{/* Buttons */}
<View className="flex-row mt-3 gap-2">
<Pressable
onPress={onConfirm}
disabled={isDisabled}
className="flex-1 py-2 rounded-lg items-center"
style={{
backgroundColor: isDisabled
? currentTheme.disabledButton
: currentTheme.confirmButton,
borderWidth: respondedAction === "confirm" ? 2 : 0,
borderColor: currentTheme.confirmButton,
}}
>
<Text
style={{ color: currentTheme.buttonText }}
className="font-medium"
>
Annehmen
</Text>
</Pressable>
<Pressable
onPress={onReject}
disabled={isDisabled}
className="flex-1 py-2 rounded-lg items-center"
style={{
backgroundColor: isDisabled
? currentTheme.disabledButton
: currentTheme.rejectButton,
borderWidth: respondedAction === "reject" ? 2 : 0,
borderColor: currentTheme.rejectButton,
}}
>
<Text
style={{ color: currentTheme.buttonText }}
className="font-medium"
>
Ablehnen
</Text>
</Pressable>
</View>
return (
<View className="mt-2">
<EventCardBase
className="m-2"
title={event.title}
startTime={event.startTime}
endTime={event.endTime}
description={event.description}
isRecurring={event.isRecurring}
>
<ConfirmRejectButtons
isDisabled={isDisabled}
respondedAction={respondedAction}
onConfirm={onConfirm}
onReject={onReject}
/>
</EventCardBase>
</View>
);
};