diff --git a/src/components/SettingForms/System/SettingSaveRecall.tsx b/src/components/SettingForms/System/SettingSaveRecall.tsx index 79f8f2a..15bc00a 100644 --- a/src/components/SettingForms/System/SettingSaveRecall.tsx +++ b/src/components/SettingForms/System/SettingSaveRecall.tsx @@ -33,8 +33,6 @@ export async function handleSystemSave(values: SystemValues) { }` ); } - - alert("System Settings Saved Successfully!"); } catch (err) { console.error(err); } diff --git a/src/components/SettingForms/System/SystemCard.tsx b/src/components/SettingForms/System/SystemCard.tsx index a2f6554..21d6cdc 100644 --- a/src/components/SettingForms/System/SystemCard.tsx +++ b/src/components/SettingForms/System/SystemCard.tsx @@ -1,57 +1,8 @@ -import React from "react"; import Card from "../../UI/Card"; import CardHeader from "../../UI/CardHeader"; -import { sendBlobFileUpload } from "./Upload"; import SystemConfigFields from "./SystemConfigFields.tsx"; const SystemCard = () => { - const [selectedFile, setSelectedFile] = React.useState(null); - const [error, setError] = React.useState(""); - - // useEffect(() => { - // (async () => { - // const result = await handleSystemRecall(); // returns { deviceName, sntpServer, sntpInterval, timeZone } | null - // if (result) { - // const { - // deviceName: dn, - // sntpServer: ss, - // sntpInterval: si, - // timeZone: tz, - // } = result; - - // setDeviceName(dn ?? ""); - // setSntpServer(ss ?? ""); - // setSntpInterval(Number.isFinite(si) ? si : 60); - // setTimeZone(tz ?? "UTC (UTC-00)"); - // } - // })(); - // }, []); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); // prevent full page reload - if (!selectedFile) { - setError("Please select a file before uploading."); - return; - } - setError(""); - - const result = await sendBlobFileUpload(selectedFile, { - timeoutMs: 30000, - fieldName: "upload", - }); - - // The helper returns a string (either success body or formatted error) - // You can decide how to distinguish. Here, we show it optimistically and let the text speak. - if ( - result.startsWith("Server returned") || - result.startsWith("Timeout") || - result.startsWith("HTTP error") || - result.startsWith("Unexpected error") - ) { - setError(result); - } - }; - return ( diff --git a/src/components/SettingForms/System/SystemConfigFields.tsx b/src/components/SettingForms/System/SystemConfigFields.tsx index c29d0c5..dd34af2 100644 --- a/src/components/SettingForms/System/SystemConfigFields.tsx +++ b/src/components/SettingForms/System/SystemConfigFields.tsx @@ -1,12 +1,14 @@ import { Formik, Field, Form } from "formik"; import FormGroup from "../components/FormGroup"; import { handleSoftReboot, handleHardReboot } from "./Reboots"; -import { handleSystemSave } from "./SettingSaveRecall"; import { timezones } from "./timezones"; import SystemFileUpload from "./SystemFileUpload"; import type { SystemValues, SystemValuesErrors } from "../../../types/types"; +import { useSystemConfig } from "../../../hooks/useSystemConfig"; const SystemConfigFields = () => { + const { saveSystemSettings, getSystemSettingsData } = useSystemConfig(); + console.log(getSystemSettingsData); const initialvalues: SystemValues = { deviceName: "", timeZone: "", @@ -15,7 +17,7 @@ const SystemConfigFields = () => { softwareUpdate: null, }; - const handleSubmit = (values: SystemValues) => handleSystemSave(values); + const handleSubmit = (values: SystemValues) => saveSystemSettings(values); const validateValues = (values: SystemValues) => { const errors: SystemValuesErrors = {}; const interval = Number(values.sntpInterval); diff --git a/src/components/SettingForms/System/SystemFileUpload.tsx b/src/components/SettingForms/System/SystemFileUpload.tsx index da310b0..08f7485 100644 --- a/src/components/SettingForms/System/SystemFileUpload.tsx +++ b/src/components/SettingForms/System/SystemFileUpload.tsx @@ -1,7 +1,7 @@ import { useFormikContext } from "formik"; - import FormGroup from "../components/FormGroup"; import { toast } from "sonner"; +import { useSystemConfig } from "../../../hooks/useSystemConfig"; type SystemFileUploadProps = { name: string; @@ -10,7 +10,16 @@ type SystemFileUploadProps = { const SystemFileUpload = ({ name, selectedFile }: SystemFileUploadProps) => { const { setFieldValue } = useFormikContext(); - const handleFileUploadClick = () => console.log(selectedFile); + const { uploadSettings } = useSystemConfig(); + + const handleFileUploadClick = () => { + if (!selectedFile) return; + uploadSettings(selectedFile, { + timeoutMs: 30000, + fieldName: "upload", + }); + }; + return (
diff --git a/src/components/SettingForms/System/Upload.tsx b/src/components/SettingForms/System/Upload.tsx index 4c2c24c..d2c06da 100644 --- a/src/components/SettingForms/System/Upload.tsx +++ b/src/components/SettingForms/System/Upload.tsx @@ -14,7 +14,7 @@ export async function sendBlobFileUpload( const form = new FormData(); form.append(fieldName, file, fileName); - const resp = await fetch('http://192.168.75.11/upload/software-update/2', { + const resp = await fetch("http://192.168.75.11/upload/hotlist-upload/2", { method: "POST", body: form, signal: controller.signal, @@ -24,6 +24,9 @@ export async function sendBlobFileUpload( const bodyText = await resp.text(); if (!resp.ok) { + // throw new Error( + // `Server returned ${resp.status}: ${resp.statusText}. Details: ${bodyText}` + // ); return `Server returned ${resp.status}: ${resp.statusText}. Details: ${bodyText}`; } return bodyText; @@ -35,7 +38,9 @@ export async function sendBlobFileUpload( if (err instanceof TypeError) { return `HTTP error uploading to /upload/software-update/2: ${err.message}`; } - return `Unexpected error uploading to /upload/software-update/2: ${err?.message ?? String(err)} ${err?.cause ?? ""}`; + return `Unexpected error uploading to /upload/software-update/2: ${ + err?.message ?? String(err) + } ${err?.cause ?? ""}`; } finally { clearTimeout(timeout); } diff --git a/src/components/SightingModal/SightingModal.tsx b/src/components/SightingModal/SightingModal.tsx index 2abd51a..bebabf8 100644 --- a/src/components/SightingModal/SightingModal.tsx +++ b/src/components/SightingModal/SightingModal.tsx @@ -14,6 +14,7 @@ const SightingModal = ({ sighting, }: SightingModalProps) => { const motionAway = (sighting?.motion ?? "").toUpperCase() === "AWAY"; + console.log(sighting); return (
diff --git a/src/components/SightingsWidget/SightingWidget.tsx b/src/components/SightingsWidget/SightingWidget.tsx index a20cf08..8ccdb9d 100644 --- a/src/components/SightingsWidget/SightingWidget.tsx +++ b/src/components/SightingsWidget/SightingWidget.tsx @@ -17,18 +17,21 @@ function useNow(tickMs = 1000) { return undefined; } -export type SightingHistoryProps = { - baseUrl: string; +type SightingHistoryProps = { + baseUrl?: string; entries?: number; // number of rows to show pollMs?: number; // poll frequency autoSelectLatest?: boolean; + title: string; + className: React.HTMLAttributes | string; }; -type SightingHistoryWidgetProps = React.HTMLAttributes; +// /type SightingHistoryWidgetProps = React.HTMLAttributes; export default function SightingHistoryWidget({ className, -}: SightingHistoryWidgetProps) { + title, +}: SightingHistoryProps) { useNow(1000); const { @@ -40,7 +43,7 @@ export default function SightingHistoryWidget({ } = useSightingFeedContext(); const onRowClick = useCallback( - (sighting: SightingType) => { + (sighting: SightingType | SightingWidgetType) => { if (!sighting) return; setSightingModalOpen(!isSightingModalOpen); setSelectedSighting(sighting); @@ -57,7 +60,7 @@ export default function SightingHistoryWidget({ return ( <> - +
{/* Rows */}
@@ -73,18 +76,31 @@ export default function SightingHistoryWidget({ onClick={() => onRowClick(obj)} > {/* Info bar */} -
-
- CH: {obj ? obj.charHeight : "—"} +
+
+ {" "} +
+ CH: {obj ? obj.charHeight : "—"} +
+
+ Seen: {obj ? obj.seenCount : "—"} +
+
+ {obj ? capitalize(obj.motion) : "—"} +
+
+ {obj ? formatAge(obj.timeStampMillis) : "—"} +
-
- Seen: {obj ? obj.seenCount : "—"} -
-
- {obj ? capitalize(obj.motion) : "—"} -
-
- {obj ? formatAge(obj.timeStampMillis) : "—"} + +
+ {isNPEDHit ? ( + + NPED HIT + + ) : ( + "" + )}
diff --git a/src/context/SightingFeedContext.tsx b/src/context/SightingFeedContext.tsx index 37797cf..0dcec7b 100644 --- a/src/context/SightingFeedContext.tsx +++ b/src/context/SightingFeedContext.tsx @@ -9,7 +9,9 @@ type SightingFeedContextType = { mostRecent: SightingWidgetType | null; side: string; selectedSighting: SightingType | null; - setSelectedSighting: (sighting: SightingType | null) => void; + setSelectedSighting: ( + sighting: SightingType | SightingWidgetType | null + ) => void; setSightingModalOpen: (isSightingModalOpen: boolean) => void; isSightingModalOpen: boolean; }; diff --git a/src/hooks/useSystemConfig.ts b/src/hooks/useSystemConfig.ts new file mode 100644 index 0000000..fbe6bf1 --- /dev/null +++ b/src/hooks/useSystemConfig.ts @@ -0,0 +1,32 @@ +import { useMutation, useQuery } from "@tanstack/react-query"; +import { sendBlobFileUpload } from "../components/SettingForms/System/Upload"; +import { toast } from "sonner"; +import { + handleSystemSave, + handleSystemRecall, +} from "../components/SettingForms/System/SettingSaveRecall"; + +export const useSystemConfig = () => { + const uploadSettingsMutation = useMutation({ + mutationKey: ["uploadSettings"], + mutationFn: sendBlobFileUpload, + onError: () => console.log("upload failed"), + onSuccess: (test) => console.log(test), + }); + + const saveSystemSettings = useMutation({ + mutationKey: ["systemSaveSettings"], + mutationFn: handleSystemSave, + onSuccess: () => toast("System Settings Saved Successfully!"), + }); + + const getSystemSettings = useQuery({ + queryKey: ["getSystemSettings"], + queryFn: handleSystemRecall, + }); + return { + uploadSettings: uploadSettingsMutation.mutate, + saveSystemSettings: saveSystemSettings.mutate, + getSystemSettingsData: getSystemSettings.data, + }; +}; diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index ef03b61..3a2a64b 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -1,7 +1,6 @@ import FrontCameraOverviewCard from "../components/FrontCameraOverview/FrontCameraOverviewCard"; import RearCameraOverviewCard from "../components/RearCameraOverview/RearCameraOverviewCard"; import SightingHistoryWidget from "../components/SightingsWidget/SightingWidget"; - import { SightingFeedProvider } from "../context/providers/SightingFeedProvider"; const Dashboard = () => { @@ -9,24 +8,30 @@ const Dashboard = () => {
- + - +
); diff --git a/src/pages/SystemSettings.tsx b/src/pages/SystemSettings.tsx index b68e106..da6e544 100644 --- a/src/pages/SystemSettings.tsx +++ b/src/pages/SystemSettings.tsx @@ -11,7 +11,7 @@ import { useNPEDAuth } from "../hooks/useNPEDAuth"; const SystemSettings = () => { const { user } = useNPEDAuth(); - console.log(user); + return (
@@ -21,7 +21,7 @@ const SystemSettings = () => { Integrations WiFi and Modem - +
diff --git a/src/types/types.ts b/src/types/types.ts index 749d2d7..ef5b89c 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -71,35 +71,36 @@ export type HotlistUploadType = { }; export type SightingWidgetType = { + debug: string; + SaFID: string; ref: number; // unique, increasing idx?: number; // client-side slot index vrm: string; - vrmSecondary?: string; // empty string means missing - countryCode?: string; - timeStamp?: string; // formatted string + vrmSecondary: string; // empty string means missing + countryCode: string; + timeStamp: string; // formatted string timeStampMillis: number; // epoch millis motion: string; // e.g., "AWAY" or "TOWARDS" - seenCount: number; - charHeight: string | number; + seenCount: string; + charHeight: string; overviewUrl: string; - detailsUrl?: string; - make?: string; - model?: string; - color?: string; - category?: string; - plateSize?: string | number; - overviewSize?: string | number; - locationName?: string; - laneID?: string | number; - radarSpeed?: string | number; - trackSpeed?: string | number; - srcCam?: 0 | 1; - plateUrlInfrared?: string; - plateUrlColour?: string; - overviewPlateRect?: [number, number, number, number]; // [x,y,w,h] normalized 0..1 - plateTrack?: [number, number, number, number][]; - metadata?: Metadata; - // list of rects normalized 0..1 + detailsUrl: string; + make: string; + model: string; + color: string; + category: string; + plateSize: string | number; + overviewSize: string | number; + locationName: string; + laneID: string | number; + radarSpeed: string | number; + trackSpeed: string | number; + srcCam: 0 | 1; + plateUrlInfrared: string; + plateUrlColour: string; + overviewPlateRect: [number, number, number, number]; // [x,y,w,h] normalized 0..1 + plateTrack: [number, number, number, number][]; + metadata: Metadata; }; export type VersionFieldType = {