From cba1b96e270ca7a3e51877c027b66662ab3b85a8 Mon Sep 17 00:00:00 2001 From: SteveGT96 Date: Thu, 31 Aug 2023 16:16:26 +0100 Subject: [PATCH 1/3] update: Add current admission fom --- .../admission/PatientAdmission.tsx | 4 +- .../currentAdmission/CurrentAdmission.tsx | 513 ++++++++++++++++++ .../admission/currentAdmission/styles.scss | 82 +++ .../admission/currentAdmission/types.ts | 33 ++ 4 files changed, 630 insertions(+), 2 deletions(-) create mode 100644 src/components/accessories/admission/currentAdmission/CurrentAdmission.tsx create mode 100644 src/components/accessories/admission/currentAdmission/styles.scss create mode 100644 src/components/accessories/admission/currentAdmission/types.ts diff --git a/src/components/accessories/admission/PatientAdmission.tsx b/src/components/accessories/admission/PatientAdmission.tsx index 3f9435caf..e12187193 100644 --- a/src/components/accessories/admission/PatientAdmission.tsx +++ b/src/components/accessories/admission/PatientAdmission.tsx @@ -6,7 +6,7 @@ import { scrollToElement } from "../../../libraries/uiUtils/scrollToElement"; import { useDispatch, useSelector } from "react-redux"; import { IState } from "../../../types"; import { AdmissionTransitionState } from "./types"; -import { AdmissionDTO, OpdDTO } from "../../../generated"; +import { AdmissionDTO, OpdDTO, PatientDTOStatusEnum } from "../../../generated"; import InfoBox from "../infoBox/InfoBox"; import ConfirmationDialog from "../confirmationDialog/ConfirmationDialog"; import checkIcon from "../../../assets/check-icon.png"; @@ -190,7 +190,7 @@ const PatientAdmission: FC = () => { return (
- {!showForm && ( + {patient?.status === PatientDTOStatusEnum.I && ( )} {open && ( diff --git a/src/components/accessories/admission/currentAdmission/CurrentAdmission.tsx b/src/components/accessories/admission/currentAdmission/CurrentAdmission.tsx new file mode 100644 index 000000000..b982ba616 --- /dev/null +++ b/src/components/accessories/admission/currentAdmission/CurrentAdmission.tsx @@ -0,0 +1,513 @@ +import { useFormik } from "formik"; +import get from "lodash.get"; +import has from "lodash.has"; +import moment from "moment"; +import React, { FC, useCallback, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useDispatch, useSelector } from "react-redux"; +import { object, string } from "yup"; +import warningIcon from "../../../../assets/warning-icon.png"; +import { + AdmissionTypeDTO, + DiseaseDTO, + DiseaseTypeDTO, + WardDTO, +} from "../../../../generated"; +import { renderDate } from "../../../../libraries/formatUtils/dataFormatting"; +import { + differenceInDays, + formatAllFieldValues, + getFromFields, +} from "../../../../libraries/formDataHandling/functions"; +import { getAdmissionTypes } from "../../../../state/admissionTypes/actions"; +import { getDischargeTypes } from "../../../../state/dischargeTypes/actions"; +import { + getDiseasesIpdIn, + getDiseasesIpdOut, +} from "../../../../state/diseases/actions"; +import { getWards } from "../../../../state/ward/actions"; +import { IState } from "../../../../types"; +import AutocompleteField from "../../autocompleteField/AutocompleteField"; +import Button from "../../button/Button"; +import ConfirmationDialog from "../../confirmationDialog/ConfirmationDialog"; +import DateField from "../../dateField/DateField"; +import TextField from "../../textField/TextField"; +import "./styles.scss"; +import { CurrentAdmissionProps } from "./types"; + +const CurrentAdmission: FC = ({ + fields, + onSubmit, + creationMode, + submitButtonLabel, + resetButtonLabel, + isLoading, + admitted, + shouldResetForm, + resetFormCallback, +}) => { + const { t } = useTranslation(); + const dispatch = useDispatch(); + + const diagnosisInList = useSelector( + (state: IState) => state.diseases.diseasesIpdIn.data + ); + + const admissionTypes = useSelector( + (state: IState) => state.admissionTypes.allAdmissionTypes.data + ); + const wards = useSelector((state: IState) => state.wards.allWards.data); + + const diagnosisOutList = useSelector( + (state: IState) => state.diseases.diseasesIpdOut.data + ); + + const dischargeTypes = useSelector( + (state: IState) => state.dischargeTypes.allDischargeTypes.data + ); + + const renderOptions = ( + data: + | ( + | WardDTO + | DiseaseDTO + | AdmissionTypeDTO + | DiseaseTypeDTO + | DiseaseDTO + )[] + | undefined + ) => { + if (data) { + return data.map((item) => { + return { + value: item.code?.toString() ?? "", + label: item.description ?? "", + }; + }); + } else return []; + }; + + const initialValues = getFromFields(fields, "value"); + + const validationSchema = object({ + ward: string().required(t("common.required")), + admType: string().required(t("common.required")), + admDate: string() + .required(t("common.required")) + .test({ + name: "admDate", + message: t("common.invaliddate"), + test: function (value) { + return moment(value).isValid(); + }, + }) + .test({ + name: "admDate", + message: t("admission.datebefore"), + test: function (value) { + return moment(this.parent.disDate).isValid() + ? moment(value).isSameOrBefore(this.parent.disDate) + : true; + }, + }), + diseaseIn: string().required(t("common.required")), + disDate: admitted + ? string() + .required(t("common.required")) + .test({ + name: "disDate", + message: t("admission.validatelastdate", { + admDate: moment(initialValues.admDate).format("DD/MM/YYYY"), + }), + test: function (value) { + return moment(value).isSameOrAfter(moment(this.parent.admDate)); + }, + }) + : string(), + + disType: admitted ? string().required(t("common.required")) : string(), + diseaseOut1: admitted ? string().required(t("common.required")) : string(), + + diseaseOut2: admitted + ? string().test({ + name: "diseaseOut2", + message: t("opd.validatedisease"), + test: function (value) { + return ( + !value || + (this.parent.diseaseOut1 && value !== this.parent.diseaseOut1) + ); + }, + }) + : string(), + + diseaseOut3: admitted + ? string().test({ + name: "diseaseOut3", + message: t("opd.validatedisease"), + test: function (value) { + return ( + !value || + (this.parent.diseaseOut1 && + this.parent.diseaseOut2 && + value !== this.parent.diseaseOut1 && + value !== this.parent.diseaseOut2) + ); + }, + }) + : string(), + }); + + const formik = useFormik({ + initialValues, + validationSchema, + enableReinitialize: true, + onSubmit: (values) => { + const formattedValues = formatAllFieldValues(fields, values); + formattedValues.diseaseIn = diagnosisInList?.find( + (item) => item.code === formattedValues.diseaseIn + ); + formattedValues.admType = admissionTypes?.find( + (item) => item.code === formattedValues.admType + ); + formattedValues.ward = wards?.find( + (item) => item.code === formattedValues.ward + ); + + formattedValues.diseaseOut1 = diagnosisOutList?.find( + (item) => item.code === formattedValues.diseaseOut1 + ); + formattedValues.diseaseOut2 = diagnosisOutList?.find( + (item) => item.code === formattedValues.diseaseOut2 + ); + formattedValues.diseaseOut3 = diagnosisOutList?.find( + (item) => item.code === formattedValues.diseaseOut3 + ); + formattedValues.disType = dischargeTypes?.find( + (item) => item.code === formattedValues.disType + ); + + onSubmit(formattedValues as any); + }, + }); + + const { setFieldValue, resetForm, handleBlur } = formik; + + const dateFieldHandleOnChange = useCallback( + (fieldName: string) => (value: any) => { + setFieldValue(fieldName, value); + formik.setFieldTouched(fieldName); + const days = differenceInDays( + new Date(formik.values.admDate), + new Date(formik.values.disDate) + ).toString(); + setFieldValue("bedDays", days); + }, + [setFieldValue] + ); + + const isValid = (fieldName: string): boolean => { + return has(formik.touched, fieldName) && has(formik.errors, fieldName); + }; + + const getErrorText = (fieldName: string): string => { + return has(formik.touched, fieldName) + ? (get(formik.errors, fieldName) as string) + : ""; + }; + + const onBlurCallback = useCallback( + (fieldName: string) => + (e: React.FocusEvent, value: string) => { + handleBlur(e); + setFieldValue(fieldName, value); + }, + [setFieldValue, handleBlur] + ); + + const [openResetConfirmation, setOpenResetConfirmation] = useState(false); + + const handleResetConfirmation = () => { + setOpenResetConfirmation(false); + formik.resetForm(); + resetFormCallback(); + }; + + useEffect(() => { + if (shouldResetForm) { + resetForm(); + resetFormCallback(); + } + }, [shouldResetForm, resetForm, resetFormCallback]); + + const diagnosisInStatus = useSelector( + (state: IState) => state.diseases.diseasesIpdIn.status + ); + const wardStatus = useSelector( + (state: IState) => state.wards.allWards.status + ); + const admTypeStatus = useSelector( + (state: IState) => state.admissionTypes.allAdmissionTypes.status + ); + + useEffect(() => { + dispatch(getDiseasesIpdOut()); + }, [dispatch, getDiseasesIpdOut]); + + useEffect(() => { + dispatch(getDiseasesIpdIn()); + dispatch(getAdmissionTypes()); + dispatch(getWards()); + }, [dispatch]); + + return ( + <> +
+
+ {creationMode + ? t("admission.newadmission") + : t("admission.editadmission") + + ": " + + renderDate(formik.values.admDate)} +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ {admitted && ( +
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+ )} +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ setOpenResetConfirmation(false)} + /> + +
+ + ); +}; + +export default CurrentAdmission; diff --git a/src/components/accessories/admission/currentAdmission/styles.scss b/src/components/accessories/admission/currentAdmission/styles.scss new file mode 100644 index 000000000..a35ecc353 --- /dev/null +++ b/src/components/accessories/admission/currentAdmission/styles.scss @@ -0,0 +1,82 @@ +@import "../../../../styles/variables"; +@import "../../../../../node_modules/susy/sass/susy"; + +.patientAdmissionForm { + display: inline-block; + flex-direction: column; + align-items: center; + width: 100%; + + .formInsertMode{ + margin: 0px 0px 20px; + } + + .patientAdmissionForm__item { + margin: 7px 0px; + padding: 0px 15px; + width: 50%; + @include susy-media($narrow) { + padding: 0px 10px; + } + @include susy-media($tablet_land) { + padding: 0px 10px; + } + @include susy-media($tablet_port) { + width: 50%; + } + @include susy-media($smartphone) { + width: 100%; + } + .dateField, + .textField, + .selectField { + width: 100%; + } + + &.fullWidth { + width: 100%; + } + + &.halfWidth { + width: 50%; + @include susy-media($smartphone) { + width: 100%; + } + } + + &.compressed { + .textField { + float: left; + width: 50%; + &:nth-of-type(1) { + padding-right: 10px; + } + } + } + } + + .patientAdmissionForm__buttonSet { + display: flex; + margin-top: 25px; + padding: 0px 15px; + flex-direction: row-reverse; + @include susy-media($smartphone_small) { + display: block; + } + + .submit_button, + .reset_button { + .MuiButton-label { + font-size: smaller; + letter-spacing: 1px; + font-weight: 600; + } + button { + @include susy-media($smartphone_small) { + width: 100%; + margin-top: 10px; + } + } + } + } +} diff --git a/src/components/accessories/admission/currentAdmission/types.ts b/src/components/accessories/admission/currentAdmission/types.ts new file mode 100644 index 000000000..28816ac6a --- /dev/null +++ b/src/components/accessories/admission/currentAdmission/types.ts @@ -0,0 +1,33 @@ +import { AdmissionDTO } from "../../../../generated"; +import { TFields } from "../../../../libraries/formDataHandling/types"; + +interface ICurrentAdmissionProps { + fields: TFields; + onSubmit: (adm: AdmissionDTO) => void; + creationMode: boolean; + submitButtonLabel: string; + resetButtonLabel: string; + isLoading: boolean; + admitted: boolean; + shouldResetForm: boolean; + resetFormCallback: () => void; +} + +export type CurrentAdmissionProps = ICurrentAdmissionProps; + +export type AdmissionFormFieldName = + | "ward" + | "transUnit" + | "admDate" + | "admType" + | "diseaseIn" + | "fhu" + | "note" + | "disDate" + | "disType" + | "bedDays" + | "diseaseOut1" + | "diseaseOut2" + | "diseaseOut3" + | "cliDiaryCharge" + | "imageryCharge"; From b9703b2a0081b5af79844cdf6586b96871d16c61 Mon Sep 17 00:00:00 2001 From: SteveGT96 Date: Fri, 15 Sep 2023 18:02:11 +0100 Subject: [PATCH 2/3] update(OH2-216): Remove current admission from admission table and add current admission component --- .../admission/PatientAdmission.tsx | 12 +- .../admissionTable/AdmissionTable.tsx | 4 +- .../currentAdmission/CurrentAdmission.tsx | 513 ------------------ .../admission/currentAdmission/styles.scss | 82 --- .../admission/currentAdmission/types.ts | 33 -- .../currentAdmission/CurrentAdmission.tsx | 73 +++ .../CurrentAdmissionData.tsx | 75 +++ .../CurrentAdmissionForm.tsx | 320 +++++++++++ .../currentAdmissionForm/consts.ts | 50 ++ .../currentAdmissionForm/types.ts | 23 + .../accessories/currentAdmission/styles.scss | 111 ++++ .../accessories/currentAdmission/types.ts | 3 + 12 files changed, 669 insertions(+), 630 deletions(-) delete mode 100644 src/components/accessories/admission/currentAdmission/CurrentAdmission.tsx delete mode 100644 src/components/accessories/admission/currentAdmission/styles.scss delete mode 100644 src/components/accessories/admission/currentAdmission/types.ts create mode 100644 src/components/accessories/currentAdmission/CurrentAdmission.tsx create mode 100644 src/components/accessories/currentAdmission/currentAdmissionData/CurrentAdmissionData.tsx create mode 100644 src/components/accessories/currentAdmission/currentAdmissionForm/CurrentAdmissionForm.tsx create mode 100644 src/components/accessories/currentAdmission/currentAdmissionForm/consts.ts create mode 100644 src/components/accessories/currentAdmission/currentAdmissionForm/types.ts create mode 100644 src/components/accessories/currentAdmission/styles.scss create mode 100644 src/components/accessories/currentAdmission/types.ts diff --git a/src/components/accessories/admission/PatientAdmission.tsx b/src/components/accessories/admission/PatientAdmission.tsx index e12187193..afdab3cbf 100644 --- a/src/components/accessories/admission/PatientAdmission.tsx +++ b/src/components/accessories/admission/PatientAdmission.tsx @@ -23,6 +23,7 @@ import PatientAdmissionTable from "./admissionTable/AdmissionTable"; import { isEmpty } from "lodash"; import { usePermission } from "../../../libraries/permissionUtils/usePermission"; import { getLastOpd } from "../../../state/opds/actions"; +import { CurrentAdmission } from "../currentAdmission/CurrentAdmission"; const PatientAdmission: FC = () => { const { t } = useTranslation(); @@ -31,6 +32,7 @@ const PatientAdmission: FC = () => { const infoBoxRef = useRef(null); const [shouldResetForm, setShouldResetForm] = useState(false); const [creationMode, setCreationMode] = useState(true); + const [isEditingCurrent, setIsEditingCurrent] = useState(false); const [showForm, setShowForm] = useState(false); const [admissionToEdit, setAdmissionToEdit] = useState(); @@ -188,11 +190,16 @@ const PatientAdmission: FC = () => { scrollToElement(null); }; + const onCurrentAdmissionChange = (value: boolean) => { + setIsEditingCurrent(value); + }; + return (
{patient?.status === PatientDTOStatusEnum.I && ( )} + {open && ( { /> = ({ const data = useSelector((state) => state.admissions.getPatientAdmissions.data - ? state.admissions.getPatientAdmissions.data + ? state.admissions.getPatientAdmissions.data.filter( + (e) => state.admissions.currentAdmissionByPatientId.data?.id !== e.id + ) : [] ); diff --git a/src/components/accessories/admission/currentAdmission/CurrentAdmission.tsx b/src/components/accessories/admission/currentAdmission/CurrentAdmission.tsx deleted file mode 100644 index b982ba616..000000000 --- a/src/components/accessories/admission/currentAdmission/CurrentAdmission.tsx +++ /dev/null @@ -1,513 +0,0 @@ -import { useFormik } from "formik"; -import get from "lodash.get"; -import has from "lodash.has"; -import moment from "moment"; -import React, { FC, useCallback, useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { useDispatch, useSelector } from "react-redux"; -import { object, string } from "yup"; -import warningIcon from "../../../../assets/warning-icon.png"; -import { - AdmissionTypeDTO, - DiseaseDTO, - DiseaseTypeDTO, - WardDTO, -} from "../../../../generated"; -import { renderDate } from "../../../../libraries/formatUtils/dataFormatting"; -import { - differenceInDays, - formatAllFieldValues, - getFromFields, -} from "../../../../libraries/formDataHandling/functions"; -import { getAdmissionTypes } from "../../../../state/admissionTypes/actions"; -import { getDischargeTypes } from "../../../../state/dischargeTypes/actions"; -import { - getDiseasesIpdIn, - getDiseasesIpdOut, -} from "../../../../state/diseases/actions"; -import { getWards } from "../../../../state/ward/actions"; -import { IState } from "../../../../types"; -import AutocompleteField from "../../autocompleteField/AutocompleteField"; -import Button from "../../button/Button"; -import ConfirmationDialog from "../../confirmationDialog/ConfirmationDialog"; -import DateField from "../../dateField/DateField"; -import TextField from "../../textField/TextField"; -import "./styles.scss"; -import { CurrentAdmissionProps } from "./types"; - -const CurrentAdmission: FC = ({ - fields, - onSubmit, - creationMode, - submitButtonLabel, - resetButtonLabel, - isLoading, - admitted, - shouldResetForm, - resetFormCallback, -}) => { - const { t } = useTranslation(); - const dispatch = useDispatch(); - - const diagnosisInList = useSelector( - (state: IState) => state.diseases.diseasesIpdIn.data - ); - - const admissionTypes = useSelector( - (state: IState) => state.admissionTypes.allAdmissionTypes.data - ); - const wards = useSelector((state: IState) => state.wards.allWards.data); - - const diagnosisOutList = useSelector( - (state: IState) => state.diseases.diseasesIpdOut.data - ); - - const dischargeTypes = useSelector( - (state: IState) => state.dischargeTypes.allDischargeTypes.data - ); - - const renderOptions = ( - data: - | ( - | WardDTO - | DiseaseDTO - | AdmissionTypeDTO - | DiseaseTypeDTO - | DiseaseDTO - )[] - | undefined - ) => { - if (data) { - return data.map((item) => { - return { - value: item.code?.toString() ?? "", - label: item.description ?? "", - }; - }); - } else return []; - }; - - const initialValues = getFromFields(fields, "value"); - - const validationSchema = object({ - ward: string().required(t("common.required")), - admType: string().required(t("common.required")), - admDate: string() - .required(t("common.required")) - .test({ - name: "admDate", - message: t("common.invaliddate"), - test: function (value) { - return moment(value).isValid(); - }, - }) - .test({ - name: "admDate", - message: t("admission.datebefore"), - test: function (value) { - return moment(this.parent.disDate).isValid() - ? moment(value).isSameOrBefore(this.parent.disDate) - : true; - }, - }), - diseaseIn: string().required(t("common.required")), - disDate: admitted - ? string() - .required(t("common.required")) - .test({ - name: "disDate", - message: t("admission.validatelastdate", { - admDate: moment(initialValues.admDate).format("DD/MM/YYYY"), - }), - test: function (value) { - return moment(value).isSameOrAfter(moment(this.parent.admDate)); - }, - }) - : string(), - - disType: admitted ? string().required(t("common.required")) : string(), - diseaseOut1: admitted ? string().required(t("common.required")) : string(), - - diseaseOut2: admitted - ? string().test({ - name: "diseaseOut2", - message: t("opd.validatedisease"), - test: function (value) { - return ( - !value || - (this.parent.diseaseOut1 && value !== this.parent.diseaseOut1) - ); - }, - }) - : string(), - - diseaseOut3: admitted - ? string().test({ - name: "diseaseOut3", - message: t("opd.validatedisease"), - test: function (value) { - return ( - !value || - (this.parent.diseaseOut1 && - this.parent.diseaseOut2 && - value !== this.parent.diseaseOut1 && - value !== this.parent.diseaseOut2) - ); - }, - }) - : string(), - }); - - const formik = useFormik({ - initialValues, - validationSchema, - enableReinitialize: true, - onSubmit: (values) => { - const formattedValues = formatAllFieldValues(fields, values); - formattedValues.diseaseIn = diagnosisInList?.find( - (item) => item.code === formattedValues.diseaseIn - ); - formattedValues.admType = admissionTypes?.find( - (item) => item.code === formattedValues.admType - ); - formattedValues.ward = wards?.find( - (item) => item.code === formattedValues.ward - ); - - formattedValues.diseaseOut1 = diagnosisOutList?.find( - (item) => item.code === formattedValues.diseaseOut1 - ); - formattedValues.diseaseOut2 = diagnosisOutList?.find( - (item) => item.code === formattedValues.diseaseOut2 - ); - formattedValues.diseaseOut3 = diagnosisOutList?.find( - (item) => item.code === formattedValues.diseaseOut3 - ); - formattedValues.disType = dischargeTypes?.find( - (item) => item.code === formattedValues.disType - ); - - onSubmit(formattedValues as any); - }, - }); - - const { setFieldValue, resetForm, handleBlur } = formik; - - const dateFieldHandleOnChange = useCallback( - (fieldName: string) => (value: any) => { - setFieldValue(fieldName, value); - formik.setFieldTouched(fieldName); - const days = differenceInDays( - new Date(formik.values.admDate), - new Date(formik.values.disDate) - ).toString(); - setFieldValue("bedDays", days); - }, - [setFieldValue] - ); - - const isValid = (fieldName: string): boolean => { - return has(formik.touched, fieldName) && has(formik.errors, fieldName); - }; - - const getErrorText = (fieldName: string): string => { - return has(formik.touched, fieldName) - ? (get(formik.errors, fieldName) as string) - : ""; - }; - - const onBlurCallback = useCallback( - (fieldName: string) => - (e: React.FocusEvent, value: string) => { - handleBlur(e); - setFieldValue(fieldName, value); - }, - [setFieldValue, handleBlur] - ); - - const [openResetConfirmation, setOpenResetConfirmation] = useState(false); - - const handleResetConfirmation = () => { - setOpenResetConfirmation(false); - formik.resetForm(); - resetFormCallback(); - }; - - useEffect(() => { - if (shouldResetForm) { - resetForm(); - resetFormCallback(); - } - }, [shouldResetForm, resetForm, resetFormCallback]); - - const diagnosisInStatus = useSelector( - (state: IState) => state.diseases.diseasesIpdIn.status - ); - const wardStatus = useSelector( - (state: IState) => state.wards.allWards.status - ); - const admTypeStatus = useSelector( - (state: IState) => state.admissionTypes.allAdmissionTypes.status - ); - - useEffect(() => { - dispatch(getDiseasesIpdOut()); - }, [dispatch, getDiseasesIpdOut]); - - useEffect(() => { - dispatch(getDiseasesIpdIn()); - dispatch(getAdmissionTypes()); - dispatch(getWards()); - }, [dispatch]); - - return ( - <> -
-
- {creationMode - ? t("admission.newadmission") - : t("admission.editadmission") + - ": " + - renderDate(formik.values.admDate)} -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- {admitted && ( -
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
- )} -
-
- -
-
- -
-
- -
-
- -
-
- setOpenResetConfirmation(false)} - /> - -
- - ); -}; - -export default CurrentAdmission; diff --git a/src/components/accessories/admission/currentAdmission/styles.scss b/src/components/accessories/admission/currentAdmission/styles.scss deleted file mode 100644 index a35ecc353..000000000 --- a/src/components/accessories/admission/currentAdmission/styles.scss +++ /dev/null @@ -1,82 +0,0 @@ -@import "../../../../styles/variables"; -@import "../../../../../node_modules/susy/sass/susy"; - -.patientAdmissionForm { - display: inline-block; - flex-direction: column; - align-items: center; - width: 100%; - - .formInsertMode{ - margin: 0px 0px 20px; - } - - .patientAdmissionForm__item { - margin: 7px 0px; - padding: 0px 15px; - width: 50%; - @include susy-media($narrow) { - padding: 0px 10px; - } - @include susy-media($tablet_land) { - padding: 0px 10px; - } - @include susy-media($tablet_port) { - width: 50%; - } - @include susy-media($smartphone) { - width: 100%; - } - .dateField, - .textField, - .selectField { - width: 100%; - } - - &.fullWidth { - width: 100%; - } - - &.halfWidth { - width: 50%; - @include susy-media($smartphone) { - width: 100%; - } - } - - &.compressed { - .textField { - float: left; - width: 50%; - &:nth-of-type(1) { - padding-right: 10px; - } - } - } - } - - .patientAdmissionForm__buttonSet { - display: flex; - margin-top: 25px; - padding: 0px 15px; - flex-direction: row-reverse; - @include susy-media($smartphone_small) { - display: block; - } - - .submit_button, - .reset_button { - .MuiButton-label { - font-size: smaller; - letter-spacing: 1px; - font-weight: 600; - } - button { - @include susy-media($smartphone_small) { - width: 100%; - margin-top: 10px; - } - } - } - } -} diff --git a/src/components/accessories/admission/currentAdmission/types.ts b/src/components/accessories/admission/currentAdmission/types.ts deleted file mode 100644 index 28816ac6a..000000000 --- a/src/components/accessories/admission/currentAdmission/types.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { AdmissionDTO } from "../../../../generated"; -import { TFields } from "../../../../libraries/formDataHandling/types"; - -interface ICurrentAdmissionProps { - fields: TFields; - onSubmit: (adm: AdmissionDTO) => void; - creationMode: boolean; - submitButtonLabel: string; - resetButtonLabel: string; - isLoading: boolean; - admitted: boolean; - shouldResetForm: boolean; - resetFormCallback: () => void; -} - -export type CurrentAdmissionProps = ICurrentAdmissionProps; - -export type AdmissionFormFieldName = - | "ward" - | "transUnit" - | "admDate" - | "admType" - | "diseaseIn" - | "fhu" - | "note" - | "disDate" - | "disType" - | "bedDays" - | "diseaseOut1" - | "diseaseOut2" - | "diseaseOut3" - | "cliDiaryCharge" - | "imageryCharge"; diff --git a/src/components/accessories/currentAdmission/CurrentAdmission.tsx b/src/components/accessories/currentAdmission/CurrentAdmission.tsx new file mode 100644 index 000000000..36ead8e96 --- /dev/null +++ b/src/components/accessories/currentAdmission/CurrentAdmission.tsx @@ -0,0 +1,73 @@ +import React, { FunctionComponent, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useDispatch, useSelector } from "react-redux"; +import { AdmissionDTO, OpdDTO, PatientDTO } from "../../../generated"; +import { updateAdmission } from "../../../state/admissions/actions"; +import { IState } from "../../../types"; +import { useFields } from "../admission/useFields"; +import { CurrentAdmissionData } from "./currentAdmissionData/CurrentAdmissionData"; +import { CurrentAdmissionForm } from "./currentAdmissionForm/CurrentAdmissionForm"; +import "./styles.scss"; +import { IOwnProps } from "./types"; + +export const CurrentAdmission: FunctionComponent = ({ + onEditChange, +}) => { + const { t } = useTranslation(); + const dispatch = useDispatch(); + const [editionMode, setEditionMode] = useState(false); + const currentAdmission = useSelector( + (state: IState) => state.admissions.currentAdmissionByPatientId.data + ); + const lastOpd = useSelector( + (state) => state.opds.lastOpd.data + ); + + const handleEdit = () => { + setEditionMode(true); + }; + + const handleDiscard = () => { + setEditionMode(false); + }; + + const fields = useFields(currentAdmission, lastOpd?.disease); + + const onSubmit = (adm: AdmissionDTO) => { + let admissionToSave: AdmissionDTO = { + ...currentAdmission, + deleted: "N", + type: adm.type, + admitted: adm.admitted, + fhu: adm.fhu, + admDate: adm.admDate, + admType: adm.admType, + diseaseIn: adm.diseaseIn, + note: adm.note, + ward: adm.ward, + }; + dispatch(updateAdmission(admissionToSave)); + }; + + useEffect(() => { + onEditChange(editionMode); + }, [editionMode]); + + return ( +
+ {currentAdmission && !editionMode && ( + + )} + {currentAdmission && editionMode && ( + + )} +
+ ); +}; diff --git a/src/components/accessories/currentAdmission/currentAdmissionData/CurrentAdmissionData.tsx b/src/components/accessories/currentAdmission/currentAdmissionData/CurrentAdmissionData.tsx new file mode 100644 index 000000000..d2eab5242 --- /dev/null +++ b/src/components/accessories/currentAdmission/currentAdmissionData/CurrentAdmissionData.tsx @@ -0,0 +1,75 @@ +import { IconButton } from "@material-ui/core"; +import { Edit } from "@material-ui/icons"; +import React, { FunctionComponent } from "react"; +import { useTranslation } from "react-i18next"; +import { useDispatch, useSelector } from "react-redux"; +import { AdmissionDTO, PatientDTO } from "../../../../generated"; +import { parseDate } from "../../../../libraries/formDataHandling/functions"; +import { IState } from "../../../../types"; +import Button from "../../button/Button"; +import TextField from "../../textField/TextField"; +import "../styles.scss"; +import AutocompleteField from "../../autocompleteField/AutocompleteField"; +import DateField from "../../dateField/DateField"; +import isEmpty from "lodash.isempty"; +import { renderDate } from "../../../../libraries/formatUtils/dataFormatting"; + +interface IOwnProps { + onEdit: () => void; + admission: AdmissionDTO; +} + +export const CurrentAdmissionData: FunctionComponent = ({ + onEdit, + admission, +}) => { + const { t } = useTranslation(); + + return ( +
+
+ + + +
+
+ {!isEmpty(admission?.ward?.description) && ( +
+ {t("admission.ward")} +

{admission?.ward?.description}

+
+ )} + {!isEmpty(admission?.fhu) && ( +
+ {t("admission.fhu")} +

{admission?.fhu}

+
+ )} + {!isEmpty(admission?.admDate) && ( +
+ {t("admission.admDate")} +

{renderDate(admission?.admDate)}

+
+ )} + {!isEmpty(admission?.admType?.description) && ( +
+ {t("admission.admType")} +

{admission?.admType?.description}

+
+ )} + {!isEmpty(admission?.diseaseIn?.description) && ( +
+ {t("admission.diseaseIn")} +

{admission?.diseaseIn?.description}

+
+ )} + {!isEmpty(admission?.note) && ( +
+ {t("admission.note")} +

{admission?.note}

+
+ )} +
+
+ ); +}; diff --git a/src/components/accessories/currentAdmission/currentAdmissionForm/CurrentAdmissionForm.tsx b/src/components/accessories/currentAdmission/currentAdmissionForm/CurrentAdmissionForm.tsx new file mode 100644 index 000000000..ffab467e8 --- /dev/null +++ b/src/components/accessories/currentAdmission/currentAdmissionForm/CurrentAdmissionForm.tsx @@ -0,0 +1,320 @@ +import { IconButton } from "@material-ui/core"; +import { Edit } from "@material-ui/icons"; +import { useFormik } from "formik"; +import get from "lodash.get"; +import has from "lodash.has"; +import React, { + FunctionComponent, + useCallback, + useEffect, + useState, +} from "react"; +import { useTranslation } from "react-i18next"; +import { useDispatch, useSelector } from "react-redux"; +import { + AdmissionTypeDTO, + DiseaseDTO, + DiseaseTypeDTO, + PatientDTO, + WardDTO, +} from "../../../../generated"; +import { + differenceInDays, + formatAllFieldValues, + getFromFields, + parseDate, +} from "../../../../libraries/formDataHandling/functions"; +import checkIcon from "../../../../assets/check-icon.png"; +import { + getPatientThunk, + updatePatient, + updatePatientReset, +} from "../../../../state/patients/actions"; +import { TAPIResponseStatus } from "../../../../state/types"; +import { IState } from "../../../../types"; +import Button from "../../button/Button"; +import InfoBox from "../../infoBox/InfoBox"; +import TextField from "../../textField/TextField"; +import { initialFields } from "./consts"; +import { IOwnProps, TActivityTransitionState } from "./types"; +import ConfirmationDialog from "../../confirmationDialog/ConfirmationDialog"; +import AutocompleteField from "../../autocompleteField/AutocompleteField"; +import DateField from "../../dateField/DateField"; +import { updateAdmissionReset } from "../../../../state/admissions/actions"; + +export const CurrentAdmissionForm: FunctionComponent = ({ + onDiscard, + onSubmit, + fields, +}) => { + const { t } = useTranslation(); + const dispatch = useDispatch(); + const [activityTransitionState, setActivityTransitionState] = + useState("IDLE"); + const patient = useSelector( + (state) => state.patients.selectedPatient.data + ); + const currentAdmission = useSelector( + (state: IState) => state.admissions.currentAdmissionByPatientId.data + ); + const status = useSelector( + (state) => state.admissions.updateAdmission.status + ); + + const errorMessage = useSelector( + (state) => + state.patients.updatePatient.error?.message || t("common.somethingwrong") + ); + + const diagnosisInList = useSelector( + (state: IState) => state.diseases.diseasesIpdIn.data + ); + + const admissionTypes = useSelector( + (state: IState) => state.admissionTypes.allAdmissionTypes.data + ); + const wards = useSelector((state: IState) => state.wards.allWards.data); + const diagnosisInStatus = useSelector( + (state: IState) => state.diseases.diseasesIpdIn.status + ); + const wardStatus = useSelector( + (state: IState) => state.wards.allWards.status + ); + const admTypeStatus = useSelector( + (state: IState) => state.admissionTypes.allAdmissionTypes.status + ); + + const renderOptions = ( + data: + | ( + | WardDTO + | DiseaseDTO + | AdmissionTypeDTO + | DiseaseTypeDTO + | DiseaseDTO + )[] + | undefined + ) => { + if (data) { + return data.map((item) => { + return { + value: item.code?.toString() ?? "", + label: item.description ?? "", + }; + }); + } else return []; + }; + + const formik = useFormik({ + initialValues: getFromFields(fields, "value"), + enableReinitialize: true, + onSubmit: (values) => { + const formattedValues = formatAllFieldValues( + initialFields(currentAdmission), + values + ); + formattedValues.diseaseIn = diagnosisInList?.find( + (item) => item.code === formattedValues.diseaseIn + ); + formattedValues.admType = admissionTypes?.find( + (item) => item.code === formattedValues.admType + ); + formattedValues.ward = wards?.find( + (item) => item.code === formattedValues.ward + ); + onSubmit({ + ...currentAdmission, + ...formattedValues, + } as any); + }, + }); + + useEffect(() => { + if (activityTransitionState === "TO_RESET") { + dispatch(updateAdmissionReset()); + if (patient?.code) { + dispatch(getPatientThunk(patient?.code?.toString())); + } + onDiscard(); + } + }, [dispatch, activityTransitionState]); + + const { setFieldValue, resetForm, handleBlur } = formik; + + const isValid = (fieldName: string): boolean => { + return has(formik.touched, fieldName) && has(formik.errors, fieldName); + }; + + const getErrorText = (fieldName: string): string => { + return has(formik.touched, fieldName) + ? (get(formik.errors, fieldName) as string) + : ""; + }; + + const dateFieldHandleOnChange = useCallback( + (fieldName: string) => (value: any) => { + setFieldValue(fieldName, value); + formik.setFieldTouched(fieldName); + const days = differenceInDays( + new Date(formik.values.admDate), + new Date(formik.values.disDate) + ).toString(); + setFieldValue("bedDays", days); + }, + [setFieldValue] + ); + + const onBlurCallback = useCallback( + (fieldName: string) => + (e: React.FocusEvent, value: string) => { + handleBlur(e); + setFieldValue(fieldName, value); + }, + [setFieldValue, handleBlur] + ); + + const isLoading = status === "LOADING"; + + return ( + <> +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+ {status === "FAIL" && ( +
+ +
+ )} + setActivityTransitionState("TO_RESET")} + handleSecondaryButtonClick={() => {}} + /> + + ); +}; diff --git a/src/components/accessories/currentAdmission/currentAdmissionForm/consts.ts b/src/components/accessories/currentAdmission/currentAdmissionForm/consts.ts new file mode 100644 index 000000000..7cde8d91c --- /dev/null +++ b/src/components/accessories/currentAdmission/currentAdmissionForm/consts.ts @@ -0,0 +1,50 @@ +import { AdmissionDTO, PatientDTO } from "../../../../generated"; +import { parseDate } from "../../../../libraries/formDataHandling/functions"; +import { TFields } from "../../../../libraries/formDataHandling/types"; +import { TCurrentAdmissionFieldName } from "./types"; + +export const initialFields = ( + patient: AdmissionDTO | undefined +): TFields => { + return { + ward: { + value: "", + type: "text", + options: [], + }, + transUnit: { + value: "10", + type: "number", + }, + fhu: { + value: "", + type: "text", + }, + admDate: { + value: parseDate(Date.now().toString()), + type: "date", + }, + admType: { + value: "", + type: "text", + options: [], + }, + diseaseIn: { + value: "", + type: "text", + options: [], + }, + note: { + value: "", + type: "text", + }, + cliDiaryCharge: { + value: "", + type: "text", + }, + imageryCharge: { + value: "", + type: "text", + }, + }; +}; diff --git a/src/components/accessories/currentAdmission/currentAdmissionForm/types.ts b/src/components/accessories/currentAdmission/currentAdmissionForm/types.ts new file mode 100644 index 000000000..ca7147d8a --- /dev/null +++ b/src/components/accessories/currentAdmission/currentAdmissionForm/types.ts @@ -0,0 +1,23 @@ +import { AdmissionDTO } from "../../../../generated"; +import { IForm, TFields } from "../../../../libraries/formDataHandling/types"; + +export type TProps = IForm; + +export type TCurrentAdmissionFieldName = + | "ward" + | "transUnit" + | "admDate" + | "admType" + | "diseaseIn" + | "fhu" + | "note" + | "cliDiaryCharge" + | "imageryCharge"; + +export type TActivityTransitionState = "IDLE" | "TO_RESET" | "FAIL"; + +export interface IOwnProps { + onDiscard: () => void; + fields: TFields; + onSubmit: (adm: AdmissionDTO) => void; +} diff --git a/src/components/accessories/currentAdmission/styles.scss b/src/components/accessories/currentAdmission/styles.scss new file mode 100644 index 000000000..e837206e2 --- /dev/null +++ b/src/components/accessories/currentAdmission/styles.scss @@ -0,0 +1,111 @@ +@import "../../../styles/variables"; +@import "../../../../node_modules/susy/sass/susy"; + +.currentAdmission { + flex-direction: column; + display: flex; + row-gap: 4px; + padding: 4px 16px; + margin-bottom: 16px; + border-radius: 5px; + .currentAdmissionData { + display: flex; + flex-direction: column; + padding: 16px; + border-radius: 4px; + row-gap: 4px; + background-color: rgba($c-grey-light, 0.2); + .currentAdmission_leading { + display: flex; + justify-content: end; + } + .currentAdmissionData__content { + display: flex; + flex-direction: column; + flex-wrap: wrap; + column-gap: 4px; + .currentAdmissionData__item { + display: flex; + flex-direction: column; + justify-content: start; + flex-grow: 0; + + &.fullWidth { + width: 100%; + } + } + .item_label { + font-size: 0.85em; + font-weight: 600; + } + } + } + .currentAdmissionForm { + display: flex; + flex-direction: column; + row-gap: 4px; + .currentAdmissionForm__item { + margin: 7px 0px; + padding: 0px 15px; + width: 50%; + @include susy-media($narrow) { + padding: 0px 10px; + } + @include susy-media($tablet_land) { + padding: 0px 10px; + } + @include susy-media($tablet_port) { + width: 50%; + } + @include susy-media($smartphone) { + width: 100%; + } + .dateField, + .textField, + .selectField { + width: 100%; + } + + &.fullWidth { + width: 100%; + } + + &.halfWidth { + width: 50%; + @include susy-media($smartphone) { + width: 100%; + } + } + + &.compressed { + .textField { + float: left; + width: 50%; + &:nth-of-type(1) { + padding-right: 10px; + } + } + } + } + .currentAdmissionForm__buttonSet { + display: flex; + margin-top: 10px; + flex-direction: row-reverse; + justify-content: end; + + .submit_button { + .MuiButton-label { + font-size: smaller; + letter-spacing: 1px; + font-weight: 600; + } + button { + @include susy-media($smartphone_small) { + width: 100%; + margin-top: 10px; + } + } + } + } + } +} diff --git a/src/components/accessories/currentAdmission/types.ts b/src/components/accessories/currentAdmission/types.ts new file mode 100644 index 000000000..a5a849d04 --- /dev/null +++ b/src/components/accessories/currentAdmission/types.ts @@ -0,0 +1,3 @@ +export interface IOwnProps { + onEditChange: (value: boolean) => void; +} From 36058439806740d6508284dc605f66271443f887 Mon Sep 17 00:00:00 2001 From: SteveGT96 Date: Mon, 18 Sep 2023 09:35:08 +0100 Subject: [PATCH 3/3] update: Update admission form and current admission component --- .../admission/PatientAdmission.tsx | 2 +- .../admission/admissionForm/AdmissionForm.tsx | 1 + .../CurrentAdmissionForm.tsx | 25 +------------------ .../currentAdmissionForm/consts.ts | 8 ------ .../currentAdmissionForm/types.ts | 4 +-- .../accessories/currentAdmission/styles.scss | 5 +++- src/mockServer/routes/admissions.js | 2 +- src/resources/i18n/en.json | 2 +- 8 files changed, 10 insertions(+), 39 deletions(-) diff --git a/src/components/accessories/admission/PatientAdmission.tsx b/src/components/accessories/admission/PatientAdmission.tsx index afdab3cbf..1c2a1ae5e 100644 --- a/src/components/accessories/admission/PatientAdmission.tsx +++ b/src/components/accessories/admission/PatientAdmission.tsx @@ -199,7 +199,7 @@ const PatientAdmission: FC = () => { {patient?.status === PatientDTOStatusEnum.I && ( )} - + {!open && } {open && ( = ({ formattedValues.admType = admissionTypes?.find( (item) => item.code === formattedValues.admType ); + formattedValues.type = formattedValues.admType?.code; formattedValues.ward = wards?.find( (item) => item.code === formattedValues.ward ); diff --git a/src/components/accessories/currentAdmission/currentAdmissionForm/CurrentAdmissionForm.tsx b/src/components/accessories/currentAdmission/currentAdmissionForm/CurrentAdmissionForm.tsx index ffab467e8..010ac8d3d 100644 --- a/src/components/accessories/currentAdmission/currentAdmissionForm/CurrentAdmissionForm.tsx +++ b/src/components/accessories/currentAdmission/currentAdmissionForm/CurrentAdmissionForm.tsx @@ -119,6 +119,7 @@ export const CurrentAdmissionForm: FunctionComponent = ({ formattedValues.admType = admissionTypes?.find( (item) => item.code === formattedValues.admType ); + formattedValues.type = formattedValues.admType?.code; formattedValues.ward = wards?.find( (item) => item.code === formattedValues.ward ); @@ -244,30 +245,6 @@ export const CurrentAdmissionForm: FunctionComponent = ({ disabled={isLoading} />
-
- -
-
- -
{ res.body = null; break; default: - res.status(200).json(admissionDTO); + res.status(200).json({ ...admissionDTO, id: 0 }); } }); server.post("/discharge").intercept((req, res) => { diff --git a/src/resources/i18n/en.json b/src/resources/i18n/en.json index ccc5dea76..fc15bbe40 100644 --- a/src/resources/i18n/en.json +++ b/src/resources/i18n/en.json @@ -583,7 +583,7 @@ "validatelastdate": "should be greater or equal to admission date {{admDate}}", "currentadmissionexists": "The patient has an ongoing admission dating from {{date}}. Discharge him first.", "patientnotadmitted": "No current admission found !", - "patientalreadyadmitted": "The patient has already been admitted, here you will find the history of admissions. To resign, go to the dismission page.", + "patientalreadyadmitted": "The patient has already been admitted, here you will find the history of admissions. To discharge, go to the Discharge page.", "updated": "Admission updated", "updatesuccess": "Admimission updated successfully", "datebefore": "Admission date must be before the discharge date",