added sounds, updated nped config and tweaks + code quality improvements

This commit is contained in:
2025-09-23 13:03:54 +01:00
parent eab7e79d01
commit c2074f86a2
27 changed files with 224 additions and 139 deletions

View File

@@ -21,13 +21,13 @@ const CameraSettingFields = ({
const initialValues = useMemo<CameraSettingValues>(
() => ({
friendlyName: initialData?.propLEDDriverControlURI?.value ?? "",
cameraAddress: "",
friendlyName: initialData?.id ?? "",
cameraAddress: initialData?.propURI?.value ?? "",
userName: "",
password: "",
id: initialData?.id,
}),
[initialData?.id, initialData?.propLEDDriverControlURI?.value]
[initialData?.id, initialData?.propURI?.value]
);
const validateValues = (values: CameraSettingValues) => {

View File

@@ -6,6 +6,7 @@ import { useSwipeable } from "react-swipeable";
import { useNavigate } from "react-router";
import { useOverviewVideo } from "../../hooks/useOverviewVideo";
import SightingOverview from "../SightingOverview/SightingOverview";
import { useSightingFeedContext } from "../../context/SightingFeedContext";
type CardProps = React.HTMLAttributes<HTMLDivElement>;
@@ -17,6 +18,7 @@ const FrontCameraOverviewCard = ({ className }: CardProps) => {
trackMouse: true,
});
const { mostRecent } = useSightingFeedContext();
return (
<Card
@@ -26,7 +28,11 @@ const FrontCameraOverviewCard = ({ className }: CardProps) => {
)}
>
<div className="flex flex-col space-y-3 h-full" {...handlers}>
<CardHeader title="Front Overview" icon={faCamera} />
<CardHeader
title="Front Overview"
icon={faCamera}
sighting={mostRecent}
/>
<SightingOverview />
{/* <SnapshotContainer side="TargetDetectionFront" /> */}
</div>

View File

@@ -6,6 +6,7 @@ import { useNavigate } from "react-router";
import CardHeader from "../UI/CardHeader";
import { faCamera } from "@fortawesome/free-regular-svg-icons";
import SightingOverview from "../SightingOverview/SightingOverview";
import { useSightingFeedContext } from "../../context/SightingFeedContext";
type CardProps = React.HTMLAttributes<HTMLDivElement>;
@@ -15,7 +16,7 @@ const RearCameraOverviewCard = ({ className }: CardProps) => {
onSwipedLeft: () => navigate("/rear-camera-settings"),
trackMouse: true,
});
const { mostRecent } = useSightingFeedContext();
return (
<Card
className={clsx(
@@ -24,7 +25,11 @@ const RearCameraOverviewCard = ({ className }: CardProps) => {
)}
>
<div className="flex flex-col space-y-3 h-full" {...handlers}>
<CardHeader title="Rear Overview" icon={faCamera} />
<CardHeader
title="Rear Overview"
icon={faCamera}
sighting={mostRecent}
/>
<SightingOverview />
</div>
</Card>

View File

@@ -1,10 +1,12 @@
import { useSound } from "react-sounds";
import Card from "../UI/Card";
import CardHeader from "../UI/CardHeader";
const SessionCard = () => {
function onStart(): void {
throw new Error("Function not implemented.");
}
const { play } = useSound("notification/notification");
// function onStart(): void {
// throw new Error("Function not implemented.");
// }
return (
<Card>
@@ -12,7 +14,9 @@ const SessionCard = () => {
<div className="flex flex-col gap-4">
<button
className="bg-[#26B170] text-white px-4 py-2 rounded hover:bg-green-700 transition w-full max-w-md"
onClick={onStart}
onClick={() => {
play();
}}
>
Start Session
</button>

View File

@@ -1,4 +1,5 @@
import type { SystemValues } from "../../../types/types";
import { CAM_BASE } from "../../../utils/config";
export async function handleSystemSave(values: SystemValues) {
const payload = {
@@ -16,7 +17,7 @@ export async function handleSystemSave(values: SystemValues) {
};
try {
const response = await fetch("http://192.168.75.11/api/update-config", {
const response = await fetch(`${CAM_BASE}/api/update-config`, {
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -39,7 +40,7 @@ export async function handleSystemSave(values: SystemValues) {
}
export async function handleSystemRecall() {
const url = "http://192.168.75.11/api/fetch-config?id=GLOBAL--Device";
const url = `${CAM_BASE}/api/fetch-config?id=GLOBAL--Device`;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 7000);

View File

@@ -8,7 +8,6 @@ import { useSystemConfig } from "../../../hooks/useSystemConfig";
const SystemConfigFields = () => {
const { saveSystemSettings, systemSettingsData } = useSystemConfig();
const initialvalues: SystemValues = {
deviceName: systemSettingsData?.deviceName ?? "",
timeZone: systemSettingsData?.timeZone ?? "",

View File

@@ -13,6 +13,7 @@ import HotListImg from "/Hotlist_Hit.svg";
import NPED_CAT_A from "/NPED_Cat_A.svg";
import NPED_CAT_B from "/NPED_Cat_B.svg";
import NPED_CAT_C from "/NPED_Cat_C.svg";
import popup from "../../assets/sounds/ui/popup_open.mp3";
import { useSound } from "react-sounds";
function useNow(tickMs = 1000) {
@@ -38,7 +39,7 @@ export default function SightingHistoryWidget({
title,
}: SightingHistoryProps) {
useNow(1000);
const { play } = useSound("notification/notification");
const { play } = useSound(popup);
const {
sightings,
setSelectedSighting,
@@ -72,14 +73,13 @@ export default function SightingHistoryWidget({
const isNPEDHitC = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
if (isNPEDHitA || isNPEDHitB || isNPEDHitC) {
play();
dispatch({
type: "ADD",
payload: obj,
});
}
});
}, [rows, dispatch, play]);
}, [dispatch, rows]);
useEffect(() => {
if (hasAutoOpenedRef.current) return;

View File

@@ -1,5 +1,4 @@
import type { SightingType } from "../../types/types";
import { useState } from "react";
type SightingWidgetDetailsProps = {
effectiveSelected: SightingType | null;
@@ -8,85 +7,38 @@ type SightingWidgetDetailsProps = {
const SightingWidgetDetails = ({
effectiveSelected,
}: SightingWidgetDetailsProps) => {
const [advancedDetailsEnabled, setAdvancedDetailsEnabled] = useState(false);
const handleDetailsClick = () =>
setAdvancedDetailsEnabled(!advancedDetailsEnabled);
return (
<>
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 text-sm">
<div>
VRM:{" "}
<span className="opacity-90">{effectiveSelected?.vrm ?? ""}</span>
</div>
<div>
Make:{" "}
<span className="opacity-90">{effectiveSelected?.make ?? ""}</span>
</div>
<div>
Model:{" "}
<span className="opacity-90">{effectiveSelected?.model ?? "—"}</span>
</div>
<div>
Colour:{" "}
<span className="opacity-90">{effectiveSelected?.color ?? ""}</span>
</div>
<div className="col-span-4">
Timestamp:{" "}
<span className="opacity-90">
{effectiveSelected?.timeStamp ?? "—"}
</span>
</div>
{advancedDetailsEnabled && (
<>
<div>
Country:{" "}
<span className="opacity-90">
{effectiveSelected?.countryCode ?? "—"}
</span>
</div>
<div>
Seen:{" "}
<span className="opacity-90">
{effectiveSelected?.seenCount ?? "—"}
</span>
</div>
<div>
Category:{" "}
<span className="opacity-90">
{effectiveSelected?.category ?? "—"}
</span>
</div>
<div>
Char Ht:{" "}
<span className="opacity-90">
{effectiveSelected?.charHeight ?? "—"}
</span>
</div>
<div>
Plate Size:{" "}
<span className="opacity-90">
{effectiveSelected?.plateSize ?? "—"}
</span>
</div>
<div>
Overview Size:{" "}
<span className="opacity-90">
{effectiveSelected?.overviewSize ?? "—"}
</span>
</div>
</>
{effectiveSelected?.vrm && (
<div>
VRM:{" "}
<span className="opacity-90">{effectiveSelected?.vrm ?? "—"}</span>
</div>
)}
{effectiveSelected?.make !== "" && (
<div>
Make:{" "}
<span className="opacity-90">{effectiveSelected?.make ?? ""}</span>
</div>
)}
{effectiveSelected?.model.trim() !== "" && (
<div>
Model:{" "}
<span className="opacity-90">
{effectiveSelected?.model ?? "—"}
</span>
</div>
)}
{effectiveSelected?.color !== "" && (
<div>
Colour:{" "}
<span className="opacity-90">
{effectiveSelected?.color ?? "—"}
</span>
</div>
)}
</div>
<div className="col-span-half">
<p
onClick={handleDetailsClick}
className="underline text-blue-300 hover:cursor-pointer"
>
Sighting Details
</p>
</div>
</>
);

View File

@@ -1,18 +1,27 @@
import type { IconProp } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import clsx from "clsx";
import NumberPlate from "../PlateStack/NumberPlate";
import type { SightingType } from "../../types/types";
type CameraOverviewHeaderProps = {
title: string;
icon?: IconProp;
img?: string;
sighting?: SightingType | null;
};
const CardHeader = ({ title, icon, img }: CameraOverviewHeaderProps) => {
const CardHeader = ({
title,
icon,
img,
sighting,
}: CameraOverviewHeaderProps) => {
// console.log(sighting?.debug.toLowerCase());
return (
<div
className={clsx(
"w-full border-b border-gray-600 flex flex-row items-center space-x-2 md:mb-6 relative"
"w-full border-b border-gray-600 flex flex-row items-center space-x-2 md:mb-6 relative justify-between"
)}
>
<div className="flex items-center space-x-2">
@@ -22,6 +31,7 @@ const CardHeader = ({ title, icon, img }: CameraOverviewHeaderProps) => {
{img && (
<img src={img} alt="Logo" width={100} height={50} className="ml-auto" />
)}
{sighting?.vrm && <NumberPlate vrm={sighting.vrm} motion={false} />}
</div>
);
};

View File

@@ -10,6 +10,7 @@ import {
} from "@fortawesome/free-solid-svg-icons";
import type { VersionFieldType } from "../../types/types";
import { useEffect, useState } from "react";
import SoundBtn from "./SoundBtn";
async function fetchVersions(
signal?: AbortSignal
@@ -120,6 +121,7 @@ export default function Header() {
size="2x"
/>
</Link>
<SoundBtn />
<Link to={"/system-settings"}>
<FontAwesomeIcon className="text-white" icon={faGear} size="2x" />
</Link>

View File

@@ -0,0 +1,22 @@
import { faVolumeHigh, faVolumeXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useSoundEnabled } from "react-sounds";
const SoundBtn = () => {
const [enabled, setEnabled] = useSoundEnabled();
const handleClick = () => {
setEnabled(!enabled);
};
return (
<button onClick={handleClick}>
<FontAwesomeIcon
icon={enabled ? faVolumeHigh : faVolumeXmark}
size="2x"
/>
</button>
);
};
export default SoundBtn;