2024-10-22 17:01:01 +03:00
|
|
|
import { useState, ReactNode } 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";
|
2025-03-06 19:50:37 +03:00
|
|
|
import { LuCamera, LuDownload, LuTrash2 } from "react-icons/lu";
|
|
|
|
|
import { FiMoreVertical } from "react-icons/fi";
|
2024-10-22 17:01:01 +03:00
|
|
|
import { FaArrowsRotate } from "react-icons/fa6";
|
|
|
|
|
import { MdImageSearch } from "react-icons/md";
|
|
|
|
|
import FrigatePlusIcon from "@/components/icons/FrigatePlusIcon";
|
|
|
|
|
import { isMobileOnly } from "react-device-detect";
|
|
|
|
|
import { 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 {
|
|
|
|
|
Tooltip,
|
|
|
|
|
TooltipContent,
|
|
|
|
|
TooltipTrigger,
|
|
|
|
|
} from "@/components/ui/tooltip";
|
|
|
|
|
import useSWR from "swr";
|
|
|
|
|
|
|
|
|
|
type SearchResultActionsProps = {
|
|
|
|
|
searchResult: SearchResult;
|
|
|
|
|
findSimilar: () => void;
|
|
|
|
|
refreshResults: () => void;
|
|
|
|
|
showObjectLifecycle: () => void;
|
2024-11-12 15:37:25 +03:00
|
|
|
showSnapshot: () => void;
|
2024-10-22 17:01:01 +03:00
|
|
|
isContextMenu?: boolean;
|
|
|
|
|
children?: ReactNode;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default function SearchResultActions({
|
|
|
|
|
searchResult,
|
|
|
|
|
findSimilar,
|
|
|
|
|
refreshResults,
|
|
|
|
|
showObjectLifecycle,
|
2024-11-12 15:37:25 +03:00
|
|
|
showSnapshot,
|
2024-10-22 17:01:01 +03:00
|
|
|
isContextMenu = false,
|
|
|
|
|
children,
|
|
|
|
|
}: SearchResultActionsProps) {
|
|
|
|
|
const { data: config } = useSWR<FrigateConfig>("config");
|
|
|
|
|
|
|
|
|
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
|
|
|
|
|
|
|
|
const handleDelete = () => {
|
|
|
|
|
axios
|
|
|
|
|
.delete(`events/${searchResult.id}`)
|
|
|
|
|
.then((resp) => {
|
|
|
|
|
if (resp.status == 200) {
|
|
|
|
|
toast.success("Tracked object deleted successfully.", {
|
|
|
|
|
position: "top-center",
|
|
|
|
|
});
|
|
|
|
|
refreshResults();
|
|
|
|
|
}
|
|
|
|
|
})
|
2025-03-08 19:01:08 +03:00
|
|
|
.catch((error) => {
|
|
|
|
|
const errorMessage =
|
|
|
|
|
error.response?.data?.message ||
|
|
|
|
|
error.response?.data?.detail ||
|
|
|
|
|
"Unknown error";
|
|
|
|
|
toast.error(`Failed to delete tracked object: ${errorMessage}`, {
|
2024-10-22 17:01:01 +03:00
|
|
|
position: "top-center",
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const MenuItem = isContextMenu ? ContextMenuItem : DropdownMenuItem;
|
|
|
|
|
|
|
|
|
|
const menuItems = (
|
|
|
|
|
<>
|
|
|
|
|
{searchResult.has_clip && (
|
2024-10-23 01:07:42 +03:00
|
|
|
<MenuItem aria-label="Download video">
|
2024-10-22 17:01:01 +03:00
|
|
|
<a
|
|
|
|
|
className="flex items-center"
|
|
|
|
|
href={`${baseUrl}api/events/${searchResult.id}/clip.mp4`}
|
|
|
|
|
download={`${searchResult.camera}_${searchResult.label}.mp4`}
|
|
|
|
|
>
|
|
|
|
|
<LuDownload className="mr-2 size-4" />
|
|
|
|
|
<span>Download video</span>
|
|
|
|
|
</a>
|
|
|
|
|
</MenuItem>
|
|
|
|
|
)}
|
|
|
|
|
{searchResult.has_snapshot && (
|
2024-10-23 01:07:42 +03:00
|
|
|
<MenuItem aria-label="Download snapshot">
|
2024-10-22 17:01:01 +03:00
|
|
|
<a
|
|
|
|
|
className="flex items-center"
|
|
|
|
|
href={`${baseUrl}api/events/${searchResult.id}/snapshot.jpg`}
|
|
|
|
|
download={`${searchResult.camera}_${searchResult.label}.jpg`}
|
|
|
|
|
>
|
|
|
|
|
<LuCamera className="mr-2 size-4" />
|
|
|
|
|
<span>Download snapshot</span>
|
|
|
|
|
</a>
|
|
|
|
|
</MenuItem>
|
|
|
|
|
)}
|
2024-12-01 21:08:03 +03:00
|
|
|
{searchResult.data.type == "object" && (
|
|
|
|
|
<MenuItem
|
|
|
|
|
aria-label="Show the object lifecycle"
|
|
|
|
|
onClick={showObjectLifecycle}
|
|
|
|
|
>
|
|
|
|
|
<FaArrowsRotate className="mr-2 size-4" />
|
|
|
|
|
<span>View object lifecycle</span>
|
|
|
|
|
</MenuItem>
|
|
|
|
|
)}
|
2024-10-22 17:01:01 +03:00
|
|
|
{config?.semantic_search?.enabled && isContextMenu && (
|
2024-10-23 01:07:42 +03:00
|
|
|
<MenuItem
|
|
|
|
|
aria-label="Find similar tracked objects"
|
|
|
|
|
onClick={findSimilar}
|
|
|
|
|
>
|
2024-10-22 17:01:01 +03:00
|
|
|
<MdImageSearch className="mr-2 size-4" />
|
|
|
|
|
<span>Find similar</span>
|
|
|
|
|
</MenuItem>
|
|
|
|
|
)}
|
|
|
|
|
{isMobileOnly &&
|
|
|
|
|
config?.plus?.enabled &&
|
|
|
|
|
searchResult.has_snapshot &&
|
|
|
|
|
searchResult.end_time &&
|
2024-12-01 17:47:37 +03:00
|
|
|
searchResult.data.type == "object" &&
|
2024-10-22 17:01:01 +03:00
|
|
|
!searchResult.plus_id && (
|
2024-11-12 15:37:25 +03:00
|
|
|
<MenuItem aria-label="Submit to Frigate Plus" onClick={showSnapshot}>
|
2024-10-22 17:01:01 +03:00
|
|
|
<FrigatePlusIcon className="mr-2 size-4 cursor-pointer text-primary" />
|
|
|
|
|
<span>Submit to Frigate+</span>
|
|
|
|
|
</MenuItem>
|
|
|
|
|
)}
|
2024-10-23 01:07:42 +03:00
|
|
|
<MenuItem
|
|
|
|
|
aria-label="Delete this tracked object"
|
|
|
|
|
onClick={() => setDeleteDialogOpen(true)}
|
|
|
|
|
>
|
2024-10-22 17:01:01 +03:00
|
|
|
<LuTrash2 className="mr-2 size-4" />
|
|
|
|
|
<span>Delete</span>
|
|
|
|
|
</MenuItem>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<AlertDialog
|
|
|
|
|
open={deleteDialogOpen}
|
|
|
|
|
onOpenChange={() => setDeleteDialogOpen(!deleteDialogOpen)}
|
|
|
|
|
>
|
|
|
|
|
<AlertDialogContent>
|
|
|
|
|
<AlertDialogHeader>
|
|
|
|
|
<AlertDialogTitle>Confirm Delete</AlertDialogTitle>
|
|
|
|
|
</AlertDialogHeader>
|
|
|
|
|
<AlertDialogDescription>
|
2024-11-04 17:07:57 +03:00
|
|
|
Deleting this tracked object removes the snapshot, any saved
|
|
|
|
|
embeddings, and any associated object lifecycle entries. Recorded
|
|
|
|
|
footage of this tracked object in History view will <em>NOT</em> be
|
|
|
|
|
deleted.
|
|
|
|
|
<br />
|
|
|
|
|
<br />
|
|
|
|
|
Are you sure you want to proceed?
|
2024-10-22 17:01:01 +03:00
|
|
|
</AlertDialogDescription>
|
|
|
|
|
<AlertDialogFooter>
|
|
|
|
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
|
|
|
<AlertDialogAction
|
|
|
|
|
className={buttonVariants({ variant: "destructive" })}
|
|
|
|
|
onClick={handleDelete}
|
|
|
|
|
>
|
|
|
|
|
Delete
|
|
|
|
|
</AlertDialogAction>
|
|
|
|
|
</AlertDialogFooter>
|
|
|
|
|
</AlertDialogContent>
|
|
|
|
|
</AlertDialog>
|
|
|
|
|
{isContextMenu ? (
|
|
|
|
|
<ContextMenu>
|
|
|
|
|
<ContextMenuTrigger>{children}</ContextMenuTrigger>
|
|
|
|
|
<ContextMenuContent>{menuItems}</ContextMenuContent>
|
|
|
|
|
</ContextMenu>
|
|
|
|
|
) : (
|
|
|
|
|
<>
|
2024-12-01 21:08:03 +03:00
|
|
|
{config?.semantic_search?.enabled &&
|
|
|
|
|
searchResult.data.type == "object" && (
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger>
|
|
|
|
|
<MdImageSearch
|
|
|
|
|
className="size-5 cursor-pointer text-primary-variant hover:text-primary"
|
|
|
|
|
onClick={findSimilar}
|
|
|
|
|
/>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>Find similar</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
)}
|
2024-10-22 17:01:01 +03:00
|
|
|
|
|
|
|
|
{!isMobileOnly &&
|
|
|
|
|
config?.plus?.enabled &&
|
|
|
|
|
searchResult.has_snapshot &&
|
|
|
|
|
searchResult.end_time &&
|
2024-12-01 17:47:37 +03:00
|
|
|
searchResult.data.type == "object" &&
|
2024-10-22 17:01:01 +03:00
|
|
|
!searchResult.plus_id && (
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger>
|
|
|
|
|
<FrigatePlusIcon
|
|
|
|
|
className="size-5 cursor-pointer text-primary-variant hover:text-primary"
|
2024-11-12 15:37:25 +03:00
|
|
|
onClick={showSnapshot}
|
2024-10-22 17:01:01 +03:00
|
|
|
/>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>Submit to Frigate+</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<DropdownMenu>
|
|
|
|
|
<DropdownMenuTrigger>
|
2025-03-06 19:50:37 +03:00
|
|
|
<FiMoreVertical className="size-5 cursor-pointer text-primary-variant hover:text-primary" />
|
2024-10-22 17:01:01 +03:00
|
|
|
</DropdownMenuTrigger>
|
|
|
|
|
<DropdownMenuContent align="end">{menuItems}</DropdownMenuContent>
|
|
|
|
|
</DropdownMenu>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|