import * as React from 'react'; import { useQuery } from '@apollo/client'; import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import Collapse from '@mui/material/Collapse'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemButton from '@mui/material/ListItemButton'; import TextField from '@mui/material/TextField'; import Autocomplete from '@mui/material/Autocomplete'; import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; import IconButton from '@mui/material/IconButton'; import ChooseAccountSubstep from 'components/ChooseAccountSubstep'; import Form from 'components/Form'; import InputCreator from 'components/InputCreator'; import FlowStepContextMenu from 'components/FlowStepContextMenu'; import AppIcon from 'components/AppIcon'; import { GET_APPS } from 'graphql/queries/get-apps'; import useFormatMessage from 'hooks/useFormatMessage'; import type { App, AppFields } from 'types/app'; import type { Step } from 'types/step'; import { StepType } from 'types/step'; import { Content, Header, Wrapper } from './style'; type FlowStepProps = { collapsed?: boolean; step: Step; index?: number; onOpen?: () => void; onClose?: () => void; onChange?: (step: Step) => void; } const optionGenerator = (app: Record): { label: string; value: string; } => ({ label: app.name as string, value: app.key as string, }); const getOption = (options: Record[], appKey: unknown) => options.find(app => app.value === appKey as string) || null; const parseStep = (step: any) => { try { const parameters = JSON.parse(step.parameters); return { ...step, parameters, } } catch (err) { // highly likely that step does not have any parameters and thus, the error is thrown return step; } }; export default function FlowStep(props: FlowStepProps): React.ReactElement | null { const { collapsed, index, onChange } = props; const contextButtonRef = React.useRef(null); const [step, setStep] = React.useState(() => parseStep(props.step)); const [anchorEl, setAnchorEl] = React.useState(null); const isTrigger = step.type === StepType.Trigger; const isAction = step.type === StepType.Action; const initialRender = React.useRef(true); const formatMessage = useFormatMessage(); const [currentSubstep, setCurrentSubstep] = React.useState(0); const { data } = useQuery(GET_APPS, { variables: { onlyWithTriggers: isTrigger }}); const apps: App[] = data?.getApps; const app = apps?.find((currentApp: App) => currentApp.key === step.appKey); // emit the step change to the parent component React.useEffect(() => { if (!initialRender.current) { onChange?.(step); } else { initialRender.current = false; } }, [step, onChange]); const appOptions = React.useMemo(() => apps?.map((app) => optionGenerator(app)), [apps]); const actionsOrTriggers = isTrigger ? app?.triggers : app?.actions; const actionOptions = React.useMemo(() => actionsOrTriggers?.map((trigger) => optionGenerator(trigger)) ?? [], [app?.key]); const substeps = React.useMemo(() => actionsOrTriggers?.find(({ key }) => key === step.key)?.subSteps, [actionsOrTriggers, step?.key]); const onAppChange = React.useCallback((event: React.SyntheticEvent, selectedOption: unknown) => { if (typeof selectedOption === 'object') { const typedSelectedOption = selectedOption as { value: string; }; const option: { value: string } = typedSelectedOption; const appKey = option?.value as string; setStep((step) => ({ ...step, appKey, parameters: {} })); } }, []); const onEventChange = React.useCallback((event: React.SyntheticEvent, selectedOption: unknown) => { if (typeof selectedOption === 'object') { const typedSelectedOption = selectedOption as { value: string; }; const option: { value: string } = typedSelectedOption; const eventKey = option?.value as string; setStep((step) => ({ ...step, key: eventKey, parameters: {}, })); } }, []); const onAccountChange = React.useCallback((connectionId: string) => { setStep((step) => ({ ...step, connection: { id: connectionId, }, })); }, []); const onParameterChange = React.useCallback((event: React.SyntheticEvent) => { const { name, value } = event.target as HTMLInputElement; setStep((step) => ({ ...step, parameters: { ...step.parameters, [name]: value } })); }, []); 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 onClose = () => props.onClose?.(); const toggleSubstep = (substepIndex: number) => setCurrentSubstep((value) => value !== substepIndex ? substepIndex : null); return (
{ isTrigger ? formatMessage('flowStep.triggerType') : formatMessage('flowStep.actionType') } {index}. {app?.name}
{/* as there are no other actions besides "delete step", we hide the context menu. */} {!isTrigger && }
toggleSubstep(0)} selected={currentSubstep === 0} divider> Choose app & event } value={getOption(appOptions, step.appKey)} onChange={onAppChange} /> {step.appKey && ( Action event } value={getOption(actionOptions, step.key)} onChange={onEventChange} /> )}
{substeps?.length > 0 && substeps.map((substep: { name: string, arguments: AppFields[] }, index: number) => ( toggleSubstep(index + 1)} selected={currentSubstep === (index + 1)} divider> {substep.name as string} {substep.name === 'Choose account' && ( )} {substep.name !== 'Choose account' && ( {substep?.arguments?.map((argument: AppFields) => ( ))} )} ))}
{anchorEl && }
) };