Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions app/next-client-app/api/scanreports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export async function getScanReportTable(
permissions: [],
jobs: [],
trigger_reuse: true,
death_table: false,
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { ColumnDef } from "@tanstack/react-table";
import { DataTableColumnHeader } from "@/components/data-table/DataTableColumnHeader";
import { EditButton } from "@/components/scanreports/EditButton";
import { Tooltips } from "@/components/core/Tooltips";
import JobDialog from "@/components/jobs/JobDialog";
import { FindGeneralStatus, DivideJobs } from "@/components/jobs/JobUtils";
import Link from "next/link";
Expand Down Expand Up @@ -89,6 +90,28 @@ export const columns: ColumnDef<ScanReportTable>[] = [
);
}
},
{
id: "note",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Note" />
),
cell: ({ row }) => {
const { death_table } = row.original;
return (
<div>
{death_table && (
<h3 className="flex">
{" "}
Death table
<Tooltips
content="This table is marked as a Death table. In the Carrot data standard, death data is typically provided in a separate file (e.g. death.csv) for mapping to the OMOP Death table."
Comment thread
AndrewThien marked this conversation as resolved.
/>
</h3>
)}
</div>
);
}
},
{
id: "edit",
header: ({ column }) => <DataTableColumnHeader column={column} title="" />,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ export default async function ScanReportsValue(props: ScanReportsValueProps) {
id={id}
tableId={tableId}
fieldId={fieldId}
tableName={table.name}
tableName={
table.death_table ? `${table.name} (Death table)` : table.name
}
fieldName={field.name}
variant="field"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ export default async function ScanReportsEditField(props: ScanReportsEditFieldPr
id={id}
tableId={tableId}
fieldId={fieldId}
tableName={table.name}
tableName={
table.death_table ? `${table.name} (Death table)` : table.name
}
fieldName={field.name}
variant="fieldUpdate"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ export default async function ScanReportsField(props: ScanReportsFieldProps) {
<TableBreadcrumbs
id={id}
tableId={tableId}
tableName={tableName.name}
tableName={
tableName.death_table
? `${tableName.name} (Death table)`
: tableName.name
}
variant="table"
/>
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export default async function UpdateTable(props: UpdateTableProps) {
<div>
<Link href={`/scanreports/${id}/tables/${tableId}`}>
<Button variant={"secondary"} className="mb-3">
Update Table: {table.name}
Update Table: {table.name} {table.death_table && "(Death table)"}
</Button>
</Link>
{(table.date_event === null || table.person_id === null) && (
Expand Down
43 changes: 24 additions & 19 deletions app/next-client-app/components/core/Tooltips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import {
} from "@/components/ui/tooltip";

import { InfoIcon } from "lucide-react";
import { ReactElement } from "react";

export function Tooltips({
content,
link,
side = "top",
}: {
content: string;
content: string | ReactElement;
Comment thread
AndrewThien marked this conversation as resolved.
Outdated
link?: string;
side?: "top" | "bottom" | "left" | "right";
}) {
Expand All @@ -26,24 +27,28 @@ export function Tooltips({
className="max-w-96 text-center whitespace-pre-wrap"
side={side}
>
<p>
{content}
{link && (
<>
{" "}
Find out more{" "}
<a
href={link}
className="underline"
target="_blank"
rel="noopener noreferrer"
>
here
</a>
.
</>
)}
</p>
{typeof content === "string" ? (
<p>
{content}
{link && (
<>
{" "}
Find out more{" "}
<a
href={link}
className="underline"
target="_blank"
rel="noopener noreferrer"
>
here
</a>
.
</>
)}
</p>
) : (
content
)}
</TooltipContent>
</Tooltip>
</TooltipProvider>
Expand Down
Comment thread
AndrewThien marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"use client";

import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "../ui/dialog";
import { toast } from "sonner";
import { Button } from "../ui/button";
import { updateScanReportTable } from "@/api/scanreports";
import { useRouter } from "next/navigation";

interface DeathTableConfirmDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
scanReportId: number;
tableId: number;
tableName: string;
isDeathTable: boolean;
onSuccess?: () => void;
}

export function DeathTableConfirmDialog({
open,
onOpenChange,
scanReportId,
tableId,
tableName,
isDeathTable,
onSuccess,
}: DeathTableConfirmDialogProps) {
const router = useRouter();
const markingAsDeath = !isDeathTable;

const handleConfirm = async () => {
const response = await updateScanReportTable(
scanReportId,
tableId,
{ death_table: markingAsDeath },
);
if (response) {
toast.error(
`Failed to update DEATH table setting: ${response.errorMessage}`,
);
} else {
toast.success(
markingAsDeath
? "Table marked as DEATH table successfully."
: "Table unmarked as DEATH table successfully.",
);
onOpenChange(false);
onSuccess?.();
router.refresh();
}
};

return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader className="text-start">
<DialogTitle>
{markingAsDeath ? "Mark as DEATH table" : "Unmark as DEATH table"}
</DialogTitle>
<DialogDescription>
{markingAsDeath ? (
<>
Are you sure you want to mark &quot;{tableName}&quot; as a
DEATH table? This will enable auto-mapping as a death table for
Comment thread
AndrewThien marked this conversation as resolved.
Outdated
any death SR uploads.
Comment thread
AndrewThien marked this conversation as resolved.
Outdated
</>
) : (
<>
Are you sure you want to unmark &quot;{tableName}&quot; as a
DEATH table? Auto-mapping as a death table will no longer apply
to this table.
</>
)}
</DialogDescription>
</DialogHeader>
<DialogFooter className="flex-col space-y-2 sm:space-y-0 sm:space-x-2">
<Button onClick={handleConfirm}>
{markingAsDeath ? "Mark as DEATH table" : "Unmark"}
</Button>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
"use client";

import { useState } from "react";
import { Button } from "@/components/ui/button";
import { updateScanReportTable } from "@/api/scanreports";
import { Save } from "lucide-react";
import { Check, Save, X } from "lucide-react";
import { toast } from "sonner";
import { FormDataFilter } from "../form-components/FormikUtils";
import { Formik } from "formik";
import { FormField, FormItem, FormLabel, FormControl, FormMessage, FormDescription } from "@/components/ui/form";
import { FormikSelect } from "../form-components/FormikSelect";
import { useRouter } from "next/navigation";
import { Checkbox } from "../ui/checkbox";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Tooltips } from "@/components/core/Tooltips";
import { enableReuseTriggerOption } from "@/constants";

interface FormData {
personId: number | null;
dateEvent: number | null;
triggerReuse: boolean;
death_table: boolean;
}

export function ScanReportTableUpdateForm({
Expand All @@ -32,6 +45,7 @@ export function ScanReportTableUpdateForm({
dateEvent: ScanReportField;
}) {
const router = useRouter();
const [isDialogOpen, setIsDialogOpen] = useState(false);
const canUpdate =
permissions.includes("CanEdit") || permissions.includes("CanAdmin");

Expand All @@ -45,6 +59,7 @@ export function ScanReportTableUpdateForm({
person_id: data.personId !== 0 ? data.personId : null,
date_event: data.dateEvent !== 0 ? data.dateEvent : null,
trigger_reuse: data.triggerReuse,
death_table: data.death_table,
};

const response = await updateScanReportTable(
Expand All @@ -68,6 +83,7 @@ export function ScanReportTableUpdateForm({
dateEvent: initialDateEvent[0].value,
personId: initialPersonId[0].value,
triggerReuse: scanreportTable.trigger_reuse,
death_table: Boolean(scanreportTable.death_table),
}}
onSubmit={(data) => {
handleSubmit(data);
Expand Down Expand Up @@ -109,6 +125,83 @@ export function ScanReportTableUpdateForm({
</FormControl>
</FormItem>

<FormItem>
<div className="flex flex-wrap items-center gap-2">
<FormLabel className="mb-0">
Does this table contain only death data for the OMOP CDM Death
table?
</FormLabel>
<Tooltips
content="In the Carrot data standard, death data is provided in a separate file (e.g. death.csv). Mark Yes if this table contains that death data to be mapped to the OMOP Death table."
/>
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Please Confirm Your Choice</DialogTitle>
<DialogDescription>
Are you sure you want to set this table as a Death table?
Doing so will result in the following:
<ul className="text-gray-500 list-disc pl-4 py-2">
<li>
Mapping Rules that are created either manually or
automatically (built from OMOP vocabulary or Reused)
will have Destination table as{" "}
<span className="font-bold">Death</span>.
</li>
<li>
All concepts in this table will be recognised as{" "}
<span className="font-bold">Cause of Death</span> in
OMOP CDM.
</li>
<li>
Destination of Date Event will be{" "}
<span className="font-bold">Death date</span> field in
OMOP CDM.
</li>
</ul>
<p className="text-muted-foreground text-pretty">
You can turn off this setting later. Mapping rules will
be refreshed when you save.
</p>
</DialogDescription>
</DialogHeader>
<DialogFooter className="flex gap-3">
<Button
type="button"
onClick={() => setIsDialogOpen(false)}
variant="outline"
>
Cancel <X className="size-4 ml-2" />
</Button>
<Button
type="button"
onClick={() => {
setFieldValue("death_table", true);
setIsDialogOpen(false);
}}
>
Confirm <Check className="size-4 ml-2" />
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<Switch
checked={values.death_table}
onCheckedChange={(checked) => {
if (checked) {
setIsDialogOpen(true);
} else {
setFieldValue("death_table", false);
}
}}
disabled={!canUpdate}
/>
<Label className="text-lg">
{values.death_table === true ? "YES" : "NO"}
</Label>
</div>
</FormItem>

{enableReuseTriggerOption === "true" && (
<FormField name="triggerReuse">
{({ field }) => (
Expand Down
1 change: 1 addition & 0 deletions app/next-client-app/types/scanreport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ interface ScanReportTable {
permissions: Permission[];
jobs: Job[];
trigger_reuse: boolean;
death_table?: boolean;
}

interface ScanReportField {
Expand Down
Loading