diff --git a/companion/app/(tabs)/(availability)/_layout.tsx b/companion/app/(tabs)/(availability)/_layout.tsx
new file mode 100644
index 0000000000..ca52620628
--- /dev/null
+++ b/companion/app/(tabs)/(availability)/_layout.tsx
@@ -0,0 +1,9 @@
+import { Stack } from "expo-router";
+
+export default function EventTypesLayout() {
+ return (
+
+
+
+ );
+}
diff --git a/companion/app/(tabs)/availability.tsx b/companion/app/(tabs)/(availability)/index.tsx
similarity index 74%
rename from companion/app/(tabs)/availability.tsx
rename to companion/app/(tabs)/(availability)/index.tsx
index 1aa8f86882..462015b486 100644
--- a/companion/app/(tabs)/availability.tsx
+++ b/companion/app/(tabs)/(availability)/index.tsx
@@ -1,6 +1,6 @@
import { Ionicons } from "@expo/vector-icons";
-import { useRouter } from "expo-router";
-import React, { useState, useMemo } from "react";
+import { Stack, useRouter } from "expo-router";
+import React, { useState, useMemo, Activity } from "react";
import {
View,
Text,
@@ -10,27 +10,27 @@ import {
ActionSheetIOS,
Alert,
Platform,
- Modal,
TextInput,
- KeyboardAvoidingView,
ScrollView,
} from "react-native";
-import { CalComAPIService, Schedule } from "../../services/calcom";
-import { Header } from "../../components/Header";
-import { FullScreenModal } from "../../components/FullScreenModal";
-import { LoadingSpinner } from "../../components/LoadingSpinner";
-import { EmptyScreen } from "../../components/EmptyScreen";
-import { showErrorAlert } from "../../utils/alerts";
-import { offlineAwareRefresh } from "../../utils/network";
-import { shadows } from "../../utils/shadows";
+import { CalComAPIService, Schedule } from "../../../services/calcom";
+import { Header } from "../../../components/Header";
+import { FullScreenModal } from "../../../components/FullScreenModal";
+import { LoadingSpinner } from "../../../components/LoadingSpinner";
+import { EmptyScreen } from "../../../components/EmptyScreen";
+import { showErrorAlert } from "../../../utils/alerts";
+import { offlineAwareRefresh } from "../../../utils/network";
+import { shadows } from "../../../utils/shadows";
import {
useSchedules,
useCreateSchedule,
useDeleteSchedule,
useDuplicateSchedule,
useSetScheduleAsDefault,
-} from "../../hooks";
+} from "../../../hooks";
+import { isLiquidGlassAvailable } from "expo-glass-effect";
+import { AvailabilityListItem } from "../../../components/availability-list-item/AvailabilityListItem";
export default function Availability() {
const router = useRouter();
@@ -261,65 +261,6 @@ export default function Availability() {
);
};
- const renderSchedule = ({ item: schedule, index }: { item: Schedule; index: number }) => {
- return (
- handleSchedulePress(schedule)}
- onLongPress={() => handleScheduleLongPress(schedule)}
- style={{ paddingHorizontal: 16, paddingVertical: 16 }}
- >
-
-
-
- {schedule.name}
- {schedule.isDefault ? (
-
- Default
-
- ) : null}
-
-
- {schedule.availability && schedule.availability.length > 0 ? (
-
- {schedule.availability.map((slot, slotIndex) => (
- 0 ? "mt-2" : ""}
- >
-
- {slot.days.join(", ")} {slot.startTime} - {slot.endTime}
-
-
- ))}
-
- ) : (
- No availability set
- )}
-
-
-
- {schedule.timeZone}
-
-
-
- {/* Three dots button - vertically centered on the right */}
- {
- e.stopPropagation();
- setSelectedSchedule(schedule);
- setShowActionsModal(true);
- }}
- >
-
-
-
-
- );
- };
-
if (loading) {
return (
@@ -356,93 +297,126 @@ export default function Availability() {
const showList = !showEmptyState && !showSearchEmptyState;
return (
-
-
+ <>
+
+ Availability
+
+
+ New
+
+
+ handleSearch(e.nativeEvent.text)}
+ obscureBackground={false}
+ barTintColor="#fff"
+ />
+
+
+
+
+
+
+
+
+ New
+
+
+
{/* Empty state - no schedules */}
- {showEmptyState ? (
-
+
+ }
+ contentInsetAdjustmentBehavior="automatic"
+ >
+
+
+
+
+ {/* Search bar and content for non-empty states */}
+
+ {/* Search empty state */}
+
}
>
-
- ) : null}
+
- {/* Search bar and content for non-empty states */}
- {!showEmptyState ? (
- <>
-
-
-
-
- New
-
-
-
- {/* Search empty state */}
- {showSearchEmptyState ? (
-
- }
- >
-
-
-
- ) : null}
-
- {/* Schedules list */}
- {showList ? (
-
-
- item.id.toString()}
- renderItem={renderSchedule}
- contentContainerStyle={{ paddingBottom: 90 }}
- refreshControl={}
- showsVerticalScrollIndicator={false}
- />
-
-
- ) : null}
- >
- ) : null}
+ {/* Schedules list */}
+
+ item.id.toString()}
+ renderItem={({ item, index }) => (
+
+ )}
+ refreshControl={}
+ showsVerticalScrollIndicator={false}
+ contentInsetAdjustmentBehavior="automatic"
+ />
+
+
{/* Create Schedule Modal */}
{/* Set as Default - only show if not already default */}
- {selectedSchedule && !selectedSchedule.isDefault ? (
+
<>
{
@@ -553,7 +529,7 @@ export default function Availability() {
>
- ) : null}
+
{/* Duplicate */}
-
+ >
);
}
diff --git a/companion/app/(tabs)/(event-types)/index.tsx b/companion/app/(tabs)/(event-types)/index.tsx
index 941205d88d..5a82fdea97 100644
--- a/companion/app/(tabs)/(event-types)/index.tsx
+++ b/companion/app/(tabs)/(event-types)/index.tsx
@@ -473,6 +473,7 @@ export default function EventTypes() {
Event Types
@@ -487,7 +488,7 @@ export default function EventTypes() {
barTintColor="#fff"
/>
-
+
Bookings
-
+
}
diff --git a/companion/components/availability-list-item/AvailabilityListItem.ios.tsx b/companion/components/availability-list-item/AvailabilityListItem.ios.tsx
new file mode 100644
index 0000000000..907af3afd1
--- /dev/null
+++ b/companion/components/availability-list-item/AvailabilityListItem.ios.tsx
@@ -0,0 +1,118 @@
+import { Ionicons } from "@expo/vector-icons";
+import { Pressable, Text, View } from "react-native";
+import { AvailabilityListItemProps } from "./AvailabilityListItem";
+import { Host, ContextMenu, Button, Image, HStack } from "@expo/ui/swift-ui";
+import { isLiquidGlassAvailable } from "expo-glass-effect";
+import { buttonStyle, frame, padding } from "@expo/ui/swift-ui/modifiers";
+
+export const AvailabilityListItem = ({
+ item: schedule,
+ handleSchedulePress,
+ onDuplicate,
+ onDelete,
+ onSetAsDefault,
+}: AvailabilityListItemProps) => {
+ const scheduleActions: {
+ label: string;
+ icon: any;
+ onPress: () => void;
+ role: "default" | "destructive";
+ }[] = [
+ ...(!schedule.isDefault && onSetAsDefault
+ ? [
+ {
+ label: "Set as Default",
+ icon: "star",
+ onPress: () => onSetAsDefault(schedule),
+ role: "default" as const,
+ },
+ ]
+ : []),
+ {
+ label: "Duplicate",
+ icon: "square.on.square",
+ onPress: () => onDuplicate?.(schedule),
+ role: "default",
+ },
+ {
+ label: "Delete",
+ icon: "trash",
+ onPress: () => onDelete?.(schedule),
+ role: "destructive",
+ },
+ ];
+
+ return (
+
+
+ handleSchedulePress(schedule)} className="flex-grow">
+
+
+ {schedule.name}
+ {schedule.isDefault && (
+
+ Default
+
+ )}
+
+
+ {schedule.availability && schedule.availability.length > 0 ? (
+
+ {schedule.availability.map((slot, slotIndex) => (
+ 0 ? "mt-2" : ""}
+ >
+
+ {slot.days.join(", ")} {slot.startTime} - {slot.endTime}
+
+
+ ))}
+
+ ) : (
+ No availability set
+ )}
+
+
+
+ {schedule.timeZone}
+
+
+
+
+ {/* Three dots button - vertically centered on the right */}
+
+
+
+ {scheduleActions.map((scheduleAction) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/companion/components/availability-list-item/AvailabilityListItem.tsx b/companion/components/availability-list-item/AvailabilityListItem.tsx
new file mode 100644
index 0000000000..3c587a4f73
--- /dev/null
+++ b/companion/components/availability-list-item/AvailabilityListItem.tsx
@@ -0,0 +1,80 @@
+import { Ionicons } from "@expo/vector-icons";
+import { Text, TouchableOpacity, View } from "react-native";
+import { Schedule } from "../../hooks";
+
+export interface AvailabilityListItemProps {
+ item: Schedule;
+ index: number;
+ handleSchedulePress: (schedule: Schedule) => void;
+ handleScheduleLongPress: (schedule: Schedule) => void;
+ setSelectedSchedule: (schedule: Schedule) => void;
+ setShowActionsModal: (show: boolean) => void;
+ onDuplicate?: (schedule: Schedule) => void;
+ onDelete?: (schedule: Schedule) => void;
+ onSetAsDefault?: (schedule: Schedule) => void;
+}
+
+export const AvailabilityListItem = ({
+ item: schedule,
+ handleSchedulePress,
+ handleScheduleLongPress,
+ setSelectedSchedule,
+ setShowActionsModal,
+}: AvailabilityListItemProps) => {
+ return (
+ handleSchedulePress(schedule)}
+ onLongPress={() => handleScheduleLongPress(schedule)}
+ style={{ paddingHorizontal: 16, paddingVertical: 16 }}
+ >
+
+
+
+ {schedule.name}
+ {schedule.isDefault && (
+
+ Default
+
+ )}
+
+
+ {schedule.availability && schedule.availability.length > 0 ? (
+
+ {schedule.availability.map((slot, slotIndex) => (
+ 0 ? "mt-2" : ""}
+ >
+
+ {slot.days.join(", ")} {slot.startTime} - {slot.endTime}
+
+
+ ))}
+
+ ) : (
+ No availability set
+ )}
+
+
+
+ {schedule.timeZone}
+
+
+
+ {/* Three dots button - vertically centered on the right */}
+ {
+ e.stopPropagation();
+ setSelectedSchedule(schedule);
+ setShowActionsModal(true);
+ }}
+ >
+
+
+
+
+ );
+};