refactor(web): replace CRA with Vite

This commit is contained in:
Ali BARIN
2025-04-28 16:16:11 +00:00
parent 1f6010bdea
commit f9772b6305
74 changed files with 2054 additions and 7400 deletions

View File

@@ -1,4 +0,0 @@
node_modules
build
source
.eslintrc.js

View File

@@ -1,10 +0,0 @@
module.exports = {
extends: [
'react-app',
'plugin:@tanstack/eslint-plugin-query/recommended',
'prettier',
],
rules: {
'react/prop-types': 'warn',
},
};

View File

@@ -1,6 +1,4 @@
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
# Getting Started
## Available Scripts
@@ -9,16 +7,11 @@ In the project directory, you can run:
### `yarn start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
Open [http://localhost:3001](http://localhost:3001) to view it in the browser.
The page will reload if you make edits.\
You will also see any lint errors in the console.
### `yarn test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `yarn build`
Builds the app for production to the `build` folder.\
@@ -28,19 +21,3 @@ The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `yarn eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).

View File

@@ -0,0 +1,39 @@
import js from '@eslint/js';
import prettier from 'eslint-config-prettier';
import reactPlugin from 'eslint-plugin-react';
import reactHooksPlugin from 'eslint-plugin-react-hooks';
import globals from 'globals';
export default [
js.configs.recommended,
{
files: ['**/*.{js,jsx}'],
languageOptions: {
ecmaVersion: 2022,
sourceType: 'module',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
globals: {
...globals.browser,
document: 'readonly',
window: 'readonly',
console: 'readonly',
JSX: true,
},
},
plugins: {
react: reactPlugin,
'react-hooks': reactHooksPlugin,
},
rules: {
...reactPlugin.configs.recommended.rules,
...reactHooksPlugin.configs.recommended.rules,
'react/prop-types': 'warn',
'react/react-in-jsx-scope': 'off',
},
},
prettier,
];

View File

@@ -71,7 +71,7 @@
transform: translateY(-40px);
}
</style>
</head>
<script type="module" src="src/index"></script></head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>

View File

@@ -1,6 +1,17 @@
{
"compilerOptions": {
"baseUrl": "src"
"baseUrl": "src",
"paths": {
"components/*": ["components/*"],
"config/*": ["config/*"],
"contexts/*": ["contexts/*"],
"helpers/*": ["helpers/*"],
"hooks/*": ["hooks/*"],
"locales/*": ["locales/*"],
"pages/*": ["pages/*"],
"propTypes/*": ["propTypes/*"],
"styles/*": ["styles/*"]
}
},
"include": ["src"]
}

View File

@@ -16,9 +16,6 @@
"@mui/material": "^5.11.10",
"@mui/x-date-pickers": "^7.28.0",
"@tanstack/react-query": "^5.24.1",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"@xyflow/react": "^12.4.4",
"axios": "^1.6.0",
"clipboard-copy": "^4.0.1",
@@ -29,29 +26,26 @@
"notistack": "^3.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.45.2",
"react-hook-form": "7.53.2",
"react-intl": "^5.20.12",
"react-json-tree": "^0.16.2",
"react-router-dom": "^6.0.2",
"react-scripts": "5.0.0",
"react-window": "^1.8.9",
"slate": "^0.94.1",
"slate-history": "^0.93.0",
"slate-react": "^0.94.2",
"slugify": "^1.6.6",
"uuid": "^9.0.0",
"web-vitals": "^1.0.1",
"yup": "^0.32.11"
},
"main": "index.js",
"scripts": {
"dev": "react-scripts start",
"build": "react-scripts build",
"build:watch": "yarn nodemon --exec react-scripts build --watch 'src/**/*.ts' --watch 'public/**/*' --ext ts,html",
"test": "react-scripts test",
"eject": "react-scripts eject",
"dev": "vite",
"build": "vite build",
"build:watch": "yarn nodemon --exec vite build --watch 'src/**/*.ts' --watch 'public/**/*' --ext ts,html",
"lint": "eslint src --ext .js,.jsx",
"prepack": "yarn build"
"prepack": "yarn build",
"preview": "vite preview"
},
"files": [
"/build"
@@ -85,16 +79,24 @@
"access": "public"
},
"devDependencies": {
"@tanstack/eslint-plugin-query": "^5.20.1",
"@emotion/babel-plugin": "^11.13.5",
"@svgr/core": "^8.1.0",
"@svgr/plugin-jsx": "^8.1.0",
"@tanstack/react-query-devtools": "^5.24.1",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"@vitejs/plugin-react": "^4.3.4",
"@welldone-software/why-did-you-render": "^8",
"eslint": "^9.25.1",
"eslint-config-prettier": "^9.1.0",
"eslint-config-react-app": "^7.0.1",
"prettier": "^3.2.5"
},
"eslintConfig": {
"extends": [
"./.eslintrc.js"
]
"eslint-plugin-react-hooks": "^5.2.0",
"globals": "^16.0.0",
"prettier": "^3.2.5",
"vite": "^6.1.0",
"vite-plugin-eslint": "^1.8.1",
"vite-tsconfig-paths": "^4.3.2"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}

View File

@@ -69,7 +69,7 @@ AccountDropdownMenu.propTypes = {
onClose: PropTypes.func.isRequired,
anchorEl: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
PropTypes.shape({ current: PropTypes.instanceOf(window.Element) }),
]),
id: PropTypes.string.isRequired,
};

View File

@@ -43,7 +43,7 @@ function AdminApplicationSettings({ appKey }) {
'data-test': 'snackbar-save-admin-apps-settings-success',
},
});
} catch (error) {
} catch {
throw new Error('Failed while saving!');
}
};

View File

@@ -37,18 +37,17 @@ function AdminApplicationUpdateOAuthClient(props) {
if (!adminOAuthClient) {
return;
}
const { name, active, ...formattedAuthDefaults } = values;
setAuthDefaultsUpdatePending(true);
await updateOAuthClient({
formattedAuthDefaults,
}).finally(() => {
await updateOAuthClient(values.formattedAuthDefaults).finally(() => {
setAuthDefaultsUpdatePending(false);
});
setDefaultValues((prev) => ({
name: prev.name,
active: prev.active,
...formattedAuthDefaults,
...values.formattedAuthDefaults,
}));
enqueueSnackbar(formatMessage('updateOAuthClient.success'), {

View File

@@ -24,7 +24,6 @@ import useCurrentUserAbility from 'hooks/useCurrentUserAbility';
import Footer from './Footer';
function createDrawerLinks({
canCreateFlows,
canReadRole,
canReadUser,
canUpdateConfig,

View File

@@ -57,7 +57,7 @@ AdminTemplateContextMenu.propTypes = {
onClose: PropTypes.func.isRequired,
anchorEl: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
PropTypes.shape({ current: PropTypes.instanceOf(window.Element) }),
]).isRequired,
};

View File

@@ -100,7 +100,7 @@ ContextMenu.propTypes = {
onMenuItemClick: PropTypes.func.isRequired,
anchorEl: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
PropTypes.shape({ current: PropTypes.instanceOf(window.Element) }),
]),
};

View File

@@ -43,7 +43,7 @@ function AppConnectionRow(props) {
{ connectionId: id },
{
onSettled: () => {
setTimeout(() => setVerificationVisible(false), 3000);
window.setTimeout(() => setVerificationVisible(false), 3000);
},
},
);

View File

@@ -17,8 +17,9 @@ import FlowSubstepTitle from 'components/FlowSubstepTitle';
import { StepPropType, SubstepPropType } from 'propTypes/propTypes';
import useTriggers from 'hooks/useTriggers';
import useActions from 'hooks/useActions';
import appConfig from 'config/app.js';
const useNewFlowEditor = process.env.REACT_APP_USE_NEW_FLOW_EDITOR === 'true';
const useNewFlowEditor = appConfig.useNewFlowEditor;
const optionGenerator = (app) => ({
label: app.name,

View File

@@ -24,8 +24,9 @@ import useAppConnections from 'hooks/useAppConnections';
import useTestConnection from 'hooks/useTestConnection';
import useOAuthClients from 'hooks/useOAuthClients';
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
import appConfig from 'config/app.js';
const useNewFlowEditor = process.env.REACT_APP_USE_NEW_FLOW_EDITOR === 'true';
const useNewFlowEditor = appConfig.useNewFlowEditor;
const ADD_CONNECTION_VALUE = 'ADD_CONNECTION';
const ADD_SHARED_CONNECTION_VALUE = 'ADD_SHARED_CONNECTION';

View File

@@ -20,7 +20,7 @@ function CodeEditor(props) {
'data-test': dataTest,
} = props;
const handleEditorOnMount = (editor, monaco) => {
const handleEditorOnMount = (editor) => {
editorRef.current = editor;
editor.onDidContentSizeChange((event) => {

View File

@@ -78,7 +78,7 @@ CustomOptions.propTypes = {
open: PropTypes.bool.isRequired,
anchorEl: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
PropTypes.shape({ current: PropTypes.instanceOf(window.Element) }),
]),
data: PropTypes.arrayOf(
PropTypes.shape({

View File

@@ -55,9 +55,10 @@ Item.propTypes = {
onOptionClick: PropTypes.func.isRequired,
};
const renderItemFactory =
({ onOptionClick }) =>
(props) => <Item onOptionClick={onOptionClick} {...props} />;
const renderItemFactory = ({ onOptionClick }) =>
function RenderItem(props) {
return <Item onOptionClick={onOptionClick} {...props} />;
};
const Options = (props) => {
const formatMessage = useFormatMessage();

View File

@@ -89,7 +89,7 @@ function ControlledCustomAutocomplete(props) {
};
const resizeObserver = React.useMemo(function syncCustomOptionsPosition() {
return new ResizeObserver(() => {
return new window.ResizeObserver(() => {
forceUpdate();
});
}, []);

View File

@@ -4,8 +4,6 @@ import { AdapterLuxon } from '@mui/x-date-pickers/AdapterLuxon';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import useFormatMessage from 'hooks/useFormatMessage';
export default function DatePickerInput({
onChange,
defaultValue = '',
@@ -15,7 +13,6 @@ export default function DatePickerInput({
maxDate,
}) {
const intl = useIntl();
const formatMessage = useFormatMessage();
const props = {
label,

View File

@@ -39,7 +39,9 @@ function DeleteApiTokenButton(props) {
},
},
);
} catch {}
} catch (error) {
console.error(error);
}
}, [deleteApiToken]);
const handleClose = () => {

View File

@@ -43,7 +43,9 @@ function DeleteRoleButton(props) {
'data-test': 'snackbar-delete-role-success',
},
});
} catch {}
} catch (error) {
console.error(error);
}
}, [deleteRole, enqueueSnackbar, formatMessage]);
const handleClose = () => {

View File

@@ -39,7 +39,9 @@ function DeleteUserButton(props) {
'data-test': 'snackbar-delete-user-success',
},
});
} catch {}
} catch (error) {
console.error(error);
}
}, [deleteUser]);
const handleClose = () => {

View File

@@ -12,7 +12,7 @@ import { Drawer as BaseDrawer } from './style';
const iOS =
typeof navigator !== 'undefined' &&
/iPad|iPhone|iPod/.test(navigator.userAgent);
/iPad|iPhone|iPod/.test(window.navigator.userAgent);
function Drawer(props) {
const { links = [], bottomLinks = [], ...drawerProps } = props;

View File

@@ -27,8 +27,9 @@ import useUpdateFlow from 'hooks/useUpdateFlow';
import useUpdateFlowStatus from 'hooks/useUpdateFlowStatus';
import FlowFolder from './FlowFolder';
import { TopBar } from './style';
import appConfig from 'config/app.js';
const useNewFlowEditor = process.env.REACT_APP_USE_NEW_FLOW_EDITOR === 'true';
const useNewFlowEditor = appConfig.useNewFlowEditor;
export default function EditorLayout() {
const { flowId } = useParams();
@@ -48,7 +49,7 @@ export default function EditorLayout() {
});
};
const onExportFlow = async (name) => {
const onExportFlow = async () => {
const flowExport = await exportFlow();
downloadJsonAsFile({

View File

@@ -17,7 +17,7 @@ export default function Edge({
const { flowActive, onStepAdd, isCreateStepPending } =
useContext(EdgesContext);
const [edgePath, labelX, labelY] = getStraightPath({
const [, labelX, labelY] = getStraightPath({
sourceX,
sourceY,
targetX,

View File

@@ -8,7 +8,7 @@ export const NodeWrapper = styled(Box)(({ theme }) => ({
padding: theme.spacing(0, 2.5),
}));
export const NodeInnerWrapper = styled(Box)(({ theme }) => ({
export const NodeInnerWrapper = styled(Box)(() => ({
maxWidth: 900,
flex: 1,
}));

View File

@@ -1,7 +1,7 @@
import { Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
export const EditorWrapper = styled(Stack)(({ theme }) => ({
export const EditorWrapper = styled(Stack)(() => ({
flexGrow: 1,
'& > div': {
flexGrow: 1,

View File

@@ -7,20 +7,16 @@ import {
InputLabel,
MenuItem,
Select,
Typography,
useMediaQuery,
} from '@mui/material';
import { useTheme } from '@mui/material/styles';
import { DateTime } from 'luxon';
import { useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import Can from 'components/Can';
import DatePickerInput from 'components/DatePickerInput';
import useExecutionFilters from 'hooks/useExecutionFilters';
import useFormatMessage from 'hooks/useFormatMessage';
export default function ExecutionFilters({ onFilterChange }) {
export default function ExecutionFilters() {
const theme = useTheme();
const formatMessage = useFormatMessage();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));

View File

@@ -185,7 +185,7 @@ ContextMenu.propTypes = {
onClose: PropTypes.func.isRequired,
anchorEl: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
PropTypes.shape({ current: PropTypes.instanceOf(window.Element) }),
]).isRequired,
onDuplicateFlow: PropTypes.func,
appKey: PropTypes.string,

View File

@@ -6,19 +6,17 @@ import {
InputLabel,
MenuItem,
Select,
Typography,
useMediaQuery,
Collapse,
} from '@mui/material';
import FilterAltIcon from '@mui/icons-material/FilterAlt';
import { useTheme } from '@mui/material/styles';
import Can from 'components/Can';
import useCurrentUserRuleConditions from 'hooks/useCurrentUserRuleConditions';
import useFlowFilters from 'hooks/useFlowFilters';
import useFormatMessage from 'hooks/useFormatMessage';
export default function FlowFilters({ onFilterChange }) {
export default function FlowFilters() {
const theme = useTheme();
const formatMessage = useFormatMessage();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));

View File

@@ -43,8 +43,9 @@ import useActions from 'hooks/useActions';
import useTriggerSubsteps from 'hooks/useTriggerSubsteps';
import useActionSubsteps from 'hooks/useActionSubsteps';
import useStepWithTestExecutions from 'hooks/useStepWithTestExecutions';
import appConfig from 'config/app.js';
const useNewFlowEditor = process.env.REACT_APP_USE_NEW_FLOW_EDITOR === 'true';
const useNewFlowEditor = appConfig.useNewFlowEditor;
const validIcon = <CheckCircleIcon color="success" />;
const errorIcon = <ErrorIcon color="error" />;

View File

@@ -48,7 +48,7 @@ FlowStepContextMenu.propTypes = {
onDelete: PropTypes.func,
anchorEl: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
PropTypes.shape({ current: PropTypes.instanceOf(window.Element) }),
]),
deletable: PropTypes.bool.isRequired,
flowId: PropTypes.string.isRequired,

View File

@@ -10,8 +10,9 @@ import FlowSubstepTitle from 'components/FlowSubstepTitle';
import InputCreator from 'components/InputCreator';
import FilterConditions from './FilterConditions';
import { StepPropType, SubstepPropType } from 'propTypes/propTypes';
import appConfig from 'config/app.js';
const useNewFlowEditor = process.env.REACT_APP_USE_NEW_FLOW_EDITOR === 'true';
const useNewFlowEditor = appConfig.useNewFlowEditor;
function FlowSubstep(props) {
const {

View File

@@ -3,7 +3,6 @@ import { Link, useSearchParams, useNavigate } from 'react-router-dom';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import Card from '@mui/material/Card';
import Typography from '@mui/material/Typography';
import IconButton from '@mui/material/IconButton';
import DeleteIcon from '@mui/icons-material/Delete';
import EditIcon from '@mui/icons-material/Edit';
@@ -24,7 +23,6 @@ import useFormatMessage from 'hooks/useFormatMessage';
import useFolders from 'hooks/useFolders';
import useDeleteFolder from 'hooks/useDeleteFolder';
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
import objectifyUrlSearchParams from 'helpers/objectifyUrlSearchParams';
import useFlowFilters from 'hooks/useFlowFilters';
import { ListItemIcon } from './style';

View File

@@ -87,7 +87,7 @@ function Form(props) {
return (
<FormProvider {...methods}>
<form
onSubmit={methods.handleSubmit(async (data, event) => {
onSubmit={methods.handleSubmit(async (data) => {
try {
return await onSubmit?.(data);
} catch (errors) {

View File

@@ -55,10 +55,10 @@ function ImportFlowDialog(props) {
}
};
const handleImportFlow = (event) => {
const handleImportFlow = () => {
if (!selectedFile) return;
const fileReader = new FileReader();
const fileReader = new window.FileReader();
fileReader.onload = async function readFileLoaded(e) {
const flowData = parseFlowFile(e.target.result);

View File

@@ -6,7 +6,7 @@ import englishMessages from 'locales/en.json';
const IntlProvider = ({ children }) => {
return (
<BaseIntlProvider
locale={navigator.language.split('-')[0]}
locale={window.navigator.language.split('-')[0]}
defaultLocale="en"
messages={englishMessages}
>

View File

@@ -2,13 +2,10 @@ import styled from '@emotion/styled';
import Box from '@mui/material/Box';
import Divider from '@mui/material/Divider';
import Link from '@mui/material/Link';
import Stack from '@mui/material/Stack';
import SvgIcon from '@mui/material/SvgIcon';
import Typography from '@mui/material/Typography';
import useAutomatischConfig from 'hooks/useAutomatischConfig';
import useFormatMessage from 'hooks/useFormatMessage';
import useVersion from 'hooks/useVersion';
const LogoImage = styled('img')(() => ({
maxWidth: 'auto',
@@ -16,7 +13,7 @@ const LogoImage = styled('img')(() => ({
}));
const LayoutFooter = () => {
const { data: config, isPending: isConfigPending } = useAutomatischConfig();
const { data: config } = useAutomatischConfig();
const formatMessage = useFormatMessage();
if (config?.data.enableFooter !== true) return null;
@@ -34,22 +31,20 @@ const LayoutFooter = () => {
href: config.data.footerTosUrl,
text: formatMessage('footer.tosLinkText'),
},
{
key: 'privacy-policy',
show: !!config.data.footerPrivacyPolicyUrl,
href: config.data.footerPrivacyPolicyUrl,
text: formatMessage('footer.privacyPolicyLinkText'),
},
{
key: 'imprint',
show: !!config.data.footerImprintUrl,
href: config.data.footerImprintUrl,
text: formatMessage('footer.imprintLinkText'),
},
,
];
return (
<Box mt="auto" position="sticky" bottom={0}>
<Box bgcolor="common.white" mt={4}>

View File

@@ -15,7 +15,7 @@ function ListItemLink(props) {
React.forwardRef(function InLineLink(linkProps, ref) {
try {
// challenge the link to check if it's absolute URL
new URL(to); // should throw an error if it's not an absolute URL
new window.URL(to); // should throw an error if it's not an absolute URL
return (
<a
{...linkProps}

View File

@@ -40,7 +40,9 @@ function LoginForm() {
});
const { token } = data;
authentication.updateToken(token);
} catch {}
} catch (error) {
console.error(error);
}
};
const renderError = () => {
@@ -49,7 +51,7 @@ function LoginForm() {
];
return errors.map((error) => (
<Alert severity="error" sx={{ mt: 2 }}>
<Alert key={error} severity="error" sx={{ mt: 2 }}>
{error}
</Alert>
));

View File

@@ -1,8 +1,8 @@
import * as React from 'react';
import { ReactComponent as MationLogoSvg } from './assets/mation-logo.svg';
import MationLogoSvg from './assets/mation-logo.svg';
const MationLogo = () => {
return <MationLogoSvg />;
return <img src={MationLogoSvg} alt="Mation Logo" />;
};
export default MationLogo;

View File

@@ -3,22 +3,11 @@ import { useFormContext } from 'react-hook-form';
import PropTypes from 'prop-types';
import ControlledCheckbox from 'components/ControlledCheckbox';
const AllEntitiesPermissions = ({
action,
subject,
disabled,
name,
syncIsCreator,
}) => {
const { getValues, formState, resetField } = useFormContext();
const AllEntitiesPermissions = ({ action, subject, disabled, name }) => {
const { getValues, resetField } = useFormContext();
const fieldName = `${name}.${subject.key}.${action.key}.allEntities`;
const defaultValue =
formState.defaultValues?.[name]?.[subject.key]?.[action.key].allEntities;
const ownEntitiesFieldName = `${name}.${subject.key}.${action.key}.ownEntities`;
const ownEntitiesFieldTouched =
formState.touchedFields?.[name]?.[subject.key]?.[action.key]
?.ownEntities === true;
const currentValue = getValues(fieldName);
@@ -28,16 +17,6 @@ const AllEntitiesPermissions = ({
}
}, [ownEntitiesFieldName, currentValue]);
const handleSyncIsCreator = (newValue) => {
if (syncIsCreator && defaultValue === false && !ownEntitiesFieldTouched) {
resetField(ownEntitiesFieldName, { defaultValue: newValue });
}
if (newValue === true) {
resetField(ownEntitiesFieldName, { defaultValue: true });
}
};
return (
<ControlledCheckbox
disabled={disabled}

View File

@@ -42,7 +42,7 @@ Popper.propTypes = {
open: PropTypes.bool.isRequired,
anchorEl: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
PropTypes.shape({ current: PropTypes.instanceOf(window.Element) }),
]),
data: PropTypes.array.isRequired,
onSuggestionClick: PropTypes.func,

View File

@@ -28,11 +28,10 @@ const getPartialArray = (array, length = array.length) => {
return array.slice(0, length);
};
const renderItemFactory =
({ onSuggestionClick }) =>
(props) => (
<SuggestionItem {...props} onSuggestionClick={onSuggestionClick} />
);
const renderItemFactory = ({ onSuggestionClick }) =>
function RenderItem(props) {
return <SuggestionItem {...props} onSuggestionClick={onSuggestionClick} />;
};
const Suggestions = (props) => {
const formatMessage = useFormatMessage();

View File

@@ -50,7 +50,9 @@ export default function ResetPasswordForm() {
},
});
navigate(URLS.LOGIN);
} catch {}
} catch (error) {
console.error(error);
}
};
const renderError = () => {
@@ -63,7 +65,7 @@ export default function ResetPasswordForm() {
];
return errors.map((error) => (
<Alert severity="error" sx={{ mt: 2 }}>
<Alert key={error} severity="error" sx={{ mt: 2 }}>
{error}
</Alert>
));

View File

@@ -23,7 +23,7 @@ export default function SplitButton(props) {
setOpen((prevOpen) => !prevOpen);
};
const handleClose = (event) => {
const handleClose = () => {
setOpen(false);
};

View File

@@ -15,8 +15,9 @@ import WebhookUrlInfo from 'components/WebhookUrlInfo';
import FlowSubstepTitle from 'components/FlowSubstepTitle';
import { useQueryClient } from '@tanstack/react-query';
import { StepPropType, SubstepPropType } from 'propTypes/propTypes';
import appConfig from 'config/app.js';
const useNewFlowEditor = process.env.REACT_APP_USE_NEW_FLOW_EDITOR === 'true';
const useNewFlowEditor = appConfig.useNewFlowEditor;
function TestSubstep(props) {
const {

View File

@@ -1,12 +1,12 @@
const backendUrl = process.env.REACT_APP_BACKEND_URL;
const backendUrl = import.meta.env.VITE_BACKEND_URL;
const computeUrl = (url, backendUrl) => {
/**
* In case `backendUrl` is a host, we append the url to it.
**/
try {
return new URL(url, backendUrl).toString();
} catch (e) {
return new window.URL(url, backendUrl).toString();
} catch {
/*
* In case `backendUrl` is not qualified, we utilize `url` alone.
**/
@@ -15,8 +15,9 @@ const computeUrl = (url, backendUrl) => {
};
const config = {
baseUrl: process.env.REACT_APP_BASE_URL,
baseUrl: import.meta.env.VITE_BASE_URL,
restApiUrl: computeUrl('/internal/api', backendUrl),
useNewFlowEditor: import.meta.env.VITE_USE_NEW_FLOW_EDITOR === 'true',
supportEmailAddress: 'support@automatisch.io',
};

View File

@@ -1,6 +1,8 @@
const parseUrlSearchParams = (event) => {
const searchParams = new URLSearchParams(event.data.payload.search);
const hashParams = new URLSearchParams(event.data.payload.hash.substring(1));
const searchParams = new window.URLSearchParams(event.data.payload.search);
const hashParams = new window.URLSearchParams(
event.data.payload.hash.substring(1),
);
const searchParamsObject = getObjectOfEntries(searchParams.entries());
const hashParamsObject = getObjectOfEntries(hashParams.entries());
@@ -33,9 +35,9 @@ export const processPopupMessage = (popup) => {
let closeCheckIntervalId;
if (popup) {
closeCheckIntervalId = setInterval(() => {
closeCheckIntervalId = window.setInterval(() => {
if (popup.closed) {
clearInterval(closeCheckIntervalId);
window.clearInterval(closeCheckIntervalId);
reject({ message: 'Error occured while verifying credentials!' });
}
@@ -51,7 +53,7 @@ export const processPopupMessage = (popup) => {
window.removeEventListener('message', messageHandler);
if (closeCheckIntervalId) {
clearInterval(closeCheckIntervalId);
window.clearInterval(closeCheckIntervalId);
}
resolve(data);

View File

@@ -2,13 +2,13 @@ const NAMESPACE = 'automatisch';
const makeKey = (key) => `${NAMESPACE}.${key}`;
export const setItem = (key, value) => {
return localStorage.setItem(makeKey(key), value);
return window.localStorage.setItem(makeKey(key), value);
};
export const getItem = (key) => {
return localStorage.getItem(makeKey(key));
return window.localStorage.getItem(makeKey(key));
};
export const removeItem = (key) => {
return localStorage.removeItem(makeKey(key));
return window.localStorage.removeItem(makeKey(key));
};

View File

@@ -1,16 +1,22 @@
import { Link as RouterLink } from 'react-router-dom';
import Link from '@mui/material/Link';
export const generateInternalLink = (link) => (str) => (
export const generateInternalLink = (link) =>
function LinkVariable(str) {
return (
<Link component={RouterLink} to={link}>
{str}
</Link>
);
);
};
export const generateExternalLink = (link) => (str) => (
export const generateExternalLink = (link) =>
function LinkVariable(str) {
return (
<Link href={link} target="_blank">
{str}
</Link>
);
);
};
export const makeBold = (str) => <strong>{str}</strong>;

View File

@@ -5,13 +5,13 @@ export default function useAdminDeleteRole(roleId) {
const queryClient = useQueryClient();
const query = useMutation({
mutationFn: async (payload) => {
mutationFn: async () => {
const { data } = await api.delete(`/v1/admin/roles/${roleId}`);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({
onSuccess: async () => {
await queryClient.invalidateQueries({
queryKey: ['admin', 'roles'],
});
},

View File

@@ -10,7 +10,7 @@ const appendTrailingSlash = (url) => {
}
return url;
}
};
/**
* Per instance, there may be different documentation. However, the paths are assumed the same.
@@ -18,10 +18,12 @@ const appendTrailingSlash = (url) => {
*/
export default function useDocsUrl(path) {
const { data: automatischInfo } = useAutomatischInfo();
const docsUrlWithTrailingSlash = appendTrailingSlash(automatischInfo?.docsUrl);
const docsUrlWithTrailingSlash = appendTrailingSlash(
automatischInfo?.docsUrl,
);
const docsUrl = docsUrlWithTrailingSlash || 'https://automatisch.io/docs/';
const absoluteUrl = new URL(path, docsUrl).toString();
const absoluteUrl = new window.URL(path, docsUrl).toString();
return absoluteUrl;
}

View File

@@ -12,11 +12,11 @@ export default function useDownloadJsonAsFile() {
replacement: '-',
});
const fileBlob = new Blob([stringifiedContents], {
const fileBlob = new window.Blob([stringifiedContents], {
type: 'application/json',
});
const fileObjectUrl = URL.createObjectURL(fileBlob);
const fileObjectUrl = window.URL.createObjectURL(fileBlob);
const temporaryDownloadLink = document.createElement('a');
temporaryDownloadLink.href = fileObjectUrl;

View File

@@ -92,7 +92,7 @@ function useDynamicData(stepId, schema) {
lastComputedVariables.current = variables;
return variables;
} catch (err) {
} catch {
return null;
}
}

View File

@@ -61,7 +61,7 @@ function useDynamicFields(stepId, schema) {
}
lastComputedVariables.current = variables;
return variables;
} catch (err) {
} catch {
return null;
}
}

View File

@@ -1,8 +1,6 @@
import { useSearchParams } from 'react-router-dom';
import { useMutation } from '@tanstack/react-query';
import { DateTime } from 'luxon';
import api from 'helpers/api';
import objectifyUrlSearchParams from 'helpers/objectifyUrlSearchParams';
export default function useExecutionFilters() {
@@ -21,7 +19,8 @@ export default function useExecutionFilters() {
const endDateTime = parseInt(endDateTimeString, 10);
const filterByStatus = (status) => {
setSearchParams((current) => {
setSearchParams(() => {
// eslint-disable-next-line no-unused-vars
const { status: currentStatus, ...rest } = searchParamsObject;
if (status) {
@@ -35,7 +34,8 @@ export default function useExecutionFilters() {
const filterByStartDateTime = (startDateTime) => {
const startDateTimeString = startDateTime?.toMillis();
setSearchParams((current) => {
setSearchParams(() => {
// eslint-disable-next-line no-unused-vars
const { startDateTime: currentStartDateTime, ...rest } =
searchParamsObject;
@@ -50,7 +50,8 @@ export default function useExecutionFilters() {
const filterByEndDateTime = (endDateTime) => {
const endDateTimeString = endDateTime?.endOf('day').toMillis();
setSearchParams((current) => {
setSearchParams(() => {
// eslint-disable-next-line no-unused-vars
const { endDateTime: currentEndDateTime, ...rest } = searchParamsObject;
if (endDateTimeString) {
@@ -65,13 +66,14 @@ export default function useExecutionFilters() {
const searchParamsObject = objectifyUrlSearchParams(searchParams);
if (value === undefined) {
// eslint-disable-next-line no-unused-vars
const { [key]: keyToRemove, ...remainingSearchParams } =
searchParamsObject;
return new URLSearchParams(remainingSearchParams).toString();
return new window.URLSearchParams(remainingSearchParams).toString();
}
return new URLSearchParams({
return new window.URLSearchParams({
...searchParamsObject,
[key]: value,
}).toString();

View File

@@ -1,7 +1,5 @@
import { useSearchParams } from 'react-router-dom';
import { useMutation } from '@tanstack/react-query';
import api from 'helpers/api';
import objectifyUrlSearchParams from 'helpers/objectifyUrlSearchParams';
export default function useFlowFilters() {
@@ -14,7 +12,8 @@ export default function useFlowFilters() {
searchParamsObject.onlyOwnedFlows === 'true' || undefined;
const filterByStatus = (status) => {
setSearchParams((current) => {
setSearchParams(() => {
// eslint-disable-next-line no-unused-vars
const { status: currentStatus, ...rest } = searchParamsObject;
if (status) {
@@ -26,7 +25,8 @@ export default function useFlowFilters() {
};
const filterByOwnership = (onlyOwnedFlows) => {
setSearchParams((current) => {
setSearchParams(() => {
// eslint-disable-next-line no-unused-vars
const { onlyOwnedFlows: currentOnlyOwnedFlows, ...rest } =
searchParamsObject;
@@ -42,13 +42,14 @@ export default function useFlowFilters() {
const searchParamsObject = objectifyUrlSearchParams(searchParams);
if (value === undefined) {
// eslint-disable-next-line no-unused-vars
const { [key]: keyToRemove, ...remainingSearchParams } =
searchParamsObject;
return new URLSearchParams(remainingSearchParams).toString();
return new window.URLSearchParams(remainingSearchParams).toString();
}
return new URLSearchParams({
return new window.URLSearchParams({
...searchParamsObject,
[key]: value,
}).toString();

View File

@@ -1,4 +1,3 @@
import userAbility from 'helpers/userAbility';
import useCurrentUser from 'hooks/useCurrentUser';
export default function useIsCurrentUserAdmin() {

View File

@@ -4,10 +4,10 @@ import api from 'helpers/api';
import React from 'react';
export default function useLazyApps({ appName } = {}, { onSuccess } = {}) {
const abortControllerRef = React.useRef(new AbortController());
const abortControllerRef = React.useRef(new window.AbortController());
React.useEffect(() => {
abortControllerRef.current = new AbortController();
abortControllerRef.current = new window.AbortController();
return () => {
abortControllerRef.current?.abort();

View File

@@ -8,8 +8,7 @@ import MetadataProvider from 'components/MetadataProvider';
import { AuthenticationProvider } from 'contexts/Authentication';
import QueryClientProvider from 'components/QueryClientProvider';
import Router from 'components/Router';
import routes from 'routes';
import reportWebVitals from './reportWebVitals';
import routes from './routes';
// Sets the default locale to English for all luxon DateTime instances created afterwards.
Settings.defaultLocale = 'en';
@@ -32,8 +31,3 @@ root.render(
</SnackbarProvider>
</Router>,
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

View File

@@ -61,7 +61,7 @@ function AdminTemplates() {
defaultValues={{ enableTemplates: config?.data.enableTemplates }}
noValidate
automaticValidation={false}
render={({ formState: { errors, isDirty } }) => (
render={() => (
<Switch
name="enableTemplates"
disabled={isUpdateConfigPending}

View File

@@ -30,7 +30,9 @@ export default function CreateFlow() {
const flowId = response.data?.id;
navigateToEditor(flowId);
} catch {}
} catch (error) {
console.error(error);
}
}
initiate();

View File

@@ -25,9 +25,6 @@ export default function Executions() {
const navigate = useNavigate();
const page = parseInt(searchParams.get('page') || '', 10) || 1;
const name = searchParams.get('name') || '';
const status = searchParams.get('status');
const startDateTime = searchParams.get('startDateTime');
const endDateTime = searchParams.get('endDateTime');
const [searchValue, setSearchValue] = React.useState(name);
const {
@@ -61,7 +58,7 @@ export default function Executions() {
const getPathWithSearchParams = (page) => {
const searchParamsObject = objectifyUrlSearchParams(searchParams);
const newSearchParams = new URLSearchParams({
const newSearchParams = new window.URLSearchParams({
...searchParamsObject,
page,
});

View File

@@ -73,7 +73,7 @@ export default function Flows() {
const getPathWithSearchParams = (page) => {
const searchParamsObject = objectifyUrlSearchParams(searchParams);
const newSearchParams = new URLSearchParams({
const newSearchParams = new window.URLSearchParams({
...searchParamsObject,
page,
});

View File

@@ -87,7 +87,7 @@ export default function UserInterface() {
'data-test': 'snackbar-update-user-interface-success',
},
});
} catch (error) {
} catch {
throw new Error('Failed while updating!');
}
};

View File

@@ -1,12 +0,0 @@
const reportWebVitals = (onPerfEntry) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View File

@@ -15,7 +15,6 @@ import Application from 'pages/Application';
import Executions from 'pages/Executions';
import Execution from 'pages/Execution';
import Flows from 'pages/Flows';
import Flow from 'pages/Flow';
import Login from 'pages/Login';
import AcceptInvitation from 'pages/AcceptInvitation';
import LoginCallback from 'pages/LoginCallback';

View File

@@ -1,5 +0,0 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

View File

@@ -0,0 +1,47 @@
import react from '@vitejs/plugin-react';
import path from 'node:path';
import { defineConfig } from 'vite';
import eslint from 'vite-plugin-eslint';
export default defineConfig(() => {
return {
// https://github.com/vitejs/vite/issues/1973#issuecomment-787571499
define: {
'process.env': {},
},
build: {
outDir: 'build',
},
plugins: [
react({
jsxImportSource: '@emotion/react',
babel: {
plugins: ['@emotion/babel-plugin'],
},
}),
eslint({
eslintPath: require.resolve('eslint'),
cache: false,
include: ['src/**/*.js', 'src/**/*.jsx'],
exclude: ['node_modules'],
}),
],
resolve: {
alias: {
components: path.resolve(__dirname, './src/components'),
config: path.resolve(__dirname, './src/config'),
contexts: path.resolve(__dirname, './src/contexts'),
helpers: path.resolve(__dirname, './src/helpers'),
hooks: path.resolve(__dirname, './src/hooks'),
locales: path.resolve(__dirname, './src/locales'),
pages: path.resolve(__dirname, './src/pages'),
propTypes: path.resolve(__dirname, './src/propTypes'),
styles: path.resolve(__dirname, './src/styles'),
},
},
server: {
open: true,
port: process.env.PORT || 3001,
},
};
});

File diff suppressed because it is too large Load Diff