feat(user-interface): introduce user interface page (#1226)
This commit is contained in:
@@ -8,7 +8,11 @@ type Params = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateConfig = async (_parent: unknown, params: Params, context: Context) => {
|
const updateConfig = async (
|
||||||
|
_parent: unknown,
|
||||||
|
params: Params,
|
||||||
|
context: Context
|
||||||
|
) => {
|
||||||
context.currentUser.can('update', 'Config');
|
context.currentUser.can('update', 'Config');
|
||||||
|
|
||||||
const config = params.input;
|
const config = params.input;
|
||||||
@@ -18,22 +22,26 @@ const updateConfig = async (_parent: unknown, params: Params, context: Context)
|
|||||||
for (const key of configKeys) {
|
for (const key of configKeys) {
|
||||||
const newValue = config[key];
|
const newValue = config[key];
|
||||||
|
|
||||||
const entryUpdate = Config
|
if (newValue) {
|
||||||
.query()
|
const entryUpdate = Config.query()
|
||||||
.insert({
|
.insert({
|
||||||
key,
|
key,
|
||||||
value: {
|
value: {
|
||||||
data: newValue
|
data: newValue,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.onConflict('key')
|
.onConflict('key')
|
||||||
.merge({
|
.merge({
|
||||||
value: {
|
value: {
|
||||||
data: newValue
|
data: newValue,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
updates.push(entryUpdate);
|
updates.push(entryUpdate);
|
||||||
|
} else {
|
||||||
|
const entryUpdate = Config.query().findOne({ key }).delete();
|
||||||
|
updates.push(entryUpdate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(updates);
|
await Promise.all(updates);
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
"graphql": "^15.6.0",
|
"graphql": "^15.6.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"luxon": "^2.3.1",
|
"luxon": "^2.3.1",
|
||||||
|
"mui-color-input": "^2.0.0",
|
||||||
"notistack": "^2.0.2",
|
"notistack": "^2.0.2",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
@@ -6,6 +6,7 @@ import CreateUser from 'pages/CreateUser';
|
|||||||
import Roles from 'pages/Roles/index.ee';
|
import Roles from 'pages/Roles/index.ee';
|
||||||
import CreateRole from 'pages/CreateRole/index.ee';
|
import CreateRole from 'pages/CreateRole/index.ee';
|
||||||
import EditRole from 'pages/EditRole/index.ee';
|
import EditRole from 'pages/EditRole/index.ee';
|
||||||
|
import UserInterface from 'pages/UserInterface';
|
||||||
|
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import Can from 'components/Can';
|
import Can from 'components/Can';
|
||||||
@@ -79,6 +80,17 @@ export default (
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path={URLS.USER_INTERFACE}
|
||||||
|
element={
|
||||||
|
<Can I="update" a="Config">
|
||||||
|
<AdminSettingsLayout>
|
||||||
|
<UserInterface />
|
||||||
|
</AdminSettingsLayout>
|
||||||
|
</Can>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path={URLS.ADMIN_SETTINGS}
|
path={URLS.ADMIN_SETTINGS}
|
||||||
element={<Navigate to={URLS.USERS} replace />}
|
element={<Navigate to={URLS.USERS} replace />}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
|
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
|
||||||
import GroupIcon from '@mui/icons-material/Group';
|
import GroupIcon from '@mui/icons-material/Group';
|
||||||
import GroupsIcon from '@mui/icons-material/Groups';
|
import GroupsIcon from '@mui/icons-material/Groups';
|
||||||
|
import BrushIcon from '@mui/icons-material/Brush';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Toolbar from '@mui/material/Toolbar';
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
@@ -18,25 +19,43 @@ type SettingsLayoutProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type DrawerLink = {
|
type DrawerLink = {
|
||||||
Icon: SvgIconComponent,
|
Icon: SvgIconComponent;
|
||||||
primary: string,
|
primary: string;
|
||||||
to: string,
|
to: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
function createDrawerLinks({ canReadRole, canReadUser }: { canReadRole: boolean; canReadUser: boolean; }) {
|
function createDrawerLinks({
|
||||||
|
canReadRole,
|
||||||
|
canReadUser,
|
||||||
|
canUpdateConfig,
|
||||||
|
}: {
|
||||||
|
canReadRole: boolean;
|
||||||
|
canReadUser: boolean;
|
||||||
|
canUpdateConfig: boolean;
|
||||||
|
}) {
|
||||||
const items = [
|
const items = [
|
||||||
canReadUser ? {
|
canReadUser
|
||||||
|
? {
|
||||||
Icon: GroupIcon,
|
Icon: GroupIcon,
|
||||||
primary: 'adminSettingsDrawer.users',
|
primary: 'adminSettingsDrawer.users',
|
||||||
to: URLS.USERS,
|
to: URLS.USERS,
|
||||||
} : null,
|
}
|
||||||
canReadRole ? {
|
: null,
|
||||||
|
canReadRole
|
||||||
|
? {
|
||||||
Icon: GroupsIcon,
|
Icon: GroupsIcon,
|
||||||
primary: 'adminSettingsDrawer.roles',
|
primary: 'adminSettingsDrawer.roles',
|
||||||
to: URLS.ROLES,
|
to: URLS.ROLES,
|
||||||
} : null
|
}
|
||||||
]
|
: null,
|
||||||
.filter(Boolean) as DrawerLink[];
|
canUpdateConfig
|
||||||
|
? {
|
||||||
|
Icon: BrushIcon,
|
||||||
|
primary: 'adminSettingsDrawer.userInterface',
|
||||||
|
to: URLS.USER_INTERFACE,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
].filter(Boolean) as DrawerLink[];
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
@@ -62,6 +81,7 @@ export default function SettingsLayout({
|
|||||||
const drawerLinks = createDrawerLinks({
|
const drawerLinks = createDrawerLinks({
|
||||||
canReadUser: currentUserAbility.can('read', 'User'),
|
canReadUser: currentUserAbility.can('read', 'User'),
|
||||||
canReadRole: currentUserAbility.can('read', 'Role'),
|
canReadRole: currentUserAbility.can('read', 'Role'),
|
||||||
|
canUpdateConfig: currentUserAbility.can('update', 'Config'),
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
41
packages/web/src/components/ColorInput/index.tsx
Normal file
41
packages/web/src/components/ColorInput/index.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { Controller, useFormContext } from 'react-hook-form';
|
||||||
|
import { MuiColorInput, MuiColorInputProps } from 'mui-color-input';
|
||||||
|
|
||||||
|
type ColorInputProps = {
|
||||||
|
shouldUnregister?: boolean;
|
||||||
|
name: string;
|
||||||
|
'data-test'?: string;
|
||||||
|
} & Partial<MuiColorInputProps>;
|
||||||
|
|
||||||
|
export default function ColorInput(props: ColorInputProps): React.ReactElement {
|
||||||
|
const { control } = useFormContext();
|
||||||
|
const {
|
||||||
|
required,
|
||||||
|
name,
|
||||||
|
shouldUnregister = false,
|
||||||
|
disabled = false,
|
||||||
|
'data-test': dataTest,
|
||||||
|
...textFieldProps
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Controller
|
||||||
|
rules={{ required }}
|
||||||
|
name={name}
|
||||||
|
control={control}
|
||||||
|
shouldUnregister={shouldUnregister}
|
||||||
|
render={({ field }) => (
|
||||||
|
<MuiColorInput
|
||||||
|
format="hex"
|
||||||
|
{...textFieldProps}
|
||||||
|
{...field}
|
||||||
|
disabled={disabled}
|
||||||
|
inputProps={{
|
||||||
|
'data-test': dataTest,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@@ -3,12 +3,12 @@ import appConfig from './app';
|
|||||||
export const CONNECTIONS = '/connections';
|
export const CONNECTIONS = '/connections';
|
||||||
export const EXECUTIONS = '/executions';
|
export const EXECUTIONS = '/executions';
|
||||||
export const EXECUTION_PATTERN = '/executions/:executionId';
|
export const EXECUTION_PATTERN = '/executions/:executionId';
|
||||||
export const EXECUTION = (executionId: string) =>
|
export const EXECUTION = (executionId: string) => `/executions/${executionId}`;
|
||||||
`/executions/${executionId}`;
|
|
||||||
|
|
||||||
export const LOGIN = '/login';
|
export const LOGIN = '/login';
|
||||||
export const LOGIN_CALLBACK = `${LOGIN}/callback`;
|
export const LOGIN_CALLBACK = `${LOGIN}/callback`;
|
||||||
export const SSO_LOGIN = (issuer: string) => `${appConfig.apiUrl}/login/saml/${issuer}`;
|
export const SSO_LOGIN = (issuer: string) =>
|
||||||
|
`${appConfig.apiUrl}/login/saml/${issuer}`;
|
||||||
export const SIGNUP = '/sign-up';
|
export const SIGNUP = '/sign-up';
|
||||||
export const FORGOT_PASSWORD = '/forgot-password';
|
export const FORGOT_PASSWORD = '/forgot-password';
|
||||||
export const RESET_PASSWORD = '/reset-password';
|
export const RESET_PASSWORD = '/reset-password';
|
||||||
@@ -17,18 +17,19 @@ export const APPS = '/apps';
|
|||||||
export const NEW_APP_CONNECTION = '/apps/new';
|
export const NEW_APP_CONNECTION = '/apps/new';
|
||||||
export const APP = (appKey: string) => `/app/${appKey}`;
|
export const APP = (appKey: string) => `/app/${appKey}`;
|
||||||
export const APP_PATTERN = '/app/:appKey';
|
export const APP_PATTERN = '/app/:appKey';
|
||||||
export const APP_CONNECTIONS = (appKey: string) =>
|
export const APP_CONNECTIONS = (appKey: string) => `/app/${appKey}/connections`;
|
||||||
`/app/${appKey}/connections`;
|
|
||||||
export const APP_CONNECTIONS_PATTERN = '/app/:appKey/connections';
|
export const APP_CONNECTIONS_PATTERN = '/app/:appKey/connections';
|
||||||
export const APP_ADD_CONNECTION = (appKey: string, shared = false) =>
|
export const APP_ADD_CONNECTION = (appKey: string, shared = false) =>
|
||||||
`/app/${appKey}/connections/add?shared=${shared}`;
|
`/app/${appKey}/connections/add?shared=${shared}`;
|
||||||
export const APP_ADD_CONNECTION_WITH_AUTH_CLIENT_ID = (appKey: string, appAuthClientId: string) =>
|
export const APP_ADD_CONNECTION_WITH_AUTH_CLIENT_ID = (
|
||||||
`/app/${appKey}/connections/add?appAuthClientId=${appAuthClientId}`;
|
appKey: string,
|
||||||
|
appAuthClientId: string
|
||||||
|
) => `/app/${appKey}/connections/add?appAuthClientId=${appAuthClientId}`;
|
||||||
export const APP_ADD_CONNECTION_PATTERN = '/app/:appKey/connections/add';
|
export const APP_ADD_CONNECTION_PATTERN = '/app/:appKey/connections/add';
|
||||||
export const APP_RECONNECT_CONNECTION = (
|
export const APP_RECONNECT_CONNECTION = (
|
||||||
appKey: string,
|
appKey: string,
|
||||||
connectionId: string,
|
connectionId: string,
|
||||||
appAuthClientId?: string,
|
appAuthClientId?: string
|
||||||
) => {
|
) => {
|
||||||
const path = `/app/${appKey}/connections/${connectionId}/reconnect`;
|
const path = `/app/${appKey}/connections/${connectionId}/reconnect`;
|
||||||
|
|
||||||
@@ -96,6 +97,7 @@ export const ROLES = `${ADMIN_SETTINGS}/roles`;
|
|||||||
export const ROLE = (roleId: string) => `${ROLES}/${roleId}`;
|
export const ROLE = (roleId: string) => `${ROLES}/${roleId}`;
|
||||||
export const ROLE_PATTERN = `${ROLES}/:roleId`;
|
export const ROLE_PATTERN = `${ROLES}/:roleId`;
|
||||||
export const CREATE_ROLE = `${ROLES}/create`;
|
export const CREATE_ROLE = `${ROLES}/create`;
|
||||||
|
export const USER_INTERFACE = `${ADMIN_SETTINGS}/user-interface`;
|
||||||
|
|
||||||
export const DASHBOARD = FLOWS;
|
export const DASHBOARD = FLOWS;
|
||||||
|
|
||||||
|
18
packages/web/src/helpers/nestObject.ts
Normal file
18
packages/web/src/helpers/nestObject.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
import set from 'lodash/set';
|
||||||
|
|
||||||
|
export default function nestObject<T = IJSONObject>(
|
||||||
|
config: IJSONObject | undefined
|
||||||
|
): Partial<T> {
|
||||||
|
if (!config) return {};
|
||||||
|
const result = {};
|
||||||
|
|
||||||
|
for (const key in config) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(config, key)) {
|
||||||
|
const value = config[key];
|
||||||
|
set(result, key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
@@ -15,6 +15,7 @@
|
|||||||
"settingsDrawer.billingAndUsage": "Billing and usage",
|
"settingsDrawer.billingAndUsage": "Billing and usage",
|
||||||
"adminSettingsDrawer.users": "Users",
|
"adminSettingsDrawer.users": "Users",
|
||||||
"adminSettingsDrawer.roles": "Roles",
|
"adminSettingsDrawer.roles": "Roles",
|
||||||
|
"adminSettingsDrawer.userInterface": "User Interface",
|
||||||
"adminSettingsDrawer.goBack": "Go to the dashboard",
|
"adminSettingsDrawer.goBack": "Go to the dashboard",
|
||||||
"app.connectionCount": "{count} connections",
|
"app.connectionCount": "{count} connections",
|
||||||
"app.flowCount": "{count} flows",
|
"app.flowCount": "{count} flows",
|
||||||
@@ -213,5 +214,12 @@
|
|||||||
"permissionSettings.cancel": "Cancel",
|
"permissionSettings.cancel": "Cancel",
|
||||||
"permissionSettings.apply": "Apply",
|
"permissionSettings.apply": "Apply",
|
||||||
"permissionSettings.title": "Conditions",
|
"permissionSettings.title": "Conditions",
|
||||||
"appAuthClientsDialog.title": "Choose your authentication client"
|
"appAuthClientsDialog.title": "Choose your authentication client",
|
||||||
|
"userInterfacePage.title": "User Interface",
|
||||||
|
"userInterfacePage.successfullyUpdated": "User interface has been updated.",
|
||||||
|
"userInterfacePage.mainColor": "Primary main color",
|
||||||
|
"userInterfacePage.darkColor": "Primary dark color",
|
||||||
|
"userInterfacePage.lightColor": "Primary light color",
|
||||||
|
"userInterfacePage.svgData": "Logo SVG code",
|
||||||
|
"userInterfacePage.submit": "Update"
|
||||||
}
|
}
|
||||||
|
130
packages/web/src/pages/UserInterface/index.tsx
Normal file
130
packages/web/src/pages/UserInterface/index.tsx
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { useMutation } from '@apollo/client';
|
||||||
|
import Container from '@mui/material/Container';
|
||||||
|
import Grid from '@mui/material/Grid';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import LoadingButton from '@mui/lab/LoadingButton';
|
||||||
|
import { useSnackbar } from 'notistack';
|
||||||
|
|
||||||
|
import { UPDATE_CONFIG } from 'graphql/mutations/update-config.ee';
|
||||||
|
import useConfig from 'hooks/useConfig';
|
||||||
|
import PageTitle from 'components/PageTitle';
|
||||||
|
import Form from 'components/Form';
|
||||||
|
import TextField from 'components/TextField';
|
||||||
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
import ColorInput from 'components/ColorInput';
|
||||||
|
import nestObject from 'helpers/nestObject';
|
||||||
|
import { Skeleton } from '@mui/material';
|
||||||
|
|
||||||
|
type UserInterface = {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
dark: string;
|
||||||
|
light: string;
|
||||||
|
main: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
logo: {
|
||||||
|
svgData: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function UserInterface(): React.ReactElement {
|
||||||
|
const formatMessage = useFormatMessage();
|
||||||
|
const [updateConfig, { loading }] = useMutation(UPDATE_CONFIG, {
|
||||||
|
refetchQueries: ['GetConfig'],
|
||||||
|
});
|
||||||
|
const { config, loading: configLoading } = useConfig([
|
||||||
|
'palette.primary.main',
|
||||||
|
'palette.primary.light',
|
||||||
|
'palette.primary.dark',
|
||||||
|
'logo.svgData',
|
||||||
|
]);
|
||||||
|
const { enqueueSnackbar } = useSnackbar();
|
||||||
|
|
||||||
|
const handleUserInterfaceUpdate = async (uiData: Partial<UserInterface>) => {
|
||||||
|
try {
|
||||||
|
await updateConfig({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
'palette.primary.main': uiData?.palette?.primary.main,
|
||||||
|
'palette.primary.dark': uiData?.palette?.primary.dark,
|
||||||
|
'palette.primary.light': uiData?.palette?.primary.light,
|
||||||
|
'logo.svgData': uiData?.logo?.svgData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
enqueueSnackbar(formatMessage('userInterfacePage.successfullyUpdated'), {
|
||||||
|
variant: 'success',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error('Failed while updating!');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||||
|
<Grid container item xs={12} sm={9} md={8} lg={6}>
|
||||||
|
<Grid item xs={12} sx={{ mb: [2, 5] }}>
|
||||||
|
<PageTitle>{formatMessage('userInterfacePage.title')}</PageTitle>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid item xs={12} justifyContent="flex-end" sx={{ pt: 5 }}>
|
||||||
|
{configLoading && (
|
||||||
|
<Stack direction="column" gap={2}>
|
||||||
|
<Skeleton variant="rounded" height={55} />
|
||||||
|
<Skeleton variant="rounded" height={55} />
|
||||||
|
<Skeleton variant="rounded" height={55} />
|
||||||
|
<Skeleton variant="rounded" height={85} />
|
||||||
|
<Skeleton variant="rounded" height={45} />
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{!configLoading && (
|
||||||
|
<Form
|
||||||
|
onSubmit={handleUserInterfaceUpdate}
|
||||||
|
defaultValues={nestObject<UserInterface>(config)}
|
||||||
|
>
|
||||||
|
<Stack direction="column" gap={2}>
|
||||||
|
<ColorInput
|
||||||
|
name="palette.primary.main"
|
||||||
|
label={formatMessage('userInterfacePage.mainColor')}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ColorInput
|
||||||
|
name="palette.primary.dark"
|
||||||
|
label={formatMessage('userInterfacePage.darkColor')}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ColorInput
|
||||||
|
name="palette.primary.light"
|
||||||
|
label={formatMessage('userInterfacePage.lightColor')}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
name="logo.svgData"
|
||||||
|
label={formatMessage('userInterfacePage.svgData')}
|
||||||
|
multiline
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LoadingButton
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
sx={{ boxShadow: 2 }}
|
||||||
|
loading={loading}
|
||||||
|
>
|
||||||
|
{formatMessage('userInterfacePage.submit')}
|
||||||
|
</LoadingButton>
|
||||||
|
</Stack>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
12
yarn.lock
12
yarn.lock
@@ -1469,6 +1469,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-12.0.0.tgz#a9583a75c3f150667771f30b60d9f059473e62c4"
|
resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-12.0.0.tgz#a9583a75c3f150667771f30b60d9f059473e62c4"
|
||||||
integrity sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==
|
integrity sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==
|
||||||
|
|
||||||
|
"@ctrl/tinycolor@^3.6.0":
|
||||||
|
version "3.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.0.tgz#53fa5fe9c34faee89469e48f91d51a3766108bc8"
|
||||||
|
integrity sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ==
|
||||||
|
|
||||||
"@dabh/diagnostics@^2.0.2":
|
"@dabh/diagnostics@^2.0.2":
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31"
|
resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31"
|
||||||
@@ -12375,6 +12380,13 @@ msgpackr@^1.6.2:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
msgpackr-extract "^2.1.2"
|
msgpackr-extract "^2.1.2"
|
||||||
|
|
||||||
|
mui-color-input@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mui-color-input/-/mui-color-input-2.0.0.tgz#49c8df63d3d18f1a1817572c0efc15bd970b35a2"
|
||||||
|
integrity sha512-Xw6OGsZVbtlZEAUVgJ08Lyv4u0YDQH+aTMJhhWm2fRin+1T+0IrVFyBtbSjJjrH4aBkkQPMCm75//7qO9zncLw==
|
||||||
|
dependencies:
|
||||||
|
"@ctrl/tinycolor" "^3.6.0"
|
||||||
|
|
||||||
multer@1.4.5-lts.1:
|
multer@1.4.5-lts.1:
|
||||||
version "1.4.5-lts.1"
|
version "1.4.5-lts.1"
|
||||||
resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac"
|
resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac"
|
||||||
|
Reference in New Issue
Block a user