storing changes, starting history list
This commit is contained in:
@@ -33,8 +33,6 @@ export async function handleSystemSave(values: SystemValues) {
|
||||
}`
|
||||
);
|
||||
}
|
||||
|
||||
alert("System Settings Saved Successfully!");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
@@ -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<File | null>(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<HTMLFormElement>) => {
|
||||
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 (
|
||||
<Card>
|
||||
<CardHeader title={"System Config"} />
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 (
|
||||
<div className="py-8 w-full">
|
||||
<FormGroup>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ const SightingModal = ({
|
||||
sighting,
|
||||
}: SightingModalProps) => {
|
||||
const motionAway = (sighting?.motion ?? "").toUpperCase() === "AWAY";
|
||||
console.log(sighting);
|
||||
return (
|
||||
<ModalComponent isModalOpen={isSightingModalOpen} close={handleClose}>
|
||||
<div>
|
||||
|
||||
@@ -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<HTMLDivElement> | string;
|
||||
};
|
||||
|
||||
type SightingHistoryWidgetProps = React.HTMLAttributes<HTMLDivElement>;
|
||||
// /type SightingHistoryWidgetProps = React.HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
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 (
|
||||
<>
|
||||
<Card className={clsx("overflow-y-auto h-100", className)}>
|
||||
<CardHeader title="Front Camera Sightings" />
|
||||
<CardHeader title={title} />
|
||||
<div className="flex flex-col gap-3 ">
|
||||
{/* Rows */}
|
||||
<div className="flex flex-col">
|
||||
@@ -73,18 +76,31 @@ export default function SightingHistoryWidget({
|
||||
onClick={() => onRowClick(obj)}
|
||||
>
|
||||
{/* Info bar */}
|
||||
<div className="flex items-center gap-3 text-xs bg-neutral-900 px-2 py-1 rounded">
|
||||
<div className="min-w-14">
|
||||
CH: {obj ? obj.charHeight : "—"}
|
||||
<div className="flex items-center gap-3 text-xs bg-neutral-900 px-2 py-1 rounded justify-between">
|
||||
<div className="flex items-center gap-3 text-xs">
|
||||
{" "}
|
||||
<div className="min-w-14">
|
||||
CH: {obj ? obj.charHeight : "—"}
|
||||
</div>
|
||||
<div className="min-w-14">
|
||||
Seen: {obj ? obj.seenCount : "—"}
|
||||
</div>
|
||||
<div className="min-w-20">
|
||||
{obj ? capitalize(obj.motion) : "—"}
|
||||
</div>
|
||||
<div className="min-w-14 opacity-80">
|
||||
{obj ? formatAge(obj.timeStampMillis) : "—"}
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-w-14">
|
||||
Seen: {obj ? obj.seenCount : "—"}
|
||||
</div>
|
||||
<div className="min-w-20">
|
||||
{obj ? capitalize(obj.motion) : "—"}
|
||||
</div>
|
||||
<div className="min-w-14 opacity-80">
|
||||
{obj ? formatAge(obj.timeStampMillis) : "—"}
|
||||
|
||||
<div className="min-w-14 opacity-80 ">
|
||||
{isNPEDHit ? (
|
||||
<span className="text-red-500 font-semibold">
|
||||
NPED HIT
|
||||
</span>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user