feat(filter): add filter app
This commit is contained in:
79
packages/backend/src/apps/filter/actions/continue/index.ts
Normal file
79
packages/backend/src/apps/filter/actions/continue/index.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
type TGroupItem = {
|
||||
key: string;
|
||||
operator: keyof TOperators;
|
||||
value: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
type TGroup = Record<'and', TGroupItem[]>;
|
||||
|
||||
const isEqual = (a: string, b: string) => a === b;
|
||||
const isNotEqual = (a: string, b: string) => !isEqual(a, b)
|
||||
const isGreaterThan = (a: string, b: string) => Number(a) > Number(b);
|
||||
const isLessThan = (a: string, b: string) => Number(a) < Number(b);
|
||||
const isGreaterThanOrEqual = (a: string, b: string) => Number(a) >= Number(b);
|
||||
const isLessThanOrEqual = (a: string, b: string) => Number(a) <= Number(b);
|
||||
const contains = (a: string, b: string) => a.includes(b);
|
||||
const doesNotContain = (a: string, b: string) => !contains(a, b);
|
||||
|
||||
type TOperatorFunc = (a: string, b: string) => boolean;
|
||||
|
||||
type TOperators = {
|
||||
equal: TOperatorFunc;
|
||||
not_equal: TOperatorFunc;
|
||||
greater_than: TOperatorFunc;
|
||||
less_than: TOperatorFunc;
|
||||
greater_than_or_equal: TOperatorFunc;
|
||||
less_than_or_equal: TOperatorFunc;
|
||||
contains: TOperatorFunc;
|
||||
not_contains: TOperatorFunc;
|
||||
};
|
||||
|
||||
const operators: TOperators = {
|
||||
'equal': isEqual,
|
||||
'not_equal': isNotEqual,
|
||||
'greater_than': isGreaterThan,
|
||||
'less_than': isLessThan,
|
||||
'greater_than_or_equal': isGreaterThanOrEqual,
|
||||
'less_than_or_equal': isLessThanOrEqual,
|
||||
'contains': contains,
|
||||
'not_contains': doesNotContain,
|
||||
};
|
||||
|
||||
const operate = (operation: keyof TOperators, a: string, b: string) => {
|
||||
return operators[operation](a, b);
|
||||
};
|
||||
|
||||
export default defineAction({
|
||||
name: 'Continue if conditions match',
|
||||
key: 'continueIfMatches',
|
||||
description: 'Let the execution continue if the conditions match',
|
||||
arguments: [],
|
||||
|
||||
async run($) {
|
||||
const orGroups = $.step.parameters.or as TGroup[];
|
||||
|
||||
const matchingGroups = orGroups.reduce((groups, group) => {
|
||||
const matchingConditions = group.and
|
||||
.filter((condition) => operate(condition.operator, condition.key, condition.value));
|
||||
|
||||
if (matchingConditions.length) {
|
||||
return groups.concat([{ and: matchingConditions }]);
|
||||
}
|
||||
|
||||
return groups;
|
||||
}, []);
|
||||
|
||||
if (matchingGroups.length === 0) {
|
||||
$.execution.exit();
|
||||
}
|
||||
|
||||
$.setActionItem({
|
||||
raw: {
|
||||
or: matchingGroups,
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
3
packages/backend/src/apps/filter/actions/index.ts
Normal file
3
packages/backend/src/apps/filter/actions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import continueIfMatches from './continue';
|
||||
|
||||
export default [continueIfMatches];
|
8
packages/backend/src/apps/filter/assets/favicon.svg
Normal file
8
packages/backend/src/apps/filter/assets/favicon.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg width="800px" height="800px" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Shape" fill="#000000" transform="translate(42.666667, 85.333333)">
|
||||
<path d="M3.55271368e-14,1.42108547e-14 L191.565013,234.666667 L192,234.666667 L192,384 L234.666667,384 L234.666667,234.666667 L426.666667,1.42108547e-14 L3.55271368e-14,1.42108547e-14 Z M214.448,192 L211.81248,192 L89.9076267,42.6666667 L336.630187,42.6666667 L214.448,192 Z">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 628 B |
0
packages/backend/src/apps/filter/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/filter/index.d.ts
vendored
Normal file
14
packages/backend/src/apps/filter/index.ts
Normal file
14
packages/backend/src/apps/filter/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import defineApp from '../../helpers/define-app';
|
||||
import actions from './actions';
|
||||
|
||||
export default defineApp({
|
||||
name: 'Filter',
|
||||
key: 'filter',
|
||||
iconUrl: '{BASE_URL}/apps/filter/assets/favicon.svg',
|
||||
authDocUrl: 'https://automatisch.io/docs/apps/filter/connection',
|
||||
supportsConnections: false,
|
||||
baseUrl: '',
|
||||
apiBaseUrl: '',
|
||||
primaryColor: '001F52',
|
||||
actions,
|
||||
});
|
3
packages/backend/src/errors/already-processed.ts
Normal file
3
packages/backend/src/errors/already-processed.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import BaseError from './base';
|
||||
|
||||
export default class AlreadyProcessedError extends BaseError { }
|
@@ -38,6 +38,13 @@ export default function computeParameters(
|
||||
};
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return {
|
||||
...result,
|
||||
[key]: value.map(item => computeParameters(item, executionSteps)),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...result,
|
||||
[key]: value,
|
||||
|
@@ -13,6 +13,7 @@ import {
|
||||
IRequest,
|
||||
} from '@automatisch/types';
|
||||
import EarlyExitError from '../errors/early-exit';
|
||||
import AlreadyProcessedError from '../errors/already-processed';
|
||||
|
||||
type GlobalVariableOptions = {
|
||||
connection?: Connection;
|
||||
@@ -77,6 +78,9 @@ const globalVariable = async (
|
||||
execution: {
|
||||
id: execution?.id,
|
||||
testRun,
|
||||
exit: () => {
|
||||
throw new EarlyExitError();
|
||||
}
|
||||
},
|
||||
lastExecutionStep: (await step?.getLastExecutionStep())?.toJSON(),
|
||||
triggerOutput: {
|
||||
@@ -93,7 +97,7 @@ const globalVariable = async (
|
||||
!$.execution.testRun
|
||||
) {
|
||||
// early exit as we do not want to process duplicate items in actual executions
|
||||
throw new EarlyExitError();
|
||||
throw new AlreadyProcessedError();
|
||||
}
|
||||
|
||||
$.triggerOutput.data.push(triggerItem);
|
||||
|
@@ -5,6 +5,8 @@ import ExecutionStep from '../models/execution-step';
|
||||
import computeParameters from '../helpers/compute-parameters';
|
||||
import globalVariable from '../helpers/global-variable';
|
||||
import HttpError from '../errors/http';
|
||||
import EarlyExitError from '../errors/early-exit';
|
||||
import AlreadyProcessedError from '../errors/already-processed';
|
||||
|
||||
type ProcessActionOptions = {
|
||||
flowId: string;
|
||||
@@ -44,13 +46,19 @@ export const processAction = async (options: ProcessActionOptions) => {
|
||||
try {
|
||||
await actionCommand.run($);
|
||||
} catch (error) {
|
||||
if (error instanceof HttpError) {
|
||||
$.actionOutput.error = error.details;
|
||||
} else {
|
||||
try {
|
||||
$.actionOutput.error = JSON.parse(error.message);
|
||||
} catch {
|
||||
$.actionOutput.error = { error: error.message };
|
||||
const shouldEarlyExit = error instanceof EarlyExitError;
|
||||
const shouldNotProcess = error instanceof AlreadyProcessedError;
|
||||
const shouldNotConsiderAsError = shouldEarlyExit || shouldNotProcess;
|
||||
|
||||
if (!shouldNotConsiderAsError) {
|
||||
if (error instanceof HttpError) {
|
||||
$.actionOutput.error = error.details;
|
||||
} else {
|
||||
try {
|
||||
$.actionOutput.error = JSON.parse(error.message);
|
||||
} catch {
|
||||
$.actionOutput.error = { error: error.message };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import Flow from '../models/flow';
|
||||
import globalVariable from '../helpers/global-variable';
|
||||
import EarlyExitError from '../errors/early-exit';
|
||||
import AlreadyProcessedError from '../errors/already-processed';
|
||||
import HttpError from '../errors/http';
|
||||
|
||||
type ProcessFlowOptions = {
|
||||
@@ -29,7 +30,11 @@ export const processFlow = async (options: ProcessFlowOptions) => {
|
||||
await triggerCommand.run($);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof EarlyExitError === false) {
|
||||
const shouldEarlyExit = error instanceof EarlyExitError;
|
||||
const shouldNotProcess = error instanceof AlreadyProcessedError;
|
||||
const shouldNotConsiderAsError = shouldEarlyExit || shouldNotProcess;
|
||||
|
||||
if (!shouldNotConsiderAsError) {
|
||||
if (error instanceof HttpError) {
|
||||
$.triggerOutput.error = error.details;
|
||||
} else {
|
||||
|
@@ -21,7 +21,7 @@ const DEFAULT_DELAY_DURATION = 0;
|
||||
export const worker = new Worker(
|
||||
'action',
|
||||
async (job) => {
|
||||
const { stepId, flowId, executionId, computedParameters } = await processAction(
|
||||
const { stepId, flowId, executionId, computedParameters, executionStep } = await processAction(
|
||||
job.data as JobData
|
||||
);
|
||||
|
||||
@@ -48,6 +48,10 @@ export const worker = new Worker(
|
||||
jobOptions.delay = delayAsMilliseconds(step.key, computedParameters);
|
||||
}
|
||||
|
||||
if (step.appKey === 'filter' && !executionStep.dataOut) {
|
||||
return;
|
||||
}
|
||||
|
||||
await actionQueue.add(jobName, jobPayload, jobOptions);
|
||||
},
|
||||
{ connection: redisConfig }
|
||||
|
1
packages/types/index.d.ts
vendored
1
packages/types/index.d.ts
vendored
@@ -296,6 +296,7 @@ export type IGlobalVariable = {
|
||||
execution?: {
|
||||
id: string;
|
||||
testRun: boolean;
|
||||
exit: () => void;
|
||||
};
|
||||
lastExecutionStep?: IExecutionStep;
|
||||
webhookUrl?: string;
|
||||
|
@@ -9,9 +9,9 @@
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@hookform/resolvers": "^2.8.8",
|
||||
"@mui/icons-material": "^5.0.1",
|
||||
"@mui/lab": "^5.0.0-alpha.60",
|
||||
"@mui/material": "^5.0.2",
|
||||
"@mui/icons-material": "^5.11.9",
|
||||
"@mui/lab": "^5.0.0-alpha.120",
|
||||
"@mui/material": "^5.11.10",
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
@@ -21,6 +21,7 @@
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"clipboard-copy": "^4.0.1",
|
||||
"compare-versions": "^4.1.3",
|
||||
"graphql": "^15.6.0",
|
||||
@@ -38,6 +39,7 @@
|
||||
"slate-history": "^0.66.0",
|
||||
"slate-react": "^0.72.9",
|
||||
"typescript": "^4.6.3",
|
||||
"uuid": "^9.0.0",
|
||||
"web-vitals": "^1.0.1",
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
|
@@ -69,7 +69,7 @@ function ControlledAutocomplete(
|
||||
},
|
||||
fieldState,
|
||||
}) => (
|
||||
<div>
|
||||
<div style={{ width:'100%' }}>
|
||||
{/* encapsulated with an element such as div to vertical spacing delegated from parent */}
|
||||
<Autocomplete
|
||||
{...autocompleteProps}
|
||||
|
@@ -0,0 +1,213 @@
|
||||
import * as React from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Box from '@mui/material/Box';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import RemoveIcon from '@mui/icons-material/Remove';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import type { IField, IFieldText, IFieldDropdown } from '@automatisch/types';
|
||||
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import InputCreator from 'components/InputCreator';
|
||||
import { EditorContext } from 'contexts/Editor';
|
||||
|
||||
type TGroupItem = {
|
||||
key: string;
|
||||
operator: string;
|
||||
value: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
type TGroup = Record<'and', TGroupItem[]>;
|
||||
|
||||
const createGroupItem = (): TGroupItem => ({
|
||||
key: '',
|
||||
operator: operators[0].value,
|
||||
value: '',
|
||||
id: uuidv4(),
|
||||
});
|
||||
|
||||
const createGroup = (): TGroup => ({
|
||||
and: [createGroupItem()]
|
||||
});
|
||||
|
||||
const operators = [
|
||||
{
|
||||
label: 'Equal',
|
||||
value: 'equal',
|
||||
},
|
||||
{
|
||||
label: 'Not Equal',
|
||||
value: 'not_equal',
|
||||
},
|
||||
{
|
||||
label: 'Greater Than',
|
||||
value: 'greater_than',
|
||||
},
|
||||
{
|
||||
label: 'Less Than',
|
||||
value: 'less_than',
|
||||
},
|
||||
{
|
||||
label: 'Greater Than Or Equal',
|
||||
value: 'greater_than_or_equal',
|
||||
},
|
||||
{
|
||||
label: 'Less Than Or Equal',
|
||||
value: 'less_than_or_equal',
|
||||
},
|
||||
{
|
||||
label: 'Contains',
|
||||
value: 'contains',
|
||||
},
|
||||
{
|
||||
label: 'Not Contains',
|
||||
value: 'not_contains',
|
||||
},
|
||||
];
|
||||
|
||||
const createStringArgument = (argumentOptions: Omit<IFieldText, 'type' | 'required' | 'variables'>): IField => {
|
||||
return {
|
||||
...argumentOptions,
|
||||
type: 'string',
|
||||
required: true,
|
||||
variables: true,
|
||||
};
|
||||
};
|
||||
|
||||
const createDropdownArgument = (argumentOptions: Omit<IFieldDropdown, 'type' | 'required'>): IField => {
|
||||
return {
|
||||
...argumentOptions,
|
||||
required: true,
|
||||
type: 'dropdown',
|
||||
};
|
||||
};
|
||||
|
||||
type FilterConditionsProps = {
|
||||
stepId: string;
|
||||
};
|
||||
|
||||
function FilterConditions(props: FilterConditionsProps): React.ReactElement {
|
||||
const {
|
||||
stepId
|
||||
} = props;
|
||||
const formatMessage = useFormatMessage();
|
||||
const { control, setValue, getValues } = useFormContext();
|
||||
const groups = useWatch({ control, name: 'parameters.or' });
|
||||
const editorContext = React.useContext(EditorContext);
|
||||
|
||||
React.useEffect(function addInitialGroupWhenEmpty() {
|
||||
const groups = getValues('parameters.or');
|
||||
|
||||
if (!groups) {
|
||||
setValue('parameters.or', [createGroup()]);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const appendGroup = React.useCallback(() => {
|
||||
const values = getValues('parameters.or');
|
||||
|
||||
setValue('parameters.or', values.concat(createGroup()))
|
||||
}, []);
|
||||
|
||||
const appendGroupItem = React.useCallback((index) => {
|
||||
const group = getValues(`parameters.or.${index}.and`);
|
||||
setValue(`parameters.or.${index}.and`, group.concat(createGroupItem()));
|
||||
}, []);
|
||||
|
||||
const removeGroupItem = React.useCallback((groupIndex, groupItemIndex) => {
|
||||
const group: TGroupItem[] = getValues(`parameters.or.${groupIndex}.and`);
|
||||
|
||||
if (group.length === 1) {
|
||||
const groups: TGroup[] = getValues('parameters.or');
|
||||
|
||||
setValue('parameters.or', groups.filter((group, index) => index !== groupIndex));
|
||||
} else {
|
||||
setValue(`parameters.or.${groupIndex}.and`, group.filter((groupItem, index) => index !== groupItemIndex));
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Stack sx={{ width: "100%" }} direction="column" spacing={2} mt={2}>
|
||||
{groups?.map((group: TGroup, groupIndex: number) => (
|
||||
<>
|
||||
{groupIndex !== 0 && <Divider />}
|
||||
|
||||
<Typography variant="subtitle2" gutterBottom>
|
||||
{groupIndex === 0 && formatMessage('filterConditions.onlyContinueIf')}
|
||||
{groupIndex !== 0 && formatMessage('filterConditions.orContinueIf')}
|
||||
</Typography>
|
||||
|
||||
{group?.and?.map((groupItem: TGroupItem, groupItemIndex: number) => (
|
||||
<Stack direction="row" spacing={2} key={`item-${groupItem.id}`}>
|
||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={{ xs: 2}} sx={{ display: 'flex', flex: 1 }}>
|
||||
<Box sx={{ display: 'flex', flex: '1 0 0px', maxWidth: ['100%', '33%'] }}>
|
||||
<InputCreator
|
||||
schema={createStringArgument({ key: `or.${groupIndex}.and.${groupItemIndex}.key`, label: 'Choose field' })}
|
||||
namePrefix="parameters"
|
||||
stepId={stepId}
|
||||
disabled={editorContext.readOnly}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ display: 'flex', flex: '1 0 0px', maxWidth: ['100%', '33%'] }}>
|
||||
<InputCreator
|
||||
schema={createDropdownArgument({ key: `or.${groupIndex}.and.${groupItemIndex}.operator`, options: operators, label: 'Choose condition' })}
|
||||
namePrefix="parameters"
|
||||
stepId={stepId}
|
||||
disabled={editorContext.readOnly}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ display: 'flex', flex: '1 0 0px', maxWidth: ['100%', '33%'] }}>
|
||||
<InputCreator
|
||||
schema={createStringArgument({ key: `or.${groupIndex}.and.${groupItemIndex}.value`, label: 'Enter text' })}
|
||||
namePrefix="parameters"
|
||||
stepId={stepId}
|
||||
disabled={editorContext.readOnly}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<IconButton
|
||||
size="small"
|
||||
edge="start"
|
||||
onClick={() => removeGroupItem(groupIndex, groupItemIndex)}
|
||||
sx={{ width: 61, height: 61 }}
|
||||
>
|
||||
<RemoveIcon />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
))}
|
||||
|
||||
<Stack spacing={1} direction="row">
|
||||
<IconButton
|
||||
size="small"
|
||||
edge="start"
|
||||
sx={{ width: 61, height: 61 }}
|
||||
onClick={() => appendGroupItem(groupIndex)}
|
||||
>
|
||||
<AddIcon /> And
|
||||
</IconButton>
|
||||
|
||||
{(groups.length - 1) === groupIndex && <IconButton
|
||||
size="small"
|
||||
edge="start"
|
||||
onClick={appendGroup}
|
||||
sx={{ width: 61, height: 61 }}
|
||||
>
|
||||
<AddIcon /> Or
|
||||
</IconButton>}
|
||||
</Stack>
|
||||
</>
|
||||
))}
|
||||
</Stack>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default FilterConditions;
|
@@ -4,11 +4,12 @@ import Collapse from '@mui/material/Collapse';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
import Button from '@mui/material/Button';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import type { IField, IStep, ISubstep } from '@automatisch/types';
|
||||
|
||||
import { EditorContext } from 'contexts/Editor';
|
||||
import FlowSubstepTitle from 'components/FlowSubstepTitle';
|
||||
import InputCreator from 'components/InputCreator';
|
||||
import type { IField, IStep, ISubstep } from '@automatisch/types';
|
||||
import FilterConditions from './FilterConditions';
|
||||
|
||||
type FlowSubstepProps = {
|
||||
substep: ISubstep;
|
||||
@@ -84,20 +85,25 @@ function FlowSubstep(props: FlowSubstepProps): React.ReactElement {
|
||||
pb: 3,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<Stack width="100%" spacing={2}>
|
||||
{args?.map((argument) => (
|
||||
<InputCreator
|
||||
key={argument.key}
|
||||
schema={argument}
|
||||
namePrefix="parameters"
|
||||
stepId={step.id}
|
||||
disabled={editorContext.readOnly}
|
||||
showOptionValue={true}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
{!!args?.length && (
|
||||
<Stack width="100%" spacing={2}>
|
||||
{args.map((argument) => (
|
||||
<InputCreator
|
||||
key={argument.key}
|
||||
schema={argument}
|
||||
namePrefix="parameters"
|
||||
stepId={step.id}
|
||||
disabled={editorContext.readOnly}
|
||||
showOptionValue={true}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{step.appKey === 'filter' && <FilterConditions stepId={step.id} />}
|
||||
|
||||
<Button
|
||||
fullWidth
|
||||
|
@@ -67,7 +67,6 @@ export default function InputCreator(
|
||||
options={preparedOptions}
|
||||
renderInput={(params) => <MuiTextField {...params} label={label} />}
|
||||
defaultValue={value as string}
|
||||
onChange={console.log}
|
||||
description={description}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
@@ -98,7 +97,6 @@ export default function InputCreator(
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
name={computedName}
|
||||
size="small"
|
||||
label={label}
|
||||
fullWidth
|
||||
helperText={description}
|
||||
|
@@ -130,7 +130,7 @@ const PowerInput = (props: PowerInputProps) => {
|
||||
/>
|
||||
</FakeInput>
|
||||
{/* ghost placer for the variables popover */}
|
||||
<div ref={editorRef} style={{ width: '100%' }} />
|
||||
<div ref={editorRef} style={{ position: 'absolute', right: 16, left: 16 }} />
|
||||
|
||||
<FormHelperText variant="outlined">{description}</FormHelperText>
|
||||
|
||||
|
@@ -58,6 +58,8 @@
|
||||
"flowEditor.triggerEvent": "Trigger event",
|
||||
"flowEditor.actionEvent": "Action event",
|
||||
"flowEditor.instantTriggerType": "Instant",
|
||||
"filterConditions.onlyContinueIf": "Only continue if…",
|
||||
"filterConditions.orContinueIf": "OR continue if…",
|
||||
"chooseConnectionSubstep.continue": "Continue",
|
||||
"chooseConnectionSubstep.addNewConnection": "Add new connection",
|
||||
"chooseConnectionSubstep.chooseConnection": "Choose connection",
|
||||
|
@@ -251,14 +251,6 @@ const extendedTheme = createTheme({
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiOutlinedInput: {
|
||||
styleOverrides: {
|
||||
inputSizeSmall: ({ theme }) => ({
|
||||
// 1.5625 = 12.5px based on 1 = 8px
|
||||
padding: theme.spacing(1.5625, 1.75),
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiTab: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
|
10816
packages/web/yarn.lock
10816
packages/web/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user