feat: add delete step functionality
This commit is contained in:

committed by
Ömer Faruk Aydın

parent
0b6eecd41d
commit
90aac874bf
@@ -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>
|
||||||
|
@@ -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>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
41
packages/web/src/components/FlowStepContextMenu/index.tsx
Normal file
41
packages/web/src/components/FlowStepContextMenu/index.tsx
Normal 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;
|
@@ -7,6 +7,7 @@ export const CREATE_STEP = gql`
|
|||||||
type
|
type
|
||||||
key
|
key
|
||||||
appKey
|
appKey
|
||||||
|
parameters
|
||||||
connection {
|
connection {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
15
packages/web/src/graphql/mutations/delete-step.ts
Normal file
15
packages/web/src/graphql/mutations/delete-step.ts
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
@@ -8,7 +8,6 @@ export const GET_APP = gql`
|
|||||||
iconUrl
|
iconUrl
|
||||||
docUrl
|
docUrl
|
||||||
primaryColor
|
primaryColor
|
||||||
connectionCount
|
|
||||||
fields {
|
fields {
|
||||||
key
|
key
|
||||||
label
|
label
|
||||||
|
Reference in New Issue
Block a user