Skip to content

Commit

Permalink
Enhance encounter data handling by adding encounterId prop across mul…
Browse files Browse the repository at this point in the history
…tiple components (#9793)
  • Loading branch information
bodhish authored Jan 6, 2025
1 parent c77a633 commit 7c58703
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 418 deletions.
7 changes: 7 additions & 0 deletions src/components/Common/Charts/ObservationChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ interface ObservationVisualizerProps {
codeGroups: CodeGroup[];
height?: number;
gridCols?: number;
encounterId: string;
}

interface ChartData {
Expand Down Expand Up @@ -98,6 +99,7 @@ const formatChartDate = (
export const ObservationVisualizer = ({
patientId,
codeGroups,
encounterId,
height = 300,
gridCols = 2,
}: ObservationVisualizerProps) => {
Expand All @@ -108,10 +110,14 @@ export const ObservationVisualizer = ({
queryKey: [
"observations",
patientId,
encounterId,
allCodes.map((c) => c.code).join(","),
],
queryFn: query(routes.observationsAnalyse, {
pathParams: { patientId },
queryParams: {
encounter: encounterId,
},
body: {
codes: allCodes,
},
Expand Down Expand Up @@ -380,6 +386,7 @@ export const ObservationVisualizer = ({
<TabsContent value="history">
<ObservationHistoryTable
patientId={patientId}
encounterId={encounterId}
codes={group.codes}
/>
</TabsContent>
Expand Down
3 changes: 3 additions & 0 deletions src/components/Common/Charts/ObservationHistoryTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ interface PaginatedResponse<T> {

interface ObservationHistoryTableProps {
patientId: string;
encounterId: string;
codes: Code[];
}

Expand All @@ -45,6 +46,7 @@ const formatDate = (dateString: string) => {

export const ObservationHistoryTable = ({
patientId,
encounterId,
codes,
}: ObservationHistoryTableProps) => {
const { ref, inView } = useInView();
Expand All @@ -57,6 +59,7 @@ export const ObservationHistoryTable = ({
const response = await query(routes.listObservations, {
pathParams: { patientId },
queryParams: {
encounter: encounterId,
limit: String(LIMIT),
codes: codes.map((c) => c.code).join(","),
offset: String(pageParam),
Expand Down
146 changes: 93 additions & 53 deletions src/components/Facility/ConsultationDetails/ObservationsList.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { useInfiniteQuery } from "@tanstack/react-query";
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { useInView } from "react-intersection-observer";

import CareIcon from "@/CAREUI/icons/CareIcon";
import PaginatedList from "@/CAREUI/misc/PaginatedList";

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

import routes from "@/Utils/request/api";
import query from "@/Utils/request/query";
import { HTTPError } from "@/Utils/request/types";
import { PaginatedResponse } from "@/Utils/request/types";
import { formatDateTime } from "@/Utils/utils";
import { Encounter } from "@/types/emr/encounter";
import { Observation } from "@/types/emr/observation";

const LIMIT = 20;

interface Props {
encounter: Encounter;
}
Expand All @@ -18,63 +25,96 @@ export default function ObservationsList(props: Props) {
const { t } = useTranslation();
const patientId = props.encounter.patient.id;
const encounterId = props.encounter.id;
const { ref, inView } = useInView();

const { data, fetchNextPage, hasNextPage, isLoading, isFetchingNextPage } =
useInfiniteQuery<PaginatedResponse<Observation>, HTTPError>({
queryKey: ["observations", patientId, encounterId],
queryFn: async ({ pageParam = 0 }) => {
const response = await query(routes.listObservations, {
pathParams: { patientId },
queryParams: {
encounter: encounterId,
ignore_group: true,
limit: String(LIMIT),
offset: String(pageParam),
},
})({ signal: new AbortController().signal });
return response as PaginatedResponse<Observation>;
},
initialPageParam: 0,
getNextPageParam: (lastPage, allPages) => {
const currentOffset = allPages.length * LIMIT;
return currentOffset < lastPage.count ? currentOffset : null;
},
});

useEffect(() => {
if (inView && hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
}, [inView, hasNextPage, isFetchingNextPage, fetchNextPage]);

if (isLoading) {
return (
<Card className="p-6">
<div className="text-lg font-medium text-muted-foreground">
{t("loading")}
</div>
</Card>
);
}

const observations = data?.pages.flatMap((page) => page.results) ?? [];

if (observations.length === 0) {
return (
<Card className="p-6">
<div className="text-lg font-medium text-muted-foreground">
{t("no_observations")}
</div>
</Card>
);
}

return (
<PaginatedList
route={routes.listObservations}
pathParams={{ patientId }}
query={{ encounter: encounterId, ignore_group: true }}
>
{() => (
<div className="mt-4 flex w-full flex-col gap-4">
<div className="flex max-h-[85vh] flex-col gap-4 overflow-y-auto overflow-x-hidden px-3">
<PaginatedList.WhenEmpty>
<Card className="p-6">
<div className="text-lg font-medium text-muted-foreground">
{t("no_observations")}
<div className="mt-4 flex w-full flex-col gap-4">
<div className="flex max-h-[85vh] flex-col gap-4 overflow-y-auto overflow-x-hidden px-3">
{observations.map((item: Observation) => (
<Card key={item.id} className="flex items-center justify-between p-4">
<div>
<div className="text-xs flex items-center gap-1 text-muted-foreground">
<CareIcon icon="l-calender" />
<span>{formatDateTime(item.effective_datetime)}</span>
</div>
<div className="font-medium">
{item.main_code.display || item.main_code.code}
</div>
{item.value.value_quantity && (
<div className="mt-1 font-medium">
{item.value.value_quantity.value}{" "}
{item.value.value_quantity.code.display}
</div>
</Card>
</PaginatedList.WhenEmpty>

<PaginatedList.Items<Observation> className="grid gap-4">
{(item) => (
<Card
key={item.id}
className="flex items-center justify-between p-4"
>
<div>
<div className="text-xs flex items-center gap-1 text-muted-foreground">
<CareIcon icon="l-calender" />
<span>{formatDateTime(item.effective_datetime)}</span>
</div>
<div className="font-medium">
{item.main_code.display || item.main_code.code}
</div>
{item.value.value_quantity && (
<div className="mt-1 font-medium">
{item.value.value_quantity.value}{" "}
{item.value.value_quantity.code.display}
</div>
)}
{item.value.value && (
<div className="mt-1 font-medium">{item.value.value}</div>
)}
{item.note && (
<div className="mt-1 text-sm text-muted-foreground">
{item.note}
</div>
)}
</div>
</Card>
)}
</PaginatedList.Items>

<div className="flex w-full items-center justify-center">
<PaginatedList.Paginator hideIfSinglePage />
{item.value.value && (
<div className="mt-1 font-medium">{item.value.value}</div>
)}
{item.note && (
<div className="mt-1 text-sm text-muted-foreground">
{item.note}
</div>
)}
</div>
</Card>
))}
{hasNextPage && (
<div ref={ref} className="flex justify-center p-4">
<div className="text-sm text-muted-foreground">
{isFetchingNextPage ? t("loading_more") : t("load_more")}
</div>
</div>
</div>
)}
</PaginatedList>
)}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ export default function QuestionnaireResponsesList({ encounter }: Props) {
route={routes.getQuestionnaireResponses}
pathParams={{
patientId: encounter.patient.id,
encounterId: encounter.id,
}}
query={{
encounter: encounter.id,
}}
>
{() => (
Expand Down Expand Up @@ -264,6 +266,7 @@ export default function QuestionnaireResponsesList({ encounter }: Props) {
type={type}
id={response.id}
patientId={encounter.patient.id}
encounterId={encounter.id}
/>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,36 @@ interface Props {
type: string;
id: string;
patientId: string;
encounterId: string;
}

export function StructuredResponseView({ type, id, patientId }: Props) {
export function StructuredResponseView({
type,
id,
patientId,
encounterId,
}: Props) {
const getRouteAndParams = () => {
const params: Record<string, string> = { patientId };
switch (type) {
case "symptom":
return {
route: symptomApi.retrieveSymptom,
pathParams: { ...params, symptomId: id },
queryParams: { encounter: encounterId },
};
case "diagnosis":
return {
route: diagnosisApi.retrieveDiagnosis,
pathParams: { ...params, diagnosisId: id },
queryParams: { encounter: encounterId },
};
case "allergy_intolerance":
return {
route: allergyApi.retrieveAllergy,
pathParams: { ...params, allergyId: id },
queryParams: { encounter: encounterId },
};
default:
return null;
}
};

Expand All @@ -47,7 +54,8 @@ export function StructuredResponseView({ type, id, patientId }: Props) {
const { data, isLoading, error } = useQuery({
queryKey: [type, id],
queryFn: query(routeConfig?.route as any, {
pathParams: routeConfig?.pathParams || { patientId },
pathParams: routeConfig?.pathParams,
queryParams: routeConfig?.queryParams,
}),
enabled: !!id && !!routeConfig,
});
Expand Down
1 change: 1 addition & 0 deletions src/pages/Encounters/tabs/EncounterPlotsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const EncounterPlotsTab = (props: EncounterTabProps) => {
<TabsContent key={tab.id} value={tab.id}>
<ObservationVisualizer
patientId={props.patient.id}
encounterId={props.encounter.id}
codeGroups={tab.groups}
gridCols={2}
/>
Expand Down
Loading

0 comments on commit 7c58703

Please sign in to comment.