updated NPED code
This commit is contained in:
19
src/App.tsx
19
src/App.tsx
@@ -4,17 +4,20 @@ import { Route, Routes } from "react-router";
|
||||
import FrontCamera from "./pages/FrontCamera";
|
||||
import RearCamera from "./pages/RearCamera";
|
||||
import SystemSettings from "./pages/SystemSettings";
|
||||
import { NPEDUserProvider } from "./context/providers/NPEDUserContextProvider";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/" element={<Container />}>
|
||||
<Route index element={<Dashboard />} />
|
||||
<Route path="front-camera-settings" element={<FrontCamera />} />
|
||||
<Route path="rear-camera-settings" element={<RearCamera />} />
|
||||
<Route path="system-settings" element={<SystemSettings />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
<NPEDUserProvider>
|
||||
<Routes>
|
||||
<Route path="/" element={<Container />}>
|
||||
<Route index element={<Dashboard />} />
|
||||
<Route path="front-camera-settings" element={<FrontCamera />} />
|
||||
<Route path="rear-camera-settings" element={<RearCamera />} />
|
||||
<Route path="system-settings" element={<SystemSettings />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</NPEDUserProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import Card from "../../UI/Card";
|
||||
import CardHeader from "../../UI/CardHeader";
|
||||
import NPEDFields from "./NPEDFields";
|
||||
import { useNPEDContext } from "../../../context/NPEDUserContext";
|
||||
|
||||
const NPEDCard = () => {
|
||||
const { user } = useNPEDContext();
|
||||
console.log(user);
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title={"NPED Config"} />
|
||||
|
||||
@@ -1,62 +1,97 @@
|
||||
import { Form, Formik, Field } from "formik";
|
||||
import FormGroup from "../components/FormGroup";
|
||||
import type { NPEDFieldType } from "../../../types/types";
|
||||
import { useNPEDAuth } from "../../../hooks/useNPEDAuh";
|
||||
import type { NPEDErrorValues, NPEDFieldType } from "../../../types/types";
|
||||
import { useNPEDAuth } from "../../../hooks/useNPEDAuth";
|
||||
import { toast } from "sonner";
|
||||
|
||||
const NPEDFields = () => {
|
||||
const { signIn, isError } = useNPEDAuth();
|
||||
const { signIn } = useNPEDAuth();
|
||||
|
||||
const initialValues = {
|
||||
username: "",
|
||||
password: "",
|
||||
clientId: "",
|
||||
frontId: "NPEDFront",
|
||||
rearId: "NPEDRear",
|
||||
};
|
||||
|
||||
const handleSubmit = (values: NPEDFieldType) => {
|
||||
console.log(isError);
|
||||
signIn(values);
|
||||
const valuesToSend = {
|
||||
...values,
|
||||
};
|
||||
signIn(valuesToSend);
|
||||
toast("Signed in successfully");
|
||||
};
|
||||
|
||||
const validateValues = (values: NPEDFieldType) => {
|
||||
const errors: NPEDErrorValues = {};
|
||||
if (!values.username) errors.username = "Required";
|
||||
if (!values.password) errors.password = "Required";
|
||||
if (!values.clientId) errors.clientId = "Required";
|
||||
return errors;
|
||||
};
|
||||
|
||||
return (
|
||||
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
|
||||
<Form className="flex flex-col space-y-2">
|
||||
<FormGroup>
|
||||
<label htmlFor="username">Username</label>
|
||||
<Field
|
||||
name="username"
|
||||
type="text"
|
||||
id="username"
|
||||
placeholder="NPED 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="NPED Password"
|
||||
className="p-1.5 border border-gray-400 rounded-lg"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<label htmlFor="clientId">Client ID</label>
|
||||
<Field
|
||||
name="clientId"
|
||||
type="text"
|
||||
id="clientId"
|
||||
placeholder="NPED client ID"
|
||||
className="p-1.5 border border-gray-400 rounded-lg"
|
||||
/>
|
||||
</FormGroup>
|
||||
<button
|
||||
type="submit"
|
||||
className="w-1/4 text-white bg-green-700 hover:bg-green-800 font-small rounded-lg text-sm px-2 py-2.5"
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
</Form>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleSubmit}
|
||||
validate={validateValues}
|
||||
>
|
||||
{({ errors, touched }) => (
|
||||
<Form className="flex flex-col space-y-5">
|
||||
<FormGroup>
|
||||
<label htmlFor="username">Username</label>
|
||||
{touched.username && errors.username && (
|
||||
<small className="absolute right-0 -top-5 text-red-500">
|
||||
{errors.username}
|
||||
</small>
|
||||
)}
|
||||
<Field
|
||||
name="username"
|
||||
type="text"
|
||||
id="username"
|
||||
placeholder="NPED username"
|
||||
className="p-1.5 border border-gray-400 rounded-lg"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<label htmlFor="password">Password</label>
|
||||
{touched.password && errors.password && (
|
||||
<small className="absolute right-0 -top-5 text-red-500">
|
||||
{errors.password}
|
||||
</small>
|
||||
)}
|
||||
<Field
|
||||
name="password"
|
||||
type="password"
|
||||
id="password"
|
||||
placeholder="NPED Password"
|
||||
className="p-1.5 border border-gray-400 rounded-lg"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<label htmlFor="clientId">Client ID</label>
|
||||
{touched.clientId && errors.clientId && (
|
||||
<small className="absolute right-0 -top-5 text-red-500">
|
||||
{errors.clientId}
|
||||
</small>
|
||||
)}
|
||||
<Field
|
||||
name="clientId"
|
||||
type="text"
|
||||
id="clientId"
|
||||
placeholder="NPED client ID"
|
||||
className="p-1.5 border border-gray-400 rounded-lg"
|
||||
/>
|
||||
</FormGroup>
|
||||
<button
|
||||
type="submit"
|
||||
className="w-1/4 text-white bg-green-700 hover:bg-green-800 font-small rounded-lg text-sm px-2 py-2.5"
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ type FormGroupProps = {
|
||||
|
||||
const FormGroup = ({ children }: FormGroupProps) => {
|
||||
return (
|
||||
<div className="flex flex-col md:flex-row items-center justify-between">
|
||||
<div className="flex flex-col md:flex-row items-center justify-between relative">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { Link } from "react-router";
|
||||
import Logo from "/MAV.svg";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faSliders } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
const Header = () => {
|
||||
return (
|
||||
<div className="relative bg-[#253445] border-b border-gray-500 items-center mx-auto px-2 sm:px-6 lg:px-8 p-4">
|
||||
<div className="relative bg-[#253445] border-b border-gray-500 items-center mx-auto px-2 sm:px-6 lg:px-8 p-4 flex flex-row justify-between">
|
||||
<div className="w-30">
|
||||
<Link to={"/"}>
|
||||
<img src={Logo} alt="Logo" width={100} height={100} />
|
||||
</Link>
|
||||
</div>
|
||||
<Link to={"/system-settings"}>
|
||||
<FontAwesomeIcon icon={faSliders} />
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
17
src/context/NPEDUserContext.tsx
Normal file
17
src/context/NPEDUserContext.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { createContext, useContext, type SetStateAction } from "react";
|
||||
import type { NPEDUser } from "../types/types";
|
||||
|
||||
type UserContextValue = {
|
||||
user: NPEDUser | null;
|
||||
setUser: React.Dispatch<SetStateAction<NPEDUser | null>>;
|
||||
};
|
||||
|
||||
export const NPEDUserContext = createContext<UserContextValue | undefined>(
|
||||
undefined
|
||||
);
|
||||
export const useNPEDContext = () => {
|
||||
const ctx = useContext(NPEDUserContext);
|
||||
if (!ctx)
|
||||
throw new Error("useNPEDContext must be used within <NPEDUserProvider>");
|
||||
return ctx;
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createContext, useContext, type ReactNode } from "react";
|
||||
import { createContext, useContext } from "react";
|
||||
import type { SightingWidgetType } from "../types/types";
|
||||
import { useSightingFeed } from "../hooks/useSightingFeed";
|
||||
|
||||
type SightingFeedContextType = {
|
||||
sightings: (SightingWidgetType | null | undefined)[];
|
||||
@@ -13,50 +12,10 @@ type SightingFeedContextType = {
|
||||
noSighting: boolean;
|
||||
};
|
||||
|
||||
type SightingFeedProviderProps = {
|
||||
url: string;
|
||||
children: ReactNode;
|
||||
side: string;
|
||||
};
|
||||
export const SightingFeedContext = createContext<
|
||||
SightingFeedContextType | undefined
|
||||
>(undefined);
|
||||
|
||||
const SightingFeedContext = createContext<SightingFeedContextType | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
export const SightingFeedProvider = ({
|
||||
children,
|
||||
url,
|
||||
side,
|
||||
}: SightingFeedProviderProps) => {
|
||||
const {
|
||||
sightings,
|
||||
selectedRef,
|
||||
setSelectedRef,
|
||||
effectiveSelected,
|
||||
mostRecent,
|
||||
isPending,
|
||||
noSighting,
|
||||
} = useSightingFeed(url);
|
||||
|
||||
return (
|
||||
<SightingFeedContext.Provider
|
||||
value={{
|
||||
sightings,
|
||||
selectedRef,
|
||||
setSelectedRef,
|
||||
effectiveSelected,
|
||||
mostRecent,
|
||||
side,
|
||||
isPending,
|
||||
noSighting,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SightingFeedContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export const useSightingFeedContext = () => {
|
||||
const ctx = useContext(SightingFeedContext);
|
||||
if (!ctx)
|
||||
|
||||
17
src/context/providers/NPEDUserContextProvider.tsx
Normal file
17
src/context/providers/NPEDUserContextProvider.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { useState, type ReactNode } from "react";
|
||||
import type { NPEDUser } from "../../types/types";
|
||||
import { NPEDUserContext } from "../NPEDUserContext";
|
||||
|
||||
type NPEDUserProviderType = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export const NPEDUserProvider = ({ children }: NPEDUserProviderType) => {
|
||||
const [user, setUser] = useState<NPEDUser | null>(null);
|
||||
|
||||
return (
|
||||
<NPEDUserContext.Provider value={{ user, setUser }}>
|
||||
{children}
|
||||
</NPEDUserContext.Provider>
|
||||
);
|
||||
};
|
||||
42
src/context/providers/SightingFeedProvider.tsx
Normal file
42
src/context/providers/SightingFeedProvider.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { useSightingFeed } from "../../hooks/useSightingFeed";
|
||||
import { SightingFeedContext } from "../SightingFeedContext";
|
||||
|
||||
type SightingFeedProviderProps = {
|
||||
url: string;
|
||||
children: ReactNode;
|
||||
side: string;
|
||||
};
|
||||
|
||||
export const SightingFeedProvider = ({
|
||||
children,
|
||||
url,
|
||||
side,
|
||||
}: SightingFeedProviderProps) => {
|
||||
const {
|
||||
sightings,
|
||||
selectedRef,
|
||||
setSelectedRef,
|
||||
effectiveSelected,
|
||||
mostRecent,
|
||||
isPending,
|
||||
noSighting,
|
||||
} = useSightingFeed(url);
|
||||
|
||||
return (
|
||||
<SightingFeedContext.Provider
|
||||
value={{
|
||||
sightings,
|
||||
selectedRef,
|
||||
setSelectedRef,
|
||||
effectiveSelected,
|
||||
mostRecent,
|
||||
side,
|
||||
isPending,
|
||||
noSighting,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SightingFeedContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,49 +0,0 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import type { NPEDFieldType } from "../types/types";
|
||||
|
||||
const url = "https://jsonplaceholder.typicode.com/posts";
|
||||
|
||||
async function signIn(loginDetails: NPEDFieldType) {
|
||||
console.log(loginDetails);
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(loginDetails),
|
||||
});
|
||||
if (!response.ok) throw new Error("cannot reach NPED endpoint");
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async function signOut() {
|
||||
const response = await fetch(url, { method: "POST" });
|
||||
if (!response.ok) throw new Error("cannot reach NPED sign out endpoint");
|
||||
return response.json();
|
||||
}
|
||||
|
||||
function setUserContext(user) {
|
||||
console.log(user);
|
||||
}
|
||||
|
||||
export const useNPEDAuth = () => {
|
||||
const signInMutation = useMutation({
|
||||
mutationKey: ["NPEDSignin"],
|
||||
mutationFn: signIn,
|
||||
onSuccess: (data) => setUserContext(data),
|
||||
});
|
||||
|
||||
const signOutMutation = useMutation({
|
||||
mutationKey: ["auth", "NPEDSignOut"],
|
||||
mutationFn: signOut,
|
||||
onSuccess: () => setUserContext(null),
|
||||
});
|
||||
|
||||
return {
|
||||
signIn: signInMutation.mutate,
|
||||
signInAsync: signInMutation.mutateAsync,
|
||||
isPending: signInMutation.isPending,
|
||||
isError: signInMutation.isError,
|
||||
error: signInMutation.error,
|
||||
data: signInMutation.data,
|
||||
|
||||
signOut: signOutMutation.mutate,
|
||||
};
|
||||
};
|
||||
85
src/hooks/useNPEDAuth.ts
Normal file
85
src/hooks/useNPEDAuth.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import type { NPEDFieldType } from "../types/types";
|
||||
import { useNPEDContext } from "../context/NPEDUserContext";
|
||||
|
||||
const base_url = import.meta.env.VITE_OUTSIDE_BASEURL;
|
||||
|
||||
async function signIn(loginDetails: NPEDFieldType) {
|
||||
const { frontId, rearId, username, password, clientId } = loginDetails;
|
||||
|
||||
const NPEDLoginURLFront = `${base_url}/update-config?id=${frontId}`;
|
||||
const NPEDLoginURLRear = `${base_url}/update-config?id=${rearId}`;
|
||||
|
||||
const frontCameraPayload = {
|
||||
id: frontId,
|
||||
fields: [
|
||||
{ property: "propEnabled", value: true },
|
||||
{ property: "propUsername", value: username },
|
||||
{ property: "propPassword", value: password },
|
||||
{ property: "propClientID", value: clientId },
|
||||
],
|
||||
};
|
||||
|
||||
const rearCameraPayload = {
|
||||
id: rearId,
|
||||
fields: [
|
||||
{ property: "propEnabled", value: true },
|
||||
{ property: "propUsername", value: username },
|
||||
{ property: "propPassword", value: password },
|
||||
{ property: "propClientID", value: clientId },
|
||||
],
|
||||
};
|
||||
|
||||
const frontCameraResponse = await fetch(NPEDLoginURLFront, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(frontCameraPayload),
|
||||
});
|
||||
|
||||
const rearCameraResponse = await fetch(NPEDLoginURLRear, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(rearCameraPayload),
|
||||
});
|
||||
|
||||
if (!frontCameraResponse.ok) throw new Error("cannot reach NPED endpoint");
|
||||
if (!rearCameraResponse.ok) throw new Error("cannot reach NPED endpoint");
|
||||
|
||||
console.log(frontCameraResponse);
|
||||
console.log(rearCameraResponse);
|
||||
return {
|
||||
frontResponse: frontCameraResponse.json(),
|
||||
rearResponse: rearCameraResponse.json(),
|
||||
};
|
||||
}
|
||||
|
||||
async function signOut() {
|
||||
const response = await fetch(url, { method: "POST" });
|
||||
if (!response.ok) throw new Error("cannot reach NPED sign out endpoint");
|
||||
return response.json();
|
||||
}
|
||||
|
||||
export const useNPEDAuth = () => {
|
||||
const { setUser } = useNPEDContext();
|
||||
|
||||
const signInMutation = useMutation({
|
||||
mutationKey: ["NPEDSignin"],
|
||||
mutationFn: signIn,
|
||||
onSuccess: async (data) => setUser(await data.frontResponse),
|
||||
});
|
||||
|
||||
const signOutMutation = useMutation({
|
||||
mutationKey: ["auth", "NPEDSignOut"],
|
||||
mutationFn: signOut,
|
||||
onSuccess: () => setUser(null),
|
||||
});
|
||||
|
||||
return {
|
||||
signIn: signInMutation.mutate,
|
||||
signInAsync: signInMutation.mutateAsync,
|
||||
isPending: signInMutation.isPending,
|
||||
isError: signInMutation.isError,
|
||||
error: signInMutation.error,
|
||||
data: signInMutation.data,
|
||||
|
||||
signOut: signOutMutation.mutate,
|
||||
};
|
||||
};
|
||||
@@ -2,7 +2,7 @@ import FrontCameraOverviewCard from "../components/FrontCameraOverview/FrontCame
|
||||
import RearCameraOverviewCard from "../components/RearCameraOverview/RearCameraOverviewCard";
|
||||
|
||||
import SightingHistoryWidget from "../components/SightingsWidget/SightingWidget";
|
||||
import { SightingFeedProvider } from "../context/SightingFeedContext";
|
||||
import { SightingFeedProvider } from "../context/providers/SightingFeedProvider";
|
||||
|
||||
const Dashboard = () => {
|
||||
return (
|
||||
|
||||
@@ -3,6 +3,7 @@ import "react-tabs/style/react-tabs.css";
|
||||
import NPEDCard from "../components/SettingForms/NPED/NPEDCard";
|
||||
import SettingForms from "../components/SettingForms/SettingForms/SettingForms";
|
||||
import NPEDHotlistCard from "../components/SettingForms/NPED/NPEDHotlistCard";
|
||||
import { Toaster } from "sonner";
|
||||
|
||||
const SystemSettings = () => {
|
||||
return (
|
||||
@@ -24,6 +25,7 @@ const SystemSettings = () => {
|
||||
</div>
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
<Toaster />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -59,9 +59,11 @@ export type InitialValuesForm = {
|
||||
};
|
||||
|
||||
export type NPEDFieldType = {
|
||||
frontId: string;
|
||||
username: string;
|
||||
password: string;
|
||||
clientId: string;
|
||||
rearId: string;
|
||||
};
|
||||
|
||||
export type HotlistUploadType = {
|
||||
@@ -97,3 +99,15 @@ export type SightingWidgetType = {
|
||||
overviewPlateRect?: [number, number, number, number]; // [x,y,w,h] normalized 0..1
|
||||
plateTrack?: [number, number, number, number][]; // list of rects normalized 0..1
|
||||
};
|
||||
|
||||
export type NPEDUser = {
|
||||
username: string;
|
||||
clientId: string;
|
||||
password?: string;
|
||||
};
|
||||
|
||||
export type NPEDErrorValues = {
|
||||
username?: string | undefined;
|
||||
password?: string | undefined;
|
||||
clientId?: string | undefined;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user