From 35a2561d1bc8fac9516966a00fb69f95fe5cf4e7 Mon Sep 17 00:00:00 2001 From: rithviknishad Date: Mon, 8 Jul 2024 19:53:41 +0530 Subject: [PATCH 1/2] Refactor rendering of array of strings to utilise `humanizeStrings` utility method --- src/Common/hooks/useFilters.tsx | 7 ++----- src/Common/utils.tsx | 18 +++++++++-------- .../InvestigationBuilder.tsx | 7 ++++--- src/Components/DeathReport/DeathReport.tsx | 11 +++++++--- .../Facility/ConsultationDetails/index.tsx | 20 ++++++++----------- .../Facility/Consultations/Mews.tsx | 6 ++++-- .../Facility/DischargedPatientsList.tsx | 4 ++-- .../Medicine/MedicinePrescriptionSummary.tsx | 3 ++- src/Components/Patient/ManagePatients.tsx | 3 ++- src/Components/Patient/PatientInfoCard.tsx | 13 +++--------- src/Components/Patient/SampleDetails.tsx | 2 +- src/Components/Patient/SamplePreview.tsx | 5 +++-- src/Utils/utils.ts | 19 ++++++++++++++++++ 13 files changed, 68 insertions(+), 50 deletions(-) diff --git a/src/Common/hooks/useFilters.tsx b/src/Common/hooks/useFilters.tsx index b86206c4b2d..781761300dc 100644 --- a/src/Common/hooks/useFilters.tsx +++ b/src/Common/hooks/useFilters.tsx @@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next"; import GenericFilterBadge from "../../CAREUI/display/FilterBadge"; import PaginationComponent from "../../Components/Common/Pagination"; import useConfig from "./useConfig"; -import { classNames } from "../../Utils/utils"; +import { classNames, humanizeStrings } from "../../Utils/utils"; import FiltersCache from "../../Utils/FiltersCache"; export type FilterState = Record; @@ -87,10 +87,7 @@ export default function useFilters({ name={name} value={ value === undefined - ? paramKey - .map((k) => qParams[k]) - .filter(Boolean) - .join(", ") + ? humanizeStrings(paramKey.map((k) => qParams[k]).filter(Boolean)) : value } onRemove={() => removeFilters(paramKey)} diff --git a/src/Common/utils.tsx b/src/Common/utils.tsx index 05022d8bc2e..4e14e3bf5ba 100644 --- a/src/Common/utils.tsx +++ b/src/Common/utils.tsx @@ -1,6 +1,7 @@ /* eslint-disable react-hooks/exhaustive-deps */ import { useEffect } from "react"; import { OptionsType } from "./constants"; +import { humanizeStrings } from "../Utils/utils"; export interface statusType { aborted?: boolean; @@ -34,14 +35,15 @@ export const parseOptionId: ( options: readonly OptionsType[], id: string | string[], ) => string = (options, id) => { - const textArray = options - .filter((option) => { - return id instanceof Array - ? id.map((i) => String(i)).includes(String(option.id)) - : String(option.id) === String(id); - }) - .map((option) => option.text); - return textArray.join(", "); + return humanizeStrings( + options + .filter((option) => { + return id instanceof Array + ? id.map((i) => String(i)).includes(String(option.id)) + : String(option.id) === String(id); + }) + .map((option) => option.text), + ); }; export const deepEqual = (x: any, y: any): boolean => { diff --git a/src/Components/Common/prescription-builder/InvestigationBuilder.tsx b/src/Components/Common/prescription-builder/InvestigationBuilder.tsx index 6de2a83f1cb..df1eb1235e9 100644 --- a/src/Components/Common/prescription-builder/InvestigationBuilder.tsx +++ b/src/Components/Common/prescription-builder/InvestigationBuilder.tsx @@ -4,6 +4,7 @@ import { PrescriptionMultiDropdown } from "./PrescriptionMultiselect"; import CareIcon from "../../../CAREUI/icons/CareIcon"; import request from "../../../Utils/request/request"; import routes from "../../../Redux/api"; +import { humanizeStrings } from "../../../Utils/utils"; export type InvestigationType = { type?: string[]; @@ -86,9 +87,9 @@ export default function InvestigationBuilder( return ( data?.results.map( (investigation) => - `${investigation.name} -- ${investigation.groups - .map((group) => ` ( ${group.name} ) `) - .join(", ")}`, + `${investigation.name} -- ${humanizeStrings( + investigation.groups.map((group) => ` ( ${group.name} ) `), + )}`, ) ?? [] ); }; diff --git a/src/Components/DeathReport/DeathReport.tsx b/src/Components/DeathReport/DeathReport.tsx index e25a59c8732..6ed8652d22d 100644 --- a/src/Components/DeathReport/DeathReport.tsx +++ b/src/Components/DeathReport/DeathReport.tsx @@ -6,7 +6,11 @@ import TextFormField from "../Form/FormFields/TextFormField"; import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; import DateFormField from "../Form/FormFields/DateFormField"; import PhoneNumberFormField from "../Form/FormFields/PhoneNumberFormField"; -import { formatDateTime, patientAgeInYears } from "../../Utils/utils"; +import { + formatDateTime, + humanizeStrings, + patientAgeInYears, +} from "../../Utils/utils"; import Page from "../Common/components/Page"; import Form from "../Form/Form"; import { useTranslation } from "react-i18next"; @@ -89,8 +93,9 @@ export default function PrintDeathReport(props: { id: string }) { patientData.medical_history && patientData.medical_history.length ) { - const medHis = patientData.medical_history; - return medHis.map((item: any) => item.disease).join(", "); + return humanizeStrings( + patientData.medical_history.map((item: any) => item.disease), + ); } else { return "None"; } diff --git a/src/Components/Facility/ConsultationDetails/index.tsx b/src/Components/Facility/ConsultationDetails/index.tsx index e46db70189a..ed22df6be01 100644 --- a/src/Components/Facility/ConsultationDetails/index.tsx +++ b/src/Components/Facility/ConsultationDetails/index.tsx @@ -11,7 +11,11 @@ import { lazy, useCallback, useState } from "react"; import DoctorVideoSlideover from "../DoctorVideoSlideover"; import { make as Link } from "../../Common/components/Link.bs"; import { PatientModel } from "../../Patient/models"; -import { formatDateTime, relativeTime } from "../../../Utils/utils"; +import { + formatDateTime, + humanizeStrings, + relativeTime, +} from "../../../Utils/utils"; import { navigate, useQueryParams } from "raviger"; import { useDispatch } from "react-redux"; @@ -93,8 +97,9 @@ export const ConsultationDetails = (props: any) => { const getPatientComorbidities = (patientData: any) => { if (patientData?.medical_history?.length) { - const medHis = patientData.medical_history; - return medHis.map((item: any) => item.disease).join(", "); + return humanizeStrings( + patientData.medical_history.map((item: any) => item.disease), + ); } else { return "None"; } @@ -113,15 +118,6 @@ export const ConsultationDetails = (props: any) => { ...res.data, symptoms_text: "", }; - // if (res.data.symptoms?.length) { - // const symptoms = res.data.symptoms - // .filter((symptom: number) => symptom !== 9) - // .map((symptom: number) => { - // const option = symptomChoices.find((i) => i.id === symptom); - // return option ? option.text.toLowerCase() : symptom; - // }); - // data.symptoms_text = symptoms.join(", "); - // } if (facilityId != data.facility || patientId != data.patient) { navigate( `/facility/${data.facility}/patient/${data.patient}/consultation/${data?.id}`, diff --git a/src/Components/Facility/Consultations/Mews.tsx b/src/Components/Facility/Consultations/Mews.tsx index 4925d2ae998..d99f85d9065 100644 --- a/src/Components/Facility/Consultations/Mews.tsx +++ b/src/Components/Facility/Consultations/Mews.tsx @@ -1,5 +1,5 @@ import { DailyRoundsModel } from "../../Patient/models"; -import { formatDateTime } from "../../../Utils/utils"; +import { formatDateTime, humanizeStrings } from "../../../Utils/utils"; const getRespScore = (value?: number) => { if (typeof value !== "number") return; @@ -78,7 +78,9 @@ export const Mews = ({ dailyRound }: { dailyRound: DailyRoundsModel }) => { MEWS
- {(data as string[]).join(", ")}{" "} + + {humanizeStrings(data as string[])} + {" "} data is missing from the last log update.
Last Updated: {formatDateTime(dailyRound.modified_date)}
diff --git a/src/Components/Facility/DischargedPatientsList.tsx b/src/Components/Facility/DischargedPatientsList.tsx index 6878613d4cd..61c9eed3fe4 100644 --- a/src/Components/Facility/DischargedPatientsList.tsx +++ b/src/Components/Facility/DischargedPatientsList.tsx @@ -15,7 +15,7 @@ import { } from "../../Common/constants"; import CareIcon from "../../CAREUI/icons/CareIcon"; import RecordMeta from "../../CAREUI/display/RecordMeta"; -import { formatPatientAge } from "../../Utils/utils"; +import { formatPatientAge, humanizeStrings } from "../../Utils/utils"; import { useTranslation } from "react-i18next"; import SwitchTabs from "../Common/components/SwitchTabs"; import SortDropdownMenu from "../Common/SortDropdown"; @@ -360,7 +360,7 @@ const DischargedPatientsList = ({ value( DIAGNOSES_FILTER_LABELS[key], key, - getDiagnosisFilterValue(key).join(", "), + humanizeStrings(getDiagnosisFilterValue(key)), ), ), badge("Declared Status", "is_declared_positive"), diff --git a/src/Components/Medicine/MedicinePrescriptionSummary.tsx b/src/Components/Medicine/MedicinePrescriptionSummary.tsx index b77acc090c4..4a476ceed1b 100644 --- a/src/Components/Medicine/MedicinePrescriptionSummary.tsx +++ b/src/Components/Medicine/MedicinePrescriptionSummary.tsx @@ -6,6 +6,7 @@ import { lazy } from "react"; import Timeline, { TimelineNode } from "../../CAREUI/display/Timeline"; import { MedibaseMedicine, Prescription } from "../Medicine/models"; import { useTranslation } from "react-i18next"; +import { humanizeStrings } from "../../Utils/utils"; const Loading = lazy(() => import("../Common/Loading")); @@ -310,7 +311,7 @@ export default function ConsultationMedicineLogs({ // If there are changes, add them to the changes array if (changesForPrescription.length > 0 && !prevPrescription.discontinued) { - const message = `Changes: ${changesForPrescription.join(", ")}`; + const message = `Changes: ${humanizeStrings(changesForPrescription)}`; changes.push({ prescriptionId: currentPrescription.id, changeMessage: message, diff --git a/src/Components/Patient/ManagePatients.tsx b/src/Components/Patient/ManagePatients.tsx index c02efc5c737..d79623d4b03 100644 --- a/src/Components/Patient/ManagePatients.tsx +++ b/src/Components/Patient/ManagePatients.tsx @@ -33,6 +33,7 @@ import SortDropdownMenu from "../Common/SortDropdown"; import SwitchTabs from "../Common/components/SwitchTabs"; import { formatPatientAge, + humanizeStrings, isAntenatal, parsePhoneNumber, } from "../../Utils/utils.js"; @@ -1027,7 +1028,7 @@ export const PatientManager = () => { value( DIAGNOSES_FILTER_LABELS[key], key, - getDiagnosisFilterValue(key).join(", "), + humanizeStrings(getDiagnosisFilterValue(key)), ), ), badge("Declared Status", "is_declared_positive"), diff --git a/src/Components/Patient/PatientInfoCard.tsx b/src/Components/Patient/PatientInfoCard.tsx index a43d49a26ac..0affa2a952e 100644 --- a/src/Components/Patient/PatientInfoCard.tsx +++ b/src/Components/Patient/PatientInfoCard.tsx @@ -19,6 +19,7 @@ import { formatDate, formatDateTime, formatPatientAge, + humanizeStrings, } from "../../Utils/utils.js"; import ABHAProfileModal from "../ABDM/ABHAProfileModal.js"; import LinkABHANumberModal from "../ABDM/LinkABHANumberModal.js"; @@ -45,16 +46,8 @@ import { AuthorizedForConsultationRelatedActions } from "../../CAREUI/misc/Autho const formatSkills = (arr: SkillModel[]) => { const skills = arr.map((skill) => skill.skill_object.name); - if (skills.length === 1) { - return skills[0]; - } - - if (skills.length === 2) { - return `${skills[0]} and ${skills[1]}`; - } - - if (skills.length === 3) { - return `${skills[0]}, ${skills[1]} and ${skills[2]}`; + if (skills.length <= 3) { + return humanizeStrings(skills); } return `${skills[0]}, ${skills[1]} and ${skills.length - 2} other skills...`; diff --git a/src/Components/Patient/SampleDetails.tsx b/src/Components/Patient/SampleDetails.tsx index a3d3df95c21..91fd2d4a122 100644 --- a/src/Components/Patient/SampleDetails.tsx +++ b/src/Components/Patient/SampleDetails.tsx @@ -409,7 +409,7 @@ export const SampleDetails = ({ id }: DetailRoute) => { Countries travelled:{" "} - {sampleDetails.patient_travel_history.join(", ")} + {sampleDetails.patient_travel_history} )} {sampleDetails?.sample_type && ( diff --git a/src/Components/Patient/SamplePreview.tsx b/src/Components/Patient/SamplePreview.tsx index a01453e1ed5..991ffba5ce1 100644 --- a/src/Components/Patient/SamplePreview.tsx +++ b/src/Components/Patient/SamplePreview.tsx @@ -1,4 +1,4 @@ -import { classNames, formatDateTime } from "../../Utils/utils"; +import { classNames, formatDateTime, humanizeStrings } from "../../Utils/utils"; import { lazy } from "react"; @@ -331,7 +331,8 @@ export default function SampleReport(props: ISamplePreviewProps) { { title: "Symptoms", value: - sampleData?.medical_conditions?.symptoms?.join(", "), + sampleData?.medical_conditions?.symptoms && + humanizeStrings(sampleData.medical_conditions.symptoms), }, { title: "First Symptom", value: "............." }, { diff --git a/src/Utils/utils.ts b/src/Utils/utils.ts index d599a494b1c..5a677ea6ef3 100644 --- a/src/Utils/utils.ts +++ b/src/Utils/utils.ts @@ -454,3 +454,22 @@ export const isPostPartum = (data_of_delivery?: string) => { export const isAntenatal = (menstruation_start_date?: string) => { return dayjs().diff(menstruation_start_date, "month") <= 9; }; + +/** + * A utility method to format an array of string to human readable format. + * + * @param values Array of strings to be made human readable. + * @returns Human readable version of the list of strings + */ +export const humanizeStrings = (strings: readonly string[]) => { + if (strings.length === 0) { + throw "Empty array of strings cannot be humanized. Array must contain one or more elements"; + } + + if (strings.length === 1) { + return strings[0]; + } + + const [last, ...items] = [...strings].reverse(); + return `${items.reverse().join(", ")} and ${last}`; +}; From 8dd54617dc7c1f1135acef9a9edeaab9a715c536 Mon Sep 17 00:00:00 2001 From: rithviknishad Date: Wed, 10 Jul 2024 09:43:41 +0530 Subject: [PATCH 2/2] handle empty arrays gracefully --- src/Utils/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Utils/utils.ts b/src/Utils/utils.ts index 5a677ea6ef3..60c49da0dc9 100644 --- a/src/Utils/utils.ts +++ b/src/Utils/utils.ts @@ -461,9 +461,9 @@ export const isAntenatal = (menstruation_start_date?: string) => { * @param values Array of strings to be made human readable. * @returns Human readable version of the list of strings */ -export const humanizeStrings = (strings: readonly string[]) => { +export const humanizeStrings = (strings: readonly string[], empty = "") => { if (strings.length === 0) { - throw "Empty array of strings cannot be humanized. Array must contain one or more elements"; + return empty; } if (strings.length === 1) {