diff --git a/webapp/src/components/App/Singlestudy/explore/TableModeList/index.tsx b/webapp/src/components/App/Singlestudy/explore/TableModeList/index.tsx
index 1f381e17bc..c281d9c774 100644
--- a/webapp/src/components/App/Singlestudy/explore/TableModeList/index.tsx
+++ b/webapp/src/components/App/Singlestudy/explore/TableModeList/index.tsx
@@ -13,10 +13,11 @@
*/
import { useEffect, useState } from "react";
-import { MenuItem } from "@mui/material";
+import { Button } from "@mui/material";
import { useOutletContext } from "react-router";
import { useUpdateEffect } from "react-use";
import { useTranslation } from "react-i18next";
+import EditIcon from "@mui/icons-material/Edit";
import DeleteIcon from "@mui/icons-material/Delete";
import { v4 as uuidv4 } from "uuid";
import PropertiesView from "../../../../common/PropertiesView";
@@ -84,7 +85,25 @@ function TableModeList() {
// Event Handlers
////////////////////////////////////////////////////////////////
- const handleDeleteTemplate = () => {
+ const handleEditClick = () => {
+ if (selectedTemplate) {
+ setDialog({
+ type: "edit",
+ templateId: selectedTemplate.id,
+ });
+ }
+ };
+
+ const handleDeleteClick = () => {
+ if (selectedTemplate) {
+ setDialog({
+ type: "delete",
+ templateId: selectedTemplate.id,
+ });
+ }
+ };
+
+ const handleDelete = () => {
setTemplates((templates) =>
templates.filter((tp) => tp.id !== dialog?.templateId),
);
@@ -106,34 +125,6 @@ function TableModeList() {
currentElement={selectedTemplate?.id}
currentElementKeyToTest="id"
setSelectedItem={({ id }) => setSelectedTemplateId(id)}
- contextMenuContent={({ element, close }) => (
- <>
-
-
- >
- )}
/>
}
onAdd={() => setDialog({ type: "add", templateId: "" })}
@@ -148,6 +139,27 @@ function TableModeList() {
studyId={study.id}
type={selectedTemplate.type}
columns={selectedTemplate.columns}
+ extraActions={
+ <>
+ }
+ onClick={handleEditClick}
+ >
+ {t("global.edit")}
+
+ }
+ onClick={handleDeleteClick}
+ >
+ {t("global.delete")}
+
+ >
+ }
/>
)}
@@ -173,7 +185,7 @@ function TableModeList() {
diff --git a/webapp/src/components/App/Singlestudy/explore/common/ListElement.tsx b/webapp/src/components/App/Singlestudy/explore/common/ListElement.tsx
index 69b9254d61..5e799e1928 100644
--- a/webapp/src/components/App/Singlestudy/explore/common/ListElement.tsx
+++ b/webapp/src/components/App/Singlestudy/explore/common/ListElement.tsx
@@ -17,14 +17,11 @@ import {
ListItemButton,
ListItemIcon,
ListItemText,
- Menu,
- PopoverPosition,
SxProps,
Theme,
Tooltip,
} from "@mui/material";
import ArrowRightOutlinedIcon from "@mui/icons-material/ArrowRightOutlined";
-import { useState } from "react";
import { IdType } from "../../../../../common/types";
import { mergeSxProp } from "../../../../../utils/muiUtils";
@@ -33,10 +30,6 @@ interface Props {
currentElement?: string;
currentElementKeyToTest?: keyof T;
setSelectedItem: (item: T, index: number) => void;
- contextMenuContent?: (props: {
- element: T;
- close: VoidFunction;
- }) => React.ReactElement;
sx?: SxProps;
}
@@ -45,43 +38,8 @@ function ListElement({
currentElement,
currentElementKeyToTest,
setSelectedItem,
- contextMenuContent: ContextMenuContent,
sx,
}: Props) {
- const [contextMenuPosition, setContextMenuPosition] =
- useState(null);
- const [elementForContext, setElementForContext] = useState();
-
- ////////////////////////////////////////////////////////////////
- // Event Handlers
- ////////////////////////////////////////////////////////////////
-
- const handleContextMenu = (element: T) => (event: React.MouseEvent) => {
- event.preventDefault();
-
- if (!ContextMenuContent) {
- return;
- }
-
- setElementForContext(element);
-
- setContextMenuPosition(
- contextMenuPosition === null
- ? {
- left: event.clientX + 2,
- top: event.clientY - 6,
- }
- : // Repeated context menu when it is already open closes it with Chrome 84 on Ubuntu
- // Other native context menus might behave different.
- // With this behavior we prevent contextmenu from the backdrop to re-locale existing context menus.
- null,
- );
- };
-
- ////////////////////////////////////////////////////////////////
- // JSX
- ////////////////////////////////////////////////////////////////
-
return (
({
justifyContent: "space-between",
py: 0,
}}
- onContextMenu={handleContextMenu(element)}
>
({
>
- {ContextMenuContent && elementForContext && (
-
- )}
))}
diff --git a/webapp/src/components/common/Form/types.ts b/webapp/src/components/common/Form/types.ts
index d7f27f94fb..a09272e8f1 100644
--- a/webapp/src/components/common/Form/types.ts
+++ b/webapp/src/components/common/Form/types.ts
@@ -26,7 +26,6 @@ import {
} from "react-hook-form";
export interface SubmitHandlerPlus<
- // TODO Make parameter required
TFieldValues extends FieldValues = FieldValues,
> {
values: TFieldValues;
diff --git a/webapp/src/components/common/TableMode.tsx b/webapp/src/components/common/TableMode.tsx
index 1809a93377..c50f5ee000 100644
--- a/webapp/src/components/common/TableMode.tsx
+++ b/webapp/src/components/common/TableMode.tsx
@@ -25,47 +25,64 @@ import {
TableModeType,
} from "../../services/api/studies/tableMode/types";
import { SubmitHandlerPlus } from "./Form/types";
-import TableForm from "./TableForm";
import UsePromiseCond from "./utils/UsePromiseCond";
import GridOffIcon from "@mui/icons-material/GridOff";
import EmptyView from "./page/EmptyView";
import { useTranslation } from "react-i18next";
+import DataGridForm, { type DataGridFormProps } from "./DataGridForm";
+import { startCase } from "lodash";
+import type { GridColumn } from "@glideapps/glide-data-grid";
export interface TableModeProps {
studyId: StudyMetadata["id"];
type: T;
columns: TableModeColumnsForType;
+ extraActions?: React.ReactNode;
}
-function TableMode(props: TableModeProps) {
- const { studyId, type, columns } = props;
- const [filteredColumns, setFilteredColumns] = useState(columns);
+function TableMode({
+ studyId,
+ type,
+ columns,
+ extraActions,
+}: TableModeProps) {
const { t } = useTranslation();
+ const [gridColumns, setGridColumns] = useState<
+ DataGridFormProps["columns"]
+ >([]);
+ const columnsDep = columns.join(",");
const res = usePromise(
() => getTableMode({ studyId, tableType: type, columns }),
- [studyId, type, columns.join(",")],
+ [studyId, type, columnsDep],
);
// Filter columns based on the data received, because the API may return
// fewer columns than requested depending on the study version
- useEffect(
- () => {
- const dataKeys = Object.keys(res.data || {});
+ useEffect(() => {
+ const rowNames = Object.keys(res.data || {});
- if (dataKeys.length === 0) {
- setFilteredColumns([]);
- return;
- }
+ if (rowNames.length === 0) {
+ setGridColumns([]);
+ return;
+ }
- const data = res.data!;
- const dataRowKeys = Object.keys(data[dataKeys[0]]);
+ const data = res.data!;
+ const columnNames = Object.keys(data[rowNames[0]]);
- setFilteredColumns(columns.filter((col) => dataRowKeys.includes(col)));
- },
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [res.data, columns.join(",")],
- );
+ setGridColumns(
+ columns
+ .filter((col) => columnNames.includes(col))
+ .map((col) => {
+ const title = startCase(col);
+ return {
+ title,
+ id: col,
+ width: title.length * 10,
+ } satisfies GridColumn;
+ }),
+ );
+ }, [res.data, columnsDep]);
////////////////////////////////////////////////////////////////
// Event Handlers
@@ -83,15 +100,19 @@ function TableMode(props: TableModeProps) {
- filteredColumns.length > 0 ? (
- 0 ? (
+
) : (
-
+
)
}
/>