diff --git a/src/components/HotlistList/HotlistList.tsx b/src/components/HotlistList/HotlistList.tsx new file mode 100644 index 0000000..7833160 --- /dev/null +++ b/src/components/HotlistList/HotlistList.tsx @@ -0,0 +1,67 @@ +import { useHotlistData } from "../../hooks/useHotListData"; +import { useIntegrationsContext } from "../../context/IntegrationsContext"; +import Card from "../UI/Card"; +import CardHeader from "../UI/CardHeader"; +import { toast } from "sonner"; +import { faTrash } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +const HotlistList = () => { + const { state, dispatch } = useIntegrationsContext(); + const { mutation } = useHotlistData(); + + const hotlists = state?.hotlistFiles; + + const handleDeleteClick = async (filename: string) => { + await mutation.mutateAsync(filename); + dispatch({ type: "DELETEHOTLIST", payload: filename }); + toast.success(`${filename} successfully deleted`); + }; + + return ( + + + {hotlists.length > 0 ? ( + + {hotlists?.map((hotlist) => ( + + + {hotlist.filename} + + + + Number of records: {hotlist.rowCount} + + + + + File Size (bytes): {hotlist.fileSizeBytes} + + + + + + handleDeleteClick(hotlist.filename)}> + + + + ))} + + ) : ( + + + No Uploaded Hotlists + + + Hotlists will appear here once there are uploaded. + + + )} + + ); +}; + +export default HotlistList; diff --git a/src/components/SettingForms/NPED/NPEDHotlist.tsx b/src/components/SettingForms/NPED/NPEDHotlist.tsx index 4ae9325..766d0b2 100644 --- a/src/components/SettingForms/NPED/NPEDHotlist.tsx +++ b/src/components/SettingForms/NPED/NPEDHotlist.tsx @@ -5,6 +5,7 @@ import { CAM_BASE } from "../../../utils/config"; const NPEDHotlist = () => { const { uploadSettings } = useSystemConfig(); + const initialValue = { file: null, }; diff --git a/src/context/providers/IntegrationsContextProvider.tsx b/src/context/providers/IntegrationsContextProvider.tsx index b2b3e86..4a5bba3 100644 --- a/src/context/providers/IntegrationsContextProvider.tsx +++ b/src/context/providers/IntegrationsContextProvider.tsx @@ -2,6 +2,7 @@ import { useEffect, useReducer, type ReactNode } from "react"; import { IntegrationsContext } from "../IntegrationsContext"; import { useCameraBlackboard } from "../../hooks/useCameraBlackboard"; import { initialState, reducer } from "../reducers/IntegrationsContextReducer"; +import { useHotlistData } from "../../hooks/useHotListData"; type IntegrationsProviderType = { children: ReactNode; @@ -10,6 +11,7 @@ type IntegrationsProviderType = { export const IntegrationsProvider = ({ children }: IntegrationsProviderType) => { const [state, dispatch] = useReducer(reducer, initialState); const { mutation } = useCameraBlackboard(); + const { query } = useHotlistData(); useEffect(() => { let isMounted = true; @@ -50,6 +52,13 @@ export const IntegrationsProvider = ({ children }: IntegrationsProviderType) => }; }, []); + useEffect(() => { + const fetchHotlistData = async () => { + dispatch({ type: "SETHOTLISTS", payload: query?.data?.hotlists }); + }; + fetchHotlistData(); + }, [query?.data]); + return ( hotlist.filename !== action.payload), + }; default: return { ...state }; } diff --git a/src/hooks/useHotListData.ts b/src/hooks/useHotListData.ts new file mode 100644 index 0000000..7bd4684 --- /dev/null +++ b/src/hooks/useHotListData.ts @@ -0,0 +1,28 @@ +import { useMutation, useQuery } from "@tanstack/react-query"; +import { CAM_BASE } from "../utils/config"; + +const fetchHotlists = async () => { + const response = await fetch(`${CAM_BASE}/Hotlist-csv-metadata`); + if (!response.ok) throw new Error("Cannot reach hotlist endpoint"); + return response.json(); +}; + +const deleteHotlist = async (filename: string) => { + const response = await fetch(`${CAM_BASE}/Hotlist-csv-delete?filename=${filename}`); + if (!response.ok) throw new Error(`Cannot delte hotlist: ${filename}`); + return response.json(); +}; + +export const useHotlistData = () => { + const query = useQuery({ + queryKey: ["fetchHotlists"], + queryFn: fetchHotlists, + }); + + const mutation = useMutation({ + mutationKey: ["deleteHotlist"], + mutationFn: (filename: string) => deleteHotlist(filename), + }); + + return { query, mutation }; +}; diff --git a/src/pages/SystemSettings.tsx b/src/pages/SystemSettings.tsx index ea2941c..1538d2e 100644 --- a/src/pages/SystemSettings.tsx +++ b/src/pages/SystemSettings.tsx @@ -11,6 +11,7 @@ import { useNPEDAuth } from "../hooks/useNPEDAuth"; import SoundSettingsCard from "../components/SettingForms/Sound/SoundSettingsCard"; import SoundUploadCard from "../components/SettingForms/Sound/SoundUploadCard"; import NPEDCategoryPopup from "../components/PopupSettings/NPEDCategoryPopup"; +import HotlistList from "../components/HotlistList/HotlistList"; const SystemSettings = () => { useNPEDAuth(); @@ -40,6 +41,7 @@ const SystemSettings = () => { + diff --git a/src/types/types.ts b/src/types/types.ts index 0f08692..c9a2105 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -417,6 +417,12 @@ export type QueuedHit = { export type DedupedSightings = ReducedSightingType[]; +export type HotlistFile = { + fileSizeBytes: number; + filename: string; + rowCount: number; +}; + export type NPEDSTATE = { sessionStarted: boolean; sessionList: ReducedSightingType[]; @@ -424,6 +430,7 @@ export type NPEDSTATE = { savedSightings: DedupedSightings; npedUser: NPEDUser; iscatEnabled: CategoryPopups; + hotlistFiles: HotlistFile[]; }; export type NPEDACTION = {
{hotlist.filename}
+ Number of records: {hotlist.rowCount} +
+ File Size (bytes): {hotlist.fileSizeBytes} +
+ Hotlists will appear here once there are uploaded. +