Skip to content
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
42 changes: 14 additions & 28 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,28 @@
import { Suspense } from "react";
import { use } from "react";
import ConceptsSection from "@/components/ConceptsSection";
import NotesSection from "@/components/NotesSection";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import DomainSelect from "@/components/DomainSelect";
import { ModeToggle } from "@/components/mode-toggle";

export default function Page(props: {
searchParams: Promise<{
conceptId?: string;
domain?: string;
}>;
}) {
return (
<Suspense fallback={<div>Loading page...</div>}>
<PageContent {...props} />
</Suspense>
);
}

function PageContent({
export default async function Page({
searchParams,
}: {
searchParams: Promise<{
conceptId?: string | string[];
ids?: string | string[];
domain?: string;
page?: string;
}>;
}) {
const params = use(searchParams);
const params = await searchParams;

const ids =
typeof params.ids === "string" ? params.ids.split(",").filter(Boolean) : [];

const domain =
typeof params.domain === "string" && params.domain.length > 0
? params.domain
: "All";

const conceptIds = Array.isArray(params.conceptId)
? params.conceptId
: params.conceptId
? [params.conceptId]
: [];
const domain = params.domain ?? "All";
const page = Math.max(1, parseInt(params.page ?? "1", 10) || 1);
return (
<div className="min-h-screen bg-muted/40 p-6">
Expand Down Expand Up @@ -64,12 +51,11 @@ function PageContent({

<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<Card className="p-0 md:col-span-1">
<ConceptsSection domain={domain} />
<ConceptsSection domain={domain} />
</Card>

<Card key={`${conceptIds.join(",")}-${domain}-${page}`} className="p-0 md:col-span-2">
<NotesSection conceptIds={conceptIds} page={page} />

<Card className="p-0 md:col-span-2">
<NotesSection conceptIds={ids} page={page} />
</Card>
</div>
</div>
Expand Down
26 changes: 9 additions & 17 deletions components/ConceptList.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,44 @@
"use client";

import { usePathname, useRouter, useSearchParams } from "next/navigation";
import type { Concept } from "@/types/OmopTables";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
import { buildSearchParams } from "@/lib/helpers";
import { useEffect, useState } from "react";

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

const onSelect = (conceptId: string) => {
if (loading) return;

setLoading(true);

const current = searchParams.getAll("conceptId");
const current = selectedConcepts;

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

const query = buildSearchParams({
conceptIds: nextConceptIds,
ids: nextConceptIds,
domain: searchParams.get("domain"),
page: 1,
});

router.replace(`${pathname}?${query}`, {
router.push(`${pathname}?${query}`, {
scroll: false,
});

};
useEffect(() => {
setLoading(false);
}, [searchParams]);

const clearSelection = () => {
const query = buildSearchParams({
conceptIds: [],
ids: [],
domain: searchParams.get("domain"),
page: 1,
});

router.replace(`${pathname}?${query}`, { scroll: false });
router.push(`${pathname}?${query}`, { scroll: false });
};

return (
Expand Down
18 changes: 13 additions & 5 deletions components/DomainSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,30 @@ export default function DomainSelect({ domain }: { domain: string }) {
const pathname = usePathname();

function handleChange(domain: string) {
const params = new URLSearchParams();
const params = new URLSearchParams(searchParams.toString());

// Keep domain if exists
// Keep domain if it's not "All"
if (domain && domain !== "All") {
params.set("domain", domain);
} else {
params.delete("domain");
}

// Keep selected concepts
const conceptIds = searchParams.getAll("conceptId")
conceptIds.forEach((id) => params.append("conceptId", id));
const ids = searchParams.get("ids")?.split(",").filter(Boolean) ?? [];

if (ids.length > 0) {
params.set("ids", ids.join(","));
}

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

router.replace(`${pathname}?${params.toString()}`, { scroll: false });
router.push(`${pathname}?${params.toString()}`, {
scroll: false,
});
}

return (
<Select value={domain} onValueChange={handleChange}>
<SelectTrigger className="w-50">
Expand Down
48 changes: 13 additions & 35 deletions components/NoteCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { ChevronDownIcon } from "@radix-ui/react-icons";
import Link from "next/link";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { buildSearchParams } from "@/lib/helpers";
import { useTransition } from "react";

export default function NoteCard({
note,
Expand All @@ -32,9 +31,7 @@ export default function NoteCard({
const pathname = usePathname();
const searchParams = useSearchParams();

const [isPending, startTransition] = useTransition();

const selectedConceptIds = searchParams.getAll("conceptId");
const ids = searchParams.get("ids")?.split(",").filter(Boolean) ?? [];

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

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

const selectConcept = (conceptId: string) => {
const existing = searchParams.getAll("conceptId");
const existing = ids;

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

const query = buildSearchParams({
conceptIds: nextConceptIds,
ids: nextConceptIds,
domain: searchParams.get("domain"),
page: 1,
});

startTransition(() => {
router.replace(`${pathname}?${query}`, {
scroll: false,
});
});
router.push(`${pathname}?${query}`, { scroll: false });
};

return (
<Card
className={`
bg-zinc-50 ring-foreground/15 dark:bg-neutral-800 mb-5
transition-opacity
${isPending ? "opacity-60 pointer-events-none" : ""}
`}
>
<CardHeader>
Expand All @@ -89,29 +80,20 @@ export default function NoteCard({
{article.title}
</Link>
) : (
<p className="text-sm text-gray-400">
Loading Case Reports...
</p>
<p className="text-sm text-gray-400">Loading Case Reports...</p>
)}
</CardTitle>

{isPending && (
<div className="text-sm text-muted-foreground">
Loading Case Reports...
</div>
)}
</CardHeader>

<CardContent className="flex flex-wrap items-center gap-2 md:flex-row">
{article ? (
<Collapsible defaultOpen={true} className="w-full rounded-md data-[state=open]:bg-muted">
<Collapsible
defaultOpen={true}
className="w-full rounded-md data-[state=open]:bg-muted"
>
<CollapsibleTrigger asChild>
<Button
variant="ghost"
className="group w-full bg-transparent"
>
<Button variant="ghost" className="group w-full bg-transparent">
Description

<ChevronDownIcon className="ml-auto transition-transform group-data-[state=open]:rotate-180" />
</Button>
</CollapsibleTrigger>
Expand All @@ -121,18 +103,14 @@ export default function NoteCard({
</CollapsibleContent>
</Collapsible>
) : (
<p className="text-sm text-gray-400">
Loading Case Reports...
</p>
<p className="text-sm text-gray-400">Loading Case Reports...</p>
)}
</CardContent>

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

return (
<Badge
Expand Down Expand Up @@ -178,4 +156,4 @@ export default function NoteCard({
</CardFooter>
</Card>
);
}
}
26 changes: 13 additions & 13 deletions components/NotesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default async function NotesList({
relatedConcepts,
notes,
conceptDetails,
total,
total
}: {
relatedConcepts: Array<{
inputConceptId: string;
Expand All @@ -32,7 +32,7 @@ export default async function NotesList({
.map((note) => note.note_source_value?.match(/PMC(\d+)/)?.[1])
.filter((id): id is string => Boolean(id)),
),
);
).sort();
if (pmcids.length === 0) return;
const articles =
pmcids.length > 0 ? await getMultiplePmcArticles(pmcids) : {};
Expand Down Expand Up @@ -99,18 +99,18 @@ export default async function NotesList({
</Badge>
</div>
<div className="grid grid-cols-1 gap-4 py-1">
{notes.map((note) => {
const pmcid = note.note_source_value?.match(/PMC(\d+)/)?.[1];
{notes.map((note) => {
const pmcid = note.note_source_value?.match(/PMC(\d+)/)?.[1];

return (
<NoteCard
key={note.note_id}
note={note}
article={pmcid ? articles[pmcid] : null}
/>
);
})}
</div>
return (
<NoteCard
key={note.note_id}
note={note}
article={pmcid ? articles[pmcid] : null}
/>
);
})}
</div>
</div>
)}
</CardContent>
Expand Down
13 changes: 5 additions & 8 deletions components/NotesPagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,13 @@ export default function NotesPagination({
const searchParams = useSearchParams();

const createPageURL = (newPage: number) => {
const params = new URLSearchParams();
const params = new URLSearchParams(searchParams.toString());

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

// Add the page number
params.set("page", String(newPage));
Expand Down
14 changes: 6 additions & 8 deletions lib/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
export function buildSearchParams({
conceptIds = [],
ids = [],
domain,
page = 1,
}: {
conceptIds?: string[];
ids?: string[];
domain?: string | null;
page?: number;
}) {
Expand All @@ -13,12 +13,10 @@ export function buildSearchParams({
if (domain && domain !== "All") {
params.set("domain", domain);
}

// Sort and add unique conceptIds
[...new Set(conceptIds)]
.map(String)
.forEach((id) => params.append("conceptId", id));

// Add unique conceptIds
if (ids.length) {
params.set("ids", [...new Set(ids)].join(","));
}
// Set page
params.set("page", String(page));

Expand Down
Loading