spark modal logic implementation

This commit is contained in:
Bobby
2021-07-01 13:04:07 +10:00
parent 3dbff60a2f
commit e8cf2a09ed
5 changed files with 282 additions and 103 deletions

View File

@@ -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}
/>
)}

View File

@@ -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);
},
},
}}
/>

View File

@@ -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}
/>
)}
</>

View File

@@ -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 };

View File

@@ -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,