Skip to content

Commit b45c7e7

Browse files
committed
change URL to ids pattern to fix stale UI issue
1 parent 27f7cff commit b45c7e7

7 files changed

Lines changed: 73 additions & 114 deletions

File tree

app/page.tsx

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,28 @@
1-
import { Suspense } from "react";
2-
import { use } from "react";
31
import ConceptsSection from "@/components/ConceptsSection";
42
import NotesSection from "@/components/NotesSection";
53
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
64
import DomainSelect from "@/components/DomainSelect";
75
import { ModeToggle } from "@/components/mode-toggle";
86

9-
export default function Page(props: {
10-
searchParams: Promise<{
11-
conceptId?: string;
12-
domain?: string;
13-
}>;
14-
}) {
15-
return (
16-
<Suspense fallback={<div>Loading page...</div>}>
17-
<PageContent {...props} />
18-
</Suspense>
19-
);
20-
}
21-
22-
function PageContent({
7+
export default async function Page({
238
searchParams,
249
}: {
2510
searchParams: Promise<{
26-
conceptId?: string | string[];
11+
ids?: string | string[];
2712
domain?: string;
2813
page?: string;
2914
}>;
3015
}) {
31-
const params = use(searchParams);
16+
const params = await searchParams;
17+
18+
const ids =
19+
typeof params.ids === "string" ? params.ids.split(",").filter(Boolean) : [];
20+
21+
const domain =
22+
typeof params.domain === "string" && params.domain.length > 0
23+
? params.domain
24+
: "All";
3225

33-
const conceptIds = Array.isArray(params.conceptId)
34-
? params.conceptId
35-
: params.conceptId
36-
? [params.conceptId]
37-
: [];
38-
const domain = params.domain ?? "All";
3926
const page = Math.max(1, parseInt(params.page ?? "1", 10) || 1);
4027
return (
4128
<div className="min-h-screen bg-muted/40 p-6">
@@ -64,12 +51,11 @@ function PageContent({
6451

6552
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
6653
<Card className="p-0 md:col-span-1">
67-
<ConceptsSection domain={domain} />
54+
<ConceptsSection domain={domain} />
6855
</Card>
6956

70-
<Card key={`${conceptIds.join(",")}-${domain}-${page}`} className="p-0 md:col-span-2">
71-
<NotesSection conceptIds={conceptIds} page={page} />
72-
57+
<Card className="p-0 md:col-span-2">
58+
<NotesSection conceptIds={ids} page={page} />
7359
</Card>
7460
</div>
7561
</div>

components/ConceptList.tsx

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,44 @@
11
"use client";
2+
23
import { usePathname, useRouter, useSearchParams } from "next/navigation";
34
import type { Concept } from "@/types/OmopTables";
45
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
56
import { Button } from "@/components/ui/button";
67
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
78
import { buildSearchParams } from "@/lib/helpers";
8-
import { useEffect, useState } from "react";
99

1010
export default function ConceptList({ concepts }: { concepts: Concept[] }) {
1111
const router = useRouter();
1212
const pathname = usePathname();
1313
const searchParams = useSearchParams();
14-
const selectedConcepts = searchParams.getAll("conceptId")
15-
const [loading, setLoading] = useState(false);
14+
const selectedConcepts =
15+
searchParams.get("ids")?.split(",").filter(Boolean) ?? [];
1616

1717
const onSelect = (conceptId: string) => {
18-
if (loading) return;
19-
20-
setLoading(true);
21-
22-
const current = searchParams.getAll("conceptId");
18+
const current = selectedConcepts;
2319

2420
const nextConceptIds = current.includes(conceptId)
2521
? current.filter((id) => id !== conceptId)
2622
: [...current, conceptId];
2723

2824
const query = buildSearchParams({
29-
conceptIds: nextConceptIds,
25+
ids: nextConceptIds,
3026
domain: searchParams.get("domain"),
3127
page: 1,
3228
});
3329

34-
router.replace(`${pathname}?${query}`, {
30+
router.push(`${pathname}?${query}`, {
3531
scroll: false,
3632
});
37-
3833
};
39-
useEffect(() => {
40-
setLoading(false);
41-
}, [searchParams]);
34+
4235
const clearSelection = () => {
4336
const query = buildSearchParams({
44-
conceptIds: [],
37+
ids: [],
4538
domain: searchParams.get("domain"),
4639
page: 1,
4740
});
48-
49-
router.replace(`${pathname}?${query}`, { scroll: false });
41+
router.push(`${pathname}?${query}`, { scroll: false });
5042
};
5143

5244
return (

components/DomainSelect.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,30 @@ export default function DomainSelect({ domain }: { domain: string }) {
1414
const pathname = usePathname();
1515

1616
function handleChange(domain: string) {
17-
const params = new URLSearchParams();
17+
const params = new URLSearchParams(searchParams.toString());
1818

19-
// Keep domain if exists
19+
// Keep domain if it's not "All"
2020
if (domain && domain !== "All") {
2121
params.set("domain", domain);
22+
} else {
23+
params.delete("domain");
2224
}
2325

2426
// Keep selected concepts
25-
const conceptIds = searchParams.getAll("conceptId")
26-
conceptIds.forEach((id) => params.append("conceptId", id));
27+
const ids = searchParams.get("ids")?.split(",").filter(Boolean) ?? [];
28+
29+
if (ids.length > 0) {
30+
params.set("ids", ids.join(","));
31+
}
2732

2833
// Reset page number
2934
params.set("page", "1");
3035

31-
router.replace(`${pathname}?${params.toString()}`, { scroll: false });
36+
router.push(`${pathname}?${params.toString()}`, {
37+
scroll: false,
38+
});
3239
}
40+
3341
return (
3442
<Select value={domain} onValueChange={handleChange}>
3543
<SelectTrigger className="w-50">

components/NoteCard.tsx

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import { ChevronDownIcon } from "@radix-ui/react-icons";
1919
import Link from "next/link";
2020
import { usePathname, useRouter, useSearchParams } from "next/navigation";
2121
import { buildSearchParams } from "@/lib/helpers";
22-
import { useTransition } from "react";
2322

2423
export default function NoteCard({
2524
note,
@@ -32,9 +31,7 @@ export default function NoteCard({
3231
const pathname = usePathname();
3332
const searchParams = useSearchParams();
3433

35-
const [isPending, startTransition] = useTransition();
36-
37-
const selectedConceptIds = searchParams.getAll("conceptId");
34+
const ids = searchParams.get("ids")?.split(",").filter(Boolean) ?? [];
3835

3936
const pmcid = note.note_source_value?.match(/PMC(\d+)/)?.[1];
4037

@@ -51,31 +48,25 @@ export default function NoteCard({
5148
);
5249

5350
const selectConcept = (conceptId: string) => {
54-
const existing = searchParams.getAll("conceptId");
51+
const existing = ids;
5552

5653
const nextConceptIds = existing.includes(conceptId)
5754
? existing.filter((id) => id !== conceptId)
5855
: [...existing, conceptId];
5956

6057
const query = buildSearchParams({
61-
conceptIds: nextConceptIds,
58+
ids: nextConceptIds,
6259
domain: searchParams.get("domain"),
6360
page: 1,
6461
});
6562

66-
startTransition(() => {
67-
router.replace(`${pathname}?${query}`, {
68-
scroll: false,
69-
});
70-
});
63+
router.push(`${pathname}?${query}`, { scroll: false });
7164
};
7265

7366
return (
7467
<Card
7568
className={`
7669
bg-zinc-50 ring-foreground/15 dark:bg-neutral-800 mb-5
77-
transition-opacity
78-
${isPending ? "opacity-60 pointer-events-none" : ""}
7970
`}
8071
>
8172
<CardHeader>
@@ -89,29 +80,20 @@ export default function NoteCard({
8980
{article.title}
9081
</Link>
9182
) : (
92-
<p className="text-sm text-gray-400">
93-
Loading Case Reports...
94-
</p>
83+
<p className="text-sm text-gray-400">Loading Case Reports...</p>
9584
)}
9685
</CardTitle>
97-
98-
{isPending && (
99-
<div className="text-sm text-muted-foreground">
100-
Loading Case Reports...
101-
</div>
102-
)}
10386
</CardHeader>
10487

10588
<CardContent className="flex flex-wrap items-center gap-2 md:flex-row">
10689
{article ? (
107-
<Collapsible defaultOpen={true} className="w-full rounded-md data-[state=open]:bg-muted">
90+
<Collapsible
91+
defaultOpen={true}
92+
className="w-full rounded-md data-[state=open]:bg-muted"
93+
>
10894
<CollapsibleTrigger asChild>
109-
<Button
110-
variant="ghost"
111-
className="group w-full bg-transparent"
112-
>
95+
<Button variant="ghost" className="group w-full bg-transparent">
11396
Description
114-
11597
<ChevronDownIcon className="ml-auto transition-transform group-data-[state=open]:rotate-180" />
11698
</Button>
11799
</CollapsibleTrigger>
@@ -121,18 +103,14 @@ export default function NoteCard({
121103
</CollapsibleContent>
122104
</Collapsible>
123105
) : (
124-
<p className="text-sm text-gray-400">
125-
Loading Case Reports...
126-
</p>
106+
<p className="text-sm text-gray-400">Loading Case Reports...</p>
127107
)}
128108
</CardContent>
129109

130110
<CardFooter className="bg-neutral-50 border-t-gray-200 dark:bg-neutral-900 dark:border-t-neutral-600">
131111
<div className="flex flex-wrap gap-2">
132112
{uniqueConcepts.map((c: Concept) => {
133-
const selected = selectedConceptIds.includes(
134-
String(c.concept_id),
135-
);
113+
const selected = ids.includes(String(c.concept_id));
136114

137115
return (
138116
<Badge
@@ -178,4 +156,4 @@ export default function NoteCard({
178156
</CardFooter>
179157
</Card>
180158
);
181-
}
159+
}

components/NotesList.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export default async function NotesList({
88
relatedConcepts,
99
notes,
1010
conceptDetails,
11-
total,
11+
total
1212
}: {
1313
relatedConcepts: Array<{
1414
inputConceptId: string;
@@ -32,7 +32,7 @@ export default async function NotesList({
3232
.map((note) => note.note_source_value?.match(/PMC(\d+)/)?.[1])
3333
.filter((id): id is string => Boolean(id)),
3434
),
35-
);
35+
).sort();
3636
if (pmcids.length === 0) return;
3737
const articles =
3838
pmcids.length > 0 ? await getMultiplePmcArticles(pmcids) : {};
@@ -99,18 +99,18 @@ export default async function NotesList({
9999
</Badge>
100100
</div>
101101
<div className="grid grid-cols-1 gap-4 py-1">
102-
{notes.map((note) => {
103-
const pmcid = note.note_source_value?.match(/PMC(\d+)/)?.[1];
102+
{notes.map((note) => {
103+
const pmcid = note.note_source_value?.match(/PMC(\d+)/)?.[1];
104104

105-
return (
106-
<NoteCard
107-
key={note.note_id}
108-
note={note}
109-
article={pmcid ? articles[pmcid] : null}
110-
/>
111-
);
112-
})}
113-
</div>
105+
return (
106+
<NoteCard
107+
key={note.note_id}
108+
note={note}
109+
article={pmcid ? articles[pmcid] : null}
110+
/>
111+
);
112+
})}
113+
</div>
114114
</div>
115115
)}
116116
</CardContent>

components/NotesPagination.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,13 @@ export default function NotesPagination({
2222
const searchParams = useSearchParams();
2323

2424
const createPageURL = (newPage: number) => {
25-
const params = new URLSearchParams();
25+
const params = new URLSearchParams(searchParams.toString());
2626

27-
// Get domain if it exists
28-
const domain = searchParams.get("domain");
29-
if (domain && domain !== "All") {
30-
params.set("domain", domain);
31-
}
27+
const ids = searchParams.get("ids")?.split(",").filter(Boolean) ?? [];
3228
// Get conceptIds and add to URL
33-
const conceptIds = searchParams.getAll("conceptId")
34-
conceptIds.forEach((id) => params.append("conceptId", id));
29+
if (ids.length > 0) {
30+
params.set("ids", ids.join(","));
31+
}
3532

3633
// Add the page number
3734
params.set("page", String(newPage));

lib/helpers.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
export function buildSearchParams({
2-
conceptIds = [],
2+
ids = [],
33
domain,
44
page = 1,
55
}: {
6-
conceptIds?: string[];
6+
ids?: string[];
77
domain?: string | null;
88
page?: number;
99
}) {
@@ -13,12 +13,10 @@ export function buildSearchParams({
1313
if (domain && domain !== "All") {
1414
params.set("domain", domain);
1515
}
16-
17-
// Sort and add unique conceptIds
18-
[...new Set(conceptIds)]
19-
.map(String)
20-
.forEach((id) => params.append("conceptId", id));
21-
16+
// Add unique conceptIds
17+
if (ids.length) {
18+
params.set("ids", [...new Set(ids)].join(","));
19+
}
2220
// Set page
2321
params.set("page", String(page));
2422

0 commit comments

Comments
 (0)