From b7b861a451ff47283e2f828143e2a65290bb6cde Mon Sep 17 00:00:00 2001 From: AdityaJ2305 Date: Tue, 25 Feb 2025 09:13:09 +0530 Subject: [PATCH 01/23] bulk beds creation --- public/locale/en.json | 2 + .../settings/locations/LocationForm.tsx | 171 +++++++++++++----- 2 files changed, 130 insertions(+), 43 deletions(-) diff --git a/public/locale/en.json b/public/locale/en.json index 1418c94c8ec..7594e420f64 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -458,6 +458,7 @@ "basic_info": "Basic Information", "basic_information": "Basic Information", "bed_capacity": "Bed Capacity", + "bed_count_validation_error": "Number of beds must be greater than 0", "bed_created_notification_one": "{{count}} Bed created successfully", "bed_created_notification_other": "{{count}} Beds created successfully", "bed_history": "Bed History", @@ -2190,6 +2191,7 @@ "tomorrow": "Tomorrow", "total_amount": "Total Amount", "total_beds": "Total Beds", + "total_number_of_beds": "Total number of beds", "total_patients": "Total Patients", "total_slots": "Total Slots", "total_staff": "Total Staff", diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index 363df562bf1..4d4ff1613fb 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -25,6 +25,7 @@ import { } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; +import routes from "@/Utils/request/api"; import mutate from "@/Utils/request/mutate"; import query from "@/Utils/request/query"; import { @@ -34,35 +35,10 @@ import { locationFormOptions, } from "@/types/location/location"; import locationApi from "@/types/location/locationApi"; - -const formSchema = z.object({ - name: z.string().min(1, { message: "Name is required" }), - description: z.string().optional(), - status: z.enum(["active", "inactive", "unknown"] as const), - operational_status: z.enum(["C", "H", "O", "U", "K", "I"] as const), - form: z.enum([ - "si", - "bu", - "wi", - "wa", - "lvl", - "co", - "ro", - "bd", - "ve", - "ho", - "ca", - "rd", - "area", - "jdn", - "vi", - ] as const), - parent: z.string().optional().nullable(), - organizations: z.array(z.string()).default([]), - availability_status: z.enum(["available", "unavailable"] as const), -}); - -type FormValues = z.infer; +import { + BatchRequestBody, + BatchSubmissionResult, +} from "@/types/questionnaire/batch"; interface Props { facilityId: string; @@ -71,17 +47,6 @@ interface Props { parentId?: string; } -const defaultValues: FormValues = { - name: "", - description: "", - status: "active", - operational_status: "O", - form: "ro", - parent: null, - organizations: [], - availability_status: "available", -}; - export default function LocationForm({ facilityId, onSuccess, @@ -98,6 +63,53 @@ export default function LocationForm({ }), enabled: !!locationId, }); + const formSchema = z.object({ + name: z.string().min(1, { message: "Name is required" }), + description: z.string().optional(), + status: z.enum(["active", "inactive", "unknown"] as const), + operational_status: z.enum(["C", "H", "O", "U", "K", "I"] as const), + form: z.enum([ + "si", + "bu", + "wi", + "wa", + "lvl", + "co", + "ro", + "bd", + "ve", + "ho", + "ca", + "rd", + "area", + "jdn", + "vi", + ] as const), + parent: z.string().optional().nullable(), + + beds_count: z + .string() + + .refine((val) => val === undefined || Number(val) >= 1, { + message: t("bed_count_validation_error"), + }), + organizations: z.array(z.string()).default([]), + availability_status: z.enum(["available", "unavailable"] as const), + }); + + type FormValues = z.infer; + + const defaultValues: FormValues = { + name: "", + description: "", + status: "active", + operational_status: "O", + form: "ro", + beds_count: "1", + parent: null, + organizations: [], + availability_status: "available", + }; const form = useForm({ resolver: zodResolver(formSchema), @@ -140,11 +152,55 @@ export default function LocationForm({ }, }); + const { mutate: submitBatch } = useMutation({ + mutationFn: mutate(routes.batchRequest, { silent: true }), + onSuccess: (data: { results: BatchSubmissionResult[] }) => { + toast.success( + t("bed_created_notification", { count: data.results.length }), + ); + queryClient.invalidateQueries({ queryKey: ["locations"] }); + onSuccess?.(); + }, + + // onError: () => { + // toast.error(t("submission_failed")); + // }, + }); + function onSubmit(values: FormValues) { + if ( + values.form === "bd" && + !location?.id && + Number(values.beds_count) > 1 + ) { + const data: LocationWrite = { + ...values, + mode: "instance", + description: values.description || "", + organizations: values.organizations, + parent: values.parent || undefined, + }; + + const batchRequest: BatchRequestBody = { + requests: Array.from( + { length: Number(values.beds_count) }, + (_, index) => ({ + url: `/api/v1/facility/${facilityId}/location/`, + method: "POST", + reference_id: `Location`, + body: { + ...data, + name: `${values.name} ${index + 1}`, + }, + }), + ), + }; + submitBatch(batchRequest); + return; + } const locationData: LocationWrite = { ...values, - // Mode = instance only for beds - mode: values.form === "bd" ? "instance" : "kind", + mode: "kind", description: values.description || "", organizations: values.organizations, parent: values.parent || undefined, @@ -195,7 +251,22 @@ export default function LocationForm({ )} /> - + {form.watch("form") === "bd" && + form.watch("beds_count") && + !isNaN(Number(form.watch("beds_count"))) && + Number(form.watch("beds_count")) > 1 && + form.watch("name")?.trim() !== "" && ( + + {Array.from( + { length: Number(form.watch("beds_count")) }, + (_, index) => ( + + {form.watch("name")}-{index + 1},{" "} + + ), + )} + + )} + {form.watch("form") === "bd" && ( + ( + + {t("total_number_of_beds")} + + + + )} + /> + )} + Date: Tue, 25 Feb 2025 10:00:59 +0530 Subject: [PATCH 02/23] a few changes --- .../settings/locations/LocationForm.tsx | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index 4d4ff1613fb..0e41102ba8a 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -63,6 +63,9 @@ export default function LocationForm({ }), enabled: !!locationId, }); + + const isEditMode = !!location?.id; + const formSchema = z.object({ name: z.string().min(1, { message: "Name is required" }), description: z.string().optional(), @@ -89,7 +92,7 @@ export default function LocationForm({ beds_count: z .string() - + .optional() .refine((val) => val === undefined || Number(val) >= 1, { message: t("bed_count_validation_error"), }), @@ -143,9 +146,7 @@ export default function LocationForm({ pathParams: { facility_id: facilityId }, }), onSuccess: () => { - toast.success( - location?.id ? t("location_updated") : t("location_created"), - ); + toast.success(isEditMode ? t("location_updated") : t("location_created")); queryClient.invalidateQueries({ queryKey: ["locations"] }); onSuccess?.(); @@ -168,11 +169,7 @@ export default function LocationForm({ }); function onSubmit(values: FormValues) { - if ( - values.form === "bd" && - !location?.id && - Number(values.beds_count) > 1 - ) { + if (values.form === "bd" && !isEditMode && Number(values.beds_count) > 1) { const data: LocationWrite = { ...values, mode: "instance", @@ -311,7 +308,7 @@ export default function LocationForm({ )} /> - {form.watch("form") === "bd" && ( + {form.watch("form") === "bd" && !isEditMode && ( {isPending ? ( - <>{location?.id ? t("updating") : t("creating")} + <>{isEditMode ? t("updating") : t("creating")} ) : ( - <>{location?.id ? t("update") : t("create")} + <>{isEditMode ? t("update") : t("create")} )} From 672f64b82955d2831ad8ad39e0aed0bdfca812a1 Mon Sep 17 00:00:00 2001 From: AdityaJ2305 Date: Tue, 25 Feb 2025 10:23:16 +0530 Subject: [PATCH 03/23] a few changes number type --- .../settings/locations/LocationForm.tsx | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index 0e41102ba8a..f128ae5ab59 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -91,11 +91,9 @@ export default function LocationForm({ parent: z.string().optional().nullable(), beds_count: z - .string() - .optional() - .refine((val) => val === undefined || Number(val) >= 1, { - message: t("bed_count_validation_error"), - }), + .number() + .min(1, t("number_min_error", { min: 1 })) + .optional(), organizations: z.array(z.string()).default([]), availability_status: z.enum(["available", "unavailable"] as const), }); @@ -108,7 +106,7 @@ export default function LocationForm({ status: "active", operational_status: "O", form: "ro", - beds_count: "1", + beds_count: 1, parent: null, organizations: [], availability_status: "available", @@ -169,7 +167,7 @@ export default function LocationForm({ }); function onSubmit(values: FormValues) { - if (values.form === "bd" && !isEditMode && Number(values.beds_count) > 1) { + if (values.form === "bd" && !isEditMode && (values.beds_count ?? 0) > 1) { const data: LocationWrite = { ...values, mode: "instance", @@ -249,16 +247,14 @@ export default function LocationForm({ )} /> {form.watch("form") === "bd" && - form.watch("beds_count") && - !isNaN(Number(form.watch("beds_count"))) && - Number(form.watch("beds_count")) > 1 && + (form.watch("beds_count") ?? 0) > 1 && form.watch("name")?.trim() !== "" && ( {Array.from( { length: Number(form.watch("beds_count")) }, (_, index) => ( - {form.watch("name")}-{index + 1},{" "} + {form.watch("name")}-{index + 1}{" "} ), )} @@ -315,7 +311,12 @@ export default function LocationForm({ render={({ field }) => ( {t("total_number_of_beds")} - + field.onChange(e.target.valueAsNumber)} + /> )} From a132d72d338739a715479d5dec6d3bad42020ebe Mon Sep 17 00:00:00 2001 From: AdityaJ2305 Date: Tue, 25 Feb 2025 23:57:55 +0530 Subject: [PATCH 04/23] move out schema --- .../settings/locations/LocationForm.tsx | 63 +++++++++---------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index 39e1410484b..dae05e0117e 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -1,5 +1,6 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { t } from "i18next"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; @@ -46,7 +47,37 @@ interface Props { locationId?: string; parentId?: string; } +const formSchema = z.object({ + name: z.string().min(1, { message: t("field_required") }), + description: z.string().optional(), + status: z.enum(["active", "inactive", "unknown"] as const), + operational_status: z.enum(["C", "H", "O", "U", "K", "I"] as const), + form: z.enum([ + "si", + "bu", + "wi", + "wa", + "lvl", + "co", + "ro", + "bd", + "ve", + "ho", + "ca", + "rd", + "area", + "jdn", + "vi", + ] as const), + parent: z.string().optional().nullable(), + beds_count: z + .number() + .min(1, t("number_min_error", { min: 1 })) + .optional(), + organizations: z.array(z.string()).default([]), + availability_status: z.enum(["available", "unavailable"] as const), +}); export default function LocationForm({ facilityId, onSuccess, @@ -66,38 +97,6 @@ export default function LocationForm({ const isEditMode = !!location?.id; - const formSchema = z.object({ - name: z.string().min(1, { message: "Name is required" }), - description: z.string().optional(), - status: z.enum(["active", "inactive", "unknown"] as const), - operational_status: z.enum(["C", "H", "O", "U", "K", "I"] as const), - form: z.enum([ - "si", - "bu", - "wi", - "wa", - "lvl", - "co", - "ro", - "bd", - "ve", - "ho", - "ca", - "rd", - "area", - "jdn", - "vi", - ] as const), - parent: z.string().optional().nullable(), - - beds_count: z - .number() - .min(1, t("number_min_error", { min: 1 })) - .optional(), - organizations: z.array(z.string()).default([]), - availability_status: z.enum(["available", "unavailable"] as const), - }); - type FormValues = z.infer; const defaultValues: FormValues = { From 00e1dd6040eb8a20322aa222df70933df6e5c10b Mon Sep 17 00:00:00 2001 From: AdityaJ2305 Date: Wed, 26 Feb 2025 00:03:25 +0530 Subject: [PATCH 05/23] move out schema --- .../settings/locations/LocationForm.tsx | 47 ++++++------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index dae05e0117e..2f79b3c94ee 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -52,25 +52,8 @@ const formSchema = z.object({ description: z.string().optional(), status: z.enum(["active", "inactive", "unknown"] as const), operational_status: z.enum(["C", "H", "O", "U", "K", "I"] as const), - form: z.enum([ - "si", - "bu", - "wi", - "wa", - "lvl", - "co", - "ro", - "bd", - "ve", - "ho", - "ca", - "rd", - "area", - "jdn", - "vi", - ] as const), + form: z.enum(LocationFormOptions), parent: z.string().optional().nullable(), - beds_count: z .number() .min(1, t("number_min_error", { min: 1 })) @@ -78,6 +61,20 @@ const formSchema = z.object({ organizations: z.array(z.string()).default([]), availability_status: z.enum(["available", "unavailable"] as const), }); + +type FormValues = z.infer; + +const defaultValues: FormValues = { + name: "", + description: "", + status: "active", + operational_status: "O", + form: "ro", + beds_count: 1, + parent: null, + organizations: [], + availability_status: "available", +}; export default function LocationForm({ facilityId, onSuccess, @@ -97,20 +94,6 @@ export default function LocationForm({ const isEditMode = !!location?.id; - type FormValues = z.infer; - - const defaultValues: FormValues = { - name: "", - description: "", - status: "active", - operational_status: "O", - form: "ro", - beds_count: 1, - parent: null, - organizations: [], - availability_status: "available", - }; - const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { From ae84991422ec5433d8bfd4b53377658737f4cc82 Mon Sep 17 00:00:00 2001 From: AdityaJ2305 Date: Wed, 26 Feb 2025 00:05:01 +0530 Subject: [PATCH 06/23] unwanted changes --- .../Facility/settings/locations/LocationForm.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index 2f79b3c94ee..69a90bf0ac1 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -41,12 +41,6 @@ import { BatchSubmissionResult, } from "@/types/questionnaire/batch"; -interface Props { - facilityId: string; - onSuccess?: () => void; - locationId?: string; - parentId?: string; -} const formSchema = z.object({ name: z.string().min(1, { message: t("field_required") }), description: z.string().optional(), @@ -63,6 +57,12 @@ const formSchema = z.object({ }); type FormValues = z.infer; +interface Props { + facilityId: string; + onSuccess?: () => void; + locationId?: string; + parentId?: string; +} const defaultValues: FormValues = { name: "", From 537c806acd35ac323de2480221afa7bfab627fe6 Mon Sep 17 00:00:00 2001 From: AdityaJ2305 Date: Wed, 26 Feb 2025 00:13:20 +0530 Subject: [PATCH 07/23] on error notify --- public/locale/en.json | 1 + src/pages/Facility/settings/locations/LocationForm.tsx | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/public/locale/en.json b/public/locale/en.json index 958f2be5e62..e03aedf19d5 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -471,6 +471,7 @@ "bed_count_validation_error": "Number of beds must be greater than 0", "bed_created_notification_one": "{{count}} Bed created successfully", "bed_created_notification_other": "{{count}} Beds created successfully", + "beds_creation_failed":"Failed to create beds", "bed_history": "Bed History", "bed_not_linked_to_camera": "This bed has not been linked to this camera.", "bed_search_placeholder": "Search by beds name", diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index 69a90bf0ac1..ae159398cc2 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -143,9 +143,9 @@ export default function LocationForm({ onSuccess?.(); }, - // onError: () => { - // toast.error(t("submission_failed")); - // }, + onError: () => { + toast.error(t("beds_creation_failed")); + }, }); function onSubmit(values: FormValues) { @@ -163,7 +163,7 @@ export default function LocationForm({ { length: Number(values.beds_count) }, (_, index) => ({ url: `/api/v1/facility/${facilityId}/location/`, - method: "POST", + method: "PST", reference_id: `Location`, body: { ...data, @@ -296,6 +296,7 @@ export default function LocationForm({ field.onChange(e.target.valueAsNumber)} /> From 0eb4f457420d404ec7775dc85364e7bd35854f34 Mon Sep 17 00:00:00 2001 From: AdityaJ2305 Date: Wed, 26 Feb 2025 00:14:28 +0530 Subject: [PATCH 08/23] typo fix --- src/pages/Facility/settings/locations/LocationForm.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index ae159398cc2..7e07c6f5933 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -297,7 +297,6 @@ export default function LocationForm({ {...field} type="number" min={1} - {...field} onChange={(e) => field.onChange(e.target.valueAsNumber)} /> From bbf420681681ccd88a516a65ef4dc6c75c45e661 Mon Sep 17 00:00:00 2001 From: AdityaJ2305 Date: Wed, 26 Feb 2025 00:17:15 +0530 Subject: [PATCH 09/23] typo fix --- src/pages/Facility/settings/locations/LocationForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index 7e07c6f5933..06df1c2f94b 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -163,7 +163,7 @@ export default function LocationForm({ { length: Number(values.beds_count) }, (_, index) => ({ url: `/api/v1/facility/${facilityId}/location/`, - method: "PST", + method: "POST", reference_id: `Location`, body: { ...data, From 74c7579aeb8b0d4632e5ca08152ac92f3b7b34d2 Mon Sep 17 00:00:00 2001 From: AdityaJ2305 Date: Wed, 26 Feb 2025 14:41:53 +0530 Subject: [PATCH 10/23] shared data and rm preview --- .../settings/locations/LocationForm.tsx | 42 +++++-------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index 06df1c2f94b..85c45d8618c 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -149,15 +149,15 @@ export default function LocationForm({ }); function onSubmit(values: FormValues) { - if (values.form === "bd" && !isEditMode && (values.beds_count ?? 0) > 1) { - const data: LocationWrite = { - ...values, - mode: "instance", - description: values.description || "", - organizations: values.organizations, - parent: values.parent || undefined, - }; + const data: LocationWrite = { + ...values, + mode: values.form === "bd" ? "instance" : "kind", + description: values.description || "", + organizations: values.organizations, + parent: values.parent || undefined, + }; + if (values.form === "bd" && !isEditMode && (values.beds_count ?? 0) > 1) { const batchRequest: BatchRequestBody = { requests: Array.from( { length: Number(values.beds_count) }, @@ -175,19 +175,12 @@ export default function LocationForm({ submitBatch(batchRequest); return; } - const locationData: LocationWrite = { - ...values, - mode: "kind", - description: values.description || "", - organizations: values.organizations, - parent: values.parent || undefined, - }; if (location?.id) { - locationData.id = location.id; + data.id = location.id; } - submitForm(locationData); + submitForm(data); } const statusOptions: { value: Status; label: string }[] = [ @@ -228,20 +221,6 @@ export default function LocationForm({ )} /> - {form.watch("form") === "bd" && - (form.watch("beds_count") ?? 0) > 1 && - form.watch("name")?.trim() !== "" && ( - - {Array.from( - { length: Number(form.watch("beds_count")) }, - (_, index) => ( - - {form.watch("name")}-{index + 1}{" "} - - ), - )} - - )} field.onChange(e.target.valueAsNumber)} /> From 3039f72b4f1d7cae910c85f75ea0f42feda779a9 Mon Sep 17 00:00:00 2001 From: AdityaJ2305 Date: Thu, 27 Feb 2025 19:12:21 +0530 Subject: [PATCH 11/23] ref id updated --- src/pages/Facility/settings/locations/LocationForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index 08cb620347c..18b3e5bb76a 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -164,7 +164,7 @@ export default function LocationForm({ (_, index) => ({ url: `/api/v1/facility/${facilityId}/location/`, method: "POST", - reference_id: `Location`, + reference_id: parentId ? `Location ${parentId}` : "Location", body: { ...data, name: `${values.name} ${index + 1}`, From 74a8924a54c91c5de0277d80d8f95bd826cdf099 Mon Sep 17 00:00:00 2001 From: AdityaJ2305 Date: Thu, 27 Feb 2025 19:47:35 +0530 Subject: [PATCH 12/23] multiple input for beds --- .../settings/locations/LocationForm.tsx | 57 ++++++++++++++----- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index 18b3e5bb76a..e3f876d810b 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -1,7 +1,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { t } from "i18next"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; @@ -83,6 +83,14 @@ export default function LocationForm({ }: Props) { const { t } = useTranslation(); const queryClient = useQueryClient(); + const [bedNames, setBedNames] = useState([]); + const updateBedName = (index: number, newName: string) => { + setBedNames((prev) => { + const updated = [...prev]; + updated[index] = newName; + return updated; + }); + }; const { data: location, isLoading } = useQuery({ queryKey: ["location", locationId], @@ -101,6 +109,16 @@ export default function LocationForm({ parent: parentId || null, }, }); + useEffect(() => { + if (form.watch("beds_count")) { + setBedNames( + Array.from( + { length: Number(form.watch("beds_count")) }, + (_, index) => `${form.watch("name")} ${index + 1}`, + ), + ); + } + }, [form.watch("beds_count"), form.watch("name")]); useEffect(() => { if (location) { @@ -158,19 +176,20 @@ export default function LocationForm({ }; if (values.form === "bd" && !isEditMode && (values.beds_count ?? 0) > 1) { + if (bedNames.some((name) => name.trim() === "")) { + toast.error("Any Bed Name can't be empty"); + return; + } const batchRequest: BatchRequestBody = { - requests: Array.from( - { length: Number(values.beds_count) }, - (_, index) => ({ - url: `/api/v1/facility/${facilityId}/location/`, - method: "POST", - reference_id: parentId ? `Location ${parentId}` : "Location", - body: { - ...data, - name: `${values.name} ${index + 1}`, - }, - }), - ), + requests: bedNames.map((name) => ({ + url: `/api/v1/facility/${facilityId}/location/`, + method: "POST", + reference_id: parentId ? `Location ${parentId}` : "Location", + body: { + ...data, + name, + }, + })), }; submitBatch(batchRequest); return; @@ -221,6 +240,18 @@ export default function LocationForm({ )} /> + {form.watch("form") === "bd" && bedNames.length > 1 && ( +
+ {bedNames.map((name, index) => ( + updateBedName(index, e.target.value)} + /> + ))} +
+ )} Date: Thu, 27 Feb 2025 19:56:59 +0530 Subject: [PATCH 13/23] hide inputs on empty name --- .../settings/locations/LocationForm.tsx | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index e3f876d810b..36d2a88f64d 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -240,18 +240,20 @@ export default function LocationForm({ )} /> - {form.watch("form") === "bd" && bedNames.length > 1 && ( -
- {bedNames.map((name, index) => ( - updateBedName(index, e.target.value)} - /> - ))} -
- )} + {form.watch("form") === "bd" && + bedNames.length > 1 && + form.watch("name").trim() !== "" && ( +
+ {bedNames.map((name, index) => ( + updateBedName(index, e.target.value)} + /> + ))} +
+ )} Date: Thu, 27 Feb 2025 20:07:24 +0530 Subject: [PATCH 14/23] name trim space --- src/pages/Facility/settings/locations/LocationForm.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index 36d2a88f64d..19f1a025140 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -42,7 +42,10 @@ import { } from "@/types/questionnaire/batch"; const formSchema = z.object({ - name: z.string().min(1, { message: t("field_required") }), + name: z + .string() + .trim() + .min(1, { message: t("field_required") }), description: z.string().optional(), status: z.enum(["active", "inactive", "unknown"] as const), operational_status: z.enum(["C", "H", "O", "U", "K", "I"] as const), From 53462064b227a0e465b704f78a8720f9bc511583 Mon Sep 17 00:00:00 2001 From: AdityaJ2305 Date: Fri, 28 Feb 2025 17:06:42 +0530 Subject: [PATCH 15/23] add accordion and ux update to support bulk beds creation --- package-lock.json | 32 ++ package.json | 1 + public/locale/en.json | 6 + src/components/ui/accordion.tsx | 58 ++++ .../settings/locations/LocationForm.tsx | 320 ++++++++++++++---- .../settings/locations/LocationSheet.tsx | 2 +- 6 files changed, 343 insertions(+), 76 deletions(-) create mode 100644 src/components/ui/accordion.tsx diff --git a/package-lock.json b/package-lock.json index 89bcdb6f66d..dc2e700a328 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@headlessui/react": "^2.2.0", "@hookform/resolvers": "^4.0.0", "@originjs/vite-plugin-federation": "^1.3.7", + "@radix-ui/react-accordion": "^1.2.3", "@radix-ui/react-alert-dialog": "^1.1.2", "@radix-ui/react-avatar": "^1.1.2", "@radix-ui/react-checkbox": "^1.1.3", @@ -3337,6 +3338,37 @@ "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", "license": "MIT" }, + "node_modules/@radix-ui/react-accordion": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.3.tgz", + "integrity": "sha512-RIQ15mrcvqIkDARJeERSuXSry2N8uYnxkdDetpfmalT/+0ntOXLkFOsh9iwlAsCv+qcmhZjbdJogIm6WBa6c4A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collapsible": "1.1.3", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "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-alert-dialog": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.6.tgz", diff --git a/package.json b/package.json index 5b2047bf801..acaef576930 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "@headlessui/react": "^2.2.0", "@hookform/resolvers": "^4.0.0", "@originjs/vite-plugin-federation": "^1.3.7", + "@radix-ui/react-accordion": "^1.2.3", "@radix-ui/react-alert-dialog": "^1.1.2", "@radix-ui/react-avatar": "^1.1.2", "@radix-ui/react-checkbox": "^1.1.3", diff --git a/public/locale/en.json b/public/locale/en.json index 6a8b484c1ec..0f3088f9989 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -308,6 +308,11 @@ "add_new_location": "Add New Location", "add_new_patient": "Add New Patient", "add_new_user": "Add New User", + "create_multiple_beds":"Create Multiple Beds", + "create_multiple_beds_description":"Enable this option to create multiple beds at once", + "individual_bed_names":"Individual Bed Names", + "reset_to_default":"Reset to Default", + "bulk_bed_creation_info":"You are about to create multiple beds. Each bed will have a unique name based on the beds names you provided.", "add_notes": "Add notes", "add_notes_about_diagnosis": "Add notes about the diagnosis...", "add_notes_about_symptom": "Add notes about the symptom...", @@ -2083,6 +2088,7 @@ "search_by_patient_name": "Search by Patient Name", "search_by_patient_no": "Search by Patient Number", "search_by_phone_number": "Search by Phone Number", + "edit_bed_names":"Edit {{count}} bed names ", "search_by_resource_title": "Search by resource title", "search_by_user_name": "Search by user name", "search_by_username": "Search by username", diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx new file mode 100644 index 00000000000..277ed2a3365 --- /dev/null +++ b/src/components/ui/accordion.tsx @@ -0,0 +1,58 @@ +import * as AccordionPrimitive from "@radix-ui/react-accordion"; +import { ChevronDown } from "lucide-react"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const Accordion = AccordionPrimitive.Root; + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AccordionItem.displayName = "AccordionItem"; + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className, + )} + {...props} + > + {children} + + + +)); +AccordionTrigger.displayName = "AccordionTrigger"; + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)); +AccordionContent.displayName = "AccordionContent"; + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; diff --git a/src/pages/Facility/settings/locations/LocationForm.tsx b/src/pages/Facility/settings/locations/LocationForm.tsx index 19f1a025140..b1afd48f270 100644 --- a/src/pages/Facility/settings/locations/LocationForm.tsx +++ b/src/pages/Facility/settings/locations/LocationForm.tsx @@ -1,13 +1,22 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { t } from "i18next"; +import { Info, RotateCcw } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import * as z from "zod"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { Alert, AlertDescription } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; import { Form, FormControl, @@ -31,12 +40,12 @@ import mutate from "@/Utils/request/mutate"; import query from "@/Utils/request/query"; import { LocationFormOptions, - LocationWrite, - OperationalStatus, - Status, + type LocationWrite, + type OperationalStatus, + type Status, } from "@/types/location/location"; import locationApi from "@/types/location/locationApi"; -import { +import type { BatchRequestBody, BatchSubmissionResult, } from "@/types/questionnaire/batch"; @@ -51,15 +60,15 @@ const formSchema = z.object({ operational_status: z.enum(["C", "H", "O", "U", "K", "I"] as const), form: z.enum(LocationFormOptions), parent: z.string().optional().nullable(), - beds_count: z - .number() - .min(1, t("number_min_error", { min: 1 })) - .optional(), + enableBulkCreation: z.boolean().default(false), + numberOfBeds: z.string().optional(), + customizeNames: z.boolean().default(false), organizations: z.array(z.string()).default([]), availability_status: z.enum(["available", "unavailable"] as const), }); type FormValues = z.infer; + interface Props { facilityId: string; onSuccess?: () => void; @@ -73,11 +82,14 @@ const defaultValues: FormValues = { status: "active", operational_status: "O", form: "ro", - beds_count: 1, parent: null, + enableBulkCreation: false, + numberOfBeds: "2", + customizeNames: false, organizations: [], availability_status: "available", }; + export default function LocationForm({ facilityId, onSuccess, @@ -87,13 +99,6 @@ export default function LocationForm({ const { t } = useTranslation(); const queryClient = useQueryClient(); const [bedNames, setBedNames] = useState([]); - const updateBedName = (index: number, newName: string) => { - setBedNames((prev) => { - const updated = [...prev]; - updated[index] = newName; - return updated; - }); - }; const { data: location, isLoading } = useQuery({ queryKey: ["location", locationId], @@ -112,16 +117,53 @@ export default function LocationForm({ parent: parentId || null, }, }); - useEffect(() => { - if (form.watch("beds_count")) { + + const updateBedName = (index: number, newName: string) => { + setBedNames((prev) => { + const updated = [...prev]; + updated[index] = newName; + return updated; + }); + }; + + const resetToDefaultNames = () => { + if (form.watch("name") && form.watch("numberOfBeds")) { setBedNames( Array.from( - { length: Number(form.watch("beds_count")) }, + { length: Number.parseInt(form.watch("numberOfBeds") ?? "0") }, (_, index) => `${form.watch("name")} ${index + 1}`, ), ); } - }, [form.watch("beds_count"), form.watch("name")]); + }; + + useEffect(() => { + if ( + form.watch("form") === "bd" && + form.watch("enableBulkCreation") && + form.watch("numberOfBeds") && + form.watch("name") + ) { + if (!form.watch("customizeNames") || bedNames.length === 0) { + resetToDefaultNames(); + } else { + const newCount = Number.parseInt(form.watch("numberOfBeds") ?? "0"); + setBedNames((prev) => { + const updated = [...prev]; + while (updated.length < newCount) { + updated.push(`${form.watch("name")} ${updated.length + 1}`); + } + return updated.slice(0, newCount); + }); + } + } + }, [ + form.watch("numberOfBeds"), + form.watch("name"), + form.watch("form"), + form.watch("enableBulkCreation"), + bedNames.length, + ]); useEffect(() => { if (location) { @@ -134,6 +176,7 @@ export default function LocationForm({ parent: parentId || null, organizations: [], availability_status: location.availability_status || "available", + customizeNames: false, }); } }, [location, form, parentId]); @@ -149,7 +192,6 @@ export default function LocationForm({ onSuccess: () => { toast.success(isEditMode ? t("location_updated") : t("location_created")); queryClient.invalidateQueries({ queryKey: ["locations"] }); - onSuccess?.(); }, }); @@ -163,7 +205,6 @@ export default function LocationForm({ queryClient.invalidateQueries({ queryKey: ["locations"] }); onSuccess?.(); }, - onError: () => { toast.error(t("beds_creation_failed")); }, @@ -178,7 +219,7 @@ export default function LocationForm({ parent: values.parent || undefined, }; - if (values.form === "bd" && !isEditMode && (values.beds_count ?? 0) > 1) { + if (values.form === "bd" && !isEditMode && values.enableBulkCreation) { if (bedNames.some((name) => name.trim() === "")) { toast.error("Any Bed Name can't be empty"); return; @@ -227,50 +268,11 @@ export default function LocationForm({ return
Loading...
; } + const showBedOptions = form.watch("form") === "bd" && !isEditMode; + return (
- ( - - {t("name")} - - - - - - )} - /> - {form.watch("form") === "bd" && - bedNames.length > 1 && - form.watch("name").trim() !== "" && ( -
- {bedNames.map((name, index) => ( - updateBedName(index, e.target.value)} - /> - ))} -
- )} - ( - - {t("description")} - -