import { useCallback, useMemo, useState, type ReactElement } from "react";
import { ScrollView, Text, View } from "react-native";
import { useIsFocused } from "@react-navigation/native";
import { Plus } from "lucide-react-native";
import { StyleSheet } from "react-native-unistyles";
import { MenuHeader } from "@/components/headers/menu-header";
import { ScheduleFormSheet } from "@/components/schedules/schedule-form-sheet";
import { SchedulesTable } from "@/components/schedules/schedules-table";
import { Button } from "@/components/ui/button";
import { LoadingSpinner } from "@/components/ui/loading-spinner";
import { useSchedules, type ScheduleHostSection } from "@/hooks/use-schedules";
import type { ScheduleSummary } from "@getpaseo/protocol/schedule/types";
type FormState =
| { mode: "closed" }
| { mode: "create" }
| { mode: "edit"; serverId: string; schedule: ScheduleSummary };
export function SchedulesScreen(): ReactElement {
const isFocused = useIsFocused();
if (!isFocused) {
return ;
}
return ;
}
function SchedulesScreenContent(): ReactElement {
const { sections, isLoading, isError, error, refetch } = useSchedules();
const [form, setForm] = useState({ mode: "closed" });
const openCreate = useCallback(() => {
setForm({ mode: "create" });
}, []);
const openEdit = useCallback((serverId: string, schedule: ScheduleSummary) => {
setForm({ mode: "edit", serverId, schedule });
}, []);
const closeForm = useCallback(() => {
setForm({ mode: "closed" });
}, []);
const headerAction = useMemo(
() => (
),
[openCreate],
);
return (
);
}
function SchedulesBody({
sections,
isLoading,
isError,
error,
onRetry,
onCreate,
onEdit,
}: {
sections: ScheduleHostSection[];
isLoading: boolean;
isError: boolean;
error: Error | null;
onRetry: () => void;
onCreate: () => void;
onEdit: (serverId: string, schedule: ScheduleSummary) => void;
}): ReactElement {
if (isLoading) {
return (
);
}
if (isError) {
return (
{error?.message ?? "Could not load schedules"}
);
}
if (sections.length === 0) {
return (
No schedules yet
);
}
return (
{sections.map((section) => (
))}
);
}
function ScheduleHostSectionView({
section,
onEdit,
}: {
section: ScheduleHostSection;
onEdit: (serverId: string, schedule: ScheduleSummary) => void;
}): ReactElement {
const handleEdit = useCallback(
(schedule: ScheduleSummary) => {
onEdit(section.serverId, schedule);
},
[onEdit, section.serverId],
);
const emptyMessage = section.isOnline ? "No schedules" : "Host offline";
return (
{section.serverName}
{section.error ? {section.error} : null}
{section.schedules.length > 0 ? (
) : null}
{section.schedules.length === 0 && !section.error ? (
{emptyMessage}
) : null}
);
}
const styles = StyleSheet.create((theme) => ({
container: {
flex: 1,
backgroundColor: theme.colors.surface0,
},
centered: {
flex: 1,
justifyContent: "center",
alignItems: "center",
gap: theme.spacing[6],
padding: theme.spacing[6],
},
scroll: {
flex: 1,
minHeight: 0,
},
scrollContent: {
gap: theme.spacing[4],
paddingTop: theme.spacing[4],
paddingBottom: theme.spacing[6],
},
section: {
gap: theme.spacing[2],
},
sectionTitle: {
width: "100%",
maxWidth: 720,
alignSelf: "center",
paddingHorizontal: theme.spacing[4],
color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.sm,
fontWeight: theme.fontWeight.medium,
},
sectionError: {
width: "100%",
maxWidth: 720,
alignSelf: "center",
paddingHorizontal: theme.spacing[4],
color: theme.colors.destructive,
fontSize: theme.fontSize.sm,
},
sectionEmpty: {
width: "100%",
maxWidth: 720,
alignSelf: "center",
paddingHorizontal: theme.spacing[4],
paddingVertical: theme.spacing[3],
},
sectionEmptyText: {
color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.sm,
},
message: {
color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.lg,
textAlign: "center",
},
// Static color holder read by the spinner; keeps the muted token without
// useUnistyles (banned in new code).
spinner: {
color: theme.colors.foregroundMuted,
},
}));