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
9 changes: 3 additions & 6 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,12 @@ function PageContent({

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

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

</Card>
</div>
</div>
Expand Down
45 changes: 22 additions & 23 deletions components/ConceptList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ 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";

export default function ConceptList({ concepts }: { concepts: Concept[] }) {
const router = useRouter();
Expand All @@ -12,34 +13,30 @@ export default function ConceptList({ concepts }: { concepts: Concept[] }) {
const selectedConcepts = searchParams.getAll("conceptId").sort();

const onSelect = (conceptId: string) => {
const currentParams = searchParams.getAll("conceptId").sort();
const current = searchParams.getAll("conceptId");

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

if (currentParams.includes(conceptId)) {
nextConceptIds = currentParams.filter((id) => id !== conceptId);
} else {
nextConceptIds = [...currentParams, conceptId];
}
const params = new URLSearchParams();
const query = buildSearchParams({
conceptIds: nextConceptIds,
domain: searchParams.get("domain"),
page: 1,
});

// Keep domain if exists
const domain = searchParams.get("domain");
if (domain && domain !== "All") {
params.set("domain", domain);
}

// Add conceptIds to Url
nextConceptIds.sort();
nextConceptIds.forEach((id) => params.append("conceptId", id));

// Reset page number
params.set("page", "1");
router.replace(`${pathname}?${params.toString()}`, { scroll: false });
router.replace(`${pathname}?${query}`, { scroll: false });
router.refresh();
};
const clearSelection = () => {
router.replace(pathname);
const query = buildSearchParams({
conceptIds: [],
domain: searchParams.get("domain"),
page: 1,
});

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

};

return (
Expand All @@ -62,7 +59,9 @@ export default function ConceptList({ concepts }: { concepts: Concept[] }) {
: "ghost"
}
className={`w-full justify-between h-auto py-3 px-4 ${
selectedConcepts.includes(c.concept_id) ? "bg-mist-100 dark:bg-neutral-800" : ""
selectedConcepts.includes(c.concept_id)
? "bg-mist-100 dark:bg-neutral-800"
: ""
}`}
onClick={() => onSelect(c.concept_id)}
>
Expand Down
53 changes: 44 additions & 9 deletions components/NoteCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ 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 @@ -31,8 +32,12 @@ export default function NoteCard({
const pathname = usePathname();
const searchParams = useSearchParams();

const [isPending, startTransition] = useTransition();

const selectedConceptIds = searchParams.getAll("conceptId");

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

const uniqueConcepts = Array.from(
new Map(
(note.concepts || []).map((c: any) => [
Expand All @@ -58,12 +63,21 @@ export default function NoteCard({
page: 1,
});

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

return (
<Card className="bg-zinc-50 ring-foreground/15 dark:bg-neutral-800 mb-5">
<Card
className={`
bg-zinc-50 ring-foreground/15 dark:bg-neutral-800 mb-5
transition-opacity
${isPending ? "opacity-60 pointer-events-none" : ""}
`}
>
<CardHeader>
<CardTitle className="text-lg">
{article?.articleUrl && pmcid ? (
Expand All @@ -75,17 +89,29 @@ export default function NoteCard({
{article.title}
</Link>
) : (
<p className="text-sm text-gray-400">Loading article...</p>
<p className="text-sm text-gray-400">
Loading article...
</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 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 @@ -95,39 +121,48 @@ export default function NoteCard({
</CollapsibleContent>
</Collapsible>
) : (
<p className="text-sm text-gray-400">Loading article...</p>
<p className="text-sm text-gray-400">
Loading article...
</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 = selectedConceptIds.includes(
String(c.concept_id),
);

return (
<Badge
key={c.concept_id}
variant={"secondary"}
variant="secondary"
onClick={() => selectConcept(String(c.concept_id))}
className={`
cursor-pointer transition hover:opacity-80
flex flex-wrap gap-2 py-0 text-sm

${selected ? "ring-2 ring-primary" : ""}

${
c.domain === "Condition"
? "bg-sky-100 dark:bg-[#1B3C53]"
: ""
}

${
c.domain === "Drug"
? "bg-emerald-100 dark:bg-[#3F4F44]"
: ""
}

${
c.domain === "Procedure"
? "bg-violet-100 dark:bg-[#49243E]"
: ""
}

${
c.domain === "Measurement"
? "bg-orange-100 dark:bg-amber-800"
Expand All @@ -143,4 +178,4 @@ export default function NoteCard({
</CardFooter>
</Card>
);
}
}
3 changes: 2 additions & 1 deletion lib/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export function buildSearchParams({

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

// Set page
Expand Down
Loading