Skip to content

Commit d5185d7

Browse files
committed
clean up filters
1 parent 4b4df81 commit d5185d7

File tree

11 files changed

+244
-112
lines changed

11 files changed

+244
-112
lines changed

src/app/(dashboard)/patients/actions.ts

+20-16
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,38 @@
22

33
import { fhirServer } from "@/lib/api/axios";
44
import { FilterFormData } from "@/model/filters";
5-
import format from "string-template";
65
import { createPatient } from "@/lib/fhir/patient";
6+
import { fetchLocations, generteBaseFilter } from "@/lib/api/server";
7+
import { createPatientFilters } from "../stats/filters";
8+
9+
export async function fetchRequiredData() {
10+
const locations = await fetchLocations();
11+
const locationMap = new Map(
12+
locations.map((location) => [location.id, location.name])
13+
);
14+
return {
15+
locations,
16+
locationMap,
17+
};
18+
}
719

820
export async function fetchData(formData: FormData) {
921
try {
1022
const data = JSON.parse(
1123
formData.getAll("data")[0] as string
1224
) as FilterFormData;
1325

14-
const queries: string[] = [];
15-
16-
data.filters.forEach((filter) => {
17-
const template = filter.template;
18-
19-
const values: Record<string, any> = {};
20-
21-
filter.params.forEach((param) => {
22-
values[param.name] = encodeURIComponent(param.value as any);
23-
});
24-
25-
console.log({ template, values });
26+
const { rawDate, baseFilter } = generteBaseFilter(data.filters);
2627

27-
queries.push(format(template, values));
28+
const query = createPatientFilters(undefined, rawDate, baseFilter, {
29+
hasCount: false,
30+
onlyActive: true,
31+
formatUrl: true,
2832
});
2933

30-
console.log(queries);
34+
console.log(query);
3135

32-
const res = await fhirServer.get("/Patient?" + queries, {});
36+
const res = await fhirServer.get(query);
3337
return (
3438
res.data.entry?.map((entry: any) => createPatient(entry.resource)) ?? []
3539
);

src/app/(dashboard)/patients/content.tsx

+26-19
Original file line numberDiff line numberDiff line change
@@ -5,49 +5,56 @@ import { Patient } from "@/lib/fhir/types";
55
import Link from "next/link";
66
import React from "react";
77

8-
type Props = {};
8+
type Props = {
9+
locationMap: Map<string, string>;
10+
};
911

10-
const Content = (props: Props) => {
12+
const Content = ({ locationMap }: Props) => {
1113
const { data } = useGenericContext();
1214
const patients = data as Patient[];
1315
return (
14-
<div>
16+
<div className="flex flex-col gap-2">
1517
{patients.map((patient) => (
1618
<div key={patient.name} className="card w-full bg-base-100 shadow-xl">
1719
<div className="card-body">
1820
<h2 className="card-title">
19-
<span>{patient.name}</span>
21+
<span>
22+
{patient.name} ({locationMap.get(patient.locationId) ?? "NA"})
23+
</span>
2024
<Link href={`/patients/${patient.id}`}>
2125
<a className="btn btn-primary">View</a>{" "}
2226
</Link>
2327
</h2>
2428
<div className="flex flex-col gap-2">
25-
<span className="badge">
26-
ART/HCC Number: {patient.identifier}
27-
</span>
28-
<span className="badge">Gender: {patient.gender}</span>
29-
<span className="badge">BirthDate: {patient.birthDate}</span>
29+
<div className="flex flex-row gap-2 flex-wrap">
30+
<span className="badge">
31+
ART/HCC Number: {patient.identifier}
32+
</span>
33+
<span className="badge">Active: {patient.active}</span>
34+
<span className="badge">Gender: {patient.gender}</span>
35+
<span className="badge">BirthDate: {patient.birthDate}</span>
36+
</div>
3037
{patient.phoneNumbers.map((value, index) => (
3138
<div key={value.number} className="flex flex-row gap-2">
3239
<span className="badge">
33-
Phone {index + 1}: {value.number}
40+
Phone {index + 1}: {value.number} -- Owner: {value.owner}
3441
</span>
35-
<span className="badge">Owner: {value.owner}</span>
3642
</div>
3743
))}
38-
<span className="badge">Active: {patient.active}</span>
3944
{patient.address.map((address, index) => (
4045
<div key={address.facility} className="flex flex-row gap-2">
41-
<span className="badge">Facility: {address.facility}</span>
46+
<span className="badge">Location: {address.facility}</span>
4247
<span className="badge">Physical: {address.physical}</span>
4348
</div>
4449
))}
45-
<span className="badge">
46-
Registration Date: {patient.registrationDate}
47-
</span>
48-
<span className="badge">
49-
Registered By: {patient.registratedBy}
50-
</span>
50+
<div className="flex flex-row gap-2 flex-wrap">
51+
<span className="badge">
52+
Registration Date: {patient.registrationDate}
53+
</span>
54+
<span className="badge">
55+
Registered By: {patient.registratedBy}
56+
</span>
57+
</div>
5158
</div>
5259
</div>
5360
</div>

src/app/(dashboard)/patients/page.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { fetchData } from "./actions";
1+
import { fetchData, fetchRequiredData } from "./actions";
22
import FilterToolbar from "../../../components/filters/toolbar";
33
import { patientFilters } from "@/model/filters";
44
import Content from "./content";
@@ -8,14 +8,16 @@ export default async function Page({
88
}: {
99
searchParams: { q: string };
1010
}) {
11+
const data = await fetchRequiredData();
1112
return (
1213
<main className="container mt-6">
1314
<FilterToolbar
1415
action={fetchData}
1516
filters={patientFilters}
1617
defaultItem={[]}
18+
prefillData={data}
1719
>
18-
<Content />
20+
<Content locationMap={data.locationMap} />
1921
</FilterToolbar>
2022
</main>
2123
);

src/app/(dashboard)/stats/actions.ts

+12-35
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use server";
22

33
import { fetchBundle } from "@/lib/fhir/bundle";
4-
import { LocationData, SummaryItem } from "@/lib/models/types";
4+
import { SummaryItem } from "@/lib/models/types";
55
import { FilterFormData } from "@/model/filters";
66
import { fhirR4 } from "@smile-cdr/fhirts";
77
import { fixDate } from "./model";
@@ -10,16 +10,10 @@ import {
1010
createQuestionnaireResponseFilters,
1111
} from "./filters";
1212
import { eachDayOfInterval } from "@/lib/utils";
13+
import { fetchLocations } from "@/lib/api/server";
1314

1415
export async function fetchRequiredData() {
15-
const locationQuery = paramGenerator("/Location", {
16-
_count: 100,
17-
type: "https://d-tree.org/fhir/location-type|facility",
18-
});
19-
var bundle = await fetchBundle([locationQuery]);
20-
const locations = getLocationData(
21-
bundle.entry?.[0]?.resource as fhirR4.Bundle
22-
);
16+
const locations = await fetchLocations();
2317
return {
2418
locations,
2519
};
@@ -99,9 +93,15 @@ export async function fetchData(formData: FormData) {
9993
baseFilter,
10094
false
10195
),
102-
createPatientFilters(["newly-diagnosed-client"], null, baseFilter, true),
103-
createPatientFilters(["client-already-on-art"], null, baseFilter, true),
104-
createPatientFilters(["exposed-infant"], null, baseFilter, true),
96+
createPatientFilters(["newly-diagnosed-client"], null, baseFilter, {
97+
hasCount: true,
98+
}),
99+
createPatientFilters(["client-already-on-art"], null, baseFilter, {
100+
hasCount: true,
101+
}),
102+
createPatientFilters(["exposed-infant"], null, baseFilter, {
103+
hasCount: true,
104+
}),
105105
createQuestionnaireResponseFilters(
106106
"patient-finish-visit",
107107
rawDate,
@@ -181,20 +181,6 @@ export async function fetchData(formData: FormData) {
181181
};
182182
}
183183

184-
const getLocationData = (bundle: fhirR4.Bundle | undefined): LocationData[] => {
185-
if (bundle == undefined) {
186-
return [];
187-
}
188-
return (
189-
bundle.entry?.map((entry) => {
190-
return {
191-
id: entry.resource?.id ?? "",
192-
name: (entry.resource as fhirR4.Location)?.name ?? "",
193-
};
194-
}) ?? []
195-
);
196-
};
197-
198184
const getResults = (
199185
bundle: fhirR4.Bundle | undefined,
200186
summary: string[],
@@ -231,12 +217,3 @@ const getResults = (
231217
}) ?? []
232218
);
233219
};
234-
235-
const paramGenerator = (
236-
resources: string,
237-
params: Record<string, string | number | string>
238-
) => {
239-
return `${resources}?${Object.keys(params)
240-
.map((key) => `${key}=${params[key]}`)
241-
.join("&")}`;
242-
};

src/app/(dashboard)/stats/filters.ts

+17-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { fhirR4 } from "@smile-cdr/fhirts";
2-
import { QueryParam, fixDate } from "./model";
1+
import { QueryParam } from "./model";
32
import { format } from "date-fns";
43

54
type PatientType =
@@ -50,13 +49,24 @@ export const createPatientFilters = (
5049
types: PatientType[] | undefined = undefined,
5150
date: string | string[] | null,
5251
baseFilter: Record<string, string>[],
53-
onlyActive = false
52+
options: {
53+
onlyActive?: boolean;
54+
hasCount?: boolean;
55+
formatUrl?: boolean;
56+
} = {
57+
hasCount: true,
58+
onlyActive: false,
59+
formatUrl: false,
60+
}
5461
) => {
55-
const query = new QueryParam({
56-
_summary: "count",
57-
});
62+
const query = new QueryParam({}, options.formatUrl);
63+
64+
if (options.hasCount == true) {
65+
query.add("_summary", "count");
66+
}
67+
5868
query.fromArray(baseFilter);
59-
if (onlyActive) {
69+
if (options.onlyActive == true) {
6070
query.add("active", true);
6171
}
6272
query.remove("date");

src/app/(dashboard)/stats/model.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ export const fixDate = (date: string | string[]) => {
55

66
export class QueryParam {
77
queries: Map<string, string> = new Map();
8-
constructor(values: Record<string, string>) {
8+
encodeUrl: boolean = false;
9+
10+
constructor(values: Record<string, string>, encodeUrl: boolean = false) {
911
this.from(values);
12+
this.encodeUrl = encodeUrl;
1013
}
1114

1215
add(key: string, value: any) {
@@ -49,9 +52,11 @@ export class QueryParam {
4952
const query = Array.from(this.queries)
5053
.map(([key, value]) => {
5154
if (key.includes("[")) {
52-
return `${key.split("[")[0]}=${value}`;
55+
return `${key.split("[")[0]}=${
56+
this.encodeUrl ? encodeURIComponent(value) : value
57+
}`;
5358
}
54-
return `${key}=${value}`;
59+
return `${key}=${this.encodeUrl ? encodeURIComponent(value) : value}`;
5560
})
5661
.join("&");
5762
return `${resources}?${query}`;

src/lib/api/server.ts

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { fetchBundle } from "../fhir/bundle";
2+
import { eachDayOfInterval, paramGenerator } from "../utils";
3+
import { fhirR4 } from "@smile-cdr/fhirts";
4+
import { LocationData, SummaryItem } from "@/lib/models/types";
5+
import { FilterFormItem } from "@/model/filters";
6+
import { fixDate } from "@/app/(dashboard)/stats/model";
7+
import format from "string-template";
8+
9+
export async function fetchLocations() {
10+
const locationQuery = paramGenerator("/Location", {
11+
_count: 100,
12+
type: "https://d-tree.org/fhir/location-type|facility",
13+
});
14+
var bundle = await fetchBundle([locationQuery]);
15+
const locations = getLocationData(
16+
bundle.entry?.[0]?.resource as fhirR4.Bundle
17+
);
18+
return locations;
19+
}
20+
21+
export const generteBaseFilter = (filters: FilterFormItem[]) => {
22+
let rawDate: string | string[] | null = null;
23+
24+
const baseFilter = filters.map((filter) => {
25+
const temp: Record<string, string> = {};
26+
27+
if (filter.template == "_tag_location") {
28+
const template = `http://smartregister.org/fhir/location-tag|${
29+
filter.params[0].value ?? ""
30+
}`;
31+
temp["_tag"] = template;
32+
} else if (filter.template == "date") {
33+
rawDate =
34+
filter.params.find((e) => e.name == "date")?.value?.split("T")[0] ??
35+
null;
36+
} else if (filter.template == "dateRange") {
37+
const value = filter.params[0].value;
38+
if (value) {
39+
const { from, to } = value as any;
40+
console.log(from, to);
41+
42+
if (from && to) {
43+
const start = new Date(from.split("T")[0]);
44+
const end = new Date(to.split("T")[0]);
45+
console.log({ start, end });
46+
47+
rawDate = eachDayOfInterval({
48+
start,
49+
end,
50+
}).map((e) => {
51+
console.log(e);
52+
53+
return e.toISOString().split("T")[0];
54+
});
55+
} else if (from) {
56+
rawDate = [from.split("T")[0]];
57+
}
58+
}
59+
} else {
60+
if (filter.template.includes("={")) {
61+
const values: Record<string, any> = {};
62+
63+
filter.params.forEach((param) => {
64+
values[param.name] = encodeURIComponent(param.value as any);
65+
});
66+
67+
const value = format(filter.template, values);
68+
temp[value.split("=")[0]] = value.split("=")[1];
69+
} else {
70+
temp[filter.template] = filter.params[0].value ?? "";
71+
}
72+
}
73+
74+
return temp;
75+
});
76+
77+
if (rawDate) {
78+
rawDate = fixDate(rawDate);
79+
}
80+
81+
return {
82+
baseFilter,
83+
rawDate,
84+
};
85+
};
86+
87+
const getLocationData = (bundle: fhirR4.Bundle | undefined): LocationData[] => {
88+
if (bundle == undefined) {
89+
return [];
90+
}
91+
return (
92+
bundle.entry?.map((entry) => {
93+
return {
94+
id: entry.resource?.id ?? "",
95+
name: (entry.resource as fhirR4.Location)?.name ?? "",
96+
};
97+
}) ?? []
98+
);
99+
};

0 commit comments

Comments
 (0)