Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added consent indicators and filters #8003

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/Common/hooks/useFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,17 @@ export default function useFilters({
return acc;
}, [] as string[]);

const show = activeFilters.length > 0 || children;

return (
<div className="col-span-3 my-2 flex w-full flex-wrap items-center gap-2">
<div
className={`col-span-3 my-2 flex w-full flex-wrap items-center gap-2 ${show ? "" : "hidden"}`}
>
{compiledBadges.map((props) => (
<FilterBadge {...props} name={t(props.name)} key={props.name} />
))}
{children}
{(activeFilters.length >= 1 || children) && (
{show && (
<button
id="clear-all-filters"
className="rounded-full border border-gray-300 bg-white px-2 py-1 text-xs text-gray-600 hover:text-gray-800"
Expand Down
42 changes: 40 additions & 2 deletions src/Components/Facility/DischargedPatientsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import useQuery from "../../Utils/request/useQuery";
import SearchInput from "../Form/SearchInput";
import {
ADMITTED_TO,
CONSENT_TYPE_CHOICES,
DISCHARGED_PATIENT_SORT_OPTIONS,
DISCHARGE_REASONS,
GENDER_TYPES,
Expand Down Expand Up @@ -160,6 +161,39 @@ const DischargedPatientsList = ({
});
};

const HasConsentTypesBadges = () => {
const badge = (key: string, value: any, id: string) => {
return (
value && (
<FilterBadge
name={key}
value={value}
onRemove={() => {
const lcat = qParams.last_consultation__consent_types
.split(",")
.filter((x: string) => x != id)
.join(",");
updateQuery({
...qParams,
last_consultation__consent_types: lcat,
});
}}
/>
)
);
};

return qParams.last_consultation__consent_types
.split(",")
.map((id: string) => {
const text = [
...CONSENT_TYPE_CHOICES,
{ id: "None", text: "No Consents" },
].find((obj) => obj.id == id)?.text;
return badge("Has Consent", text, id);
});
};

const queryField = <T,>(name: string, defaultValue?: T) => {
return {
name,
Expand Down Expand Up @@ -385,8 +419,12 @@ const DischargedPatientsList = ({
),
]}
children={
qParams.last_consultation_admitted_bed_type_list &&
LastAdmittedToTypeBadges()
<>
{qParams.last_consultation_admitted_bed_type_list &&
LastAdmittedToTypeBadges()}
{qParams.last_consultation__consent_types &&
HasConsentTypesBadges()}
</>
}
/>
</div>
Expand Down
8 changes: 7 additions & 1 deletion src/Components/Facility/models.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import { InvestigationType } from "../Common/prescription-builder/InvestigationB
import { ProcedureType } from "../Common/prescription-builder/ProcedureBuilder";
import { ConsultationDiagnosis, CreateDiagnosis } from "../Diagnosis/types";
import { NormalPrescription, PRNPrescription } from "../Medicine/models";
import { AssignedToObjectModel, DailyRoundsModel } from "../Patient/models";
import {
AssignedToObjectModel,
DailyRoundsModel,
FileUploadModel,
} from "../Patient/models";
import { EncounterSymptom } from "../Symptoms/types";
import { UserBareMinimum } from "../Users/models";

Expand Down Expand Up @@ -105,6 +109,7 @@ export interface PatientConsentModel {
patient_code_status:
| (typeof CONSENT_PATIENT_CODE_STATUS_CHOICES)[number]["id"]
| null;
files: FileUploadModel[] | null;
archived: boolean;
archived_by?: UserBareMinimum;
archived_date: string;
Expand Down Expand Up @@ -180,6 +185,7 @@ export interface ConsultationModel {
is_readmission?: boolean;
medico_legal_case?: boolean;
investigation?: InvestigationType[];
has_consents?: boolean;
}

export interface PatientStatsModel {
Expand Down
92 changes: 89 additions & 3 deletions src/Components/Patient/ManagePatients.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as Notification from "../../Utils/Notifications.js";

import {
ADMITTED_TO,
CONSENT_TYPE_CHOICES,
DISCHARGE_REASONS,
GENDER_TYPES,
PATIENT_CATEGORIES,
Expand Down Expand Up @@ -194,6 +195,8 @@ export const PatientManager = () => {
qParams.last_consultation_discharge_date_after || undefined,
last_consultation_admitted_bed_type_list:
qParams.last_consultation_admitted_bed_type_list || undefined,
last_consultation__consent_types:
qParams.last_consultation__consent_types || undefined,
last_consultation__new_discharge_reason:
qParams.last_consultation__new_discharge_reason || undefined,
last_consultation_current_bed__location:
Expand Down Expand Up @@ -395,6 +398,17 @@ export const PatientManager = () => {
},
);

const { data: patientsWithNoConsentsData } = useQuery(routes.patientList, {
query: {
...qParams,
limit: 1,
last_consultation__consent_types: "None",
is_active: "True",
},
});

const patientsWithNoConsents = patientsWithNoConsentsData?.count;

const { data: permittedFacilities } = useQuery(
routes.getPermittedFacilities,
{
Expand Down Expand Up @@ -431,6 +445,39 @@ export const PatientManager = () => {
});
};

const HasConsentTypesBadges = () => {
const badge = (key: string, value: any, id: string) => {
return (
value && (
<FilterBadge
name={key}
value={value}
onRemove={() => {
const lcat = qParams.last_consultation__consent_types
.split(",")
.filter((x: string) => x != id)
.join(",");
updateQuery({
...qParams,
last_consultation__consent_types: lcat,
});
}}
/>
)
);
};

return qParams.last_consultation__consent_types
.split(",")
.map((id: string) => {
const text = [
...CONSENT_TYPE_CHOICES,
{ id: "None", text: "No Consents" },
].find((obj) => obj.id == id)?.text;
return badge("Has Consent", text, id);
});
};

const getDiagnosisFilterValue = (key: DiagnosesFilterKey) => {
const ids: string[] = (qParams[key] ?? "").split(",");
return ids.map((id) => diagnoses.find((obj) => obj.id == id)?.label ?? id);
Expand Down Expand Up @@ -657,6 +704,20 @@ export const PatientManager = () => {
</span>
</span>
)}
{patient.last_consultation?.has_consents || (
<span className="relative inline-flex">
<Chip
size="small"
variant="danger"
startIcon="l-file"
text="No consents recorded"
/>
<span className="absolute -right-1 -top-1 flex h-3 w-3 items-center justify-center">
<span className="center absolute inline-flex h-4 w-4 animate-ping rounded-full bg-red-400"></span>
<span className="relative inline-flex h-3 w-3 rounded-full bg-red-600"></span>
</span>
</span>
)}
</div>
</div>
</div>
Expand Down Expand Up @@ -944,7 +1005,25 @@ export const PatientManager = () => {
</div>
</div>
</div>
<div className="col-span-3 mt-6 flex flex-wrap">
{!qParams.last_consultation__consent_types &&
(patientsWithNoConsents || 0) > 0 && (
<div className="flex w-full items-center gap-4 rounded-lg bg-red-500/10 p-4 text-sm text-red-500">
<CareIcon icon="l-info-circle" className="text-xl" />
<p className="font-semibold">
{patientsWithNoConsents} patients admitted missing consent
records&nbsp;
<button
onClick={() =>
updateQuery({ last_consultation__consent_types: "None" })
}
className="underline"
>
Click to view
</button>
</p>
</div>
)}
<div className="col-span-3 flex flex-wrap">
<FilterBadges
badges={({
badge,
Expand Down Expand Up @@ -1051,8 +1130,15 @@ export const PatientManager = () => {
),
]}
children={
qParams.last_consultation_admitted_bed_type_list &&
LastAdmittedToTypeBadges()
(qParams.last_consultation_admitted_bed_type_list ||
qParams.last_consultation__consent_types) && (
<>
{qParams.last_consultation_admitted_bed_type_list &&
LastAdmittedToTypeBadges()}
{qParams.last_consultation__consent_types &&
HasConsentTypesBadges()}
</>
)
}
/>
</div>
Expand Down
61 changes: 11 additions & 50 deletions src/Components/Patient/PatientConsentRecords.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useState } from "react";
import {
CONSENT_PATIENT_CODE_STATUS_CHOICES,
CONSENT_TYPE_CHOICES,
Expand Down Expand Up @@ -34,12 +34,6 @@ export default function PatientConsentRecords(props: {
patient_code_status: 4,
});

const refetchAll = () => {
refetch();
refetchFiles();
refetchArchivedFiles();
};

const fileUpload = useFileUpload({
type: "CONSENT_RECORD",
allowedExtensions: ["pdf", "jpg", "jpeg", "png"],
Expand All @@ -48,10 +42,10 @@ export default function PatientConsentRecords(props: {
const fileManager = useFileManager({
type: "CONSENT_RECORD",
onArchive: async () => {
refetchAll();
refetch();
},
onEdit: async () => {
refetchAll();
refetch();
},
});

Expand All @@ -73,36 +67,6 @@ export default function PatientConsentRecords(props: {

const consentRecords = consentRecordsData?.results;

const { data: unarchivedFiles, refetch: refetchFiles } = useQuery(
routes.viewUpload,
{
query: {
file_type: "CONSENT_RECORD",
associating_id: consentRecords?.map((cr) => cr.id).join(","),
limit: 1000,
offset: 0,
is_archived: false,
},
prefetch: (consentRecords?.length || 0) > 0 && showArchived,
},
);

const { data: archivedFiles, refetch: refetchArchivedFiles } = useQuery(
routes.viewUpload,
{
query: {
file_type: "CONSENT_RECORD",
associating_id: consentRecords?.map((cr) => cr.id).join(","),
limit: 1000,
offset: 0,
is_archived: true,
},
prefetch: (consentRecords?.length || 0) > 0 && !showArchived,
},
);

const files = showArchived ? archivedFiles : unarchivedFiles;

const selectField = (name: string) => {
return {
name,
Expand Down Expand Up @@ -135,13 +99,6 @@ export default function PatientConsentRecords(props: {
refetch();
};

useEffect(() => {
if (consentRecords && consentRecords.length > 0) {
refetchFiles();
refetchArchivedFiles();
}
}, [consentRecords]);

return (
<Page
title={"Patient Consent Records"}
Expand Down Expand Up @@ -280,13 +237,15 @@ export default function PatientConsentRecords(props: {
<div className="flex-1">
{consentRecords?.filter(
(r) =>
files?.results.filter((f) => f.associating_id === r.id).length,
r.files?.filter(
(f) =>
f.associating_id === r.id && f.is_archived === showArchived,
).length,
).length === 0 ? (
<div className="flex h-32 items-center justify-center text-gray-500">
No consent records found
</div>
) : (
(!unarchivedFiles || !archivedFiles) &&
!consentRecords && (
<div className="skeleton-animate-alpha h-32 rounded-lg" />
)
Expand All @@ -301,8 +260,10 @@ export default function PatientConsentRecords(props: {
archiveFile={fileManager.archiveFile}
editFile={fileManager.editFile}
showArchive={showArchived}
files={files?.results.filter(
(f) => f.associating_id === record.id,
files={record.files?.filter(
(f) =>
f.associating_id === record.id &&
showArchived === f.is_archived,
)}
/>
))}
Expand Down
Loading
Loading