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

Encounter Info Card #10792

Merged
merged 9 commits into from
Feb 27, 2025
80 changes: 59 additions & 21 deletions src/components/Patient/EncounterQuestionnaire.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { useQuery } from "@tanstack/react-query";
import { t } from "i18next";
import { navigate } from "raviger";

import { Card, CardContent } from "@/components/ui/card";

import Page from "@/components/Common/Page";
import PatientInfoCard from "@/components/Patient/PatientInfoCard";
import { QuestionnaireForm } from "@/components/Questionnaire/QuestionnaireForm";

import useAppHistory from "@/hooks/useAppHistory";

import routes from "@/Utils/request/api";
import query from "@/Utils/request/query";
import { formatDateTime } from "@/Utils/utils";

interface Props {
facilityId: string;
patientId: string;
Expand All @@ -24,29 +30,61 @@ export default function EncounterQuestionnaire({
subjectType,
}: Props) {
const { goBack } = useAppHistory();
const { data: encounter } = useQuery({
queryKey: ["encounter", encounterId],
queryFn: query(routes.encounter.get, {
pathParams: { id: encounterId ?? "" },
queryParams: { facility: facilityId },
}),
enabled: !!encounterId,
});
return (
<Page title={t("questionnaire")}>
<Card className="mt-2">
<CardContent className="lg:p-4 p-0">
<QuestionnaireForm
facilityId={facilityId}
patientId={patientId}
subjectType={subjectType}
encounterId={encounterId}
questionnaireSlug={questionnaireSlug}
onSubmit={() => {
if (encounterId) {
navigate(
`/facility/${facilityId}/patient/${patientId}/encounter/${encounterId}/updates`,
);
} else {
navigate(`/patient/${patientId}/updates`);
}
}}
onCancel={() => goBack()}
/>
</CardContent>
</Card>
<div className="flex flex-col space-y-4 mt-4">
{encounter && (
<div className="size-full rounded-lg border bg-white text-black shadow">
<PatientInfoCard
patient={encounter.patient}
encounter={encounter}
fetchPatientData={() => {}}
disabled={true}
/>

<div className="flex flex-col justify-between gap-2 px-4 py-1 md:flex-row">
<div className="font-base flex flex-col text-xs leading-relaxed text-secondary-700 md:text-right">
<div className="flex items-center">
<span className="text-secondary-900">
{t("last_modified")}:{" "}
</span>
&nbsp;
{formatDateTime(encounter.modified_date)}
</div>
</div>
</div>
</div>
)}
<Card className="mt-2">
<CardContent className="lg:p-4 p-0">
<QuestionnaireForm
facilityId={facilityId}
patientId={patientId}
subjectType={subjectType}
encounterId={encounterId}
questionnaireSlug={questionnaireSlug}
onSubmit={() => {
if (encounterId) {
navigate(
`/facility/${facilityId}/patient/${patientId}/encounter/${encounterId}/updates`,
);
} else {
navigate(`/patient/${patientId}/updates`);
}
}}
onCancel={() => goBack()}
/>
</CardContent>
</Card>
</div>
</Page>
);
}
243 changes: 134 additions & 109 deletions src/components/Patient/PatientInfoCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,25 @@ import {
import { Avatar } from "@/components/Common/Avatar";
import { LocationHistorySheet } from "@/components/Location/LocationHistorySheet";
import { LocationTree } from "@/components/Location/LocationTree";
import LinkDepartmentsSheet from "@/components/Patient/LinkDepartmentsSheet";

import { PLUGIN_Component } from "@/PluginEngine";
import routes from "@/Utils/request/api";
import mutate from "@/Utils/request/mutate";
import { formatDateTime, formatPatientAge } from "@/Utils/utils";
import { Encounter, completedEncounterStatus } from "@/types/emr/encounter";
import { Patient } from "@/types/emr/newPatient";

import LinkDepartmentsSheet from "./LinkDepartmentsSheet";
import { FacilityOrganization } from "@/types/facilityOrganization/facilityOrganization";

export interface PatientInfoCardProps {
patient: Patient;
encounter: Encounter;
fetchPatientData?: (state: { aborted: boolean }) => void;
disabled?: boolean;
}

export default function PatientInfoCard(props: PatientInfoCardProps) {
const { patient, encounter } = props;
const { patient, encounter, disabled = false } = props;
const { t } = useTranslation();
const queryClient = useQueryClient();

Expand Down Expand Up @@ -324,36 +325,36 @@ export default function PatientInfoCard(props: PatientInfoCardProps) {
)
}

<LinkDepartmentsSheet
entityType="encounter"
entityId={encounter.id}
currentOrganizations={encounter.organizations}
facilityId={encounter.facility.id}
trigger={
<div className="flex flex-wrap gap-2">
{encounter.organizations.map((org) => (
<Badge
key={org.id}
className="capitalize gap-1 py-1 px-2 cursor-pointer hover:bg-secondary-100"
variant="outline"
title={`Organization: ${org.name}${org.description ? ` - ${org.description}` : ""}`}
>
<Building className="w-4 h-4 text-blue-400" />
{org.name}
</Badge>
))}
{encounter.organizations.length === 0 && (
<Badge
className="capitalize gap-1 py-1 px-2 cursor-pointer hover:bg-secondary-100"
variant="outline"
>
<Building className="w-4 h-4 text-blue-400" />
Add Organizations
</Badge>
)}
</div>
}
/>
{!disabled ? (
<LinkDepartmentsSheet
entityType="encounter"
entityId={encounter.id}
currentOrganizations={encounter.organizations}
facilityId={encounter.facility.id}
trigger={
<div className="flex flex-wrap gap-2">
{encounter.organizations.map((org) =>
organizationBadge(org),
)}
{encounter.organizations.length === 0 && (
<Badge
className="capitalize gap-1 py-1 px-2 cursor-pointer hover:bg-secondary-100"
variant="outline"
>
<Building className="w-4 h-4 text-blue-400" />
Add Organizations
</Badge>
)}
</div>
}
/>
) : (
<div className="flex flex-wrap gap-2">
{encounter.organizations.map((org) =>
organizationBadge(org),
)}
</div>
)}
{props.encounter.current_location ? (
<Popover>
<PopoverTrigger asChild>
Expand Down Expand Up @@ -401,18 +402,22 @@ export default function PatientInfoCard(props: PatientInfoCardProps) {
<LocationTree
location={props.encounter.current_location}
/>
<div className="border-b border-dashed border-gray-200 my-2" />
<Button
variant="outline"
className="border-gray-400 w-full"
>
<Link
href={`/facility/${props.encounter.facility.id}/patient/${props.patient.id}/encounter/${props.encounter.id}/questionnaire/location_association`}
className="text-sm text-gray-950 font-semibold"
>
{t("update_location")}
</Link>
</Button>
{!disabled && (
<>
<div className="border-b border-dashed border-gray-200 my-2" />
<Button
variant="outline"
className="border-gray-400 w-full"
>
<Link
href={`/facility/${props.encounter.facility.id}/patient/${props.patient.id}/encounter/${props.encounter.id}/questionnaire/location_association`}
className="text-sm text-gray-950 font-semibold"
>
{t("update_location")}
</Link>
</Button>
</>
)}
</div>
</PopoverContent>
</Popover>
Expand All @@ -439,78 +444,98 @@ export default function PatientInfoCard(props: PatientInfoCardProps) {
className="flex flex-col items-center justify-end gap-4 px-4 py-1 2xl:flex-row"
id="consultation-buttons"
>
{!completedEncounterStatus.includes(encounter.status) && (
<div
className="flex w-full flex-col gap-3 lg:w-auto 2xl:flex-row"
data-cy="update-encounter-button"
>
<AlertDialog>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="primary">
{t("update")}
<ChevronDown className="ml-2 h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>{t("actions")}</DropdownMenuLabel>
<DropdownMenuItem>
<Link
href={`/facility/${encounter.facility.id}/patient/${patient.id}/encounter/${encounter.id}/treatment_summary`}
className="cursor-pointer text-gray-800"
>
{t("treatment_summary")}
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link
href={`/facility/${encounter.facility.id}/patient/${patient.id}/encounter/${encounter.id}/files/discharge_summary`}
className="cursor-pointer text-gray-800"
>
{t("discharge_summary")}
</Link>
</DropdownMenuItem>
<AlertDialogTrigger asChild>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
{t("mark_as_complete")}
{!completedEncounterStatus.includes(encounter.status) &&
!disabled && (
<div
className="flex w-full flex-col gap-3 lg:w-auto 2xl:flex-row"
data-cy="update-encounter-button"
>
<AlertDialog>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="primary">
{t("update")}
<ChevronDown className="ml-2 h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>{t("actions")}</DropdownMenuLabel>
<DropdownMenuItem>
<Link
href={`/facility/${encounter.facility.id}/patient/${patient.id}/encounter/${encounter.id}/treatment_summary`}
className="cursor-pointer text-gray-800"
>
{t("treatment_summary")}
</Link>
</DropdownMenuItem>
</AlertDialogTrigger>
<DropdownMenuItem asChild>
<Link
href={`/facility/${encounter.facility.id}/patient/${patient.id}/encounter/${encounter.id}/files/discharge_summary`}
className="cursor-pointer text-gray-800"
>
{t("discharge_summary")}
</Link>
</DropdownMenuItem>
<AlertDialogTrigger asChild>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
{t("mark_as_complete")}
</DropdownMenuItem>
</AlertDialogTrigger>
<PLUGIN_Component
__name="PatientInfoCardActions"
encounter={encounter}
/>
</DropdownMenuContent>
</DropdownMenu>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{t("mark_as_complete")}
</AlertDialogTitle>
<AlertDialogDescription>
{t("mark_encounter_as_complete_confirmation")}
</AlertDialogDescription>
</AlertDialogHeader>

<PLUGIN_Component
__name="PatientInfoCardActions"
__name="PatientInfoCardMarkAsComplete"
encounter={encounter}
/>
</DropdownMenuContent>
</DropdownMenu>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{t("mark_as_complete")}</AlertDialogTitle>
<AlertDialogDescription>
{t("mark_encounter_as_complete_confirmation")}
</AlertDialogDescription>
</AlertDialogHeader>

<PLUGIN_Component
__name="PatientInfoCardMarkAsComplete"
encounter={encounter}
/>

<AlertDialogFooter>
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
<AlertDialogFooter>
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>

<AlertDialogAction
className={cn(buttonVariants({ variant: "primary" }))}
onClick={handleMarkAsComplete}
data-cy="mark-encounter-as-complete"
>
{t("mark_as_complete")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
)}
<AlertDialogAction
className={cn(buttonVariants({ variant: "primary" }))}
onClick={handleMarkAsComplete}
data-cy="mark-encounter-as-complete"
>
{t("mark_as_complete")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
)}
</div>
</section>
</>
);

function organizationBadge(org: FacilityOrganization) {
return (
<Badge
key={org.id}
className={cn(
"capitalize gap-1 py-1 px-2 hover:bg-secondary-100",
!disabled && "cursor-pointer ",
)}
variant="outline"
title={`Organization: ${org.name}${org.description ? ` - ${org.description}` : ""}`}
>
<Building className="w-4 h-4 text-blue-400" />
{org.name}
</Badge>
);
}
}
Loading