import { useCallback, useState } from "react"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "../ui/dialog"; import { Label } from "../ui/label"; import { RadioGroup, RadioGroupItem } from "../ui/radio-group"; import { Button } from "../ui/button"; import axios from "axios"; import { toast } from "sonner"; import { isDesktop } from "react-device-detect"; import { Drawer, DrawerContent, DrawerTrigger } from "../ui/drawer"; import { useNavigate } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { SelectSeparator } from "../ui/select"; import ActivityIndicator from "../indicators/activity-indicator"; import { LuBug, LuPlay, LuX } from "react-icons/lu"; import { ExportMode } from "@/types/filter"; import { TimeRange } from "@/types/timeline"; import { cn } from "@/lib/utils"; import { CustomTimeSelector } from "./CustomTimeSelector"; const REPLAY_TIME_OPTIONS = ["1", "5", "timeline", "custom"] as const; type ReplayTimeOption = (typeof REPLAY_TIME_OPTIONS)[number]; type DebugReplayContentProps = { currentTime: number; latestTime: number; range?: TimeRange; selectedOption: ReplayTimeOption; isStarting: boolean; onSelectedOptionChange: (option: ReplayTimeOption) => void; onStart: () => void; onCancel: () => void; setRange: (range: TimeRange | undefined) => void; setMode: (mode: ExportMode) => void; }; export function DebugReplayContent({ currentTime, latestTime, range, selectedOption, isStarting, onSelectedOptionChange, onStart, onCancel, setRange, setMode, }: DebugReplayContentProps) { const { t } = useTranslation(["views/replay"]); return (
{isDesktop && ( <> {t("dialog.title")} {t("dialog.description")} )} {/* Time range */}
onSelectedOptionChange(value as ReplayTimeOption) } > {REPLAY_TIME_OPTIONS.map((opt) => (
))}
{/* Custom time inputs */} {selectedOption === "custom" && ( )} {isDesktop && }
{t("button.cancel", { ns: "common" })}
); } type DebugReplayDialogProps = { camera: string; currentTime: number; latestTime: number; range?: TimeRange; mode: ExportMode; setRange: (range: TimeRange | undefined) => void; setMode: (mode: ExportMode) => void; }; export default function DebugReplayDialog({ camera, currentTime, latestTime, range, mode, setRange, setMode, }: DebugReplayDialogProps) { const { t } = useTranslation(["views/replay"]); const navigate = useNavigate(); const [selectedOption, setSelectedOption] = useState("1"); const [isStarting, setIsStarting] = useState(false); const handleTimeOptionChange = useCallback( (option: ReplayTimeOption) => { setSelectedOption(option); if (option === "custom" || option === "timeline") { return; } const minutes = parseInt(option, 10); const end = latestTime; setRange({ after: end - minutes * 60, before: end }); }, [latestTime, setRange], ); const handleStart = useCallback(() => { if (!range || range.before <= range.after) { toast.error( t("dialog.toast.error", { error: "End time must be after start time" }), { position: "top-center" }, ); return; } setIsStarting(true); axios .post("debug_replay/start", { camera: camera, start_time: range.after, end_time: range.before, }) .then((response) => { if (response.status === 200) { toast.success(t("dialog.toast.success"), { position: "top-center", }); setMode("none"); setRange(undefined); navigate("/replay"); } }) .catch((error) => { const errorMessage = error.response?.data?.message || error.response?.data?.detail || "Unknown error"; if (error.response?.status === 409) { toast.error(t("dialog.toast.alreadyActive"), { position: "top-center", closeButton: true, dismissible: false, action: ( ), }); } else { toast.error(t("dialog.toast.error", { error: errorMessage }), { position: "top-center", }); } }) .finally(() => { setIsStarting(false); }); }, [camera, range, navigate, setMode, setRange, t]); const handleCancel = useCallback(() => { setMode("none"); setRange(undefined); }, [setMode, setRange]); const Overlay = isDesktop ? Dialog : Drawer; const Trigger = isDesktop ? DialogTrigger : DrawerTrigger; const Content = isDesktop ? DialogContent : DrawerContent; return ( <> { if (!open) { setMode("none"); } }} > {!isDesktop && ( )} ); } type SaveDebugReplayOverlayProps = { className: string; show: boolean; isStarting: boolean; onSave: () => void; onCancel: () => void; }; export function SaveDebugReplayOverlay({ className, show, isStarting, onSave, onCancel, }: SaveDebugReplayOverlayProps) { const { t } = useTranslation(["views/replay"]); return (
); }