Webatrice: update deps (#4700)
* save work * fix reset styling * fix toast reducer * update non-react deps * update react libraries * remove jquery, use sanitize-html instead * add missing change * fix deps and dev deps * update workflow to target Node 16 * run @mui/codemod to remove @mui/styles * add default body font size * update react 17 to 18 * declare enum before use * add rel attr to links * fix font sizing issue * trailing commas * refactor deep destructuring Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
This commit is contained in:
parent
5854a635ca
commit
26d7fe2ff0
17 changed files with 26225 additions and 3552 deletions
2
.github/workflows/web-build.yml
vendored
2
.github/workflows/web-build.yml
vendored
|
@ -28,7 +28,7 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node_version:
|
node_version:
|
||||||
- 12
|
- 16
|
||||||
- lts/*
|
- lts/*
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
1
webclient/.npmrc
Normal file
1
webclient/.npmrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
legacy-peer-deps=true
|
29369
webclient/package-lock.json
generated
29369
webclient/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -2,40 +2,6 @@
|
||||||
"name": "webclient",
|
"name": "webclient",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
|
||||||
"@emotion/react": "^11.8.2",
|
|
||||||
"@emotion/styled": "^11.8.1",
|
|
||||||
"@mui/icons-material": "^5.5.1",
|
|
||||||
"@mui/material": "^5.5.1",
|
|
||||||
"@mui/styles": "^5.5.1",
|
|
||||||
"@testing-library/jest-dom": "^5.16.2",
|
|
||||||
"@testing-library/react": "^12.1.3",
|
|
||||||
"crypto-js": "^4.1.1",
|
|
||||||
"dexie": "^3.2.2",
|
|
||||||
"final-form": "^4.20.6",
|
|
||||||
"final-form-set-field-touched": "^1.0.1",
|
|
||||||
"i18next": "^21.6.13",
|
|
||||||
"i18next-browser-languagedetector": "^6.1.3",
|
|
||||||
"jquery": "^3.6.0",
|
|
||||||
"lodash": "^4.17.21",
|
|
||||||
"prop-types": "^15.8.1",
|
|
||||||
"protobufjs": "^6.11.3",
|
|
||||||
"react": "^17.0.2",
|
|
||||||
"react-dom": "^17.0.2",
|
|
||||||
"react-final-form": "^6.5.8",
|
|
||||||
"react-final-form-listeners": "^1.0.3",
|
|
||||||
"react-i18next": "^11.15.6",
|
|
||||||
"react-redux": "^7.2.6",
|
|
||||||
"react-router-dom": "^6.2.2",
|
|
||||||
"react-scripts": "5.0.0",
|
|
||||||
"react-virtualized-auto-sizer": "^1.0.6",
|
|
||||||
"react-window": "^1.8.6",
|
|
||||||
"redux": "^4.1.2",
|
|
||||||
"redux-form": "^8.3.8",
|
|
||||||
"redux-thunk": "^2.4.1",
|
|
||||||
"rxjs": "^7.5.4",
|
|
||||||
"typescript": "^4.6.2"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prebuild": "node prebuild.js",
|
"prebuild": "node prebuild.js",
|
||||||
"prestart": "node prebuild.js",
|
"prestart": "node prebuild.js",
|
||||||
|
@ -50,6 +16,61 @@
|
||||||
"prepare": "cd .. && husky install",
|
"prepare": "cd .. && husky install",
|
||||||
"translate": "node prebuild.js -i18nOnly"
|
"translate": "node prebuild.js -i18nOnly"
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.8.2",
|
||||||
|
"@emotion/styled": "^11.8.1",
|
||||||
|
"@mui/icons-material": "^5.5.1",
|
||||||
|
"@mui/material": "^5.5.1",
|
||||||
|
"crypto-js": "^4.1.1",
|
||||||
|
"dexie": "^3.2.2",
|
||||||
|
"final-form": "^4.20.6",
|
||||||
|
"final-form-set-field-touched": "^1.0.1",
|
||||||
|
"i18next": "^22.0.4",
|
||||||
|
"i18next-browser-languagedetector": "^7.0.0",
|
||||||
|
"i18next-icu": "^2.0.3",
|
||||||
|
"intl-messageformat": "^10.2.1",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"protobufjs": "^7.1.2",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-final-form": "^6.5.8",
|
||||||
|
"react-final-form-listeners": "^1.0.3",
|
||||||
|
"react-i18next": "^12.0.0",
|
||||||
|
"react-redux": "^8.0.4",
|
||||||
|
"react-router-dom": "^6.2.2",
|
||||||
|
"react-scripts": "5.0.1",
|
||||||
|
"react-virtualized-auto-sizer": "^1.0.6",
|
||||||
|
"react-window": "^1.8.6",
|
||||||
|
"redux": "^4.1.2",
|
||||||
|
"redux-form": "^8.3.8",
|
||||||
|
"redux-thunk": "^2.4.1",
|
||||||
|
"rxjs": "^7.5.4",
|
||||||
|
"sanitize-html": "^2.7.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.17.5",
|
||||||
|
"@mui/types": "^7.1.3",
|
||||||
|
"@testing-library/jest-dom": "^5.16.2",
|
||||||
|
"@testing-library/react": "^13.4.0",
|
||||||
|
"@types/jest": "29.2.0",
|
||||||
|
"@types/jquery": "^3.5.14",
|
||||||
|
"@types/lodash": "^4.14.179",
|
||||||
|
"@types/node": "18.11.7",
|
||||||
|
"@types/prop-types": "^15.7.4",
|
||||||
|
"@types/react": "18.0.24",
|
||||||
|
"@types/react-dom": "18.0.8",
|
||||||
|
"@types/react-redux": "^7.1.23",
|
||||||
|
"@types/react-router-dom": "^5.3.3",
|
||||||
|
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||||
|
"@types/react-window": "^1.8.5",
|
||||||
|
"@types/redux-form": "^8.3.3",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.14.0",
|
||||||
|
"@typescript-eslint/parser": "^5.14.0",
|
||||||
|
"fs-extra": "^10.0.1",
|
||||||
|
"husky": "^8.0.1",
|
||||||
|
"typescript": "^4.6.2"
|
||||||
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
},
|
},
|
||||||
|
@ -69,28 +90,5 @@
|
||||||
"moduleNameMapper": {
|
"moduleNameMapper": {
|
||||||
"\\.(css|less)$": "identity-obj-proxy"
|
"\\.(css|less)$": "identity-obj-proxy"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@babel/core": "^7.17.5",
|
|
||||||
"@mui/types": "^7.1.3",
|
|
||||||
"@types/jest": "27.4.1",
|
|
||||||
"@types/jquery": "^3.5.14",
|
|
||||||
"@types/lodash": "^4.14.179",
|
|
||||||
"@types/node": "17.0.21",
|
|
||||||
"@types/prop-types": "^15.7.4",
|
|
||||||
"@types/react": "17.0.39",
|
|
||||||
"@types/react-dom": "17.0.13",
|
|
||||||
"@types/react-redux": "^7.1.23",
|
|
||||||
"@types/react-router-dom": "^5.3.3",
|
|
||||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
|
||||||
"@types/react-window": "^1.8.5",
|
|
||||||
"@types/redux-form": "^8.3.3",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.14.0",
|
|
||||||
"@typescript-eslint/parser": "^5.14.0",
|
|
||||||
"fs-extra": "^10.0.1",
|
|
||||||
"husky": "^7.0.4",
|
|
||||||
"i18next-icu": "^2.0.3",
|
|
||||||
"identity-obj-proxy": "^3.0.0",
|
|
||||||
"intl-messageformat": "^9.11.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,33 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import makeStyles from '@mui/styles/makeStyles';
|
import { styled } from '@mui/material/styles';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextField from '@mui/material/TextField';
|
||||||
import ErrorOutlinedIcon from '@mui/icons-material/ErrorOutlined';
|
import ErrorOutlinedIcon from '@mui/icons-material/ErrorOutlined';
|
||||||
|
|
||||||
import './InputField.css';
|
import './InputField.css';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const PREFIX = 'InputField';
|
||||||
root: {
|
|
||||||
|
const classes = {
|
||||||
|
root: `${PREFIX}-root`
|
||||||
|
};
|
||||||
|
|
||||||
|
const Root = styled('div')(({ theme }) => ({
|
||||||
|
[`&.${classes.root}`]: {
|
||||||
'& .InputField-error': {
|
'& .InputField-error': {
|
||||||
color: theme.palette.error.main
|
color: theme.palette.error.main
|
||||||
},
|
},
|
||||||
|
|
||||||
'& .InputField-warning': {
|
'& .InputField-warning': {
|
||||||
color: theme.palette.warning.main
|
color: theme.palette.warning.main
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const InputField = ({ input, meta: { touched, error, warning }, ...args }) => {
|
const InputField = ({ input, meta, ...args }) => {
|
||||||
const classes = useStyles();
|
const { touched, error, warning } = meta;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'InputField ' + classes.root}>
|
<Root className={'InputField ' + classes.root}>
|
||||||
{ touched && (
|
{ touched && (
|
||||||
<div className="InputField-validation">
|
<div className="InputField-validation">
|
||||||
{
|
{
|
||||||
|
@ -47,7 +53,7 @@ const InputField = ({ input, meta: { touched, error, warning }, ...args }) => {
|
||||||
size="small"
|
size="small"
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Root>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Select, MenuItem } from '@mui/material';
|
import { Select, MenuItem } from '@mui/material';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
|
@ -7,7 +8,6 @@ import IconButton from '@mui/material/IconButton';
|
||||||
import WifiTetheringIcon from '@mui/icons-material/WifiTethering';
|
import WifiTetheringIcon from '@mui/icons-material/WifiTethering';
|
||||||
import PortableWifiOffIcon from '@mui/icons-material/PortableWifiOff';
|
import PortableWifiOffIcon from '@mui/icons-material/PortableWifiOff';
|
||||||
import InputLabel from '@mui/material/InputLabel';
|
import InputLabel from '@mui/material/InputLabel';
|
||||||
import makeStyles from '@mui/styles/makeStyles';
|
|
||||||
import Check from '@mui/icons-material/Check';
|
import Check from '@mui/icons-material/Check';
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
import EditRoundedIcon from '@mui/icons-material/Edit';
|
import EditRoundedIcon from '@mui/icons-material/Edit';
|
||||||
|
@ -29,8 +29,14 @@ enum TestConnection {
|
||||||
SUCCESS = 'success',
|
SUCCESS = 'success',
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const PREFIX = 'KnownHosts';
|
||||||
root: {
|
|
||||||
|
const classes = {
|
||||||
|
root: `${PREFIX}-root`
|
||||||
|
};
|
||||||
|
|
||||||
|
const Root = styled('div')(({ theme }) => ({
|
||||||
|
[`&.${classes.root}`]: {
|
||||||
'& .KnownHosts-error': {
|
'& .KnownHosts-error': {
|
||||||
color: theme.palette.error.main
|
color: theme.palette.error.main
|
||||||
},
|
},
|
||||||
|
@ -50,13 +56,13 @@ const useStyles = makeStyles(theme => ({
|
||||||
color: theme.palette.success.main
|
color: theme.palette.success.main
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const KnownHosts = (props) => {
|
const KnownHosts = (props) => {
|
||||||
const { input: { onChange }, meta, disabled } = props;
|
const { input: { onChange }, meta, disabled } = props;
|
||||||
const { touched, error, warning } = meta;
|
const { touched, error, warning } = meta;
|
||||||
const classes = useStyles();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [hostsState, setHostsState] = useState({
|
const [hostsState, setHostsState] = useState({
|
||||||
|
@ -195,7 +201,7 @@ const KnownHosts = (props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'KnownHosts ' + classes.root}>
|
<Root className={'KnownHosts ' + classes.root}>
|
||||||
<FormControl className='KnownHosts-form' size='small' variant='outlined'>
|
<FormControl className='KnownHosts-form' size='small' variant='outlined'>
|
||||||
{ touched && (
|
{ touched && (
|
||||||
<div className='KnownHosts-validation'>
|
<div className='KnownHosts-validation'>
|
||||||
|
@ -272,8 +278,8 @@ const KnownHosts = (props) => {
|
||||||
<Toast open={showCreateToast} onClose={() => setShowCreateToast(false)}>{ t('KnownHosts.toast', { mode: 'created' }) }</Toast>
|
<Toast open={showCreateToast} onClose={() => setShowCreateToast(false)}>{ t('KnownHosts.toast', { mode: 'created' }) }</Toast>
|
||||||
<Toast open={showDeleteToast} onClose={() => setShowDeleteToast(false)}>{ t('KnownHosts.toast', { mode: 'deleted' }) }</Toast>
|
<Toast open={showDeleteToast} onClose={() => setShowDeleteToast(false)}>{ t('KnownHosts.toast', { mode: 'deleted' }) }</Toast>
|
||||||
<Toast open={showEditToast} onClose={() => setShowEditToast(false)}>{ t('KnownHosts.toast', { mode: 'edited' }) }</Toast>
|
<Toast open={showEditToast} onClose={() => setShowEditToast(false)}>{ t('KnownHosts.toast', { mode: 'edited' }) }</Toast>
|
||||||
</div>
|
</Root>
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default KnownHosts;
|
export default KnownHosts;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import makeStyles from '@mui/styles/makeStyles';
|
import { styled } from '@mui/material/styles';
|
||||||
import Popover from '@mui/material/Popover';
|
import Popover from '@mui/material/Popover';
|
||||||
|
|
||||||
import { CardDTO, TokenDTO } from 'services';
|
import { CardDTO, TokenDTO } from 'services';
|
||||||
|
@ -10,17 +10,24 @@ import TokenDetails from '../TokenDetails/TokenDetails';
|
||||||
|
|
||||||
import './CardCallout.css';
|
import './CardCallout.css';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const PREFIX = 'CardCallout';
|
||||||
popover: {
|
|
||||||
|
const classes = {
|
||||||
|
popover: `${PREFIX}-popover`,
|
||||||
|
popoverContent: `${PREFIX}-popoverContent`
|
||||||
|
};
|
||||||
|
|
||||||
|
const Root = styled('span')(({ theme }) => ({
|
||||||
|
[`& .${classes.popover}`]: {
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
},
|
},
|
||||||
popoverContent: {
|
|
||||||
|
[`& .${classes.popoverContent}`]: {
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
},
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const CardCallout = ({ name }) => {
|
const CardCallout = ({ name }) => {
|
||||||
const classes = useStyles();
|
|
||||||
const [card, setCard] = useState<CardDTO>(null);
|
const [card, setCard] = useState<CardDTO>(null);
|
||||||
const [token, setToken] = useState<TokenDTO>(null);
|
const [token, setToken] = useState<TokenDTO>(null);
|
||||||
const [anchorEl, setAnchorEl] = useState<Element>(null);
|
const [anchorEl, setAnchorEl] = useState<Element>(null);
|
||||||
|
@ -48,7 +55,7 @@ const CardCallout = ({ name }) => {
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className='callout'>
|
<Root className='callout'>
|
||||||
<span
|
<span
|
||||||
onMouseEnter={handlePopoverOpen}
|
onMouseEnter={handlePopoverOpen}
|
||||||
onMouseLeave={handlePopoverClose}
|
onMouseLeave={handlePopoverClose}
|
||||||
|
@ -80,7 +87,7 @@ const CardCallout = ({ name }) => {
|
||||||
</Popover>
|
</Popover>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</span>
|
</Root>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { createContext, FC, ReactChild, ReactNode, useContext, useEffect, useReducer, ContextType, Context } from 'react'
|
import { createContext, FC, PropsWithChildren, ReactChild, ReactNode, useContext, useEffect, useReducer, ContextType, Context } from 'react'
|
||||||
|
|
||||||
import { ACTIONS, initialState, reducer } from './reducer';
|
import { ACTIONS, initialState, reducer } from './reducer';
|
||||||
import Toast from './Toast'
|
import Toast from './Toast'
|
||||||
|
@ -24,15 +24,15 @@ const ToastContext: Context<any> = createContext<ToastState>({
|
||||||
removeToast: (key) => {},
|
removeToast: (key) => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ToastProvider: FC<ReactNode> = (props) => {
|
export const ToastProvider: FC<PropsWithChildren> = (props) => {
|
||||||
const { children } = props
|
const { children } = props
|
||||||
const [state, dispatch] = useReducer(reducer, initialState)
|
const [state, dispatch] = useReducer(reducer, initialState)
|
||||||
const providerState = {
|
const providerState = {
|
||||||
toasts: state.toasts,
|
toasts: state.toasts,
|
||||||
addToast: (key, children) => dispatch({ type: ACTIONS.ADD_TOAST, payload: { key, children } }),
|
addToast: (key, children) => dispatch({ type: ACTIONS.ADD_TOAST, payload: { key, children } }),
|
||||||
openToast: key => dispatch({ type: ACTIONS.OPEN_TOAST, payload: key }),
|
openToast: key => dispatch({ type: ACTIONS.OPEN_TOAST, payload: { key } }),
|
||||||
closeToast: key => dispatch({ type: ACTIONS.CLOSE_TOAST, payload: key }),
|
closeToast: key => dispatch({ type: ACTIONS.CLOSE_TOAST, payload: { key } }),
|
||||||
removeToast: key => dispatch({ type: ACTIONS.REMOVE_TOAST, payload: key }),
|
removeToast: key => dispatch({ type: ACTIONS.REMOVE_TOAST, payload: { key } }),
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<ToastContext.Provider value={providerState}>
|
<ToastContext.Provider value={providerState}>
|
||||||
|
@ -41,7 +41,7 @@ export const ToastProvider: FC<ReactNode> = (props) => {
|
||||||
{Array.from(state.toasts).map(([key, value]) => {
|
{Array.from(state.toasts).map(([key, value]) => {
|
||||||
const { isOpen, children } = value;
|
const { isOpen, children } = value;
|
||||||
return (
|
return (
|
||||||
<Toast key={key} open={isOpen} onClose={() => dispatch({ type: ACTIONS.CLOSE_TOAST, payload: key })}>
|
<Toast key={key} open={isOpen} onClose={() => dispatch({ type: ACTIONS.CLOSE_TOAST, payload: { key } })}>
|
||||||
{children}
|
{children}
|
||||||
</Toast>
|
</Toast>
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,41 +6,54 @@ export const ACTIONS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialState = {
|
export const initialState = {
|
||||||
toasts: new Map()
|
toasts: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function reducer(state, action) {
|
export function reducer(state, { type, payload }) {
|
||||||
const { type, payload } = action
|
const { key, children } = payload;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ACTIONS.ADD_TOAST: {
|
case ACTIONS.ADD_TOAST: {
|
||||||
const newState = { ...state }
|
return {
|
||||||
newState.toasts = new Map(Array.from(state.toasts))
|
...state,
|
||||||
const { toasts } = newState;
|
toasts: {
|
||||||
const { key, children } = payload
|
...state.toasts,
|
||||||
toasts.set(key, { isOpen: false, children })
|
[key]: {
|
||||||
return newState
|
isOpen: false,
|
||||||
|
children,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
case ACTIONS.OPEN_TOAST: {
|
case ACTIONS.OPEN_TOAST: {
|
||||||
const newState = { ...state }
|
return {
|
||||||
newState.toasts = new Map(Array.from(state.toasts))
|
...state,
|
||||||
const { toasts } = newState;
|
toasts: {
|
||||||
const toast = toasts.get(payload)
|
...state.toasts,
|
||||||
toasts.set(payload, { isOpen: true, children: toast.children })
|
[key]: {
|
||||||
return newState
|
...state.toasts[key],
|
||||||
|
isOpen: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
case ACTIONS.CLOSE_TOAST: {
|
case ACTIONS.CLOSE_TOAST: {
|
||||||
const newState = { ...state }
|
return {
|
||||||
newState.toasts = new Map(Array.from(state.toasts))
|
...state,
|
||||||
const { toasts } = newState;
|
toasts: {
|
||||||
const toast = toasts.get(payload)
|
...state.toasts,
|
||||||
toasts.set(payload, { isOpen: false, children: toast.children })
|
[key]: {
|
||||||
return newState
|
...state.toasts[key],
|
||||||
|
isOpen: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
case ACTIONS.REMOVE_TOAST: {
|
case ACTIONS.REMOVE_TOAST: {
|
||||||
const newState = { ...state }
|
const newState = { ...state };
|
||||||
newState.toasts = new Map(Array.from(state.toasts))
|
delete newState.toasts[key];
|
||||||
newState.toasts.delete(payload)
|
|
||||||
return newState
|
return newState;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw Error('Please pick an available action')
|
throw Error('Please pick an available action')
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
import { useTranslation, Trans } from 'react-i18next';
|
import { useTranslation, Trans } from 'react-i18next';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Navigate } from 'react-router-dom';
|
import { Navigate } from 'react-router-dom';
|
||||||
import makeStyles from '@mui/styles/makeStyles';
|
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
|
|
||||||
import { Images } from 'images';
|
import { Images } from 'images';
|
||||||
|
@ -11,8 +11,14 @@ import { RouteEnum } from 'types';
|
||||||
|
|
||||||
import './Initialize.css';
|
import './Initialize.css';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const PREFIX = 'Initialize';
|
||||||
root: {
|
|
||||||
|
const classes = {
|
||||||
|
root: `${PREFIX}-root`
|
||||||
|
};
|
||||||
|
|
||||||
|
const Root = styled('div')(({ theme }) => ({
|
||||||
|
[`&.${classes.root}`]: {
|
||||||
'& .Initialize-graphics': {
|
'& .Initialize-graphics': {
|
||||||
color: theme.palette.primary.contrastText,
|
color: theme.palette.primary.contrastText,
|
||||||
},
|
},
|
||||||
|
@ -20,17 +26,16 @@ const useStyles = makeStyles(theme => ({
|
||||||
'& .Initialize-graphics__bar': {
|
'& .Initialize-graphics__bar': {
|
||||||
backgroundColor: theme.palette.primary.contrastText,
|
backgroundColor: theme.palette.primary.contrastText,
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const Initialize = ({ initialized }: InitializeProps) => {
|
const Initialize = ({ initialized }: InitializeProps) => {
|
||||||
const classes = useStyles();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return initialized
|
return initialized
|
||||||
? <Navigate to={RouteEnum.LOGIN} />
|
? <Navigate to={RouteEnum.LOGIN} />
|
||||||
: (
|
: (
|
||||||
<div className={'Initialize ' + classes.root}>
|
<Root className={'Initialize ' + classes.root}>
|
||||||
<div className='Initialize-content'>
|
<div className='Initialize-content'>
|
||||||
<img src={Images.Logo} alt="logo" />
|
<img src={Images.Logo} alt="logo" />
|
||||||
<Typography variant="subtitle1" className='subtitle'>{ t('InitializeContainer.title') }</Typography>
|
<Typography variant="subtitle1" className='subtitle'>{ t('InitializeContainer.title') }</Typography>
|
||||||
|
@ -48,7 +53,7 @@ const Initialize = ({ initialized }: InitializeProps) => {
|
||||||
<div className="topBar Initialize-graphics__bar" />
|
<div className="topBar Initialize-graphics__bar" />
|
||||||
<div className="bottomBar Initialize-graphics__bar" />
|
<div className="bottomBar Initialize-graphics__bar" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Root>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Navigate } from 'react-router-dom';
|
import { Navigate } from 'react-router-dom';
|
||||||
import makeStyles from '@mui/styles/makeStyles';
|
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import Paper from '@mui/material/Paper';
|
import Paper from '@mui/material/Paper';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
|
@ -20,8 +20,14 @@ import { ServerSelectors, ServerTypes } from 'store';
|
||||||
import './Login.css';
|
import './Login.css';
|
||||||
import { useToast } from 'components/Toast';
|
import { useToast } from 'components/Toast';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const PREFIX = 'Login';
|
||||||
root: {
|
|
||||||
|
const classes = {
|
||||||
|
root: `${PREFIX}-root`
|
||||||
|
};
|
||||||
|
|
||||||
|
const Root = styled('div')(({ theme }) => ({
|
||||||
|
[`&.${classes.root}`]: {
|
||||||
'& .login-content__header': {
|
'& .login-content__header': {
|
||||||
color: theme.palette.success.light
|
color: theme.palette.success.light
|
||||||
},
|
},
|
||||||
|
@ -54,11 +60,10 @@ const useStyles = makeStyles(theme => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const Login = ({ state, description, connectOptions }: LoginProps) => {
|
const Login = ({ state, description, connectOptions }: LoginProps) => {
|
||||||
const classes = useStyles();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const isConnected = AuthenticationService.isConnected(state);
|
const isConnected = AuthenticationService.isConnected(state);
|
||||||
|
@ -220,7 +225,7 @@ const Login = ({ state, description, connectOptions }: LoginProps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'login overflow-scroll ' + classes.root}>
|
<Root className={'login overflow-scroll ' + classes.root}>
|
||||||
{ isConnected && <Navigate to={RouteEnum.SERVER} />}
|
{ isConnected && <Navigate to={RouteEnum.SERVER} />}
|
||||||
|
|
||||||
<div className="login__wrapper">
|
<div className="login__wrapper">
|
||||||
|
@ -333,7 +338,7 @@ const Login = ({ state, description, connectOptions }: LoginProps) => {
|
||||||
onSubmit={handleAccountActivationDialogSubmit}
|
onSubmit={handleAccountActivationDialogSubmit}
|
||||||
handleClose={closeActivateAccountDialog}
|
handleClose={closeActivateAccountDialog}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Root>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
import Dialog from '@mui/material/Dialog';
|
import Dialog from '@mui/material/Dialog';
|
||||||
import DialogContent from '@mui/material/DialogContent';
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
import DialogTitle from '@mui/material/DialogTitle';
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import makeStyles from '@mui/styles/makeStyles';
|
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
|
@ -13,16 +13,21 @@ import { KnownHostForm } from 'forms';
|
||||||
|
|
||||||
import './KnownHostDialog.css';
|
import './KnownHostDialog.css';
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const PREFIX = 'KnownHostDialog';
|
||||||
root: {
|
|
||||||
|
const classes = {
|
||||||
|
root: `${PREFIX}-root`
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledDialog = styled(Dialog)(({ theme }) => ({
|
||||||
|
[`&.${classes.root}`]: {
|
||||||
'& .dialog-title__wrapper': {
|
'& .dialog-title__wrapper': {
|
||||||
borderColor: theme.palette.grey[300]
|
borderColor: theme.palette.grey[300]
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const KnownHostDialog = ({ handleClose, onRemove, onSubmit, isOpen, host }: any) => {
|
const KnownHostDialog = ({ handleClose, onRemove, onSubmit, isOpen, host }: any) => {
|
||||||
const classes = useStyles();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const mode = host ? 'edit' : 'add';
|
const mode = host ? 'edit' : 'add';
|
||||||
|
@ -34,7 +39,7 @@ const KnownHostDialog = ({ handleClose, onRemove, onSubmit, isOpen, host }: any)
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog className={'KnownHostDialog ' + classes.root} onClose={handleOnClose} open={isOpen}>
|
<StyledDialog className={'KnownHostDialog ' + classes.root} onClose={handleOnClose} open={isOpen}>
|
||||||
<DialogTitle className='dialog-title'>
|
<DialogTitle className='dialog-title'>
|
||||||
<div className='dialog-title__wrapper'>
|
<div className='dialog-title__wrapper'>
|
||||||
<Typography variant='h2'>{ t('KnownHostDialog.title', { mode }) }</Typography>
|
<Typography variant='h2'>{ t('KnownHostDialog.title', { mode }) }</Typography>
|
||||||
|
@ -52,7 +57,7 @@ const KnownHostDialog = ({ handleClose, onRemove, onSubmit, isOpen, host }: any)
|
||||||
</Typography>
|
</Typography>
|
||||||
<KnownHostForm onRemove={onRemove} onSubmit={onSubmit} host={host}></KnownHostForm>
|
<KnownHostForm onRemove={onRemove} onSubmit={onSubmit} host={host}></KnownHostForm>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</StyledDialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ export function useReduxEffect(
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
|
||||||
const handleChange = (): void => {
|
const handleChange = (): void => {
|
||||||
const state = store.getState();
|
const state: any = store.getState();
|
||||||
const action = state.action;
|
const action = state.action;
|
||||||
const previousValue = currentValue.current;
|
const previousValue = currentValue.current;
|
||||||
currentValue.current = action.count;
|
currentValue.current = action.count;
|
||||||
|
|
|
@ -17,7 +17,6 @@ body,
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1.4;
|
|
||||||
font-family:
|
font-family:
|
||||||
-apple-system,
|
-apple-system,
|
||||||
BlinkMacSystemFont,
|
BlinkMacSystemFont,
|
||||||
|
|
|
@ -1,26 +1,25 @@
|
||||||
|
import { StrictMode } from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
import { Theme, StyledEngineProvider } from '@mui/material';
|
import { Theme, StyledEngineProvider } from '@mui/material';
|
||||||
import { ThemeProvider } from '@mui/material/styles';
|
import { ThemeProvider } from '@mui/material/styles';
|
||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
|
|
||||||
import { AppShell } from 'containers';
|
|
||||||
|
|
||||||
|
import { AppShell } from './containers';
|
||||||
import { materialTheme } from './material-theme';
|
import { materialTheme } from './material-theme';
|
||||||
import './i18n';
|
|
||||||
|
|
||||||
|
import './i18n';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
declare module '@mui/styles/defaultTheme' {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
||||||
interface DefaultTheme extends Theme {}
|
|
||||||
}
|
|
||||||
|
|
||||||
const AppWithMaterialTheme = () => (
|
const AppWithMaterialTheme = () => (
|
||||||
|
<StrictMode>
|
||||||
<StyledEngineProvider injectFirst>
|
<StyledEngineProvider injectFirst>
|
||||||
<ThemeProvider theme={materialTheme}>
|
<ThemeProvider theme={materialTheme}>
|
||||||
<AppShell />
|
<AppShell />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</StyledEngineProvider>
|
</StyledEngineProvider>
|
||||||
|
</StrictMode>
|
||||||
);
|
);
|
||||||
|
|
||||||
ReactDOM.render(<AppWithMaterialTheme />, document.getElementById('root'));
|
const container = document.getElementById('root');
|
||||||
|
const root = createRoot(container!);
|
||||||
|
|
||||||
|
root.render(<AppWithMaterialTheme />);
|
||||||
|
|
|
@ -215,7 +215,10 @@ export const materialTheme = createTheme({
|
||||||
lineHeight: 1.4,
|
lineHeight: 1.4,
|
||||||
color: palette.grey[500],
|
color: palette.grey[500],
|
||||||
},
|
},
|
||||||
// body1: {},
|
body1: {
|
||||||
|
fontSize: '.75rem',
|
||||||
|
lineHeight: 1.4,
|
||||||
|
},
|
||||||
// body2: {},
|
// body2: {},
|
||||||
// button: {},
|
// button: {},
|
||||||
// caption: {},
|
// caption: {},
|
||||||
|
|
|
@ -1,52 +1,17 @@
|
||||||
import $ from 'jquery';
|
import sanitize from 'sanitize-html';
|
||||||
|
|
||||||
export function sanitizeHtml(msg: string): string {
|
export function sanitizeHtml(msg: string): string {
|
||||||
const $div = $('<div>').html(msg);
|
return sanitize(msg, {
|
||||||
const whitelist = {
|
allowedTags: ['br', 'a', 'img', 'center', 'b', 'font'],
|
||||||
tags: 'br,a,img,center,b,font',
|
allowedAttributes: {
|
||||||
attrs: ['href', 'color'],
|
'*': ['href', 'color', 'rel', 'target'],
|
||||||
href: ['http://', 'https://', 'ftp://', '//']
|
},
|
||||||
};
|
allowedSchemes: ['http', 'https', 'ftp'],
|
||||||
|
transformTags: {
|
||||||
// remove all tags, attributes, and href protocols except some
|
'a': sanitize.simpleTransform('a', {
|
||||||
enforceTagWhitelist($div, whitelist.tags);
|
target: '_blank',
|
||||||
enforceAttrWhitelist($div, whitelist.attrs);
|
rel: 'noopener noreferrer',
|
||||||
enforceHrefWhitelist($div, whitelist.href);
|
}),
|
||||||
|
|
||||||
return $div.html();
|
|
||||||
}
|
|
||||||
|
|
||||||
function enforceTagWhitelist($el: JQuery<HTMLElement>, tags: string): void {
|
|
||||||
$el.find('*').not(tags).each(function enforceTag() {
|
|
||||||
$(this).replaceWith(this.innerHTML);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function enforceAttrWhitelist($el: JQuery<HTMLElement>, attrs: string[]): void {
|
|
||||||
$el.find('*').each(function enforceAttribute() {
|
|
||||||
const attributes = this.attributes;
|
|
||||||
let i = attributes.length;
|
|
||||||
while (i--) {
|
|
||||||
const attr = attributes[i];
|
|
||||||
if ($.inArray(attr.name, attrs) === -1) {
|
|
||||||
this.removeAttributeNode(attr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function enforceHrefWhitelist($el: JQuery<HTMLElement>, hrefs: string[]): void {
|
|
||||||
$el.find('[href]').each(function enforceHref() {
|
|
||||||
const $_el = $(this);
|
|
||||||
const attributeValue = $_el.attr('href');
|
|
||||||
|
|
||||||
for (let protocol in hrefs) {
|
|
||||||
if (attributeValue.indexOf(hrefs[protocol]) === 0) {
|
|
||||||
$_el.attr('target', '_blank');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$_el.removeAttr('href');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue