Merge pull request #1537 from automatisch/web/types

chore: Use types from the web package
This commit is contained in:
Ömer Faruk Aydın
2024-01-15 13:37:43 +01:00
committed by GitHub
77 changed files with 934 additions and 378 deletions

View File

@@ -1,4 +1,4 @@
import type { IApp, IField, IJSONObject } from '@automatisch/types';
import type { IApp, IField, IJSONObject } from 'types';
import LoadingButton from '@mui/lab/LoadingButton';
import Alert from '@mui/material/Alert';
import Dialog from '@mui/material/Dialog';

View File

@@ -19,7 +19,7 @@ import InputLabel from '@mui/material/InputLabel';
import OutlinedInput from '@mui/material/OutlinedInput';
import FormControl from '@mui/material/FormControl';
import Box from '@mui/material/Box';
import type { IApp } from '@automatisch/types';
import type { IApp } from 'types';
import * as URLS from 'config/urls';
import AppIcon from 'components/AppIcon';

View File

@@ -1,5 +1,5 @@
import React from 'react';
import type { IField } from '@automatisch/types';
import type { IField } from 'types';
import LoadingButton from '@mui/lab/LoadingButton';
import Alert from '@mui/material/Alert';
import Dialog from '@mui/material/Dialog';

View File

@@ -1,5 +1,5 @@
import React, { useCallback, useMemo } from 'react';
import type { IApp } from '@automatisch/types';
import type { IApp } from 'types';
import { FieldValues, SubmitHandler } from 'react-hook-form';
import { useMutation } from '@apollo/client';
import { CREATE_APP_CONFIG } from 'graphql/mutations/create-app-config';

View File

@@ -1,6 +1,6 @@
import React, { useCallback, useMemo } from 'react';
import { useParams } from 'react-router-dom';
import type { IApp } from '@automatisch/types';
import type { IApp } from 'types';
import { FieldValues, SubmitHandler } from 'react-hook-form';
import { useMutation } from '@apollo/client';
import { UPDATE_APP_AUTH_CLIENT } from 'graphql/mutations/update-app-auth-client';

View File

@@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
import Menu from '@mui/material/Menu';
import type { PopoverProps } from '@mui/material/Popover';
import MenuItem from '@mui/material/MenuItem';
import type { IConnection } from '@automatisch/types';
import type { IConnection } from 'types';
import * as URLS from 'config/urls';
import useFormatMessage from 'hooks/useFormatMessage';

View File

@@ -11,7 +11,7 @@ import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
import { DateTime } from 'luxon';
import * as React from 'react';
import type { IConnection } from '@automatisch/types';
import type { IConnection } from 'types';
import ConnectionContextMenu from 'components/AppConnectionContextMenu';
import { DELETE_CONNECTION } from 'graphql/mutations/delete-connection';
import { TEST_CONNECTION } from 'graphql/queries/test-connection';
@@ -83,8 +83,8 @@ function AppConnectionRow(props: AppConnectionRowProps): React.ReactElement {
enqueueSnackbar(formatMessage('connection.deletedMessage'), {
variant: 'success',
SnackbarProps: {
'data-test': 'snackbar-delete-connection-success'
}
'data-test': 'snackbar-delete-connection-success',
},
});
} else if (action.type === 'test') {
setVerificationVisible(true);

View File

@@ -1,7 +1,7 @@
import * as React from 'react';
import { useQuery } from '@apollo/client';
import type { IConnection } from '@automatisch/types';
import type { IConnection } from 'types';
import { GET_APP_CONNECTIONS } from 'graphql/queries/get-app-connections';
import AppConnectionRow from 'components/AppConnectionRow';
import NoResultFound from 'components/NoResultFound';

View File

@@ -8,7 +8,7 @@ import * as URLS from 'config/urls';
import AppFlowRow from 'components/FlowRow';
import NoResultFound from 'components/NoResultFound';
import useFormatMessage from 'hooks/useFormatMessage';
import type { IFlow } from '@automatisch/types';
import type { IFlow } from 'types';
type AppFlowsProps = {
appKey: string;

View File

@@ -7,7 +7,7 @@ import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import useFormatMessage from 'hooks/useFormatMessage';
import AppIcon from 'components/AppIcon';
import type { IApp } from '@automatisch/types';
import type { IApp } from 'types';
import { CardContent, Typography } from './style';

View File

@@ -7,13 +7,7 @@ import ListItem from '@mui/material/ListItem';
import TextField from '@mui/material/TextField';
import Autocomplete from '@mui/material/Autocomplete';
import Chip from '@mui/material/Chip';
import type {
IApp,
IStep,
ISubstep,
ITrigger,
IAction,
} from '@automatisch/types';
import type { IApp, IStep, ISubstep, ITrigger, IAction } from 'types';
import useFormatMessage from 'hooks/useFormatMessage';
import useApps from 'hooks/useApps';

View File

@@ -6,7 +6,7 @@ import ListItem from '@mui/material/ListItem';
import TextField from '@mui/material/TextField';
import * as React from 'react';
import type { IApp, IConnection, IStep, ISubstep } from '@automatisch/types';
import type { IApp, IConnection, IStep, ISubstep } from 'types';
import AddAppConnection from 'components/AddAppConnection';
import AppAuthClientsDialog from 'components/AppAuthClientsDialog/index.ee';
import FlowSubstepTitle from 'components/FlowSubstepTitle';

View File

@@ -1,9 +1,12 @@
import * as React from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import FormHelperText from '@mui/material/FormHelperText';
import Autocomplete, { AutocompleteProps, createFilterOptions } from '@mui/material/Autocomplete';
import Autocomplete, {
AutocompleteProps,
createFilterOptions,
} from '@mui/material/Autocomplete';
import Typography from '@mui/material/Typography';
import type { IFieldDropdownOption } from '@automatisch/types';
import type { IFieldDropdownOption } from 'types';
interface ControlledAutocompleteProps
extends AutocompleteProps<IFieldDropdownOption, boolean, boolean, boolean> {
@@ -23,8 +26,8 @@ const filterOptions = createFilterOptions<IFieldDropdownOption>({
stringify: ({ label, value }) => `
${label}
${value}
`
})
`,
});
function ControlledAutocomplete(
props: ControlledAutocompleteProps

View File

@@ -3,7 +3,7 @@ import Popper from '@mui/material/Popper';
import Tab from '@mui/material/Tab';
import * as React from 'react';
import type { IFieldDropdownOption } from '@automatisch/types';
import type { IFieldDropdownOption } from 'types';
import Suggestions from 'components/PowerInput/Suggestions';
import TabPanel from 'components/TabPanel';

View File

@@ -1,4 +1,4 @@
import type { IFieldDropdownOption } from '@automatisch/types';
import type { IFieldDropdownOption } from 'types';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText';
import throttle from 'lodash/throttle';
@@ -13,7 +13,7 @@ import { SearchInputWrapper } from './style';
interface OptionsProps {
data: readonly IFieldDropdownOption[];
onOptionClick: (event: React.MouseEvent, option: any) => void;
};
}
const SHORT_LIST_LENGTH = 4;
const LIST_ITEM_HEIGHT = 64;
@@ -21,80 +21,89 @@ const LIST_ITEM_HEIGHT = 64;
const computeListHeight = (currentLength: number) => {
const numberOfRenderedItems = Math.min(SHORT_LIST_LENGTH, currentLength);
return LIST_ITEM_HEIGHT * numberOfRenderedItems;
}
const renderItemFactory = ({ onOptionClick }: Pick<OptionsProps, 'onOptionClick'>) => (props: ListChildComponentProps) => {
const { index, style, data } = props;
const suboption = data[index];
return (
<ListItemButton
sx={{ pl: 4 }}
divider
onClick={(event) => onOptionClick(event, suboption)}
data-test="power-input-suggestion-item"
key={index}
style={style}
>
<ListItemText
primary={suboption.label}
primaryTypographyProps={{
variant: 'subtitle1',
title: 'Property name',
sx: { fontWeight: 700 },
}}
secondary={suboption.value}
secondaryTypographyProps={{
variant: 'subtitle2',
title: 'Sample value',
noWrap: true,
}}
/>
</ListItemButton>
);
};
const renderItemFactory =
({ onOptionClick }: Pick<OptionsProps, 'onOptionClick'>) =>
(props: ListChildComponentProps) => {
const { index, style, data } = props;
const suboption = data[index];
return (
<ListItemButton
sx={{ pl: 4 }}
divider
onClick={(event) => onOptionClick(event, suboption)}
data-test="power-input-suggestion-item"
key={index}
style={style}
>
<ListItemText
primary={suboption.label}
primaryTypographyProps={{
variant: 'subtitle1',
title: 'Property name',
sx: { fontWeight: 700 },
}}
secondary={suboption.value}
secondaryTypographyProps={{
variant: 'subtitle2',
title: 'Sample value',
noWrap: true,
}}
/>
</ListItemButton>
);
};
const Options = (props: OptionsProps) => {
const formatMessage = useFormatMessage();
const {
data,
onOptionClick
} = props;
const [filteredData, setFilteredData] = React.useState<readonly IFieldDropdownOption[]>(
data
const { data, onOptionClick } = props;
const [filteredData, setFilteredData] =
React.useState<readonly IFieldDropdownOption[]>(data);
React.useEffect(
function syncOptions() {
setFilteredData((filteredData) => {
if (filteredData.length === 0 && filteredData.length !== data.length) {
return data;
}
return filteredData;
});
},
[data]
);
React.useEffect(function syncOptions() {
setFilteredData((filteredData) => {
if (filteredData.length === 0 && filteredData.length !== data.length) {
return data;
}
return filteredData;
})
}, [data]);
const renderItem = React.useMemo(() => renderItemFactory({
onOptionClick
}), [onOptionClick]);
const renderItem = React.useMemo(
() =>
renderItemFactory({
onOptionClick,
}),
[onOptionClick]
);
const onSearchChange = React.useMemo(
() =>
throttle((event: React.ChangeEvent) => {
const search = (event.target as HTMLInputElement).value.toLowerCase();
() =>
throttle((event: React.ChangeEvent) => {
const search = (event.target as HTMLInputElement).value.toLowerCase();
if (!search) {
setFilteredData(data);
return;
}
if (!search) {
setFilteredData(data);
return;
}
const newFilteredData = data.filter(option => `${option.label}\n${option.value}`.toLowerCase().includes(search.toLowerCase()));
const newFilteredData = data.filter((option) =>
`${option.label}\n${option.value}`
.toLowerCase()
.includes(search.toLowerCase())
);
setFilteredData(newFilteredData);
}, 400),
[data]
);
setFilteredData(newFilteredData);
}, 400),
[data]
);
return (
<>
@@ -122,5 +131,4 @@ const Options = (props: OptionsProps) => {
);
};
export default Options;

View File

@@ -5,7 +5,7 @@ import FormHelperText from '@mui/material/FormHelperText';
import { AutocompleteProps } from '@mui/material/Autocomplete';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import ClearIcon from '@mui/icons-material/Clear';
import type { IFieldDropdownOption } from '@automatisch/types';
import type { IFieldDropdownOption } from 'types';
import { ActionButtonsWrapper } from './style';
import ClickAwayListener from '@mui/base/ClickAwayListener';

View File

@@ -8,7 +8,7 @@ import IconButton from '@mui/material/IconButton';
import RemoveIcon from '@mui/icons-material/Remove';
import AddIcon from '@mui/icons-material/Add';
import { IFieldDynamic } from '@automatisch/types';
import { IFieldDynamic } from 'types';
import InputCreator from 'components/InputCreator';
import { EditorContext } from 'contexts/Editor';

View File

@@ -3,7 +3,7 @@ import { useMutation } from '@apollo/client';
import Box from '@mui/material/Box';
import IconButton from '@mui/material/IconButton';
import AddIcon from '@mui/icons-material/Add';
import type { IFlow, IStep } from '@automatisch/types';
import type { IFlow, IStep } from 'types';
import { GET_FLOW } from 'graphql/queries/get-flow';
import { CREATE_STEP } from 'graphql/mutations/create-step';

View File

@@ -17,7 +17,7 @@ import useFormatMessage from 'hooks/useFormatMessage';
import { UPDATE_FLOW_STATUS } from 'graphql/mutations/update-flow-status';
import { UPDATE_FLOW } from 'graphql/mutations/update-flow';
import { GET_FLOW } from 'graphql/queries/get-flow';
import type { IFlow } from '@automatisch/types';
import type { IFlow } from 'types';
import * as URLS from 'config/urls';
export default function EditorLayout(): React.ReactElement {

View File

@@ -4,7 +4,7 @@ import Stack from '@mui/material/Stack';
import Box from '@mui/material/Box';
import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import type { IExecution } from '@automatisch/types';
import type { IExecution } from 'types';
import useFormatMessage from 'hooks/useFormatMessage';

View File

@@ -5,7 +5,7 @@ import CardActionArea from '@mui/material/CardActionArea';
import Chip from '@mui/material/Chip';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import { DateTime } from 'luxon';
import type { IExecution } from '@automatisch/types';
import type { IExecution } from 'types';
import * as URLS from 'config/urls';
import useFormatMessage from 'hooks/useFormatMessage';

View File

@@ -8,7 +8,7 @@ import Tab from '@mui/material/Tab';
import Typography from '@mui/material/Typography';
import Tooltip from '@mui/material/Tooltip';
import Box from '@mui/material/Box';
import type { IApp, IExecutionStep, IStep } from '@automatisch/types';
import type { IApp, IExecutionStep, IStep } from 'types';
import TabPanel from 'components/TabPanel';
import SearchableJSONViewer from 'components/SearchableJSONViewer';

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import type { IStep } from '@automatisch/types';
import type { IStep } from 'types';
import AppIcon from 'components/AppIcon';
import IntermediateStepCount from 'components/IntermediateStepCount';

View File

@@ -7,7 +7,7 @@ import Chip from '@mui/material/Chip';
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
import { DateTime } from 'luxon';
import type { IFlow } from '@automatisch/types';
import type { IFlow } from 'types';
import FlowAppIcons from 'components/FlowAppIcons';
import FlowContextMenu from 'components/FlowContextMenu';
import useFormatMessage from 'hooks/useFormatMessage';

View File

@@ -14,13 +14,7 @@ import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
import type { BaseSchema } from 'yup';
import type {
IApp,
ITrigger,
IAction,
IStep,
ISubstep,
} from '@automatisch/types';
import type { IApp, ITrigger, IAction, IStep, ISubstep } from 'types';
import { EditorContext } from 'contexts/Editor';
import { StepExecutionsProvider } from 'contexts/StepExecutions';

View File

@@ -8,7 +8,7 @@ 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 type { IField, IFieldText, IFieldDropdown } from 'types';
import useFormatMessage from 'hooks/useFormatMessage';
import InputCreator from 'components/InputCreator';
@@ -19,7 +19,7 @@ type TGroupItem = {
operator: string;
value: string;
id: string;
}
};
type TGroup = Record<'and', TGroupItem[]>;
@@ -31,7 +31,7 @@ const createGroupItem = (): TGroupItem => ({
});
const createGroup = (): TGroup => ({
and: [createGroupItem()]
and: [createGroupItem()],
});
const operators = [
@@ -69,7 +69,9 @@ const operators = [
},
];
const createStringArgument = (argumentOptions: Omit<IFieldText, 'type' | 'required' | 'variables'>): IField => {
const createStringArgument = (
argumentOptions: Omit<IFieldText, 'type' | 'required' | 'variables'>
): IField => {
return {
...argumentOptions,
type: 'string',
@@ -78,7 +80,9 @@ const createStringArgument = (argumentOptions: Omit<IFieldText, 'type' | 'requir
};
};
const createDropdownArgument = (argumentOptions: Omit<IFieldDropdown, 'type' | 'required'>): IField => {
const createDropdownArgument = (
argumentOptions: Omit<IFieldDropdown, 'type' | 'required'>
): IField => {
return {
...argumentOptions,
required: true,
@@ -91,9 +95,7 @@ type FilterConditionsProps = {
};
function FilterConditions(props: FilterConditionsProps): React.ReactElement {
const {
stepId
} = props;
const { stepId } = props;
const formatMessage = useFormatMessage();
const { control, setValue, getValues } = useFormContext();
const groups = useWatch({ control, name: 'parameters.or' });
@@ -110,7 +112,7 @@ function FilterConditions(props: FilterConditionsProps): React.ReactElement {
const appendGroup = React.useCallback(() => {
const values = getValues('parameters.or');
setValue('parameters.or', values.concat(createGroup()))
setValue('parameters.or', values.concat(createGroup()));
}, []);
const appendGroupItem = React.useCallback((index) => {
@@ -124,65 +126,107 @@ function FilterConditions(props: FilterConditionsProps): React.ReactElement {
if (group.length === 1) {
const groups: TGroup[] = getValues('parameters.or');
setValue('parameters.or', groups.filter((group, index) => index !== groupIndex));
setValue(
'parameters.or',
groups.filter((group, index) => index !== groupIndex)
);
} else {
setValue(`parameters.or.${groupIndex}.and`, group.filter((groupItem, index) => index !== groupItemIndex));
setValue(
`parameters.or.${groupIndex}.and`,
group.filter((groupItem, index) => index !== groupItemIndex)
);
}
}, []);
return (
<React.Fragment>
<Stack sx={{ width: "100%" }} direction="column" spacing={2} mt={2}>
<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')}
{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>
{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={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>
<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>
<IconButton
size="small"
edge="start"
onClick={() => removeGroupItem(groupIndex, groupItemIndex)}
sx={{ width: 61, height: 61 }}
>
<RemoveIcon />
</IconButton>
</Stack>
))}
)
)}
<Stack spacing={1} direction="row">
<IconButton
@@ -194,14 +238,16 @@ function FilterConditions(props: FilterConditionsProps): React.ReactElement {
<AddIcon /> And
</IconButton>
{(groups.length - 1) === groupIndex && <IconButton
size="small"
edge="start"
onClick={appendGroup}
sx={{ width: 61, height: 61 }}
>
<AddIcon /> Or
</IconButton>}
{groups.length - 1 === groupIndex && (
<IconButton
size="small"
edge="start"
onClick={appendGroup}
sx={{ width: 61, height: 61 }}
>
<AddIcon /> Or
</IconButton>
)}
</Stack>
</>
))}

View File

@@ -4,7 +4,7 @@ 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 { IStep, ISubstep } from '@automatisch/types';
import type { IStep, ISubstep } from 'types';
import { EditorContext } from 'contexts/Editor';
import FlowSubstepTitle from 'components/FlowSubstepTitle';
@@ -54,7 +54,7 @@ function FlowSubstep(props: FlowSubstepProps): React.ReactElement {
pb: 3,
flexDirection: 'column',
alignItems: 'flex-start',
position: 'relative'
position: 'relative',
}}
>
{!!args?.length && (

View File

@@ -1,7 +1,7 @@
import * as React from 'react';
import MuiTextField from '@mui/material/TextField';
import CircularProgress from '@mui/material/CircularProgress';
import type { IField, IFieldDropdownOption } from '@automatisch/types';
import type { IField, IFieldDropdownOption } from 'types';
import useDynamicFields from 'hooks/useDynamicFields';
import useDynamicData from 'hooks/useDynamicData';

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import { JSONTree } from 'react-json-tree';
import type { IJSONObject } from '@automatisch/types';
import type { IJSONObject } from 'types';
type JSONViewerProps = {
data: IJSONObject;

View File

@@ -14,7 +14,7 @@ import Typography from '@mui/material/Typography';
import * as React from 'react';
import { useFormContext } from 'react-hook-form';
import { IPermissionCatalog } from '@automatisch/types';
import { IPermissionCatalog } from 'types';
import ControlledCheckbox from 'components/ControlledCheckbox';
import useFormatMessage from 'hooks/useFormatMessage';
@@ -118,7 +118,9 @@ export default function PermissionSettings(props: PermissionSettingsProps) {
{action.subjects.includes(subject) && (
<ControlledCheckbox
name={`${fieldPrefix}.${action.key}.conditions.${condition.key}`}
dataTest={`${condition.key}-${action.key.toLowerCase()}-checkbox`}
dataTest={`${
condition.key
}-${action.key.toLowerCase()}-checkbox`}
defaultValue={defaultChecked}
disabled={
getValues(

View File

@@ -1,4 +1,4 @@
import type { IStep } from '@automatisch/types';
import type { IStep } from 'types';
import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore';
import Box from '@mui/material/Box';
@@ -20,7 +20,7 @@ type SuggestionsProps = {
data: {
id: string;
name: string;
output: Record<string, unknown>[]
output: Record<string, unknown>[];
}[];
onSuggestionClick: (variable: any) => void;
};
@@ -31,69 +31,73 @@ const LIST_ITEM_HEIGHT = 64;
const computeListHeight = (currentLength: number) => {
const numberOfRenderedItems = Math.min(SHORT_LIST_LENGTH, currentLength);
return LIST_ITEM_HEIGHT * numberOfRenderedItems;
}
};
const getPartialArray = (array: any[], length = array.length) => {
return array.slice(0, length);
};
const renderItemFactory = ({ onSuggestionClick }: Pick<SuggestionsProps, 'onSuggestionClick'>) => (props: ListChildComponentProps) => {
const { index, style, data } = props;
const renderItemFactory =
({ onSuggestionClick }: Pick<SuggestionsProps, 'onSuggestionClick'>) =>
(props: ListChildComponentProps) => {
const { index, style, data } = props;
const suboption = data[index];
const suboption = data[index];
return (
<ListItemButton
sx={{ pl: 4 }}
divider
onClick={() => onSuggestionClick(suboption)}
data-test="power-input-suggestion-item"
key={index}
style={style}
>
<ListItemText
primary={suboption.label}
primaryTypographyProps={{
variant: 'subtitle1',
title: 'Property name',
sx: { fontWeight: 700 },
}}
secondary={suboption.sampleValue || ''}
secondaryTypographyProps={{
variant: 'subtitle2',
title: 'Sample value',
noWrap: true,
}}
/>
</ListItemButton>
);
}
return (
<ListItemButton
sx={{ pl: 4 }}
divider
onClick={() => onSuggestionClick(suboption)}
data-test="power-input-suggestion-item"
key={index}
style={style}
>
<ListItemText
primary={suboption.label}
primaryTypographyProps={{
variant: 'subtitle1',
title: 'Property name',
sx: { fontWeight: 700 },
}}
secondary={suboption.sampleValue || ''}
secondaryTypographyProps={{
variant: 'subtitle2',
title: 'Sample value',
noWrap: true,
}}
/>
</ListItemButton>
);
};
const Suggestions = (props: SuggestionsProps) => {
const formatMessage = useFormatMessage();
const {
data,
onSuggestionClick = () => null
} = props;
const { data, onSuggestionClick = () => null } = props;
const [current, setCurrent] = React.useState<number | null>(0);
const [listLength, setListLength] = React.useState<number>(SHORT_LIST_LENGTH);
const [filteredData, setFilteredData] = React.useState<any[]>(
data
const [filteredData, setFilteredData] = React.useState<any[]>(data);
React.useEffect(
function syncOptions() {
setFilteredData((filteredData) => {
if (filteredData.length === 0 && filteredData.length !== data.length) {
return data;
}
return filteredData;
});
},
[data]
);
React.useEffect(function syncOptions() {
setFilteredData((filteredData) => {
if (filteredData.length === 0 && filteredData.length !== data.length) {
return data;
}
return filteredData;
})
}, [data]);
const renderItem = React.useMemo(() => renderItemFactory({
onSuggestionClick
}), [onSuggestionClick]);
const renderItem = React.useMemo(
() =>
renderItemFactory({
onSuggestionClick,
}),
[onSuggestionClick]
);
const expandList = () => {
setListLength(Infinity);
@@ -122,12 +126,12 @@ const Suggestions = (props: SuggestionsProps) => {
return {
id: stepWithOutput.id,
name: stepWithOutput.name,
output: stepWithOutput.output
.filter(option => `${option.label}\n${option.sampleValue}`
output: stepWithOutput.output.filter((option) =>
`${option.label}\n${option.sampleValue}`
.toLowerCase()
.includes(search.toLowerCase())
)
}
),
};
})
.filter((stepWithOutput) => stepWithOutput.output.length);
@@ -161,14 +165,27 @@ const Suggestions = (props: SuggestionsProps) => {
(current === index ? <ExpandLess /> : <ExpandMore />)}
</ListItemButton>
<Collapse in={current === index || filteredData.length === 1} timeout="auto" unmountOnExit>
<Collapse
in={current === index || filteredData.length === 1}
timeout="auto"
unmountOnExit
>
<FixedSizeList
height={computeListHeight(getPartialArray((option.output as any) || [], listLength).length)}
height={computeListHeight(
getPartialArray((option.output as any) || [], listLength)
.length
)}
width="100%"
itemSize={LIST_ITEM_HEIGHT}
itemCount={getPartialArray((option.output as any) || [], listLength).length}
itemCount={
getPartialArray((option.output as any) || [], listLength)
.length
}
overscanCount={2}
itemData={getPartialArray((option.output as any) || [], listLength)}
itemData={getPartialArray(
(option.output as any) || [],
listLength
)}
data-test="power-input-suggestion-group"
>
{renderItem}

View File

@@ -1,4 +1,4 @@
import type { IStep } from '@automatisch/types';
import type { IStep } from 'types';
const joinBy = (delimiter = '.', ...args: string[]) =>
args.filter(Boolean).join(delimiter);
@@ -10,7 +10,12 @@ type TProcessPayload = {
parentLabel?: string;
};
const process = ({ data, parentKey, index, parentLabel = '' }: TProcessPayload): any[] => {
const process = ({
data,
parentKey,
index,
parentLabel = '',
}: TProcessPayload): any[] => {
if (typeof data !== 'object') {
return [
{
@@ -24,27 +29,19 @@ const process = ({ data, parentKey, index, parentLabel = '' }: TProcessPayload):
const entries = Object.entries(data);
return entries.flatMap(([name, sampleValue]) => {
const label = joinBy(
'.',
parentLabel,
(index as number)?.toString(),
name
);
const label = joinBy('.', parentLabel, (index as number)?.toString(), name);
const value = joinBy(
'.',
parentKey,
(index as number)?.toString(),
name
);
const value = joinBy('.', parentKey, (index as number)?.toString(), name);
if (Array.isArray(sampleValue)) {
return sampleValue.flatMap((item, index) => process({
data: item,
parentKey: value,
index,
parentLabel: label
}));
return sampleValue.flatMap((item, index) =>
process({
data: item,
parentKey: value,
index,
parentLabel: label,
})
);
}
if (typeof sampleValue === 'object' && sampleValue !== null) {
@@ -77,8 +74,9 @@ export const processStepWithExecutions = (steps: IStep[]): any[] => {
.map((step: IStep, index: number) => ({
id: step.id,
// TODO: replace with step.name once introduced
name: `${index + 1}. ${(step.appKey || '').charAt(0)?.toUpperCase() + step.appKey?.slice(1)
}`,
name: `${index + 1}. ${
(step.appKey || '').charAt(0)?.toUpperCase() + step.appKey?.slice(1)
}`,
output: process({
data: step.executionSteps?.[0]?.dataOut || {},
parentKey: `step.${step.id}`,

View File

@@ -3,7 +3,7 @@ import throttle from 'lodash/throttle';
import isEmpty from 'lodash/isEmpty';
import { Box, Typography } from '@mui/material';
import { IJSONObject } from '@automatisch/types';
import { IJSONObject } from 'types';
import JSONViewer from 'components/JSONViewer';
import SearchInput from 'components/SearchInput';
import useFormatMessage from 'hooks/useFormatMessage';

View File

@@ -1,7 +1,7 @@
import { Text, Descendant } from 'slate';
import { withHistory } from 'slate-history';
import { ReactEditor, withReact } from 'slate-react';
import { IFieldDropdownOption } from '@automatisch/types';
import { IFieldDropdownOption } from 'types';
import type {
CustomEditor,

View File

@@ -13,7 +13,7 @@ import { EXECUTE_FLOW } from 'graphql/mutations/execute-flow';
import JSONViewer from 'components/JSONViewer';
import WebhookUrlInfo from 'components/WebhookUrlInfo';
import FlowSubstepTitle from 'components/FlowSubstepTitle';
import type { IStep, ISubstep } from '@automatisch/types';
import type { IStep, ISubstep } from 'types';
type TestSubstepProps = {
substep: ISubstep;
@@ -62,7 +62,7 @@ function TestSubstep(props: TestSubstepProps): React.ReactElement {
EXECUTE_FLOW,
{
refetchQueries: ['GetStepWithTestExecutions'],
context: { autoSnackbar: false }
context: { autoSnackbar: false },
}
);
const response = data?.executeFlow?.data;

View File

@@ -5,7 +5,7 @@ import get from 'lodash/get';
import set from 'lodash/set';
import * as React from 'react';
import { IJSONObject } from '@automatisch/types';
import { IJSONObject } from 'types';
import useConfig from 'hooks/useConfig';
import useAutomatischInfo from 'hooks/useAutomatischInfo';
import { defaultTheme, mationTheme } from 'styles/theme';

View File

@@ -11,7 +11,7 @@ import Divider from '@mui/material/Divider';
import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography';
import { TBillingCardAction } from '@automatisch/types';
import { TBillingCardAction } from 'types';
import TrialOverAlert from 'components/TrialOverAlert/index.ee';
import SubscriptionCancelledAlert from 'components/SubscriptionCancelledAlert/index.ee';
import CheckoutCompletedAlert from 'components/CheckoutCompletedAlert/index.ee';

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import type { IStep } from '@automatisch/types';
import type { IStep } from 'types';
export const StepExecutionsContext = React.createContext<IStep[]>([]);

View File

@@ -1,4 +1,4 @@
import type { IAuthenticationStep, IJSONObject } from '@automatisch/types';
import type { IAuthenticationStep, IJSONObject } from 'types';
import apolloClient from 'graphql/client';
import MUTATIONS from 'graphql/mutations';

View File

@@ -1,5 +1,5 @@
import template from 'lodash/template';
import type { IAuthenticationStepField, IJSONObject } from '@automatisch/types';
import type { IAuthenticationStepField, IJSONObject } from 'types';
const interpolate = /{([\s\S]+?)}/g;

View File

@@ -1,4 +1,4 @@
import { IRole, IPermission } from '@automatisch/types';
import { IRole, IPermission } from 'types';
type ComputeAction = {
conditions: Record<string, boolean>;

View File

@@ -1,5 +1,5 @@
import template from 'lodash/template';
import type { IAuthenticationStepField, IJSONObject } from '@automatisch/types';
import type { IAuthenticationStepField, IJSONObject } from 'types';
const interpolate = /{([\s\S]+?)}/g;

View File

@@ -1,4 +1,4 @@
import { IJSONObject } from '@automatisch/types';
import { IJSONObject } from 'types';
import set from 'lodash/set';
export default function nestObject<T = IJSONObject>(

View File

@@ -1,5 +1,9 @@
import { PureAbility, fieldPatternMatcher, mongoQueryMatcher } from '@casl/ability';
import { IUser } from '@automatisch/types';
import {
PureAbility,
fieldPatternMatcher,
mongoQueryMatcher,
} from '@casl/ability';
import { IUser } from 'types';
// Must be kept in sync with `packages/backend/src/helpers/user-ability.ts`!
export default function userAbility(user: IUser) {
@@ -9,7 +13,7 @@ export default function userAbility(user: IUser) {
// We're not using mongo, but our fields, conditions match
const options = {
conditionsMatcher: mongoQueryMatcher,
fieldMatcher: fieldPatternMatcher
fieldMatcher: fieldPatternMatcher,
};
if (!role || !permissions) {

View File

@@ -1,22 +1,16 @@
import { useQuery } from '@apollo/client';
import { IApp } from '@automatisch/types';
import { IApp } from 'types';
import { GET_APP } from 'graphql/queries/get-app';
type QueryResponse = {
getApp: IApp;
}
};
export default function useApp(key: string) {
const {
data,
loading
} = useQuery<QueryResponse>(
GET_APP,
{
variables: { key }
}
);
const { data, loading } = useQuery<QueryResponse>(GET_APP, {
variables: { key },
});
const app = data?.getApp;
return {

View File

@@ -1,5 +1,5 @@
import { useLazyQuery } from '@apollo/client';
import { AppAuthClient } from '@automatisch/types';
import { AppAuthClient } from 'types';
import * as React from 'react';
import { GET_APP_AUTH_CLIENT } from 'graphql/queries/get-app-auth-client.ee';

View File

@@ -1,5 +1,5 @@
import { useLazyQuery } from '@apollo/client';
import { AppAuthClient } from '@automatisch/types';
import { AppAuthClient } from 'types';
import * as React from 'react';
import { GET_APP_AUTH_CLIENTS } from 'graphql/queries/get-app-auth-clients.ee';

View File

@@ -1,23 +1,17 @@
import { useQuery } from '@apollo/client';
import { AppConfig } from '@automatisch/types';
import { AppConfig } from 'types';
import { GET_APP_CONFIG } from 'graphql/queries/get-app-config.ee';
type QueryResponse = {
getAppConfig: AppConfig;
}
};
export default function useAppConfig(key: string) {
const {
data,
loading
} = useQuery<QueryResponse>(
GET_APP_CONFIG,
{
variables: { key },
context: { autoSnackbar: false }
}
);
const { data, loading } = useQuery<QueryResponse>(GET_APP_CONFIG, {
variables: { key },
context: { autoSnackbar: false },
});
const appConfig = data?.getAppConfig;
return {

View File

@@ -1,5 +1,5 @@
import { useQuery } from '@apollo/client';
import { IApp } from '@automatisch/types';
import { IApp } from 'types';
import { GET_APPS } from 'graphql/queries/get-apps';

View File

@@ -1,4 +1,4 @@
import { IApp } from '@automatisch/types';
import { IApp } from 'types';
import * as React from 'react';
import { processStep } from 'helpers/authenticationSteps';
@@ -10,14 +10,18 @@ type UseAuthenticateAppParams = {
appAuthClientId?: string;
useShared?: boolean;
connectionId?: string;
}
};
type AuthenticatePayload = {
fields?: Record<string, string>;
appAuthClientId?: string;
}
};
function getSteps(auth: IApp['auth'], hasConnection: boolean, useShared: boolean) {
function getSteps(
auth: IApp['auth'],
hasConnection: boolean,
useShared: boolean
) {
if (hasConnection) {
if (useShared) {
return auth?.sharedReconnectionSteps;
@@ -34,26 +38,17 @@ function getSteps(auth: IApp['auth'], hasConnection: boolean, useShared: boolean
}
export default function useAuthenticateApp(payload: UseAuthenticateAppParams) {
const {
appKey,
appAuthClientId,
connectionId,
useShared = false,
} = payload;
const { appKey, appAuthClientId, connectionId, useShared = false } = payload;
const { app } = useApp(appKey);
const [
authenticationInProgress,
setAuthenticationInProgress
] = React.useState(false);
const [authenticationInProgress, setAuthenticationInProgress] =
React.useState(false);
const steps = getSteps(app?.auth, !!connectionId, useShared);
const authenticate = React.useMemo(() => {
if (!steps?.length) return;
return async function authenticate(payload: AuthenticatePayload = {}) {
const {
fields,
} = payload;
const { fields } = payload;
setAuthenticationInProgress(true);
const response: Record<string, any> = {
@@ -62,7 +57,7 @@ export default function useAuthenticateApp(payload: UseAuthenticateAppParams) {
connection: {
id: connectionId,
},
fields
fields,
};
let stepIndex = 0;
@@ -90,7 +85,7 @@ export default function useAuthenticateApp(payload: UseAuthenticateAppParams) {
setAuthenticationInProgress(false);
}
}
};
}, [steps, appKey, appAuthClientId, connectionId]);
return {

View File

@@ -2,15 +2,21 @@ import * as React from 'react';
import { useQuery } from '@apollo/client';
import { useLocation } from 'react-router-dom';
import { DateTime } from 'luxon';
import { TSubscription } from '@automatisch/types';
import { TSubscription } from 'types';
import { GET_BILLING_AND_USAGE } from 'graphql/queries/get-billing-and-usage.ee';
function transform(billingAndUsageData: NonNullable<UseBillingAndUsageDataReturn>) {
function transform(
billingAndUsageData: NonNullable<UseBillingAndUsageDataReturn>
) {
const nextBillDate = billingAndUsageData.subscription.nextBillDate;
const nextBillDateTitle = nextBillDate.title;
const nextBillDateTitleDateObject = DateTime.fromMillis(Number(nextBillDateTitle));
const formattedNextBillDateTitle = nextBillDateTitleDateObject.isValid ? nextBillDateTitleDateObject.toFormat('LLL dd, yyyy') : nextBillDateTitle;
const nextBillDateTitleDateObject = DateTime.fromMillis(
Number(nextBillDateTitle)
);
const formattedNextBillDateTitle = nextBillDateTitleDateObject.isValid
? nextBillDateTitleDateObject.toFormat('LLL dd, yyyy')
: nextBillDateTitle;
return {
...billingAndUsageData,
@@ -19,8 +25,8 @@ function transform(billingAndUsageData: NonNullable<UseBillingAndUsageDataReturn
nextBillDate: {
...billingAndUsageData.subscription.nextBillDate,
title: formattedNextBillDateTitle,
}
}
},
},
};
}
@@ -28,27 +34,35 @@ type UseBillingAndUsageDataReturn = {
subscription: TSubscription;
usage: {
task: number;
}
};
} | null;
export default function useBillingAndUsageData(): UseBillingAndUsageDataReturn {
const location = useLocation();
const state = location.state as { checkoutCompleted: boolean };
const { data, loading, startPolling, stopPolling } = useQuery(GET_BILLING_AND_USAGE);
const { data, loading, startPolling, stopPolling } = useQuery(
GET_BILLING_AND_USAGE
);
const checkoutCompleted = state?.checkoutCompleted;
const hasSubscription = !!data?.getBillingAndUsage?.subscription?.status;
React.useEffect(function pollDataUntilSubscriptionIsCreated() {
if (checkoutCompleted && !hasSubscription) {
startPolling(1000);
}
}, [checkoutCompleted, hasSubscription, startPolling]);
React.useEffect(
function pollDataUntilSubscriptionIsCreated() {
if (checkoutCompleted && !hasSubscription) {
startPolling(1000);
}
},
[checkoutCompleted, hasSubscription, startPolling]
);
React.useEffect(function stopPollingWhenSubscriptionIsCreated() {
if (checkoutCompleted && hasSubscription) {
stopPolling();
}
}, [checkoutCompleted, hasSubscription, stopPolling]);
React.useEffect(
function stopPollingWhenSubscriptionIsCreated() {
if (checkoutCompleted && hasSubscription) {
stopPolling();
}
},
[checkoutCompleted, hasSubscription, stopPolling]
);
if (loading) return null;

View File

@@ -1,14 +1,16 @@
import { useQuery } from '@apollo/client';
import { IJSONObject } from '@automatisch/types';
import { IJSONObject } from 'types';
import { GET_CONFIG } from 'graphql/queries/get-config.ee';
type QueryResponse = {
getConfig: IJSONObject;
}
};
export default function useConfig(keys?: string[]) {
const { data, loading } = useQuery<QueryResponse>(GET_CONFIG, { variables: { keys } });
const { data, loading } = useQuery<QueryResponse>(GET_CONFIG, {
variables: { keys },
});
return {
config: data?.getConfig,

View File

@@ -1,5 +1,5 @@
import { useQuery } from '@apollo/client';
import { IUser } from '@automatisch/types';
import { IUser } from 'types';
import { GET_CURRENT_USER } from 'graphql/queries/get-current-user';

View File

@@ -4,11 +4,7 @@ import { useFormContext } from 'react-hook-form';
import set from 'lodash/set';
import type { UseFormReturn } from 'react-hook-form';
import isEqual from 'lodash/isEqual';
import type {
IField,
IFieldDropdownSource,
IJSONObject,
} from '@automatisch/types';
import type { IField, IFieldDropdownSource, IJSONObject } from 'types';
import { GET_DYNAMIC_DATA } from 'graphql/queries/get-dynamic-data';

View File

@@ -8,7 +8,7 @@ import type {
IField,
IFieldDropdownAdditionalFields,
IJSONObject,
} from '@automatisch/types';
} from 'types';
import { GET_DYNAMIC_FIELDS } from 'graphql/queries/get-dynamic-fields';

View File

@@ -1,10 +1,10 @@
import { useQuery } from '@apollo/client';
import { TInvoice } from '@automatisch/types';
import { TInvoice } from 'types';
import { GET_INVOICES } from 'graphql/queries/get-invoices.ee';
type UseInvoicesReturn = {
invoices: TInvoice[],
invoices: TInvoice[];
loading: boolean;
};
@@ -13,6 +13,6 @@ export default function useInvoices(): UseInvoicesReturn {
return {
invoices: data?.getInvoices || [],
loading: loading
loading: loading,
};
}

View File

@@ -1,12 +1,12 @@
import { useQuery } from '@apollo/client';
import type { Notification } from '@automatisch/types';
import type { Notification } from 'types';
import { GET_NOTIFICATIONS } from 'graphql/queries/get-notifications';
type UseNotificationsReturn = {
notifications: Notification[];
loading: boolean;
}
};
export default function useNotifications(): UseNotificationsReturn {
const { data, loading } = useQuery(GET_NOTIFICATIONS);

View File

@@ -1,6 +1,6 @@
import { useQuery } from '@apollo/client';
import { TPaymentPlan } from '@automatisch/types';
import { TPaymentPlan } from 'types';
import { GET_PAYMENT_PLANS } from 'graphql/queries/get-payment-plans.ee';
type UsePaymentPlansReturn = {
@@ -13,6 +13,6 @@ export default function usePaymentPlans(): UsePaymentPlansReturn {
return {
plans: data?.getPaymentPlans || [],
loading
loading,
};
}

View File

@@ -1,5 +1,5 @@
import { useQuery } from '@apollo/client';
import { IPermissionCatalog } from '@automatisch/types';
import { IPermissionCatalog } from 'types';
import { GET_PERMISSION_CATALOG } from 'graphql/queries/get-permission-catalog.ee';

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import { useLazyQuery } from '@apollo/client';
import { IRole } from '@automatisch/types';
import { IRole } from 'types';
import { GET_ROLE } from 'graphql/queries/get-role.ee';

View File

@@ -1,17 +1,19 @@
import { useQuery } from '@apollo/client';
import { IRole } from '@automatisch/types';
import { IRole } from 'types';
import { GET_ROLES } from 'graphql/queries/get-roles.ee';
type QueryResponse = {
getRoles: IRole[];
}
};
export default function useRoles() {
const { data, loading } = useQuery<QueryResponse>(GET_ROLES, { context: { autoSnackbar: false } });
const { data, loading } = useQuery<QueryResponse>(GET_ROLES, {
context: { autoSnackbar: false },
});
return {
roles: data?.getRoles || [],
loading
loading,
};
}

View File

@@ -1,6 +1,6 @@
import { QueryResult, useQuery } from '@apollo/client';
import { TSamlAuthProvider } from '@automatisch/types';
import { TSamlAuthProvider } from 'types';
import { GET_SAML_AUTH_PROVIDER } from 'graphql/queries/get-saml-auth-provider';
type UseSamlAuthProviderReturn = {

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import { useLazyQuery } from '@apollo/client';
import { TSamlAuthProviderRole } from '@automatisch/types';
import { TSamlAuthProviderRole } from 'types';
import { GET_SAML_AUTH_PROVIDER_ROLE_MAPPINGS } from 'graphql/queries/get-saml-auth-provider-role-mappings';

View File

@@ -1,6 +1,6 @@
import { useQuery } from '@apollo/client';
import { TSamlAuthProvider } from '@automatisch/types';
import { TSamlAuthProvider } from 'types';
import { LIST_SAML_AUTH_PROVIDERS } from 'graphql/queries/list-saml-auth-providers.ee';
type UseSamlAuthProvidersReturn = {

View File

@@ -1,12 +1,12 @@
import * as React from 'react';
import { useLazyQuery } from '@apollo/client';
import { IUser } from '@automatisch/types';
import { IUser } from 'types';
import { GET_USER } from 'graphql/queries/get-user';
type QueryResponse = {
getUser: IUser;
}
};
export default function useUser(userId?: string) {
const [getUser, { data, loading }] = useLazyQuery<QueryResponse>(GET_USER);
@@ -15,14 +15,14 @@ export default function useUser(userId?: string) {
if (userId) {
getUser({
variables: {
id: userId
}
id: userId,
},
});
}
}, [userId]);
return {
user: data?.getUser,
loading
loading,
};
}

View File

@@ -1,5 +1,5 @@
import { useQuery } from '@apollo/client';
import { IUser } from '@automatisch/types';
import { IUser } from 'types';
import { GET_USERS } from 'graphql/queries/get-users';

View File

@@ -3,7 +3,7 @@ import Grid from '@mui/material/Grid';
import CircularProgress from '@mui/material/CircularProgress';
import Divider from '@mui/material/Divider';
import { useQuery } from '@apollo/client';
import { IApp } from '@automatisch/types';
import { IApp } from 'types';
import PageTitle from 'components/PageTitle';
import Container from 'components/Container';

View File

@@ -6,7 +6,7 @@ import Grid from '@mui/material/Grid';
import Divider from '@mui/material/Divider';
import CircularProgress from '@mui/material/CircularProgress';
import AddIcon from '@mui/icons-material/Add';
import type { IApp } from '@automatisch/types';
import type { IApp } from 'types';
import Can from 'components/Can';
import NoResultFound from 'components/NoResultFound';

View File

@@ -1,5 +1,5 @@
import { useMutation } from '@apollo/client';
import { TSamlAuthProvider, TSamlAuthProviderRole } from '@automatisch/types';
import { TSamlAuthProvider, TSamlAuthProviderRole } from 'types';
import LoadingButton from '@mui/lab/LoadingButton';
import Divider from '@mui/material/Divider';
import Stack from '@mui/material/Stack';
@@ -65,8 +65,8 @@ function RoleMappings({ provider, providerLoading }: RoleMappingsProps) {
enqueueSnackbar(formatMessage('roleMappingsForm.successfullySaved'), {
variant: 'success',
SnackbarProps: {
'data-test': 'snackbar-update-role-mappings-success'
}
'data-test': 'snackbar-update-role-mappings-success',
},
});
}
} catch (error) {

View File

@@ -1,5 +1,5 @@
import { useFieldArray, useFormContext } from 'react-hook-form';
import { IRole } from '@automatisch/types';
import { IRole } from 'types';
import MuiTextField from '@mui/material/TextField';
import Stack from '@mui/material/Stack';
import DeleteIcon from '@mui/icons-material/Delete';

View File

@@ -1,5 +1,5 @@
import { QueryResult, useMutation } from '@apollo/client';
import { IRole, TSamlAuthProvider } from '@automatisch/types';
import { IRole, TSamlAuthProvider } from 'types';
import LoadingButton from '@mui/lab/LoadingButton';
import Stack from '@mui/material/Stack';
import MuiTextField from '@mui/material/TextField';
@@ -94,8 +94,8 @@ function SamlConfiguration({
enqueueSnackbar(formatMessage('authenticationForm.successfullySaved'), {
variant: 'success',
SnackbarProps: {
'data-test': 'snackbar-save-saml-provider-success'
}
'data-test': 'snackbar-save-saml-provider-success',
},
});
} catch (error) {
throw new Error('Failed while saving!');

View File

@@ -1,5 +1,5 @@
import { useMutation } from '@apollo/client';
import { IRole, IUser } from '@automatisch/types';
import { IRole, IUser } from 'types';
import LoadingButton from '@mui/lab/LoadingButton';
import Grid from '@mui/material/Grid';
import Stack from '@mui/material/Stack';

View File

@@ -1,5 +1,5 @@
import { useMutation } from '@apollo/client';
import { IRole, IUser } from '@automatisch/types';
import { IRole, IUser } from 'types';
import LoadingButton from '@mui/lab/LoadingButton';
import Grid from '@mui/material/Grid';
import Skeleton from '@mui/material/Skeleton';

View File

@@ -5,7 +5,7 @@ import Grid from '@mui/material/Grid';
import Box from '@mui/material/Box';
import AlertTitle from '@mui/material/AlertTitle';
import Alert from '@mui/material/Alert';
import type { IExecutionStep } from '@automatisch/types';
import type { IExecutionStep } from 'types';
import useFormatMessage from 'hooks/useFormatMessage';
import ExecutionHeader from 'components/ExecutionHeader';

View File

@@ -7,7 +7,7 @@ import CircularProgress from '@mui/material/CircularProgress';
import Divider from '@mui/material/Divider';
import Pagination from '@mui/material/Pagination';
import PaginationItem from '@mui/material/PaginationItem';
import type { IExecution } from '@automatisch/types';
import type { IExecution } from 'types';
import NoResultFound from 'components/NoResultFound';
import ExecutionRow from 'components/ExecutionRow';

View File

@@ -9,7 +9,7 @@ import CircularProgress from '@mui/material/CircularProgress';
import Divider from '@mui/material/Divider';
import Pagination from '@mui/material/Pagination';
import PaginationItem from '@mui/material/PaginationItem';
import type { IFlow } from '@automatisch/types';
import type { IFlow } from 'types';
import Can from 'components/Can';
import FlowRow from 'components/FlowRow';

493
packages/web/src/types/index.d.ts vendored Normal file
View File

@@ -0,0 +1,493 @@
import type { AxiosInstance, AxiosRequestConfig } from 'axios';
export type IHttpClient = AxiosInstance;
import type { Request } from 'express';
// Type definitions for automatisch
export type IJSONValue =
| string
| number
| boolean
| null
| IJSONObject
| IJSONArray;
export type IJSONArray = Array<IJSONValue>;
export interface IJSONObject {
[x: string]: IJSONValue;
}
export interface IConnection {
id: string;
key: string;
data: string;
formattedData?: IJSONObject;
userId: string;
verified: boolean;
count?: number;
flowCount?: number;
appData?: IApp;
createdAt: string;
reconnectable?: boolean;
appAuthClientId?: string;
}
export interface IExecutionStep {
id: string;
executionId: string;
stepId: IStep['id'];
step: IStep;
dataIn: IJSONObject;
dataOut: IJSONObject;
errorDetails: IJSONObject;
status: string;
createdAt: string;
updatedAt: string;
}
export interface IExecution {
id: string;
flowId: string;
flow: IFlow;
testRun: boolean;
status: 'success' | 'failure';
executionSteps: IExecutionStep[];
updatedAt: string | Date;
createdAt: string | Date;
}
export interface IStep {
id: string;
name?: string;
flowId: string;
key?: string;
appKey?: string;
iconUrl: string;
webhookUrl?: string;
type: 'action' | 'trigger';
connectionId?: string;
status: string;
position: number;
parameters: IJSONObject;
connection?: Partial<IConnection>;
flow: IFlow;
executionSteps: IExecutionStep[];
// FIXME: remove this property once execution steps are properly exposed via queries
output?: IJSONObject;
appData?: IApp;
}
export interface IFlow {
id: string;
name: string;
userId: string;
active: boolean;
status: 'paused' | 'published' | 'draft';
steps: IStep[];
createdAt: string | Date;
updatedAt: string | Date;
remoteWebhookId: string;
lastInternalId: () => Promise<string>;
}
export interface IUser {
id: string;
fullName: string;
email: string;
password: string;
connections: IConnection[];
flows: IFlow[];
steps: IStep[];
role: IRole;
permissions: IPermission[];
createdAt: string | Date;
updatedAt: string | Date;
trialExpiryDate: string | Date;
}
export interface IRole {
id: string;
key: string;
name: string;
description: string;
isAdmin: boolean;
permissions: IPermission[];
}
export interface IPermission {
id: string;
action: string;
subject: string;
conditions: string[];
}
export interface IPermissionCatalog {
actions: { label: string; key: string; subjects: string[] }[];
subjects: { label: string; key: string }[];
conditions: { label: string; key: string }[];
}
export interface IConfig {
id: string;
key: string;
value: IJSONObject;
}
export interface IFieldDropdown {
key: string;
label: string;
type: 'dropdown';
required: boolean;
readOnly?: boolean;
value?: string | boolean;
placeholder?: string | null;
description?: string;
docUrl?: string;
clickToCopy?: boolean;
variables?: boolean;
dependsOn?: string[];
options?: IFieldDropdownOption[];
source?: IFieldDropdownSource;
additionalFields?: IFieldDropdownAdditionalFields;
}
export interface IFieldDropdownSource {
type: string;
name: string;
arguments: {
name: string;
value: string;
}[];
}
export interface IFieldDropdownAdditionalFields {
type: string;
name: string;
arguments: {
name: string;
value: string;
}[];
}
export interface IFieldDropdownOption {
label: string;
value: boolean | string | number;
}
export interface IFieldText {
key: string;
label: string;
type: 'string';
required?: boolean;
readOnly?: boolean;
value?: string;
placeholder?: string | null;
description?: string;
docUrl?: string;
clickToCopy?: boolean;
variables?: boolean;
dependsOn?: string[];
}
export interface IFieldDynamic {
key: string;
label: string;
type: 'dynamic';
required?: boolean;
readOnly?: boolean;
description?: string;
value?: Record<string, unknown>[];
fields: (IFieldDropdown | IFieldText)[];
}
export type IField = IFieldDropdown | IFieldText | IFieldDynamic;
export interface IAuthenticationStepField {
name: string;
value: string | null;
properties?: {
name: string;
value: string;
}[];
}
export interface IAuthenticationStep {
type: 'mutation' | 'openWithPopup';
name: string;
arguments: IAuthenticationStepField[];
}
export interface IApp {
name: string;
key: string;
iconUrl: string;
docUrl?: string;
authDocUrl: string;
primaryColor: string;
supportsConnections: boolean;
apiBaseUrl: string;
baseUrl: string;
auth?: IAuth;
connectionCount?: number;
flowCount?: number;
beforeRequest?: TBeforeRequest[];
dynamicData?: IDynamicData;
dynamicFields?: IDynamicFields;
triggers?: ITrigger[];
actions?: IAction[];
connections?: IConnection[];
}
export type TBeforeRequest = {
($: IGlobalVariable, requestConfig: AxiosRequestConfig): AxiosRequestConfig;
};
export interface IDynamicData {
[index: string]: any;
}
export interface IDynamicFields {
[index: string]: any;
}
export interface IAuth {
generateAuthUrl?($: IGlobalVariable): Promise<void>;
verifyCredentials?($: IGlobalVariable): Promise<void>;
isStillVerified?($: IGlobalVariable): Promise<boolean>;
refreshToken?($: IGlobalVariable): Promise<void>;
verifyWebhook?($: IGlobalVariable): Promise<boolean>;
isRefreshTokenRequested?: boolean;
fields?: IField[];
authenticationSteps?: IAuthenticationStep[];
reconnectionSteps?: IAuthenticationStep[];
sharedAuthenticationSteps?: IAuthenticationStep[];
sharedReconnectionSteps?: IAuthenticationStep[];
}
export interface ITriggerOutput {
data: ITriggerItem[];
error?: IJSONObject;
}
export interface ITriggerItem {
raw: IJSONObject;
meta: {
internalId: string;
};
}
export interface IBaseTrigger {
name: string;
key: string;
type?: 'webhook' | 'polling';
showWebhookUrl?: boolean;
pollInterval?: number;
description: string;
useSingletonWebhook?: boolean;
singletonWebhookRefValueParameter?: string;
getInterval?(parameters: IStep['parameters']): string;
run?($: IGlobalVariable): Promise<void>;
testRun?($: IGlobalVariable): Promise<void>;
registerHook?($: IGlobalVariable): Promise<void>;
unregisterHook?($: IGlobalVariable): Promise<void>;
}
export interface IRawTrigger extends IBaseTrigger {
arguments?: IField[];
}
export interface ITrigger extends IBaseTrigger {
substeps?: ISubstep[];
}
export interface IActionOutput {
data: IActionItem;
error?: IJSONObject;
}
export interface IActionItem {
raw: IJSONObject;
}
export interface IBaseAction {
name: string;
key: string;
description: string;
run?($: IGlobalVariable): Promise<void>;
}
export interface IRawAction extends IBaseAction {
arguments?: IField[];
}
export interface IAction extends IBaseAction {
substeps?: ISubstep[];
}
export interface IAuthentication {
client: unknown;
verifyCredentials(): Promise<IJSONObject>;
isStillVerified(): Promise<boolean>;
}
export interface ISubstep {
key: string;
name: string;
arguments?: IField[];
}
export type IHttpClientParams = {
$: IGlobalVariable;
baseURL?: string;
beforeRequest?: TBeforeRequest[];
};
export type IGlobalVariable = {
auth: {
set: (args: IJSONObject) => Promise<null>;
data: IJSONObject;
};
app?: IApp;
http?: IHttpClient;
request?: IRequest;
flow?: {
id: string;
lastInternalId: string;
isAlreadyProcessed?: (internalId: string) => boolean;
remoteWebhookId?: string;
setRemoteWebhookId?: (remoteWebhookId: string) => Promise<void>;
};
step?: {
id: string;
appKey: string;
parameters: IJSONObject;
};
nextStep?: {
id: string;
appKey: string;
parameters: IJSONObject;
};
execution?: {
id: string;
testRun: boolean;
exit: () => void;
};
getLastExecutionStep?: () => Promise<IExecutionStep>;
webhookUrl?: string;
singletonWebhookUrl?: string;
triggerOutput?: ITriggerOutput;
actionOutput?: IActionOutput;
pushTriggerItem?: (triggerItem: ITriggerItem) => void;
setActionItem?: (actionItem: IActionItem) => void;
};
export type TPaymentPlan = {
price: string;
name: string;
limit: string;
productId: string;
};
export type TSubscription = {
status: string;
monthlyQuota: {
title: string;
action: BillingCardAction;
};
nextBillDate: {
title: string;
action: BillingCardAction;
};
nextBillAmount: {
title: string;
action: BillingCardAction;
};
};
type TBillingCardAction = TBillingTextCardAction | TBillingLinkCardAction;
type TBillingTextCardAction = {
type: 'text';
text: string;
};
type TBillingLinkCardAction = {
type: 'link';
text: string;
src: string;
};
type TInvoice = {
id: number;
amount: number;
currency: string;
payout_date: string;
receipt_url: string;
};
type TSamlAuthProvider = {
id: string;
name: string;
certificate: string;
signatureAlgorithm: 'sha1' | 'sha256' | 'sha512';
issuer: string;
entryPoint: string;
firstnameAttributeName: string;
surnameAttributeName: string;
emailAttributeName: string;
roleAttributeName: string;
defaultRoleId: string;
active: boolean;
loginUrl: string;
};
type TSamlAuthProviderRole = {
id: string;
samlAuthProviderId: string;
roleId: string;
remoteRoleName: string;
};
type AppConfig = {
id: string;
key: string;
allowCustomConnection: boolean;
canConnect: boolean;
canCustomConnect: boolean;
shared: boolean;
disabled: boolean;
};
type AppAuthClient = {
id: string;
name: string;
appConfigId: string;
authDefaults: string;
formattedAuthDefaults: IJSONObject;
active: boolean;
};
type Notification = {
name: string;
createdAt: string;
documentationUrl: string;
description: string;
};
declare module 'axios' {
interface AxiosResponse {
httpError?: IJSONObject;
}
interface AxiosRequestConfig {
additionalProperties?: Record<string, unknown>;
}
// ref: https://github.com/axios/axios/issues/5095
interface AxiosInstance {
create(config?: CreateAxiosDefaults): AxiosInstance;
}
}
export interface IRequest extends Request {
rawBody?: Buffer;
currentUser?: IUser;
}