From 7b4f747b6a6fb354d6256e49d3452b2c111eeec3 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 15 Dec 2025 10:47:04 -0700 Subject: [PATCH] Add proper filtering and display of cases --- web/src/pages/Exports.tsx | 294 ++++++++++++++++++++++++++++---------- 1 file changed, 220 insertions(+), 74 deletions(-) diff --git a/web/src/pages/Exports.tsx b/web/src/pages/Exports.tsx index 5a5aa19c7..febbdf41e 100644 --- a/web/src/pages/Exports.tsx +++ b/web/src/pages/Exports.tsx @@ -15,12 +15,19 @@ import Heading from "@/components/ui/heading"; import { Input } from "@/components/ui/input"; import { Toaster } from "@/components/ui/sonner"; import useKeyboardListener from "@/hooks/use-keyboard-listener"; -import { useSearchEffect } from "@/hooks/use-overlay-state"; +import { useOverlayState, useSearchEffect } from "@/hooks/use-overlay-state"; import { cn } from "@/lib/utils"; import { DeleteClipType, Export, ExportCase } from "@/types/export"; import axios from "axios"; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { + MutableRefObject, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; import { isMobile } from "react-device-detect"; import { useTranslation } from "react-i18next"; @@ -55,35 +62,12 @@ function Exports() { const [search, setSearch] = useState(""); - const filteredCases = useMemo(() => { - if (!search || !cases) { - return cases; - } - - return cases.filter( - (caseItem) => - caseItem.name.toLowerCase().includes(search.toLowerCase()) || - (caseItem.description && - caseItem.description.toLowerCase().includes(search.toLowerCase())), - ); - }, [search, cases]); - - const filteredExports = useMemo(() => { - if (!search) { - return exports; - } - - return exports.filter((exp) => - exp.name - .toLowerCase() - .replaceAll("_", " ") - .includes(search.toLowerCase()), - ); - }, [exports, search]); - // Viewing const [selected, setSelected] = useState(); + const [selectedCaseId, setSelectedCaseId] = useOverlayState< + string | undefined + >("caseId", undefined); const [selectedAspect, setSelectedAspect] = useState(0.0); useSearchEffect("id", (id) => { @@ -95,7 +79,22 @@ function Exports() { return true; }); - // Deleting + useSearchEffect("caseId", (caseId: string) => { + if (!cases) { + return false; + } + + const exists = cases.some((c) => c.id === caseId); + + if (!exists) { + return false; + } + + setSelectedCaseId(caseId); + return true; + }); + + // Modifying const [deleteClip, setDeleteClip] = useState(); @@ -112,8 +111,6 @@ function Exports() { }); }, [deleteClip, mutate]); - // Renaming - const onHandleRename = useCallback( (id: string, update: string) => { axios @@ -136,7 +133,7 @@ function Exports() { }); }); }, - [mutate, t], + [mutate, setDeleteClip, t], ); // Keyboard Listener @@ -144,6 +141,11 @@ function Exports() { const contentRef = useRef(null); useKeyboardListener([], undefined, contentRef); + const selectedCase = useMemo( + () => cases?.find((c) => c.id === selectedCaseId), + [cases, selectedCaseId], + ); + return (
@@ -227,63 +229,207 @@ function Exports() {
)} -
- {filteredCases?.length || filteredExports.length ? ( -
- {cases?.length && ( -
- {t("headings.cases")} -
- {cases.map((item) => ( - {}} - /> - ))} -
-
- )} + {selectedCase ? ( + + ) : ( + + )} +
+ ); +} -
- {t("headings.uncategorizedExports")} +type AllExportsViewProps = { + contentRef: MutableRefObject; + search: string; + cases?: ExportCase[]; + exports: Export[]; + setSelectedCaseId: (id: string) => void; + setSelected: (e: Export) => void; + renameClip: (id: string, update: string) => void; + setDeleteClip: (d: DeleteClipType | undefined) => void; +}; +function AllExportsView({ + contentRef, + search, + cases, + exports, + setSelectedCaseId, + setSelected, + renameClip, + setDeleteClip, +}: AllExportsViewProps) { + const { t } = useTranslation(["views/exports"]); + + // Filter + + const filteredCases = useMemo(() => { + if (!search || !cases) { + return cases; + } + + return cases.filter( + (caseItem) => + caseItem.name.toLowerCase().includes(search.toLowerCase()) || + (caseItem.description && + caseItem.description.toLowerCase().includes(search.toLowerCase())), + ); + }, [search, cases]); + + const filteredExports = useMemo(() => { + if (!search) { + return exports; + } + + return exports.filter((exp) => + exp.name + .toLowerCase() + .replaceAll("_", " ") + .includes(search.toLowerCase()), + ); + }, [exports, search]); + + return ( +
+ {filteredCases?.length || filteredExports.length ? ( +
+ {cases?.length && ( +
+ {t("headings.cases")}
- {exports.map((item) => ( - ( + - setDeleteClip({ file, exportName }) - } + exportCase={item} + onSelect={() => { + setSelectedCaseId(item.id); + }} /> ))}
+ )} + +
+ {t("headings.uncategorizedExports")} +
+ {exports.map((item) => ( + + setDeleteClip({ file, exportName }) + } + /> + ))} +
- ) : ( -
- - {t("noExports")} -
- )} +
+ ) : ( +
+ + {t("noExports")} +
+ )} +
+ ); +} + +type CaseViewProps = { + contentRef: MutableRefObject; + selectedCase: ExportCase; + exports?: Export[]; + search: string; + setSelected: (e: Export) => void; + renameClip: (id: string, update: string) => void; + setDeleteClip: (d: DeleteClipType | undefined) => void; +}; +function CaseView({ + contentRef, + selectedCase, + exports, + search, + setSelected, + renameClip, + setDeleteClip, +}: CaseViewProps) { + const filteredExports = useMemo(() => { + const caseExports = (exports || []).filter( + (e) => e.export_case == selectedCase.id, + ); + + if (!search) { + return caseExports; + } + + return caseExports.filter((exp) => + exp.name + .toLowerCase() + .replaceAll("_", " ") + .includes(search.toLowerCase()), + ); + }, [selectedCase, exports, search]); + + return ( +
+
+ + {selectedCase.name} + +
+ {selectedCase.description} +
+
+
+ {exports.map((item) => ( + + setDeleteClip({ file, exportName }) + } + /> + ))}
);