bugfix/feedback-Jan-26 #21

Merged
TobaOjo merged 4 commits from bugfix/feedback-Jan-26 into develop 2026-01-06 12:29:21 +00:00
10 changed files with 77 additions and 21 deletions

View File

@@ -1,7 +1,7 @@
{ {
"name": "in-car-system-fe", "name": "in-car-system-fe",
"private": true, "private": true,
"version": "1.0.20", "version": "1.0.23",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -84,15 +84,23 @@ const SoundUpload = () => {
if (currentUrlRef.current) { if (currentUrlRef.current) {
URL.revokeObjectURL(currentUrlRef.current); URL.revokeObjectURL(currentUrlRef.current);
} }
if (e.target?.files && e.target?.files[0]?.type === "audio/mpeg") { const files = e.target?.files;
currentUrlRef.current = URL.createObjectURL(e.target.files[0]); if (
files &&
(files[0]?.type === "audio/mp3" ||
files[0]?.type === "audio/mpeg" ||
files[0]?.name.endsWith(".wav"))
) {
const file = e.target?.files;
if(file === null) return;
currentUrlRef.current = URL.createObjectURL(file[0]);
const url = currentUrlRef.current; const url = currentUrlRef.current;
setFieldValue("soundUrl", url); setFieldValue("soundUrl", url);
setFieldValue("name", e.target.files[0].name); setFieldValue("name", file[0].name);
setFieldValue("soundFileName", e.target.files[0].name); setFieldValue("soundFileName", file[0].name);
setFieldValue("soundFile", e.target.files[0]); setFieldValue("soundFile", file[0]);
setFieldValue("uploadedAt", Date.now()); setFieldValue("uploadedAt", Date.now());
if (e?.target?.files[0]?.size >= 1 * 1024 * 1024) { if (file[0]?.size >= 1 * 1024 * 1024) {
setFieldError("soundFile", "larger than 1mb"); setFieldError("soundFile", "larger than 1mb");
toast.error("File larger than 1MB"); toast.error("File larger than 1MB");
return; return;

View File

@@ -5,16 +5,29 @@ import { timezones } from "./timezones";
import SystemFileUpload from "./SystemFileUpload"; import SystemFileUpload from "./SystemFileUpload";
import type { SystemValues, SystemValuesErrors } from "../../../types/types"; import type { SystemValues, SystemValuesErrors } from "../../../types/types";
import { useDNSSettings, useSystemConfig } from "../../../hooks/useSystemConfig"; import { useDNSSettings, useSystemConfig } from "../../../hooks/useSystemConfig";
import { ValidateIPaddress } from "../../../utils/utils"; import { ValidateIPaddress, isUtcOutOfSync } from "../../../utils/utils";
import { toast } from "sonner"; import { toast } from "sonner";
// import { useSystemStatus } from "../../../hooks/useSystemStatus"; import { useSystemStatus } from "../../../hooks/useSystemStatus";
const SystemConfigFields = () => { const SystemConfigFields = () => {
const { saveSystemSettings, systemSettingsData, saveSystemSettingsLoading } = useSystemConfig(); const { saveSystemSettings, systemSettingsData, saveSystemSettingsLoading } = useSystemConfig();
// const { systemStatusQuery } = useSystemStatus(); const { systemStatusQuery } = useSystemStatus();
const { hardRebootMutation } = useReboots(); const { hardRebootMutation } = useReboots();
const { dnsQuery, dnsMutation } = useDNSSettings(); const { dnsQuery, dnsMutation } = useDNSSettings();
if (systemStatusQuery?.isLoading || !systemStatusQuery?.data) {
return <div>Loading...</div>;
}
const utcTime = systemStatusQuery?.data?.SystemStatus?.utctime;
const localDate = systemStatusQuery?.data?.SystemStatus?.localdate;
const localTime = systemStatusQuery?.data?.SystemStatus?.localtime;
const utcOutOfSync = isUtcOutOfSync({
utctime: utcTime,
localdate: localDate,
localtime: localTime,
});
const sntpInterval = systemSettingsData?.sntpInterval; const sntpInterval = systemSettingsData?.sntpInterval;
const dnsPrimary = dnsQuery?.data?.propNameServerPrimary?.value; const dnsPrimary = dnsQuery?.data?.propNameServerPrimary?.value;
const dnsSecondary = dnsQuery?.data?.propNameServerSecondary?.value; const dnsSecondary = dnsQuery?.data?.propNameServerSecondary?.value;
@@ -71,6 +84,13 @@ const SystemConfigFields = () => {
> >
{({ values, errors, touched, isSubmitting }) => ( {({ values, errors, touched, isSubmitting }) => (
<Form className="flex flex-col space-y-5 px-2"> <Form className="flex flex-col space-y-5 px-2">
<div>
{utcOutOfSync?.outOfSync ? (
<span className="text-red-800 bg-red-300 border border-red-800 rounded-lg p-2">UTC is out of sync</span>
) : (
<span className="text-green-300 bg-green-800 border border-green-600 rounded-lg p-2">UTC is in sync</span>
)}
</div>
<FormGroup> <FormGroup>
<label htmlFor="deviceName" className="font-medium whitespace-nowrap md:w-1/2 text-left"> <label htmlFor="deviceName" className="font-medium whitespace-nowrap md:w-1/2 text-left">
Device Name Device Name

View File

@@ -2,6 +2,7 @@ import { useFormikContext } from "formik";
import FormGroup from "../components/FormGroup"; import FormGroup from "../components/FormGroup";
import { toast } from "sonner"; import { toast } from "sonner";
import { useSystemConfig } from "../../../hooks/useSystemConfig"; import { useSystemConfig } from "../../../hooks/useSystemConfig";
import { CAM_BASE } from "../../../utils/config";
type SystemFileUploadProps = { type SystemFileUploadProps = {
name: string; name: string;
@@ -19,7 +20,7 @@ const SystemFileUpload = ({ name, selectedFile }: SystemFileUploadProps) => {
opts: { opts: {
timeoutMs: 30000, timeoutMs: 30000,
fieldName: "upload", fieldName: "upload",
uploadUrl: "http://192.168.75.11/upload/software-update/4", uploadUrl: `${CAM_BASE}/upload/software-update/4`,
}, },
}; };
uploadSettings(settings); uploadSettings(settings);

View File

@@ -227,10 +227,6 @@ const SightingModal = ({ isSightingModalOpen, handleClose, sighting, onDelete }:
<dt className="text-gray-300">Motion</dt> <dt className="text-gray-300">Motion</dt>
<dd className="font-medium text-2xl ">{sighting?.motion ?? "-"}</dd> <dd className="font-medium text-2xl ">{sighting?.motion ?? "-"}</dd>
</div> </div>
<div>
<dt className="text-gray-300">Seen Count</dt>
<dd className="font-medium text-2xl">{sighting?.seenCount ?? "-"}</dd>
</div>
{sighting?.make && sighting.make.trim() && sighting.make.toLowerCase() !== "disabled" && ( {sighting?.make && sighting.make.trim() && sighting.make.toLowerCase() !== "disabled" && (
<div> <div>

View File

@@ -1,8 +1,8 @@
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { useFileUpload } from "./useFileUpload";
import { getSoundFileURL } from "../utils/utils"; import { getSoundFileURL } from "../utils/utils";
import type { SoundUploadValue } from "../types/types"; import type { SoundUploadValue } from "../types/types";
import { resolveSoundSource } from "../utils/soundResolver"; import { resolveSoundSource } from "../utils/soundResolver";
import { useGetUploadedFiles } from "./useGetUploadedFiles";
export function useCachedSoundSrc( export function useCachedSoundSrc(
selected: string | undefined, selected: string | undefined,
@@ -13,7 +13,7 @@ export function useCachedSoundSrc(
const resolved = resolveSoundSource(selected, soundOptions); const resolved = resolveSoundSource(selected, soundOptions);
const { query } = useFileUpload({ const { query } = useGetUploadedFiles({
queryKey: resolved?.type === "uploaded" ? [resolved?.url] : undefined, queryKey: resolved?.type === "uploaded" ? [resolved?.url] : undefined,
}); });

View File

@@ -1,4 +1,12 @@
export async function getOrCacheBlob(url: string) { export async function getOrCacheBlob(url: string) {
const cacheAvailable = typeof window !== "undefined" && "caches" in window;
if (!cacheAvailable) {
const res = await fetch(url, { cache: "no-store" });
if (!res.ok) throw new Error(`Fetch failed: ${res.status}`);
return await res.blob();
}
const cache = await caches.open("app-sounds-v1"); const cache = await caches.open("app-sounds-v1");
const hit = await cache.match(url); const hit = await cache.match(url);
if (hit) return await hit.blob(); if (hit) return await hit.blob();
@@ -11,6 +19,11 @@ export async function getOrCacheBlob(url: string) {
} }
export async function evictFromCache(url: string) { export async function evictFromCache(url: string) {
const cacheAvailable = typeof window !== "undefined" && "caches" in window;
if (!cacheAvailable) {
return;
}
const cache = await caches.open("app-sounds-v1"); const cache = await caches.open("app-sounds-v1");
await cache.delete(url); await cache.delete(url);
} }

View File

@@ -1,5 +1,4 @@
const rawCamBase = import.meta.env.VITE_AGX_BOX_URL; const rawCamBase = import.meta.env.VITE_CHRIS_BOX_URL;
const environment = import.meta.env.MODE; const environment = import.meta.env.MODE;
export const CAM_BASE = export const CAM_BASE = environment === "development" ? rawCamBase : window.location.origin;
environment === "development" ? rawCamBase : window.location.origin;

View File

@@ -197,3 +197,21 @@ export const ValidateIPaddress = (value: string | undefined) => {
return "Invalid IP address format"; return "Invalid IP address format";
} }
}; };
export function isUtcOutOfSync(
data: {
utctime: number;
localdate: string;
localtime: string;
},
maxDriftMs = 60_000
) {
const utc = new Date(data.utctime);
const [d, m, y] = data.localdate.split("/").map(Number);
const [hh, mm, ss] = data.localtime.split(":").map(Number);
// Construct local Date in devices local timezone
const local = new Date(y, m - 1, d, hh, mm, ss);
const driftMs = Math.abs(utc.getTime() - local.getTime() + utc.getTimezoneOffset() * 60_000);
return { driftMs, outOfSync: driftMs > maxDriftMs };
}

View File

@@ -27,7 +27,7 @@ export default defineConfig({
host: true, host: true,
proxy: { proxy: {
"/api": { "/api": {
target: "http://100.118.196.113:8080", target: "http://100.72.72.70:8080",
changeOrigin: true, changeOrigin: true,
}, },
}, },
@@ -40,3 +40,4 @@ export default defineConfig({
__GIT_TIMESTAMP__: JSON.stringify(gitCommitTimeStamp), __GIT_TIMESTAMP__: JSON.stringify(gitCommitTimeStamp),
}, },
}); });
// Previous target: "http://100.118.196.113:8080",