mirror of
https://github.com/hanzoai/calendar.git
synced 2026-10-30 14:46:43 +00:00
* feat(companion): add dark mode support using NativeWind system preference - Set userInterfaceStyle to 'automatic' in app.json to follow system preference - Add useColorScheme hook from NativeWind to detect color scheme - Update root layout with dynamic colors for StatusBar, containers, and modals - Update tabs layout with dynamic colors for tab bar and icons - Update all Stack.Screen options to use dynamic background colors and blur effects - Support both iOS and Android with appropriate dark mode colors Co-Authored-By: [email protected] <[email protected]> * fix(companion): use NativeWind dark: variant classes for automatic dark mode Use dark: Tailwind variant classes instead of conditional logic based on useColorScheme for the container background. This ensures NativeWind automatically applies dark mode styles based on system preference. Co-Authored-By: [email protected] <[email protected]> * fix(companion): use darkMode 'media' for automatic dark mode detection - Changed darkMode from 'class' to 'media' in tailwind.config.js - Updated global.css to use @media (prefers-color-scheme: dark) instead of .dark selector - This ensures NativeWind properly detects system color scheme on iOS/Android Co-Authored-By: [email protected] <[email protected]> * dark mode starting * feat(companion): add dark mode support to core infrastructure (Phase 1) * feat(companion): add dark mode support to tab pages (Phase 2) * fix(companion): add dark mode support to booking list components for font visibility * fix(companion): add dark mode support to EventTypeListItem components * event types and bookings page * Availability list page * more page, and some ui fixes * feat(companion): add dark mode support to detail screens and modals - BookingDetailScreen (iOS & Android): dynamic colors for all UI elements - AvailabilityDetailScreen (iOS & Android): dark mode backgrounds and text - profile-sheet (iOS & Android): dark mode support for profile modal - RescheduleScreen (all platforms): dark mode for date/time pickers - EditLocationScreen (iOS & Android): dark mode for location editor - AddGuestsScreen: partial dark mode implementation (in progress) Uses iOS system colors: #000000 (black), #1C1C1E (secondary), #FFFFFF (white), #38383A (border), #8E8E93 (text secondary) * fix typecheck and lint * feat(companion): add dark mode support to remaining screens - ScreenWrapper: Fix hardcoded #800020 color with dynamic destructive color - EditAvailabilityDayScreen: Add dark mode for Android/Web and iOS - EditAvailabilityHoursScreen: Add dark mode for Android/Web and iOS - EditAvailabilityNameScreen: Add dark mode for Android/Web and iOS - EditAvailabilityOverrideScreen: Add dark mode for Web, iOS, and Android - MarkNoShowScreen: Add dark mode with dynamic colors for no-show states - MeetingSessionDetailsScreen: Add dark mode for session details - ViewRecordingsScreen: Add dark mode for recordings list Uses iOS system colors (#000000, #1C1C1E, #38383A, #8E8E93) and useColorScheme hook for automatic theme detection. * fix(companion): fix iOS modal dark mode backgrounds for transparent glass UI * fix(companion): fix dark mode for booking action screens in transparent background mode * fix(companion): fix Android dark mode for transparent background in EditAvailabilityOverrideScreen * refactor(companion): centralize dark mode colors and align with main website - Update tailwind.config.js with centralized color definitions - Create constants/colors.ts for inline style color references - Replace iOS system colors with main website neutral grays: - #1C1C1E -> #171717 (secondary dark background) - #38383A -> #4D4D4D (dark border) - #8E8E93 -> #A3A3A3 (secondary text dark) - Keep pure black (#000000) for main backgrounds (OLED friendly) - Add semantic color mappings for consistent UI styling * feat(companion): complete dark mode implementation across all priority files - Priority 1: Updated SettingsUI.tsx, BasicsTab.tsx, LimitsTab.tsx, AdvancedTab.tsx, RecurringTab.tsx, AvailabilityTab.tsx, event-type-detail.tsx - Priority 2: Updated edit-availability-hours/name/override/day.tsx, oauth/callback.tsx - Priority 3: Updated AvailabilityListScreen.tsx with dark mode for modals and list items - Priority 4: Updated BookingActionsModal.tsx, BookingActionsModal.ios.tsx, GlassModalHeader.tsx, LoginScreen.tsx, LocationsList.tsx, SearchHeader.tsx, toast.tsx Dark mode colors used: - Main bg: #000000 (cal.bg.dark) - Secondary bg: #171717 (cal.bg.secondaryDark) - Muted bg: #262626 (cal.bg.mutedDark) - Borders: #4D4D4D (cal.border.dark) - Secondary text: #A3A3A3 (cal.text.secondaryDark) * fix(companion): add dark mode support to iOS header, GlassView modals, and main content background * fix(companion): add dark mode support to toggle/switch components * fix(companion): use iOS green (#34C759) for toggle ON state in dark mode * fix(companion): use white track + black thumb for dark mode toggles * fix(companion): add dark mode support to login page logo and button * dark mode to many pages, centralize the colors * dark mode skelaton * Availability pages android * toggles * headers * red color for dark mode * copy button * white flash on bottom ui native ios sheet * bottom sheet buttons * bottom sheet in gray color * bottom sheet again.. yeahh * toggle green * toggle green * fix lint and typechecks * fix exntension error * Update companion/components/event-type-detail/tabs/LimitsTabDatePicker.android.tsx Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * address cubic comments --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: [email protected] <[email protected]> Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
522 lines
18 KiB
TypeScript
522 lines
18 KiB
TypeScript
import { Ionicons } from "@expo/vector-icons";
|
||
import { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from "react";
|
||
import { Alert, Pressable, ScrollView, Switch, Text, useColorScheme, View } from "react-native";
|
||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||
import { DateTimePickerAndroid } from "@react-native-community/datetimepicker";
|
||
import { useUpdateSchedule } from "@/hooks/useSchedules";
|
||
import type { Schedule } from "@/services/calcom";
|
||
import { showErrorAlert, showSuccessAlert } from "@/utils/alerts";
|
||
import { getColors } from "@/constants/colors";
|
||
|
||
// Convert 24-hour time to 12-hour format with AM/PM
|
||
const formatTime12Hour = (time24: string): string => {
|
||
const [hours, minutes] = time24.split(":");
|
||
const hour = parseInt(hours, 10);
|
||
const min = minutes || "00";
|
||
const period = hour >= 12 ? "PM" : "AM";
|
||
const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
|
||
return `${hour12}:${min} ${period}`;
|
||
};
|
||
|
||
// Parse time string to Date object (for DatePicker)
|
||
const timeStringToDate = (timeStr: string): Date => {
|
||
const [hours, minutes] = timeStr.split(":");
|
||
const date = new Date();
|
||
date.setHours(parseInt(hours, 10));
|
||
date.setMinutes(parseInt(minutes || "0", 10));
|
||
date.setSeconds(0);
|
||
date.setMilliseconds(0);
|
||
return date;
|
||
};
|
||
|
||
// Convert Date to time string (HH:mm)
|
||
const dateToTimeString = (date: Date): string => {
|
||
const hours = date.getHours().toString().padStart(2, "0");
|
||
const minutes = date.getMinutes().toString().padStart(2, "0");
|
||
return `${hours}:${minutes}`;
|
||
};
|
||
|
||
// Normalize time string to HH:mm format (API may return HH:mm:ss)
|
||
const normalizeTimeString = (time: string): string => {
|
||
return time.substring(0, 5);
|
||
};
|
||
|
||
// Convert Date to date string (YYYY-MM-DD)
|
||
const dateToDateString = (date: Date): string => {
|
||
const year = date.getFullYear();
|
||
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
||
const day = date.getDate().toString().padStart(2, "0");
|
||
return `${year}-${month}-${day}`;
|
||
};
|
||
|
||
// Parse date string to Date object
|
||
const dateStringToDate = (dateStr: string): Date => {
|
||
const [year, month, day] = dateStr.split("-").map(Number);
|
||
return new Date(year, month - 1, day);
|
||
};
|
||
|
||
// Format date for display
|
||
const formatDateForDisplay = (date: Date): string => {
|
||
return date.toLocaleDateString("en-US", {
|
||
weekday: "long",
|
||
month: "long",
|
||
day: "numeric",
|
||
year: "numeric",
|
||
});
|
||
};
|
||
|
||
export interface EditAvailabilityOverrideScreenProps {
|
||
schedule: Schedule | null;
|
||
overrideIndex?: number;
|
||
onSuccess: () => void;
|
||
onSavingChange?: (isSaving: boolean) => void;
|
||
onEditOverride?: (index: number) => void;
|
||
transparentBackground?: boolean;
|
||
}
|
||
|
||
export interface EditAvailabilityOverrideScreenHandle {
|
||
submit: () => void;
|
||
}
|
||
|
||
export const EditAvailabilityOverrideScreen = forwardRef<
|
||
EditAvailabilityOverrideScreenHandle,
|
||
EditAvailabilityOverrideScreenProps
|
||
>(function EditAvailabilityOverrideScreen(
|
||
{
|
||
schedule,
|
||
overrideIndex,
|
||
onSuccess,
|
||
onSavingChange,
|
||
onEditOverride,
|
||
transparentBackground = false,
|
||
},
|
||
ref
|
||
) {
|
||
const insets = useSafeAreaInsets();
|
||
const colorScheme = useColorScheme();
|
||
const isDark = colorScheme === "dark";
|
||
const theme = getColors(isDark);
|
||
const backgroundStyle = transparentBackground
|
||
? "bg-transparent"
|
||
: isDark
|
||
? "bg-black"
|
||
: `bg-[${theme.backgroundMuted}]`;
|
||
|
||
// Use mutation hook for cache-synchronized updates
|
||
const { mutate: updateSchedule, isPending: isMutating } = useUpdateSchedule();
|
||
|
||
const isEditing = overrideIndex !== undefined;
|
||
|
||
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
|
||
const [isUnavailable, setIsUnavailable] = useState(false);
|
||
const [startTime, setStartTime] = useState<Date>(timeStringToDate("09:00"));
|
||
const [endTime, setEndTime] = useState<Date>(timeStringToDate("17:00"));
|
||
|
||
// Initialize from existing override if editing
|
||
useEffect(() => {
|
||
if (schedule && isEditing && schedule.overrides) {
|
||
const override = schedule.overrides[overrideIndex];
|
||
if (override) {
|
||
setSelectedDate(dateStringToDate(override.date ?? ""));
|
||
// Normalize time strings to HH:mm (API may return HH:mm:ss)
|
||
const start = normalizeTimeString(override.startTime ?? "00:00");
|
||
const end = normalizeTimeString(override.endTime ?? "00:00");
|
||
setIsUnavailable(start === "00:00" && end === "00:00");
|
||
setStartTime(timeStringToDate(start === "00:00" ? "09:00" : start));
|
||
setEndTime(timeStringToDate(end === "00:00" ? "17:00" : end));
|
||
}
|
||
}
|
||
}, [schedule, isEditing, overrideIndex]);
|
||
|
||
// Notify parent of saving state
|
||
useEffect(() => {
|
||
onSavingChange?.(isMutating);
|
||
}, [isMutating, onSavingChange]);
|
||
|
||
// Open native Android date picker
|
||
const openDatePicker = useCallback(() => {
|
||
DateTimePickerAndroid.open({
|
||
value: selectedDate,
|
||
mode: "date",
|
||
onChange: (event, date) => {
|
||
if (event.type === "set" && date) {
|
||
setSelectedDate(date);
|
||
}
|
||
},
|
||
});
|
||
}, [selectedDate]);
|
||
|
||
// Open native Android time picker for start time
|
||
const openStartTimePicker = useCallback(() => {
|
||
DateTimePickerAndroid.open({
|
||
value: startTime,
|
||
mode: "time",
|
||
is24Hour: false,
|
||
onChange: (event, date) => {
|
||
if (event.type === "set" && date) {
|
||
setStartTime(date);
|
||
}
|
||
},
|
||
});
|
||
}, [startTime]);
|
||
|
||
// Open native Android time picker for end time
|
||
const openEndTimePicker = useCallback(() => {
|
||
DateTimePickerAndroid.open({
|
||
value: endTime,
|
||
mode: "time",
|
||
is24Hour: false,
|
||
onChange: (event, date) => {
|
||
if (event.type === "set" && date) {
|
||
setEndTime(date);
|
||
}
|
||
},
|
||
});
|
||
}, [endTime]);
|
||
|
||
const saveOverrides = useCallback(
|
||
(
|
||
newOverrides: { date: string; startTime: string; endTime: string }[],
|
||
successMessage: string
|
||
) => {
|
||
if (!schedule) return;
|
||
|
||
updateSchedule(
|
||
{ id: schedule.id, updates: { overrides: newOverrides } },
|
||
{
|
||
onSuccess: () => {
|
||
showSuccessAlert("Success", successMessage);
|
||
onSuccess();
|
||
},
|
||
onError: () => {
|
||
showErrorAlert("Error", "Failed to save override. Please try again.");
|
||
},
|
||
}
|
||
);
|
||
},
|
||
[schedule, onSuccess, updateSchedule]
|
||
);
|
||
|
||
const handleDeleteOverride = useCallback(
|
||
(indexToDelete: number) => {
|
||
if (!schedule || !schedule.overrides) return;
|
||
|
||
const override = schedule.overrides[indexToDelete];
|
||
const dateDisplay = formatDateForDisplay(dateStringToDate(override?.date ?? ""));
|
||
|
||
Alert.alert(
|
||
"Delete Override",
|
||
`Are you sure you want to delete the override for ${dateDisplay}?`,
|
||
[
|
||
{ text: "Cancel", style: "cancel" },
|
||
{
|
||
text: "Delete",
|
||
style: "destructive",
|
||
onPress: async () => {
|
||
const newOverrides = schedule.overrides
|
||
? schedule.overrides
|
||
.filter((_, idx) => idx !== indexToDelete)
|
||
.map((o) => ({
|
||
date: o.date ?? "",
|
||
startTime: (o.startTime ?? "00:00").substring(0, 5),
|
||
endTime: (o.endTime ?? "00:00").substring(0, 5),
|
||
}))
|
||
: [];
|
||
await saveOverrides(newOverrides, "Override deleted successfully");
|
||
},
|
||
},
|
||
]
|
||
);
|
||
},
|
||
[schedule, saveOverrides]
|
||
);
|
||
|
||
const handleSubmit = useCallback(() => {
|
||
if (!schedule || isMutating) return;
|
||
|
||
const dateStr = dateToDateString(selectedDate);
|
||
|
||
// Validate end time is after start time (only when not marking as unavailable)
|
||
// Compare time strings to avoid issues with Date object day components
|
||
const startTimeStr = dateToTimeString(startTime);
|
||
const endTimeStr = dateToTimeString(endTime);
|
||
if (!isUnavailable && endTimeStr <= startTimeStr) {
|
||
showErrorAlert("Error", "End time must be after start time");
|
||
return;
|
||
}
|
||
|
||
// Build the new override
|
||
const newOverride = {
|
||
date: dateStr,
|
||
startTime: isUnavailable ? "00:00" : dateToTimeString(startTime),
|
||
endTime: isUnavailable ? "00:00" : dateToTimeString(endTime),
|
||
};
|
||
|
||
// Build new overrides array
|
||
let newOverrides: { date: string; startTime: string; endTime: string }[] = [];
|
||
|
||
if (schedule.overrides && Array.isArray(schedule.overrides)) {
|
||
newOverrides = schedule.overrides.map((o) => ({
|
||
date: o.date ?? "",
|
||
startTime: (o.startTime ?? "00:00").substring(0, 5),
|
||
endTime: (o.endTime ?? "00:00").substring(0, 5),
|
||
}));
|
||
}
|
||
|
||
const successMessage = isEditing
|
||
? "Override updated successfully"
|
||
: "Override added successfully";
|
||
|
||
if (isEditing && overrideIndex !== undefined) {
|
||
// Check if date was changed to one that already exists (exclude current override)
|
||
const existingIndex = newOverrides.findIndex(
|
||
(o, idx) => o.date === dateStr && idx !== overrideIndex
|
||
);
|
||
if (existingIndex >= 0) {
|
||
Alert.alert(
|
||
"Date Already Exists",
|
||
"An override for this date already exists. Do you want to replace it?",
|
||
[
|
||
{ text: "Cancel", style: "cancel" },
|
||
{
|
||
text: "Replace",
|
||
onPress: () => {
|
||
// Remove the conflicting override and update the current one
|
||
const filteredOverrides = newOverrides.filter((_, idx) => idx !== existingIndex);
|
||
// Adjust index if the removed item was before our edit index
|
||
const adjustedIndex =
|
||
existingIndex < overrideIndex ? overrideIndex - 1 : overrideIndex;
|
||
filteredOverrides[adjustedIndex] = newOverride;
|
||
saveOverrides(filteredOverrides, "Override updated successfully");
|
||
},
|
||
},
|
||
]
|
||
);
|
||
return;
|
||
}
|
||
// Update existing override
|
||
newOverrides[overrideIndex] = newOverride;
|
||
} else {
|
||
// Check if date already exists
|
||
const existingIndex = newOverrides.findIndex((o) => o.date === dateStr);
|
||
if (existingIndex >= 0) {
|
||
Alert.alert(
|
||
"Date Already Exists",
|
||
"An override for this date already exists. Do you want to replace it?",
|
||
[
|
||
{ text: "Cancel", style: "cancel" },
|
||
{
|
||
text: "Replace",
|
||
onPress: () => {
|
||
newOverrides[existingIndex] = newOverride;
|
||
saveOverrides(newOverrides, "Override replaced successfully");
|
||
},
|
||
},
|
||
]
|
||
);
|
||
return;
|
||
}
|
||
// Add new override
|
||
newOverrides.push(newOverride);
|
||
}
|
||
|
||
saveOverrides(newOverrides, successMessage);
|
||
}, [
|
||
schedule,
|
||
selectedDate,
|
||
isUnavailable,
|
||
startTime,
|
||
endTime,
|
||
isEditing,
|
||
overrideIndex,
|
||
saveOverrides,
|
||
isMutating,
|
||
]);
|
||
|
||
// Expose submit to parent via ref
|
||
useImperativeHandle(
|
||
ref,
|
||
() => ({
|
||
submit: handleSubmit,
|
||
}),
|
||
[handleSubmit]
|
||
);
|
||
|
||
if (!schedule) {
|
||
return (
|
||
<View className={`flex-1 items-center justify-center ${backgroundStyle}`}>
|
||
<Text className="text-[#A3A3A3]">No schedule data</Text>
|
||
</View>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<ScrollView
|
||
className={`flex-1 ${backgroundStyle}`}
|
||
contentContainerStyle={{
|
||
padding: 16,
|
||
paddingBottom: insets.bottom + 16,
|
||
}}
|
||
showsVerticalScrollIndicator={!transparentBackground}
|
||
>
|
||
<Text className="mb-2 px-1 text-[13px] font-medium text-[#A3A3A3]">Date</Text>
|
||
<Pressable
|
||
onPress={openDatePicker}
|
||
className={`mb-4 rounded-xl px-4 py-3.5 ${
|
||
transparentBackground
|
||
? isDark
|
||
? "border border-[#4D4D4D]/40 bg-[#171717]/80"
|
||
: "border border-gray-300/40 bg-white/60"
|
||
: isDark
|
||
? "bg-[#171717]"
|
||
: "bg-white"
|
||
}`}
|
||
>
|
||
<View className="flex-row items-center justify-between">
|
||
<View className="flex-row items-center">
|
||
<View className="mr-3 h-9 w-9 items-center justify-center rounded-lg bg-[#007AFF]/10">
|
||
<Ionicons name="calendar" size={20} color="#007AFF" />
|
||
</View>
|
||
<Text className={`text-[17px] ${isDark ? "text-white" : "text-black"}`}>
|
||
{formatDateForDisplay(selectedDate)}
|
||
</Text>
|
||
</View>
|
||
<Ionicons name="chevron-forward" size={18} color={isDark ? "#A3A3A3" : "#C7C7CC"} />
|
||
</View>
|
||
</Pressable>
|
||
|
||
<View
|
||
className={`mb-4 flex-row items-center justify-between rounded-xl px-4 py-3.5 ${
|
||
transparentBackground
|
||
? isDark
|
||
? "border border-[#4D4D4D]/40 bg-[#171717]/80"
|
||
: "border border-gray-300/40 bg-white/60"
|
||
: isDark
|
||
? "bg-[#171717]"
|
||
: "bg-white"
|
||
}`}
|
||
>
|
||
<View className="flex-row items-center">
|
||
<View className="mr-3 h-9 w-9 items-center justify-center rounded-lg bg-[#FF3B30]/10">
|
||
<Ionicons name="moon-outline" size={20} color="#FF3B30" />
|
||
</View>
|
||
<View>
|
||
<Text className={`text-[17px] ${isDark ? "text-white" : "text-black"}`}>
|
||
Mark as unavailable
|
||
</Text>
|
||
<Text className="text-[13px] text-[#A3A3A3]">Block this entire day</Text>
|
||
</View>
|
||
</View>
|
||
<Switch
|
||
value={isUnavailable}
|
||
onValueChange={setIsUnavailable}
|
||
trackColor={{
|
||
false: isDark ? theme.backgroundEmphasis : "#E5E5EA",
|
||
true: isDark ? "#34C759" : "#000000",
|
||
}}
|
||
thumbColor="#FFFFFF"
|
||
/>
|
||
</View>
|
||
|
||
{!isUnavailable && (
|
||
<>
|
||
<Text className="mb-2 px-1 text-[13px] font-medium text-[#A3A3A3]">Available Hours</Text>
|
||
<View
|
||
className={`mb-4 rounded-xl px-4 py-3.5 ${
|
||
transparentBackground
|
||
? isDark
|
||
? "border border-[#4D4D4D]/40 bg-[#171717]/80"
|
||
: "border border-gray-300/40 bg-white/60"
|
||
: isDark
|
||
? "bg-[#171717]"
|
||
: "bg-white"
|
||
}`}
|
||
>
|
||
<View className="flex-row items-center">
|
||
<Pressable onPress={openStartTimePicker} className="flex-1">
|
||
<Text className="mb-1 text-[13px] text-[#A3A3A3]">Start Time</Text>
|
||
<Text className={`text-[17px] ${isDark ? "text-white" : "text-black"}`}>
|
||
{formatTime12Hour(dateToTimeString(startTime))}
|
||
</Text>
|
||
</Pressable>
|
||
|
||
<Text className="mx-4 text-[17px] text-[#A3A3A3]">–</Text>
|
||
|
||
<Pressable onPress={openEndTimePicker} className="flex-1">
|
||
<Text className="mb-1 text-[13px] text-[#A3A3A3]">End Time</Text>
|
||
<Text className={`text-[17px] ${isDark ? "text-white" : "text-black"}`}>
|
||
{formatTime12Hour(dateToTimeString(endTime))}
|
||
</Text>
|
||
</Pressable>
|
||
</View>
|
||
</View>
|
||
</>
|
||
)}
|
||
|
||
{!isEditing && schedule.overrides && schedule.overrides.length > 0 && (
|
||
<>
|
||
<Text className="mb-2 mt-4 px-1 text-[13px] font-medium text-[#A3A3A3]">
|
||
Existing Overrides
|
||
</Text>
|
||
<View
|
||
className={`overflow-hidden rounded-xl ${
|
||
transparentBackground
|
||
? isDark
|
||
? "border border-[#4D4D4D]/40 bg-[#171717]/80"
|
||
: "border border-gray-300/40 bg-white/60"
|
||
: isDark
|
||
? "bg-[#171717]"
|
||
: "bg-white"
|
||
}`}
|
||
>
|
||
{schedule.overrides.map((override, index) => (
|
||
<Pressable
|
||
key={override.date}
|
||
className={`flex-row items-center justify-between px-4 py-3 ${
|
||
index > 0
|
||
? isDark
|
||
? "border-t border-[#4D4D4D]"
|
||
: "border-t border-[#E5E5EA]"
|
||
: ""
|
||
}`}
|
||
onPress={() => onEditOverride?.(index)}
|
||
>
|
||
<View className="flex-1">
|
||
<Text className={`text-[15px] ${isDark ? "text-white" : "text-black"}`}>
|
||
{formatDateForDisplay(dateStringToDate(override.date ?? ""))}
|
||
</Text>
|
||
{normalizeTimeString(override.startTime ?? "00:00") === "00:00" &&
|
||
normalizeTimeString(override.endTime ?? "00:00") === "00:00" ? (
|
||
<Text className="text-[13px] text-[#FF3B30]">Unavailable</Text>
|
||
) : (
|
||
<Text className="text-[13px] text-[#A3A3A3]">
|
||
{formatTime12Hour(normalizeTimeString(override.startTime ?? "00:00"))} –{" "}
|
||
{formatTime12Hour(normalizeTimeString(override.endTime ?? "00:00"))}
|
||
</Text>
|
||
)}
|
||
</View>
|
||
<View className="flex-row items-center">
|
||
<Pressable
|
||
className="rounded-full bg-[#FF3B30]/10 p-2"
|
||
onPress={() => handleDeleteOverride(index)}
|
||
>
|
||
<Ionicons name="trash-outline" size={18} color="#FF3B30" />
|
||
</Pressable>
|
||
<Pressable
|
||
className="ml-3 rounded-full bg-[#A3A3A3]/10 p-2"
|
||
onPress={() => onEditOverride?.(index)}
|
||
>
|
||
<Ionicons name="chevron-forward" size={18} color="#A3A3A3" />
|
||
</Pressable>
|
||
</View>
|
||
</Pressable>
|
||
))}
|
||
</View>
|
||
</>
|
||
)}
|
||
</ScrollView>
|
||
);
|
||
});
|
||
|
||
export default EditAvailabilityOverrideScreen;
|