diff --git a/.env b/.env index b3bf713..4ea0dc8 100644 --- a/.env +++ b/.env @@ -1,2 +1,3 @@ VITE_BASEURL=http://192.168.75.11/ -VITE_TESTURL=http://100.82.205.44/SightingListRear/sightingSummary?mostRecentRef=-1 \ No newline at end of file +VITE_TESTURL=http://100.82.205.44/SightingListRear/sightingSummary?mostRecentRef=-1 +VITE_OUTSIDE_BASEURL=http://100.82.205.44/api \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 56d0bd1..5ec98d7 100644 --- a/src/App.tsx +++ b/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 ( - - }> - } /> - } /> - } /> - } /> - - + + + }> + } /> + } /> + } /> + } /> + + + ); } diff --git a/src/components/SettingForms/NPED/NPEDCard.tsx b/src/components/SettingForms/NPED/NPEDCard.tsx index e7f222f..c60ddba 100644 --- a/src/components/SettingForms/NPED/NPEDCard.tsx +++ b/src/components/SettingForms/NPED/NPEDCard.tsx @@ -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 ( diff --git a/src/components/SettingForms/NPED/NPEDFields.tsx b/src/components/SettingForms/NPED/NPEDFields.tsx index ddc6cdb..e8a1293 100644 --- a/src/components/SettingForms/NPED/NPEDFields.tsx +++ b/src/components/SettingForms/NPED/NPEDFields.tsx @@ -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 ( - -
- - - - - - - - - - - - - -
+ + {({ errors, touched }) => ( +
+ + + {touched.username && errors.username && ( + + {errors.username} + + )} + + + + + {touched.password && errors.password && ( + + {errors.password} + + )} + + + + + {touched.clientId && errors.clientId && ( + + {errors.clientId} + + )} + + + +
+ )}
); }; diff --git a/src/components/SettingForms/components/FormGroup.tsx b/src/components/SettingForms/components/FormGroup.tsx index 081fc7b..907c221 100644 --- a/src/components/SettingForms/components/FormGroup.tsx +++ b/src/components/SettingForms/components/FormGroup.tsx @@ -6,7 +6,7 @@ type FormGroupProps = { const FormGroup = ({ children }: FormGroupProps) => { return ( -
+
{children}
); diff --git a/src/components/UI/Header.tsx b/src/components/UI/Header.tsx index 204b671..fd84343 100644 --- a/src/components/UI/Header.tsx +++ b/src/components/UI/Header.tsx @@ -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 ( -
+
Logo
+ + +
); }; diff --git a/src/context/NPEDUserContext.tsx b/src/context/NPEDUserContext.tsx new file mode 100644 index 0000000..1acce93 --- /dev/null +++ b/src/context/NPEDUserContext.tsx @@ -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>; +}; + +export const NPEDUserContext = createContext( + undefined +); +export const useNPEDContext = () => { + const ctx = useContext(NPEDUserContext); + if (!ctx) + throw new Error("useNPEDContext must be used within "); + return ctx; +}; diff --git a/src/context/SightingFeedContext.tsx b/src/context/SightingFeedContext.tsx index 7f4a1fa..69605c6 100644 --- a/src/context/SightingFeedContext.tsx +++ b/src/context/SightingFeedContext.tsx @@ -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( - undefined -); - -export const SightingFeedProvider = ({ - children, - url, - side, -}: SightingFeedProviderProps) => { - const { - sightings, - selectedRef, - setSelectedRef, - effectiveSelected, - mostRecent, - isPending, - noSighting, - } = useSightingFeed(url); - - return ( - - {children} - - ); -}; - -// eslint-disable-next-line react-refresh/only-export-components export const useSightingFeedContext = () => { const ctx = useContext(SightingFeedContext); if (!ctx) diff --git a/src/context/providers/NPEDUserContextProvider.tsx b/src/context/providers/NPEDUserContextProvider.tsx new file mode 100644 index 0000000..b634c57 --- /dev/null +++ b/src/context/providers/NPEDUserContextProvider.tsx @@ -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(null); + + return ( + + {children} + + ); +}; diff --git a/src/context/providers/SightingFeedProvider.tsx b/src/context/providers/SightingFeedProvider.tsx new file mode 100644 index 0000000..a0708b1 --- /dev/null +++ b/src/context/providers/SightingFeedProvider.tsx @@ -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 ( + + {children} + + ); +}; diff --git a/src/hooks/useNPEDAuh.ts b/src/hooks/useNPEDAuh.ts deleted file mode 100644 index 258f90d..0000000 --- a/src/hooks/useNPEDAuh.ts +++ /dev/null @@ -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, - }; -}; diff --git a/src/hooks/useNPEDAuth.ts b/src/hooks/useNPEDAuth.ts new file mode 100644 index 0000000..fb93de6 --- /dev/null +++ b/src/hooks/useNPEDAuth.ts @@ -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, + }; +}; diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index ad38ba5..4ad4f56 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -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 ( diff --git a/src/pages/SystemSettings.tsx b/src/pages/SystemSettings.tsx index 6603d64..11ca42b 100644 --- a/src/pages/SystemSettings.tsx +++ b/src/pages/SystemSettings.tsx @@ -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 = () => {
+
); }; diff --git a/src/types/types.ts b/src/types/types.ts index 64b3e77..3ecc360 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -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; +}; diff --git a/vite.config.ts b/vite.config.ts index 4ff4f8f..672fe08 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -5,4 +5,5 @@ import tailwindcss from "@tailwindcss/vite"; // https://vite.dev/config/ export default defineConfig({ plugins: [react(), tailwindcss()], + server: { host: true }, });