Files
calchat/apps/client/src/components/EventCardBase.tsx
Linus Waldowsky 0a2aef2098 fix: recurring event display and AI query improvements
- Use occurrenceStart instead of startTime in getEventsInRange so
  recurring events show their actual occurrence date to the AI
- Add lazy CalDAV sync in ChatService (syncOnce before first DB access)
- Add CaldavService.sync() with internal config check (silent no-op)
- Show German recurrence description (e.g. "Jede Woche") instead of
  generic "Wiederkehrend" in EventCardBase via formatRecurrenceRule()
- Move RepeatType and REPEAT_TYPE_LABELS from editEvent to shared
- Separate calendar overlay useFocusEffect from event loading
2026-02-09 18:17:39 +01:00

127 lines
3.2 KiB
TypeScript

import { View, Text } from "react-native";
import { Feather } from "@expo/vector-icons";
import { ReactNode } from "react";
import { useThemeStore } from "../stores/ThemeStore";
import { CardBase } from "./CardBase";
import {
isMultiDayEvent,
formatDateWithWeekday,
formatDateWithWeekdayShort,
formatTime,
formatRecurrenceRule,
} from "@calchat/shared";
type EventCardBaseProps = {
className?: string;
title: string;
startTime: Date;
endTime: Date;
description?: string;
recurrenceRule?: string;
children?: ReactNode;
};
function formatDuration(start: Date, end: Date): string {
const startDate = new Date(start);
const endDate = new Date(end);
const diffMs = endDate.getTime() - startDate.getTime();
const diffMins = Math.round(diffMs / 60000);
if (diffMins < 60) {
return `${diffMins} min`;
}
const hours = Math.floor(diffMins / 60);
const mins = diffMins % 60;
if (mins === 0) {
return hours === 1 ? "1 Std" : `${hours} Std`;
}
return `${hours} Std ${mins} min`;
}
export const EventCardBase = ({
className,
title,
startTime,
endTime,
description,
recurrenceRule,
children,
}: EventCardBaseProps) => {
const { theme } = useThemeStore();
const multiDay = isMultiDayEvent(startTime, endTime);
return (
<CardBase title={title} className={className} borderWidth={2}>
{/* Date */}
<View className="flex-row items-center mb-1">
<Feather
name="calendar"
size={16}
color={theme.textPrimary}
style={{ marginRight: 8 }}
/>
{multiDay ? (
<Text style={{ color: theme.textPrimary }}>
{formatDateWithWeekdayShort(startTime)} {" "}
{formatDateWithWeekday(endTime)}
</Text>
) : (
<Text style={{ color: theme.textPrimary }}>
{formatDateWithWeekday(startTime)}
</Text>
)}
</View>
{/* Time with duration */}
<View className="flex-row items-center mb-1">
<Feather
name="clock"
size={16}
color={theme.textPrimary}
style={{ marginRight: 8 }}
/>
{multiDay ? (
<Text style={{ color: theme.textPrimary }}>
{formatTime(startTime)} {formatTime(endTime)}
</Text>
) : (
<Text style={{ color: theme.textPrimary }}>
{formatTime(startTime)} - {formatTime(endTime)} (
{formatDuration(startTime, endTime)})
</Text>
)}
</View>
{/* Recurring indicator */}
{recurrenceRule && (
<View className="flex-row items-center mb-1">
<Feather
name="repeat"
size={16}
color={theme.textPrimary}
style={{ marginRight: 8 }}
/>
<Text style={{ color: theme.textPrimary }}>
{formatRecurrenceRule(recurrenceRule)}
</Text>
</View>
)}
{/* Description */}
{description && (
<Text style={{ color: theme.textPrimary }} className="text-sm mt-1">
{description}
</Text>
)}
{/* Action buttons slot */}
{children}
</CardBase>
);
};
export default EventCardBase;