refactor: add CardBase and ModalBase components

- Add CardBase: reusable card with header, content, footer
  - Configurable via props: padding, border, text size, background
- Add ModalBase: modal wrapper using CardBase internally
  - Provides backdrop, click-outside-to-close, Android back button
- Refactor EventCardBase to use CardBase
- Refactor DeleteEventModal to use ModalBase
- Refactor EventOverlay (calendar.tsx) to use ModalBase
- Update CLAUDE.md with component documentation
This commit is contained in:
2026-01-25 21:50:19 +01:00
parent 2b999d9b0f
commit 726334c155
7 changed files with 366 additions and 265 deletions

View File

@@ -1,6 +1,7 @@
import { Modal, Pressable, Text, View } from "react-native";
import { Pressable, Text, View } from "react-native";
import { RecurringDeleteMode } from "@calchat/shared";
import { useThemeStore } from "../stores/ThemeStore";
import { ModalBase } from "./ModalBase";
type DeleteEventModalProps = {
visible: boolean;
@@ -13,24 +14,20 @@ type DeleteEventModalProps = {
type DeleteOption = {
mode: RecurringDeleteMode;
label: string;
description: string;
};
const RECURRING_DELETE_OPTIONS: DeleteOption[] = [
{
mode: "single",
label: "Nur dieses Vorkommen",
description: "Nur der ausgewaehlte Termin wird entfernt",
},
{
mode: "future",
label: "Dieses und zukuenftige",
description: "Dieser und alle folgenden Termine werden entfernt",
label: "Dieses und zukünftige",
},
{
mode: "all",
label: "Alle Vorkommen",
description: "Die gesamte Terminserie wird geloescht",
},
];
@@ -43,120 +40,61 @@ export const DeleteEventModal = ({
}: DeleteEventModalProps) => {
const { theme } = useThemeStore();
const title = isRecurring
? "Wiederkehrenden Termin löschen"
: "Termin loeschen";
return (
<Modal
<ModalBase
visible={visible}
transparent={true}
animationType="fade"
onRequestClose={onCancel}
onClose={onCancel}
title={title}
subtitle={eventTitle}
footer={{ label: "Abbrechen", onPress: onCancel }}
>
<Pressable
className="flex-1 justify-center items-center"
style={{ backgroundColor: "rgba(0,0,0,0.5)" }}
onPress={onCancel}
>
<Pressable
className="w-11/12 rounded-2xl overflow-hidden"
style={{
backgroundColor: theme.primeBg,
borderWidth: 4,
borderColor: theme.borderPrimary,
}}
onPress={(e) => e.stopPropagation()}
>
{/* Header */}
<View
className="px-4 py-3"
{isRecurring ? (
// Recurring event: show three options
RECURRING_DELETE_OPTIONS.map((option) => (
<Pressable
key={option.mode}
onPress={() => onConfirm(option.mode)}
className="py-3 px-4 rounded-lg mb-2"
style={{
backgroundColor: theme.chatBot,
borderBottomWidth: 3,
borderBottomColor: theme.borderPrimary,
backgroundColor: theme.secondaryBg,
borderWidth: 1,
borderColor: theme.borderPrimary,
}}
>
<Text
className="font-bold text-lg"
className="font-medium text-base"
style={{ color: theme.textPrimary }}
>
{isRecurring
? "Wiederkehrenden Termin loeschen"
: "Termin loeschen"}
</Text>
<Text style={{ color: theme.textSecondary }} numberOfLines={1}>
{eventTitle}
</Text>
</View>
{/* Content */}
<View className="p-4">
{isRecurring ? (
// Recurring event: show three options
RECURRING_DELETE_OPTIONS.map((option) => (
<Pressable
key={option.mode}
onPress={() => onConfirm(option.mode)}
className="py-3 px-4 rounded-lg mb-2"
style={{
backgroundColor: theme.secondaryBg,
borderWidth: 1,
borderColor: theme.borderPrimary,
}}
>
<Text
className="font-medium text-base"
style={{ color: theme.textPrimary }}
>
{option.label}
</Text>
<Text
className="text-sm mt-1"
style={{ color: theme.textMuted }}
>
{option.description}
</Text>
</Pressable>
))
) : (
// Non-recurring event: simple confirmation
<View>
<Text
className="text-base mb-4"
style={{ color: theme.textPrimary }}
>
Möchtest du diesen Termin wirklich löschen?
</Text>
<Pressable
onPress={() => onConfirm("all")}
className="py-3 px-4 rounded-lg"
style={{
backgroundColor: theme.rejectButton,
}}
>
<Text
className="font-medium text-base text-center"
style={{ color: theme.buttonText }}
>
Loeschen
</Text>
</Pressable>
</View>
)}
</View>
{/* Cancel button */}
<Pressable
onPress={onCancel}
className="py-3 items-center"
style={{
borderTopWidth: 1,
borderTopColor: theme.placeholderBg,
}}
>
<Text style={{ color: theme.primeFg }} className="font-bold">
Abbrechen
{option.label}
</Text>
</Pressable>
</Pressable>
</Pressable>
</Modal>
))
) : (
// Non-recurring event: simple confirmation
<View>
<Text className="text-base mb-4" style={{ color: theme.textPrimary }}>
Moechtest du diesen Termin wirklich loeschen?
</Text>
<Pressable
onPress={() => onConfirm("all")}
className="py-3 px-4 rounded-lg"
style={{
backgroundColor: theme.rejectButton,
}}
>
<Text
className="font-medium text-base text-center"
style={{ color: theme.buttonText }}
>
Loeschen
</Text>
</Pressable>
</View>
)}
</ModalBase>
);
};