import { useState, ReactNode, useCallback } from "react"; import { SearchResult } from "@/types/search"; import { FrigateConfig } from "@/types/frigateConfig"; import { baseUrl } from "@/api/baseUrl"; import { toast } from "sonner"; import axios from "axios"; import { FiMoreVertical } from "react-icons/fi"; import { Button, buttonVariants } from "@/components/ui/button"; import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger, } from "@/components/ui/context-menu"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import useSWR from "swr"; import { Trans, useTranslation } from "react-i18next"; import BlurredIconButton from "../button/BlurredIconButton"; import { useIsAdmin } from "@/hooks/use-is-admin"; import { useNavigate } from "react-router-dom"; type SearchResultActionsProps = { searchResult: SearchResult; findSimilar: () => void; refreshResults: () => void; showTrackingDetails: () => void; addTrigger: () => void; isContextMenu?: boolean; children?: ReactNode; }; export default function SearchResultActions({ searchResult, findSimilar, refreshResults, showTrackingDetails, addTrigger, isContextMenu = false, children, }: SearchResultActionsProps) { const { t } = useTranslation(["views/explore", "views/replay", "common"]); const isAdmin = useIsAdmin(); const navigate = useNavigate(); const [isStarting, setIsStarting] = useState(false); const { data: config } = useSWR("config"); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const handleDelete = () => { axios .delete(`events/${searchResult.id}`) .then((resp) => { if (resp.status == 200) { toast.success(t("searchResult.deleteTrackedObject.toast.success"), { position: "top-center", }); refreshResults(); } }) .catch((error) => { const errorMessage = error.response?.data?.message || error.response?.data?.detail || "Unknown error"; toast.error( t("searchResult.deleteTrackedObject.toast.error", { errorMessage }), { position: "top-center", }, ); }); }; const handleDebugReplay = useCallback( (event: SearchResult) => { setIsStarting(true); axios .post("debug_replay/start", { camera: event.camera, start_time: event.start_time, end_time: event.end_time, }) .then((response) => { if (response.status === 200) { toast.success(t("dialog.toast.success", { ns: "views/replay" }), { position: "top-center", }); 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", { ns: "views/replay" }), { position: "top-center", closeButton: true, dismissible: false, action: ( ), }, ); } else { toast.error(t("dialog.toast.error", { error: errorMessage }), { position: "top-center", }); } }) .finally(() => { setIsStarting(false); }); }, [navigate, t], ); const MenuItem = isContextMenu ? ContextMenuItem : DropdownMenuItem; const menuItems = ( <> {searchResult.has_clip && ( {t("itemMenu.downloadVideo.label")} )} {searchResult.has_snapshot && ( {t("itemMenu.downloadSnapshot.label")} )} {searchResult.has_snapshot && config?.cameras[searchResult.camera].snapshots.clean_copy && ( {t("itemMenu.downloadCleanSnapshot.label")} )} {searchResult.data.type == "object" && ( {t("itemMenu.viewTrackingDetails.label")} )} {config?.semantic_search?.enabled && searchResult.data.type == "object" && ( {t("itemMenu.findSimilar.label")} )} {isAdmin && config?.semantic_search?.enabled && searchResult.data.type == "object" && ( {t("itemMenu.addTrigger.label")} )} {searchResult.has_clip && ( { handleDebugReplay(searchResult); }} > {isStarting ? t("dialog.starting", { ns: "views/replay" }) : t("itemMenu.debugReplay.label")} )} {isAdmin && ( setDeleteDialogOpen(true)} > {t("button.delete", { ns: "common" })} )} ); return ( <> setDeleteDialogOpen(!deleteDialogOpen)} > {t("dialog.confirmDelete.title")} dialog.confirmDelete.desc {t("button.cancel", { ns: "common" })} {t("button.delete", { ns: "common" })} {isContextMenu ? ( {children} {menuItems} ) : ( <> {menuItems} )} ); }