develop #22

Merged
TobaOjo merged 74 commits from develop into main 2025-12-09 15:54:53 +00:00
63 changed files with 2760 additions and 51 deletions
Showing only changes of commit dbadc7388c - Show all commits

View File

@@ -0,0 +1,25 @@
import { Tabs, Tab, TabList, TabPanel } from "react-tabs";
import "react-tabs/style/react-tabs.css";
import Card from "../../../ui/Card";
import SystemConfig from "./SystemConfig";
import CardHeader from "../../../ui/CardHeader";
const Settings = () => {
return (
<div>
<Tabs>
<TabList>
<Tab>Systems</Tab>
</TabList>
<TabPanel>
<Card className="p-4">
<CardHeader title="System Configuration" />
<SystemConfig />
</Card>
</TabPanel>
</Tabs>
</div>
);
};
export default Settings;

View File

@@ -0,0 +1,133 @@
import { Formik, Form, Field } from "formik";
import { useSystemSettings } from "../hooks/useSystemSettings";
import type { SystemSettings } from "../../../types/types";
import { toast } from "sonner";
const SystemConfig = () => {
const { systemSettingsQuery, systemSettingsMutation } = useSystemSettings();
const timeZoneOptions = systemSettingsQuery?.data?.propLocalTimeZone?.accepted;
const timeZoneOpts = timeZoneOptions?.split(",").map((option: string) => option.trim().replace(/\[|\]/g, ""));
const timeSourceOptions = systemSettingsQuery?.data?.propTimeSource?.accepted;
const timeSourceOpts = timeSourceOptions?.split(",").map((option: string) => option.trim().replace(/\[|\]/g, ""));
const deviceName = systemSettingsQuery?.data?.propDeviceName?.value;
const timeZone = systemSettingsQuery?.data?.propLocalTimeZone?.value;
const SNTPServer = systemSettingsQuery?.data?.propSNTPServer?.value;
const SNTPInterval = systemSettingsQuery?.data?.propSNTPIntervalMinutes?.value;
const timeSource = systemSettingsQuery?.data?.propTimeSource?.value;
// const primaryServer = systemSettingsQuery?.data?.propPrimaryDNSServer?.value;
// const secondaryServer = systemSettingsQuery?.data?.propSecondaryDNSServer?.value;
const initialValues = {
deviceName: deviceName ?? "",
timeZone: timeZone ?? "",
localTimeZone: timeZone ?? "",
SNTPServer: SNTPServer ?? "",
SNTPInterval: SNTPInterval ?? 60,
SNTPIntervalMinutes: SNTPInterval ?? 60,
primaryServer: "",
secondaryServer: "",
timeSource: timeSource ?? "",
};
const handleSubmit = async (values: SystemSettings) => {
const result = await systemSettingsMutation.mutateAsync(values);
console.log(result);
if (result.id) {
toast.success("System settings updated successfully");
}
};
return (
<Formik initialValues={initialValues} onSubmit={handleSubmit} enableReinitialize>
<Form>
<div className="flex flex-row justify-between items-center mb-4">
<label htmlFor="deviceName">Device Name</label>
<Field
name="deviceName"
type="text"
className="p-2 border border-gray-400 rounded-lg w-full max-w-xs"
placeholder="Enter device name"
autoComplete="off"
/>
</div>
<div className="flex flex-row justify-between items-center mb-4">
<label htmlFor="timeZone">Timezone</label>
<Field
name="timeZone"
as="select"
className="p-2 border border-gray-400 rounded-lg w-full max-w-xs bg-[#253445] "
autoComplete="off"
>
{timeZoneOpts?.map((option: string) => (
<option key={option} value={option}>
{option}
</option>
))}
</Field>
</div>
<div className="flex flex-row justify-between items-center mb-4">
<label htmlFor="timeSource">Time Source</label>
<Field
name="timeSource"
as="select"
className="p-2 border border-gray-400 rounded-lg w-full max-w-xs bg-[#253445] "
autoComplete="off"
>
{timeSourceOpts?.map((option: string) => (
<option key={option} value={option}>
{option}
</option>
))}
</Field>
</div>
<div className="flex flex-row justify-between items-center mb-4">
<label htmlFor="SNTPServer">SNTP Server</label>
<Field
name="SNTPServer"
type="text"
className="p-2 border border-gray-400 rounded-lg w-full max-w-xs"
placeholder="Enter SNTP server"
autoComplete="off"
/>
</div>
<div className="flex flex-row justify-between items-center mb-4">
<label htmlFor="SNTPInterval">SNTP Interval</label>
<Field
name="SNTPInterval"
type="number"
className="p-2 border border-gray-400 rounded-lg w-full max-w-xs"
placeholder="Enter SNTP interval"
autoComplete="off"
/>
</div>
<div className="flex flex-row justify-between items-center mb-4">
<label htmlFor="primaryServer">Primary DNS Server</label>
<Field
name="primaryServer"
type="text"
className="p-2 border border-gray-400 rounded-lg w-full max-w-xs"
placeholder="Enter primary DNS server"
autoComplete="off"
/>
</div>
<div className="flex flex-row justify-between items-center mb-4">
<label htmlFor="secondaryServer">Secondary DNS Server</label>
<Field
name="secondaryServer"
type="text"
className="p-2 border border-gray-400 rounded-lg w-full max-w-xs"
placeholder="Enter secondary DNS server"
autoComplete="off"
/>
</div>
<button type="submit" className="px-4 py-2 bg-green-700 text-white rounded-lg">
Save Settings
</button>
</Form>
</Formik>
);
};
export default SystemConfig;

View File

@@ -0,0 +1,54 @@
import { useQuery, useMutation } from "@tanstack/react-query";
import { CAMBASE } from "../../../utils/config";
import type { SystemSettings } from "../../../types/types";
const camBase = import.meta.env.MODE !== "development" ? CAMBASE : "";
const fetchSystemSettings = async () => {
const response = await fetch(`${camBase}/api/fetch-config?id=GLOBAL--Device`);
if (!response.ok) {
throw new Error("Failed to fetch system settings");
}
return response.json();
};
const postSystemSettings = async (settings: SystemSettings) => {
const systemSettingConfig = {
id: "GLOBAL--Device",
fields: [
{ property: "propDeviceName", value: settings.deviceName },
{ property: "propSNTPServer", value: settings.SNTPServer },
{
property: "propSNTPIntervalMinutes",
value: Number(settings.SNTPIntervalMinutes),
},
{ property: "propLocalTimeZone", value: settings.localTimeZone },
{ property: "propTimeSource", value: settings.timeSource },
],
};
const response = await fetch(`${camBase}/api/update-config`, {
method: "POST",
body: JSON.stringify(systemSettingConfig),
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error("Failed to update system settings");
}
return response.json();
};
export const useSystemSettings = () => {
const systemSettingsQuery = useQuery({
queryKey: ["systemSettings"],
queryFn: fetchSystemSettings,
});
const systemSettingsMutation = useMutation({
mutationKey: ["updateSystemSettings"],
mutationFn: postSystemSettings,
});
return { systemSettingsQuery, systemSettingsMutation };
};

View File

@@ -2,12 +2,14 @@ import { createRootRoute, Outlet } from "@tanstack/react-router";
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
import Header from "../ui/Header";
import Footer from "../ui/Footer";
import { Toaster } from "sonner";
const RootLayout = () => (
<>
<Header />
<main className="p-4 min-h-screen">
<Outlet />
<Toaster />
</main>
<Footer />
<TanStackRouterDevtools position="bottom-right" />

View File

@@ -1,6 +1,5 @@
import { createFileRoute } from "@tanstack/react-router";
import CameraGrid from "../features/cameras/components/CameraGrid";
import { Toaster } from "sonner";
export const Route = createFileRoute("/baywatch")({
component: RouteComponent,
@@ -10,7 +9,6 @@ function RouteComponent() {
return (
<div>
<CameraGrid />
<Toaster />
</div>
);
}

View File

@@ -1,9 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import Settings from "../features/settings/components/Settings";
export const Route = createFileRoute('/settings')({
export const Route = createFileRoute("/settings")({
component: RouteComponent,
})
});
function RouteComponent() {
return <div>Hello "/settings"!</div>
return (
<div>
<Settings />
</div>
);
}

View File

@@ -175,3 +175,13 @@ export type ColourDetectionPayload = {
cameraFeedID: "A" | "B" | "C";
regions: ColourData[];
};
export type SystemSettings = {
deviceName: string;
localTimeZone: string;
timeSource: string;
SNTPServer: string;
SNTPIntervalMinutes: number;
primaryServer?: string;
secondaryServer?: string;
};

View File

@@ -13,4 +13,12 @@ export default defineConfig({
react(),
tailwindcss(),
],
server: {
proxy: {
"/api": {
target: "http://100.115.125.56",
changeOrigin: true,
},
},
},
});