mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-28 16:06:41 +01:00
action settings overhaul with form input support
This commit is contained in:
@@ -19,7 +19,7 @@
|
||||
"@mui/lab": "^5.0.0-alpha.50",
|
||||
"@mui/material": "^5.0.0",
|
||||
"@mui/styles": "^5.0.0",
|
||||
"@rowy/form-builder": "^0.3.1",
|
||||
"@rowy/form-builder": "^0.4.2",
|
||||
"@rowy/multiselect": "^0.2.3",
|
||||
"@tinymce/tinymce-react": "^3.12.6",
|
||||
"algoliasearch": "^4.8.6",
|
||||
|
||||
@@ -63,6 +63,7 @@ export default function CodeEditorHelper({
|
||||
|
||||
<Button
|
||||
size="small"
|
||||
color="primary"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={docLink}
|
||||
|
||||
@@ -4,7 +4,7 @@ export const InlineOpenInNewIcon = styled("span")(() => ({
|
||||
position: "relative",
|
||||
width: "1em",
|
||||
height: "1em",
|
||||
marginLeft: "0.25em",
|
||||
marginLeft: "0.25ch",
|
||||
display: "inline-block",
|
||||
verticalAlign: "baseline",
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import CircularProgressOptical from "@src/components/CircularProgressOptical";
|
||||
|
||||
import { useProjectContext } from "@src/contexts/ProjectContext";
|
||||
import { functions } from "@src/firebase";
|
||||
import { formatPath } from "@src/utils/fns";
|
||||
import { useConfirmation } from "@src/components/ConfirmationDialog";
|
||||
import { useActionParams } from "./FormDialog/Context";
|
||||
import { runRoutes } from "@src/constants/runRoutes";
|
||||
@@ -112,9 +111,15 @@ export default function ActionFab({
|
||||
: "redo"
|
||||
: "run";
|
||||
|
||||
const needsParams = Array.isArray(config.params) && config.params.length > 0;
|
||||
const needsParams =
|
||||
config.friction === "params" &&
|
||||
Array.isArray(config.params) &&
|
||||
config.params.length > 0;
|
||||
const needsConfirmation =
|
||||
typeof config.confirmation === "string" && config.confirmation !== "";
|
||||
(!config.friction || config.friction === "confirmation") &&
|
||||
typeof config.confirmation === "string" &&
|
||||
config.confirmation !== "";
|
||||
|
||||
return (
|
||||
<Fab
|
||||
onClick={
|
||||
|
||||
@@ -15,10 +15,10 @@ const yupOrderKeys = (acc, currKey) => {
|
||||
else return [...acc, currKey];
|
||||
};
|
||||
|
||||
const validationCompiler = (validation) =>
|
||||
Object.keys(validation)
|
||||
.reduce(yupOrderKeys, [])
|
||||
.reduce(yupReducer(validation), yup);
|
||||
// const validationCompiler = (validation) =>
|
||||
// Object.keys(validation)
|
||||
// .reduce(yupOrderKeys, [])
|
||||
// .reduce(yupReducer(validation), yup);
|
||||
|
||||
export default function ParamsDialog({
|
||||
column,
|
||||
@@ -42,10 +42,6 @@ export default function ParamsDialog({
|
||||
validation:{array:null,required:'needs to specific the cohort to new cohort',max:[1,'only one cohort is allowed']},
|
||||
}]
|
||||
*/
|
||||
const fields = column.config.params.map((field) => ({
|
||||
...field,
|
||||
validation: field.validation ? validationCompiler(field.validation) : null,
|
||||
}));
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
@@ -53,7 +49,7 @@ export default function ParamsDialog({
|
||||
<FormDialog
|
||||
onClose={handleClose}
|
||||
title={`${column.name as string}`}
|
||||
fields={fields}
|
||||
fields={column.config.params}
|
||||
values={{}}
|
||||
onSubmit={handleRun}
|
||||
SubmitButtonProps={{ children: "Run" }}
|
||||
|
||||
102
src/components/fields/Action/FormFieldSnippets.tsx
Normal file
102
src/components/fields/Action/FormFieldSnippets.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import { useState } from "react";
|
||||
import { useSnackbar } from "notistack";
|
||||
import { FieldType } from "@rowy/form-builder";
|
||||
|
||||
import { Button, Menu, MenuItem } from "@mui/material";
|
||||
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
|
||||
|
||||
const FORM_FIELD_SNIPPETS = [
|
||||
{
|
||||
label: "Text field",
|
||||
value: {
|
||||
type: FieldType.shortText,
|
||||
name: "username",
|
||||
label: "Username",
|
||||
placeholder: "foo_bar",
|
||||
required: true,
|
||||
maxCharacters: 15,
|
||||
validation: [
|
||||
["min", 3, "Must be at least 3 characters"],
|
||||
{
|
||||
0: "notOneOf",
|
||||
1: ["admin", "administrator"],
|
||||
2: "Reserved username",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Single select",
|
||||
value: {
|
||||
type: FieldType.singleSelect,
|
||||
name: "team",
|
||||
label: "Team",
|
||||
required: true,
|
||||
options: ["Blue", "Orange"],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Multi select",
|
||||
value: {
|
||||
type: FieldType.multiSelect,
|
||||
name: "roles",
|
||||
label: "Roles",
|
||||
required: true,
|
||||
options: ["ADMIN", "EDITOR", "VIEWER"],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default function FormFieldSnippets() {
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const [snippetMenuAnchor, setSnippetMenuAnchor] = useState<any | null>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
size="small"
|
||||
endIcon={<ArrowDropDownIcon />}
|
||||
onClick={(e) => setSnippetMenuAnchor(e.currentTarget)}
|
||||
id="snippet-button"
|
||||
aria-controls="snippet-menu"
|
||||
aria-haspopup="true"
|
||||
aria-expanded={!!snippetMenuAnchor ? "true" : "false"}
|
||||
>
|
||||
Copy snippet
|
||||
</Button>
|
||||
<Menu
|
||||
id="snippet-menu"
|
||||
anchorEl={snippetMenuAnchor}
|
||||
open={!!snippetMenuAnchor}
|
||||
onClose={() => setSnippetMenuAnchor(null)}
|
||||
MenuListProps={{ "aria-labelledby": "snippet-button" }}
|
||||
anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
|
||||
transformOrigin={{ horizontal: "right", vertical: "top" }}
|
||||
>
|
||||
{FORM_FIELD_SNIPPETS.map((snippet) => (
|
||||
<MenuItem
|
||||
key={snippet.label}
|
||||
children={snippet.label}
|
||||
onClick={() => {
|
||||
// Write validation as nested array above, but must be converted
|
||||
// to [ { 0: …, 1: …, … } ] since Firestore doesn't support
|
||||
// nested arrays
|
||||
const sanitized: any = snippet.value;
|
||||
if (Array.isArray(snippet.value.validation))
|
||||
sanitized.validation = snippet.value.validation.reduce(
|
||||
(a, c, i) => ({ ...a, [i]: c }),
|
||||
{}
|
||||
);
|
||||
|
||||
navigator.clipboard.writeText(
|
||||
JSON.stringify(sanitized, undefined, 2)
|
||||
);
|
||||
enqueueSnackbar("Copied to clipboard");
|
||||
setSnippetMenuAnchor(null);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,16 +1,36 @@
|
||||
import { lazy, Suspense } from "react";
|
||||
import { lazy, Suspense, useState } from "react";
|
||||
import _get from "lodash/get";
|
||||
import stringify from "json-stable-stringify-without-jsonify";
|
||||
|
||||
import {
|
||||
Typography,
|
||||
Stepper,
|
||||
Step,
|
||||
StepButton,
|
||||
StepContent,
|
||||
Stack,
|
||||
Grid,
|
||||
TextField,
|
||||
Switch,
|
||||
FormControlLabel,
|
||||
Divider,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
FormControlLabel,
|
||||
RadioGroup,
|
||||
Radio,
|
||||
Typography,
|
||||
InputLabel,
|
||||
Link,
|
||||
Checkbox,
|
||||
FormHelperText,
|
||||
} from "@mui/material";
|
||||
import ExpandIcon from "@mui/icons-material/KeyboardArrowDown";
|
||||
|
||||
import MultiSelect from "@rowy/multiselect";
|
||||
import FieldSkeleton from "@src/components/SideDrawer/Form/FieldSkeleton";
|
||||
import { useProjectContext } from "@src/contexts/ProjectContext";
|
||||
import { InputLabel } from "@mui/material";
|
||||
import CodeEditorHelper from "@src/components/CodeEditor/CodeEditorHelper";
|
||||
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
||||
import FormFieldSnippets from "./FormFieldSnippets";
|
||||
|
||||
import { useProjectContext } from "@src/contexts/ProjectContext";
|
||||
import { WIKI_LINKS } from "@src/constants/externalLinks";
|
||||
|
||||
const CodeEditor = lazy(
|
||||
@@ -20,165 +40,459 @@ const CodeEditor = lazy(
|
||||
|
||||
const Settings = ({ config, onChange }) => {
|
||||
const { tableState, roles } = useProjectContext();
|
||||
|
||||
const [activeStep, setActiveStep] = useState(0);
|
||||
|
||||
const columnOptions = Object.values(tableState?.columns ?? {}).map((c) => ({
|
||||
label: c.name,
|
||||
value: c.key,
|
||||
}));
|
||||
|
||||
const formattedParamsJson = stringify(
|
||||
Array.isArray(config.params) ? config.params : [],
|
||||
{ space: 2 }
|
||||
);
|
||||
const [codeValid, setCodeValid] = useState(true);
|
||||
|
||||
const scriptExtraLibs = [
|
||||
[
|
||||
"declare class ref {",
|
||||
" /**",
|
||||
" * Reference object of the row running the action script",
|
||||
" */",
|
||||
"static id:string",
|
||||
"static path:string",
|
||||
"static parentId:string",
|
||||
"static tablePath:string",
|
||||
"}",
|
||||
].join("\n"),
|
||||
[
|
||||
"declare class actionParams {",
|
||||
" /**",
|
||||
" * actionParams are provided by dialog popup form",
|
||||
" */",
|
||||
(config.params ?? []).filter(Boolean).map((param) => {
|
||||
const validationKeys = Object.keys(param.validation ?? {});
|
||||
if (validationKeys.includes("string")) {
|
||||
return `static ${param.name}: string`;
|
||||
} else if (validationKeys.includes("array")) {
|
||||
return `static ${param.name}: any[]`;
|
||||
} else return `static ${param.name}: any`;
|
||||
}),
|
||||
"}",
|
||||
].join("\n"),
|
||||
];
|
||||
|
||||
// Backwards-compatibility: previously user could set `confirmation` without
|
||||
// having to set `friction: confirmation`
|
||||
const showConfirmationField =
|
||||
config.friction === "confirmation" ||
|
||||
(!config.friction &&
|
||||
typeof config.confirmation === "string" &&
|
||||
config.confirmation !== "");
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography variant="overline">Allowed roles</Typography>
|
||||
<Typography variant="body2">
|
||||
Authenticated user must have at least one of these to run the script
|
||||
</Typography>
|
||||
<MultiSelect
|
||||
label="Allowed roles"
|
||||
options={roles ?? []}
|
||||
value={config.requiredRoles ?? []}
|
||||
onChange={onChange("requiredRoles")}
|
||||
/>
|
||||
<Stepper
|
||||
nonLinear
|
||||
activeStep={activeStep}
|
||||
orientation="vertical"
|
||||
sx={{
|
||||
mt: 0,
|
||||
|
||||
<Typography variant="overline">Required fields</Typography>
|
||||
<Typography variant="body2">
|
||||
All of the selected fields must have a value for the script to run
|
||||
</Typography>
|
||||
"& .MuiStepLabel-root": { width: "100%" },
|
||||
"& .MuiStepLabel-label": {
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
typography: "subtitle2",
|
||||
"&.Mui-active": { typography: "subtitle2" },
|
||||
},
|
||||
"& .MuiStepLabel-label svg": {
|
||||
display: "block",
|
||||
marginLeft: "auto",
|
||||
my: ((24 - 18) / 2 / 8) * -1,
|
||||
transition: (theme) => theme.transitions.create("transform"),
|
||||
},
|
||||
"& .Mui-active svg": {
|
||||
transform: "rotate(180deg)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Step>
|
||||
<StepButton onClick={() => setActiveStep(0)}>
|
||||
Requirements
|
||||
<ExpandIcon />
|
||||
</StepButton>
|
||||
|
||||
<MultiSelect
|
||||
label="Required fields"
|
||||
options={columnOptions}
|
||||
value={config.requiredFields ?? []}
|
||||
onChange={onChange("requiredFields")}
|
||||
/>
|
||||
<Divider />
|
||||
<Typography variant="overline">Confirmation template</Typography>
|
||||
<Typography variant="body2">
|
||||
The action button will not ask for confirmation if this is left empty
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
label="Confirmation template"
|
||||
placeholder="Are sure you want to invest {{stockName}}?"
|
||||
value={config.confirmation}
|
||||
onChange={(e) => {
|
||||
onChange("confirmation")(e.target.value);
|
||||
}}
|
||||
fullWidth
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={config.isActionScript}
|
||||
onChange={() =>
|
||||
onChange("isActionScript")(!Boolean(config.isActionScript))
|
||||
}
|
||||
name="actionScript"
|
||||
/>
|
||||
}
|
||||
label="Set as an action script"
|
||||
/>
|
||||
{!Boolean(config.isActionScript) ? (
|
||||
<TextField
|
||||
label="Callable name"
|
||||
name="callableName"
|
||||
value={config.callableName}
|
||||
fullWidth
|
||||
onChange={(e) => {
|
||||
onChange("callableName")(e.target.value);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<InputLabel>Action script</InputLabel>
|
||||
<CodeEditorHelper
|
||||
docLink={WIKI_LINKS.fieldTypesAction}
|
||||
additionalVariables={[]}
|
||||
/>
|
||||
|
||||
<Suspense fallback={<FieldSkeleton height={300} />}>
|
||||
<CodeEditor
|
||||
minHeight={300}
|
||||
value={config.script}
|
||||
extraLibs={[
|
||||
[
|
||||
"declare class ref {",
|
||||
" /**",
|
||||
" * Reference object of the row running the action script",
|
||||
" */",
|
||||
"static id:string",
|
||||
"static path:string",
|
||||
"static parentId:string",
|
||||
"static tablePath:string",
|
||||
"}",
|
||||
].join("\n"),
|
||||
[
|
||||
"declare class actionParams {",
|
||||
" /**",
|
||||
" * actionParams are provided by dialog popup form",
|
||||
" */",
|
||||
(config.params ?? []).map((param) => {
|
||||
const validationKeys = Object.keys(param.validation);
|
||||
if (validationKeys.includes("string")) {
|
||||
return `static ${param.name}:string`;
|
||||
} else if (validationKeys.includes("array")) {
|
||||
return `static ${param.name}:any[]`;
|
||||
} else return `static ${param.name}:any`;
|
||||
}),
|
||||
"}",
|
||||
].join("\n"),
|
||||
]}
|
||||
onChange={onChange("script")}
|
||||
/>
|
||||
</Suspense>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={config.redo?.enabled}
|
||||
onChange={() =>
|
||||
onChange("redo.enabled")(!Boolean(config.redo?.enabled))
|
||||
}
|
||||
name="redo toggle"
|
||||
/>
|
||||
}
|
||||
label="User can redo (re-runs the same script)"
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={config.undo?.enabled}
|
||||
onChange={() =>
|
||||
onChange("undo.enabled")(!Boolean(config.undo?.enabled))
|
||||
}
|
||||
name="undo toggle"
|
||||
/>
|
||||
}
|
||||
label="User can undo"
|
||||
/>
|
||||
{config["undo.enabled"] && (
|
||||
<>
|
||||
<Typography variant="overline">
|
||||
Undo confirmation template
|
||||
</Typography>
|
||||
<TextField
|
||||
label="Template"
|
||||
placeholder="are you sure you want to sell your stocks in {{stockName}}"
|
||||
value={config["undo.confirmation"]}
|
||||
onChange={(e) => {
|
||||
onChange("undo.confirmation")(e.target.value);
|
||||
<StepContent>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<MultiSelect
|
||||
label="Required roles"
|
||||
options={roles ?? []}
|
||||
value={config.requiredRoles ?? []}
|
||||
onChange={onChange("requiredRoles")}
|
||||
TextFieldProps={{
|
||||
id: "requiredRoles",
|
||||
helperText:
|
||||
"The user must have at least one of these roles to run the script",
|
||||
}}
|
||||
fullWidth
|
||||
/>
|
||||
<Typography variant="overline">Undo action script</Typography>
|
||||
<Suspense fallback={<FieldSkeleton height={300} />}>
|
||||
<CodeEditor
|
||||
minHeight={300}
|
||||
value={config["undo.script"]}
|
||||
onChange={onChange("undo.script")}
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<MultiSelect
|
||||
label="Required fields"
|
||||
options={columnOptions}
|
||||
value={config.requiredFields ?? []}
|
||||
onChange={onChange("requiredFields")}
|
||||
TextFieldProps={{
|
||||
id: "requiredFields",
|
||||
helperText:
|
||||
"All the selected fields must have a value for the script to run",
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</StepContent>
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
<StepButton onClick={() => setActiveStep(1)}>
|
||||
Confirmation
|
||||
<ExpandIcon />
|
||||
</StepButton>
|
||||
|
||||
<StepContent>
|
||||
<Stack spacing={3}>
|
||||
<FormControl component="fieldset">
|
||||
<FormLabel component="legend">
|
||||
Clicking the action button will:
|
||||
</FormLabel>
|
||||
|
||||
<RadioGroup
|
||||
aria-label="Action button friction"
|
||||
name="friction"
|
||||
defaultValue={
|
||||
typeof config.confirmation === "string" &&
|
||||
config.confirmation !== ""
|
||||
? "confirmation"
|
||||
: "none"
|
||||
}
|
||||
value={config.friction}
|
||||
onChange={(e) => onChange("friction")(e.target.value)}
|
||||
>
|
||||
<FormControlLabel
|
||||
value="none"
|
||||
control={<Radio />}
|
||||
label="Run the action immediately"
|
||||
/>
|
||||
</Suspense>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
<FormControlLabel
|
||||
value="confirmation"
|
||||
control={<Radio />}
|
||||
label="Ask the user for confirmation"
|
||||
/>
|
||||
<FormControlLabel
|
||||
value="params"
|
||||
control={<Radio />}
|
||||
label={
|
||||
<>
|
||||
<Typography variant="inherit">
|
||||
Ask the user for input in a form (Alpha)
|
||||
</Typography>
|
||||
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
This feature is currently undocumented and is subject to
|
||||
change in future minor versions
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
|
||||
{showConfirmationField && (
|
||||
<TextField
|
||||
id="confirmation"
|
||||
label="Confirmation template"
|
||||
placeholder="Are sure you want to invest {{stockName}}?"
|
||||
value={config.confirmation}
|
||||
onChange={(e) => onChange("confirmation")(e.target.value)}
|
||||
fullWidth
|
||||
helperText="The action button will not ask for confirmation if this is left empty"
|
||||
/>
|
||||
)}
|
||||
|
||||
{config.friction === "params" && (
|
||||
<FormControl>
|
||||
<Grid container spacing={1} sx={{ mb: 0.5 }}>
|
||||
<Grid item xs>
|
||||
<InputLabel variant="filled">Form fields</InputLabel>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<FormFieldSnippets />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Suspense fallback={<FieldSkeleton height={300} />}>
|
||||
<CodeEditor
|
||||
minHeight={200}
|
||||
defaultLanguage="json"
|
||||
value={formattedParamsJson}
|
||||
onChange={(v) => {
|
||||
try {
|
||||
if (v) {
|
||||
const parsed = JSON.parse(v);
|
||||
onChange("params")(parsed);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`Failed to parse JSON: ${e}`);
|
||||
setCodeValid(false);
|
||||
}
|
||||
}}
|
||||
onValidStatusUpdate={({ isValid }) => setCodeValid(isValid)}
|
||||
error={!codeValid}
|
||||
/>
|
||||
</Suspense>
|
||||
|
||||
{!codeValid && (
|
||||
<FormHelperText error variant="filled">
|
||||
Invalid JSON
|
||||
</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
)}
|
||||
</Stack>
|
||||
</StepContent>
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
<StepButton onClick={() => setActiveStep(2)}>
|
||||
Action
|
||||
<ExpandIcon />
|
||||
</StepButton>
|
||||
|
||||
<StepContent>
|
||||
<Stack spacing={3}>
|
||||
<FormControl component="fieldset">
|
||||
<FormLabel component="legend">
|
||||
Clicking the action button will run a:
|
||||
</FormLabel>
|
||||
<RadioGroup
|
||||
aria-label="Action will run"
|
||||
name="isActionScript"
|
||||
value={config.isActionScript ? "actionScript" : "cloudFunction"}
|
||||
onChange={(e) =>
|
||||
onChange("isActionScript")(e.target.value === "actionScript")
|
||||
}
|
||||
>
|
||||
<FormControlLabel
|
||||
value="actionScript"
|
||||
control={<Radio />}
|
||||
label={
|
||||
<>
|
||||
<Typography variant="inherit">Script</Typography>
|
||||
<Typography variant="caption" color="textSecondary">
|
||||
Write JavaScript code below that will be executed by
|
||||
Rowy Run.{" "}
|
||||
<Link
|
||||
href={WIKI_LINKS.rowyRun}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Requires Rowy Run setup
|
||||
<InlineOpenInNewIcon />
|
||||
</Link>
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<FormControlLabel
|
||||
value="cloudFunction"
|
||||
control={<Radio />}
|
||||
label={
|
||||
<>
|
||||
<Typography variant="inherit">Callable</Typography>
|
||||
<Typography variant="caption" color="textSecondary">
|
||||
A{" "}
|
||||
<Link
|
||||
href="https://firebase.google.com/docs/functions/callable"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
callable function
|
||||
<InlineOpenInNewIcon />
|
||||
</Link>{" "}
|
||||
you’ve deployed on your Firestore or Google Cloud
|
||||
project
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
|
||||
{!config.isActionScript ? (
|
||||
<TextField
|
||||
id="callableName"
|
||||
label="Callable name"
|
||||
name="callableName"
|
||||
value={config.callableName}
|
||||
fullWidth
|
||||
onChange={(e) => onChange("callableName")(e.target.value)}
|
||||
helperText={
|
||||
<>
|
||||
Write the name of the callable function you’ve deployed to
|
||||
your project.{" "}
|
||||
<Link
|
||||
href={`https://console.firebase.google.com/project/rowyio/functions/list`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
View your callable functions
|
||||
<InlineOpenInNewIcon />
|
||||
</Link>
|
||||
<br />
|
||||
Your callable function must be compatible with Rowy Action
|
||||
columns.{" "}
|
||||
<Link
|
||||
href={WIKI_LINKS.fieldTypesAction + "#callable"}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
View requirements
|
||||
<InlineOpenInNewIcon />
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<FormControl>
|
||||
<InputLabel variant="filled">Action script</InputLabel>
|
||||
<Suspense fallback={<FieldSkeleton height={300} />}>
|
||||
<CodeEditor
|
||||
minHeight={200}
|
||||
value={config.script}
|
||||
onChange={onChange("script")}
|
||||
extraLibs={scriptExtraLibs}
|
||||
/>
|
||||
</Suspense>
|
||||
<CodeEditorHelper
|
||||
docLink={WIKI_LINKS.fieldTypesAction + "#script"}
|
||||
additionalVariables={[]}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<Grid container>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={config.redo?.enabled}
|
||||
onChange={() =>
|
||||
onChange("redo.enabled")(
|
||||
!Boolean(config.redo?.enabled)
|
||||
)
|
||||
}
|
||||
name="redo"
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<>
|
||||
<Typography variant="inherit">
|
||||
User can redo
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Re-runs the script above
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
style={{ marginLeft: -11 }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={config.undo?.enabled}
|
||||
onChange={() =>
|
||||
onChange("undo.enabled")(
|
||||
!Boolean(config.undo?.enabled)
|
||||
)
|
||||
}
|
||||
name="undo"
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<>
|
||||
<Typography variant="inherit">
|
||||
User can undo
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Runs a new script
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
style={{ marginLeft: -11 }}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</StepContent>
|
||||
</Step>
|
||||
|
||||
{config.isActionScript && _get(config, "undo.enabled") && (
|
||||
<Step>
|
||||
<StepButton onClick={() => setActiveStep(3)}>
|
||||
Undo action
|
||||
<ExpandIcon />
|
||||
</StepButton>
|
||||
|
||||
<StepContent>
|
||||
<Stack spacing={3}>
|
||||
{(showConfirmationField ||
|
||||
!config.friction ||
|
||||
config.friction === "none") && (
|
||||
<TextField
|
||||
id="undo.confirmation"
|
||||
label="Undo confirmation template"
|
||||
placeholder="Are you sure you want to sell your stocks in {{stockName}}?"
|
||||
value={_get(config, "undo.confirmation")}
|
||||
onChange={(e) => {
|
||||
onChange("undo.confirmation")(e.target.value);
|
||||
}}
|
||||
fullWidth
|
||||
helperText={
|
||||
<>
|
||||
{showConfirmationField &&
|
||||
"Override the confirmation message above. "}
|
||||
The action button will not ask for confirmation if this is
|
||||
left empty{showConfirmationField && "."}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormControl>
|
||||
<InputLabel variant="filled">Undo script</InputLabel>
|
||||
<Suspense fallback={<FieldSkeleton height={300} />}>
|
||||
<CodeEditor
|
||||
value={_get(config, "undo.script")}
|
||||
onChange={onChange("undo.script")}
|
||||
extraLibs={scriptExtraLibs}
|
||||
/>
|
||||
</Suspense>
|
||||
<CodeEditorHelper
|
||||
docLink={WIKI_LINKS.fieldTypesAction + "#script"}
|
||||
additionalVariables={[]}
|
||||
/>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</StepContent>
|
||||
</Step>
|
||||
)}
|
||||
</>
|
||||
</Stepper>
|
||||
);
|
||||
};
|
||||
export default Settings;
|
||||
|
||||
@@ -2664,10 +2664,10 @@
|
||||
estree-walker "^1.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@rowy/form-builder@^0.3.1":
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@rowy/form-builder/-/form-builder-0.3.1.tgz#7359a05563c99ae4c222fa5336a57ce7a344ae29"
|
||||
integrity sha512-5IlNtBb6V5VdbpYWhO4P6QIBVqI42SVko15Bmn1YLd0JyoI3wza+seFBK0PgrBUB/OzTEA/hsBN5cyWhqlKt2Q==
|
||||
"@rowy/form-builder@^0.4.2":
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@rowy/form-builder/-/form-builder-0.4.2.tgz#e1693b16af31ed486b7314cd657cd3a1c9c3e937"
|
||||
integrity sha512-bedChgzyL7BxeQVijaxAXYJqcZtTaCLy4CfqInbsKEB+0OJ3nWi2c9l9d+v8yf6QJ2+3ERyveVfuKq6HAVTQ/g==
|
||||
dependencies:
|
||||
"@hookform/resolvers" "^2.6.0"
|
||||
"@mdi/js" "^5.9.55"
|
||||
|
||||
Reference in New Issue
Block a user