From 9414e001f32bd61c9217529be45751de020a751a Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Sun, 23 Feb 2025 17:56:48 -0600 Subject: [PATCH] Edit sub labels from the UI (#16764) * Add ability to edit sub labels from tracked object detail dialog * add allowEmpty prop * use TextEntryDialog * clean up * text consistency --- .../overlay/detail/SearchDetailDialog.tsx | 95 +++++++++++++++++++ .../overlay/dialog/TextEntryDialog.tsx | 19 +++- 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/web/src/components/overlay/detail/SearchDetailDialog.tsx b/web/src/components/overlay/detail/SearchDetailDialog.tsx index 03054d811..9d3610e49 100644 --- a/web/src/components/overlay/detail/SearchDetailDialog.tsx +++ b/web/src/components/overlay/detail/SearchDetailDialog.tsx @@ -71,6 +71,8 @@ import { } from "@/components/ui/popover"; import { LuInfo } from "react-icons/lu"; import { TooltipPortal } from "@radix-ui/react-tooltip"; +import { FaPencilAlt } from "react-icons/fa"; +import TextEntryDialog from "@/components/overlay/dialog/TextEntryDialog"; const SEARCH_TABS = [ "details", @@ -288,6 +290,7 @@ function ObjectDetailsTab({ // data const [desc, setDesc] = useState(search?.data.description); + const [isSubLabelDialogOpen, setIsSubLabelDialogOpen] = useState(false); const handleDescriptionFocus = useCallback(() => { setInputFocused(true); @@ -430,6 +433,74 @@ function ObjectDetailsTab({ [search, config], ); + const handleSubLabelSave = useCallback( + (text: string) => { + if (!search) return; + + // set score to 1.0 if we're manually entering a sub label + const subLabelScore = + text === "" ? undefined : search.data?.sub_label_score || 1.0; + + axios + .post(`${apiHost}api/events/${search.id}/sub_label`, { + camera: search.camera, + subLabel: text, + subLabelScore: subLabelScore, + }) + .then((response) => { + if (response.status === 200) { + toast.success("Successfully updated sub label.", { + position: "top-center", + }); + + mutate( + (key) => + typeof key === "string" && + (key.includes("events") || + key.includes("events/search") || + key.includes("events/explore")), + (currentData: SearchResult[][] | SearchResult[] | undefined) => { + if (!currentData) return currentData; + return currentData.flat().map((event) => + event.id === search.id + ? { + ...event, + sub_label: text, + data: { + ...event.data, + sub_label_score: subLabelScore, + }, + } + : event, + ); + }, + { + optimisticData: true, + rollbackOnError: true, + revalidate: false, + }, + ); + + setSearch({ + ...search, + sub_label: text, + data: { + ...search.data, + sub_label_score: subLabelScore, + }, + }); + setIsSubLabelDialogOpen(false); + } + }) + .catch(() => { + toast.error("Failed to update sub label.", { + position: "top-center", + }); + }); + }, + [search, apiHost, mutate, setSearch], + ); + return (