2022-05-26 19:31:56 +10:00
|
|
|
|
import { useState, Suspense, useMemo, createElement } from "react";
|
|
|
|
|
|
import { useAtom, useSetAtom } from "jotai";
|
|
|
|
|
|
import { set } from "lodash-es";
|
|
|
|
|
|
import { ErrorBoundary } from "react-error-boundary";
|
|
|
|
|
|
import { IColumnModalProps } from "@src/components/ColumnModals";
|
|
|
|
|
|
|
|
|
|
|
|
import { Typography, Stack } from "@mui/material";
|
|
|
|
|
|
|
|
|
|
|
|
import Modal from "@src/components/Modal";
|
|
|
|
|
|
import { getFieldProp } from "@src/components/fields";
|
|
|
|
|
|
import DefaultValueInput from "./DefaultValueInput";
|
|
|
|
|
|
import { InlineErrorFallback } from "@src/components/ErrorFallback";
|
|
|
|
|
|
import Loading from "@src/components/Loading";
|
|
|
|
|
|
|
|
|
|
|
|
import {
|
2022-07-18 14:40:46 +10:00
|
|
|
|
projectScope,
|
2022-05-26 19:31:56 +10:00
|
|
|
|
rowyRunAtom,
|
|
|
|
|
|
confirmDialogAtom,
|
2022-07-18 14:40:46 +10:00
|
|
|
|
} from "@src/atoms/projectScope";
|
2022-05-26 19:31:56 +10:00
|
|
|
|
import {
|
|
|
|
|
|
tableScope,
|
|
|
|
|
|
tableSettingsAtom,
|
|
|
|
|
|
updateColumnAtom,
|
|
|
|
|
|
} from "@src/atoms/tableScope";
|
|
|
|
|
|
import { useSnackLogContext } from "@src/contexts/SnackLogContext";
|
|
|
|
|
|
import { FieldType } from "@src/constants/fields";
|
|
|
|
|
|
import { runRoutes } from "@src/constants/runRoutes";
|
|
|
|
|
|
import { useSnackbar } from "notistack";
|
2022-06-28 15:54:33 +10:00
|
|
|
|
import {
|
|
|
|
|
|
getTableSchemaPath,
|
|
|
|
|
|
getTableBuildFunctionPathname,
|
|
|
|
|
|
} from "@src/utils/table";
|
2022-05-26 19:31:56 +10:00
|
|
|
|
|
|
|
|
|
|
export default function ColumnConfigModal({
|
2022-06-07 13:03:29 +10:00
|
|
|
|
onClose,
|
2022-05-26 19:31:56 +10:00
|
|
|
|
column,
|
|
|
|
|
|
}: IColumnModalProps) {
|
2022-07-18 14:40:46 +10:00
|
|
|
|
const [rowyRun] = useAtom(rowyRunAtom, projectScope);
|
2022-05-26 19:31:56 +10:00
|
|
|
|
const [tableSettings] = useAtom(tableSettingsAtom, tableScope);
|
|
|
|
|
|
const updateColumn = useSetAtom(updateColumnAtom, tableScope);
|
2022-07-18 14:40:46 +10:00
|
|
|
|
const confirm = useSetAtom(confirmDialogAtom, projectScope);
|
2022-05-26 19:31:56 +10:00
|
|
|
|
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
|
|
|
|
|
|
const snackLogContext = useSnackLogContext();
|
|
|
|
|
|
|
|
|
|
|
|
const [showRebuildPrompt, setShowRebuildPrompt] = useState(false);
|
|
|
|
|
|
const [newConfig, setNewConfig] = useState(column.config ?? {});
|
|
|
|
|
|
const customFieldSettings = getFieldProp("settings", column.type);
|
|
|
|
|
|
const settingsValidator = getFieldProp("settingsValidator", column.type);
|
|
|
|
|
|
const initializable = getFieldProp("initializable", column.type);
|
|
|
|
|
|
|
|
|
|
|
|
const rendedFieldSettings = useMemo(
|
|
|
|
|
|
() =>
|
2022-12-08 18:58:26 +08:00
|
|
|
|
[FieldType.derivative, FieldType.aggregate, FieldType.formula].includes(
|
|
|
|
|
|
column.type
|
|
|
|
|
|
) && newConfig.renderFieldType
|
2022-05-26 19:31:56 +10:00
|
|
|
|
? getFieldProp("settings", newConfig.renderFieldType)
|
|
|
|
|
|
: null,
|
|
|
|
|
|
[newConfig.renderFieldType, column.type]
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const [errors, setErrors] = useState({});
|
|
|
|
|
|
|
|
|
|
|
|
const validateSettings = () => {
|
|
|
|
|
|
if (settingsValidator) {
|
|
|
|
|
|
const errors = settingsValidator(newConfig);
|
|
|
|
|
|
setErrors(errors);
|
|
|
|
|
|
return errors;
|
|
|
|
|
|
}
|
|
|
|
|
|
setErrors({});
|
|
|
|
|
|
return {};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleChange = (key: string) => (update: any) => {
|
|
|
|
|
|
if (
|
|
|
|
|
|
showRebuildPrompt === false &&
|
|
|
|
|
|
(key.includes("defaultValue") || column.type === FieldType.derivative) &&
|
|
|
|
|
|
column.config?.[key] !== update
|
|
|
|
|
|
) {
|
|
|
|
|
|
setShowRebuildPrompt(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
const updatedConfig = set({ ...newConfig }, key, update);
|
|
|
|
|
|
setNewConfig(updatedConfig);
|
|
|
|
|
|
validateSettings();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Modal
|
|
|
|
|
|
maxWidth="md"
|
2022-06-07 13:03:29 +10:00
|
|
|
|
onClose={onClose}
|
2022-05-30 17:49:20 +10:00
|
|
|
|
title={`${column.name}: Config`}
|
2022-05-26 19:31:56 +10:00
|
|
|
|
disableBackdropClick
|
|
|
|
|
|
disableEscapeKeyDown
|
|
|
|
|
|
children={
|
|
|
|
|
|
<Suspense fallback={<Loading fullScreen={false} />}>
|
|
|
|
|
|
<>
|
|
|
|
|
|
{initializable && (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<section style={{ marginTop: 1 }}>
|
|
|
|
|
|
{/* top margin fixes visual bug */}
|
|
|
|
|
|
<ErrorBoundary FallbackComponent={InlineErrorFallback}>
|
|
|
|
|
|
<DefaultValueInput
|
|
|
|
|
|
handleChange={handleChange}
|
|
|
|
|
|
column={{ ...column, config: newConfig }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</ErrorBoundary>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{customFieldSettings && (
|
|
|
|
|
|
<Stack
|
|
|
|
|
|
spacing={3}
|
|
|
|
|
|
sx={{ borderTop: 1, borderColor: "divider", pt: 3 }}
|
|
|
|
|
|
>
|
|
|
|
|
|
{createElement(customFieldSettings, {
|
|
|
|
|
|
config: newConfig,
|
|
|
|
|
|
onChange: handleChange,
|
|
|
|
|
|
fieldName: column.fieldName,
|
|
|
|
|
|
onBlur: validateSettings,
|
|
|
|
|
|
errors,
|
|
|
|
|
|
})}
|
|
|
|
|
|
</Stack>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{rendedFieldSettings && (
|
|
|
|
|
|
<Stack
|
|
|
|
|
|
spacing={3}
|
|
|
|
|
|
sx={{ borderTop: 1, borderColor: "divider", pt: 3 }}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Typography variant="subtitle1">
|
|
|
|
|
|
Rendered field config
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
{createElement(rendedFieldSettings, {
|
|
|
|
|
|
config: newConfig,
|
|
|
|
|
|
onChange: handleChange,
|
|
|
|
|
|
onBlur: validateSettings,
|
|
|
|
|
|
errors,
|
|
|
|
|
|
})}
|
|
|
|
|
|
</Stack>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{/* {
|
|
|
|
|
|
<ConfigForm
|
|
|
|
|
|
type={type}
|
|
|
|
|
|
|
|
|
|
|
|
config={newConfig}
|
|
|
|
|
|
/>
|
|
|
|
|
|
} */}
|
|
|
|
|
|
</>
|
|
|
|
|
|
</Suspense>
|
|
|
|
|
|
}
|
|
|
|
|
|
actions={{
|
|
|
|
|
|
primary: {
|
|
|
|
|
|
onClick: async () => {
|
|
|
|
|
|
const errors = validateSettings();
|
|
|
|
|
|
if (Object.keys(errors).length > 0) {
|
|
|
|
|
|
confirm({
|
|
|
|
|
|
title: "Invalid settings",
|
|
|
|
|
|
body: (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<Typography>Please fix the following settings:</Typography>
|
|
|
|
|
|
<ul style={{ paddingLeft: "1.5em" }}>
|
|
|
|
|
|
{Object.entries(errors).map(([key, message]) => (
|
|
|
|
|
|
<li key={key}>
|
|
|
|
|
|
<>
|
|
|
|
|
|
<code>{key}</code>: {message}
|
|
|
|
|
|
</>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</>
|
|
|
|
|
|
),
|
|
|
|
|
|
confirm: "Fix",
|
|
|
|
|
|
hideCancel: true,
|
|
|
|
|
|
handleConfirm: () => {},
|
|
|
|
|
|
});
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const savingSnack = enqueueSnackbar("Saving changes…");
|
|
|
|
|
|
|
|
|
|
|
|
await updateColumn({
|
|
|
|
|
|
key: column.key,
|
|
|
|
|
|
config: { config: newConfig },
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (showRebuildPrompt) {
|
|
|
|
|
|
confirm({
|
|
|
|
|
|
title: "Deploy changes?",
|
|
|
|
|
|
body: "You need to re-deploy this table’s cloud function to apply the changes you made. You can also re-deploy later.",
|
|
|
|
|
|
confirm: "Deploy",
|
|
|
|
|
|
cancel: "Later",
|
|
|
|
|
|
handleConfirm: async () => {
|
|
|
|
|
|
if (!rowyRun) return;
|
|
|
|
|
|
snackLogContext.requestSnackLog();
|
|
|
|
|
|
rowyRun({
|
|
|
|
|
|
route: runRoutes.buildFunction,
|
|
|
|
|
|
body: {
|
|
|
|
|
|
tablePath: tableSettings.collection,
|
2022-06-15 14:26:52 +10:00
|
|
|
|
// pathname must match old URL format
|
2022-06-28 15:54:33 +10:00
|
|
|
|
pathname: getTableBuildFunctionPathname(
|
|
|
|
|
|
tableSettings.id,
|
|
|
|
|
|
tableSettings.tableType
|
|
|
|
|
|
),
|
2022-06-02 00:50:18 +10:00
|
|
|
|
tableConfigPath: getTableSchemaPath(tableSettings),
|
2022-05-26 19:31:56 +10:00
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
closeSnackbar(savingSnack);
|
|
|
|
|
|
enqueueSnackbar("Changes saved");
|
|
|
|
|
|
|
2022-06-07 13:03:29 +10:00
|
|
|
|
onClose();
|
2022-05-26 19:31:56 +10:00
|
|
|
|
setShowRebuildPrompt(false);
|
|
|
|
|
|
},
|
|
|
|
|
|
children: "Update",
|
|
|
|
|
|
},
|
|
|
|
|
|
secondary: {
|
2022-06-07 13:03:29 +10:00
|
|
|
|
onClick: onClose,
|
2022-05-26 19:31:56 +10:00
|
|
|
|
children: "Cancel",
|
|
|
|
|
|
},
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|