feat: make step in editor configurable
This commit is contained in:

committed by
Ömer Faruk Aydın

parent
4985fb422e
commit
95a63affe7
@@ -1,52 +1,54 @@
|
|||||||
import { GraphQLString, GraphQLNonNull, GraphQLInt, GraphQLEnumType } from 'graphql';
|
import { GraphQLNonNull } from 'graphql';
|
||||||
import Step from '../../models/step';
|
import Step from '../../models/step';
|
||||||
import Flow from '../../models/flow';
|
import Flow from '../../models/flow';
|
||||||
import stepType from '../types/step';
|
import stepType, { stepInputType } from '../types/step';
|
||||||
import RequestWithCurrentUser from '../../types/express/request-with-current-user';
|
import RequestWithCurrentUser from '../../types/express/request-with-current-user';
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
flowId: number,
|
input: {
|
||||||
key: string,
|
key: string;
|
||||||
appKey: string,
|
appKey: string;
|
||||||
type: string
|
flow: {
|
||||||
connectionId: number
|
id: number;
|
||||||
}
|
};
|
||||||
|
connection: {
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const createStepResolver = async (params: Params, req: RequestWithCurrentUser) => {
|
const createStepResolver = async (
|
||||||
const flow = await Flow.query().findOne({
|
params: Params,
|
||||||
id: params.flowId,
|
req: RequestWithCurrentUser
|
||||||
user_id: req.currentUser.id
|
) => {
|
||||||
}).throwIfNotFound();
|
const { input } = params;
|
||||||
|
|
||||||
|
const flow = await Flow.query()
|
||||||
|
.findOne({
|
||||||
|
id: input.flow.id,
|
||||||
|
user_id: req.currentUser.id,
|
||||||
|
})
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
const step = await Step.query().insertAndFetch({
|
const step = await Step.query().insertAndFetch({
|
||||||
flowId: flow.id,
|
flowId: flow.id,
|
||||||
key: params.key,
|
key: input.key,
|
||||||
appKey: params.appKey,
|
appKey: input.appKey,
|
||||||
type: params.type,
|
type: 'action',
|
||||||
connectionId: params.connectionId,
|
connectionId: input.connection?.id,
|
||||||
|
position: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
return step;
|
return step;
|
||||||
}
|
};
|
||||||
|
|
||||||
const createStep = {
|
const createStep = {
|
||||||
type: stepType,
|
type: stepType,
|
||||||
args: {
|
args: {
|
||||||
flowId: { type: GraphQLNonNull(GraphQLInt) },
|
input: { type: new GraphQLNonNull(stepInputType) },
|
||||||
key: { type: GraphQLNonNull(GraphQLString) },
|
|
||||||
appKey: { type: GraphQLNonNull(GraphQLString) },
|
|
||||||
type: {
|
|
||||||
type: new GraphQLEnumType({
|
|
||||||
name: 'StepInputEnumType',
|
|
||||||
values: {
|
|
||||||
trigger: { value: 'trigger' },
|
|
||||||
action: { value: 'action' },
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
connectionId: { type: GraphQLNonNull(GraphQLInt) }
|
|
||||||
},
|
},
|
||||||
resolve: (_: any, params: Params, req: RequestWithCurrentUser) => createStepResolver(params, req)
|
resolve: (_: any, params: Params, req: RequestWithCurrentUser) =>
|
||||||
|
createStepResolver(params, req),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default createStep;
|
export default createStep;
|
||||||
|
@@ -1,34 +1,41 @@
|
|||||||
import { GraphQLInt, GraphQLString, GraphQLNonNull } from 'graphql';
|
import { GraphQLInt, GraphQLString, GraphQLNonNull } from 'graphql';
|
||||||
import Flow from '../../models/flow';
|
import Flow from '../../models/flow';
|
||||||
import Step from '../../models/step';
|
import Step from '../../models/step';
|
||||||
import stepType from '../types/step';
|
import stepType, { stepInputType } from '../types/step';
|
||||||
import availableAppsEnumType from '../types/available-apps-enum-type';
|
import availableAppsEnumType from '../types/available-apps-enum-type';
|
||||||
import RequestWithCurrentUser from '../../types/express/request-with-current-user';
|
import RequestWithCurrentUser from '../../types/express/request-with-current-user';
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
id: number,
|
input: {
|
||||||
flowId: number,
|
id: number,
|
||||||
key: string,
|
key: string,
|
||||||
appKey: string,
|
appKey: string,
|
||||||
connectionId: number
|
flow: {
|
||||||
|
id: number,
|
||||||
|
},
|
||||||
|
connection: {
|
||||||
|
id: number
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const updateStepResolver = async (params: Params, req: RequestWithCurrentUser) => {
|
const updateStepResolver = async (params: Params, req: RequestWithCurrentUser) => {
|
||||||
|
const { input } = params;
|
||||||
|
|
||||||
const flow = await Flow.query().findOne({
|
const flow = await Flow.query().findOne({
|
||||||
user_id: req.currentUser.id,
|
user_id: req.currentUser.id,
|
||||||
id: params.flowId
|
id: input.flow.id
|
||||||
}).throwIfNotFound();
|
}).throwIfNotFound();
|
||||||
|
|
||||||
let step = await Step.query().findOne({
|
let step = await Step.query().findOne({
|
||||||
flow_id: flow.id,
|
flow_id: flow.id,
|
||||||
id: params.id
|
id: input.id
|
||||||
}).throwIfNotFound();
|
}).throwIfNotFound();
|
||||||
|
|
||||||
step = await step.$query().patchAndFetch({
|
step = await Step.query().patchAndFetchById(input.id, {
|
||||||
...step,
|
key: input.key,
|
||||||
key: params.key,
|
appKey: input.appKey,
|
||||||
appKey: params.appKey,
|
connectionId: input.connection.id,
|
||||||
connectionId: params.connectionId
|
});
|
||||||
})
|
|
||||||
|
|
||||||
return step;
|
return step;
|
||||||
}
|
}
|
||||||
@@ -36,10 +43,7 @@ const updateStepResolver = async (params: Params, req: RequestWithCurrentUser) =
|
|||||||
const updateStep = {
|
const updateStep = {
|
||||||
type: stepType,
|
type: stepType,
|
||||||
args: {
|
args: {
|
||||||
id: { type: GraphQLNonNull(GraphQLInt) },
|
input: { type: new GraphQLNonNull(stepInputType) }
|
||||||
flowId: { type: GraphQLNonNull(GraphQLInt) },
|
|
||||||
key: { type: GraphQLString },
|
|
||||||
appKey: { type: availableAppsEnumType },
|
|
||||||
},
|
},
|
||||||
resolve: (_: any, params: Params, req: RequestWithCurrentUser) => updateStepResolver(params, req)
|
resolve: (_: any, params: Params, req: RequestWithCurrentUser) => updateStepResolver(params, req)
|
||||||
};
|
};
|
||||||
|
@@ -3,6 +3,7 @@ import {
|
|||||||
GraphQLString,
|
GraphQLString,
|
||||||
GraphQLEnumType,
|
GraphQLEnumType,
|
||||||
GraphQLInt,
|
GraphQLInt,
|
||||||
|
GraphQLInputObjectType,
|
||||||
} from 'graphql';
|
} from 'graphql';
|
||||||
import ConnectionType from './connection';
|
import ConnectionType from './connection';
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ const stepType = new GraphQLObjectType({
|
|||||||
name: 'Step',
|
name: 'Step',
|
||||||
fields: {
|
fields: {
|
||||||
id: { type: GraphQLInt },
|
id: { type: GraphQLInt },
|
||||||
|
previousStepId: { type: GraphQLInt },
|
||||||
key: { type: GraphQLString },
|
key: { type: GraphQLString },
|
||||||
appKey: { type: GraphQLString },
|
appKey: { type: GraphQLString },
|
||||||
type: {
|
type: {
|
||||||
@@ -26,4 +28,30 @@ const stepType = new GraphQLObjectType({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const stepInputType = new GraphQLInputObjectType({
|
||||||
|
name: 'StepInput',
|
||||||
|
fields: {
|
||||||
|
id: { type: GraphQLInt },
|
||||||
|
previousStepId: { type: GraphQLInt },
|
||||||
|
key: { type: GraphQLString },
|
||||||
|
appKey: { type: GraphQLString },
|
||||||
|
connection: {
|
||||||
|
type: new GraphQLInputObjectType({
|
||||||
|
name: 'StepConnectionInput',
|
||||||
|
fields: {
|
||||||
|
id: { type: GraphQLInt },
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
flow: {
|
||||||
|
type: new GraphQLInputObjectType({
|
||||||
|
name: 'StepFlowInput',
|
||||||
|
fields: {
|
||||||
|
id: { type: GraphQLInt },
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
export default stepType;
|
export default stepType;
|
||||||
|
@@ -26,7 +26,7 @@ class Step extends Base {
|
|||||||
properties: {
|
properties: {
|
||||||
id: { type: 'integer' },
|
id: { type: 'integer' },
|
||||||
flowId: { type: 'integer' },
|
flowId: { type: 'integer' },
|
||||||
key: { type: 'string', minLength: 1, maxLength: 255 },
|
key: { type: ['string', null] },
|
||||||
appKey: { type: 'string', minLength: 1, maxLength: 255 },
|
appKey: { type: 'string', minLength: 1, maxLength: 255 },
|
||||||
type: { type: 'string', enum: ['action', 'trigger'] },
|
type: { type: 'string', enum: ['action', 'trigger'] },
|
||||||
connectionId: { type: 'integer' },
|
connectionId: { type: 'integer' },
|
||||||
|
@@ -28,7 +28,7 @@
|
|||||||
"react-hook-form": "^7.17.2",
|
"react-hook-form": "^7.17.2",
|
||||||
"react-intl": "^5.20.12",
|
"react-intl": "^5.20.12",
|
||||||
"react-router-dom": "^6.0.2",
|
"react-router-dom": "^6.0.2",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "5.0.0",
|
||||||
"typescript": "^4.1.2",
|
"typescript": "^4.1.2",
|
||||||
"web-vitals": "^1.0.1"
|
"web-vitals": "^1.0.1"
|
||||||
},
|
},
|
||||||
|
@@ -3,8 +3,8 @@ import Avatar from '@mui/material/Avatar';
|
|||||||
import type { AvatarProps } from '@mui/material/Avatar';
|
import type { AvatarProps } from '@mui/material/Avatar';
|
||||||
|
|
||||||
type AppIconProps = {
|
type AppIconProps = {
|
||||||
name: string;
|
name?: string;
|
||||||
url: string;
|
url?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,17 +1,85 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
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 AddIcon from '@mui/icons-material/Add';
|
||||||
|
|
||||||
|
import { GET_FLOW } from 'graphql/queries/get-flow';
|
||||||
|
import { CREATE_STEP } from 'graphql/mutations/create-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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateHandlerFactory(flowId: number, previousStepId: number) {
|
||||||
|
return function createStepUpdateHandler(
|
||||||
|
cache: any,
|
||||||
|
mutationResult: any,
|
||||||
|
) {
|
||||||
|
const { data } = mutationResult;
|
||||||
|
const { createStep: createdStep } = data;
|
||||||
|
const { getFlow: flow } = cache.readQuery({
|
||||||
|
query: GET_FLOW,
|
||||||
|
variables: { id: flowId },
|
||||||
|
});
|
||||||
|
const steps = flow.steps.reduce((steps: any[], currentStep: any) => {
|
||||||
|
if (currentStep.id === previousStepId) {
|
||||||
|
return [...steps, currentStep, createdStep];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...steps, currentStep];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
cache.writeQuery({
|
||||||
|
query: GET_FLOW,
|
||||||
|
variables: { id: flowId },
|
||||||
|
data: { getFlow: { ...flow, steps } },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default function Editor(props: EditorProps): React.ReactElement {
|
export default function Editor(props: EditorProps): React.ReactElement {
|
||||||
const [currentStep, setCurrentStep] = React.useState<number | null>(null);
|
const [updateStep] = useMutation(UPDATE_STEP);
|
||||||
|
const [createStep] = useMutation(CREATE_STEP);
|
||||||
|
const [currentStep, setCurrentStep] = React.useState<number | null>(0);
|
||||||
const { flow } = props;
|
const { flow } = props;
|
||||||
|
|
||||||
|
const onStepChange = React.useCallback((step: any) => {
|
||||||
|
const mutationInput = {
|
||||||
|
id: step.id,
|
||||||
|
key: step.key,
|
||||||
|
appKey: step.appKey,
|
||||||
|
previousStepId: step.previousStepId,
|
||||||
|
connection: {
|
||||||
|
id: step.connection?.id
|
||||||
|
},
|
||||||
|
flow: {
|
||||||
|
id: flow.id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
updateStep({ variables: { input: mutationInput } });
|
||||||
|
}, [updateStep, flow.id]);
|
||||||
|
|
||||||
|
const addStep = React.useCallback((previousStepId) => {
|
||||||
|
const mutationInput = {
|
||||||
|
previousStepId: previousStepId,
|
||||||
|
flow: {
|
||||||
|
id: flow.id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
createStep({
|
||||||
|
variables: { input: mutationInput },
|
||||||
|
update: updateHandlerFactory(flow.id, previousStepId),
|
||||||
|
});
|
||||||
|
}, [createStep, flow.id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
@@ -23,15 +91,22 @@ export default function Editor(props: EditorProps): React.ReactElement {
|
|||||||
gap={2}
|
gap={2}
|
||||||
>
|
>
|
||||||
{flow?.steps?.map((step, index) => (
|
{flow?.steps?.map((step, index) => (
|
||||||
<FlowStep
|
<React.Fragment key={`${step}-${index}`}>
|
||||||
key={step.id}
|
<FlowStep
|
||||||
step={step}
|
key={step.id}
|
||||||
index={index + 1}
|
step={step}
|
||||||
collapsed={currentStep !== index}
|
index={index + 1}
|
||||||
onOpen={() => setCurrentStep(index)}
|
collapsed={currentStep !== index}
|
||||||
onClose={() => setCurrentStep(null)}
|
onOpen={() => setCurrentStep(index)}
|
||||||
/>
|
onClose={() => setCurrentStep(null)}
|
||||||
|
onChange={onStepChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<IconButton onClick={() => addStep(step.id)} color="primary">
|
||||||
|
<AddIcon />
|
||||||
|
</IconButton>
|
||||||
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@@ -38,9 +38,9 @@ export default function EditorLayout(): React.ReactElement {
|
|||||||
<Box pr={1}>
|
<Box pr={1}>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={
|
control={
|
||||||
<Switch checked={false} />
|
<Switch checked={flow?.active} />
|
||||||
}
|
}
|
||||||
label={formatMessage('flow.inactive')}
|
label={flow?.active ? formatMessage('flow.active') : formatMessage('flow.inactive')}
|
||||||
labelPlacement="start"
|
labelPlacement="start"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
@@ -17,7 +17,7 @@ export default function FlowRow(props: FlowRowProps): React.ReactElement {
|
|||||||
const { flow } = props;
|
const { flow } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={URLS.FLOW(flow.id)}>
|
<Link to={URLS.FLOW(flow.id.toString())}>
|
||||||
<Card sx={{ mb: 1 }}>
|
<Card sx={{ mb: 1 }}>
|
||||||
<CardActionArea>
|
<CardActionArea>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
|
@@ -3,13 +3,20 @@ 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 Button from '@mui/material/Button';
|
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 AppIcon from 'components/AppIcon';
|
import AppIcon from 'components/AppIcon';
|
||||||
import { GET_APP } from 'graphql/queries/get-app';
|
import { GET_APPS } from 'graphql/queries/get-apps';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
import type { App } from 'types/app';
|
||||||
import type { Step } from 'types/step';
|
import type { Step } from 'types/step';
|
||||||
import { StepType } from 'types/step';
|
import { StepType } from 'types/step';
|
||||||
import { Header, Wrapper } from './style';
|
import { Content, Header, Wrapper } from './style';
|
||||||
|
|
||||||
type FlowStepProps = {
|
type FlowStepProps = {
|
||||||
collapsed?: boolean;
|
collapsed?: boolean;
|
||||||
@@ -17,41 +24,99 @@ type FlowStepProps = {
|
|||||||
index?: number;
|
index?: number;
|
||||||
onOpen?: () => void;
|
onOpen?: () => void;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
|
onChange?: (step: Step) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FlowStep(props: FlowStepProps): React.ReactElement | null {
|
const optionGenerator = (app: App): { label: string; value: string; } => ({
|
||||||
const { collapsed, index, step } = props;
|
label: app.name,
|
||||||
const formatMessage = useFormatMessage();
|
value: app.key,
|
||||||
const { data } = useQuery(GET_APP, { variables: { key: step?.appKey }})
|
});
|
||||||
const app = data?.getApp;
|
|
||||||
|
|
||||||
if (!app) return null;
|
const getOption = (options: Record<string, unknown>[], appKey: string) => options.find(app => app.value === appKey);
|
||||||
|
|
||||||
|
export default function FlowStep(props: FlowStepProps): React.ReactElement | null {
|
||||||
|
const { collapsed, index, onChange } = props;
|
||||||
|
const [step, setStep] = React.useState<Step>(props.step);
|
||||||
|
const formatMessage = useFormatMessage();
|
||||||
|
const [currentSubstep, setCurrentSubstep] = React.useState<number | null>(0);
|
||||||
|
const { data } = useQuery(GET_APPS)
|
||||||
|
const apps: App[] = data?.getApps;
|
||||||
|
|
||||||
|
// emit the step change to the parent component
|
||||||
|
React.useEffect(() => {
|
||||||
|
onChange?.(step);
|
||||||
|
}, [step, onChange]);
|
||||||
|
|
||||||
|
const appAndEventOptions = React.useMemo(() => apps?.map(optionGenerator), [apps]);
|
||||||
|
|
||||||
|
if (!apps) return null;
|
||||||
|
|
||||||
|
const app = apps.find((currentApp: App) => currentApp.key === step.appKey);
|
||||||
|
|
||||||
const onOpen = () => collapsed && props.onOpen?.();
|
const onOpen = () => collapsed && props.onOpen?.();
|
||||||
const onClose = () => props.onClose?.();
|
const onClose = () => props.onClose?.();
|
||||||
|
|
||||||
|
const toggleSubstep = (substepIndex: number) => setCurrentSubstep((value) => value !== substepIndex ? substepIndex : null);
|
||||||
|
|
||||||
|
const onAppAndEventChange = (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;
|
||||||
|
const updatedStep = { ...step, appKey };
|
||||||
|
setStep(updatedStep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper elevation={collapsed ? 1 : 4} onClick={onOpen}>
|
<Wrapper elevation={collapsed ? 1 : 4} onClick={onOpen}>
|
||||||
<Header borderBottom={!collapsed}>
|
<Header collapsed={collapsed}>
|
||||||
<Stack direction="row" alignItems="center" gap={2}>
|
<Stack direction="row" alignItems="center" gap={2}>
|
||||||
<AppIcon url={app.iconUrl} name={app.name} />
|
<AppIcon url={app?.iconUrl} name={app?.name} />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Typography variant="caption">
|
<Typography variant="caption">
|
||||||
{step.type === StepType.Trigger ? formatMessage('flowStep.triggerType') : formatMessage('flowStep.actionType')}
|
{
|
||||||
|
step.type === StepType.Trigger ?
|
||||||
|
formatMessage('flowStep.triggerType') :
|
||||||
|
formatMessage('flowStep.actionType')
|
||||||
|
}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
{index}. {app.name}
|
{index}. {app?.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Header>
|
</Header>
|
||||||
|
|
||||||
{!collapsed && (
|
{true && (
|
||||||
<Button onClick={onClose}>
|
<Collapse in={!collapsed}>
|
||||||
Close
|
<Content>
|
||||||
</Button>
|
<List>
|
||||||
|
<ListItemButton onClick={() => toggleSubstep(0)}>
|
||||||
|
Choose app & event
|
||||||
|
</ListItemButton>
|
||||||
|
<Collapse in={currentSubstep === 0} timeout="auto" unmountOnExit>
|
||||||
|
<ListItem sx={{ pt: 2 }}>
|
||||||
|
<Autocomplete
|
||||||
|
disablePortal
|
||||||
|
id="combo-box-demo"
|
||||||
|
options={appAndEventOptions}
|
||||||
|
sx={{ width: 300 }}
|
||||||
|
renderInput={(params) => <TextField {...params} label="Choose app & event" />}
|
||||||
|
value={getOption(appAndEventOptions, step.appKey)}
|
||||||
|
onChange={onAppAndEventChange}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
</Collapse>
|
||||||
|
</List>
|
||||||
|
</Content>
|
||||||
|
|
||||||
|
<Button onClick={onClose}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</Collapse>
|
||||||
)}
|
)}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
)
|
)
|
||||||
|
@@ -3,14 +3,21 @@ import Card from '@mui/material/Card';
|
|||||||
|
|
||||||
export const Wrapper = styled(Card)`
|
export const Wrapper = styled(Card)`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
overflow: unset;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type HeaderProps = {
|
type HeaderProps = {
|
||||||
borderBottom?: boolean;
|
collapsed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Header = styled('div', { shouldForwardProp: prop => prop !== 'borderBottom' })<HeaderProps>`
|
export const Header = styled('div', { shouldForwardProp: prop => prop !== 'collapsed' })<HeaderProps>`
|
||||||
border-bottom: 1px solid ${({ theme, borderBottom }) => borderBottom ? alpha(theme.palette.divider, .8) : 'transparent'};
|
padding: ${({ theme }) => theme.spacing(2)};
|
||||||
padding: ${({ theme }) => theme.spacing(2, 2)};
|
cursor: ${({ collapsed }) => collapsed ? 'pointer' : 'unset'};
|
||||||
cursor: ${({ borderBottom }) => borderBottom ? 'unset' : 'pointer'};
|
`;
|
||||||
`;
|
|
||||||
|
export const Content = styled('div')`
|
||||||
|
border: 1px solid ${({ theme }) => alpha(theme.palette.divider, .8)};
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
padding: ${({ theme }) => theme.spacing(2, 0)};
|
||||||
|
`;
|
||||||
|
15
packages/web/src/graphql/mutations/create-step.ts
Normal file
15
packages/web/src/graphql/mutations/create-step.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const CREATE_STEP = gql`
|
||||||
|
mutation CreateStep($input: StepInput!) {
|
||||||
|
createStep(input: $input) {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
key
|
||||||
|
appKey
|
||||||
|
connection {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
15
packages/web/src/graphql/mutations/update-step.ts
Normal file
15
packages/web/src/graphql/mutations/update-step.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const UPDATE_STEP = gql`
|
||||||
|
mutation UpdateStep($input: StepInput!) {
|
||||||
|
updateStep(input: $input) {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
key
|
||||||
|
appKey
|
||||||
|
connection {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
@@ -55,6 +55,17 @@ export const GET_APP = gql`
|
|||||||
name
|
name
|
||||||
key
|
key
|
||||||
description
|
description
|
||||||
|
subSteps {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actions {
|
||||||
|
name
|
||||||
|
key
|
||||||
|
description
|
||||||
|
subSteps {
|
||||||
|
name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ export const GET_FLOW = gql`
|
|||||||
getFlow(id: $id) {
|
getFlow(id: $id) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
active
|
||||||
steps {
|
steps {
|
||||||
id
|
id
|
||||||
type
|
type
|
||||||
|
@@ -30,8 +30,8 @@
|
|||||||
"connection.deletedMessage": "The connection has been deleted.",
|
"connection.deletedMessage": "The connection has been deleted.",
|
||||||
"connection.addedAt": "added {datetime}",
|
"connection.addedAt": "added {datetime}",
|
||||||
"createFlow.creating": "Creating a flow...",
|
"createFlow.creating": "Creating a flow...",
|
||||||
"flow.active": "Enabled",
|
"flow.active": "ON",
|
||||||
"flow.inactive": "Disabled",
|
"flow.inactive": "OFF",
|
||||||
"flowStep.triggerType": "Trigger",
|
"flowStep.triggerType": "Trigger",
|
||||||
"flowStep.actionType": "Action",
|
"flowStep.actionType": "Action",
|
||||||
"flows.create": "Create flow",
|
"flows.create": "Create flow",
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useParams, } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Grid from '@mui/material/Grid';
|
import Grid from '@mui/material/Grid';
|
||||||
|
|
||||||
|
@@ -20,6 +20,8 @@ type App = {
|
|||||||
fields: AppFields[];
|
fields: AppFields[];
|
||||||
authenticationSteps: any[];
|
authenticationSteps: any[];
|
||||||
reconnectionSteps: any[];
|
reconnectionSteps: any[];
|
||||||
|
triggers: any[];
|
||||||
|
actions: any[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type { App, AppFields };
|
export type { App, AppFields };
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import type { Step } from './step';
|
import type { Step } from './step';
|
||||||
|
|
||||||
export type Flow = {
|
export type Flow = {
|
||||||
id: string;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
steps: Step[];
|
steps: Step[];
|
||||||
|
active: boolean;
|
||||||
};
|
};
|
||||||
|
@@ -9,5 +9,8 @@ export type Step = {
|
|||||||
name: string;
|
name: string;
|
||||||
appKey: string;
|
appKey: string;
|
||||||
type: StepType;
|
type: StepType;
|
||||||
connectionId: number;
|
previousStepId: number | null;
|
||||||
|
connection: {
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user