diff --git a/src/components/CodeEditor/useMonacoCustomizations.ts b/src/components/CodeEditor/useMonacoCustomizations.ts
index b1a9dce9..68abab40 100644
--- a/src/components/CodeEditor/useMonacoCustomizations.ts
+++ b/src/components/CodeEditor/useMonacoCustomizations.ts
@@ -165,7 +165,6 @@ export default function useMonacoCustomizations({
rendererOptions: { "just-types": "true" },
});
const newLib = result.lines.join("\n").replaceAll("export ", "");
- // console.log(newLib);
monaco?.languages.typescript.javascriptDefaults.addExtraLib(newLib);
}
};
@@ -191,7 +190,6 @@ export default function useMonacoCustomizations({
// Set row definitions
useEffect(() => {
if (!monaco || !rowyRun || !tableState?.columns) return;
- console.log("setting row definitions");
try {
const rowDefinition =
Object.keys(tableState.columns)
diff --git a/src/components/Table/ContextMenu/MenuContent.tsx b/src/components/Table/ContextMenu/MenuContent.tsx
index f092ef2d..579b909e 100644
--- a/src/components/Table/ContextMenu/MenuContent.tsx
+++ b/src/components/Table/ContextMenu/MenuContent.tsx
@@ -1,4 +1,4 @@
-import { Menu } from "@mui/material";
+import { Divider, Menu } from "@mui/material";
import { default as MenuItem } from "./MenuItem";
import { IContextMenuItem } from "./MenuItem";
@@ -6,14 +6,14 @@ interface IMenuContents {
anchorEl: HTMLElement;
open: boolean;
handleClose: () => void;
- items: IContextMenuItem[];
+ groups: IContextMenuItem[][];
}
export function MenuContents({
anchorEl,
open,
handleClose,
- items,
+ groups,
}: IMenuContents) {
const handleContext = (e: React.MouseEvent) => e.preventDefault();
@@ -41,8 +41,13 @@ export function MenuContents({
}}
onContextMenu={handleContext}
>
- {items.map((item, indx: number) => (
-
+ {groups.map((items, groupIndex) => (
+ <>
+ {groupIndex > 0 && }
+ {items.map((item, index: number) => (
+
+ ))}
+ >
))}
);
diff --git a/src/components/Table/ContextMenu/index.tsx b/src/components/Table/ContextMenu/index.tsx
index 620b27be..caddca08 100644
--- a/src/components/Table/ContextMenu/index.tsx
+++ b/src/components/Table/ContextMenu/index.tsx
@@ -3,26 +3,88 @@ import { getColumnType, getFieldProp } from "@src/components/fields";
import { useProjectContext } from "@src/contexts/ProjectContext";
import { MenuContents } from "./MenuContent";
import { useContextMenuAtom } from "@src/atoms/ContextMenu";
-
+import { FieldType } from "@src/constants/fields";
+import DuplicateIcon from "@src/assets/icons/Copy";
+import DeleteIcon from "@mui/icons-material/DeleteOutlined";
+import { useAppContext } from "@src/contexts/AppContext";
+import { IContextMenuItem } from "./MenuItem";
+import { useConfirmation } from "@src/components/ConfirmationDialog/Context";
export default function ContextMenu() {
- const { tableState } = useProjectContext();
+ const { requestConfirmation } = useConfirmation();
+ const { tableState, deleteRow, addRow } = useProjectContext();
+ const { userRoles } = useAppContext();
const { anchorEle, selectedCell, resetContextMenu } = useContextMenuAtom();
const columns = tableState?.columns;
const selectedColIndex = selectedCell?.colIndex;
const selectedColumn = _find(columns, { index: selectedColIndex });
if (!selectedColumn) return <>>;
- const configActions =
- getFieldProp("contextMenuActions", selectedColumn.type) ||
- function empty() {};
- const actions = configActions(selectedCell, resetContextMenu) || [];
+ const menuActions = getFieldProp("contextMenuActions", selectedColumn.type);
- if (!anchorEle || actions.length === 0) return <>>;
+ const actionGroups: IContextMenuItem[][] = [];
+
+ const actions = menuActions
+ ? menuActions(selectedCell, resetContextMenu)
+ : [];
+ if (actions.length > 0) actionGroups.push(actions);
+
+ let hasRenderedFieldActions = false;
+ if (selectedColumn.type === FieldType.derivative) {
+ const renderedFieldMenuActions = getFieldProp(
+ "contextMenuActions",
+ selectedColumn.config.renderFieldType
+ );
+ if (renderedFieldMenuActions) {
+ actionGroups.push(
+ renderedFieldMenuActions(selectedCell, resetContextMenu)
+ );
+ hasRenderedFieldActions = true;
+ }
+ }
+ if (!anchorEle || (actions.length === 0 && !hasRenderedFieldActions))
+ return <>>;
+ const row = tableState?.rows[selectedCell!.rowIndex];
+ if (userRoles.includes("ADMIN") && row) {
+ const rowActions = [
+ {
+ label: "Duplicate Row",
+ icon: ,
+ onClick: () => {
+ const { ref, ...clonedRow } = row;
+ addRow!(clonedRow, undefined, { type: "smaller" });
+ },
+ },
+ {
+ label: "Delete Row",
+ variant: "secondary",
+ icon: ,
+ onClick: () => {
+ requestConfirmation({
+ title: "Delete row?",
+ customBody: (
+ <>
+ Row path:
+
+
+ {row.ref.path}
+
+ >
+ ),
+ confirm: "Delete",
+ confirmColor: "error",
+ handleConfirm: () => deleteRow?.(row.id),
+ });
+ resetContextMenu();
+ },
+ },
+ ];
+ actionGroups.push(rowActions);
+ }
return (
);
}
diff --git a/src/components/Table/formatters/FinalColumn.tsx b/src/components/Table/formatters/FinalColumn.tsx
index 534bf983..afe1935d 100644
--- a/src/components/Table/formatters/FinalColumn.tsx
+++ b/src/components/Table/formatters/FinalColumn.tsx
@@ -45,14 +45,8 @@ export default function FinalColumn({ row }: FormatterProps) {
color="inherit"
disabled={!addRow}
onClick={() => {
- const clonedRow = { ...row };
- // remove metadata
- delete clonedRow.ref;
- delete clonedRow.rowHeight;
- Object.keys(clonedRow).forEach((key) => {
- if (clonedRow[key] === undefined) delete clonedRow[key];
- });
- if (addRow) addRow!(clonedRow, undefined, { type: "smaller" });
+ const { ref, ...clonedRow } = row;
+ addRow!(clonedRow, undefined, { type: "smaller" });
}}
aria-label="Duplicate row"
className="row-hover-iconButton"
diff --git a/src/components/fields/Derivative/ContextMenuActions.tsx b/src/components/fields/Derivative/ContextMenuActions.tsx
index e05f22f1..626da1ad 100644
--- a/src/components/fields/Derivative/ContextMenuActions.tsx
+++ b/src/components/fields/Derivative/ContextMenuActions.tsx
@@ -1,15 +1,11 @@
import _find from "lodash/find";
import _get from "lodash/get";
-import Cut from "@mui/icons-material/ContentCut";
-import CopyCells from "@src/assets/icons/CopyCells";
-import Paste from "@mui/icons-material/ContentPaste";
-import EvalIcon from "@mui/icons-material/Replay";
-
+import ReEvalIcon from "@mui/icons-material/Replay";
+import EvalIcon from "@mui/icons-material/PlayCircle";
import { useProjectContext } from "@src/contexts/ProjectContext";
import { useSnackbar } from "notistack";
import { SelectedCell } from "@src/atoms/ContextMenu";
-import { getFieldProp, getColumnType } from "@src/components/fields";
import { runRoutes } from "@src/constants/runRoutes";
export interface IContextMenuActions {
@@ -22,7 +18,7 @@ export default function ContextMenuActions(
selectedCell: SelectedCell,
reset: () => void | Promise
): IContextMenuActions[] {
- const { tableState, deleteCell, updateCell, rowyRun } = useProjectContext();
+ const { tableState, rowyRun } = useProjectContext();
const { enqueueSnackbar } = useSnackbar();
const columns = tableState?.columns;
const rows = tableState?.rows;
@@ -30,18 +26,18 @@ export default function ContextMenuActions(
const selectedColIndex = selectedCell?.colIndex;
const selectedCol = _find(columns, { index: selectedColIndex });
if (!selectedCol) return [];
+ // don't show evalute button if function has external dependency
+ const code =
+ selectedCol.config.derivativeFn ?? selectedCol.config.script ?? "";
+ if (code.includes("require(")) return [];
const selectedRow = rows?.[selectedRowIndex];
const cellValue = _get(selectedRow, selectedCol.key);
- console.log({
- selectedCol,
- schemaDocPath: tableState?.config.tableConfig.path,
- });
const handleClose = async () => await reset?.();
const handleEvaluate = async () => {
try {
if (!selectedCol || !rowyRun || !selectedRow) return;
- rowyRun({
+ const result = await rowyRun({
route: runRoutes.evaluateDerivative,
body: {
ref: {
@@ -51,13 +47,22 @@ export default function ContextMenuActions(
columnKey: selectedCol.key,
},
});
+ if (result.success === false) {
+ enqueueSnackbar(result.message, { variant: "error" });
+ }
} catch (error) {
enqueueSnackbar(`Failed: ${error}`, { variant: "error" });
}
handleClose();
};
+ const isEmpty =
+ cellValue === "" || cellValue === null || cellValue === undefined;
const contextMenuActions = [
- { label: "evalute", icon: , onClick: handleEvaluate },
+ {
+ label: isEmpty ? "Evaluate" : "Re-evaluate",
+ icon: isEmpty ? : ,
+ onClick: handleEvaluate,
+ },
];
return contextMenuActions;
diff --git a/src/hooks/useTable/useTableData.tsx b/src/hooks/useTable/useTableData.tsx
index e1e2b657..a0087a78 100644
--- a/src/hooks/useTable/useTableData.tsx
+++ b/src/hooks/useTable/useTableData.tsx
@@ -35,8 +35,6 @@ const rowsReducer = (prevRows: any, update: any) => {
switch (update.type) {
case "onSnapshot":
const snapshotDocs = update.docs;
- console.log("onSnapshot", snapshotDocs);
-
// Get rows that may not be part of the snapshot
// Rows with missing required fields haven’t been written to the db yet
// Out of order rows will appear on top
@@ -168,7 +166,6 @@ const useTableData = () => {
const unsubscribe = query.limit(limit).onSnapshot(
(snapshot) => {
- console.log("snapshot", snapshot);
// if (snapshot.docs.length > 0) {
rowsDispatch({
type: "onSnapshot",