updated forms along with addg tabs
This commit is contained in:
@@ -25,6 +25,7 @@
|
|||||||
"react-modal": "^3.16.3",
|
"react-modal": "^3.16.3",
|
||||||
"react-router": "^7.8.0",
|
"react-router": "^7.8.0",
|
||||||
"react-swipeable": "^7.0.2",
|
"react-swipeable": "^7.0.2",
|
||||||
|
"react-tabs": "^6.1.0",
|
||||||
"tailwindcss": "^4.1.11"
|
"tailwindcss": "^4.1.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -3,14 +3,18 @@ import NavigationArrow from "../UI/NavigationArrow";
|
|||||||
|
|
||||||
type SnapshotContainerProps = {
|
type SnapshotContainerProps = {
|
||||||
side: string;
|
side: string;
|
||||||
|
settingsPage?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SnapshotContainer = ({ side }: SnapshotContainerProps) => {
|
export const SnapshotContainer = ({
|
||||||
|
side,
|
||||||
|
settingsPage,
|
||||||
|
}: SnapshotContainerProps) => {
|
||||||
const { canvasRef } = useGetOverviewSnapshot(side);
|
const { canvasRef } = useGetOverviewSnapshot(side);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full aspect-video">
|
<div className="relative w-full aspect-video">
|
||||||
<NavigationArrow side={side} />
|
<NavigationArrow side={side} settingsPage={settingsPage} />
|
||||||
<canvas ref={canvasRef} className="w-full h-full object-contain block " />
|
<canvas ref={canvasRef} className="w-full h-full object-contain block " />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,21 +5,20 @@ import type {
|
|||||||
} from "../../types/types";
|
} from "../../types/types";
|
||||||
|
|
||||||
const CameraSettingFields = () => {
|
const CameraSettingFields = () => {
|
||||||
const initialValues = {
|
const initialValues: CameraSettingValues = {
|
||||||
friendlyName: "",
|
friendlyName: "",
|
||||||
cameraAddress: "",
|
cameraAddress: "",
|
||||||
userName: "",
|
userName: "",
|
||||||
password: "",
|
password: "",
|
||||||
setupCamera: 1,
|
setupCamera: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateValues = (values: CameraSettingValues) => {
|
const validateValues = (values: CameraSettingValues) => {
|
||||||
const errors: CameraSettingErrorValues = {};
|
const errors: CameraSettingErrorValues = {};
|
||||||
|
|
||||||
if (!values.friendlyName) errors.friendlyName = "Required";
|
if (!values.friendlyName) errors.friendlyName = "Required";
|
||||||
if (!values.cameraAddress) errors.cameraAddress = "Required";
|
if (!values.cameraAddress) errors.cameraAddress = "Required";
|
||||||
if (!values.userName) errors.userName = "Required";
|
if (!values.userName) errors.userName = "Required";
|
||||||
if (!values.password) errors.password = "Required";
|
if (!values.password) errors.password = "Required";
|
||||||
console.log(errors);
|
|
||||||
return errors;
|
return errors;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -35,100 +34,101 @@ const CameraSettingFields = () => {
|
|||||||
validate={validateValues}
|
validate={validateValues}
|
||||||
validateOnChange={false}
|
validateOnChange={false}
|
||||||
>
|
>
|
||||||
{({ errors }) => {
|
{({ errors, touched, setFieldValue }) => (
|
||||||
return (
|
<Form className="flex flex-col space-y-4 p-2">
|
||||||
<Form className="flex flex-col space-y-4 p-2">
|
<div className="flex flex-col space-y-2 relative">
|
||||||
<div className="flex flex-col space-y-2">
|
<label htmlFor="friendlyName">Friendly Name</label>
|
||||||
<label htmlFor="friendlyName" className="relative">
|
{touched.friendlyName && errors.friendlyName && (
|
||||||
Friendly Name
|
<small className="absolute right-0 top-0 text-red-500">
|
||||||
</label>
|
{errors.friendlyName}
|
||||||
{errors.friendlyName && (
|
</small>
|
||||||
<small className="absolute right-0 text-red-500">
|
)}
|
||||||
{errors?.friendlyName}
|
<Field
|
||||||
</small>
|
id="friendlyName"
|
||||||
)}
|
name="friendlyName"
|
||||||
<Field
|
type="text"
|
||||||
name={"friendlyName"}
|
className="p-2 border border-gray-400 rounded-lg"
|
||||||
type="text"
|
placeholder="Enter camera name"
|
||||||
className="p-2 border border-gray-400 rounded-lg"
|
/>
|
||||||
placeholder="Enter camera name"
|
</div>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<label htmlFor="setupCamera" className="relative">
|
|
||||||
Setup Camera
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<Field
|
<div className="flex flex-col space-y-2 relative">
|
||||||
as="select"
|
<label htmlFor="setupCamera">Setup Camera</label>
|
||||||
name="setupCamera"
|
<Field
|
||||||
className="p-2 border border-gray-400 rounded-lg text-white"
|
as="select"
|
||||||
>
|
id="setupCamera"
|
||||||
<option value={1} className="bg-[#253445]">
|
name="setupCamera"
|
||||||
1
|
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445]"
|
||||||
</option>
|
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
|
||||||
<option value={2} className="bg-[#253445]">
|
setFieldValue("setupCamera", parseInt(e.target.value, 10))
|
||||||
2
|
}
|
||||||
</option>
|
|
||||||
<option value={3} className="bg-[#253445]">
|
|
||||||
3
|
|
||||||
</option>
|
|
||||||
<option value={4} className="bg-[#253445]">
|
|
||||||
4
|
|
||||||
</option>
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<label htmlFor="cameraAddress">Camera Address</label>
|
|
||||||
{errors.cameraAddress && (
|
|
||||||
<small className="absolute right-0 text-red-500">
|
|
||||||
{errors?.cameraAddress}
|
|
||||||
</small>
|
|
||||||
)}
|
|
||||||
<Field
|
|
||||||
name={"cameraAddress"}
|
|
||||||
type="text"
|
|
||||||
className="p-2 border border-gray-400 rounded-lg"
|
|
||||||
placeholder="123, London Road..."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<label htmlFor="userName">User Name</label>
|
|
||||||
{errors.userName && (
|
|
||||||
<small className="absolute right-0 text-red-500">
|
|
||||||
{errors?.userName}
|
|
||||||
</small>
|
|
||||||
)}
|
|
||||||
<Field
|
|
||||||
name={"userName"}
|
|
||||||
type="text"
|
|
||||||
className="p-2 border border-gray-400 rounded-lg"
|
|
||||||
placeholder="Enter user name"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<label htmlFor="password">Password</label>
|
|
||||||
{errors.password && (
|
|
||||||
<small className="absolute right-0 text-red-500">
|
|
||||||
{errors?.password}
|
|
||||||
</small>
|
|
||||||
)}
|
|
||||||
<Field
|
|
||||||
name={"password"}
|
|
||||||
type="password"
|
|
||||||
className="p-2 border border-gray-400 rounded-lg"
|
|
||||||
placeholder="Enter password"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="bg-blue-800 rounded-lg p-2 mx-auto"
|
|
||||||
>
|
>
|
||||||
Save settings
|
<option value={1}>1</option>
|
||||||
</button>
|
<option value={2}>2</option>
|
||||||
</Form>
|
<option value={3}>3</option>
|
||||||
);
|
<option value={4}>4</option>
|
||||||
}}
|
</Field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col space-y-2 relative">
|
||||||
|
<label htmlFor="cameraAddress">Camera Address</label>
|
||||||
|
{touched.cameraAddress && errors.cameraAddress && (
|
||||||
|
<small className="absolute right-0 top-0 text-red-500">
|
||||||
|
{errors.cameraAddress}
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
|
<Field
|
||||||
|
id="cameraAddress"
|
||||||
|
name="cameraAddress"
|
||||||
|
type="text"
|
||||||
|
className="p-2 border border-gray-400 rounded-lg"
|
||||||
|
placeholder="123, London Road..."
|
||||||
|
autoComplete="street-address"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col space-y-2 relative">
|
||||||
|
<label htmlFor="userName">User Name</label>
|
||||||
|
{touched.userName && errors.userName && (
|
||||||
|
<small className="absolute right-0 top-0 text-red-500">
|
||||||
|
{errors.userName}
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
|
<Field
|
||||||
|
id="userName"
|
||||||
|
name="userName"
|
||||||
|
type="text"
|
||||||
|
className="p-2 border border-gray-400 rounded-lg"
|
||||||
|
placeholder="Enter user name"
|
||||||
|
autoComplete="username"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col space-y-2 relative">
|
||||||
|
<label htmlFor="password">Password</label>
|
||||||
|
{touched.password && errors.password && (
|
||||||
|
<small className="absolute right-0 top-0 text-red-500">
|
||||||
|
{errors.password}
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
|
<Field
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
className="p-2 border border-gray-400 rounded-lg"
|
||||||
|
placeholder="Enter password"
|
||||||
|
autoComplete="new-password"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="bg-blue-800 text-white rounded-lg p-2 mx-auto"
|
||||||
|
>
|
||||||
|
Save settings
|
||||||
|
</button>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const FrontCameraOverviewCard = () => {
|
|||||||
<Card className={clsx("relative min-h-[40vh] md:min-h-[60vh] h-auto")}>
|
<Card className={clsx("relative min-h-[40vh] md:min-h-[60vh] h-auto")}>
|
||||||
<div className="flex flex-col space-y-3 h-full" {...handlers}>
|
<div className="flex flex-col space-y-3 h-full" {...handlers}>
|
||||||
<CardHeader title="Front Overiew" icon={faCamera} />
|
<CardHeader title="Front Overiew" icon={faCamera} />
|
||||||
<SnapshotContainer side="CameraFront" />
|
<SnapshotContainer side="TargetDetectionFront" />
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,15 +7,17 @@ import CardHeader from "../UI/CardHeader";
|
|||||||
const OverviewVideoContainer = ({
|
const OverviewVideoContainer = ({
|
||||||
title,
|
title,
|
||||||
side,
|
side,
|
||||||
|
settingsPage,
|
||||||
}: {
|
}: {
|
||||||
title: string;
|
title: string;
|
||||||
side: string;
|
side: string;
|
||||||
|
settingsPage?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Card className={clsx("min-h-[40vh] md:min-h-[60vh] h-auto")}>
|
<Card className={clsx("min-h-[40vh] md:min-h-[60vh] h-auto")}>
|
||||||
<div className="relative flex flex-col space-y-3 h-full">
|
<div className="relative flex flex-col space-y-3 h-full">
|
||||||
<CardHeader title={title} icon={faCamera} />
|
<CardHeader title={title} icon={faCamera} />
|
||||||
<SnapshotContainer side={side} />
|
<SnapshotContainer side={side} settingsPage={settingsPage} />
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Formik, Form, Field } from "formik";
|
||||||
|
|
||||||
|
const OutputForm = () => {
|
||||||
|
const initialValues = {
|
||||||
|
includeVRM: false,
|
||||||
|
includeMotion: false,
|
||||||
|
includeTimestamp: false,
|
||||||
|
timeStampFormat: "utc",
|
||||||
|
};
|
||||||
|
return <div>OutputForm</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OutputForm;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { GB } from "country-flag-icons/react/3x2";
|
import { GB } from "country-flag-icons/react/3x2";
|
||||||
import { formatNumberPlate } from "../../utils/utils";
|
import { formatNumberPlate } from "../../utils/utils";
|
||||||
import type { Sighting } from "../../types/types";
|
import type { SightingType } from "../../types/types";
|
||||||
|
|
||||||
type NumberPlateProps = {
|
type NumberPlateProps = {
|
||||||
sighting: Sighting;
|
sighting: SightingType;
|
||||||
};
|
};
|
||||||
|
|
||||||
const NumberPlate = ({ sighting }: NumberPlateProps) => {
|
const NumberPlate = ({ sighting }: NumberPlateProps) => {
|
||||||
|
|||||||
14
src/components/SettingForms/BearerType/BearerTypeCard.tsx
Normal file
14
src/components/SettingForms/BearerType/BearerTypeCard.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import Card from "../../UI/Card";
|
||||||
|
import CardHeader from "../../UI/CardHeader";
|
||||||
|
import BearerTypeFields from "./BearerTypeFields";
|
||||||
|
|
||||||
|
const BearerTypeCard = () => {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader title="Bearer Type" />
|
||||||
|
<BearerTypeFields />
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BearerTypeCard;
|
||||||
35
src/components/SettingForms/BearerType/BearerTypeFields.tsx
Normal file
35
src/components/SettingForms/BearerType/BearerTypeFields.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Field, useFormikContext } from "formik";
|
||||||
|
|
||||||
|
import FormToggle from "../components/FormToggle";
|
||||||
|
|
||||||
|
export const ValuesComponent = () => {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const BearerTypeFields = () => {
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
console.log(values);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col space-y-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<label htmlFor="format">Format</label>
|
||||||
|
<Field
|
||||||
|
as="select"
|
||||||
|
name="format"
|
||||||
|
id="format"
|
||||||
|
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445]"
|
||||||
|
>
|
||||||
|
<option value="JSON">JSON</option>
|
||||||
|
<option value="BOF2">BOF2</option>
|
||||||
|
</Field>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col space-y-4">
|
||||||
|
<FormToggle name="enabled" label="Enabled" />
|
||||||
|
<FormToggle name="verbose" label="Verbose" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BearerTypeFields;
|
||||||
14
src/components/SettingForms/Channel1-JSON/ChannelCard.tsx
Normal file
14
src/components/SettingForms/Channel1-JSON/ChannelCard.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import Card from "../../UI/Card";
|
||||||
|
import CardHeader from "../../UI/CardHeader";
|
||||||
|
import ChannelFields from "./ChannelFields";
|
||||||
|
|
||||||
|
const ChannelCard = () => {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader title="Channel 1 (JSON)" />
|
||||||
|
<ChannelFields />
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChannelCard;
|
||||||
64
src/components/SettingForms/Channel1-JSON/ChannelFields.tsx
Normal file
64
src/components/SettingForms/Channel1-JSON/ChannelFields.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { Field, useFormikContext } from "formik";
|
||||||
|
import FormGroup from "../components/FormGroup";
|
||||||
|
|
||||||
|
const ChannelFields = () => {
|
||||||
|
useFormikContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col space-y-2">
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="backoffice" className="m-0">
|
||||||
|
Back Office URL
|
||||||
|
</label>
|
||||||
|
<Field
|
||||||
|
name={"backOfficeURL"}
|
||||||
|
type="text"
|
||||||
|
id="backoffice"
|
||||||
|
placeholder="https://www.backoffice.com"
|
||||||
|
className="p-1.5 border border-gray-400 rounded-lg"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="username">Username</label>
|
||||||
|
<Field
|
||||||
|
name={"username"}
|
||||||
|
type="text"
|
||||||
|
id="username"
|
||||||
|
placeholder="Back office username"
|
||||||
|
className="p-1.5 border border-gray-400 rounded-lg"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="password">Password</label>
|
||||||
|
<Field
|
||||||
|
name={"password"}
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
placeholder="Back office password"
|
||||||
|
className="p-1.5 border border-gray-400 rounded-lg"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="connectTimeoutSeconds">Connect Timeout Seconds</label>
|
||||||
|
<Field
|
||||||
|
name={"connectTimeoutSeconds"}
|
||||||
|
type="number"
|
||||||
|
id="connectTimeoutSeconds"
|
||||||
|
className="p-1.5 border border-gray-400 rounded-lg"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="readTimeoutSeconds">Read Timeout Seconds</label>
|
||||||
|
<Field
|
||||||
|
name={"readTimeoutSeconds"}
|
||||||
|
type="number"
|
||||||
|
id="readTimeoutSeconds"
|
||||||
|
placeholder="https://example.com"
|
||||||
|
className="p-1.5 border border-gray-400 rounded-lg"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChannelFields;
|
||||||
12
src/components/SettingForms/NPED/NPEDCard.tsx
Normal file
12
src/components/SettingForms/NPED/NPEDCard.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import Card from "../../UI/Card";
|
||||||
|
import CardHeader from "../../UI/CardHeader";
|
||||||
|
|
||||||
|
const NPEDCard = () => {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader title={"NPED Config and Hotlist"} />
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NPEDCard;
|
||||||
0
src/components/SettingForms/NPED/NPEDFields.tsx
Normal file
0
src/components/SettingForms/NPED/NPEDFields.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import Card from "../../UI/Card";
|
||||||
|
import CardHeader from "../../UI/CardHeader";
|
||||||
|
import OverviewTextFields from "./OverviewTextFields";
|
||||||
|
|
||||||
|
const OverviewTextCard = () => {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader title={"Overview Text"} />
|
||||||
|
<OverviewTextFields />
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OverviewTextCard;
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { Field, useFormikContext } from "formik";
|
||||||
|
import FormGroup from "../components/FormGroup";
|
||||||
|
import FormToggle from "../components/FormToggle";
|
||||||
|
|
||||||
|
const OverviewTextFields = () => {
|
||||||
|
useFormikContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col space-y-2">
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="overviewQuality">Include VRM</label>
|
||||||
|
<FormToggle name="includeVRM" />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="includeMotion">Include Motion</label>
|
||||||
|
<FormToggle name="includeMotion" />
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="includeCameraName">Include Camera Name</label>
|
||||||
|
<FormToggle name="includeCameraName" />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="includeTimestamp">Include Timestamp</label>
|
||||||
|
<FormToggle name="includeTimestamp" />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="">Timestamp Format</label>
|
||||||
|
<Field
|
||||||
|
as="select"
|
||||||
|
name="timestampFormat"
|
||||||
|
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445]"
|
||||||
|
>
|
||||||
|
<option value={"UTC"}>UTC</option>
|
||||||
|
<option value={"LOCAL"}>Local</option>
|
||||||
|
</Field>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="overlayPosition">Overlay Position</label>
|
||||||
|
<Field
|
||||||
|
as="select"
|
||||||
|
name="overlayPosition"
|
||||||
|
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445]"
|
||||||
|
>
|
||||||
|
<option value={"Top"}>Top</option>
|
||||||
|
<option value={"Bottom"}>Bottom</option>
|
||||||
|
</Field>
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OverviewTextFields;
|
||||||
84
src/components/SettingForms/SettingForms/SettingForms.tsx
Normal file
84
src/components/SettingForms/SettingForms/SettingForms.tsx
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { Formik, Form } from "formik";
|
||||||
|
import BearerTypeCard from "../BearerType/BearerTypeCard";
|
||||||
|
import ChannelCard from "../Channel1-JSON/ChannelCard";
|
||||||
|
import type { InitialValuesForm } from "../../../types/types";
|
||||||
|
import { useState } from "react";
|
||||||
|
import AdvancedToggle from "../../UI/AdvancedToggle";
|
||||||
|
import OverviewTextCard from "../OverviewText/OverviewTextCard";
|
||||||
|
import SightingDataCard from "../SightingData/SightingDataCard";
|
||||||
|
|
||||||
|
const SettingForms = () => {
|
||||||
|
const [advancedToggle, setAdvancedToggle] = useState(false);
|
||||||
|
|
||||||
|
const initialValues = {
|
||||||
|
format: "JSON",
|
||||||
|
enabled: false,
|
||||||
|
verbose: false,
|
||||||
|
backOfficeURL: "",
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
connectTimeoutSeconds: 0,
|
||||||
|
readTimeoutSeconds: 0,
|
||||||
|
overviewQuality: "high",
|
||||||
|
overviewImageScaleFactor: "full",
|
||||||
|
overviewType: "Plate Overview",
|
||||||
|
invertMotion: false,
|
||||||
|
maxPlateValueLength: 0,
|
||||||
|
vrmToTransit: "plain VRM ASCII (default)",
|
||||||
|
staticReadAction: "Use Lane Direction",
|
||||||
|
noRegionAction: "send",
|
||||||
|
countryCodeType: "IBAN 2 Character code (default)",
|
||||||
|
filterMinConfidence: 0,
|
||||||
|
filterMaxConfidence: 100,
|
||||||
|
overviewQualityOverride: 0,
|
||||||
|
sightingDataEnabled: false,
|
||||||
|
sighthingDataVerbose: false,
|
||||||
|
includeVRM: false,
|
||||||
|
includeMotion: false,
|
||||||
|
includeTimestamp: false,
|
||||||
|
timestampFormat: "UTC",
|
||||||
|
includeCameraName: false,
|
||||||
|
customFieldA: "",
|
||||||
|
customFieldB: "",
|
||||||
|
customFieldC: "",
|
||||||
|
customFieldD: "",
|
||||||
|
overlayPosition: "Top",
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (values: InitialValuesForm) => {
|
||||||
|
alert(JSON.stringify(values));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
|
||||||
|
<Form className="flex flex-col space-y-3">
|
||||||
|
<div className="mx-auto grid grid-cols-1 sm:grid-cols-1 lg:grid-cols-2 gap-2 px-2 sm:px-4 lg:px-0 w-full">
|
||||||
|
<BearerTypeCard />
|
||||||
|
<ChannelCard />
|
||||||
|
</div>
|
||||||
|
<AdvancedToggle
|
||||||
|
advancedToggle={advancedToggle}
|
||||||
|
onAdvancedChange={setAdvancedToggle}
|
||||||
|
/>
|
||||||
|
{advancedToggle && (
|
||||||
|
<>
|
||||||
|
<div className="md:col-span-2">
|
||||||
|
<SightingDataCard />
|
||||||
|
</div>
|
||||||
|
<div className="md:col-span-2">
|
||||||
|
<OverviewTextCard />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="w-1/4 text-white bg-blue-700 hover:bg-blue-800 font-small rounded-lg text-sm px-2 py-2.5"
|
||||||
|
>
|
||||||
|
Save changes
|
||||||
|
</button>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SettingForms;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import Card from "../../UI/Card";
|
||||||
|
import CardHeader from "../../UI/CardHeader";
|
||||||
|
import SightingDataFields from "./SightingDataFields";
|
||||||
|
|
||||||
|
const SightingDataCard = () => {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader title={"Sighting Data"} />
|
||||||
|
<SightingDataFields />
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SightingDataCard;
|
||||||
123
src/components/SettingForms/SightingData/SightingDataFields.tsx
Normal file
123
src/components/SettingForms/SightingData/SightingDataFields.tsx
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import { Field, useFormikContext } from "formik";
|
||||||
|
import FormGroup from "../components/FormGroup";
|
||||||
|
import FormToggle from "../components/FormToggle";
|
||||||
|
|
||||||
|
const SightingDataFields = () => {
|
||||||
|
useFormikContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col space-y-2">
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="overviewQuality">Overview Quality</label>
|
||||||
|
<Field
|
||||||
|
name="overviewQuality"
|
||||||
|
as="select"
|
||||||
|
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445]"
|
||||||
|
>
|
||||||
|
<option value="HIGH">High</option>
|
||||||
|
<option value="MEDIUM">Medium</option>
|
||||||
|
<option value="LOW">Low</option>
|
||||||
|
</Field>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="overviewImageScaleFactor">
|
||||||
|
Overview Image Scale Factor
|
||||||
|
</label>
|
||||||
|
<Field
|
||||||
|
as="select"
|
||||||
|
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445]"
|
||||||
|
>
|
||||||
|
<option value="HIGH">Full</option>
|
||||||
|
<option value="MEDIUM">3/4</option>
|
||||||
|
<option value="LOW">2/4</option>
|
||||||
|
<option value="LOW">1/4</option>
|
||||||
|
</Field>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="overviewType">Overview Type</label>
|
||||||
|
<Field
|
||||||
|
as="select"
|
||||||
|
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445]"
|
||||||
|
>
|
||||||
|
<option value="PlainOverview">Plain Overview</option>
|
||||||
|
<option value="IncludePlatePatches">Include Plate Patches</option>
|
||||||
|
</Field>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="invertMotion">Invert Motion</label>
|
||||||
|
<FormToggle name="invertMotion" />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="maxPlateValueLength">Max Plate Value Length</label>
|
||||||
|
<Field
|
||||||
|
name="maxPlateValueLength"
|
||||||
|
type={"number"}
|
||||||
|
className="p-1.5 border border-gray-400 rounded-lg"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="vrmToTransit">VRM To Transit</label>
|
||||||
|
<Field
|
||||||
|
as="select"
|
||||||
|
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445]"
|
||||||
|
>
|
||||||
|
<option value="PlainOverview">plain VRM ASCII (default)</option>
|
||||||
|
<option value="IncludePlatePatches">plain VRM ASCII (default)</option>
|
||||||
|
</Field>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="staticReadAction">Static Read Action</label>
|
||||||
|
<Field
|
||||||
|
as="select"
|
||||||
|
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445]"
|
||||||
|
>
|
||||||
|
<option value="UseLaneDirection">Use Lane Direction</option>
|
||||||
|
<option value="IncludePlatePatches">plain VRM ASCII (default)</option>
|
||||||
|
</Field>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="noRegionAction">No Region Action</label>
|
||||||
|
<Field
|
||||||
|
as="select"
|
||||||
|
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445]"
|
||||||
|
>
|
||||||
|
<option value="UseLaneDirection">Send</option>
|
||||||
|
<option value="IncludePlatePatches">plain VRM ASCII (default)</option>
|
||||||
|
</Field>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="countryCodeType">Country Code Type</label>
|
||||||
|
<Field
|
||||||
|
as="select"
|
||||||
|
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445]"
|
||||||
|
>
|
||||||
|
<option value="IBAN 2 Character code (default)">
|
||||||
|
IBAN 2 Character code (default)
|
||||||
|
</option>
|
||||||
|
<option value="IncludePlatePatches">plain VRM ASCII (default)</option>
|
||||||
|
</Field>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="overviewQualityOverride">
|
||||||
|
Overview Quality Override
|
||||||
|
</label>
|
||||||
|
<Field
|
||||||
|
name="maxPlateValueLength"
|
||||||
|
type={"number"}
|
||||||
|
className="p-1.5 border border-gray-400 rounded-lg"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="sightingDataEnabled">Sighting Data Enabled</label>
|
||||||
|
<FormToggle name="sightingDataEnabled" />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="sighthingDataVerbose">Sighting Data Verbose</label>
|
||||||
|
<FormToggle name="sighthingDataVerbose" />
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SightingDataFields;
|
||||||
15
src/components/SettingForms/components/FormGroup.tsx
Normal file
15
src/components/SettingForms/components/FormGroup.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
type FormGroupProps = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const FormGroup = ({ children }: FormGroupProps) => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col md:flex-row items-center justify-between">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FormGroup;
|
||||||
17
src/components/SettingForms/components/FormToggle.tsx
Normal file
17
src/components/SettingForms/components/FormToggle.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Field } from "formik";
|
||||||
|
|
||||||
|
const FormToggle = ({ name, label }: { name: string; label?: string }) => {
|
||||||
|
return (
|
||||||
|
<label className="flex items-center gap-3 cursor-pointer select-none w-50">
|
||||||
|
<span className="text-sm">{label}</span>
|
||||||
|
<Field id={name} type="checkbox" name={name} className="sr-only peer" />
|
||||||
|
<div
|
||||||
|
className="relative w-10 h-5 rounded-full bg-gray-300 transition peer-checked:bg-blue-500 after:content-['']
|
||||||
|
after:absolute after:top-0.5 after:left-0.5 after:w-4 after:h-4 after:rounded-full after:bg-white after:shadow after:transition
|
||||||
|
after:duration-300 peer-checked:after:translate-x-5"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FormToggle;
|
||||||
32
src/components/UI/AdvancedToggle.tsx
Normal file
32
src/components/UI/AdvancedToggle.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
type AdvancedToggleProps = {
|
||||||
|
advancedToggle: boolean;
|
||||||
|
onAdvancedChange: (advancedToggle: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AdvancedToggle = ({
|
||||||
|
advancedToggle,
|
||||||
|
onAdvancedChange,
|
||||||
|
}: AdvancedToggleProps) => {
|
||||||
|
return (
|
||||||
|
<div className="mx-auto">
|
||||||
|
<label className="flex flex-row space-x-2">
|
||||||
|
<span>Advanced Settings</span>
|
||||||
|
<input
|
||||||
|
name="advancedSettings"
|
||||||
|
type="checkbox"
|
||||||
|
id="advancedSettings"
|
||||||
|
className="sr-only peer"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="relative w-10 h-5 rounded-full bg-gray-300 transition peer-checked:bg-blue-500 after:content-['']
|
||||||
|
after:absolute after:top-0.5 after:left-0.5 after:w-4 after:h-4 after:rounded-full after:bg-white after:shadow after:transition
|
||||||
|
after:duration-300 peer-checked:after:translate-x-5"
|
||||||
|
onClick={() => onAdvancedChange(!advancedToggle)}
|
||||||
|
></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AdvancedToggle;
|
||||||
@@ -5,7 +5,7 @@ import clsx from "clsx";
|
|||||||
|
|
||||||
type CameraOverviewHeaderProps = {
|
type CameraOverviewHeaderProps = {
|
||||||
title: string;
|
title: string;
|
||||||
icon: IconProp;
|
icon?: IconProp;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CardHeader = ({ title, icon }: CameraOverviewHeaderProps) => {
|
const CardHeader = ({ title, icon }: CameraOverviewHeaderProps) => {
|
||||||
@@ -15,7 +15,7 @@ const CardHeader = ({ title, icon }: CameraOverviewHeaderProps) => {
|
|||||||
"w-full border-b border-gray-600 flex flex-row items-center space-x-2 md:mb-6"
|
"w-full border-b border-gray-600 flex flex-row items-center space-x-2 md:mb-6"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={icon} className="size-4" />
|
{icon && <FontAwesomeIcon icon={icon} className="size-4" />}
|
||||||
<h2 className="text-xl">{title}</h2>
|
<h2 className="text-xl">{title}</h2>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,25 +4,48 @@ import { useNavigate } from "react-router";
|
|||||||
|
|
||||||
type NavigationArrowProps = {
|
type NavigationArrowProps = {
|
||||||
side: string;
|
side: string;
|
||||||
|
settingsPage?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const NavigationArrow = ({ side }: NavigationArrowProps) => {
|
const NavigationArrow = ({ side, settingsPage }: NavigationArrowProps) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const navigationDest = (side: string) => {
|
const navigationDest = (side: string) => {
|
||||||
if (side === "CameraFront") {
|
if (settingsPage) {
|
||||||
navigate("/front-camera-settings");
|
|
||||||
} else if (side === "Rear") {
|
|
||||||
navigate("/");
|
navigate("/");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (side === "TargetDetectionFront") {
|
||||||
|
navigate("/front-camera-settings");
|
||||||
} else if (side === "TargetDetectionRear") {
|
} else if (side === "TargetDetectionRear") {
|
||||||
navigate("/Rear-Camera-settings");
|
navigate("/Rear-Camera-settings");
|
||||||
} else {
|
|
||||||
navigate("/");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (settingsPage) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{side === "TargetDetectionFront" ? (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faArrowRight}
|
||||||
|
className="absolute top-[50%] right-[2%] backdrop-blur-md hover:cursor-pointer animate-bounce"
|
||||||
|
onClick={() => navigationDest(side)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faArrowLeft}
|
||||||
|
className="absolute top-[50%] left-[2%] backdrop-blur-md hover:cursor-pointer animate-bounce"
|
||||||
|
onClick={() => navigationDest(side)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{side === "CameraFront" || side === "Rear" ? (
|
{side === "TargetDetectionFront" ? (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faArrowLeft}
|
icon={faArrowLeft}
|
||||||
className="absolute top-[50%] left-[2%] backdrop-blur-md hover:cursor-pointer animate-bounce"
|
className="absolute top-[50%] left-[2%] backdrop-blur-md hover:cursor-pointer animate-bounce"
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ const apiUrl = import.meta.env.VITE_BASEURL;
|
|||||||
|
|
||||||
async function fetchSnapshot(cameraSide: string) {
|
async function fetchSnapshot(cameraSide: string) {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`http://100.116.253.81/Colour-preview`
|
// `http://100.116.253.81/Colour-preview`
|
||||||
//`${apiUrl}/${cameraSide}-preview`
|
`${apiUrl}/${cameraSide}-preview`
|
||||||
);
|
);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("Cannot reach endpoint");
|
throw new Error("Cannot reach endpoint");
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ const FrontCamera = () => {
|
|||||||
className="mx-auto grid grid-cols-1 sm:grid-cols-1 lg:grid-cols-2 gap-4 px-2 sm:px-4 lg:px-0 w-full"
|
className="mx-auto grid grid-cols-1 sm:grid-cols-1 lg:grid-cols-2 gap-4 px-2 sm:px-4 lg:px-0 w-full"
|
||||||
{...handlers}
|
{...handlers}
|
||||||
>
|
>
|
||||||
<OverviewVideoContainer title={"Front Camera"} side="Front" />
|
<OverviewVideoContainer
|
||||||
|
title={"Front Camera"}
|
||||||
|
side="TargetDetectionFront"
|
||||||
|
settingsPage={true}
|
||||||
|
/>
|
||||||
<CameraSettings title="Front Camera Settings" />
|
<CameraSettings title="Front Camera Settings" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,7 +16,11 @@ const RearCamera = () => {
|
|||||||
{...handlers}
|
{...handlers}
|
||||||
>
|
>
|
||||||
<CameraSettings title="Rear Camera Settings" />
|
<CameraSettings title="Rear Camera Settings" />
|
||||||
<OverviewVideoContainer title={"Rear Camera"} side={"Rear"} />
|
<OverviewVideoContainer
|
||||||
|
title={"Rear Camera"}
|
||||||
|
side={"TargetDetectionRear"}
|
||||||
|
settingsPage={true}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,27 @@
|
|||||||
import Output from "../components/Output/Output";
|
import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
|
||||||
|
import "react-tabs/style/react-tabs.css";
|
||||||
|
import NPEDCard from "../components/SettingForms/NPED/NPEDCard";
|
||||||
|
import SettingForms from "../components/SettingForms/SettingForms/SettingForms";
|
||||||
|
|
||||||
const SystemSettings = () => {
|
const SystemSettings = () => {
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto grid grid-cols-1 sm:grid-cols-1 lg:grid-cols-2 gap-2 px-2 sm:px-4 lg:px-0 w-full">
|
<div className="m-4">
|
||||||
<Output />
|
<Tabs selectedTabClassName="bg-gray-300 text-gray-900 font-semibold border-none">
|
||||||
<Output />
|
<TabList>
|
||||||
|
<Tab>Output</Tab>
|
||||||
|
<Tab>Integrations</Tab>
|
||||||
|
</TabList>
|
||||||
|
<TabPanel>
|
||||||
|
<div className="flex flex-col space-y-3">
|
||||||
|
<SettingForms />
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel>
|
||||||
|
<div className="mx-auto grid grid-cols-1 sm:grid-cols-1 lg:grid-cols-2 gap-2 px-2 sm:px-4 lg:px-0 w-full">
|
||||||
|
<NPEDCard />
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,3 +40,20 @@ export type CameraSettingValues = {
|
|||||||
export type CameraSettingErrorValues = Partial<
|
export type CameraSettingErrorValues = Partial<
|
||||||
Record<keyof CameraSettingValues, string>
|
Record<keyof CameraSettingValues, string>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
export type BearerTypeFieldType = {
|
||||||
|
format: string;
|
||||||
|
enabled: boolean;
|
||||||
|
verbose: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InitialValuesForm = {
|
||||||
|
format: string;
|
||||||
|
enabled: boolean;
|
||||||
|
verbose: boolean;
|
||||||
|
backOfficeURL: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
connectTimeoutSeconds: number;
|
||||||
|
readTimeoutSeconds: number;
|
||||||
|
};
|
||||||
|
|||||||
12
yarn.lock
12
yarn.lock
@@ -1029,7 +1029,7 @@ chownr@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4"
|
resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4"
|
||||||
integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==
|
integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==
|
||||||
|
|
||||||
clsx@^2.1.1:
|
clsx@^2.0.0, clsx@^2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
|
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
|
||||||
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
|
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
|
||||||
@@ -1761,7 +1761,7 @@ prelude-ls@^1.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||||
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
|
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
|
||||||
|
|
||||||
prop-types@^15.7.2, prop-types@^15.8.1:
|
prop-types@^15.5.0, prop-types@^15.7.2, prop-types@^15.8.1:
|
||||||
version "15.8.1"
|
version "15.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||||
@@ -1830,6 +1830,14 @@ react-swipeable@^7.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/react-swipeable/-/react-swipeable-7.0.2.tgz#ef8858096a47144ba7060675af1cd672e00ecc12"
|
resolved "https://registry.yarnpkg.com/react-swipeable/-/react-swipeable-7.0.2.tgz#ef8858096a47144ba7060675af1cd672e00ecc12"
|
||||||
integrity sha512-v1Qx1l+aC2fdxKa9aKJiaU/ZxmJ5o98RMoFwUqAAzVWUcxgfHFXDDruCKXhw6zIYXm6V64JiHgP9f6mlME5l8w==
|
integrity sha512-v1Qx1l+aC2fdxKa9aKJiaU/ZxmJ5o98RMoFwUqAAzVWUcxgfHFXDDruCKXhw6zIYXm6V64JiHgP9f6mlME5l8w==
|
||||||
|
|
||||||
|
react-tabs@^6.1.0:
|
||||||
|
version "6.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-tabs/-/react-tabs-6.1.0.tgz#a1fc9d9b8db4c6e7bb327a1b6783bc51a1c457a1"
|
||||||
|
integrity sha512-6QtbTRDKM+jA/MZTTefvigNxo0zz+gnBTVFw2CFVvq+f2BuH0nF0vDLNClL045nuTAdOoK/IL1vTP0ZLX0DAyQ==
|
||||||
|
dependencies:
|
||||||
|
clsx "^2.0.0"
|
||||||
|
prop-types "^15.5.0"
|
||||||
|
|
||||||
react@^19.1.1:
|
react@^19.1.1:
|
||||||
version "19.1.1"
|
version "19.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-19.1.1.tgz#06d9149ec5e083a67f9a1e39ce97b06a03b644af"
|
resolved "https://registry.yarnpkg.com/react/-/react-19.1.1.tgz#06d9149ec5e083a67f9a1e39ce97b06a03b644af"
|
||||||
|
|||||||
Reference in New Issue
Block a user