From 6db0fb2fc09c68e789e5ed47542a9239e56f215c Mon Sep 17 00:00:00 2001 From: jwnasambu Date: Thu, 8 Aug 2024 19:37:14 +0300 Subject: [PATCH] Add inline notification --- src/results/result-form-field.component.tsx | 105 ++++----- src/results/result-form.component.tsx | 230 ++++++++------------ 2 files changed, 132 insertions(+), 203 deletions(-) diff --git a/src/results/result-form-field.component.tsx b/src/results/result-form-field.component.tsx index 375e4fd..5b89c2d 100644 --- a/src/results/result-form-field.component.tsx +++ b/src/results/result-form-field.component.tsx @@ -1,50 +1,19 @@ import React from "react"; import styles from "./result-form.scss"; -import { TextInput, Select, SelectItem } from "@carbon/react"; -import { TFunction, useTranslation } from "react-i18next"; +import { + TextInput, + Select, + SelectItem, + InlineNotification, +} from "@carbon/react"; +import { useTranslation } from "react-i18next"; import { ConceptReference } from "./result-form.resource"; -import { Controller, UseFormRegister } from "react-hook-form"; -import { ZodSchema, z } from "zod"; - -const isNumeric = (concept: ConceptReference): boolean => - concept.datatype.display === "Numeric"; - -const createResultFormFieldSchema = ( - concepts: ConceptReference[], - t: TFunction -): ZodSchema => { - const fields = concepts.reduce((acc, concept) => { - const fieldValidation = isNumeric(concept) - ? z.coerce - .number() - .min(0, { message: t("enterValidNumber", "Enter a valid number") }) - .optional() - : z - .string() - .min(1, { message: t("fieldRequired", "This field is required") }) - .optional(); - - return { ...acc, [concept.uuid]: fieldValidation }; - }, {} as Record); - - const schema = z.object(fields).refine( - (data) => { - return Object.values(data).some( - (value) => value !== undefined && value !== null && value !== "" - ); - }, - { - message: t("atLeastOneField", "At least one field must be filled out"), - } - ); - - return schema; -}; +import { Controller } from "react-hook-form"; interface ResultFormFieldProps { - register: UseFormRegister<{ testResult: string }>; concept: ConceptReference; control: any; + register: any; errors: any; } @@ -54,6 +23,7 @@ const ResultFormField: React.FC = ({ errors, }) => { const { t } = useTranslation(); + const isTextOrNumeric = (concept) => concept.datatype?.display === "Text" || concept.datatype?.display === "Numeric"; @@ -63,16 +33,16 @@ const ResultFormField: React.FC = ({ const printValueRange = (concept: ConceptReference) => { if (concept?.datatype?.display === "Numeric") { const maxVal = Math.max( - concept?.hiAbsolute ?? 0, - concept?.hiCritical ?? 0, - concept?.hiNormal ?? 0 + concept?.hiAbsolute, + concept?.hiCritical, + concept?.hiNormal ); const minVal = Math.min( - concept?.lowAbsolute ?? 0, - concept?.lowCritical ?? 0, - concept?.lowNormal ?? 0 + concept?.lowAbsolute, + concept?.lowCritical, + concept?.lowNormal ); - return ` (${minVal} - ${maxVal > 0 ? maxVal : "--"} ${ + return ` (${minVal ?? 0} - ${maxVal > 0 ? maxVal : "--"} ${ concept?.units ?? "" })`; } @@ -81,9 +51,23 @@ const ResultFormField: React.FC = ({ return ( <> + {Object.keys(errors).length > 0 && ( + + )} {isTextOrNumeric(concept) && ( ( = ({ : "") } autoFocus - invalid={!!errors[concept.uuid]} - invalidText={errors[concept.uuid]?.message} /> )} /> @@ -109,19 +91,22 @@ const ResultFormField: React.FC = ({ ( + {member?.answers?.map((answer) => ( = ({ order, patientUuid }) => { const { t } = useTranslation(); const session = useSession(); const { laboratoryOrderTypeUuid, encounterTypeUuid } = useConfig(); - const [showErrorNotification, setShowErrorNotification] = useState(false); - const { control, register, @@ -52,7 +43,6 @@ const ResultForm: React.FC = ({ order, patientUuid }) => { } = useForm<{ testResult: string }>({ defaultValues: {}, }); - const isTablet = useLayoutType() === "tablet"; const { patient, isLoading: isLoadingPatient } = usePatient(patientUuid); const { concept, isLoading: isLoadingConcepts } = useGetOrderConceptByUuid( @@ -68,21 +58,26 @@ const ResultForm: React.FC = ({ order, patientUuid }) => { } }, [patient, patientUuid]); + const [showErrorNotification, setShowErrorNotification] = useState(false); + const onSubmit = (data, e) => { e.preventDefault(); - let obsValue = []; - const submissionDatetime = new Date().toISOString(); + const formValues = getValues(); + const isEmptyForm = Object.values(formValues).every( + (value) => value === "" || value === null + ); - const isAtLeastOneFieldFilled = concept.setMembers.some((member) => { - return getValues()[member.uuid]; - }); - - if (!isAtLeastOneFieldFilled) { + if (isEmptyForm) { setShowErrorNotification(true); return; } + setShowErrorNotification(false); + + let obsValue = []; + const submissionDatetime = new Date().toISOString(); + if (concept.set && concept.setMembers.length > 0) { let groupMembers = []; concept.setMembers.forEach((member) => { @@ -91,10 +86,10 @@ const ResultForm: React.FC = ({ order, patientUuid }) => { member.datatype.display === "Numeric" || member.datatype.display === "Text" ) { - value = getValues()[member.uuid]; + value = getValues()[`${member.uuid}`]; } else if (member.datatype.display === "Coded") { value = { - uuid: getValues()[member.uuid], + uuid: getValues()[`${member.uuid}`], }; } const groupMember = { @@ -118,10 +113,10 @@ const ResultForm: React.FC = ({ order, patientUuid }) => { concept.datatype.display === "Numeric" || concept.datatype.display === "Text" ) { - value = getValues()[concept.uuid]; + value = getValues()[`${concept.uuid}`]; } else if (concept.datatype.display === "Coded") { value = { - uuid: getValues()[concept.uuid], + uuid: getValues()[`${concept.uuid}`], }; } @@ -133,6 +128,7 @@ const ResultForm: React.FC = ({ order, patientUuid }) => { obsDatetime: submissionDatetime, }); } + const encounterPayload = { encounterDatetime: submissionDatetime, patient: patientUuid, @@ -152,114 +148,70 @@ const ResultForm: React.FC = ({ order, patientUuid }) => { orderer: order.orderer, }; - UpdateOrderResult(encounterPayload, orderDiscontinuationPayload) - .then( - (response) => { - if (response.ok) { - showSnackbar({ - isLowContrast: true, - title: t("updateEncounter", "Update lab results"), - kind: "success", - subtitle: t( - "generateSuccessfully", - "You have successfully updated test results" - ), - }); - - const abortController = new AbortController(); - setFulfillerStatus(order.uuid, "COMPLETED", abortController).then( - () => { - showSnackbar({ - isLowContrast: true, - title: t("markOrderFulfillStatus", "Test order completed"), - kind: "success", - subtitle: t( - "testOrderCompletedSuccessfully", - "You have successfully completed the test order" - ), - }); - mutate( - (key) => - typeof key === "string" && - key.startsWith( - `${restBaseUrl}/order?orderTypes=${laboratoryOrderTypeUuid}` - ), - undefined, - { revalidate: true } - ); - closeOverlay(); - }, - (err) => { - showNotification({ - title: t( - "errorMarkingOrderFulfillStatus", - "Error occurred while marking order fulfill status" - ), - kind: "error", - critical: true, - description: err?.message, - }); - } - ); - - return response; - } - }, - (err) => { - showNotification({ - title: t( - "errorUpdatingEncounter", - "Error occurred while updating test results" + UpdateOrderResult(encounterPayload, orderDiscontinuationPayload).then( + (response) => { + if (response.ok) { + showSnackbar({ + isLowContrast: true, + title: t("updateEncounter", "Update lab results"), + kind: "success", + subtitle: t( + "generateSuccessfully", + "You have successfully updated test results" ), - kind: "error", - critical: true, - description: err?.message, }); - } - ) - .then((resp) => { - const abortController = new AbortController(); - setFulfillerStatus(order.uuid, "COMPLETED", abortController).then( - () => { - showSnackbar({ - isLowContrast: true, - title: t("markOrderFulfillStatus", "Test order completed"), - kind: "success", - subtitle: t( - "testOrderCompletedSuccessfully", - "You have successfully completed the test order" - ), - }); - mutate( - (key) => - typeof key === "string" && - key.startsWith( - `${restBaseUrl}/order?orderTypes=${laboratoryOrderTypeUuid}` + + const abortController = new AbortController(); + setFulfillerStatus(order.uuid, "COMPLETED", abortController).then( + () => { + showSnackbar({ + isLowContrast: true, + title: t("markOrderFulfillStatus", "Test order completed"), + kind: "success", + subtitle: t( + "testOrderCompletedSuccessfully", + "You have successfully completed the test order" ), - undefined, - { revalidate: true } - ); - closeOverlay(); - }, - (err) => { - showNotification({ - title: t( - "errorMarkingOrderFulfillStatus", - "Error occurred while marking order fulfill status" - ), - kind: "error", - critical: true, - description: err?.message, - }); - } - ); - }); - }; + }); + mutate( + (key) => + typeof key === "string" && + key.startsWith( + `${restBaseUrl}/order?orderTypes=${laboratoryOrderTypeUuid}` + ), + undefined, + { revalidate: true } + ); + closeOverlay(); + }, + (err) => { + showNotification({ + title: t( + "errorMarkingOrderFulfillStatus", + "Error occurred while marking order fulfill status" + ), + kind: "error", + critical: true, + description: err?.message, + }); + } + ); - const onError = (errors: FieldErrors) => { - if (errors) { - setShowErrorNotification(true); - } + return response; + } + }, + (err) => { + showNotification({ + title: t( + "errorUpdatingEncounter", + "Error occurred while updating test results" + ), + kind: "error", + critical: true, + description: err?.message, + }); + } + ); }; if (isLoadingPatient || isLoadingConcepts) { @@ -271,19 +223,6 @@ const ResultForm: React.FC = ({ order, patientUuid }) => { - {showErrorNotification && ( - - setShowErrorNotification(false)} - /> - - )}
{concept.setMembers.length > 0 &&
{concept.display}
} @@ -297,6 +236,11 @@ const ResultForm: React.FC = ({ order, patientUuid }) => { )}
+ {showErrorNotification && ( +
+ {t("errorEmptyForm", "Please fill in all required fields")} +
+ )} - @@ -318,4 +259,3 @@ const ResultForm: React.FC = ({ order, patientUuid }) => { }; export default ResultForm; -``;