Merge branch 'rc' of https://github.com/rowyio/rowy into rc

This commit is contained in:
shamsmosowi
2021-11-30 19:32:43 +07:00
39 changed files with 888 additions and 796 deletions

View File

@@ -13,12 +13,12 @@
"@emotion/react": "^11.4.0",
"@emotion/styled": "^11.3.0",
"@hookform/resolvers": "^2.8.1",
"@mdi/js": "^6.2.95",
"@mdi/js": "^6.5.95",
"@monaco-editor/react": "^4.3.1",
"@mui/icons-material": "^5.0.0",
"@mui/lab": "^5.0.0-alpha.50",
"@mui/material": "^5.0.0",
"@mui/styles": "^5.0.0",
"@mui/icons-material": "^5.2.0",
"@mui/lab": "^5.0.0-alpha.58",
"@mui/material": "^5.2.2",
"@mui/styles": "^5.2.2",
"@rowy/form-builder": "^0.4.2",
"@rowy/multiselect": "^0.2.3",
"@tinymce/tinymce-react": "^3.12.6",
@@ -62,7 +62,6 @@
"react-router-hash-link": "^2.4.3",
"react-scripts": "^4.0.3",
"react-usestateref": "^1.0.5",
"semver": "^7.3.5",
"serve": "^11.3.2",
"swr": "^1.0.1",
"tinymce": "^5.9.2",

View File

@@ -1,9 +1,10 @@
import SvgIcon, { SvgIconProps } from "@mui/material/SvgIcon";
import { mdiClockEditOutline } from "@mdi/js";
export default function UpdatedAt(props: SvgIconProps) {
return (
<SvgIcon {...props}>
<path d="m19.06 14.88 2.05 2-6 6.07H13v-2.01l6.06-6.06ZM12 2a10 10 0 0 1 9.98 9.373 2.561 2.561 0 0 0-2.001.047A8 8 0 0 0 4 12a8.001 8.001 0 0 0 7 7.938v2.013C5.941 21.447 2 17.164 2 12 2 6.477 6.477 2 12 2Zm9.42 11.35 1.28 1.28c.21.21.21.56 0 .77l-1 .95-2.05-2 1-1a.55.55 0 0 1 .77 0ZM12.5 7v5.25l4.018 2.384-1.051 1.045L11 13V7h1.5Z" />
<path d={mdiClockEditOutline} />
</SvgIcon>
);
}

View File

@@ -63,7 +63,14 @@ export default function DiffEditor({
return (
<TrapFocus open={fullScreen}>
<Box
sx={{ ...boxSx, ...containerProps?.sx }}
sx={[
boxSx,
...(Array.isArray(containerProps?.sx)
? containerProps!.sx
: containerProps?.sx
? [containerProps.sx]
: []),
]}
style={fullScreen ? { height: "100%" } : {}}
>
<MonacoDiffEditor

View File

@@ -66,7 +66,14 @@ export default function CodeEditor({
return (
<TrapFocus open={fullScreen}>
<Box
sx={{ ...boxSx, ...containerProps?.sx }}
sx={[
boxSx,
...(Array.isArray(containerProps?.sx)
? containerProps!.sx
: containerProps?.sx
? [containerProps.sx]
: []),
]}
style={fullScreen ? { height: "100%" } : {}}
>
<Editor

View File

@@ -6,7 +6,7 @@ import githubLightTheme from "./github-light-default.json";
import githubDarkTheme from "./github-dark-default.json";
import { useTheme } from "@mui/material";
import type { SxProps, Theme } from "@mui/system";
import type { SystemStyleObject, Theme } from "@mui/system";
import { useProjectContext } from "@src/contexts/ProjectContext";
import { getFieldProp } from "@src/components/fields";
@@ -189,7 +189,7 @@ export default function useMonacoCustomizations({
}
}, [monaco, tableState?.columns]);
let boxSx: SxProps<Theme> = {
let boxSx: SystemStyleObject<Theme> = {
minWidth: 400,
minHeight,
height: minHeight,

View File

@@ -13,7 +13,7 @@ export default function HelperText(props: IHelperTextProps) {
style={{
marginTop: theme.spacing(-3),
padding: theme.spacing(0, 1.5),
...theme.typography.body2,
...(theme.typography.body2 as any),
color: theme.palette.text.secondary,
}}
/>

View File

@@ -26,9 +26,9 @@ export interface IScrollableDialogContentProps extends DialogContentProps {
export default function ScrollableDialogContent({
disableTopDivider = false,
disableBottomDivider = false,
dividerSx,
topDividerSx,
bottomDividerSx,
dividerSx = [],
topDividerSx = [],
bottomDividerSx = [],
...props
}: IScrollableDialogContentProps) {
const [scrollInfo, setRef] = useScrollInfo();
@@ -40,7 +40,10 @@ export default function ScrollableDialogContent({
style={{
visibility: scrollInfo.y.percentage > 0 ? "visible" : "hidden",
}}
sx={{ ...dividerSx, ...topDividerSx }}
sx={[
...(Array.isArray(dividerSx) ? dividerSx : [dividerSx]),
...(Array.isArray(topDividerSx) ? topDividerSx : [topDividerSx]),
]}
/>
)}
@@ -51,7 +54,12 @@ export default function ScrollableDialogContent({
style={{
visibility: scrollInfo.y.percentage < 1 ? "visible" : "hidden",
}}
sx={{ ...dividerSx, ...bottomDividerSx }}
sx={[
...(Array.isArray(dividerSx) ? dividerSx : [dividerSx]),
...(Array.isArray(bottomDividerSx)
? bottomDividerSx
: [bottomDividerSx]),
]}
/>
)}
</>

View File

@@ -22,7 +22,7 @@ import ScrollableDialogContent, {
} from "./ScrollableDialogContent";
export interface IModalProps extends Partial<Omit<DialogProps, "title">> {
onClose: () => void;
onClose: (setOpen: React.Dispatch<React.SetStateAction<boolean>>) => void;
disableBackdropClick?: boolean;
disableEscapeKeyDown?: boolean;
@@ -73,7 +73,7 @@ export default function Modal({
setOpen(false);
setEmphasizeCloseButton(false);
setTimeout(() => onClose(), 300);
setTimeout(() => onClose(setOpen), 300);
};
const [emphasizeCloseButton, setEmphasizeCloseButton] = useState(false);

View File

@@ -25,9 +25,9 @@ import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp";
import { useConfirmation } from "@src/components/ConfirmationDialog/Context";
import { useProjectContext } from "@src/contexts/ProjectContext";
import { formatPath, asyncForEach } from "@src/utils/fns";
import routes from "@src/constants/routes";
// import routes from "@src/constants/routes";
import { runRoutes } from "@src/constants/runRoutes";
import { config } from "process";
// import { config } from "process";
import { WIKI_LINKS } from "@src/constants/externalLinks";
const useStyles = makeStyles((theme) =>
@@ -266,7 +266,7 @@ export default function BulkActions({ selectedRows, columns, clearSelection }) {
},
}}
SelectProps={{
classes: { root: classes.select },
classes: { select: classes.select },
displayEmpty: true,
MenuProps: {
anchorOrigin: { vertical: "top", horizontal: "left" },

View File

@@ -12,6 +12,7 @@ import Loading from "@src/components/Loading";
import { useProjectContext } from "@src/contexts/ProjectContext";
import { useConfirmation } from "@src/components/ConfirmationDialog";
import { useSnackLogContext } from "@src/contexts/SnackLogContext";
import { FieldType } from "@src/constants/fields";
import { runRoutes } from "@src/constants/runRoutes";
@@ -27,6 +28,7 @@ export default function FieldSettings(props: IMenuModalProps) {
const { requestConfirmation } = useConfirmation();
const { tableState, rowyRun } = useProjectContext();
const snackLogContext = useSnackLogContext();
const rendedFieldSettings = useMemo(
() =>
@@ -164,6 +166,7 @@ export default function FieldSettings(props: IMenuModalProps) {
cancel: "Later",
handleConfirm: async () => {
if (!rowyRun) return;
snackLogContext.requestSnackLog();
rowyRun({
route: runRoutes.buildFunction,
body: {

View File

@@ -58,7 +58,7 @@ export const TableContainer = styled("div", {
border: "none",
backgroundColor: "transparent",
...theme.typography.caption,
...(theme.typography.caption as any),
// fontSize: "0.8125rem",
lineHeight: "inherit !important",
@@ -136,14 +136,13 @@ export const TableContainer = styled("div", {
".row-hover-iconButton": {
color: theme.palette.text.disabled,
transitionDuration: "0s",
".rdg-row:hover &": {
color: theme.palette.text.primary,
backgroundColor: alpha(
theme.palette.action.hover,
theme.palette.action.hoverOpacity * 1.5
),
},
},
".rdg-row:hover .row-hover-iconButton": {
color: theme.palette.text.primary,
backgroundColor: alpha(
theme.palette.action.hover,
theme.palette.action.hoverOpacity * 1.5
),
},
".cell-collapse-padding": {

View File

@@ -47,6 +47,8 @@ export default function AddRow() {
ref={anchorEl}
>
<Button
variant="contained"
color="primary"
disabled={isCollectionGroup() || !addRow}
onClick={handleClick}
startIcon={<AddRowIcon />}
@@ -55,6 +57,8 @@ export default function AddRow() {
</Button>
<Button
variant="contained"
color="primary"
aria-label="Select row add position"
aria-haspopup="menu"
style={{ padding: 0 }}

View File

@@ -6,7 +6,7 @@ import Ansi from "ansi-to-react";
import { TIME_FORMAT } from "constants/dates";
const Root = styled("div")(({ theme }) => ({
...theme.typography.caption,
...(theme.typography.caption as any),
fontFamily: theme.typography.fontFamilyMono,
// TODO:
color: "#CCC",

View File

@@ -118,6 +118,7 @@ export default function BuildLogsSnack({ onClose, onOpenPanel }) {
aria-label="Expand"
size="small"
onClick={() => setExpanded(!expanded)}
style={{ color: "white" }}
>
{expanded ? <CollapseIcon /> : <ExpandIcon />}
</IconButton>
@@ -135,13 +136,19 @@ export default function BuildLogsSnack({ onClose, onOpenPanel }) {
buildLogExpanded: 0,
});
}}
style={{ color: "white" }}
>
<OpenIcon />
</IconButton>
</Tooltip>
<Tooltip title="Close">
<IconButton aria-label="Close" size="small" onClick={onClose}>
<IconButton
aria-label="Close"
size="small"
onClick={onClose}
style={{ color: "white" }}
>
<CloseIcon />
</IconButton>
</Tooltip>

View File

@@ -30,7 +30,7 @@ const Accordion = styled(MuiAccordion)(({ theme }) => ({
marginTop: 0,
"&::before": { display: "none" },
...theme.typography.caption,
...(theme.typography.caption as any),
fontFamily: theme.typography.fontFamilyMono,
}));

View File

@@ -26,7 +26,7 @@ const Accordion = styled(MuiAccordion)(({ theme }) => ({
marginTop: 0,
"&::before": { display: "none" },
...theme.typography.caption,
...(theme.typography.caption as any),
fontFamily: theme.typography.fontFamilyMono,
}));

View File

@@ -6,7 +6,7 @@ export const CloudLogSubheader = styled((props) => (
zIndex: 2,
"&:not(:first-child)": { marginTop: theme.spacing(2) },
...theme.typography.subtitle2,
...(theme.typography.subtitle2 as any),
padding: theme.spacing((32 - 20) / 2 / 8, 1.5),
lineHeight: "20px",

View File

@@ -0,0 +1,83 @@
import { useRef, useState } from "react";
import {
Button,
ButtonProps,
Menu,
MenuItem,
Divider,
ListItemIcon,
} from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
import EmailIcon from "@mui/icons-material/EmailOutlined";
import { extensionTypes, extensionNames, ExtensionType } from "./utils";
import { EMAIL_REQUEST } from "@src/constants/externalLinks";
export interface IAddExtensionButtonProps extends Partial<ButtonProps> {
handleAddExtension: (type: ExtensionType) => void;
}
export default function AddExtensionButton({
handleAddExtension,
...props
}: IAddExtensionButtonProps) {
const addButtonRef = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(false);
const handleChooseAddType = (type: ExtensionType) => {
setOpen(false);
handleAddExtension(type);
};
return (
<>
<Button
color="primary"
startIcon={<AddIcon />}
onClick={() => setOpen(true)}
ref={addButtonRef}
sx={{
alignSelf: { sm: "flex-end" },
mt: {
sm: `calc(var(--dialog-title-height) * -1 + (var(--dialog-title-height) - 32px) / 2)`,
},
mx: { xs: "var(--dialog-spacing)", sm: undefined },
mr: { sm: 8 },
mb: { xs: 1.5, sm: 2 },
}}
{...props}
>
Add Extension
</Button>
<Menu
anchorEl={addButtonRef.current}
open={open}
onClose={() => setOpen(false)}
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
transformOrigin={{ vertical: "top", horizontal: "right" }}
>
{extensionTypes.map((type) => (
<MenuItem onClick={() => handleChooseAddType(type)}>
{extensionNames[type]}
</MenuItem>
))}
<Divider variant="middle" />
<MenuItem
component="a"
href={EMAIL_REQUEST}
target="_blank"
rel="noopener noreferrer"
>
<ListItemIcon>
<EmailIcon aria-label="Send email" />
</ListItemIcon>
Request new Extension
</MenuItem>
</Menu>
</>
);
}

View File

@@ -1,43 +1,27 @@
import { useState, useRef } from "react";
import { format, formatRelative } from "date-fns";
import {
Stack,
ButtonBase,
List,
ListItem,
ListItemText,
Avatar,
Button,
IconButton,
Menu,
MenuItem,
Divider,
ListItemIcon,
Switch,
Tooltip,
Typography,
} from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
import ExtensionIcon from "@src/assets/icons/Extension";
import DuplicateIcon from "@src/assets/icons/Copy";
import EditIcon from "@mui/icons-material/EditOutlined";
import DeleteIcon from "@mui/icons-material/DeleteOutlined";
import EmailIcon from "@mui/icons-material/EmailOutlined";
import EmptyState from "@src/components/EmptyState";
import {
extensionTypes,
extensionNames,
IExtension,
ExtensionType,
} from "./utils";
import { extensionNames, IExtension } from "./utils";
import { DATE_TIME_FORMAT } from "@src/constants/dates";
import { EMAIL_REQUEST } from "@src/constants/externalLinks";
export interface IExtensionListProps {
extensions: IExtension[];
handleAddExtension: (type: ExtensionType) => void;
handleUpdateActive: (index: number, active: boolean) => void;
handleDuplicate: (index: number) => void;
handleEdit: (index: number) => void;
@@ -46,204 +30,127 @@ export interface IExtensionListProps {
export default function ExtensionList({
extensions,
handleAddExtension,
handleUpdateActive,
handleDuplicate,
handleEdit,
handleDelete,
}: IExtensionListProps) {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const addButtonRef = useRef(null);
const activeExtensionCount = extensions.filter(
(extension) => extension.active
).length;
const handleAddButton = () => {
setAnchorEl(addButtonRef.current);
};
const handleChooseAddType = (type: ExtensionType) => {
handleClose();
handleAddExtension(type);
};
const handleClose = () => {
setAnchorEl(null);
};
if (extensions.length === 0)
return (
<EmptyState
message="Add your first extension above"
description="Your extensions will appear here"
Icon={ExtensionIcon}
style={{ height: 89 * 3 - 1 }}
/>
);
return (
<>
<Stack
direction="row"
spacing={2}
justifyContent="space-between"
alignItems="center"
style={{ marginTop: 0 }}
>
<Typography
variant="subtitle2"
component="h2"
style={{ fontFeatureSettings: "'case', 'tnum'" }}
>
Extensions ({activeExtensionCount} / {extensions.length})
</Typography>
<Button
color="primary"
startIcon={<AddIcon />}
onClick={handleAddButton}
ref={addButtonRef}
>
Add Extension
</Button>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={handleClose}
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
transformOrigin={{ vertical: "top", horizontal: "right" }}
>
{extensionTypes.map((type) => (
<MenuItem onClick={() => handleChooseAddType(type)}>
{extensionNames[type]}
</MenuItem>
))}
<Divider variant="middle" />
<MenuItem
component="a"
href={EMAIL_REQUEST}
target="_blank"
rel="noopener noreferrer"
>
<ListItemIcon>
<EmailIcon aria-label="Send email" />
</ListItemIcon>
Request new Extension
</MenuItem>
</Menu>
</Stack>
{extensions.length === 0 ? (
<ButtonBase
onClick={handleAddButton}
sx={{
width: "100%",
height: 72 * 3,
borderRadius: 1,
"&:hover": { bgcolor: "action.hover" },
}}
>
<EmptyState
message="Add your first extension"
description="Your extensions will appear here."
Icon={ExtensionIcon}
/>
</ButtonBase>
) : (
<List style={{ paddingTop: 0, minHeight: 72 * 3 }}>
{extensions.map((extensionObject, index) => (
<ListItem
disableGutters
dense={false}
divider={index !== extensions.length - 1}
children={
<ListItemText
primary={extensionObject.name}
secondary={extensionNames[extensionObject.type]}
primaryTypographyProps={{
style: {
minHeight: 40,
display: "flex",
alignItems: "center",
},
}}
/>
}
secondaryAction={
<Stack alignItems="flex-end">
<Stack direction="row" alignItems="center" spacing={1}>
<Tooltip
title={extensionObject.active ? "Deactivate" : "Activate"}
>
<Switch
checked={extensionObject.active}
onClick={() =>
handleUpdateActive(index, !extensionObject.active)
}
inputProps={{ "aria-label": "Activate" }}
sx={{ mr: 1 }}
/>
</Tooltip>
<Tooltip title="Duplicate">
<IconButton
aria-label="Duplicate"
onClick={() => handleDuplicate(index)}
>
<DuplicateIcon />
</IconButton>
</Tooltip>
<Tooltip title="Edit">
<IconButton
aria-label="Edit"
onClick={() => handleEdit(index)}
>
<EditIcon />
</IconButton>
</Tooltip>
<Tooltip title="Delete">
<IconButton
aria-label="Delete"
color="error"
onClick={() => handleDelete(index)}
sx={{ "&&": { mr: -1.5 } }}
>
<DeleteIcon />
</IconButton>
</Tooltip>
</Stack>
<Tooltip
title={
<>
Last updated
<br />
by {extensionObject.lastEditor.displayName}
<br />
at{" "}
{format(
extensionObject.lastEditor.lastUpdate,
DATE_TIME_FORMAT
)}
</>
}
>
<Stack direction="row" spacing={1} alignItems="center">
<Typography
variant="body2"
sx={{ color: "text.disabled" }}
>
{formatRelative(
extensionObject.lastEditor.lastUpdate,
new Date()
)}
</Typography>
<Avatar
alt={`${extensionObject.lastEditor.displayName}s profile photo`}
src={extensionObject.lastEditor.photoURL}
sx={{ width: 24, height: 24, "&&": { mr: -0.5 } }}
/>
</Stack>
</Tooltip>
</Stack>
}
<List style={{ minHeight: 89 * 3 - 1 }} disablePadding>
{extensions.map((extensionObject, index) => (
<ListItem
disableGutters
dense={false}
divider={index !== extensions.length - 1}
children={
<ListItemText
primary={extensionObject.name}
secondary={extensionNames[extensionObject.type]}
primaryTypographyProps={{
style: {
minHeight: 40,
display: "flex",
alignItems: "center",
},
}}
/>
))}
</List>
)}
</>
}
secondaryAction={
<Stack alignItems="flex-end">
<Stack direction="row" alignItems="center" spacing={1}>
<Tooltip
title={extensionObject.active ? "Deactivate" : "Activate"}
>
<Switch
checked={extensionObject.active}
onClick={() =>
handleUpdateActive(index, !extensionObject.active)
}
inputProps={{ "aria-label": "Activate" }}
sx={{ mr: 1 }}
/>
</Tooltip>
<Tooltip title="Duplicate">
<IconButton
aria-label="Duplicate"
onClick={() => handleDuplicate(index)}
>
<DuplicateIcon />
</IconButton>
</Tooltip>
<Tooltip title="Edit">
<IconButton
aria-label="Edit"
onClick={() => handleEdit(index)}
>
<EditIcon />
</IconButton>
</Tooltip>
<Tooltip title="Delete…">
<IconButton
aria-label="Delete…"
color="error"
onClick={() => handleDelete(index)}
sx={{ "&&": { mr: -1.5 } }}
>
<DeleteIcon />
</IconButton>
</Tooltip>
</Stack>
<Tooltip
title={
<>
Last updated
<br />
by {extensionObject.lastEditor.displayName}
<br />
at{" "}
{format(
extensionObject.lastEditor.lastUpdate,
DATE_TIME_FORMAT
)}
</>
}
>
<Stack direction="row" spacing={1} alignItems="center">
<Typography variant="body2" sx={{ color: "text.disabled" }}>
{formatRelative(
extensionObject.lastEditor.lastUpdate,
new Date()
)}
</Typography>
<Avatar
alt={`${extensionObject.lastEditor.displayName}s profile photo`}
src={extensionObject.lastEditor.photoURL}
sx={{ width: 24, height: 24, "&&": { mr: -0.5 } }}
/>
</Stack>
</Tooltip>
</Stack>
}
sx={{
flexWrap: { xs: "wrap", sm: "nowrap" },
"& .MuiListItemSecondaryAction-root": {
position: { xs: "static", sm: "absolute" },
width: { xs: "100%", sm: "auto" },
transform: { xs: "none", sm: "translateY(-50%)" },
},
pr: { xs: 0, sm: 216 / 8 },
}}
/>
))}
</List>
);
}

View File

@@ -162,7 +162,7 @@ export default function ExtensionModal({
onClick: () => {
let warningMessage;
if (!validation.condition && !validation.extensionBody) {
warningMessage = "Condition and extension body are not valid";
warningMessage = "Condition and Extension body are not valid";
} else if (!validation.condition) {
warningMessage = "Condition is not valid";
} else if (!validation.extensionBody) {

View File

@@ -1,11 +1,10 @@
import { useState } from "react";
import _isEqual from "lodash/isEqual";
import { Breadcrumbs } from "@mui/material";
import TableHeaderButton from "../TableHeaderButton";
import ExtensionIcon from "@src/assets/icons/Extension";
import Modal from "@src/components/Modal";
import AddExtensionButton from "./AddExtensionButton";
import ExtensionList from "./ExtensionList";
import ExtensionModal from "./ExtensionModal";
import ExtensionMigration from "./ExtensionMigration";
@@ -40,12 +39,6 @@ export default function Extensions() {
const snackLogContext = useSnackLogContext();
const edited = !_isEqual(currentExtensionObjects, localExtensionsObjects);
const tablePathTokens =
tableState?.tablePath?.split("/").filter(function (_, i) {
// replace IDs with dash that appears at even indexes
return i % 2 === 0;
}) ?? [];
const handleOpen = () => {
if (tableState?.config.sparks) {
// migration is required
@@ -56,13 +49,16 @@ export default function Extensions() {
}
};
const handleClose = () => {
const handleClose = (
setOpen: React.Dispatch<React.SetStateAction<boolean>>
) => {
if (edited) {
setOpen(true);
requestConfirmation({
title: "Discard changes",
body: "You will lose changes you have made to extensions",
title: "Discard changes?",
confirm: "Discard",
handleConfirm: () => {
setOpen(false);
setLocalExtensionsObjects(currentExtensionObjects);
setOpenExtensionList(false);
},
@@ -83,8 +79,8 @@ export default function Extensions() {
const handleSaveDeploy = async () => {
handleSaveExtensions();
try {
snackLogContext.requestSnackLog();
if (rowyRun)
if (rowyRun) {
snackLogContext.requestSnackLog();
rowyRun({
route: runRoutes.buildFunction,
body: {
@@ -93,7 +89,8 @@ export default function Extensions() {
tableConfigPath: tableState?.config.tableConfig.path,
},
});
analytics.logEvent("deployed_extensions");
analytics.logEvent("deployed_extensions");
}
} catch (e) {
console.error(e);
}
@@ -163,8 +160,8 @@ export default function Extensions() {
const handleDelete = (index: number) => {
requestConfirmation({
title: `Delete ${localExtensionsObjects[index].name}?`,
body: "This extension will be permanently deleted.",
title: `Delete ${localExtensionsObjects[index].name}?`,
body: "This Extension will be permanently deleted when you save",
confirm: "Confirm",
handleConfirm: () => {
setLocalExtensionsObjects(
@@ -180,6 +177,10 @@ export default function Extensions() {
lastUpdate: Date.now(),
});
const activeExtensionCount = localExtensionsObjects.filter(
(extension) => extension.active
).length;
return (
<>
<TableHeaderButton
@@ -191,33 +192,32 @@ export default function Extensions() {
{openExtensionList && !!tableState && (
<Modal
onClose={handleClose}
disableBackdropClick={edited}
disableEscapeKeyDown={edited}
maxWidth="sm"
fullWidth
title="Extensions"
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={
<>
<Breadcrumbs aria-label="breadcrumb">
{tablePathTokens.map((pathToken) => (
<code>{pathToken}</code>
))}
</Breadcrumbs>
<ExtensionList
extensions={localExtensionsObjects}
handleAddExtension={(type: ExtensionType) => {
setExtensionModal({
mode: "add",
extensionObject: emptyExtensionObject(
type,
currentEditor()
),
});
}}
handleUpdateActive={handleUpdateActive}
handleEdit={handleEdit}
handleDuplicate={handleDuplicate}
handleDelete={handleDelete}
/>
</>
<ExtensionList
extensions={localExtensionsObjects}
handleUpdateActive={handleUpdateActive}
handleEdit={handleEdit}
handleDuplicate={handleDuplicate}
handleDelete={handleDelete}
/>
}
actions={{
primary: {

View File

@@ -0,0 +1,83 @@
import { useRef, useState } from "react";
import {
Button,
ButtonProps,
Menu,
MenuItem,
Divider,
ListItemIcon,
} from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
import EmailIcon from "@mui/icons-material/EmailOutlined";
import { webhookTypes, webhookNames, WebhookType } from "./utils";
import { EMAIL_REQUEST } from "@src/constants/externalLinks";
export interface IAddWebhookButtonProps extends Partial<ButtonProps> {
handleAddWebhook: (type: WebhookType) => void;
}
export default function AddWebhookButton({
handleAddWebhook,
...props
}: IAddWebhookButtonProps) {
const addButtonRef = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(false);
const handleChooseAddType = (type: WebhookType) => {
setOpen(false);
handleAddWebhook(type);
};
return (
<>
<Button
color="primary"
startIcon={<AddIcon />}
onClick={() => setOpen(true)}
ref={addButtonRef}
sx={{
alignSelf: { sm: "flex-end" },
mt: {
sm: `calc(var(--dialog-title-height) * -1 + (var(--dialog-title-height) - 32px) / 2)`,
},
mx: { xs: "var(--dialog-spacing)", sm: undefined },
mr: { sm: 8 },
mb: { xs: 1.5, sm: 2 },
}}
{...props}
>
Add webhook
</Button>
<Menu
anchorEl={addButtonRef.current}
open={open}
onClose={() => setOpen(false)}
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
transformOrigin={{ vertical: "top", horizontal: "right" }}
>
{webhookTypes.map((type) => (
<MenuItem onClick={() => handleChooseAddType(type)}>
{webhookNames[type]}
</MenuItem>
))}
<Divider variant="middle" />
<MenuItem
component="a"
href={EMAIL_REQUEST}
target="_blank"
rel="noopener noreferrer"
>
<ListItemIcon>
<EmailIcon aria-label="Send email" />
</ListItemIcon>
Request new webhook
</MenuItem>
</Menu>
</>
);
}

View File

@@ -1,3 +1,6 @@
import { Typography } from "@mui/material";
import WarningIcon from "@mui/icons-material/WarningAmber";
export const webhookTypes = [
"basic",
"typeform",
@@ -36,7 +39,7 @@ const additionalVariables = [
},
];
export default {
export const webhookBasic = {
name: "Basic",
parser: {
additionalVariables,
@@ -60,6 +63,13 @@ export default {
}`,
},
auth: (webhookObject, setWebhookObject) => {
return <>no auth option for basic atm</>;
return (
<Typography color="text.disabled">
<WarningIcon aria-label="Warning" style={{ verticalAlign: "bottom" }} />
&nbsp; Verification is not currently available for basic webhooks
</Typography>
);
},
};
export default webhookBasic;

View File

@@ -1,7 +1,8 @@
import { Typography, Link, TextField } from "@mui/material";
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
export default {
name: "Sendgrid",
export const webhookSendgrid = {
name: "SendGrid",
parser: {
additionalVariables: null,
extraLibs: null,
@@ -32,24 +33,25 @@ export default {
return (
<>
<Typography gutterBottom>
To verify the webhook call is sent from Sendgrid, You can enable
signed event webhooks
Enable Signed Event Webhooks on SendGrid by following{" "}
<Link
href={
"https://docs.sendgrid.com/for-developers/tracking-events/getting-started-event-webhook-security-features#the-signed-event-webhook"
}
href="https://docs.sendgrid.com/for-developers/tracking-events/getting-started-event-webhook-security-features#the-signed-event-webhook"
target="_blank"
rel="noopener noreferrer"
variant="body2"
underline="always"
variant="inherit"
>
here
</Link>{" "}
to set the secret, then add it below
these instructions
<InlineOpenInNewIcon />
</Link>
<br />
Then add the secret below.
</Typography>
<TextField
label={"Verification Key"}
id="sendgrid-verification-key"
label="Verification key"
value={webhookObject.auth.secret}
fullWidth
multiline
onChange={(e) => {
setWebhookObject({
@@ -62,3 +64,5 @@ export default {
);
},
};
export default webhookSendgrid;

View File

@@ -1,6 +1,7 @@
import { Typography, Link, TextField } from "@mui/material";
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
export default {
export const webhookTypeform = {
name: "Typeform",
parser: {
additionalVariables: null,
@@ -55,23 +56,24 @@ export default {
return (
<>
<Typography gutterBottom>
To verify the webhook call is sent from typeform, you need to add
secret on your webhook config on be follow the instructions{" "}
Add a secret to your Typeform webhook config by following{" "}
<Link
href={
"https://developers.typeform.com/webhooks/secure-your-webhooks/"
}
href="https://developers.typeform.com/webhooks/secure-your-webhooks/"
target="_blank"
rel="noopener noreferrer"
variant="body2"
underline="always"
variant="inherit"
>
here
</Link>{" "}
to set the secret, then add it below
these instructions
<InlineOpenInNewIcon />
</Link>
<br />
Then add the secret below.
</Typography>
<TextField
label={"Typeform Secret"}
id="typeform-secret"
label="Typeform secret"
fullWidth
value={webhookObject.auth.secret}
onChange={(e) => {
setWebhookObject({
@@ -84,3 +86,5 @@ export default {
);
},
};
export default webhookTypeform;

View File

@@ -1,25 +1,23 @@
import { IWebhookModalStepProps } from "./WebhookModal";
import {
FormControl,
FormLabel,
FormControlLabel,
Switch,
Typography,
} from "@mui/material";
import { FormControlLabel, Checkbox, Typography } from "@mui/material";
import { webhookSchemas } from "./utils";
export default function Step1Endpoint({
webhookObject,
setWebhookObject,
}: IWebhookModalStepProps) {
return (
<FormControl component="fieldset">
<FormLabel component="legend" className="visually-hidden">
Verification
</FormLabel>
<>
<Typography variant="inherit" paragraph>
Verification prevents malicious requests from being sent to this webhook
endpoint
</Typography>
<FormControlLabel
labelPlacement="start"
control={
<Switch
<Checkbox
checked={webhookObject.auth?.enabled}
onClick={() =>
setWebhookObject({
@@ -32,17 +30,16 @@ export default function Step1Endpoint({
}
/>
}
label="Enable Verification"
label="Enable verification for this webhook"
sx={{ mb: 2 }}
/>
{webhookObject.auth?.enabled ? (
webhookSchemas[webhookObject.type].auth(webhookObject, setWebhookObject)
) : (
<Typography>
Verification of webhooks is optional however it prevents malicious
actors from spoofing the original sender
</Typography>
)}
{webhookObject.auth?.enabled &&
webhookSchemas[webhookObject.type].auth(
webhookObject,
setWebhookObject
)}
{}
</FormControl>
</>
);
}

View File

@@ -34,8 +34,8 @@ export default function Step4Body({
return (
<>
<Typography gutterBottom>
Write the webhook parsed function. The returned object of the parser
will be added as new row{" "}
Write a function to parse webhook requests. Return an object, which will
be added as a new row.{" "}
<Link
href={
WIKI_LINKS[`webhooks${_upperFirst(webhookObject.type)}`] ||

View File

@@ -1,37 +1,27 @@
import { useState, useRef } from "react";
import { useAtom } from "jotai";
import { format, formatRelative } from "date-fns";
import {
Stack,
ButtonBase,
List,
ListItem,
ListItemText,
Avatar,
Button,
IconButton,
Menu,
MenuItem,
Divider,
ListItemIcon,
Switch,
Tooltip,
Typography,
} from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
import WebhookIcon from "@src/assets/icons/Webhook";
import LogsIcon from "@src/assets/icons/CloudLogs";
import EditIcon from "@mui/icons-material/EditOutlined";
import DeleteIcon from "@mui/icons-material/DeleteOutlined";
import CopyIcon from "@src/assets/icons/Copy";
import EmailIcon from "@mui/icons-material/EmailOutlined";
import LinkIcon from "@mui/icons-material/Link";
import EmptyState from "@src/components/EmptyState";
import { webhookTypes, webhookNames, IWebhook, WebhookType } from "./utils";
import { webhookNames, IWebhook } from "./utils";
import { DATE_TIME_FORMAT } from "@src/constants/dates";
import { useProjectContext } from "@src/contexts/ProjectContext";
import { EMAIL_REQUEST } from "@src/constants/externalLinks";
import {
modalAtom,
cloudLogFiltersAtom,
@@ -39,7 +29,6 @@ import {
export interface IWebhookListProps {
webhooks: IWebhook[];
handleAddWebhook: (type: WebhookType) => void;
handleUpdateActive: (index: number, active: boolean) => void;
handleEdit: (index: number) => void;
handleDelete: (index: number) => void;
@@ -47,243 +36,157 @@ export interface IWebhookListProps {
export default function WebhookList({
webhooks,
handleAddWebhook,
handleUpdateActive,
handleEdit,
handleDelete,
}: IWebhookListProps) {
const { settings, tableState } = useProjectContext();
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const addButtonRef = useRef(null);
const [, setModal] = useAtom(modalAtom);
const [, setCloudLogFilters] = useAtom(cloudLogFiltersAtom);
const activeWebhookCount = webhooks.filter(
(webhook) => webhook.active
).length;
const handleAddButton = () => {
setAnchorEl(addButtonRef.current);
};
const handleChooseAddType = (type: WebhookType) => {
handleClose();
handleAddWebhook(type);
};
const handleClose = () => {
setAnchorEl(null);
};
const baseUrl = `${settings?.services?.hooks}/wh/${tableState?.tablePath}/`;
if (webhooks.length === 0)
return (
<EmptyState
message="Add your first webhook above"
description="Your webhooks will appear here"
Icon={WebhookIcon}
style={{ height: 89 * 3 - 1 }}
/>
);
return (
<>
<Stack
direction="row"
spacing={2}
justifyContent="space-between"
alignItems="center"
style={{ marginTop: 0 }}
>
<Typography
variant="subtitle2"
component="h2"
style={{ fontFeatureSettings: "'case', 'tnum'" }}
>
Webhooks ({activeWebhookCount} / {webhooks.length})
</Typography>
<Button
color="primary"
startIcon={<AddIcon />}
onClick={handleAddButton}
ref={addButtonRef}
>
Add webhook
</Button>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={handleClose}
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
transformOrigin={{ vertical: "top", horizontal: "right" }}
>
{webhookTypes.map((type) => (
<MenuItem onClick={() => handleChooseAddType(type)}>
{webhookNames[type]}
</MenuItem>
))}
<Divider variant="middle" />
<MenuItem
component="a"
href={EMAIL_REQUEST}
target="_blank"
rel="noopener noreferrer"
>
<ListItemIcon>
<EmailIcon aria-label="Send email" />
</ListItemIcon>
Request new webhook
</MenuItem>
</Menu>
</Stack>
{webhooks.length === 0 ? (
<ButtonBase
onClick={handleAddButton}
sx={{
width: "100%",
height: 72 * 3,
borderRadius: 1,
"&:hover": { bgcolor: "action.hover" },
}}
>
<EmptyState
message="Add your first webhook"
description="Your webhooks will appear here."
Icon={WebhookIcon}
/>
</ButtonBase>
) : (
<List style={{ paddingTop: 0, minHeight: 72 * 3 }}>
{webhooks.map((webhook, index) => (
<ListItem
disableGutters
dense={false}
divider={index !== webhooks.length - 1}
children={
<ListItemText
primary={
<>
{webhook.name} <code>{webhookNames[webhook.type]}</code>
</>
}
secondary={
<div style={{ display: "flex", alignItems: "center" }}>
<div
style={{
maxWidth: 340,
overflowX: "auto",
whiteSpace: "nowrap",
}}
>
<Typography variant="caption">
<code>
{baseUrl}
{webhook.endpoint}
</code>
</Typography>
</div>
<Tooltip title="copy to clipboard">
<IconButton
onClick={() =>
navigator.clipboard.writeText(
`${baseUrl}${webhook.endpoint}`
)
}
>
<CopyIcon />
</IconButton>
</Tooltip>
</div>
}
primaryTypographyProps={{
style: {
minHeight: 40,
display: "flex",
alignItems: "center",
},
}}
/>
}
secondaryAction={
<Stack alignItems="flex-end">
<Stack direction="row" alignItems="center" spacing={1}>
<Tooltip title={webhook.active ? "Deactivate" : "Activate"}>
<Switch
checked={webhook.active}
onClick={() =>
handleUpdateActive(index, !webhook.active)
}
inputProps={{ "aria-label": "Activate" }}
sx={{ mr: 1 }}
/>
</Tooltip>
<Tooltip title="Logs">
<IconButton
aria-label="Logs"
onClick={() => {
setModal("cloudLogs");
setCloudLogFilters({
type: "webhook",
timeRange: { type: "days", value: 7 },
webhook: [webhook.endpoint],
});
}}
>
<LogsIcon />
</IconButton>
</Tooltip>
<Tooltip title="Edit">
<IconButton
aria-label="Edit"
onClick={() => handleEdit(index)}
>
<EditIcon />
</IconButton>
</Tooltip>
<Tooltip title="Delete">
<IconButton
aria-label="Delete"
color="error"
onClick={() => handleDelete(index)}
sx={{ "&&": { mr: -1.5 } }}
>
<DeleteIcon />
</IconButton>
</Tooltip>
</Stack>
<Tooltip
title={
<>
Last updated
<br />
by {webhook.lastEditor.displayName}
<br />
at{" "}
{format(
webhook.lastEditor.lastUpdate,
DATE_TIME_FORMAT
)}
</>
}
<List style={{ paddingTop: 0, minHeight: 89 * 3 - 1 }} disablePadding>
{webhooks.map((webhook, index) => (
<ListItem
disableGutters
dense={false}
divider={index !== webhooks.length - 1}
children={
<ListItemText
primary={webhook.name}
secondary={
<>
{webhookNames[webhook.type]}{" "}
<code
style={{
userSelect: "all",
paddingRight: 0,
}}
>
<Stack direction="row" spacing={1} alignItems="center">
<Typography
variant="body2"
sx={{ color: "text.disabled" }}
<Tooltip title="Endpoint ID">
<span>{webhook.endpoint}</span>
</Tooltip>
<Tooltip title="Copy endpoint URL">
<IconButton
onClick={() =>
navigator.clipboard.writeText(
baseUrl + webhook.endpoint
)
}
size="small"
color="secondary"
sx={{ my: (20 - 32) / 2 / 8 }}
>
{formatRelative(
webhook.lastEditor.lastUpdate,
new Date()
)}
</Typography>
<Avatar
alt={`${webhook.lastEditor.displayName}s profile photo`}
src={webhook.lastEditor.photoURL}
sx={{ width: 24, height: 24, "&&": { mr: -0.5 } }}
/>
</Stack>
</Tooltip>
</Stack>
<LinkIcon fontSize="small" />
</IconButton>
</Tooltip>
</code>
</>
}
primaryTypographyProps={{
style: {
minHeight: 40,
display: "flex",
alignItems: "center",
},
}}
/>
))}
</List>
)}
</>
}
secondaryAction={
<Stack alignItems="flex-end">
<Stack direction="row" alignItems="center" spacing={1}>
<Tooltip title={webhook.active ? "Deactivate" : "Activate"}>
<Switch
checked={webhook.active}
onClick={() => handleUpdateActive(index, !webhook.active)}
inputProps={{ "aria-label": "Activate" }}
sx={{ mr: 1 }}
/>
</Tooltip>
<Tooltip title="Logs">
<IconButton
aria-label="Logs"
onClick={() => {
setModal("cloudLogs");
setCloudLogFilters({
type: "webhook",
timeRange: { type: "days", value: 7 },
webhook: [webhook.endpoint],
});
}}
>
<LogsIcon />
</IconButton>
</Tooltip>
<Tooltip title="Edit">
<IconButton
aria-label="Edit"
onClick={() => handleEdit(index)}
>
<EditIcon />
</IconButton>
</Tooltip>
<Tooltip title="Delete…">
<IconButton
aria-label="Delete…"
color="error"
onClick={() => handleDelete(index)}
sx={{ "&&": { mr: -1.5 } }}
>
<DeleteIcon />
</IconButton>
</Tooltip>
</Stack>
<Tooltip
title={
<>
Last updated
<br />
by {webhook.lastEditor.displayName}
<br />
at {format(webhook.lastEditor.lastUpdate, DATE_TIME_FORMAT)}
</>
}
>
<Stack direction="row" spacing={1} alignItems="center">
<Typography variant="body2" sx={{ color: "text.disabled" }}>
{formatRelative(webhook.lastEditor.lastUpdate, new Date())}
</Typography>
<Avatar
alt={`${webhook.lastEditor.displayName}s profile photo`}
src={webhook.lastEditor.photoURL}
sx={{ width: 24, height: 24, "&&": { mr: -0.5 } }}
/>
</Stack>
</Tooltip>
</Stack>
}
sx={{
flexWrap: { xs: "wrap", sm: "nowrap" },
"& .MuiListItemSecondaryAction-root": {
position: { xs: "static", sm: "absolute" },
width: { xs: "100%", sm: "auto" },
transform: { xs: "none", sm: "translateY(-50%)" },
},
pr: { xs: 0, sm: 216 / 8 },
}}
/>
))}
</List>
);
}

View File

@@ -2,7 +2,13 @@ import { useState } from "react";
import _isEqual from "lodash/isEqual";
import useStateRef from "react-usestateref";
import { Grid, TextField, FormControlLabel, Switch } from "@mui/material";
import {
Grid,
TextField,
FormControlLabel,
Switch,
Stack,
} from "@mui/material";
import Modal, { IModalProps } from "@src/components/Modal";
import SteppedAccordion from "@src/components/SteppedAccordion";
@@ -16,7 +22,9 @@ import { webhookNames, IWebhook } from "./utils";
import CopyIcon from "@src/assets/icons/Copy";
import { useProjectContext } from "@src/contexts/ProjectContext";
import { IconButton, Tooltip, Typography } from "@mui/material";
type StepValidation = Record<"condition" | "parser", boolean>;
export interface IWebhookModalStepProps {
webhookObject: IWebhook;
setWebhookObject: React.Dispatch<React.SetStateAction<IWebhook>>;
@@ -128,36 +136,39 @@ export default function WebhookModal({
}activated`}
/>
</Grid>
<div style={{ display: "flex", alignItems: "center" }}>
<Typography>URL:</Typography>
<div
style={{
maxWidth: "90%",
overflowX: "auto",
whiteSpace: "nowrap",
}}
>
<Typography variant="caption">
<code>
{baseUrl}
{webhookObject.endpoint}
</code>
</Typography>
</div>
<Tooltip title="copy to clipboard">
<IconButton
onClick={() =>
navigator.clipboard.writeText(
`${baseUrl}${webhookObject.endpoint}`
)
}
>
<CopyIcon />
</IconButton>
</Tooltip>
</div>
</Grid>
<Stack direction="row" alignItems="center" style={{ marginTop: 0 }}>
<Typography
variant="inherit"
style={{ whiteSpace: "nowrap", flexShrink: 0 }}
>
Endpoint URL:
</Typography>
&nbsp;
<Typography
variant="caption"
style={{ overflowX: "auto", whiteSpace: "nowrap", flexGrow: 1 }}
>
<code>
{baseUrl}
{webhookObject.endpoint}
</code>
</Typography>
<Tooltip title="Copy endpoint URL">
<IconButton
onClick={() =>
navigator.clipboard.writeText(
`${baseUrl}${webhookObject.endpoint}`
)
}
sx={{ flexShrink: 0, mr: -0.75 }}
>
<CopyIcon />
</IconButton>
</Tooltip>
</Stack>
<SteppedAccordion
steps={[
{
@@ -178,6 +189,7 @@ export default function WebhookModal({
content: <Step3Body {...stepProps} />,
},
]}
style={{ marginTop: "var(--dialog-contents-spacing)" }}
/>
</>
}

View File

@@ -1,11 +1,10 @@
import { useState } from "react";
import _isEqual from "lodash/isEqual";
import { Breadcrumbs } from "@mui/material";
import TableHeaderButton from "../TableHeaderButton";
import WebhookIcon from "@src/assets/icons/Webhook";
import Modal from "@src/components/Modal";
import AddWebhookButton from "./AddWebhookButton";
import WebhookList from "./WebhookList";
import WebhookModal from "./WebhookModal";
@@ -34,26 +33,25 @@ export default function Webhooks() {
webhookObject: IWebhook;
index?: number;
} | null>(null);
if (!compatibleRowyRunVersion?.({ minVersion: "1.2.0" })) return <></>;
const edited = !_isEqual(currentWebhooks, localWebhooksObjects);
const tablePathTokens =
tableState?.tablePath?.split("/").filter(function (_, i) {
// replace IDs with dash that appears at even indexes
return i % 2 === 0;
}) ?? [];
if (!compatibleRowyRunVersion?.({ minVersion: "1.2.0" })) return null;
const edited = !_isEqual(currentWebhooks, localWebhooksObjects);
const handleOpen = () => {
setOpenWebhookList(true);
};
const handleClose = () => {
const handleClose = (
setOpen: React.Dispatch<React.SetStateAction<boolean>>
) => {
if (edited) {
setOpen(true);
requestConfirmation({
title: "Discard changes",
body: "You will lose changes you have made to webhooks",
title: "Discard changes?",
confirm: "Discard",
handleConfirm: () => {
setOpen(false);
setLocalWebhooksObjects(currentWebhooks);
setOpenWebhookList(false);
},
@@ -142,8 +140,8 @@ export default function Webhooks() {
const handleDelete = (index: number) => {
requestConfirmation({
title: `Delete ${localWebhooksObjects[index].name}?`,
body: "This webhook will be permanently deleted.",
title: `Delete ${localWebhooksObjects[index].name}?`,
body: "This webhook will be permanently deleted when you save",
confirm: "Confirm",
handleConfirm: () => {
setLocalWebhooksObjects(
@@ -159,6 +157,10 @@ export default function Webhooks() {
lastUpdate: Date.now(),
});
const activeWebhookCount = localWebhooksObjects.filter(
(webhook) => webhook.active
).length;
return (
<>
<TableHeaderButton
@@ -170,29 +172,31 @@ export default function Webhooks() {
{openWebhookList && !!tableState && (
<Modal
onClose={handleClose}
disableBackdropClick={edited}
disableEscapeKeyDown={edited}
maxWidth="sm"
fullWidth
title="Webhooks"
title={`Webhooks (${activeWebhookCount}\u2009/\u2009${localWebhooksObjects.length})`}
header={
<AddWebhookButton
handleAddWebhook={(type: WebhookType) => {
setWebhookModal({
mode: "add",
webhookObject: emptyWebhookObject(type, currentEditor()),
});
}}
variant={
localWebhooksObjects.length === 0 ? "contained" : "outlined"
}
/>
}
children={
<>
<Breadcrumbs aria-label="breadcrumb">
{tablePathTokens.map((pathToken) => (
<code>{pathToken}</code>
))}
</Breadcrumbs>
<WebhookList
webhooks={localWebhooksObjects}
handleAddWebhook={(type: WebhookType) => {
setWebhookModal({
mode: "add",
webhookObject: emptyWebhookObject(type, currentEditor()),
});
}}
handleUpdateActive={handleUpdateActive}
handleEdit={handleEdit}
handleDelete={handleDelete}
/>
</>
<WebhookList
webhooks={localWebhooksObjects}
handleUpdateActive={handleUpdateActive}
handleEdit={handleEdit}
handleDelete={handleDelete}
/>
}
actions={{
primary: {

View File

@@ -40,9 +40,9 @@ const additionalVariables = [
export type WebhookType = typeof webhookTypes[number];
export const webhookNames: Record<WebhookType, string> = {
sendgrid: "Sendgrid",
sendgrid: "SendGrid",
typeform: "Typeform",
//github:"Github",
//github:"GitHub",
// shopify: "Shopify",
// twitter: "Twitter",
// stripe: "Stripe",
@@ -73,6 +73,7 @@ export const webhookSchemas = {
typeform,
sendgrid,
};
export function emptyWebhookObject(
type: WebhookType,
user: IWebhookEditor

View File

@@ -30,9 +30,9 @@ export default function FadeList({
children,
disableTopDivider = true,
disableBottomDivider = false,
dividerSx,
topDividerSx,
bottomDividerSx,
dividerSx = [],
topDividerSx = [],
bottomDividerSx = [],
listSx,
}: IFadeListProps) {
const [scrollInfo, setRef] = useScrollInfo();
@@ -42,7 +42,12 @@ export default function FadeList({
{!disableTopDivider &&
scrollInfo.y.percentage !== null &&
scrollInfo.y.percentage > 0 && (
<Divider sx={{ ...dividerSx, ...topDividerSx }} />
<Divider
sx={[
...(Array.isArray(dividerSx) ? dividerSx : [dividerSx]),
...(Array.isArray(topDividerSx) ? topDividerSx : [topDividerSx]),
]}
/>
)}
<MemoizedList ref={setRef} sx={listSx}>
@@ -52,7 +57,14 @@ export default function FadeList({
{!disableBottomDivider &&
scrollInfo.y.percentage !== null &&
scrollInfo.y.percentage < 1 && (
<Divider sx={{ ...dividerSx, ...bottomDividerSx }} />
<Divider
sx={[
...(Array.isArray(dividerSx) ? dividerSx : [dividerSx]),
...(Array.isArray(bottomDividerSx)
? bottomDividerSx
: [bottomDividerSx]),
]}
/>
)}
</>
);

View File

@@ -57,7 +57,7 @@ export default function Checkbox({
letterSpacing: "inherit",
flexGrow: 1,
overflowX: "hidden",
mt: 0,
mt: "0 !important",
},
"& .MuiSwitch-root": { mr: -0.75 },

View File

@@ -52,7 +52,7 @@ export default function ConnectServiceSelect({
SelectProps={{
renderValue: (value) => `${(value as any[]).length} selected`,
displayEmpty: true,
classes: { root: classes.selectRoot },
classes: { select: classes.selectRoot },
...TextFieldProps.SelectProps,
// Must have this set to prevent MUI transforming `value`
// prop for this component to a comma-separated string

View File

@@ -2,7 +2,7 @@ import { lazy } from "react";
import { IFieldConfig, FieldType } from "@src/components/fields/types";
import withHeavyCell from "../_withTableCell/withHeavyCell";
import DurationIcon from "@mui/icons-material/Timer";
import DurationIcon from "@mui/icons-material/TimerOutlined";
import BasicCell from "../_BasicCell/BasicCellNull";
import NullEditor from "@src/components/Table/editors/NullEditor";

View File

@@ -60,7 +60,7 @@ export default function Json({
className={fieldClasses.root}
style={{
whiteSpace: "pre-wrap",
...theme.typography.caption,
...(theme.typography.caption as any),
fontFamily: theme.typography.fontFamilyMono,
wordBreak: "break-word",
}}
@@ -94,7 +94,10 @@ export default function Json({
<TabPanel value="tree" sx={{ p: 0 }}>
<div
className={fieldClasses.root}
style={{ overflowX: "auto", ...theme.typography.caption }}
style={{
overflowX: "auto",
...(theme.typography.caption as any),
}}
>
<ReactJson
src={sanitizedValue}

View File

@@ -4,6 +4,7 @@ import { DataGridHandle } from "react-data-grid";
import _sortBy from "lodash/sortBy";
import _find from "lodash/find";
import firebase from "firebase/app";
import { compare } from "compare-versions";
import { Button } from "@mui/material";
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
@@ -19,7 +20,6 @@ import { rowyRun, IRowyRunRequestProps } from "@src/utils/rowyRun";
import { rowyUser } from "@src/utils/fns";
import { WIKI_LINKS } from "@src/constants/externalLinks";
import { runRoutes } from "@src/constants/runRoutes";
import semver from "semver";
export type Table = {
id: string;
@@ -375,13 +375,12 @@ export const ProjectContextProvider: React.FC = ({ children }) => {
minVersion?: string;
maxVersion?: string;
}) => {
// example: "1.0.0", "1.0.0-beta.1", "1.0.0-rc.1+1"
const version = rowyRunVersion.split("-")[0];
if (!version) return false;
if (minVersion && semver.lt(version, minVersion)) return false;
if (maxVersion && semver.gt(version, maxVersion)) return false;
if (!rowyRunVersion) return false;
if (minVersion && compare(rowyRunVersion, minVersion, "<")) return false;
if (maxVersion && compare(rowyRunVersion, maxVersion, ">")) return false;
return true;
};
// A ref to the data grid. Contains data grid functions
const dataGridRef = useRef<DataGridHandle>(null);
const sideDrawerRef = useRef<SideDrawerRef>();

View File

@@ -200,7 +200,7 @@ export const components = (theme: Theme): ThemeOptions => {
"--dialog-contents-spacing": theme.spacing(3),
"& > * + *": { marginTop: "var(--dialog-contents-spacing)" },
...theme.typography.body2,
...(theme.typography.body2 as any),
},
},
},
@@ -266,7 +266,7 @@ export const components = (theme: Theme): ThemeOptions => {
caretColor: theme.palette.primary.main,
".Mui-error &": { caretColor: theme.palette.error.main },
},
inputSizeSmall: theme.typography.body2,
inputSizeSmall: theme.typography.body2 as any,
},
},
MuiFilledInput: {
@@ -341,7 +341,7 @@ export const components = (theme: Theme): ThemeOptions => {
MuiInputLabel: {
styleOverrides: {
root: {
...theme.typography.button,
...(theme.typography.button as any),
color: theme.palette.text.primary,
},
filled: {
@@ -360,7 +360,7 @@ export const components = (theme: Theme): ThemeOptions => {
MuiFormLabel: {
styleOverrides: {
root: {
...theme.typography.button,
...(theme.typography.button as any),
color: theme.palette.text.primary,
},
},
@@ -441,7 +441,7 @@ export const components = (theme: Theme): ThemeOptions => {
defaultProps: { disableSticky: true },
styleOverrides: {
root: {
...theme.typography.subtitle2,
...(theme.typography.subtitle2 as any),
color: theme.palette.text.primary,
lineHeight: "32px",
userSelect: "none",
@@ -1037,7 +1037,7 @@ export const components = (theme: Theme): ThemeOptions => {
MuiAlertTitle: {
styleOverrides: {
root: {
...theme.typography.subtitle2,
...(theme.typography.subtitle2 as any),
lineHeight: "1.5rem",
},
},
@@ -1091,7 +1091,7 @@ export const components = (theme: Theme): ThemeOptions => {
styleOverrides: {
root: {
"& .PrivatePickersYear-yearButton": {
...theme.typography.button,
...(theme.typography.button as any),
fontSize: "1rem",
},
},

305
yarn.lock
View File

@@ -1460,7 +1460,7 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.15.4", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.15.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
@@ -1488,6 +1488,13 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.16.3":
version "7.16.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5"
integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.7.2":
version "7.8.7"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.7.tgz#8fefce9802db54881ba59f90bb28719b4996324d"
@@ -1709,6 +1716,17 @@
"@emotion/weak-memoize" "^0.2.5"
stylis "^4.0.3"
"@emotion/cache@^11.6.0":
version "11.6.0"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.6.0.tgz#65fbdbbe4382f1991d8b20853c38e63ecccec9a1"
integrity sha512-ElbsWY1KMwEowkv42vGo0UPuLgtPYfIs9BxxVrmvsaJVvktknsHYYlx5NQ5g6zLDcOTyamlDc7FkRg2TAcQDKQ==
dependencies:
"@emotion/memoize" "^0.7.4"
"@emotion/sheet" "^1.1.0"
"@emotion/utils" "^1.0.0"
"@emotion/weak-memoize" "^0.2.5"
stylis "^4.0.10"
"@emotion/hash@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
@@ -1721,6 +1739,13 @@
dependencies:
"@emotion/memoize" "^0.7.4"
"@emotion/is-prop-valid@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.1.1.tgz#cbd843d409dfaad90f9404e7c0404c55eae8c134"
integrity sha512-bW1Tos67CZkOURLc0OalnfxtSXQJMrAMV0jZTVGJUPSOd4qgjF3+tTD5CwJM13PHA8cltGW1WGbbvV9NpvUZPw==
dependencies:
"@emotion/memoize" "^0.7.4"
"@emotion/memoize@^0.7.4", "@emotion/memoize@^0.7.5":
version "0.7.5"
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50"
@@ -1755,6 +1780,11 @@
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.2.tgz#1d9ffde531714ba28e62dac6a996a8b1089719d0"
integrity sha512-QQPB1B70JEVUHuNtzjHftMGv6eC3Y9wqavyarj4x4lg47RACkeSfNo5pxIOKizwS9AEFLohsqoaxGQj4p0vSIw==
"@emotion/sheet@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.1.0.tgz#56d99c41f0a1cda2726a05aa6a20afd4c63e58d2"
integrity sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==
"@emotion/styled@^11.3.0":
version "11.3.0"
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.3.0.tgz#d63ee00537dfb6ff612e31b0e915c5cf9925a207"
@@ -2362,10 +2392,10 @@
resolved "https://registry.yarnpkg.com/@mdi/js/-/js-5.9.55.tgz#8f5bc4d924c23f30dab20545ddc768e778bbc882"
integrity sha512-BbeHMgeK2/vjdJIRnx12wvQ6s8xAYfvMmEAVsUx9b+7GiQGQ9Za8jpwp17dMKr9CgKRvemlAM4S7S3QOtEbp4A==
"@mdi/js@^6.2.95":
version "6.2.95"
resolved "https://registry.yarnpkg.com/@mdi/js/-/js-6.2.95.tgz#decf0f86035990248f25b0a4e246a7d152211273"
integrity sha512-fbD22sEBathqVSQWcxshEtzhhRNFmMnV64z6T7DClRbQ9N5axorykt3Suv2zPzLDyiqH7UhNRu0VPvPCPDNpnQ==
"@mdi/js@^6.5.95":
version "6.5.95"
resolved "https://registry.yarnpkg.com/@mdi/js/-/js-6.5.95.tgz#2d895b013408f213252b77c30e0fdaaba6dc8b4b"
integrity sha512-x/bwEoAGP+Mo10Dfk5audNIPi7Yz8ZBrILcbXLW3ShOI/njpgodzpgpC2WYK3D2ZSC392peRRemIFb/JsyzzYQ==
"@monaco-editor/loader@^1.2.0":
version "1.2.0"
@@ -2382,129 +2412,129 @@
"@monaco-editor/loader" "^1.2.0"
prop-types "^15.7.2"
"@mui/core@5.0.0-alpha.50":
version "5.0.0-alpha.50"
resolved "https://registry.yarnpkg.com/@mui/core/-/core-5.0.0-alpha.50.tgz#055b7ccf8557f849e8e61a39a8e3943f8abad9b0"
integrity sha512-szmmzbyAjEQku17IoEWs7t77n0Kr4zVnT5fREfMd+CnCA4YDifr68ccGJfWaeoGp5Zw/JmfKjQJXLY4GgT6i/g==
"@mui/base@5.0.0-alpha.58":
version "5.0.0-alpha.58"
resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.58.tgz#01ab59a028f314e2f9a79f903a8336ac45853652"
integrity sha512-YZorCbbzkokQZUnj+sdjUWIe+jaesuSVpKgwWS2mWdE50v1Ti/qMmevIrOT1lvFAilpj80Bkcg4KtlGWBJ6utQ==
dependencies:
"@babel/runtime" "^7.15.4"
"@emotion/is-prop-valid" "^1.1.0"
"@mui/utils" "^5.0.1"
"@babel/runtime" "^7.16.3"
"@emotion/is-prop-valid" "^1.1.1"
"@mui/utils" "^5.2.2"
"@popperjs/core" "^2.4.4"
clsx "^1.1.1"
prop-types "^15.7.2"
react-is "^17.0.2"
"@mui/icons-material@^5.0.0":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.0.3.tgz#2a9d4f9078f375cef40f63a240b59cefa4a27eec"
integrity sha512-Lktn+4GNnXdVrOCUUvNNvOD9VyrGazWBsJy0BQeQgBe/+IjFMdlcNrDEUIlGlA5ZXOq7Mr/Mv9Os02mgF65jiw==
"@mui/icons-material@^5.2.0":
version "5.2.0"
resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.2.0.tgz#6c6135bb2d7891e29d6f9419df402b82dd663517"
integrity sha512-NvyrVaGKpP4R1yFw8BCnE0QcsQ67RtpgxPr4FtH8q60MDYPuPVczLOn5Ash5CFavoDWur/NfM/4DpT54yf3InA==
dependencies:
"@babel/runtime" "^7.15.4"
"@babel/runtime" "^7.16.3"
"@mui/lab@^5.0.0-alpha.50":
version "5.0.0-alpha.50"
resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.50.tgz#4102698b176eba28ef16ee1fcb6b0677f2fb221b"
integrity sha512-8DgRjQjjObrHpdFx5HSjw27zQlSf0gc+I911ZSs+YIn7TADQgtg3QzaHqe5o54b+91LhMfugQAtPlRjqXMaEKA==
"@mui/lab@^5.0.0-alpha.58":
version "5.0.0-alpha.58"
resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.58.tgz#47c3b2976df066119fa040131a63139677ba5a66"
integrity sha512-Vn3bWlID2SgCb7KX3d29uSLWv3JDEHJ+QiApnaPuUqqymwHARbQUk+b2h6wfQCz/WqaR0MtmsRwA6OrrLsL3Eg==
dependencies:
"@babel/runtime" "^7.15.4"
"@babel/runtime" "^7.16.3"
"@date-io/date-fns" "^2.11.0"
"@date-io/dayjs" "^2.11.0"
"@date-io/luxon" "^2.11.1"
"@date-io/moment" "^2.11.0"
"@mui/core" "5.0.0-alpha.50"
"@mui/system" "^5.0.3"
"@mui/utils" "^5.0.1"
"@mui/base" "5.0.0-alpha.58"
"@mui/system" "^5.2.2"
"@mui/utils" "^5.2.2"
clsx "^1.1.1"
prop-types "^15.7.2"
react-is "^17.0.2"
react-transition-group "^4.4.2"
rifm "^0.12.0"
rifm "^0.12.1"
"@mui/material@^5.0.0":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.0.3.tgz#5ac4d9f7d9635efc9cf7347373e49db41f1b065b"
integrity sha512-Qj2hwSi63qrYRJuHrUFdN83lCT6HXyLzpCniDTvK7NlqXCnpy4F3Gg0hRJTnO4hNqTTBV+SCOKTSbfuzHl/dnQ==
"@mui/material@^5.2.2":
version "5.2.2"
resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.2.2.tgz#4dfbc9186a83e16a9dcdcc10e4a70ecaf641b1a3"
integrity sha512-vqmZq+v59CT4V84WcvYkYldnjC6uRddYx0TJqgl2h5YRbbPYCGVVywVvg9cBwxy4j5xI3F2WH6z7WGkHqkJIQA==
dependencies:
"@babel/runtime" "^7.15.4"
"@mui/core" "5.0.0-alpha.50"
"@mui/system" "^5.0.3"
"@mui/types" "^7.0.0"
"@mui/utils" "^5.0.1"
"@popperjs/core" "^2.4.4"
"@types/react-transition-group" "^4.4.3"
"@babel/runtime" "^7.16.3"
"@mui/base" "5.0.0-alpha.58"
"@mui/system" "^5.2.2"
"@mui/types" "^7.1.0"
"@mui/utils" "^5.2.2"
"@types/react-transition-group" "^4.4.4"
clsx "^1.1.1"
csstype "^3.0.9"
csstype "^3.0.10"
hoist-non-react-statics "^3.3.2"
prop-types "^15.7.2"
react-is "^17.0.2"
react-transition-group "^4.4.2"
"@mui/private-theming@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.0.1.tgz#50a0ea6ad5a8d1d78072859c4bdaaa6b6584d986"
integrity sha512-R8Cf2+32cG1OXFAqTighA5Mx9R5BQ57cN1ZVaNgfgdbI87Yig2fVMdFSPrw3txcjKlnwsvFJF8AdwQMqq1tJ3Q==
"@mui/private-theming@^5.2.2":
version "5.2.2"
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.2.2.tgz#ede801bc4b6939aedf5900edcece981fde8fa210"
integrity sha512-BfTjZ5ao6KY4Sg11lgaVuQ9uUq8unaM2u9/RKDD12If0B2Vp/AhRSe7i5OTd+wErmK2guTX0kPSraGZzwDEIVg==
dependencies:
"@babel/runtime" "^7.15.4"
"@mui/utils" "^5.0.1"
"@babel/runtime" "^7.16.3"
"@mui/utils" "^5.2.2"
prop-types "^15.7.2"
"@mui/styled-engine@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.0.1.tgz#401e3e0ff846ad1b1e7e097c8050b36d7b68343e"
integrity sha512-j40nCbaKr1HAZYqpX61XvZYsadYskjo3u6+pRFFaewSViAkkD1rjjbubpnh15nqVfYmijtHMZJ9/l1x1hamvfQ==
"@mui/styled-engine@^5.2.0":
version "5.2.0"
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.2.0.tgz#5c97e2b1b6c4c2d9991f07517ed862972d362b85"
integrity sha512-NZ4pWYQcM5wreUfiXRd7IMFRF+Nq1vMzsIdXtXNjgctJTKHunrofasoBqv+cqevO+hqT75ezSbNHyaXzOXp6Mg==
dependencies:
"@babel/runtime" "^7.15.4"
"@emotion/cache" "^11.4.0"
"@babel/runtime" "^7.16.3"
"@emotion/cache" "^11.6.0"
prop-types "^15.7.2"
"@mui/styles@^5.0.0":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.0.1.tgz#1634d08d892b5c7e85c9f84e4fc8bc02a5fb0f7a"
integrity sha512-hCtR2ZVOkoIhpTan02I4UEShnZxe59WwhKRJqauMs/addXByhAHHCNheTdiV++Irl/fyyFObmzPM0CUD3q6FIA==
"@mui/styles@^5.2.2":
version "5.2.2"
resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.2.2.tgz#34c9ba1d6d5a0b9399fbd0c6e85b8f2dcfe54fbd"
integrity sha512-Hfg/3nAU+25RVzpgB0xyE1JjrOCI4rjujrJ0K4tS7M/U4NKZQrmtinc5ldvCaZUlxb51bZGs5V1MGn2dLKZVNQ==
dependencies:
"@babel/runtime" "^7.15.4"
"@babel/runtime" "^7.16.3"
"@emotion/hash" "^0.8.0"
"@mui/private-theming" "^5.0.1"
"@mui/types" "^7.0.0"
"@mui/utils" "^5.0.1"
"@mui/private-theming" "^5.2.2"
"@mui/types" "^7.1.0"
"@mui/utils" "^5.2.2"
clsx "^1.1.1"
csstype "^3.0.9"
csstype "^3.0.10"
hoist-non-react-statics "^3.3.2"
jss "^10.8.0"
jss-plugin-camel-case "^10.8.0"
jss-plugin-default-unit "^10.8.0"
jss-plugin-global "^10.8.0"
jss-plugin-nested "^10.8.0"
jss-plugin-props-sort "^10.8.0"
jss-plugin-rule-value-function "^10.8.0"
jss-plugin-vendor-prefixer "^10.8.0"
jss "^10.8.2"
jss-plugin-camel-case "^10.8.2"
jss-plugin-default-unit "^10.8.2"
jss-plugin-global "^10.8.2"
jss-plugin-nested "^10.8.2"
jss-plugin-props-sort "^10.8.2"
jss-plugin-rule-value-function "^10.8.2"
jss-plugin-vendor-prefixer "^10.8.2"
prop-types "^15.7.2"
"@mui/system@^5.0.3":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.0.3.tgz#6c126be5c2796fe89af93aa0e76d1c3c6dcccf45"
integrity sha512-pNkW9p6Dj/ch3YCKjvrXrBhKsAyJ1/l273Q0hh60hhTKHJXgKBVONQ8r7l1fFHtufI0NNz6UGMVJooIXg4JJtA==
"@mui/system@^5.2.2":
version "5.2.2"
resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.2.2.tgz#81ef74f0c269d18b99a2d0253833d6554bbf5198"
integrity sha512-221tPOcZC8A89GOt6LH9YPTj2Iqf880iqrHd7AHT/HznBKOlLrnWD83pCuLPyX2jeFz4OzhvmGbdt5a74UEgaA==
dependencies:
"@babel/runtime" "^7.15.4"
"@mui/private-theming" "^5.0.1"
"@mui/styled-engine" "^5.0.1"
"@mui/types" "^7.0.0"
"@mui/utils" "^5.0.1"
"@babel/runtime" "^7.16.3"
"@mui/private-theming" "^5.2.2"
"@mui/styled-engine" "^5.2.0"
"@mui/types" "^7.1.0"
"@mui/utils" "^5.2.2"
clsx "^1.1.1"
csstype "^3.0.9"
csstype "^3.0.10"
prop-types "^15.7.2"
"@mui/types@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.0.0.tgz#a7398502bc9c508875aafcbe28aea599b2c3d203"
integrity sha512-M/tkF2pZ4uoPhZ8pnNhlVnOFtz6F3dnYKIsnj8MuXKT6d26IE2u0UjA8B0275ggN74dR9rlHG5xJt5jgDx/Ung==
"@mui/types@^7.1.0":
version "7.1.0"
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.1.0.tgz#5ed928c5a41cfbf9a4be82ea3bbdc47bcc9610d5"
integrity sha512-Hh7ALdq/GjfIwLvqH3XftuY3bcKhupktTm+S6qRIDGOtPtRuq2L21VWzOK4p7kblirK0XgGVH5BLwa6u8z/6QQ==
"@mui/utils@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.0.1.tgz#d4f0f41b82db6ac273920a1b5b6a4de7879271f5"
integrity sha512-GWO104N+o9KG5fKiTEYnAg7kONKEg3vLN+VROAU0f3it6lFGLCVPcQYex/1gJ4QAy96u6Ez8/Hmmhi1+3cX0tQ==
"@mui/utils@^5.2.2":
version "5.2.2"
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.2.2.tgz#972aab7d2564e77c06e0c3c11e7b1aec6e37c927"
integrity sha512-0u9ImUfpCfTxmvQTfUzTSS+jKWMX15MBZeZCRQZ0f7o9Yi8BlrLj33lMx0mFBkUSYdTXnqL4yfOn7RBzV01HMQ==
dependencies:
"@babel/runtime" "^7.15.4"
"@babel/runtime" "^7.16.3"
"@types/prop-types" "^15.7.4"
"@types/react-is" "^16.7.1 || ^17.0.0"
prop-types "^15.7.2"
@@ -3244,10 +3274,10 @@
"@types/history" "*"
"@types/react" "*"
"@types/react-transition-group@^4.4.3":
version "4.4.3"
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.3.tgz#b0994da0a7023d67dbb4a8910a62112bc00d5688"
integrity sha512-fUx5muOWSYP8Bw2BUQ9M9RK9+W1XBK/7FLJ8PTQpnpTEkn0ccyMffyEQvan4C3h53gHdx7KE5Qrxi/LnUGQtdg==
"@types/react-transition-group@^4.4.4":
version "4.4.4"
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.4.tgz#acd4cceaa2be6b757db61ed7b432e103242d163e"
integrity sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==
dependencies:
"@types/react" "*"
@@ -6043,7 +6073,12 @@ cssstyle@^2.3.0:
dependencies:
cssom "~0.3.6"
csstype@^3.0.2, csstype@^3.0.9:
csstype@^3.0.10:
version "3.0.10"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5"
integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==
csstype@^3.0.2:
version "3.0.9"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.9.tgz#6410af31b26bd0520933d02cbc64fce9ce3fbf0b"
integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==
@@ -10136,70 +10171,70 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
jss-plugin-camel-case@^10.8.0:
version "10.8.0"
resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.8.0.tgz#575fd849202d36713a6970796458e375754446c7"
integrity sha512-yxlXrXwcCdGw+H4BC187dEu/RFyW8joMcWfj8Rk9UPgWTKu2Xh7Sib4iW3xXjHe/t5phOHF1rBsHleHykWix7g==
jss-plugin-camel-case@^10.8.2:
version "10.8.2"
resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.8.2.tgz#8d7f915c8115afaff8cbde08faf610ec9892fba6"
integrity sha512-2INyxR+1UdNuKf4v9It3tNfPvf7IPrtkiwzofeKuMd5D58/dxDJVUQYRVg/n460rTlHUfsEQx43hDrcxi9dSPA==
dependencies:
"@babel/runtime" "^7.3.1"
hyphenate-style-name "^1.0.3"
jss "10.8.0"
jss "10.8.2"
jss-plugin-default-unit@^10.8.0:
version "10.8.0"
resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.8.0.tgz#98db5962e62abbf43f1cc111e62cb70ffb09db59"
integrity sha512-9XJV546cY9zV9OvIE/v/dOaxSi4062VfYQQfwbplRExcsU2a79Yn+qDz/4ciw6P4LV1Naq90U+OffAGRHfNq/Q==
jss-plugin-default-unit@^10.8.2:
version "10.8.2"
resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.8.2.tgz#c66f12e02e0815d911b85c02c2a979ee7b4ce69a"
integrity sha512-UZ7cwT9NFYSG+SEy7noRU50s4zifulFdjkUNKE+u6mW7vFP960+RglWjTgMfh79G6OENZmaYnjHV/gcKV4nSxg==
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.8.0"
jss "10.8.2"
jss-plugin-global@^10.8.0:
version "10.8.0"
resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.8.0.tgz#0c2b0c056087f5846d600f3332eeb7a1a8b9c9f2"
integrity sha512-H/8h/bHd4e7P0MpZ9zaUG8NQSB2ie9rWo/vcCP6bHVerbKLGzj+dsY22IY3+/FNRS8zDmUyqdZx3rD8k4nmH4w==
jss-plugin-global@^10.8.2:
version "10.8.2"
resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.8.2.tgz#1a35632a693cf50113bcc5ffe6b51969df79c4ec"
integrity sha512-UaYMSPsYZ7s/ECGoj4KoHC2jwQd5iQ7K+FFGnCAILdQrv7hPmvM2Ydg45ThT/sH46DqktCRV2SqjRuxeBH8nRA==
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.8.0"
jss "10.8.2"
jss-plugin-nested@^10.8.0:
version "10.8.0"
resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.8.0.tgz#7ef9a815e9c9fbede41a8f52ce75cffb4c3b86d5"
integrity sha512-MhmINZkSxyFILcFBuDoZmP1+wj9fik/b9SsjoaggkGjdvMQCES21mj4K5ZnRGVm448gIXyi9j/eZjtDzhaHUYQ==
jss-plugin-nested@^10.8.2:
version "10.8.2"
resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.8.2.tgz#79f3c7f75ea6a36ae72fe52e777035bb24d230c7"
integrity sha512-acRvuPJOb930fuYmhkJaa994EADpt8TxI63Iyg96C8FJ9T2xRyU5T6R1IYKRwUiqZo+2Sr7fdGzRTDD4uBZaMA==
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.8.0"
jss "10.8.2"
tiny-warning "^1.0.2"
jss-plugin-props-sort@^10.8.0:
version "10.8.0"
resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.8.0.tgz#2a83e8ca80d72828495bad57b485f7d55a33543b"
integrity sha512-VY+Wt5WX5GMsXDmd+Ts8+O16fpiCM81svbox++U3LDbJSM/g9FoMx3HPhwUiDfmgHL9jWdqEuvSl/JAk+mh6mQ==
jss-plugin-props-sort@^10.8.2:
version "10.8.2"
resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.8.2.tgz#e25a7471868652c394562b6dc5433dcaea7dff6f"
integrity sha512-wqdcjayKRWBZnNpLUrXvsWqh+5J5YToAQ+8HNBNw0kZxVvCDwzhK2Nx6AKs7p+5/MbAh2PLgNW5Ym/ysbVAuqQ==
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.8.0"
jss "10.8.2"
jss-plugin-rule-value-function@^10.8.0:
version "10.8.0"
resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.8.0.tgz#e011ed180789229e7ea8f75c222d34810bcab520"
integrity sha512-R8N8Ma6Oye1F9HroiUuHhVjpPsVq97uAh+rMI6XwKLqirIu2KFb5x33hPj+vNBMxSHc9jakhf5wG0BbQ7fSDOg==
jss-plugin-rule-value-function@^10.8.2:
version "10.8.2"
resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.8.2.tgz#55354b55f1b2968a15976729968f767f02d64049"
integrity sha512-bW0EKAs+0HXpb6BKJhrn94IDdiWb0CnSluTkh0rGEgyzY/nmD1uV/Wf6KGlesGOZ9gmJzQy+9FFdxIUID1c9Ug==
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.8.0"
jss "10.8.2"
tiny-warning "^1.0.2"
jss-plugin-vendor-prefixer@^10.8.0:
version "10.8.0"
resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.8.0.tgz#024b6d77be50b68e5dfca2c75f68091d8b722d61"
integrity sha512-G1zD0J8dFwKZQ+GaZaay7A/Tg7lhDw0iEkJ/iFFA5UPuvZFpMprCMQttXcTBhLlhhWnyZ8YPn4yqp+amrhQekw==
jss-plugin-vendor-prefixer@^10.8.2:
version "10.8.2"
resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.8.2.tgz#ebb4a482642f34091e454901e21176441dd5f475"
integrity sha512-DeGv18QsSiYLSVIEB2+l0af6OToUe0JB+trpzUxyqD2QRC/5AzzDrCrYffO5AHZ81QbffYvSN/pkfZaTWpRXlg==
dependencies:
"@babel/runtime" "^7.3.1"
css-vendor "^2.0.8"
jss "10.8.0"
jss "10.8.2"
jss@10.8.0, jss@^10.8.0:
version "10.8.0"
resolved "https://registry.yarnpkg.com/jss/-/jss-10.8.0.tgz#5063ee73aabd9f228ea3849df7962f0d2e213a42"
integrity sha512-6fAMLJrVQ8epM5ghghxWqCwRR0ZamP2cKbOAtzPudcCMSNdAqtvmzQvljUZYR8OXJIeb/IpZeOXA1sDXms4R1w==
jss@10.8.2, jss@^10.8.2:
version "10.8.2"
resolved "https://registry.yarnpkg.com/jss/-/jss-10.8.2.tgz#4b2a30b094b924629a64928236017a52c7c97505"
integrity sha512-FkoUNxI329CKQ9OQC8L72MBF9KPf5q8mIupAJ5twU7G7XREW7ahb+7jFfrjZ4iy1qvhx1HwIWUIvkZBDnKkEdQ==
dependencies:
"@babel/runtime" "^7.3.1"
csstype "^3.0.2"
@@ -14287,10 +14322,10 @@ rgba-regex@^1.0.0:
resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
rifm@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/rifm/-/rifm-0.12.0.tgz#8d3a9dc0de9c190e0de9bdc8861a91a221dc1341"
integrity sha512-PqOl+Mo2lyqrKiD34FPlnQ+ksD3F+a62TQlphiZshgriyHdfjn6jGyqUZhd+s3nsMYXwXYDdjrrv8wX7QsOG3g==
rifm@^0.12.1:
version "0.12.1"
resolved "https://registry.yarnpkg.com/rifm/-/rifm-0.12.1.tgz#8fa77f45b7f1cda2a0068787ac821f0593967ac4"
integrity sha512-OGA1Bitg/dSJtI/c4dh90svzaUPt228kzFsUkJbtA2c964IqEAwWXeL9ZJi86xWv3j5SMqRvGULl7bA6cK0Bvg==
rimraf@2, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3:
version "2.7.1"
@@ -15319,7 +15354,7 @@ stylehacks@^4.0.0:
postcss "^7.0.0"
postcss-selector-parser "^3.0.0"
stylis@^4.0.3:
stylis@^4.0.10, stylis@^4.0.3:
version "4.0.10"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240"
integrity sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==