From 75a47966c3a4db30b7cf03a34cdff30a9c4790e5 Mon Sep 17 00:00:00 2001 From: Saikiran Patil <84263946+saikiranpatil@users.noreply.github.com> Date: Tue, 10 Dec 2024 19:29:53 +0530 Subject: [PATCH 01/14] prefilled middleware prefilled middleware displayed as message in asset congiguration, added new attributes `message` and `messageClass` to `FormFeild` component` --- .../Assets/AssetType/HL7Monitor.tsx | 17 ++++---------- src/components/CameraFeed/ConfigureCamera.tsx | 22 +++++++----------- src/components/Form/FormFields/FormField.tsx | 23 +++++++++++++++++++ src/components/Form/FormFields/Utils.ts | 2 ++ 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/components/Assets/AssetType/HL7Monitor.tsx b/src/components/Assets/AssetType/HL7Monitor.tsx index 76d761383bd..c53ae325111 100644 --- a/src/components/Assets/AssetType/HL7Monitor.tsx +++ b/src/components/Assets/AssetType/HL7Monitor.tsx @@ -100,20 +100,13 @@ const HL7Monitor = (props: HL7MonitorProps) => { label={

Middleware Hostname

- {resolvedMiddleware?.source != "asset" && ( -
- - - Middleware hostname sourced from asset{" "} - {resolvedMiddleware?.source} - -
- )}
} + message={ + resolvedMiddleware?.source != "asset" + ? `Middleware hostname sourced from asset ${resolvedMiddleware?.source}` + : undefined + } placeholder={resolvedMiddleware?.hostname} value={middlewareHostname} onChange={(e) => setMiddlewareHostname(e.value)} diff --git a/src/components/CameraFeed/ConfigureCamera.tsx b/src/components/CameraFeed/ConfigureCamera.tsx index eec453053d6..3a80a585b16 100644 --- a/src/components/CameraFeed/ConfigureCamera.tsx +++ b/src/components/CameraFeed/ConfigureCamera.tsx @@ -149,22 +149,16 @@ export default function ConfigureCamera(props: Props) { label={

{t("middleware_hostname")}

- {!!props.asset.resolved_middleware && - props.asset.resolved_middleware.source != "asset" && ( -
- - - {t("middleware_hostname_sourced_from", { - source: props.asset.resolved_middleware?.source, - })} - -
- )}
} + message={ + !!props.asset.resolved_middleware && + props.asset.resolved_middleware.source != "asset" + ? t("middleware_hostname_sourced_from", { + source: props.asset.resolved_middleware?.source, + }) + : undefined + } placeholder={ props.asset.resolved_middleware?.hostname ?? t("middleware_hostname_example") diff --git a/src/components/Form/FormFields/FormField.tsx b/src/components/Form/FormFields/FormField.tsx index 7f9c2699d64..d5533877fbc 100644 --- a/src/components/Form/FormFields/FormField.tsx +++ b/src/components/Form/FormFields/FormField.tsx @@ -48,6 +48,25 @@ export const FieldErrorText = (props: ErrorProps) => { ); }; +type MessageProps = { + message: string | undefined; + className?: string | undefined; +}; + +export const FieldMessageText = (props: MessageProps) => { + return ( + + {props.message} + + ); +}; + const FormField = ({ field, ...props @@ -73,6 +92,10 @@ const FormField = ({
{props.children}
+ ); }; diff --git a/src/components/Form/FormFields/Utils.ts b/src/components/Form/FormFields/Utils.ts index 1e88bcbd6a0..b017f7b92ee 100644 --- a/src/components/Form/FormFields/Utils.ts +++ b/src/components/Form/FormFields/Utils.ts @@ -20,9 +20,11 @@ export type FieldChangeEventHandler = (event: FieldChangeEvent) => void; export type FormFieldBaseProps = { label?: React.ReactNode; labelSuffix?: React.ReactNode; + message?: string; disabled?: boolean; className?: string; required?: boolean; + messageClassName?: string; labelClassName?: string; errorClassName?: string; name: string; From 4effcf0e9e0649fa410528d21a1239849b126c73 Mon Sep 17 00:00:00 2001 From: Saikiran Patil <84263946+saikiranpatil@users.noreply.github.com> Date: Tue, 10 Dec 2024 20:05:31 +0530 Subject: [PATCH 02/14] add fine message text in create location form --- src/components/Facility/AddLocationForm.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Facility/AddLocationForm.tsx b/src/components/Facility/AddLocationForm.tsx index bb0039285f5..ff4bec38382 100644 --- a/src/components/Facility/AddLocationForm.tsx +++ b/src/components/Facility/AddLocationForm.tsx @@ -209,6 +209,7 @@ export const AddLocationForm = ({ facilityId, locationId }: Props) => { name="Location Middleware Address" type="text" label="Location Middleware Address" + message="Leave blank to apply facility middleware to assets" value={middlewareAddress} onChange={(e) => setMiddlewareAddress(e.value)} error={errors.middlewareAddress} From 3c9172071436bc98d07a3721997c2a64cd45b0eb Mon Sep 17 00:00:00 2001 From: Saikiran Patil <84263946+saikiranpatil@users.noreply.github.com> Date: Tue, 10 Dec 2024 20:27:37 +0530 Subject: [PATCH 03/14] Add fine text facility confiuration page add fine default behaviour text in facility configuration page --- src/components/Facility/FacilityConfigure.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Facility/FacilityConfigure.tsx b/src/components/Facility/FacilityConfigure.tsx index 1cb7c7d69cf..a4b9bc5756b 100644 --- a/src/components/Facility/FacilityConfigure.tsx +++ b/src/components/Facility/FacilityConfigure.tsx @@ -150,6 +150,7 @@ export const FacilityConfigure = (props: IProps) => { Date: Tue, 10 Dec 2024 20:55:31 +0530 Subject: [PATCH 04/14] corrected mispelled address word --- src/components/Facility/FacilityConfigure.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Facility/FacilityConfigure.tsx b/src/components/Facility/FacilityConfigure.tsx index a4b9bc5756b..315181a3ab0 100644 --- a/src/components/Facility/FacilityConfigure.tsx +++ b/src/components/Facility/FacilityConfigure.tsx @@ -150,7 +150,7 @@ export const FacilityConfigure = (props: IProps) => { Date: Wed, 11 Dec 2024 00:10:14 +0530 Subject: [PATCH 05/14] added resolved middleware text at location management --- .../Facility/LocationManagement.tsx | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/components/Facility/LocationManagement.tsx b/src/components/Facility/LocationManagement.tsx index 2f80a879651..ab6865ebf88 100644 --- a/src/components/Facility/LocationManagement.tsx +++ b/src/components/Facility/LocationManagement.tsx @@ -27,6 +27,7 @@ interface Props { interface LocationProps extends LocationModel { facilityId: string; disabled: boolean; + facilityMiddleware?: string; setShowDeletePopup: (e: { open: boolean; name: string; id: string }) => void; } @@ -42,6 +43,18 @@ export default function LocationManagement({ facilityId }: Props) { name: "", id: "", }); + const [facilityMiddleware, setFacilityMiddleware] = useState< + string | undefined + >(undefined); + + useQuery(routes.getPermittedFacility, { + pathParams: { id: facilityId }, + onResponse: (res) => { + if (res.data) { + setFacilityMiddleware(res.data?.middleware_address); + } + }, + }); const closeDeleteFailModal = () => { setShowDeleteFailModal({ ...showDeleteFailModal, open: false }); @@ -121,6 +134,7 @@ export default function LocationManagement({ facilityId }: Props) { {description || "-"}

-

+ Middleware Address: -

+ + {!middleware_address && facilityMiddleware && ( + + Fetched from facility + + )}

- {middleware_address || "-"} + {middleware_address || facilityMiddleware || "-"}

Date: Wed, 11 Dec 2024 00:37:59 +0530 Subject: [PATCH 06/14] Fixed typo in CSS class name --- src/components/Facility/LocationManagement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Facility/LocationManagement.tsx b/src/components/Facility/LocationManagement.tsx index ab6865ebf88..4ff0f3a1655 100644 --- a/src/components/Facility/LocationManagement.tsx +++ b/src/components/Facility/LocationManagement.tsx @@ -287,7 +287,7 @@ const Location = ({ Middleware Address: {!middleware_address && facilityMiddleware && ( - + Fetched from facility )} From 6fa2c556a798866897073059c1c0da812d8beb35 Mon Sep 17 00:00:00 2001 From: Saikiran Patil <84263946+saikiranpatil@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:32:52 +0530 Subject: [PATCH 07/14] added (fetched from facility) badge in location management page --- src/components/Facility/LocationManagement.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Facility/LocationManagement.tsx b/src/components/Facility/LocationManagement.tsx index 4ff0f3a1655..954badd8e05 100644 --- a/src/components/Facility/LocationManagement.tsx +++ b/src/components/Facility/LocationManagement.tsx @@ -287,8 +287,10 @@ const Location = ({ Middleware Address: {!middleware_address && facilityMiddleware && ( - - Fetched from facility + + + Fetched from facility + )}

Date: Thu, 12 Dec 2024 12:52:40 +0530 Subject: [PATCH 08/14] used shadcn label component in formfeild, changed color of message to gray --- src/components/Form/FormFields/FormField.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/Form/FormFields/FormField.tsx b/src/components/Form/FormFields/FormField.tsx index d5533877fbc..1a90f059586 100644 --- a/src/components/Form/FormFields/FormField.tsx +++ b/src/components/Form/FormFields/FormField.tsx @@ -1,3 +1,5 @@ +import { Label } from "@radix-ui/react-label"; + import { FieldError } from "@/components/Form/FieldValidators"; import { FormFieldBaseProps } from "@/components/Form/FormFields/Utils"; @@ -14,7 +16,7 @@ type LabelProps = { export const FieldLabel = (props: LabelProps) => { return ( - + ); }; @@ -57,8 +59,8 @@ export const FieldMessageText = (props: MessageProps) => { return ( From d7e4a45a802d52aa18b3d68fcd45ea3afb62d7d2 Mon Sep 17 00:00:00 2001 From: Saikiran Patil <84263946+saikiranpatil@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:00:29 +0530 Subject: [PATCH 09/14] minor changes --- src/components/Form/FormFields/FormField.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Form/FormFields/FormField.tsx b/src/components/Form/FormFields/FormField.tsx index 1a90f059586..7013f7caa37 100644 --- a/src/components/Form/FormFields/FormField.tsx +++ b/src/components/Form/FormFields/FormField.tsx @@ -1,4 +1,4 @@ -import { Label } from "@radix-ui/react-label"; +import { Label } from "@/components/ui/label"; import { FieldError } from "@/components/Form/FieldValidators"; import { FormFieldBaseProps } from "@/components/Form/FormFields/Utils"; From be5285de04928340dfff978e4974216eaadb2bca Mon Sep 17 00:00:00 2001 From: Saikiran Patil <84263946+saikiranpatil@users.noreply.github.com> Date: Sun, 15 Dec 2024 19:27:11 +0530 Subject: [PATCH 10/14] used shadcn badge in location management page --- .../Facility/LocationManagement.tsx | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/components/Facility/LocationManagement.tsx b/src/components/Facility/LocationManagement.tsx index 954badd8e05..674a922dbe0 100644 --- a/src/components/Facility/LocationManagement.tsx +++ b/src/components/Facility/LocationManagement.tsx @@ -20,6 +20,8 @@ import routes from "@/Utils/request/api"; import request from "@/Utils/request/request"; import useQuery from "@/Utils/request/useQuery"; +import { Badge } from "../ui/badge"; + interface Props { facilityId: string; } @@ -267,14 +269,9 @@ const Location = ({ > {name}

-
-

- {location_type} -

-
+ + {location_type} +

{!middleware_address && facilityMiddleware && ( - - - Fetched from facility - - + + Fetched from facility + )}

Date: Mon, 16 Dec 2024 13:37:39 +0530 Subject: [PATCH 11/14] updated useQuery --- src/components/Facility/LocationManagement.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Facility/LocationManagement.tsx b/src/components/Facility/LocationManagement.tsx index 4e6e77c0a3f..1fe0551b4d3 100644 --- a/src/components/Facility/LocationManagement.tsx +++ b/src/components/Facility/LocationManagement.tsx @@ -4,6 +4,8 @@ import RecordMeta from "@/CAREUI/display/RecordMeta"; import CareIcon from "@/CAREUI/icons/CareIcon"; import PaginatedList from "@/CAREUI/misc/PaginatedList"; +import { Badge } from "@/components/ui/badge"; + import ButtonV2, { Cancel } from "@/components/Common/ButtonV2"; import ConfirmDialog from "@/components/Common/ConfirmDialog"; import DialogModal from "@/components/Common/Dialog"; @@ -20,8 +22,6 @@ import routes from "@/Utils/request/api"; import request from "@/Utils/request/request"; import useTanStackQueryInstead from "@/Utils/request/useQuery"; -import { Badge } from "../ui/badge"; - interface Props { facilityId: string; } @@ -49,7 +49,7 @@ export default function LocationManagement({ facilityId }: Props) { string | undefined >(undefined); - useQuery(routes.getPermittedFacility, { + useTanStackQueryInstead(routes.getPermittedFacility, { pathParams: { id: facilityId }, onResponse: (res) => { if (res.data) { From b9b0ed0ca7c1089884d2ae32b19ed72c01c100b6 Mon Sep 17 00:00:00 2001 From: Saikiran Patil <84263946+saikiranpatil@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:39:55 +0530 Subject: [PATCH 12/14] changed useTanstackuseQuery to useQuery --- .../Facility/LocationManagement.tsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/components/Facility/LocationManagement.tsx b/src/components/Facility/LocationManagement.tsx index 1fe0551b4d3..e9c1323142f 100644 --- a/src/components/Facility/LocationManagement.tsx +++ b/src/components/Facility/LocationManagement.tsx @@ -1,3 +1,4 @@ +import { useQuery } from "@tanstack/react-query"; import { useState } from "react"; import RecordMeta from "@/CAREUI/display/RecordMeta"; @@ -19,6 +20,7 @@ import useAuthUser from "@/hooks/useAuthUser"; import AuthorizeFor, { NonReadOnlyUsers } from "@/Utils/AuthorizeFor"; import * as Notification from "@/Utils/Notifications"; import routes from "@/Utils/request/api"; +import query from "@/Utils/request/query"; import request from "@/Utils/request/request"; import useTanStackQueryInstead from "@/Utils/request/useQuery"; @@ -45,19 +47,16 @@ export default function LocationManagement({ facilityId }: Props) { name: "", id: "", }); - const [facilityMiddleware, setFacilityMiddleware] = useState< - string | undefined - >(undefined); - useTanStackQueryInstead(routes.getPermittedFacility, { - pathParams: { id: facilityId }, - onResponse: (res) => { - if (res.data) { - setFacilityMiddleware(res.data?.middleware_address); - } - }, + const { data } = useQuery({ + queryKey: [routes.getPermittedFacility.path, facilityId], + queryFn: query(routes.getPermittedFacility, { + pathParams: { id: facilityId }, + }), }); + const facilityMiddleware = data?.middleware_address; + const closeDeleteFailModal = () => { setShowDeleteFailModal({ ...showDeleteFailModal, open: false }); }; From c28d3a08d15ee1b4131f51aa9bbab0cd139340ef Mon Sep 17 00:00:00 2001 From: Saikiran Patil <84263946+saikiranpatil@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:52:22 +0530 Subject: [PATCH 13/14] used react forms in facility configuration page --- package-lock.json | 202 ++++++++++++++++-- package.json | 8 +- src/components/Facility/FacilityConfigure.tsx | 191 ++++++++--------- src/components/ui/form.tsx | 184 ++++++++++++++++ 4 files changed, 462 insertions(+), 123 deletions(-) create mode 100644 src/components/ui/form.tsx diff --git a/package-lock.json b/package-lock.json index 94a84d3d179..d3057b01be0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,15 +17,16 @@ "@googlemaps/typescript-guards": "^2.0.3", "@headlessui/react": "^2.2.0", "@hello-pangea/dnd": "^17.0.0", + "@hookform/resolvers": "^3.9.1", "@pnotify/core": "^5.2.0", "@pnotify/mobile": "^5.2.0", "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-icons": "^1.3.2", - "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-popover": "^1.1.2", "@radix-ui/react-scroll-area": "^1.2.0", - "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-toast": "^1.2.2", "@radix-ui/react-tooltip": "^1.1.4", "@sentry/browser": "^8.42.0", @@ -56,6 +57,7 @@ "react-copy-to-clipboard": "^5.1.0", "react-dom": "18.3.1", "react-google-recaptcha": "^3.1.0", + "react-hook-form": "^7.54.1", "react-i18next": "^15.1.3", "react-infinite-scroll-component": "^6.1.0", "react-pdf": "^9.1.1", @@ -113,7 +115,7 @@ "vite-plugin-checker": "^0.8.0", "vite-plugin-pwa": "^0.20.5", "vite-plugin-static-copy": "^2.0.0", - "zod": "^3.23.8" + "zod": "^3.24.1" }, "engines": { "node": ">=22.11.0" @@ -2646,6 +2648,15 @@ "react-dom": "^18.0.0" } }, + "node_modules/@hookform/resolvers": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.1.tgz", + "integrity": "sha512-ud2HqmGBM0P0IABqoskKWI6PEf6ZDDBZkFqe2Vnl+mTHCEHzr3ISjjZyCwTjC/qpL25JC9aIDkloQejvMeq0ug==", + "license": "MIT", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -3687,6 +3698,24 @@ } } }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", @@ -3752,6 +3781,24 @@ } } }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-direction": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", @@ -3891,11 +3938,35 @@ } }, "node_modules/@radix-ui/react-label": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.0.tgz", - "integrity": "sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.1.tgz", + "integrity": "sha512-UUw5E4e/2+4kFMH7+YxORXGWggtY6sM8WIwh5RZchhLuUg2H1hc98Py+pr8HMz6rdaYrK2t296ZEjYLOCO5uUw==", + "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.0" + "@radix-ui/react-primitive": "2.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -3952,6 +4023,24 @@ } } }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popover": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.2.tgz", @@ -3988,6 +4077,24 @@ } } }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popper": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", @@ -4106,6 +4213,24 @@ } } }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz", @@ -4183,12 +4308,12 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", - "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", + "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0" + "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -4200,6 +4325,21 @@ } } }, + "node_modules/@radix-ui/react-slot/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-toast": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.2.tgz", @@ -4268,6 +4408,24 @@ } } }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", @@ -15776,6 +15934,22 @@ "react": ">=16.4.1" } }, + "node_modules/react-hook-form": { + "version": "7.54.1", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.1.tgz", + "integrity": "sha512-PUNzFwQeQ5oHiiTUO7GO/EJXGEtuun2Y1A59rLnZBBj+vNEOWt/3ERTiG1/zt7dVeJEM+4vDX/7XQ/qanuvPMg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-i18next": { "version": "15.1.3", "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.1.3.tgz", @@ -20931,9 +21105,9 @@ } }, "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", "dev": true, "license": "MIT", "funding": { diff --git a/package.json b/package.json index e5eaf3342c9..8edf7842d55 100644 --- a/package.json +++ b/package.json @@ -56,15 +56,16 @@ "@googlemaps/typescript-guards": "^2.0.3", "@headlessui/react": "^2.2.0", "@hello-pangea/dnd": "^17.0.0", + "@hookform/resolvers": "^3.9.1", "@pnotify/core": "^5.2.0", "@pnotify/mobile": "^5.2.0", "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-icons": "^1.3.2", - "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-popover": "^1.1.2", "@radix-ui/react-scroll-area": "^1.2.0", - "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-toast": "^1.2.2", "@radix-ui/react-tooltip": "^1.1.4", "@sentry/browser": "^8.42.0", @@ -95,6 +96,7 @@ "react-copy-to-clipboard": "^5.1.0", "react-dom": "18.3.1", "react-google-recaptcha": "^3.1.0", + "react-hook-form": "^7.54.1", "react-i18next": "^15.1.3", "react-infinite-scroll-component": "^6.1.0", "react-pdf": "^9.1.1", @@ -152,7 +154,7 @@ "vite-plugin-checker": "^0.8.0", "vite-plugin-pwa": "^0.20.5", "vite-plugin-static-copy": "^2.0.0", - "zod": "^3.23.8" + "zod": "^3.24.1" }, "browserslist": { "production": [ diff --git a/src/components/Facility/FacilityConfigure.tsx b/src/components/Facility/FacilityConfigure.tsx index 5dc938df99b..501b6959fd3 100644 --- a/src/components/Facility/FacilityConfigure.tsx +++ b/src/components/Facility/FacilityConfigure.tsx @@ -1,109 +1,72 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { useQuery } from "@tanstack/react-query"; import { t } from "i18next"; import { navigate } from "raviger"; -import { useReducer, useState } from "react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; + +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; -import { Submit } from "@/components/Common/ButtonV2"; import Loading from "@/components/Common/Loading"; import Page from "@/components/Common/Page"; -import TextFormField from "@/components/Form/FormFields/TextFormField"; -import { FieldChangeEvent } from "@/components/Form/FormFields/Utils"; import { PLUGIN_Component } from "@/PluginEngine"; import * as Notification from "@/Utils/Notifications"; import routes from "@/Utils/request/api"; +import query from "@/Utils/request/query"; import request from "@/Utils/request/request"; -import useTanStackQueryInstead from "@/Utils/request/useQuery"; - -const initForm = { - name: "", - state: 0, - district: 0, - localbody: 0, - ward: 0, - middleware_address: "", -}; -const initialState = { - form: { ...initForm }, - errors: {}, -}; - -const FormReducer = (state = initialState, action: any) => { - switch (action.type) { - case "set_form": { - return { - ...state, - form: action.form, - }; - } - case "set_error": { - return { - ...state, - errors: action.errors, - }; - } - default: - return state; - } -}; +import { Submit } from "../Common/ButtonV2"; interface IProps { facilityId: string; } +const formSchema = z.object({ + middleware_address: z + .string() + .nonempty({ message: "Middleware Address is required" }) + .regex(/^(?!https?:\/\/)[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)*\.[a-zA-Z]{2,}$/, { + message: "Invalid Middleware Address", + }), +}); + export const FacilityConfigure = (props: IProps) => { - const [state, dispatch] = useReducer(FormReducer, initialState); const { facilityId } = props; const [isLoading, setIsLoading] = useState(false); - const { loading } = useTanStackQueryInstead(routes.getPermittedFacility, { - pathParams: { id: facilityId }, - onResponse: (res) => { - if (res.data) { - const formData = { - name: res.data.name, - state: res.data.state, - district: res.data.district, - local_body: res.data.local_body, - ward: res.data.ward, - middleware_address: res.data.middleware_address, - }; - dispatch({ type: "set_form", form: formData }); - } - }, + const { isPending: loading, data } = useQuery({ + queryKey: [routes.getPermittedFacility.path, facilityId], + queryFn: query(routes.getPermittedFacility, { + pathParams: { id: facilityId }, + }), }); - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setIsLoading(true); - if (!state.form.middleware_address) { - dispatch({ - type: "set_error", - errors: { middleware_address: ["Middleware Address is required"] }, - }); - setIsLoading(false); - return; - } - if ( - state.form.middleware_address.match( - /^(?!https?:\/\/)[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)*\.[a-zA-Z]{2,}$/, - ) === null - ) { - dispatch({ - type: "set_error", - errors: { - middleware_address: ["Invalid Middleware Address"], - }, - }); - setIsLoading(false); - return; - } + const onSubmit = async (values: z.infer) => { + if (!data) return; - const data = { - ...state.form, - middleware_address: state.form.middleware_address, + const formData = { + name: data.name, + state: data.state, + district: data.district, + local_body: data.local_body, + ward: data.ward, + middleware_address: values.middleware_address, }; + setIsLoading(true); + console.log(formData); + const { res, error } = await request(routes.partialUpdateFacility, { pathParams: { id: facilityId }, body: data, @@ -123,14 +86,18 @@ export const FacilityConfigure = (props: IProps) => { setIsLoading(false); }; - const handleChange = (e: FieldChangeEvent) => { - dispatch({ - type: "set_form", - form: { ...state.form, [e.name]: e.value }, - }); - }; + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { middleware_address: "" }, + }); + + useEffect(() => { + if (data && data.middleware_address) { + form.setValue("middleware_address", data.middleware_address); + } + }, [form, data]); - if (isLoading || loading) { + if (isLoading || !data || loading) { return ; } @@ -138,30 +105,42 @@ export const FacilityConfigure = (props: IProps) => {

-
-
-
- + + + ( + + + Facility Middleware Address + * + + + + + + This address will be applied to all assets when asset and + location middleware are Unspecified. + + + + )} + /> +
+
-
-
- -
- + +
= FieldPath, +> = { + name: TName; +}; + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue, +); + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ); +}; + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error("useFormField should be used within "); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; +}; + +type FormItemContextValue = { + id: string; +}; + +const FormItemContext = React.createContext( + {} as FormItemContextValue, +); + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId(); + + return ( + +
+ + ); +}); +FormItem.displayName = "FormItem"; + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField(); + + return ( +