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

Enhance encounter data handling by adding encounterId prop across all equests #9793

Merged
merged 1 commit into from
Jan 6, 2025
Merged
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
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
Loading