restructure and consolidate TableModals

This commit is contained in:
Sidney Alcantara
2022-06-07 13:03:29 +10:00
parent 1f56ffcc64
commit 6ad2df45f5
80 changed files with 376 additions and 401 deletions

View File

@@ -3,7 +3,7 @@ import { atomWithStorage, atomWithHash } from "jotai/utils";
import type { PopoverProps } from "@mui/material";
import type { ColumnConfig, TableFilter } from "@src/types/table";
import { SEVERITY_LEVELS } from "@src/components/TableToolbar/CloudLogs/CloudLogSeverityIcon";
import { SEVERITY_LEVELS } from "@src/components/TableModals/CloudLogsModal/CloudLogSeverityIcon";
/**
* Open table column menu. Set to `null` to close.
@@ -80,7 +80,7 @@ export const sideDrawerShowHiddenFieldsAtom = atomWithStorage(
/**
* Opens a table modal. Set to `null` to close.
* Modals: cloud logs, extensions, webhooks, export.
* Modals: cloud logs, extensions, webhooks, export, import, import CSV.
*
* @example Basic usage:
* ```
@@ -94,9 +94,22 @@ export const sideDrawerShowHiddenFieldsAtom = atomWithStorage(
* ```
*/
export const tableModalAtom = atomWithHash<
"cloudLogs" | "extensions" | "webhooks" | "export" | null
| "cloudLogs"
| "extensions"
| "webhooks"
| "export"
| "import"
| "importCsv"
| null
>("tableModal", null, { replaceState: true });
export type ImportCsvData = { columns: string[]; rows: Record<string, any>[] };
/** Store import CSV popover and wizard state */
export const importCsvAtom = atom<{
importType: "csv" | "tsv";
csvData: ImportCsvData | null;
}>({ importType: "csv", csvData: null });
/** Store side drawer open state */
export const sideDrawerOpenAtom = atom(false);

View File

@@ -29,7 +29,7 @@ import { useSnackbar } from "notistack";
import { getTableSchemaPath } from "@src/utils/table";
export default function ColumnConfigModal({
handleClose,
onClose,
column,
}: IColumnModalProps) {
const [rowyRun] = useAtom(rowyRunAtom, globalScope);
@@ -82,7 +82,7 @@ export default function ColumnConfigModal({
return (
<Modal
maxWidth="md"
onClose={handleClose}
onClose={onClose}
title={`${column.name}: Config`}
disableBackdropClick
disableEscapeKeyDown
@@ -203,13 +203,13 @@ export default function ColumnConfigModal({
closeSnackbar(savingSnack);
enqueueSnackbar("Changes saved");
handleClose();
onClose();
setShowRebuildPrompt(false);
},
children: "Update",
},
secondary: {
onClick: handleClose,
onClick: onClose,
children: "Cancel",
},
}}

View File

@@ -14,7 +14,7 @@ import {
import { ColumnConfig } from "@src/types/table";
export interface IColumnModalProps {
handleClose: () => void;
onClose: () => void;
column: ColumnConfig;
}
@@ -24,22 +24,21 @@ export default function ColumnModals() {
if (!columnModal) return null;
const handleClose = () => setColumnModal(RESET);
const onClose = () => setColumnModal(RESET);
if (columnModal.type === "new")
return <NewColumnModal handleClose={handleClose} />;
if (columnModal.type === "new") return <NewColumnModal onClose={onClose} />;
const column = tableSchema.columns?.[columnModal.columnKey ?? ""];
if (!column) return null;
if (columnModal.type === "name")
return <NameChangeModal handleClose={handleClose} column={column} />;
return <NameChangeModal onClose={onClose} column={column} />;
if (columnModal.type === "type")
return <TypeChangeModal handleClose={handleClose} column={column} />;
return <TypeChangeModal onClose={onClose} column={column} />;
if (columnModal.type === "config")
return <ColumnConfigModal handleClose={handleClose} column={column} />;
return <ColumnConfigModal onClose={onClose} column={column} />;
return null;
}

View File

@@ -8,7 +8,7 @@ import Modal from "@src/components/Modal";
import { tableScope, updateColumnAtom } from "@src/atoms/tableScope";
export default function NameChangeModal({
handleClose,
onClose,
column,
}: IColumnModalProps) {
const updateColumn = useSetAtom(updateColumnAtom, tableScope);
@@ -16,7 +16,7 @@ export default function NameChangeModal({
return (
<Modal
onClose={handleClose}
onClose={onClose}
title="Rename column"
maxWidth="xs"
children={
@@ -35,12 +35,12 @@ export default function NameChangeModal({
primary: {
onClick: () => {
updateColumn({ key: column.key, config: { name: newName } });
handleClose();
onClose();
},
children: "Update",
},
secondary: {
onClick: handleClose,
onClick: onClose,
children: "Cancel",
},
}}

View File

@@ -27,8 +27,8 @@ const AUDIT_FIELD_TYPES = [
];
export default function NewColumnModal({
handleClose,
}: Pick<IColumnModalProps, "handleClose">) {
onClose,
}: Pick<IColumnModalProps, "onClose">) {
const [updateTable] = useAtom(updateTableAtom, globalScope);
const [tableSettings] = useAtom(tableSettingsAtom, tableScope);
const addColumn = useSetAtom(addColumnAtom, tableScope);
@@ -73,7 +73,7 @@ export default function NewColumnModal({
return (
<Modal
onClose={handleClose}
onClose={onClose}
title="Add new column"
fullWidth
maxWidth="xs"
@@ -159,7 +159,7 @@ export default function NewColumnModal({
if (requireConfiguration) {
setColumnModal({ type: "config", columnKey: fieldKey });
} else {
handleClose();
onClose();
}
logEvent(analytics, "create_column", { type });
},
@@ -171,7 +171,7 @@ export default function NewColumnModal({
children: requireConfiguration ? "Next" : "Add",
},
secondary: {
onClick: handleClose,
onClick: onClose,
children: "Cancel",
},
}}

View File

@@ -12,7 +12,7 @@ import { getFieldProp } from "@src/components/fields";
import { analytics, logEvent } from "analytics";
export default function TypeChangeModal({
handleClose,
onClose,
column,
}: IColumnModalProps) {
const updateColumn = useSetAtom(updateColumnAtom, tableScope);
@@ -20,7 +20,7 @@ export default function TypeChangeModal({
return (
<Modal
onClose={handleClose}
onClose={onClose}
title="Change column type"
children={
<>
@@ -41,7 +41,7 @@ export default function TypeChangeModal({
onClick: () => {
const prevType = column.type;
updateColumn({ key: column.key, config: { type: newType } });
handleClose();
onClose();
logEvent(analytics, "change_column_type", { newType, prevType });
},
children: "Update",

View File

@@ -52,7 +52,7 @@ export default function ConfirmDialog() {
}}
maxWidth={maxWidth}
TransitionComponent={SlideTransitionMui}
style={{ cursor: "default" }}
sx={{ cursor: "default", zIndex: (theme) => theme.zIndex.modal + 50 }}
>
<DialogTitle>{title}</DialogTitle>

View File

@@ -13,7 +13,7 @@ import {
import { APP_BAR_HEIGHT } from "@src/layouts/Navigation";
// FIXME:
// import ImportWizard from "@src/components/TableWizards/ImportWizard";
// import ImportWizard from "@src/components/TableModals/ImportWizard";
// import ImportCSV from "@src/components/TableToolbar/ImportCsv";
export default function EmptyTable() {

View File

@@ -19,7 +19,7 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import EmptyState from "@src/components/EmptyState";
import BuildLogList from "./BuildLogList";
import CloudLogSubheader from "@src/components/TableToolbar/CloudLogs/CloudLogSubheader";
import CloudLogSubheader from "@src/components/TableModals/CloudLogsModal/CloudLogSubheader";
import { DATE_TIME_FORMAT } from "@src/constants/dates";
import useBuildLogs from "./useBuildLogs";

View File

@@ -1,6 +1,7 @@
import useSWR from "swr";
import { useAtom } from "jotai";
import { startCase } from "lodash-es";
import { ITableModalProps } from "@src/components/TableModals";
import {
LinearProgress,
@@ -15,7 +16,7 @@ import {
import RefreshIcon from "@mui/icons-material/Refresh";
import { CloudLogs as LogsIcon } from "@src/assets/icons";
import Modal, { IModalProps } from "@src/components/Modal";
import Modal from "@src/components/Modal";
import TableToolbarButton from "@src/components/TableToolbar/TableToolbarButton";
import MultiSelect from "@rowy/multiselect";
import TimeRangeSelect from "./TimeRangeSelect";
@@ -38,7 +39,7 @@ import {
} from "@src/atoms/tableScope";
import { cloudLogFetcher } from "./utils";
export default function CloudLogsModal(props: IModalProps) {
export default function CloudLogsModal({ onClose }: ITableModalProps) {
const [projectId] = useAtom(projectIdAtom, globalScope);
const [rowyRun] = useAtom(rowyRunAtom, globalScope);
const [compatibleRowyRunVersion] = useAtom(
@@ -74,7 +75,8 @@ export default function CloudLogsModal(props: IModalProps) {
return (
<Modal
{...props}
title="Cloud logs"
onClose={onClose}
maxWidth="xl"
fullWidth
fullHeight

View File

@@ -0,0 +1,2 @@
export * from "./CloudLogsModal";
export { default } from "./CloudLogsModal";

View File

@@ -1,10 +1,8 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import { useAtom, useSetAtom } from "jotai";
import { RESET } from "jotai/utils";
import { isEqual } from "lodash-es";
import { ITableModalProps } from "@src/components/TableModals";
import TableToolbarButton from "@src/components/TableToolbar/TableToolbarButton";
import { Extension as ExtensionIcon } from "@src/assets/icons";
import Modal from "@src/components/Modal";
import AddExtensionButton from "./AddExtensionButton";
import ExtensionList from "./ExtensionList";
@@ -14,9 +12,7 @@ import ExtensionMigration from "./ExtensionMigration";
import {
globalScope,
currentUserAtom,
projectSettingsAtom,
rowyRunAtom,
rowyRunModalAtom,
confirmDialogAtom,
} from "@src/atoms/globalScope";
import {
@@ -24,7 +20,6 @@ import {
tableSettingsAtom,
tableSchemaAtom,
updateTableSchemaAtom,
tableModalAtom,
} from "@src/atoms/tableScope";
import { useSnackLogContext } from "@src/contexts/SnackLogContext";
@@ -33,16 +28,13 @@ import { runRoutes } from "@src/constants/runRoutes";
import { analytics, logEvent } from "@src/analytics";
import { getTableSchemaPath } from "@src/utils/table";
export default function Extensions() {
export default function ExtensionsModal({ onClose }: ITableModalProps) {
const [currentUser] = useAtom(currentUserAtom, globalScope);
const [projectSettings] = useAtom(projectSettingsAtom, globalScope);
const [rowyRun] = useAtom(rowyRunAtom, globalScope);
const openRowyRunModal = useSetAtom(rowyRunModalAtom, globalScope);
const confirm = useSetAtom(confirmDialogAtom, globalScope);
const [tableSettings] = useAtom(tableSettingsAtom, tableScope);
const [tableSchema] = useAtom(tableSchemaAtom, tableScope);
const [updateTableSchema] = useAtom(updateTableSchemaAtom, tableScope);
const [modal, setModal] = useAtom(tableModalAtom, tableScope);
const currentExtensionObjects = (tableSchema.extensionObjects ??
[]) as IExtension[];
@@ -50,10 +42,11 @@ export default function Extensions() {
currentExtensionObjects
);
const open = modal === "extensions";
const setOpen = (open: boolean) => setModal(open ? "extensions" : RESET);
const [openMigrationGuide, setOpenMigrationGuide] = useState(false);
useEffect(() => {
if (tableSchema.sparks) setOpenMigrationGuide(true);
}, [tableSchema.sparks]);
const [extensionModal, setExtensionModal] = useState<{
mode: "add" | "update";
extensionObject: IExtension;
@@ -63,25 +56,6 @@ export default function Extensions() {
const snackLogContext = useSnackLogContext();
const edited = !isEqual(currentExtensionObjects, localExtensionsObjects);
if (!projectSettings.rowyRunUrl)
return (
<TableToolbarButton
title="Extensions"
onClick={() => openRowyRunModal({ feature: "Extensions" })}
icon={<ExtensionIcon />}
/>
);
const handleOpen = () => {
if (tableSchema.sparks) {
// migration is required
console.log("Extension migration required.");
setOpenMigrationGuide(true);
} else {
setOpen(true);
}
};
const handleClose = (
_setOpen: React.Dispatch<React.SetStateAction<boolean>>
) => {
@@ -90,14 +64,15 @@ export default function Extensions() {
confirm({
title: "Discard changes?",
confirm: "Discard",
cancel: "Keep",
handleConfirm: () => {
_setOpen(false);
setLocalExtensionsObjects(currentExtensionObjects);
setOpen(false);
onClose();
},
});
} else {
setOpen(false);
onClose();
}
};
@@ -105,7 +80,7 @@ export default function Extensions() {
if (updateTableSchema)
await updateTableSchema({ extensionObjects: localExtensionsObjects });
if (callback) callback();
setOpen(false);
onClose();
};
const handleSaveDeploy = async () => {
@@ -216,55 +191,49 @@ export default function Extensions() {
return (
<>
<TableToolbarButton
title="Extensions"
onClick={handleOpen}
icon={<ExtensionIcon />}
<Modal
onClose={handleClose}
disableBackdropClick={edited}
disableEscapeKeyDown={edited}
maxWidth="sm"
fullWidth
title={`Extensions (${activeExtensionCount}\u2009/\u2009${localExtensionsObjects.length})`}
header={
<AddExtensionButton
handleAddExtension={(type: ExtensionType) => {
setExtensionModal({
mode: "add",
extensionObject: emptyExtensionObject(type, currentEditor()),
});
}}
variant={
localExtensionsObjects.length === 0 ? "contained" : "outlined"
}
/>
}
children={
<ExtensionList
extensions={localExtensionsObjects}
handleUpdateActive={handleUpdateActive}
handleEdit={handleEdit}
handleDuplicate={handleDuplicate}
handleDelete={handleDelete}
/>
}
actions={{
primary: {
children: "Save & Deploy",
onClick: handleSaveDeploy,
disabled: !edited,
},
secondary: {
children: "Save",
onClick: () => handleSaveExtensions(),
disabled: !edited,
},
}}
/>
{open && (
<Modal
onClose={handleClose}
disableBackdropClick={edited}
disableEscapeKeyDown={edited}
maxWidth="sm"
fullWidth
title={`Extensions (${activeExtensionCount}\u2009/\u2009${localExtensionsObjects.length})`}
header={
<AddExtensionButton
handleAddExtension={(type: ExtensionType) => {
setExtensionModal({
mode: "add",
extensionObject: emptyExtensionObject(type, currentEditor()),
});
}}
variant={
localExtensionsObjects.length === 0 ? "contained" : "outlined"
}
/>
}
children={
<ExtensionList
extensions={localExtensionsObjects}
handleUpdateActive={handleUpdateActive}
handleEdit={handleEdit}
handleDuplicate={handleDuplicate}
handleDelete={handleDelete}
/>
}
actions={{
primary: {
children: "Save & Deploy",
onClick: handleSaveDeploy,
disabled: !edited,
},
secondary: {
children: "Save",
onClick: () => handleSaveExtensions(),
disabled: !edited,
},
}}
/>
)}
{extensionModal && (
<ExtensionModal
handleClose={() => setExtensionModal(null)}
@@ -279,7 +248,6 @@ export default function Extensions() {
handleClose={() => setOpenMigrationGuide(false)}
handleUpgradeComplete={() => {
setOpenMigrationGuide(false);
setOpen(true);
}}
/>
)}

View File

@@ -11,7 +11,7 @@ import {
Checkbox,
} from "@mui/material";
import MultiSelect from "@rowy/multiselect";
import ColumnSelect from "@src/components/TableToolbar/ColumnSelect";
import ColumnSelect from "@src/components/Table/ColumnSelect";
import {
globalScope,

View File

@@ -1,7 +1,7 @@
import { IExtensionModalStepProps } from "./ExtensionModal";
import { Typography } from "@mui/material";
import ColumnSelect from "@src/components/TableToolbar/ColumnSelect";
import ColumnSelect from "@src/components/Table/ColumnSelect";
import { FieldType } from "@src/constants/fields";

View File

@@ -0,0 +1,2 @@
export * from "./ExtensionsModal";
export { default } from "./ExtensionsModal";

View File

@@ -3,6 +3,7 @@ import useMemoValue from "use-memo-value";
import { useAtom, useSetAtom } from "jotai";
import { useSnackbar } from "notistack";
import { mergeWith, find, isEqual } from "lodash-es";
import { ITableModalProps } from "@src/components/TableModals";
import {
useTheme,
@@ -13,7 +14,7 @@ import {
AlertTitle,
} from "@mui/material";
import WizardDialog from "@src/components/TableWizards/WizardDialog";
import WizardDialog from "@src/components/TableModals/WizardDialog";
import Step1Columns from "./Step1Columns";
import Step2NewColumns from "./Step2NewColumns";
import Step3Preview from "./Step3Preview";
@@ -23,11 +24,12 @@ import {
tableSchemaAtom,
addColumnAtom,
addRowAtom,
importCsvAtom,
ImportCsvData,
} from "@src/atoms/tableScope";
import { ColumnConfig } from "@src/types/table";
import { getFieldProp } from "@src/components/fields";
import { analytics, logEvent } from "@src/analytics";
import { ImportType } from "@src/components/TableToolbar/ImportCsv";
export type CsvConfig = {
pairs: { csvKey: string; columnKey: string }[];
@@ -35,35 +37,22 @@ export type CsvConfig = {
};
export interface IStepProps {
csvData: NonNullable<IImportCsvWizardProps["csvData"]>;
csvData: NonNullable<ImportCsvData>;
config: CsvConfig;
setConfig: React.Dispatch<React.SetStateAction<CsvConfig>>;
updateConfig: (value: Partial<CsvConfig>) => void;
isXs: boolean;
}
export interface IImportCsvWizardProps {
importType: ImportType;
handleClose: () => void;
csvData: {
columns: string[];
rows: Record<string, any>[];
} | null;
}
export default function ImportCsvWizard({
importType,
handleClose,
csvData,
}: IImportCsvWizardProps) {
export default function ImportCsvWizard({ onClose }: ITableModalProps) {
const [tableSchema] = useAtom(tableSchemaAtom, tableScope);
const addColumn = useSetAtom(addColumnAtom, tableScope);
// const addRow = useSetAtom(addRowAtom, tableScope);
const [{ importType, csvData }] = useAtom(importCsvAtom, tableScope);
const { enqueueSnackbar } = useSnackbar();
const theme = useTheme();
const isXs = useMediaQuery(theme.breakpoints.down("sm"));
const [open, setOpen] = useState(true);
const columns = useMemoValue(tableSchema.columns ?? {}, isEqual);
const [config, setConfig] = useState<CsvConfig>({
@@ -109,19 +98,15 @@ export default function ImportCsvWizard({
logEvent(analytics, "import_success", { type: importType });
// Close wizard
setOpen(false);
setTimeout(handleClose, 300);
onClose();
};
if (!csvData) return null;
return (
<WizardDialog
open={open}
onClose={() => {
setOpen(false);
setTimeout(handleClose, 300);
}}
open
onClose={onClose}
title="Import CSV or TSV"
steps={
[

View File

@@ -14,7 +14,7 @@ import {
import ArrowIcon from "@mui/icons-material/ArrowForward";
import { IStepProps } from ".";
import FadeList from "@src/components/TableWizards/ScrollableList";
import FadeList from "@src/components/TableModals/ScrollableList";
import Column from "@src/components/Table/Column";
import MultiSelect from "@rowy/multiselect";
@@ -24,7 +24,7 @@ import {
tableColumnsOrderedAtom,
} from "@src/atoms/tableScope";
import { FieldType } from "@src/constants/fields";
import { suggestType } from "@src/components/TableWizards/ImportWizard/utils";
import { suggestType } from "@src/components/TableModals/ImportWizard/utils";
export default function Step1Columns({
csvData,

View File

@@ -6,13 +6,13 @@ import { Grid, Typography, Divider, ButtonBase } from "@mui/material";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import { IStepProps } from ".";
import ScrollableList from "@src/components/TableWizards/ScrollableList";
import ScrollableList from "@src/components/TableModals/ScrollableList";
import Column from "@src/components/Table/Column";
import Cell from "@src/components/TableWizards/Cell";
import Cell from "@src/components/Table/Cell";
import FieldsDropdown from "@src/components/ColumnModals/FieldsDropdown";
import { FieldType } from "@src/constants/fields";
import { SELECTABLE_TYPES } from "@src/components/TableWizards/ImportWizard/utils";
import { SELECTABLE_TYPES } from "@src/components/TableModals/ImportWizard/utils";
export default function Step2NewColumns({
csvData,

View File

@@ -4,7 +4,7 @@ import { parseJSON } from "date-fns";
import { styled, Grid } from "@mui/material";
import Column from "@src/components/Table/Column";
import Cell from "@src/components/TableWizards/Cell";
import Cell from "@src/components/Table/Cell";
import { IStepProps } from ".";
import { tableScope, tableSchemaAtom } from "@src/atoms/tableScope";

View File

@@ -1,10 +1,11 @@
import { useState, useEffect, useCallback } from "react";
import { useAtom, useSetAtom } from "jotai";
import { merge } from "lodash-es";
import { ITableModalProps } from "@src/components/TableModals";
import { useTheme, useMediaQuery, Typography } from "@mui/material";
import WizardDialog from "@src/components/TableWizards/WizardDialog";
import WizardDialog from "@src/components/TableModals/WizardDialog";
import Step1Columns from "./Step1Columns";
import Step2Rename from "./Step2Rename";
import Step3Types from "./Step3Types";
@@ -32,7 +33,7 @@ export interface IStepProps {
isXs: boolean;
}
export default function ImportWizard() {
export default function ImportWizard({ onClose }: ITableModalProps) {
const setTableFilters = useSetAtom(tableFiltersAtom, tableScope);
const setTableOrders = useSetAtom(tableOrdersAtom, tableScope);
const [tableRows] = useAtom(tableRowsAtom, tableScope);
@@ -40,9 +41,6 @@ export default function ImportWizard() {
const theme = useTheme();
const isXs = useMediaQuery(theme.breakpoints.down("sm"));
const [open, setOpen] = useState(false);
// if (importWizardRef) importWizardRef.current = { open, setOpen };
const [config, setConfig] = useState<TableColumnsConfig>({});
const updateConfig: IStepProps["updateConfig"] = useCallback((value) => {
setConfig((prev) => ({ ...merge(prev, value) }));
@@ -50,23 +48,22 @@ export default function ImportWizard() {
// Reset table filters and orders on open
useEffect(() => {
if (!open) return;
setTableFilters([]);
setTableOrders([]);
}, [open, setTableFilters, setTableOrders]);
}, [setTableFilters, setTableOrders]);
if (tableRows.length === 0) return null;
const handleFinish = () => {
// FIXME: Investigate if this overwrites
// tableActions?.table.updateConfig("columns", config);
setOpen(false);
onClose();
};
return (
<WizardDialog
open={open}
onClose={() => setOpen(false)}
open
onClose={onClose}
title="Import"
steps={[
{

View File

@@ -19,7 +19,7 @@ import {
import DragHandleIcon from "@mui/icons-material/DragHandle";
import { AddColumn as AddColumnIcon } from "@src/assets/icons";
import ScrollableList from "@src/components/TableWizards/ScrollableList";
import ScrollableList from "@src/components/TableModals/ScrollableList";
import Column from "@src/components/Table/Column";
import EmptyState from "@src/components/EmptyState";

View File

@@ -13,7 +13,7 @@ import EditIcon from "@mui/icons-material/Edit";
import DoneIcon from "@mui/icons-material/Done";
import { IStepProps } from ".";
import ScrollableList from "@src/components/TableWizards/ScrollableList";
import ScrollableList from "@src/components/TableModals/ScrollableList";
import Column from "@src/components/Table/Column";
export default function Step2Rename({

View File

@@ -5,9 +5,9 @@ import { Grid, Typography, Divider, ButtonBase } from "@mui/material";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import { IStepProps } from ".";
import ScrollableList from "@src/components/TableWizards/ScrollableList";
import ScrollableList from "@src/components/TableModals/ScrollableList";
import Column from "@src/components/Table/Column";
import Cell from "@src/components/TableWizards/Cell";
import Cell from "@src/components/Table/Cell";
import FieldsDropdown from "@src/components/ColumnModals/FieldsDropdown";
import { tableScope, tableRowsAtom } from "@src/atoms/tableScope";

View File

@@ -3,7 +3,7 @@ import { IStepProps } from ".";
import { styled, Grid } from "@mui/material";
import Column from "@src/components/Table/Column";
import Cell from "@src/components/TableWizards/Cell";
import Cell from "@src/components/Table/Cell";
import { tableScope, tableRowsAtom } from "@src/atoms/tableScope";

View File

@@ -0,0 +1,35 @@
import { lazy } from "react";
import { useAtom } from "jotai";
import { RESET } from "jotai/utils";
import { tableScope, tableModalAtom } from "@src/atoms/tableScope";
// prettier-ignore
const CloudLogsModal = lazy(() => import("./CloudLogsModal" /* webpackChunkName: "TableModals-CloudLogsModal" */));
// prettier-ignore
const ExtensionsModal = lazy(() => import("./ExtensionsModal" /* webpackChunkName: "TableModals-ExtensionsModal" */));
// prettier-ignore
const WebhooksModal = lazy(() => import("./WebhooksModal" /* webpackChunkName: "TableModals-WebhooksModal" */));
// prettier-ignore
const ImportWizard = lazy(() => import("./ImportWizard" /* webpackChunkName: "TableModals-ImportWizard" */));
// prettier-ignore
const ImportCsvWizard = lazy(() => import("./ImportCsvWizard" /* webpackChunkName: "TableModals-ImportCsvWizard" */));
export interface ITableModalProps {
onClose: () => void;
}
export default function TableModals() {
const [tableModal, setTableModal] = useAtom(tableModalAtom, tableScope);
if (!tableModal) return null;
const onClose = () => setTableModal(RESET);
if (tableModal === "cloudLogs") return <CloudLogsModal onClose={onClose} />;
if (tableModal === "extensions") return <ExtensionsModal onClose={onClose} />;
if (tableModal === "webhooks") return <WebhooksModal onClose={onClose} />;
if (tableModal === "import") return <ImportWizard onClose={onClose} />;
if (tableModal === "importCsv") return <ImportCsvWizard onClose={onClose} />;
return null;
}

View File

@@ -1,7 +1,7 @@
import { Typography } from "@mui/material";
import WarningIcon from "@mui/icons-material/WarningAmber";
import { TableSettings } from "@src/types/table";
import { IWebhook } from "@src/components/TableToolbar/Webhooks/utils";
import { IWebhook } from "@src/components/TableModals/WebhooksModal/utils";
export const webhookTypes = [
"basic",

View File

@@ -1,7 +1,7 @@
import { Typography, Link, TextField } from "@mui/material";
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
import { TableSettings } from "@src/types/table";
import { IWebhook } from "@src/components/TableToolbar/Webhooks/utils";
import { IWebhook } from "@src/components/TableModals/WebhooksModal/utils";
export const webhookSendgrid = {
name: "SendGrid",

View File

@@ -1,7 +1,7 @@
import { Typography, Link, TextField } from "@mui/material";
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
import { TableSettings } from "@src/types/table";
import { IWebhook } from "@src/components/TableToolbar/Webhooks/utils";
import { IWebhook } from "@src/components/TableModals/WebhooksModal/utils";
export const webhookTypeform = {
name: "Typeform",

View File

@@ -1,7 +1,7 @@
import { Typography, Link, TextField } from "@mui/material";
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
import { TableSettings } from "@src/types/table";
import { IWebhook } from "@src/components/TableToolbar/Webhooks/utils";
import { IWebhook } from "@src/components/TableModals/WebhooksModal/utils";
export const webhook = {
name: "Web Form",

View File

@@ -1,8 +1,8 @@
import { useState } from "react";
import { useAtom, useSetAtom } from "jotai";
import { RESET } from "jotai/utils";
import { isEqual } from "lodash-es";
import { useSnackbar } from "notistack";
import { ITableModalProps } from "@src/components/TableModals";
import TableToolbarButton from "@src/components/TableToolbar/TableToolbarButton";
import WebhookIcon from "@mui/icons-material/Webhook";
@@ -31,7 +31,7 @@ import { runRoutes } from "@src/constants/runRoutes";
import { analytics, logEvent } from "@src/analytics";
import { getTableSchemaPath } from "@src/utils/table";
export default function Webhooks() {
export default function WebhooksModal({ onClose }: ITableModalProps) {
const [currentUser] = useAtom(currentUserAtom, globalScope);
const [rowyRun] = useAtom(rowyRunAtom, globalScope);
const [compatibleRowyRunVersion] = useAtom(
@@ -43,16 +43,12 @@ export default function Webhooks() {
const [tableSettings] = useAtom(tableSettingsAtom, tableScope);
const [tableSchema] = useAtom(tableSchemaAtom, tableScope);
const [updateTableSchema] = useAtom(updateTableSchemaAtom, tableScope);
const [modal, setModal] = useAtom(tableModalAtom, tableScope);
const { enqueueSnackbar } = useSnackbar();
const currentWebhooks = (tableSchema.webhooks ?? []) as IWebhook[];
const [localWebhooksObjects, setLocalWebhooksObjects] =
useState(currentWebhooks);
const open = modal === "webhooks";
const setOpen = (open: boolean) => setModal(open ? "webhooks" : RESET);
const [webhookModal, setWebhookModal] = useState<{
mode: "add" | "update";
webhookObject: IWebhook;
@@ -72,8 +68,6 @@ export default function Webhooks() {
const edited = !isEqual(currentWebhooks, localWebhooksObjects);
const handleOpen = () => setOpen(true);
const handleClose = (
_setOpen: React.Dispatch<React.SetStateAction<boolean>>
) => {
@@ -82,24 +76,24 @@ export default function Webhooks() {
confirm({
title: "Discard changes?",
confirm: "Discard",
cancel: "Keep",
handleConfirm: () => {
_setOpen(false);
setLocalWebhooksObjects(currentWebhooks);
setOpen(false);
onClose();
},
});
} else {
setOpen(false);
onClose();
}
};
const handleSaveWebhooks = async (callback?: Function) => {
if (updateTableSchema)
if (updateTableSchema) {
await updateTableSchema({ webhooks: localWebhooksObjects });
}
if (callback) callback();
setOpen(false);
// TODO: convert to async function that awaits for the document write to complete
await new Promise((resolve) => setTimeout(resolve, 500));
onClose();
};
const handleSaveDeploy = () =>
@@ -196,64 +190,56 @@ export default function Webhooks() {
return (
<>
<TableToolbarButton
title="Webhooks"
onClick={handleOpen}
icon={<WebhookIcon />}
<Modal
onClose={handleClose}
disableBackdropClick={edited}
disableEscapeKeyDown={edited}
maxWidth="sm"
fullWidth
title={`Webhooks (${activeWebhookCount}\u2009/\u2009${localWebhooksObjects.length})`}
header={
<AddWebhookButton
handleAddWebhook={(type: WebhookType) => {
setWebhookModal({
mode: "add",
webhookObject: emptyWebhookObject(
type,
currentEditor(),
tableSettings
),
});
}}
variant={
localWebhooksObjects.length === 0 ? "contained" : "outlined"
}
/>
}
children={
<WebhookList
webhooks={localWebhooksObjects}
handleUpdateActive={handleUpdateActive}
handleEdit={handleEdit}
handleDelete={handleDelete}
/>
}
actions={{
primary: {
children: "Save & Deploy",
onClick: () => {
handleSaveDeploy();
},
disabled: !edited,
},
secondary: {
children: "Save",
onClick: () => {
handleSaveWebhooks();
},
disabled: !edited,
},
}}
/>
{open && (
<Modal
onClose={handleClose}
disableBackdropClick={edited}
disableEscapeKeyDown={edited}
maxWidth="sm"
fullWidth
title={`Webhooks (${activeWebhookCount}\u2009/\u2009${localWebhooksObjects.length})`}
header={
<AddWebhookButton
handleAddWebhook={(type: WebhookType) => {
setWebhookModal({
mode: "add",
webhookObject: emptyWebhookObject(
type,
currentEditor(),
tableSettings
),
});
}}
variant={
localWebhooksObjects.length === 0 ? "contained" : "outlined"
}
/>
}
children={
<WebhookList
webhooks={localWebhooksObjects}
handleUpdateActive={handleUpdateActive}
handleEdit={handleEdit}
handleDelete={handleDelete}
/>
}
actions={{
primary: {
children: "Save & Deploy",
onClick: () => {
handleSaveDeploy();
},
disabled: !edited,
},
secondary: {
children: "Save",
onClick: () => {
handleSaveWebhooks();
},
disabled: !edited,
},
}}
/>
)}
{webhookModal && (
<WebhookModal
handleClose={() => setWebhookModal(null)}

View File

@@ -0,0 +1,2 @@
export * from "./WebhooksModal";
export { default } from "./WebhooksModal";

View File

@@ -0,0 +1,2 @@
export * from "./TableModals";
export { default } from "./TableModals";

View File

@@ -1,40 +0,0 @@
import { useAtom, useSetAtom } from "jotai";
import { RESET } from "jotai/utils";
import TableToolbarButton from "@src/components/TableToolbar/TableToolbarButton";
import { CloudLogs as LogsIcon } from "@src/assets/icons";
import CloudLogsModal from "./CloudLogsModal";
import {
globalScope,
projectSettingsAtom,
rowyRunModalAtom,
} from "@src/atoms/globalScope";
import { tableScope, tableModalAtom } from "@src/atoms/tableScope";
export default function CloudLogs() {
const [projectSettings] = useAtom(projectSettingsAtom, globalScope);
const openRowyRunModal = useSetAtom(rowyRunModalAtom, globalScope);
const [modal, setModal] = useAtom(tableModalAtom, tableScope);
const open = modal === "cloudLogs";
const setOpen = (open: boolean) => setModal(open ? "cloudLogs" : RESET);
return (
<>
<TableToolbarButton
title="Cloud logs"
icon={<LogsIcon />}
onClick={
projectSettings.rowyRunUrl
? () => setOpen(true)
: () => openRowyRunModal({ feature: "Cloud logs" })
}
/>
{open && (
<CloudLogsModal onClose={() => setOpen(false)} title="Cloud logs" />
)}
</>
);
}

View File

@@ -1,2 +0,0 @@
export * from "./CloudLogs";
export { default } from "./CloudLogs";

View File

@@ -15,7 +15,7 @@ import {
FormControlLabel,
Checkbox,
} from "@mui/material";
import ColumnSelect from "@src/components/TableToolbar/ColumnSelect";
import ColumnSelect from "@src/components/Table/ColumnSelect";
import {
tableScope,

View File

@@ -16,7 +16,7 @@ import {
Radio,
FormHelperText,
} from "@mui/material";
import ColumnSelect from "@src/components/TableToolbar/ColumnSelect";
import ColumnSelect from "@src/components/Table/ColumnSelect";
import {
tableScope,

View File

@@ -1,2 +0,0 @@
export * from "./Extensions";
export { default } from "./Extensions";

View File

@@ -4,7 +4,7 @@ import { useForm } from "react-hook-form";
import { Grid, MenuItem, TextField, InputLabel } from "@mui/material";
import MultiSelect from "@rowy/multiselect";
import ColumnSelect from "@src/components/TableToolbar/ColumnSelect";
import ColumnSelect from "@src/components/Table/ColumnSelect";
import FormAutosave from "@src/components/ColumnModals/ColumnConfigModal/FormAutosave";
import FieldSkeleton from "@src/components/SideDrawer/FieldSkeleton";

View File

@@ -8,7 +8,7 @@ import VisibilityIcon from "@mui/icons-material/VisibilityOutlined";
import VisibilityOffIcon from "@mui/icons-material/VisibilityOffOutlined";
import IconSlash from "@src/components/IconSlash";
import ColumnSelect, { ColumnItem } from "./ColumnSelect";
import ColumnSelect, { ColumnItem } from "@src/components/Table/ColumnSelect";
import ButtonWithStatus from "@src/components/ButtonWithStatus";
import {

View File

@@ -1,5 +1,5 @@
import React, { useState, useCallback, useRef } from "react";
import { useAtom } from "jotai";
import { useState, useCallback, useRef } from "react";
import { useAtom, useSetAtom } from "jotai";
import { parse } from "csv-parse/browser/esm";
import { useDropzone } from "react-dropzone";
import { useDebouncedCallback } from "use-debounce";
@@ -26,17 +26,13 @@ import { Upload as FileUploadIcon } from "@src/assets/icons";
import CheckIcon from "@mui/icons-material/CheckCircle";
import { globalScope, userRolesAtom } from "@src/atoms/globalScope";
import { tableScope, tableSettingsAtom } from "@src/atoms/tableScope";
import {
tableScope,
tableSettingsAtom,
tableModalAtom,
importCsvAtom,
} from "@src/atoms/tableScope";
import { analytics, logEvent } from "@src/analytics";
// FIXME:
// import ImportCsvWizard, {
// IImportCsvWizardProps,
// } from "@src/components/TableWizards/ImportCsvWizard";
export enum ImportType {
csv = "csv",
tsv = "tsv",
}
export enum ImportMethod {
paste = "paste",
@@ -45,23 +41,24 @@ export enum ImportMethod {
}
export interface IImportCsvProps {
render?: (
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void
) => React.ReactNode;
PopoverProps?: Partial<MuiPopoverProps>;
}
export default function ImportCsv({ render, PopoverProps }: IImportCsvProps) {
export default function ImportCsv({ PopoverProps }: IImportCsvProps) {
const [userRoles] = useAtom(userRolesAtom, globalScope);
const [tableSettings] = useAtom(tableSettingsAtom, tableScope);
const [{ importType, csvData }, setImportCsv] = useAtom(
importCsvAtom,
tableScope
);
const openTableModal = useSetAtom(tableModalAtom, tableScope);
const { enqueueSnackbar } = useSnackbar();
const importTypeRef = useRef(ImportType.csv);
const importTypeRef = useRef(importType);
const importMethodRef = useRef(ImportMethod.upload);
const [open, setOpen] = useState<HTMLButtonElement | null>(null);
const [tab, setTab] = useState("upload");
const [csvData, setCsvData] =
useState</* IImportCsvWizardProps["csvData"] */ any>(null);
const [error, setError] = useState("");
const validCsv =
csvData !== null && csvData?.columns.length > 0 && csvData?.rows.length > 0;
@@ -70,32 +67,38 @@ export default function ImportCsv({ render, PopoverProps }: IImportCsvProps) {
setOpen(event.currentTarget);
const handleClose = () => {
setOpen(null);
setCsvData(null);
setImportCsv({ importType: "csv", csvData: null });
setTab("upload");
setError("");
};
const popoverId = open ? "csv-popover" : undefined;
const parseCsv = (csvString: string) =>
parse(csvString, { delimiter: [",", "\t"] }, (err, rows) => {
if (err) {
setError(err.message);
} else {
const columns = rows.shift() ?? [];
if (columns.length === 0) {
setError("No columns detected");
const parseCsv = useCallback(
(csvString: string) =>
parse(csvString, { delimiter: [",", "\t"] }, (err, rows) => {
if (err) {
setError(err.message);
} else {
const mappedRows = rows.map((row: any) =>
row.reduce(
(a: any, c: any, i: number) => ({ ...a, [columns[i]]: c }),
{}
)
);
setCsvData({ columns, rows: mappedRows });
setError("");
const columns = rows.shift() ?? [];
if (columns.length === 0) {
setError("No columns detected");
} else {
const mappedRows = rows.map((row: any) =>
row.reduce(
(a: any, c: any, i: number) => ({ ...a, [columns[i]]: c }),
{}
)
);
setImportCsv({
importType: importTypeRef.current,
csvData: { columns, rows: mappedRows },
});
setError("");
}
}
}
});
}),
[setImportCsv]
);
const onDrop = useCallback(
async (acceptedFiles: File[]) => {
@@ -105,9 +108,7 @@ export default function ImportCsv({ render, PopoverProps }: IImportCsvProps) {
reader.onload = (event: any) => parseCsv(event.target.result);
reader.readAsText(file);
importTypeRef.current =
file.type === "text/tab-separated-values"
? ImportType.tsv
: ImportType.csv;
file.type === "text/tab-separated-values" ? "tsv" : "csv";
} catch (error) {
enqueueSnackbar(`Please import a .tsv or .csv file`, {
variant: "error",
@@ -118,7 +119,7 @@ export default function ImportCsv({ render, PopoverProps }: IImportCsvProps) {
});
}
},
[enqueueSnackbar]
[enqueueSnackbar, parseCsv]
);
const { getRootProps, getInputProps, isDragActive } = useDropzone({
@@ -141,8 +142,8 @@ export default function ImportCsv({ render, PopoverProps }: IImportCsvProps) {
getFirstLine?.replace(strInQuotes, tabsWithSpace) ?? "";
const tabPattern = /\t/;
return tabPattern.test(formatString)
? (importTypeRef.current = ImportType.tsv)
: (importTypeRef.current = ImportType.csv);
? (importTypeRef.current = "tsv")
: (importTypeRef.current = "csv");
}
const handlePaste = useDebouncedCallback((value: string) => {
parseCsv(value);
@@ -166,21 +167,15 @@ export default function ImportCsv({ render, PopoverProps }: IImportCsvProps) {
});
}, 1000);
const [openWizard, setOpenWizard] = useState(false);
if (tableSettings.readOnly && !userRoles.includes("ADMIN")) return null;
return (
<>
{render ? (
render(handleOpen)
) : (
<TableToolbarButton
title="Import CSV or TSV"
onClick={handleOpen}
icon={<ImportIcon />}
/>
)}
<TableToolbarButton
title="Import CSV or TSV"
onClick={handleOpen}
icon={<ImportIcon />}
/>
<Popover
id={popoverId}
@@ -204,7 +199,10 @@ export default function ImportCsv({ render, PopoverProps }: IImportCsvProps) {
<TabList
onChange={(_, v) => {
setTab(v);
setCsvData(null);
setImportCsv({
importType: importTypeRef.current,
csvData: null,
});
setError("");
}}
aria-label="Import CSV method tabs"
@@ -299,7 +297,11 @@ export default function ImportCsv({ render, PopoverProps }: IImportCsvProps) {
label="Paste CSV or TSV text"
placeholder="column, column, …"
onChange={(e) => {
if (csvData !== null) setCsvData(null);
if (csvData !== null)
setImportCsv({
importType: importTypeRef.current,
csvData: null,
});
handlePaste(e.target.value);
}}
sx={{
@@ -325,7 +327,11 @@ export default function ImportCsv({ render, PopoverProps }: IImportCsvProps) {
label="Paste URL to CSV or TSV file"
placeholder="https://"
onChange={(e) => {
if (csvData !== null) setCsvData(null);
if (csvData !== null)
setImportCsv({
importType: importTypeRef.current,
csvData: null,
});
handleUrl(e.target.value);
}}
helperText={loading ? "Fetching…" : error}
@@ -346,7 +352,7 @@ export default function ImportCsv({ render, PopoverProps }: IImportCsvProps) {
minWidth: 100,
}}
onClick={() => {
setOpenWizard(true);
openTableModal("importCsv");
logEvent(analytics, `import_${importMethodRef.current}`, {
type: importTypeRef.current,
});
@@ -355,14 +361,6 @@ export default function ImportCsv({ render, PopoverProps }: IImportCsvProps) {
Continue
</Button>
</Popover>
{/* {openWizard && csvData && (
<ImportCsvWizard
importType={importTypeRef.current}
handleClose={() => setOpenWizard(false)}
csvData={csvData}
/>
)} */}
</>
);
}

View File

@@ -1,7 +1,13 @@
import { lazy, Suspense } from "react";
import { useAtom } from "jotai";
import { useAtom, useSetAtom } from "jotai";
import { Stack } from "@mui/material";
import WebhookIcon from "@mui/icons-material/Webhook";
import {
Extension as ExtensionIcon,
CloudLogs as CloudLogsIcon,
} from "@src/assets/icons";
import TableToolbarButton from "./TableToolbarButton";
import { ButtonSkeleton } from "./TableToolbarSkeleton";
import AddRow from "./AddRow";
@@ -9,16 +15,21 @@ import LoadedRowsStatus from "./LoadedRowsStatus";
import TableSettings from "./TableSettings";
import HiddenFields from "./HiddenFields";
import RowHeight from "./RowHeight";
import BuildLogsSnack from "./CloudLogs/BuildLogs/BuildLogsSnack";
import { globalScope, userRolesAtom } from "@src/atoms/globalScope";
import {
globalScope,
projectSettingsAtom,
userRolesAtom,
compatibleRowyRunVersionAtom,
rowyRunModalAtom,
} from "@src/atoms/globalScope";
import {
tableScope,
tableSettingsAtom,
tableSchemaAtom,
tableModalAtom,
} from "@src/atoms/tableScope";
import { FieldType } from "@src/constants/fields";
import { useSnackLogContext } from "@src/contexts/SnackLogContext";
// prettier-ignore
const Filters = lazy(() => import("./Filters" /* webpackChunkName: "Filters" */));
@@ -27,21 +38,21 @@ const Export = lazy(() => import("./Export" /* webpackChunkName: "Export" */));
// prettier-ignore
const ImportCsv = lazy(() => import("./ImportCsv" /* webpackChunkName: "ImportCsv" */));
// prettier-ignore
const CloudLogs = lazy(() => import("./CloudLogs" /* webpackChunkName: "CloudLogs" */));
// prettier-ignore
const Extensions = lazy(() => import("./Extensions" /* webpackChunkName: "Extensions" */));
// prettier-ignore
const Webhooks = lazy(() => import("./Webhooks" /* webpackChunkName: "Webhooks" */));
// prettier-ignore
const ReExecute = lazy(() => import("./ReExecute" /* webpackChunkName: "ReExecute" */));
export const TABLE_TOOLBAR_HEIGHT = 44;
export default function TableToolbar() {
const [projectSettings] = useAtom(projectSettingsAtom, globalScope);
const [userRoles] = useAtom(userRolesAtom, globalScope);
const [compatibleRowyRunVersion] = useAtom(
compatibleRowyRunVersionAtom,
globalScope
);
const openRowyRunModal = useSetAtom(rowyRunModalAtom, globalScope);
const [tableSettings] = useAtom(tableSettingsAtom, tableScope);
const [tableSchema] = useAtom(tableSchemaAtom, tableScope);
const snackLogContext = useSnackLogContext();
const openTableModal = useSetAtom(tableModalAtom, tableScope);
const hasDerivatives =
Object.values(tableSchema.columns ?? {}).filter(
@@ -74,16 +85,16 @@ export default function TableToolbar() {
}}
>
<AddRow />
{/* Spacer */} <div />
<div /> {/* Spacer */}
<HiddenFields />
<Suspense fallback={<ButtonSkeleton />}>
<Filters />
</Suspense>
{/* Spacer */} <div />
<div /> {/* Spacer */}
<LoadedRowsStatus />
<div style={{ flexGrow: 1, minWidth: 64 }} />
<RowHeight />
{/* Spacer */} <div />
<div /> {/* Spacer */}
{tableSettings.tableType !== "collectionGroup" && (
<Suspense fallback={<ButtonSkeleton />}>
<ImportCsv />
@@ -94,30 +105,40 @@ export default function TableToolbar() {
</Suspense>
{userRoles.includes("ADMIN") && (
<>
{/* Spacer */} <div />
<Suspense fallback={<ButtonSkeleton />}>
<Webhooks />
</Suspense>
<Suspense fallback={<ButtonSkeleton />}>
<Extensions />
</Suspense>
<Suspense fallback={<ButtonSkeleton />}>
<CloudLogs />
</Suspense>
{snackLogContext.isSnackLogOpen && (
<Suspense fallback={null}>
<BuildLogsSnack
onClose={snackLogContext.closeSnackLog}
onOpenPanel={alert}
/>
</Suspense>
)}
<div /> {/* Spacer */}
<TableToolbarButton
title="Webhooks"
onClick={() => {
if (compatibleRowyRunVersion({ minVersion: "1.2.0" })) {
openTableModal("webhooks");
} else {
openRowyRunModal({ feature: "Webhooks", version: "1.2.0" });
}
}}
icon={<WebhookIcon />}
/>
<TableToolbarButton
title="Extensions"
onClick={() => {
if (projectSettings.rowyRunUrl) openTableModal("extensions");
else openRowyRunModal({ feature: "Extensions" });
}}
icon={<ExtensionIcon />}
/>
<TableToolbarButton
title="Cloud logs"
icon={<CloudLogsIcon />}
onClick={() => {
if (projectSettings.rowyRunUrl) openTableModal("cloudLogs");
else openRowyRunModal({ feature: "Cloud logs" });
}}
/>
{(hasDerivatives || hasExtensions) && (
<Suspense fallback={<ButtonSkeleton />}>
<ReExecute />
</Suspense>
)}
{/* Spacer */} <div />
<div /> {/* Spacer */}
<TableSettings />
</>
)}

View File

@@ -1,2 +0,0 @@
export * from "./Webhooks";
export { default } from "./Webhooks";

View File

@@ -1,7 +0,0 @@
import * as React from "react";
export interface ITableWizardsProps {}
export default function TableWizards(props: ITableWizardsProps) {
return <div></div>;
}

View File

@@ -1,2 +0,0 @@
export * from "./TableWizards";
export { default } from "./TableWizards";

View File

@@ -1,4 +1,4 @@
import { useRef, Suspense } from "react";
import { useRef, Suspense, lazy } from "react";
import { useAtom, Provider } from "jotai";
import { DebugAtoms } from "@src/atoms/utils";
import { useParams } from "react-router-dom";
@@ -15,6 +15,7 @@ import Table from "@src/components/Table";
import SideDrawer from "@src/components/SideDrawer";
import ColumnMenu from "@src/components/ColumnMenu";
import ColumnModals from "@src/components/ColumnModals";
import TableModals from "@src/components/TableModals";
import { currentUserAtom, globalScope } from "@src/atoms/globalScope";
import TableSourceFirestore from "@src/sources/TableSourceFirestore";
@@ -27,9 +28,14 @@ import {
} from "@src/atoms/tableScope";
import useBeforeUnload from "@src/hooks/useBeforeUnload";
import ActionParamsProvider from "@src/components/fields/Action/FormDialog/Provider";
import { useSnackLogContext } from "@src/contexts/SnackLogContext";
// prettier-ignore
const BuildLogsSnack = lazy(() => import("@src/components/TableModals/CloudLogsModal/BuildLogs/BuildLogsSnack" /* webpackChunkName: "TableModals-BuildLogsSnack" */));
function TablePage() {
const [tableSchema] = useAtom(tableSchemaAtom, tableScope);
const snackLogContext = useSnackLogContext();
// Warn user about leaving when they have a table modal open
useBeforeUnload(columnModalAtom, tableScope);
@@ -71,6 +77,18 @@ function TablePage() {
<ColumnMenu />
<ColumnModals />
</Suspense>
<Suspense fallback={null}>
<TableModals />
{snackLogContext.isSnackLogOpen && (
<Suspense fallback={null}>
<BuildLogsSnack
onClose={snackLogContext.closeSnackLog}
onOpenPanel={alert}
/>
</Suspense>
)}
</Suspense>
</ActionParamsProvider>
);
}

View File

@@ -4,8 +4,8 @@ import type {
DocumentData,
DocumentReference,
} from "firebase/firestore";
import { IExtension } from "@src/components/TableToolbar/Extensions/utils";
import { IWebhook } from "@src/components/TableToolbar/Webhooks/utils";
import { IExtension } from "@src/components/TableModals/ExtensionsModal/utils";
import { IWebhook } from "@src/components/TableModals/WebhooksModal/utils";
/**
* A standard function to update a doc in the database