feat: add delete step functionality

This commit is contained in:
Ali BARIN
2022-01-30 16:22:30 +01:00
committed by Ömer Faruk Aydın
parent 0b6eecd41d
commit 90aac874bf
6 changed files with 88 additions and 6 deletions

View File

@@ -1,6 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { useMutation } from '@apollo/client'; import { useMutation } from '@apollo/client';
import { ApolloCache, FetchResult } from '@apollo/client';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import IconButton from '@mui/material/IconButton'; import IconButton from '@mui/material/IconButton';
import AddIcon from '@mui/icons-material/Add'; import AddIcon from '@mui/icons-material/Add';
@@ -10,7 +9,6 @@ import { CREATE_STEP } from 'graphql/mutations/create-step';
import { UPDATE_STEP } from 'graphql/mutations/update-step'; import { UPDATE_STEP } from 'graphql/mutations/update-step';
import FlowStep from 'components/FlowStep'; import FlowStep from 'components/FlowStep';
import type { Flow } from 'types/flow'; import type { Flow } from 'types/flow';
import type { Step } from 'types/step';
type EditorProps = { type EditorProps = {
flow: Flow; flow: Flow;
@@ -42,7 +40,7 @@ function updateHandlerFactory(flowId: string, previousStepId: string) {
export default function Editor(props: EditorProps): React.ReactElement { export default function Editor(props: EditorProps): React.ReactElement {
const [updateStep] = useMutation(UPDATE_STEP); const [updateStep] = useMutation(UPDATE_STEP);
const [createStep] = useMutation(CREATE_STEP); const [createStep, { loading: creationInProgress }] = useMutation(CREATE_STEP);
const [currentStep, setCurrentStep] = React.useState<number | null>(0); const [currentStep, setCurrentStep] = React.useState<number | null>(0);
const { flow } = props; const { flow } = props;
@@ -96,7 +94,7 @@ export default function Editor(props: EditorProps): React.ReactElement {
alignItems="center" alignItems="center"
alignSelf="center" alignSelf="center"
py={3} py={3}
gap={2} gap={1}
> >
{flow?.steps?.map((step, index) => ( {flow?.steps?.map((step, index) => (
<React.Fragment key={`${step}-${index}`}> <React.Fragment key={`${step}-${index}`}>
@@ -110,7 +108,7 @@ export default function Editor(props: EditorProps): React.ReactElement {
onChange={onStepChange} onChange={onStepChange}
/> />
<IconButton onClick={() => addStep(step.id)} color="primary"> <IconButton onClick={() => addStep(step.id)} color="primary" disabled={creationInProgress}>
<AddIcon /> <AddIcon />
</IconButton> </IconButton>
</React.Fragment> </React.Fragment>

View File

@@ -2,6 +2,7 @@ import * as React from 'react';
import { useQuery } from '@apollo/client'; import { useQuery } from '@apollo/client';
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
import Collapse from '@mui/material/Collapse'; import Collapse from '@mui/material/Collapse';
import List from '@mui/material/List'; import List from '@mui/material/List';
@@ -9,7 +10,10 @@ import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton'; import ListItemButton from '@mui/material/ListItemButton';
import TextField from '@mui/material/TextField'; import TextField from '@mui/material/TextField';
import Autocomplete from '@mui/material/Autocomplete'; import Autocomplete from '@mui/material/Autocomplete';
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
import IconButton from '@mui/material/IconButton';
import FlowStepContextMenu from 'components/FlowStepContextMenu';
import AppIcon from 'components/AppIcon'; import AppIcon from 'components/AppIcon';
import { GET_APPS } from 'graphql/queries/get-apps'; import { GET_APPS } from 'graphql/queries/get-apps';
import useFormatMessage from 'hooks/useFormatMessage'; import useFormatMessage from 'hooks/useFormatMessage';
@@ -49,7 +53,9 @@ const parseStep = (step: any) => {
export default function FlowStep(props: FlowStepProps): React.ReactElement | null { export default function FlowStep(props: FlowStepProps): React.ReactElement | null {
const { collapsed, index, onChange } = props; const { collapsed, index, onChange } = props;
const contextButtonRef = React.useRef<HTMLButtonElement | null>(null);
const [step, setStep] = React.useState<Step>(() => parseStep(props.step)); const [step, setStep] = React.useState<Step>(() => parseStep(props.step));
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null);
const isTrigger = step.type === StepType.Trigger; const isTrigger = step.type === StepType.Trigger;
const isAction = step.type === StepType.Action; const isAction = step.type === StepType.Action;
const initialRender = React.useRef<boolean>(true); const initialRender = React.useRef<boolean>(true);
@@ -97,6 +103,14 @@ export default function FlowStep(props: FlowStepProps): React.ReactElement | nul
if (!apps) return null; if (!apps) return null;
const onContextMenuClose = (event: React.SyntheticEvent) => {
event.stopPropagation();
setAnchorEl(null);
}
const onContextMenuClick = (event: React.SyntheticEvent) => {
event.stopPropagation();
setAnchorEl(contextButtonRef.current);
}
const onOpen = () => collapsed && props.onOpen?.(); const onOpen = () => collapsed && props.onOpen?.();
const onClose = () => props.onClose?.(); const onClose = () => props.onClose?.();
@@ -121,6 +135,13 @@ export default function FlowStep(props: FlowStepProps): React.ReactElement | nul
{index}. {app?.name} {index}. {app?.name}
</Typography> </Typography>
</div> </div>
<Box display="flex" flex={1} justifyContent="end">
{/* as there are no other actions besides "delete step", we hide the context menu. */}
{!isTrigger && <IconButton color="primary" onClick={onContextMenuClick} ref={contextButtonRef}>
<MoreHorizIcon />
</IconButton>}
</Box>
</Stack> </Stack>
</Header> </Header>
@@ -170,6 +191,13 @@ export default function FlowStep(props: FlowStepProps): React.ReactElement | nul
</Button> </Button>
</Collapse> </Collapse>
)} )}
{anchorEl && <FlowStepContextMenu
stepId={step.id}
deletable={!isTrigger}
onClose={onContextMenuClose}
anchorEl={anchorEl}
/>}
</Wrapper> </Wrapper>
) )
}; };

View File

@@ -0,0 +1,41 @@
import * as React from 'react';
import { useMutation } from '@apollo/client';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import type { PopoverProps } from '@mui/material/Popover';
import { DELETE_STEP } from 'graphql/mutations/delete-step';
import useFormatMessage from 'hooks/useFormatMessage';
type FlowStepContextMenuProps = {
stepId: string;
onClose: PopoverProps['onClose'];
anchorEl: HTMLButtonElement;
deletable: boolean;
};
function FlowStepContextMenu(props: FlowStepContextMenuProps): React.ReactElement {
const { stepId, onClose, anchorEl, deletable } = props;
const [deleteStep] = useMutation(DELETE_STEP);
const formatMessage = useFormatMessage();
const deleteActionHandler = React.useCallback(async (event: React.SyntheticEvent) => {
event.stopPropagation();
await deleteStep({ variables: { id: stepId }});
}, [stepId]);
return (
<Menu
open={true}
onClose={onClose}
hideBackdrop={false}
anchorEl={anchorEl}
>
{deletable && <MenuItem onClick={deleteActionHandler}>
{formatMessage('connection.delete')}
</MenuItem>}
</Menu>
);
};
export default FlowStepContextMenu;

View File

@@ -7,6 +7,7 @@ export const CREATE_STEP = gql`
type type
key key
appKey appKey
parameters
connection { connection {
id id
} }

View File

@@ -0,0 +1,15 @@
import { gql } from '@apollo/client';
export const DELETE_STEP = gql`
mutation DeleteStep($id: String!) {
deleteStep(id: $id) {
id
flow {
id
steps {
id
}
}
}
}
`;

View File

@@ -8,7 +8,6 @@ export const GET_APP = gql`
iconUrl iconUrl
docUrl docUrl
primaryColor primaryColor
connectionCount
fields { fields {
key key
label label