Skip to content

Commit

Permalink
Merge pull request #762 from Health-Informatics-UoN/fix/761/use-state
Browse files Browse the repository at this point in the history
Fix Mapper stops responding when add/delete concept quickly
  • Loading branch information
AndrewThien authored Jun 21, 2024
2 parents fcf241c + 2e3b2e3 commit 387bda6
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 49 deletions.
2 changes: 0 additions & 2 deletions app/next-client-app/api/concepts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ export async function addConcept(data: {}) {
},
body: JSON.stringify(data),
});
revalidatePath("");
} catch (error: any) {
// Only return a response when there is an error
return { errorMessage: error.message };
Expand All @@ -56,5 +55,4 @@ export async function deleteConcept(conceptId: number) {
"Content-type": "application/json",
},
});
revalidatePath("");
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ import { DataTableColumnHeader } from "@/components/data-table/DataTableColumnHe
import { ConceptTags } from "@/components/concepts/concept-tags";
import AddConcept from "@/components/concepts/add-concept";
import { EditButton } from "@/components/scanreports/EditButton";
import { Suspense } from "react";
import { Skeleton } from "@/components/ui/skeleton";

export const columns: ColumnDef<ScanReportField>[] = [
export const columns = (
addSR: (concept: ScanReportConcept, c: Concept) => void,
deleteSR: (id: number) => void
): ColumnDef<ScanReportField>[] => [
{
id: "Name",
accessorKey: "name",
Expand Down Expand Up @@ -51,7 +56,13 @@ export const columns: ColumnDef<ScanReportField>[] = [
enableSorting: false,
cell: ({ row }) => {
const { concepts } = row.original;
return <ConceptTags concepts={concepts ?? []} />;
return (
// Just in case the concepts tags need more time to load some data
// --> showing skeleton having same width with the concept tag area
<Suspense fallback={<Skeleton className="h-5 w-[250px]" />}>
<ConceptTags concepts={concepts ?? []} deleteSR={deleteSR} />
</Suspense>
);
},
},
{
Expand All @@ -67,6 +78,7 @@ export const columns: ColumnDef<ScanReportField>[] = [
parentId={scan_report_table.toString()}
location="SR-Fields"
disabled={canEdit ? false : true}
addSR={addSR}
/>
);
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
"use client";

import { ColumnDef } from "@tanstack/react-table";
import { DataTableColumnHeader } from "@/components/data-table/DataTableColumnHeader";
import { ConceptTags } from "@/components/concepts/concept-tags";
import AddConcept from "@/components/concepts/add-concept";
import { Suspense } from "react";
import { Skeleton } from "@/components/ui/skeleton";

export const columns: ColumnDef<ScanReportValue>[] = [
export const columns = (
addSR: (concept: ScanReportConcept, c: Concept) => void,
deleteSR: (id: number) => void
): ColumnDef<ScanReportValue>[] => [
{
id: "Value",
accessorKey: "value",
Expand Down Expand Up @@ -50,7 +54,13 @@ export const columns: ColumnDef<ScanReportValue>[] = [
enableSorting: false,
cell: ({ row }) => {
const { concepts } = row.original;
return <ConceptTags concepts={concepts ?? []} />;
return (
// Just in case the concepts tags need more time to load some data
// --> showing skeleton having same width with the concept tag area
<Suspense fallback={<Skeleton className="h-5 w-[250px]" />}>
<ConceptTags concepts={concepts ?? []} deleteSR={deleteSR} />
</Suspense>
);
},
},
{
Expand All @@ -66,6 +76,7 @@ export const columns: ColumnDef<ScanReportValue>[] = [
parentId={scan_report_field.toString()}
location="SR-Values"
disabled={canEdit ? false : true}
addSR={addSR}
/>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ import {
getScanReportTable,
getScanReportValues,
} from "@/api/scanreports";
import { DataTable } from "@/components/data-table";
import { objToQuery } from "@/lib/client-utils";
import { DataTableFilter } from "@/components/data-table/DataTableFilter";
import { FilterParameters } from "@/types/filter";
import { getConceptFilters, getScanReportConcepts } from "@/api/concepts";
import { addConceptsToResults } from "@/lib/concept-utils";
import { columns } from "./columns";
import { ButtonsRow } from "@/components/scanreports/ButtonsRow";
import { ConceptDataTable } from "@/components/concepts/ConceptDataTable";
import { columns } from "./columns";

interface ScanReportsValueProps {
params: {
Expand All @@ -42,13 +40,12 @@ export default async function ScanReportsValue({
const combinedParams = { ...defaultParams, ...searchParams };
const query = objToQuery(combinedParams);

const filter = <DataTableFilter filter="value" filterText="value" />;

const scanReportsValues = await getScanReportValues(query);
const scanReportsName = await getScanReport(id);
const tableName = await getScanReportTable(tableId);
const fieldName = await getScanReportField(fieldId);
const permissions = await getScanReportPermissions(id);
const scanReportsValues = await getScanReportValues(query);

const scanReportsConcepts = await getScanReportConcepts(
`object_id__in=${scanReportsValues.results
.map((item) => item.id)
Expand All @@ -60,12 +57,6 @@ export default async function ScanReportsValue({
scanReportsConcepts?.map((item) => item.concept).join(",")
)
: [];
const scanReportsResult = addConceptsToResults(
scanReportsValues.results,
scanReportsConcepts,
conceptsFilter,
permissions
);

return (
<div className="pt-10 px-16">
Expand Down Expand Up @@ -111,13 +102,17 @@ export default async function ScanReportsValue({
permissions={permissions.permissions}
/>
<div>
<DataTable
columns={columns}
data={scanReportsResult}
<ConceptDataTable
count={scanReportsValues.count}
Filter={filter}
clickableRow={false}
permissions={permissions}
scanReportsConcepts={scanReportsConcepts}
conceptsFilter={conceptsFilter}
scanReportsData={scanReportsValues.results}
defaultPageSize={defaultPageSize}
columns={columns}
clickable={false}
filterCol="value"
filterText="value "
/>
</div>
</div>
Expand Down
25 changes: 10 additions & 15 deletions app/next-client-app/app/scanreports/[id]/tables/[tableId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ import {
getScanReportPermissions,
getScanReportTable,
} from "@/api/scanreports";
import { DataTable } from "@/components/data-table";
import { objToQuery } from "@/lib/client-utils";
import { DataTableFilter } from "@/components/data-table/DataTableFilter";
import { FilterParameters } from "@/types/filter";
import { getConceptFilters, getScanReportConcepts } from "@/api/concepts";
import { addConceptsToResults } from "@/lib/concept-utils";
import { ButtonsRow } from "@/components/scanreports/ButtonsRow";
import { ConceptDataTable } from "@/components/concepts/ConceptDataTable";

interface ScanReportsFieldProps {
params: {
Expand All @@ -39,7 +37,6 @@ export default async function ScanReportsField({
};
const combinedParams = { ...defaultParams, ...searchParams };
const query = objToQuery(combinedParams);
const filter = <DataTableFilter filter="name" filterText="field" />;

const scanReportsFields = await getScanReportFields(query);
const scanReportsName = await getScanReport(id);
Expand All @@ -57,12 +54,6 @@ export default async function ScanReportsField({
scanReportsConcepts?.map((item) => item.concept).join(",")
)
: [];
const scanReportsResult = addConceptsToResults(
scanReportsFields.results,
scanReportsConcepts,
conceptsFilter,
permissions
);

return (
<div className="pt-10 px-16">
Expand Down Expand Up @@ -100,13 +91,17 @@ export default async function ScanReportsField({
permissions={permissions.permissions}
/>
<div>
<DataTable
columns={columns}
data={scanReportsResult}
<ConceptDataTable
count={scanReportsFields.count}
Filter={filter}
linkPrefix="fields/"
permissions={permissions}
scanReportsConcepts={scanReportsConcepts}
conceptsFilter={conceptsFilter}
scanReportsData={scanReportsFields.results}
defaultPageSize={defaultPageSize}
columns={columns}
filterCol="name"
filterText="field "
linkPrefix="fields/"
/>
</div>
</div>
Expand Down
94 changes: 94 additions & 0 deletions app/next-client-app/components/concepts/ConceptDataTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"use client";

import { DataTable } from "@/components/data-table";
import { DataTableFilter } from "@/components/data-table/DataTableFilter";
import { addConceptsToResults } from "@/lib/concept-utils";
import { useEffect, useState } from "react";

interface CustomDataTableProps<T> {
scanReportsData: T[];
scanReportsConcepts: ScanReportConcept[];
conceptsFilter: Concept[];
permissions: PermissionsResponse;
count: number;
defaultPageSize: 10 | 20 | 30 | 40 | 50;
columns: (
addConcept: (newConcept: ScanReportConcept, newConFilter: Concept) => void,
deleteConcept: (id: number) => void
) => any;
clickable?: boolean;
filterCol: string;
filterText: string;
linkPrefix?: string;
}

export function ConceptDataTable<
T extends { id: number; concepts?: Concept[]; permissions: Permission[] }
>({
scanReportsData,
scanReportsConcepts,
conceptsFilter,
permissions,
count,
defaultPageSize,
columns,
clickable,
filterCol,
filterText,
linkPrefix,
}: CustomDataTableProps<T>) {
const filter = <DataTableFilter filter={filterCol} filterText={filterText} />;

// Set the needed Concepts and Concepts filter in state, so we can mutate them individually.
const [neededConcepts, setNeededConcepts] = useState(scanReportsConcepts);
const [neededConceptFilter, setNeededConceptFilter] =
useState(conceptsFilter);

// Necessary for pagination
useEffect(() => {
setNeededConcepts(scanReportsConcepts);
}, [scanReportsConcepts]);

useEffect(() => {
setNeededConceptFilter(conceptsFilter);
}, [conceptsFilter]);

const deleteConcept = (id: number) => {
// filter it out.
const updatedConcepts = neededConcepts.filter(
(concept) => concept.id !== id
);
setNeededConcepts(updatedConcepts);
};

const addConcept = (newConcept: ScanReportConcept, newConFilter: Concept) => {
// merge it.

const updatedConcepts = [...neededConcepts, newConcept];
setNeededConcepts(updatedConcepts);

const updatedConceptFilters = [...neededConceptFilter, newConFilter];
setNeededConceptFilter(updatedConceptFilters);
};

const scanReportsResult = addConceptsToResults(
scanReportsData,
neededConcepts,
neededConceptFilter,
permissions
);

return (
<div>
<DataTable
columns={columns(addConcept, deleteConcept)}
data={scanReportsResult}
count={count}
Filter={filter}
clickableRow={clickable}
defaultPageSize={defaultPageSize}
linkPrefix={linkPrefix}
/>
</div>
);
}
23 changes: 21 additions & 2 deletions app/next-client-app/components/concepts/add-concept.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { addConcept } from "@/api/concepts";
import {
addConcept,
getConceptFilters,
getScanReportConcepts,
} from "@/api/concepts";
import { getScanReportField } from "@/api/scanreports";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
Expand All @@ -10,13 +14,15 @@ interface AddConceptProps {
parentId: string;
location: string;
disabled: boolean;
addSR: (concept: ScanReportConcept, c: Concept) => void;
}

export default function AddConcept({
rowId,
parentId,
location,
disabled,
addSR,
}: AddConceptProps) {
const handleSubmit = async (conceptCode: number) => {
try {
Expand All @@ -38,10 +44,23 @@ export default function AddConcept({
creation_type: "M",
table_id: await determineTableId(location, parentId),
});
// Because the response only returned when there is an error

if (response) {
toast.error(`Adding concept failed. ${response.errorMessage}`);
} else {
const newConcepts = await getScanReportConcepts(`object_id=${rowId}`);
const filteredConcepts = await getConceptFilters(
newConcepts?.map((item) => item.concept).join(",")
);
// Filter the concept and concept filter
const newConcept = newConcepts.filter(
(c) => c.concept == conceptCode
)[0];
const filteredConcept = filteredConcepts.filter(
(c) => c.concept_id == conceptCode
)[0];

addSR(newConcept, filteredConcept);
toast.success(`OMOP Concept successfully added.`);
}
} catch (error) {
Expand Down
Loading

0 comments on commit 387bda6

Please sign in to comment.