Skip to content

Commit

Permalink
Merge branch 'develop' into hideAssetType#6789
Browse files Browse the repository at this point in the history
  • Loading branch information
shyamprakash123 committed Feb 4, 2024
2 parents 2fbba6f + 4e53a3e commit e7299ff
Show file tree
Hide file tree
Showing 13 changed files with 255 additions and 17 deletions.
2 changes: 1 addition & 1 deletion netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ status = 200
X-Content-Type-Options = "nosniff"
Content-Security-Policy = '''
default-src 'self';
script-src 'self' 'nonce-f51b9742' https://plausible.10bedicu.in;
script-src 'self' blob: 'nonce-f51b9742' https://plausible.10bedicu.in;
style-src 'self' 'unsafe-inline';
connect-src *;
img-src 'self' blob: data: https://cdn.coronasafe.network https://egov-s3-facility-10bedicu.s3.amazonaws.com https://egov-s3-patient-data-10bedicu.s3.amazonaws.com;
Expand Down
17 changes: 9 additions & 8 deletions src/Common/hooks/useAsyncOptions.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { debounce } from "lodash-es";
import { useMemo, useState } from "react";
import { useDispatch } from "react-redux";
import { mergeQueryOptions } from "../../Utils/utils";

interface IUseAsyncOptionsArgs {
debounceInterval?: number;
queryResponseExtractor?: (data: any) => any;
}

/**
* Deprecated. This is no longer needed as `useQuery` with `mergeQueryOptions`
* can be reused for this.
*
* Hook to implement async autocompletes with ease and typesafety.
*
* See `DiagnosisSelectFormField` for usage.
Expand Down Expand Up @@ -51,14 +55,11 @@ export function useAsyncOptions<T extends Record<string, unknown>>(
);

const mergeValueWithQueryOptions = (selected?: T[]) => {
if (!selected?.length) return queryOptions;

return [
...selected,
...queryOptions.filter(
(option) => !selected.find((s) => s[uniqueKey] === option[uniqueKey])
),
];
return mergeQueryOptions(
selected ?? [],
queryOptions,
(obj) => obj[uniqueKey]
);
};

return {
Expand Down
17 changes: 14 additions & 3 deletions src/Components/ABDM/LinkABHANumberModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import TextFormField from "../Form/FormFields/TextFormField";
import { classNames } from "../../Utils/utils";
import request from "../../Utils/request/request";
import routes from "../../Redux/api";
import { ABDMError } from "./models";
import { ABDMError, ABHAQRContent } from "./models";

export const validateRule = (
condition: boolean,
Expand Down Expand Up @@ -188,9 +188,20 @@ const ScanABHAQRSection = ({
setIsLoading(true);

try {
const abha = JSON.parse(value);
const abha = JSON.parse(value) as ABHAQRContent;

const { res, data } = await request(routes.abha.linkViaQR, {
body: { ...abha, patientId },
body: {
patientId,
hidn: abha?.hidn,
phr: abha?.hid,
name: abha?.name,
gender: abha?.gender,
dob: abha?.dob.replace(/\//g, "-"),
address: abha?.address,
"dist name": abha?.district_name,
"state name": abha?.["state name"],
},
});

if (res?.status === 200 || res?.status === 202) {
Expand Down
26 changes: 26 additions & 0 deletions src/Components/ABDM/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,29 @@ export interface IcreateHealthFacilityTBody {
export interface IpartialUpdateHealthFacilityTBody {
hf_id: string;
}

export interface ILinkViaQRBody {
hidn: string;
phr: string;
name: string;
gender: "M" | "F" | "O";
dob: string;
address?: string;
"dist name"?: string;
"state name"?: string;
patientId?: string;
}

export interface ABHAQRContent {
address: string;
distlgd: string;
district_name: string;
dob: string;
gender: "M";
hid: string;
hidn: string;
mobile: string;
name: string;
"state name": string;
statelgd: string;
}
13 changes: 13 additions & 0 deletions src/Components/Diagnosis/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import routes from "../../Redux/api";
import request from "../../Utils/request/request";
import { ICD11DiagnosisModel } from "./types";

// TODO: cache ICD11 responses and hit the cache if present instead of making an API call.

export const getDiagnosisById = async (id: ICD11DiagnosisModel["id"]) => {
return (await request(routes.getICD11Diagnosis, { pathParams: { id } })).data;
};

export const getDiagnosesByIds = async (ids: ICD11DiagnosisModel["id"][]) => {
return Promise.all([...new Set(ids)].map(getDiagnosisById));
};
81 changes: 81 additions & 0 deletions src/Components/Patient/DiagnosesFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { useEffect, useState } from "react";
import { ICD11DiagnosisModel } from "../Diagnosis/types";
import { getDiagnosesByIds } from "../Diagnosis/utils";
import { useTranslation } from "react-i18next";
import AutocompleteMultiSelectFormField from "../Form/FormFields/AutocompleteMultiselect";
import useQuery from "../../Utils/request/useQuery";
import routes from "../../Redux/api";
import { mergeQueryOptions } from "../../Utils/utils";
import { debounce } from "lodash-es";

export const FILTER_BY_DIAGNOSES_KEYS = [
"diagnoses",
"diagnoses_confirmed",
"diagnoses_unconfirmed",
"diagnoses_provisional",
"diagnoses_differential",
] as const;

export const DIAGNOSES_FILTER_LABELS = {
diagnoses: "Diagnoses (of any verification status)",
diagnoses_unconfirmed: "Unconfirmed Diagnoses",
diagnoses_provisional: "Provisional Diagnoses",
diagnoses_differential: "Differential Diagnoses",
diagnoses_confirmed: "Confirmed Diagnoses",
} as const;

export type DiagnosesFilterKey = (typeof FILTER_BY_DIAGNOSES_KEYS)[number];

interface Props {
name: DiagnosesFilterKey;
value?: string;
onChange: (event: { name: DiagnosesFilterKey; value: string }) => void;
}
export default function DiagnosesFilter(props: Props) {
const { t } = useTranslation();
const [diagnoses, setDiagnoses] = useState<ICD11DiagnosisModel[]>([]);
const { data, loading, refetch } = useQuery(routes.listICD11Diagnosis);

useEffect(() => {
if (!props.value) {
setDiagnoses([]);
return;
}
if (diagnoses.map((d) => d.id).join(",") === props.value) {
return;
}

// Re-use the objects which we already have, fetch the rest.
const ids = props.value.split(",");
const existing = diagnoses.filter(({ id }) => ids.includes(id));
const objIds = existing.map((o) => o.id);
const diagnosesToBeFetched = ids.filter((id) => !objIds.includes(id));
getDiagnosesByIds(diagnosesToBeFetched).then((data) => {
const retrieved = data.filter(Boolean) as ICD11DiagnosisModel[];
setDiagnoses([...existing, ...retrieved]);
});
}, [props.value]);

return (
<AutocompleteMultiSelectFormField
label={DIAGNOSES_FILTER_LABELS[props.name]}
labelClassName="text-sm"
name="icd11_search"
className="w-full"
placeholder={t("search_icd11_placeholder")}
value={diagnoses}
onChange={(e) => {
setDiagnoses(e.value);
props.onChange({
name: props.name,
value: e.value.map((o) => o.id).join(","),
});
}}
options={mergeQueryOptions(diagnoses, data ?? [], (obj) => obj.id)}
optionLabel={(option) => option.label}
optionValue={(option) => option}
onQuery={debounce((query: string) => refetch({ query: { query } }), 300)}
isLoading={loading}
/>
);
}
50 changes: 50 additions & 0 deletions src/Components/Patient/ManagePatients.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ import { triggerGoal } from "../../Integrations/Plausible.js";
import useAuthUser from "../../Common/hooks/useAuthUser.js";
import useQuery from "../../Utils/request/useQuery.js";
import routes from "../../Redux/api.js";
import {
DIAGNOSES_FILTER_LABELS,
DiagnosesFilterKey,
FILTER_BY_DIAGNOSES_KEYS,
} from "./DiagnosesFilter.js";
import { ICD11DiagnosisModel } from "../Diagnosis/types.js";
import { getDiagnosesByIds } from "../Diagnosis/utils.js";

const Loading = lazy(() => import("../Common/Loading"));

Expand Down Expand Up @@ -110,6 +117,7 @@ export const PatientManager = () => {
name: "",
});
const authUser = useAuthUser();
const [diagnoses, setDiagnoses] = useState<ICD11DiagnosisModel[]>([]);
const [showDialog, setShowDialog] = useState(false);
const [showDoctors, setShowDoctors] = useState(false);
const [showDoctorConnect, setShowDoctorConnect] = useState(false);
Expand Down Expand Up @@ -231,8 +239,33 @@ export const PatientManager = () => {
qParams.last_consultation_is_telemedicine || undefined,
is_antenatal: qParams.is_antenatal || undefined,
ventilator_interface: qParams.ventilator_interface || undefined,
diagnoses: qParams.diagnoses || undefined,
diagnoses_confirmed: qParams.diagnoses_confirmed || undefined,
diagnoses_provisional: qParams.diagnoses_provisional || undefined,
diagnoses_unconfirmed: qParams.diagnoses_unconfirmed || undefined,
diagnoses_differential: qParams.diagnoses_differential || undefined,
};

useEffect(() => {
const ids: string[] = [];
FILTER_BY_DIAGNOSES_KEYS.forEach((key) => {
ids.push(...(qParams[key] ?? "").split(",").filter(Boolean));
});
const existing = diagnoses.filter(({ id }) => ids.includes(id));
const objIds = existing.map((o) => o.id);
const diagnosesToBeFetched = ids.filter((id) => !objIds.includes(id));
getDiagnosesByIds(diagnosesToBeFetched).then((data) => {
const retrieved = data.filter(Boolean) as ICD11DiagnosisModel[];
setDiagnoses([...existing, ...retrieved]);
});
}, [
qParams.diagnoses,
qParams.diagnoses_confirmed,
qParams.diagnoses_provisional,
qParams.diagnoses_unconfirmed,
qParams.diagnoses_differential,
]);

useEffect(() => {
if (params.facility) {
setShowDoctorConnect(true);
Expand Down Expand Up @@ -395,6 +428,11 @@ export const PatientManager = () => {
qParams.last_consultation_is_telemedicine,
qParams.is_antenatal,
qParams.ventilator_interface,
qParams.diagnoses,
qParams.diagnoses_confirmed,
qParams.diagnoses_provisional,
qParams.diagnoses_unconfirmed,
qParams.diagnoses_differential,
]);

const getTheCategoryFromId = () => {
Expand Down Expand Up @@ -520,6 +558,11 @@ export const PatientManager = () => {
});
};

const getDiagnosisFilterValue = (key: DiagnosesFilterKey) => {
const ids: string[] = (qParams[key] ?? "").split(",");
return ids.map((id) => diagnoses.find((obj) => obj.id == id)?.label ?? id);
};

let patientList: ReactNode[] = [];
if (data && data.length) {
patientList = data.map((patient: any) => {
Expand Down Expand Up @@ -1025,6 +1068,13 @@ export const PatientManager = () => {
...range("Age", "age"),
badge("SRF ID", "srf_id"),
{ name: "LSG Body", value: localbodyName, paramKey: "lsgBody" },
...FILTER_BY_DIAGNOSES_KEYS.map((key) =>
value(
DIAGNOSES_FILTER_LABELS[key],
key,
getDiagnosisFilterValue(key).join(", ")
)
),
badge("Declared Status", "is_declared_positive"),
...dateRange("Result", "date_of_result"),
...dateRange("Declared positive", "date_declared_positive"),
Expand Down
34 changes: 34 additions & 0 deletions src/Components/Patient/PatientFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
} from "../Form/FormFields/Utils";
import MultiSelectMenuV2 from "../Form/MultiSelectMenuV2";
import SelectMenuV2 from "../Form/SelectMenuV2";
import DiagnosesFilter, { FILTER_BY_DIAGNOSES_KEYS } from "./DiagnosesFilter";

const getDate = (value: any) =>
value && dayjs(value).isValid() && dayjs(value).toDate();
Expand Down Expand Up @@ -98,6 +99,11 @@ export default function PatientFilter(props: any) {
filter.last_consultation_is_telemedicine || null,
is_antenatal: filter.is_antenatal || null,
ventilator_interface: filter.ventilator_interface || null,
diagnoses: filter.diagnoses || null,
diagnoses_confirmed: filter.diagnoses_confirmed || null,
diagnoses_provisional: filter.diagnoses_provisional || null,
diagnoses_unconfirmed: filter.diagnoses_unconfirmed || null,
diagnoses_differential: filter.diagnoses_differential || null,
});
const dispatch: any = useDispatch();

Expand Down Expand Up @@ -211,6 +217,11 @@ export default function PatientFilter(props: any) {
last_consultation_is_telemedicine,
is_antenatal,
ventilator_interface,
diagnoses,
diagnoses_confirmed,
diagnoses_provisional,
diagnoses_unconfirmed,
diagnoses_differential,
} = filterState;
const data = {
district: district || "",
Expand Down Expand Up @@ -273,6 +284,11 @@ export default function PatientFilter(props: any) {
last_consultation_is_telemedicine || "",
is_antenatal: is_antenatal || "",
ventilator_interface: ventilator_interface || "",
diagnoses: diagnoses || "",
diagnoses_confirmed: diagnoses_confirmed || "",
diagnoses_provisional: diagnoses_provisional || "",
diagnoses_unconfirmed: diagnoses_unconfirmed || "",
diagnoses_differential: diagnoses_differential || "",
};
onChange(data);
};
Expand Down Expand Up @@ -459,6 +475,24 @@ export default function PatientFilter(props: any) {
</div>
</div>
</AccordionV2>
<AccordionV2
title={
<h1 className="mb-4 text-left text-xl font-bold text-purple-500">
ICD-11 Diagnoses based
</h1>
}
expanded
className="w-full"
>
{FILTER_BY_DIAGNOSES_KEYS.map((name) => (
<DiagnosesFilter
key={name}
name={name}
value={filterState[name]}
onChange={handleFormFieldChange}
/>
))}
</AccordionV2>
<AccordionV2
title={
<h1 className="mb-4 text-left text-xl font-bold text-purple-500">
Expand Down
Loading

0 comments on commit e7299ff

Please sign in to comment.