mirror of
https://github.com/rowyio/rowy.git
synced 2026-07-12 05:19:40 +02:00
spark modal logic implementation
This commit is contained in:
@@ -1,16 +1,60 @@
|
||||
import { Box, Button, Typography } from "@material-ui/core";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
makeStyles,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Typography,
|
||||
} from "@material-ui/core";
|
||||
import React, { useState } from "react";
|
||||
import { parseSparkConfig, serialiseSpark, ISpark } from "./utils";
|
||||
import {
|
||||
parseSparkConfig,
|
||||
serialiseSpark,
|
||||
sparkTypes,
|
||||
ISpark,
|
||||
ISparkType,
|
||||
} from "./utils";
|
||||
import EmptyState from "components/EmptyState";
|
||||
import AddIcon from "@material-ui/icons/Add";
|
||||
import EmptyIcon from "@material-ui/icons/AddBox";
|
||||
import { useRef } from "react";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
hoverableEmptyState: {
|
||||
borderRadius: theme.spacing(1),
|
||||
cursor: "pointer",
|
||||
padding: theme.spacing(2),
|
||||
"&:hover": {
|
||||
background: theme.palette.background.paper,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export interface ISparkListProps {
|
||||
sparks: ISpark[];
|
||||
handleAddSpark: () => void;
|
||||
handleAddSpark: (type: ISparkType) => void;
|
||||
}
|
||||
|
||||
export default function SparkList({ sparks, handleAddSpark }: ISparkListProps) {
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
const addButtonRef = useRef(null);
|
||||
const classes = useStyles();
|
||||
|
||||
const activeSparkCount = sparks.filter((spark) => spark.active).length;
|
||||
|
||||
const handleAddButton = () => {
|
||||
setAnchorEl(addButtonRef.current);
|
||||
};
|
||||
|
||||
const handleChooseAddType = (type: ISparkType) => {
|
||||
handleClose();
|
||||
handleAddSpark(type);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
@@ -22,15 +66,38 @@ export default function SparkList({ sparks, handleAddSpark }: ISparkListProps) {
|
||||
<Typography variant="overline">
|
||||
SPARKS ({activeSparkCount}/{sparks.length})
|
||||
</Typography>
|
||||
<Button startIcon={<AddIcon />} onClick={handleAddSpark}>
|
||||
<Button
|
||||
startIcon={<AddIcon />}
|
||||
onClick={handleAddButton}
|
||||
ref={addButtonRef}
|
||||
>
|
||||
ADD SPARK
|
||||
</Button>
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleClose}
|
||||
>
|
||||
{sparkTypes.map((type) => (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleChooseAddType(type);
|
||||
}}
|
||||
>
|
||||
{type}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</Box>
|
||||
|
||||
{sparks.length === 0 && (
|
||||
<EmptyState
|
||||
message="No Sparks Found"
|
||||
message="Add your first spark"
|
||||
description={"When you add sparks, your sparks should be shown here."}
|
||||
Icon={EmptyIcon}
|
||||
className={classes.hoverableEmptyState}
|
||||
onClick={handleAddButton}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { parseSparkConfig, serialiseSpark, ISpark } from "./utils";
|
||||
import EmptyState from "components/EmptyState";
|
||||
import { emptySparkObject, ISpark, ISparkType, triggerTypes } from "./utils";
|
||||
import BackIcon from "@material-ui/icons/ArrowBack";
|
||||
import Modal from "components/Modal";
|
||||
import CodeEditor from "../../editors/CodeEditor";
|
||||
@@ -99,37 +98,30 @@ function TabPanel(props: TabPanelProps) {
|
||||
export interface ISparkModalProps {
|
||||
// sparks: ISpark[];
|
||||
handleClose: () => void;
|
||||
handleSave: () => void;
|
||||
handleSave: (sparkObject: ISpark) => void;
|
||||
mode: "add" | "update";
|
||||
type: ISparkType;
|
||||
}
|
||||
|
||||
export default function SparkModal({
|
||||
handleClose,
|
||||
handleSave,
|
||||
mode,
|
||||
type,
|
||||
}: ISparkModalProps) {
|
||||
const [activated, setActivated] = useState(false);
|
||||
const [sparkObject, setSparkObject] = useState<ISpark>(
|
||||
mode === "add" ? emptySparkObject(type) : emptySparkObject(type)
|
||||
);
|
||||
const [tabIndex, setTabIndex] = useState(0);
|
||||
const [firestoreFields, setFirestoreFields] = useState<string[]>([]);
|
||||
const classes = useStyles();
|
||||
const { tableState } = useFiretableContext();
|
||||
const columns = Object.keys(tableState?.columns ?? {});
|
||||
|
||||
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
|
||||
const handleChange = (_, newValue: number) => {
|
||||
setTabIndex(newValue);
|
||||
};
|
||||
|
||||
const handleNewField = () => {
|
||||
setFirestoreFields([...firestoreFields, ""]);
|
||||
};
|
||||
|
||||
const handleUpdateField = (newValue, index) => {
|
||||
setFirestoreFields(
|
||||
firestoreFields.map((value, i) => (i === index ? newValue : value))
|
||||
);
|
||||
};
|
||||
|
||||
const handleRemoveField = (index) => {
|
||||
setFirestoreFields(firestoreFields.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onClose={handleClose}
|
||||
@@ -163,6 +155,13 @@ export default function SparkModal({
|
||||
label="Spark Name"
|
||||
variant="filled"
|
||||
fullWidth
|
||||
value={sparkObject.name}
|
||||
onChange={(event) => {
|
||||
setSparkObject({
|
||||
...sparkObject,
|
||||
name: event.target.value,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
@@ -171,11 +170,16 @@ export default function SparkModal({
|
||||
alignItems="center"
|
||||
className={classes.hoverable}
|
||||
onClick={() => {
|
||||
setActivated(!activated);
|
||||
setSparkObject({
|
||||
...sparkObject,
|
||||
active: !sparkObject.active,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Switch color="primary" checked={activated} />
|
||||
<Typography>Spark is {!activated && "de"}activated</Typography>
|
||||
<Switch color="primary" checked={sparkObject.active} />
|
||||
<Typography>
|
||||
Spark is {!sparkObject.active && "de"}activated
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
@@ -183,7 +187,7 @@ export default function SparkModal({
|
||||
<TextField
|
||||
size="small"
|
||||
label="Spark Type"
|
||||
value="task"
|
||||
value={type}
|
||||
variant="filled"
|
||||
fullWidth
|
||||
disabled
|
||||
@@ -227,17 +231,29 @@ export default function SparkModal({
|
||||
Triggers
|
||||
</Typography>
|
||||
</Box>
|
||||
{["create", "update", "delete"].map((trigger, index) => (
|
||||
{triggerTypes.map((trigger) => (
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
className={classes.hoverable}
|
||||
onClick={() => {
|
||||
if (sparkObject.triggers.includes(trigger)) {
|
||||
setSparkObject({
|
||||
...sparkObject,
|
||||
triggers: sparkObject.triggers.filter(
|
||||
(t) => t !== trigger
|
||||
),
|
||||
});
|
||||
} else {
|
||||
setSparkObject({
|
||||
...sparkObject,
|
||||
triggers: [...sparkObject.triggers, trigger],
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={false}
|
||||
onChange={() => {
|
||||
console.log("yay");
|
||||
}}
|
||||
checked={sparkObject.triggers.includes(trigger)}
|
||||
name={trigger}
|
||||
/>
|
||||
<Typography>{trigger}</Typography>
|
||||
@@ -255,52 +271,89 @@ export default function SparkModal({
|
||||
Required Fields (Optional)
|
||||
</Typography>
|
||||
</Box>
|
||||
{tableState?.columns &&
|
||||
Object.keys(tableState?.columns).map((trigger) => (
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
className={classes.hoverable}
|
||||
>
|
||||
<Checkbox
|
||||
checked={false}
|
||||
onChange={() => {
|
||||
console.log("yay");
|
||||
}}
|
||||
name={trigger}
|
||||
/>
|
||||
<Typography>{trigger}</Typography>
|
||||
</Box>
|
||||
))}
|
||||
{firestoreFields.map((trigger, index) => (
|
||||
<Box display="flex" alignItems="center">
|
||||
<IconButton
|
||||
color="secondary"
|
||||
component="span"
|
||||
className={classes.removeField}
|
||||
onClick={() => {
|
||||
handleRemoveField(index);
|
||||
}}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
<TextField
|
||||
label="Firestore field"
|
||||
variant="outlined"
|
||||
value={trigger}
|
||||
size="small"
|
||||
onChange={(event) => {
|
||||
handleUpdateField(event.target.value, index);
|
||||
}}
|
||||
{columns.map((field) => (
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
className={classes.hoverable}
|
||||
onClick={() => {
|
||||
if (sparkObject.requiredFields.includes(field)) {
|
||||
setSparkObject({
|
||||
...sparkObject,
|
||||
requiredFields: sparkObject.requiredFields.filter(
|
||||
(t) => t !== field
|
||||
),
|
||||
});
|
||||
} else {
|
||||
setSparkObject({
|
||||
...sparkObject,
|
||||
requiredFields: [
|
||||
...sparkObject.requiredFields,
|
||||
field,
|
||||
],
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={sparkObject.requiredFields.includes(field)}
|
||||
name={field}
|
||||
/>
|
||||
<Typography>{field}</Typography>
|
||||
</Box>
|
||||
))}
|
||||
{sparkObject.requiredFields.map((trigger, index) => {
|
||||
const isFiretableColumn = columns.includes(trigger);
|
||||
if (isFiretableColumn) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box display="flex" alignItems="center">
|
||||
<IconButton
|
||||
color="secondary"
|
||||
component="span"
|
||||
className={classes.removeField}
|
||||
onClick={() => {
|
||||
setSparkObject({
|
||||
...sparkObject,
|
||||
requiredFields: sparkObject.requiredFields.filter(
|
||||
(t) => t !== trigger
|
||||
),
|
||||
});
|
||||
}}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
<TextField
|
||||
label="Firestore field"
|
||||
variant="outlined"
|
||||
value={trigger}
|
||||
size="small"
|
||||
onChange={(event) => {
|
||||
setSparkObject({
|
||||
...sparkObject,
|
||||
requiredFields: sparkObject.requiredFields.map(
|
||||
(value, i) =>
|
||||
i === index ? event.target.value : value
|
||||
),
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
<Button
|
||||
variant="text"
|
||||
color="secondary"
|
||||
className={classes.addField}
|
||||
startIcon={<AddIcon />}
|
||||
onClick={handleNewField}
|
||||
onClick={() => {
|
||||
setSparkObject({
|
||||
...sparkObject,
|
||||
requiredFields: [...sparkObject.requiredFields, ""],
|
||||
});
|
||||
}}
|
||||
>
|
||||
Add a new Firestore field
|
||||
</Button>
|
||||
@@ -311,10 +364,13 @@ export default function SparkModal({
|
||||
Conditions
|
||||
</Typography>
|
||||
<CodeEditor
|
||||
script={"currentSparks"}
|
||||
script={sparkObject.shouldRun}
|
||||
height="100%"
|
||||
handleChange={(newValue) => {
|
||||
// setLocalSparks(newValue);
|
||||
setSparkObject({
|
||||
...sparkObject,
|
||||
shouldRun: newValue,
|
||||
});
|
||||
}}
|
||||
onValideStatusUpdate={({ isValid }) => {
|
||||
// setIsSparksValid(isValid);
|
||||
@@ -335,10 +391,13 @@ export default function SparkModal({
|
||||
</Typography>
|
||||
{/* TODO break spark body fields into editable UI components */}
|
||||
<CodeEditor
|
||||
script={"currentSparks"}
|
||||
script={sparkObject.sparkBody}
|
||||
height="100%"
|
||||
handleChange={(newValue) => {
|
||||
// setLocalSparks(newValue);
|
||||
setSparkObject({
|
||||
...sparkObject,
|
||||
sparkBody: newValue,
|
||||
});
|
||||
}}
|
||||
onValideStatusUpdate={({ isValid }) => {
|
||||
// setIsSparksValid(isValid);
|
||||
@@ -356,8 +415,10 @@ export default function SparkModal({
|
||||
}
|
||||
actions={{
|
||||
primary: {
|
||||
children: "Update Settings",
|
||||
onClick: handleSave,
|
||||
children: mode === "add" ? "Save" : "Update",
|
||||
onClick: () => {
|
||||
handleSave(sparkObject);
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -25,7 +25,7 @@ import CodeEditor from "../../editors/CodeEditor";
|
||||
import SparkList from "./SparkList";
|
||||
import SparkModal from "./SparkModal";
|
||||
|
||||
import { parseSparkConfig, serialiseSpark, ISpark } from "./utils";
|
||||
import { serialiseSpark, sparkTypes, ISpark, ISparkType } from "./utils";
|
||||
|
||||
export default function SparksEditor() {
|
||||
const snack = useSnackContext();
|
||||
@@ -38,7 +38,10 @@ export default function SparksEditor() {
|
||||
const [localSparks, setLocalSparks] = useState(currentSparks);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [isSparksValid, setIsSparksValid] = useState(true);
|
||||
const [sparkModalOpen, setSparkModalOpen] = useState(false);
|
||||
const [sparkModal, setSparkModal] = useState<{
|
||||
mode: "add" | "update";
|
||||
type: ISparkType;
|
||||
} | null>(null);
|
||||
const snackLogContext = useSnackLogContext();
|
||||
|
||||
const tablePathTokens =
|
||||
@@ -104,6 +107,14 @@ export default function SparksEditor() {
|
||||
});
|
||||
};
|
||||
|
||||
const handleAddSpark = (sparkObject: ISpark) => {
|
||||
tableActions?.table.updateConfig("sparkObjects", [
|
||||
...currentSparkObjects,
|
||||
sparkObject,
|
||||
]);
|
||||
setSparkModal(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableHeaderButton
|
||||
@@ -127,8 +138,8 @@ export default function SparksEditor() {
|
||||
</Breadcrumbs>
|
||||
<SparkList
|
||||
sparks={currentSparkObjects}
|
||||
handleAddSpark={() => {
|
||||
setSparkModalOpen(true);
|
||||
handleAddSpark={(type: ISparkType) => {
|
||||
setSparkModal({ mode: "add", type });
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
@@ -150,14 +161,14 @@ export default function SparksEditor() {
|
||||
/>
|
||||
)}
|
||||
|
||||
{sparkModalOpen && (
|
||||
{sparkModal && (
|
||||
<SparkModal
|
||||
handleClose={() => {
|
||||
setSparkModalOpen(false);
|
||||
}}
|
||||
handleSave={() => {
|
||||
console.log("SAVE");
|
||||
setSparkModal(null);
|
||||
}}
|
||||
handleSave={handleAddSpark}
|
||||
mode={sparkModal.mode}
|
||||
type={sparkModal.type}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,24 +1,57 @@
|
||||
type ISparkType =
|
||||
| "task"
|
||||
| "docSync"
|
||||
| "historySnapshot"
|
||||
| "algoliaIndex"
|
||||
| "meiliIndex"
|
||||
| "bigqueryIndex"
|
||||
| "slackMessage"
|
||||
| "sendgridEmail"
|
||||
| "apiCall"
|
||||
| "twilioMessage";
|
||||
|
||||
type ISparkTrigger = "create" | "update" | "delete";
|
||||
|
||||
interface ISpark {
|
||||
// firetable meta fields
|
||||
name: string;
|
||||
active: boolean;
|
||||
|
||||
// ft build fields
|
||||
triggers: "create" | "update" | "delete";
|
||||
type:
|
||||
| "task"
|
||||
| "docSync"
|
||||
| "historySnapshot"
|
||||
| "algoliaIndex"
|
||||
| "meiliIndex"
|
||||
| "bigqueryIndex"
|
||||
| "slackMessage"
|
||||
| "sendgridEmail"
|
||||
| "apiCall"
|
||||
| "twilioMessage";
|
||||
shouldRun: boolean;
|
||||
requiredFields?: string[];
|
||||
sparkBody: any;
|
||||
triggers: ISparkTrigger[];
|
||||
type: ISparkType;
|
||||
requiredFields: string[];
|
||||
sparkBody: string;
|
||||
shouldRun: boolean | string;
|
||||
|
||||
// TODO break spark body into editable fields
|
||||
}
|
||||
|
||||
const triggerTypes: ISparkTrigger[] = ["create", "update", "delete"];
|
||||
|
||||
const sparkTypes: ISparkType[] = [
|
||||
"task",
|
||||
"docSync",
|
||||
"historySnapshot",
|
||||
"algoliaIndex",
|
||||
"meiliIndex",
|
||||
"bigqueryIndex",
|
||||
"slackMessage",
|
||||
"sendgridEmail",
|
||||
"apiCall",
|
||||
"twilioMessage",
|
||||
];
|
||||
|
||||
function emptySparkObject(type: ISparkType): ISpark {
|
||||
return {
|
||||
name: "",
|
||||
active: false,
|
||||
triggers: [],
|
||||
type,
|
||||
sparkBody: "{}",
|
||||
requiredFields: [],
|
||||
shouldRun: "return true;",
|
||||
};
|
||||
}
|
||||
|
||||
/* Convert spark objects into a single ft-build readable string */
|
||||
@@ -45,5 +78,11 @@ function parseSparkConfig(sparkConfig): ISpark[] {
|
||||
}
|
||||
}
|
||||
|
||||
export { parseSparkConfig, serialiseSpark };
|
||||
export type { ISpark };
|
||||
export {
|
||||
parseSparkConfig,
|
||||
serialiseSpark,
|
||||
sparkTypes,
|
||||
triggerTypes,
|
||||
emptySparkObject,
|
||||
};
|
||||
export type { ISpark, ISparkType };
|
||||
|
||||
@@ -77,6 +77,7 @@ const useFiretable = (
|
||||
rowHeight: tableConfig.rowHeight,
|
||||
webhooks: tableConfig.doc?.webhooks,
|
||||
sparks: tableConfig.doc?.sparks,
|
||||
sparkObjects: tableConfig.doc?.sparkObjects,
|
||||
tableConfig,
|
||||
},
|
||||
rows: tableState.rows,
|
||||
|
||||
Reference in New Issue
Block a user