refactor(web): remove typescript
This commit is contained in:
@@ -15,11 +15,9 @@ import Box from '@mui/material/Box';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Tabs from '@mui/material/Tabs';
|
||||
import Tab from '@mui/material/Tab';
|
||||
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import { GET_APP } from 'graphql/queries/get-app';
|
||||
import * as URLS from 'config/urls';
|
||||
|
||||
import AppIcon from 'components/AppIcon';
|
||||
import Container from 'components/Container';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
@@ -27,17 +25,11 @@ import AdminApplicationSettings from 'components/AdminApplicationSettings';
|
||||
import AdminApplicationAuthClients from 'components/AdminApplicationAuthClients';
|
||||
import AdminApplicationCreateAuthClient from 'components/AdminApplicationCreateAuthClient';
|
||||
import AdminApplicationUpdateAuthClient from 'components/AdminApplicationUpdateAuthClient';
|
||||
|
||||
type AdminApplicationParams = {
|
||||
appKey: string;
|
||||
};
|
||||
|
||||
export default function AdminApplication(): React.ReactElement | null {
|
||||
export default function AdminApplication() {
|
||||
const theme = useTheme();
|
||||
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md'));
|
||||
const formatMessage = useFormatMessage();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const connectionsPathMatch = useMatch({
|
||||
path: URLS.ADMIN_APP_CONNECTIONS_PATTERN,
|
||||
end: false,
|
||||
@@ -50,16 +42,11 @@ export default function AdminApplication(): React.ReactElement | null {
|
||||
path: URLS.ADMIN_APP_AUTH_CLIENTS_PATTERN,
|
||||
end: false,
|
||||
});
|
||||
|
||||
const { appKey } = useParams() as AdminApplicationParams;
|
||||
const { appKey } = useParams();
|
||||
const { data, loading } = useQuery(GET_APP, { variables: { key: appKey } });
|
||||
|
||||
const app = data?.getApp || {};
|
||||
|
||||
const goToAuthClientsPage = () => navigate('auth-clients');
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
@@ -3,17 +3,13 @@ import Grid from '@mui/material/Grid';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { IApp } from 'types';
|
||||
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import Container from 'components/Container';
|
||||
import SearchInput from 'components/SearchInput';
|
||||
import AppRow from 'components/AppRow';
|
||||
|
||||
import * as URLS from 'config/urls';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import { GET_APPS } from 'graphql/queries/get-apps';
|
||||
|
||||
function AdminApplications() {
|
||||
const formatMessage = useFormatMessage();
|
||||
const [appName, setAppName] = React.useState(null);
|
||||
@@ -21,11 +17,9 @@ function AdminApplications() {
|
||||
variables: { name: appName },
|
||||
});
|
||||
const apps = data?.getApps;
|
||||
|
||||
const onSearchChange = React.useCallback((event) => {
|
||||
setAppName(event.target.value);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Grid container item xs={12} sm={10} md={9}>
|
||||
@@ -50,7 +44,7 @@ function AdminApplications() {
|
||||
)}
|
||||
|
||||
{!appsLoading &&
|
||||
apps?.map((app: IApp) => (
|
||||
apps?.map((app) => (
|
||||
<Grid item xs={12} key={app.name}>
|
||||
<AppRow application={app} url={URLS.ADMIN_APP(app.key)} />
|
||||
</Grid>
|
||||
@@ -59,5 +53,4 @@ function AdminApplications() {
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export default AdminApplications;
|
@@ -17,12 +17,10 @@ import Grid from '@mui/material/Grid';
|
||||
import Tabs from '@mui/material/Tabs';
|
||||
import Tab from '@mui/material/Tab';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useAppConfig from 'hooks/useAppConfig.ee';
|
||||
import { GET_APP } from 'graphql/queries/get-app';
|
||||
import * as URLS from 'config/urls';
|
||||
|
||||
import SplitButton from 'components/SplitButton';
|
||||
import ConditionalIconButton from 'components/ConditionalIconButton';
|
||||
import AppConnections from 'components/AppConnections';
|
||||
@@ -31,23 +29,9 @@ import AddAppConnection from 'components/AddAppConnection';
|
||||
import AppIcon from 'components/AppIcon';
|
||||
import Container from 'components/Container';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
|
||||
type ApplicationParams = {
|
||||
appKey: string;
|
||||
connectionId?: string;
|
||||
};
|
||||
|
||||
type ConnectionOption = {
|
||||
key: string;
|
||||
label: string;
|
||||
'data-test': string;
|
||||
to: string;
|
||||
};
|
||||
|
||||
const ReconnectConnection = (props: any): React.ReactElement => {
|
||||
const ReconnectConnection = (props) => {
|
||||
const { application, onClose } = props;
|
||||
const { connectionId } = useParams() as ApplicationParams;
|
||||
|
||||
const { connectionId } = useParams();
|
||||
return (
|
||||
<AddAppConnection
|
||||
onClose={onClose}
|
||||
@@ -56,8 +40,7 @@ const ReconnectConnection = (props: any): React.ReactElement => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Application(): React.ReactElement | null {
|
||||
export default function Application() {
|
||||
const theme = useTheme();
|
||||
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md'));
|
||||
const formatMessage = useFormatMessage();
|
||||
@@ -67,19 +50,17 @@ export default function Application(): React.ReactElement | null {
|
||||
});
|
||||
const flowsPathMatch = useMatch({ path: URLS.APP_FLOWS_PATTERN, end: false });
|
||||
const [searchParams] = useSearchParams();
|
||||
const { appKey } = useParams() as ApplicationParams;
|
||||
const { appKey } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const { data, loading } = useQuery(GET_APP, { variables: { key: appKey } });
|
||||
const { appConfig } = useAppConfig(appKey);
|
||||
|
||||
const connectionId = searchParams.get('connectionId') || undefined;
|
||||
const goToApplicationPage = () => navigate('connections');
|
||||
const app = data?.getApp || {};
|
||||
|
||||
const connectionOptions = React.useMemo(() => {
|
||||
const shouldHaveCustomConnection =
|
||||
appConfig?.canConnect && appConfig?.canCustomConnect;
|
||||
const options: ConnectionOption[] = [
|
||||
const options = [
|
||||
{
|
||||
label: formatMessage('app.addConnection'),
|
||||
key: 'addConnection',
|
||||
@@ -87,7 +68,6 @@ export default function Application(): React.ReactElement | null {
|
||||
to: URLS.APP_ADD_CONNECTION(appKey, appConfig?.canConnect),
|
||||
},
|
||||
];
|
||||
|
||||
if (shouldHaveCustomConnection) {
|
||||
options.push({
|
||||
label: formatMessage('app.addCustomConnection'),
|
||||
@@ -96,12 +76,9 @@ export default function Application(): React.ReactElement | null {
|
||||
to: URLS.APP_ADD_CONNECTION(appKey),
|
||||
});
|
||||
}
|
||||
|
||||
return options;
|
||||
}, [appKey, appConfig]);
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={{ py: 3 }}>
|
||||
@@ -132,7 +109,7 @@ export default function Application(): React.ReactElement | null {
|
||||
component={Link}
|
||||
to={URLS.CREATE_FLOW_WITH_APP_AND_CONNECTION(
|
||||
appKey,
|
||||
connectionId
|
||||
connectionId,
|
||||
)}
|
||||
fullWidth
|
||||
icon={<AddIcon />}
|
@@ -6,8 +6,6 @@ import Grid from '@mui/material/Grid';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import type { IApp } from 'types';
|
||||
|
||||
import Can from 'components/Can';
|
||||
import NoResultFound from 'components/NoResultFound';
|
||||
import ConditionalIconButton from 'components/ConditionalIconButton';
|
||||
@@ -19,26 +17,21 @@ import SearchInput from 'components/SearchInput';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import { GET_CONNECTED_APPS } from 'graphql/queries/get-connected-apps';
|
||||
import * as URLS from 'config/urls';
|
||||
|
||||
export default function Applications(): React.ReactElement {
|
||||
export default function Applications() {
|
||||
const navigate = useNavigate();
|
||||
const formatMessage = useFormatMessage();
|
||||
const [appName, setAppName] = React.useState(null);
|
||||
const { data, loading } = useQuery(GET_CONNECTED_APPS, {
|
||||
variables: { name: appName },
|
||||
});
|
||||
|
||||
const apps: IApp[] = data?.getConnectedApps;
|
||||
const apps = data?.getConnectedApps;
|
||||
const hasApps = apps?.length;
|
||||
|
||||
const onSearchChange = React.useCallback((event) => {
|
||||
setAppName(event.target.value);
|
||||
}, []);
|
||||
|
||||
const goToApps = React.useCallback(() => {
|
||||
navigate(URLS.APPS);
|
||||
}, [navigate]);
|
||||
|
||||
return (
|
||||
<Box sx={{ py: 3 }}>
|
||||
<Container>
|
||||
@@ -97,7 +90,7 @@ export default function Applications(): React.ReactElement {
|
||||
)}
|
||||
|
||||
{!loading &&
|
||||
apps?.map((app: IApp) => (
|
||||
apps?.map((app) => (
|
||||
<AppRow key={app.name} application={app} url={URLS.APP(app.key)} />
|
||||
))}
|
||||
|
@@ -1,36 +1,25 @@
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { TSamlAuthProvider, TSamlAuthProviderRole } from 'types';
|
||||
import LoadingButton from '@mui/lab/LoadingButton';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import Form from 'components/Form';
|
||||
import { UPSERT_SAML_AUTH_PROVIDERS_ROLE_MAPPINGS } from 'graphql/mutations/upsert-saml-auth-providers-role-mappings';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useSamlAuthProviderRoleMappings from 'hooks/useSamlAuthProviderRoleMappings';
|
||||
|
||||
import RoleMappingsFieldArray from './RoleMappingsFieldsArray';
|
||||
|
||||
type RoleMappingsProps = {
|
||||
provider?: TSamlAuthProvider;
|
||||
providerLoading: boolean;
|
||||
};
|
||||
|
||||
function generateFormRoleMappings(roleMappings: TSamlAuthProviderRole[]) {
|
||||
function generateFormRoleMappings(roleMappings) {
|
||||
if (roleMappings.length === 0) {
|
||||
return [{ roleId: '', remoteRoleName: '' }];
|
||||
}
|
||||
|
||||
return roleMappings.map(({ roleId, remoteRoleName }) => ({
|
||||
roleId,
|
||||
remoteRoleName,
|
||||
}));
|
||||
}
|
||||
|
||||
function RoleMappings({ provider, providerLoading }: RoleMappingsProps) {
|
||||
function RoleMappings({ provider, providerLoading }) {
|
||||
const formatMessage = useFormatMessage();
|
||||
const enqueueSnackbar = useEnqueueSnackbar();
|
||||
const { roleMappings, loading: roleMappingsLoading } =
|
||||
@@ -39,8 +28,7 @@ function RoleMappings({ provider, providerLoading }: RoleMappingsProps) {
|
||||
upsertSamlAuthProvidersRoleMappings,
|
||||
{ loading: upsertRoleMappingsLoading },
|
||||
] = useMutation(UPSERT_SAML_AUTH_PROVIDERS_ROLE_MAPPINGS);
|
||||
|
||||
const handleRoleMappingsUpdate = async (values: any) => {
|
||||
const handleRoleMappingsUpdate = async (values) => {
|
||||
try {
|
||||
if (provider?.id) {
|
||||
await upsertSamlAuthProvidersRoleMappings({
|
||||
@@ -48,16 +36,10 @@ function RoleMappings({ provider, providerLoading }: RoleMappingsProps) {
|
||||
input: {
|
||||
samlAuthProviderId: provider.id,
|
||||
samlAuthProvidersRoleMappings: values.roleMappings.map(
|
||||
({
|
||||
({ roleId, remoteRoleName }) => ({
|
||||
roleId,
|
||||
remoteRoleName,
|
||||
}: {
|
||||
roleId: string;
|
||||
remoteRoleName: string;
|
||||
}) => ({
|
||||
roleId,
|
||||
remoteRoleName,
|
||||
})
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
@@ -73,18 +55,15 @@ function RoleMappings({ provider, providerLoading }: RoleMappingsProps) {
|
||||
throw new Error('Failed while saving!');
|
||||
}
|
||||
};
|
||||
|
||||
const defaultValues = useMemo(
|
||||
() => ({
|
||||
roleMappings: generateFormRoleMappings(roleMappings),
|
||||
}),
|
||||
[roleMappings]
|
||||
[roleMappings],
|
||||
);
|
||||
|
||||
if (providerLoading || !provider?.id || roleMappingsLoading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Divider sx={{ pt: 2 }} />
|
||||
@@ -108,5 +87,4 @@ function RoleMappings({ provider, providerLoading }: RoleMappingsProps) {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default RoleMappings;
|
@@ -1,22 +1,17 @@
|
||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||
import { IRole } from 'types';
|
||||
import MuiTextField from '@mui/material/TextField';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Button from '@mui/material/Button';
|
||||
|
||||
import useRoles from 'hooks/useRoles.ee';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
|
||||
import ControlledAutocomplete from 'components/ControlledAutocomplete';
|
||||
import TextField from 'components/TextField';
|
||||
import { Divider, Typography } from '@mui/material';
|
||||
|
||||
function generateRoleOptions(roles: IRole[]) {
|
||||
function generateRoleOptions(roles) {
|
||||
return roles?.map(({ name: label, id: value }) => ({ label, value }));
|
||||
}
|
||||
|
||||
function RoleMappingsFieldArray() {
|
||||
const formatMessage = useFormatMessage();
|
||||
const { control } = useFormContext();
|
||||
@@ -25,9 +20,8 @@ function RoleMappingsFieldArray() {
|
||||
control,
|
||||
name: 'roleMappings',
|
||||
});
|
||||
|
||||
const handleAppendMapping = () => append({ roleId: '', remoteRoleName: '' });
|
||||
const handleRemoveMapping = (index: number) => () => remove(index);
|
||||
const handleRemoveMapping = (index) => () => remove(index);
|
||||
return (
|
||||
<>
|
||||
{fields.length === 0 && (
|
||||
@@ -89,5 +83,4 @@ function RoleMappingsFieldArray() {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default RoleMappingsFieldArray;
|
@@ -1,26 +1,16 @@
|
||||
import { QueryResult, useMutation } from '@apollo/client';
|
||||
import { IRole, TSamlAuthProvider } from 'types';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import LoadingButton from '@mui/lab/LoadingButton';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import MuiTextField from '@mui/material/TextField';
|
||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
import * as React from 'react';
|
||||
|
||||
import ControlledAutocomplete from 'components/ControlledAutocomplete';
|
||||
import Form from 'components/Form';
|
||||
import Switch from 'components/Switch';
|
||||
import TextField from 'components/TextField';
|
||||
|
||||
import { UPSERT_SAML_AUTH_PROVIDER } from 'graphql/mutations/upsert-saml-auth-provider';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useRoles from 'hooks/useRoles.ee';
|
||||
|
||||
type SamlConfigurationProps = {
|
||||
provider?: TSamlAuthProvider;
|
||||
providerLoading: boolean;
|
||||
refetchProvider: QueryResult<TSamlAuthProvider | undefined>['refetch'];
|
||||
};
|
||||
|
||||
const defaultValues = {
|
||||
active: false,
|
||||
name: '',
|
||||
@@ -34,26 +24,17 @@ const defaultValues = {
|
||||
roleAttributeName: '',
|
||||
defaultRoleId: '',
|
||||
};
|
||||
|
||||
function generateRoleOptions(roles: IRole[]) {
|
||||
function generateRoleOptions(roles) {
|
||||
return roles?.map(({ name: label, id: value }) => ({ label, value }));
|
||||
}
|
||||
|
||||
function SamlConfiguration({
|
||||
provider,
|
||||
providerLoading,
|
||||
refetchProvider,
|
||||
}: SamlConfigurationProps) {
|
||||
function SamlConfiguration({ provider, providerLoading, refetchProvider }) {
|
||||
const formatMessage = useFormatMessage();
|
||||
const { roles, loading: rolesLoading } = useRoles();
|
||||
const enqueueSnackbar = useEnqueueSnackbar();
|
||||
const [upsertSamlAuthProvider, { loading }] = useMutation(
|
||||
UPSERT_SAML_AUTH_PROVIDER
|
||||
UPSERT_SAML_AUTH_PROVIDER,
|
||||
);
|
||||
|
||||
const handleProviderUpdate = async (
|
||||
providerDataToUpdate: Partial<TSamlAuthProvider>
|
||||
) => {
|
||||
const handleProviderUpdate = async (providerDataToUpdate) => {
|
||||
try {
|
||||
const {
|
||||
name,
|
||||
@@ -68,7 +49,6 @@ function SamlConfiguration({
|
||||
active,
|
||||
defaultRoleId,
|
||||
} = providerDataToUpdate;
|
||||
|
||||
await upsertSamlAuthProvider({
|
||||
variables: {
|
||||
input: {
|
||||
@@ -86,11 +66,9 @@ function SamlConfiguration({
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!provider?.id) {
|
||||
await refetchProvider();
|
||||
}
|
||||
|
||||
enqueueSnackbar(formatMessage('authenticationForm.successfullySaved'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
@@ -101,11 +79,9 @@ function SamlConfiguration({
|
||||
throw new Error('Failed while saving!');
|
||||
}
|
||||
};
|
||||
|
||||
if (providerLoading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Form
|
||||
defaultValues={provider || defaultValues}
|
||||
@@ -209,5 +185,4 @@ function SamlConfiguration({
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
export default SamlConfiguration;
|
@@ -1,15 +1,11 @@
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Stack from '@mui/material/Stack';
|
||||
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import Container from 'components/Container';
|
||||
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useSamlAuthProvider from 'hooks/useSamlAuthProvider';
|
||||
|
||||
import SamlConfiguration from './SamlConfiguration';
|
||||
import RoleMappings from './RoleMappings';
|
||||
|
||||
function AuthenticationPage() {
|
||||
const formatMessage = useFormatMessage();
|
||||
const {
|
||||
@@ -17,7 +13,6 @@ function AuthenticationPage() {
|
||||
loading: providerLoading,
|
||||
refetch: refetchProvider,
|
||||
} = useSamlAuthProvider();
|
||||
|
||||
return (
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Grid container item xs={12} sm={10} md={9}>
|
||||
@@ -41,5 +36,4 @@ function AuthenticationPage() {
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export default AuthenticationPage;
|
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import Grid from '@mui/material/Grid';
|
||||
|
||||
import * as URLS from 'config/urls';
|
||||
import UsageDataInformation from 'components/UsageDataInformation/index.ee';
|
||||
import Invoices from 'components/Invoices/index.ee';
|
||||
@@ -9,20 +8,16 @@ import PageTitle from 'components/PageTitle';
|
||||
import Container from 'components/Container';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useCloud from 'hooks/useCloud';
|
||||
|
||||
function BillingAndUsageSettings() {
|
||||
const isCloud = useCloud();
|
||||
const formatMessage = useFormatMessage();
|
||||
|
||||
// redirect to the initial settings page
|
||||
if (isCloud === false) {
|
||||
return <Navigate to={URLS.SETTINGS} replace={true} />;
|
||||
}
|
||||
|
||||
// render nothing until we know if it's cloud or not
|
||||
// here, `isCloud` is not `false`, but `undefined`
|
||||
if (!isCloud) return <React.Fragment />;
|
||||
|
||||
return (
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Grid container item xs={12} sm={9} md={8}>
|
||||
@@ -43,5 +38,4 @@ function BillingAndUsageSettings() {
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export default BillingAndUsageSettings;
|
@@ -6,31 +6,22 @@ import PermissionCatalogField from 'components/PermissionCatalogField/index.ee';
|
||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
import * as React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import Container from 'components/Container';
|
||||
import Form from 'components/Form';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import TextField from 'components/TextField';
|
||||
import * as URLS from 'config/urls';
|
||||
import { CREATE_ROLE } from 'graphql/mutations/create-role.ee';
|
||||
import {
|
||||
RoleWithComputedPermissions,
|
||||
getPermissions,
|
||||
} from 'helpers/computePermissions.ee';
|
||||
import { getPermissions } from 'helpers/computePermissions.ee';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
|
||||
export default function CreateRole(): React.ReactElement {
|
||||
export default function CreateRole() {
|
||||
const navigate = useNavigate();
|
||||
const formatMessage = useFormatMessage();
|
||||
const [createRole, { loading }] = useMutation(CREATE_ROLE);
|
||||
const enqueueSnackbar = useEnqueueSnackbar();
|
||||
|
||||
const handleRoleCreation = async (
|
||||
roleData: Partial<RoleWithComputedPermissions>
|
||||
) => {
|
||||
const handleRoleCreation = async (roleData) => {
|
||||
try {
|
||||
const permissions = getPermissions(roleData.computedPermissions);
|
||||
|
||||
await createRole({
|
||||
variables: {
|
||||
input: {
|
||||
@@ -40,20 +31,17 @@ export default function CreateRole(): React.ReactElement {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
enqueueSnackbar(formatMessage('createRole.successfullyCreated'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-create-role-success',
|
||||
},
|
||||
});
|
||||
|
||||
navigate(URLS.ROLES);
|
||||
} catch (error) {
|
||||
throw new Error('Failed while creating!');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Grid container item xs={12} sm={10} md={9}>
|
@@ -1,5 +1,4 @@
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { IRole, IUser } from 'types';
|
||||
import LoadingButton from '@mui/lab/LoadingButton';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Stack from '@mui/material/Stack';
|
||||
@@ -7,7 +6,6 @@ import MuiTextField from '@mui/material/TextField';
|
||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
import * as React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import Can from 'components/Can';
|
||||
import Container from 'components/Container';
|
||||
import ControlledAutocomplete from 'components/ControlledAutocomplete';
|
||||
@@ -18,19 +16,16 @@ import * as URLS from 'config/urls';
|
||||
import { CREATE_USER } from 'graphql/mutations/create-user.ee';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useRoles from 'hooks/useRoles.ee';
|
||||
|
||||
function generateRoleOptions(roles: IRole[]) {
|
||||
function generateRoleOptions(roles) {
|
||||
return roles?.map(({ name: label, id: value }) => ({ label, value }));
|
||||
}
|
||||
|
||||
export default function CreateUser(): React.ReactElement {
|
||||
export default function CreateUser() {
|
||||
const navigate = useNavigate();
|
||||
const formatMessage = useFormatMessage();
|
||||
const [createUser, { loading }] = useMutation(CREATE_USER);
|
||||
const { roles, loading: rolesLoading } = useRoles();
|
||||
const enqueueSnackbar = useEnqueueSnackbar();
|
||||
|
||||
const handleUserCreation = async (userData: Partial<IUser>) => {
|
||||
const handleUserCreation = async (userData) => {
|
||||
try {
|
||||
await createUser({
|
||||
variables: {
|
||||
@@ -44,7 +39,6 @@ export default function CreateUser(): React.ReactElement {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
enqueueSnackbar(formatMessage('createUser.successfullyCreated'), {
|
||||
variant: 'success',
|
||||
persist: true,
|
||||
@@ -52,13 +46,11 @@ export default function CreateUser(): React.ReactElement {
|
||||
'data-test': 'snackbar-create-user-success',
|
||||
},
|
||||
});
|
||||
|
||||
navigate(URLS.USERS);
|
||||
} catch (error) {
|
||||
throw new Error('Failed while creating!');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Grid container item xs={12} sm={10} md={9}>
|
@@ -6,7 +6,6 @@ import Stack from '@mui/material/Stack';
|
||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
import * as React from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import Container from 'components/Container';
|
||||
import Form from 'components/Form';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
@@ -15,31 +14,21 @@ import TextField from 'components/TextField';
|
||||
import * as URLS from 'config/urls';
|
||||
import { UPDATE_ROLE } from 'graphql/mutations/update-role.ee';
|
||||
import {
|
||||
RoleWithComputedPermissions,
|
||||
getPermissions,
|
||||
getRoleWithComputedPermissions,
|
||||
} from 'helpers/computePermissions.ee';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useRole from 'hooks/useRole.ee';
|
||||
|
||||
type EditRoleParams = {
|
||||
roleId: string;
|
||||
};
|
||||
|
||||
export default function EditRole(): React.ReactElement {
|
||||
export default function EditRole() {
|
||||
const formatMessage = useFormatMessage();
|
||||
const [updateRole, { loading }] = useMutation(UPDATE_ROLE);
|
||||
const navigate = useNavigate();
|
||||
const { roleId } = useParams<EditRoleParams>();
|
||||
const { roleId } = useParams();
|
||||
const { role, loading: roleLoading } = useRole(roleId);
|
||||
const enqueueSnackbar = useEnqueueSnackbar();
|
||||
|
||||
const handleRoleUpdate = async (
|
||||
roleData: Partial<RoleWithComputedPermissions>
|
||||
) => {
|
||||
const handleRoleUpdate = async (roleData) => {
|
||||
try {
|
||||
const newPermissions = getPermissions(roleData.computedPermissions);
|
||||
|
||||
await updateRole({
|
||||
variables: {
|
||||
input: {
|
||||
@@ -50,22 +39,18 @@ export default function EditRole(): React.ReactElement {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
enqueueSnackbar(formatMessage('editRole.successfullyUpdated'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-edit-role-success',
|
||||
},
|
||||
});
|
||||
|
||||
navigate(URLS.ROLES);
|
||||
} catch (error) {
|
||||
throw new Error('Failed while updating!');
|
||||
}
|
||||
};
|
||||
|
||||
const roleWithComputedPermissions = getRoleWithComputedPermissions(role);
|
||||
|
||||
return (
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Grid container item xs={12} sm={10} md={9}>
|
@@ -1,5 +1,4 @@
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { IRole, IUser } from 'types';
|
||||
import LoadingButton from '@mui/lab/LoadingButton';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Skeleton from '@mui/material/Skeleton';
|
||||
@@ -8,7 +7,6 @@ import MuiTextField from '@mui/material/TextField';
|
||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
import * as React from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import Can from 'components/Can';
|
||||
import Container from 'components/Container';
|
||||
import ControlledAutocomplete from 'components/ControlledAutocomplete';
|
||||
@@ -20,25 +18,18 @@ import { UPDATE_USER } from 'graphql/mutations/update-user.ee';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useRoles from 'hooks/useRoles.ee';
|
||||
import useUser from 'hooks/useUser';
|
||||
|
||||
type EditUserParams = {
|
||||
userId: string;
|
||||
};
|
||||
|
||||
function generateRoleOptions(roles: IRole[]) {
|
||||
function generateRoleOptions(roles) {
|
||||
return roles?.map(({ name: label, id: value }) => ({ label, value }));
|
||||
}
|
||||
|
||||
export default function EditUser(): React.ReactElement {
|
||||
export default function EditUser() {
|
||||
const formatMessage = useFormatMessage();
|
||||
const [updateUser, { loading }] = useMutation(UPDATE_USER);
|
||||
const { userId } = useParams<EditUserParams>();
|
||||
const { userId } = useParams();
|
||||
const { user, loading: userLoading } = useUser(userId);
|
||||
const { roles, loading: rolesLoading } = useRoles();
|
||||
const enqueueSnackbar = useEnqueueSnackbar();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleUserUpdate = async (userDataToUpdate: Partial<IUser>) => {
|
||||
const handleUserUpdate = async (userDataToUpdate) => {
|
||||
try {
|
||||
await updateUser({
|
||||
variables: {
|
||||
@@ -52,7 +43,6 @@ export default function EditUser(): React.ReactElement {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
enqueueSnackbar(formatMessage('editUser.successfullyUpdated'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
@@ -60,13 +50,11 @@ export default function EditUser(): React.ReactElement {
|
||||
persist: true,
|
||||
},
|
||||
});
|
||||
|
||||
navigate(URLS.USERS);
|
||||
} catch (error) {
|
||||
throw new Error('Failed while updating!');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Grid container item xs={12} sm={10} md={9}>
|
@@ -3,47 +3,36 @@ import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import * as URLS from 'config/urls';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import { CREATE_FLOW } from 'graphql/mutations/create-flow';
|
||||
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
export default function CreateFlow(): React.ReactElement {
|
||||
export default function CreateFlow() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
const formatMessage = useFormatMessage();
|
||||
const [createFlow] = useMutation(CREATE_FLOW);
|
||||
|
||||
const appKey = searchParams.get('appKey');
|
||||
const connectionId = searchParams.get('connectionId');
|
||||
|
||||
React.useEffect(() => {
|
||||
async function initiate() {
|
||||
const variables: { [key: string]: string } = {};
|
||||
|
||||
const variables = {};
|
||||
if (appKey) {
|
||||
variables.triggerAppKey = appKey;
|
||||
}
|
||||
|
||||
if (connectionId) {
|
||||
variables.connectionId = connectionId;
|
||||
}
|
||||
|
||||
const response = await createFlow({
|
||||
variables: {
|
||||
input: variables,
|
||||
},
|
||||
});
|
||||
const flowId = response.data?.createFlow?.id;
|
||||
|
||||
navigate(URLS.FLOW_EDITOR(flowId), { replace: true });
|
||||
}
|
||||
|
||||
initiate();
|
||||
}, [createFlow, navigate, appKey, connectionId]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import EditorLayout from 'components/EditorLayout';
|
||||
|
||||
export default function FlowEditor(): React.ReactElement {
|
||||
export default function FlowEditor() {
|
||||
return <EditorLayout />;
|
||||
}
|
@@ -2,8 +2,7 @@ import * as React from 'react';
|
||||
import { Routes, Route } from 'react-router-dom';
|
||||
import CreateFlowPage from './create';
|
||||
import EditorPage from './index';
|
||||
|
||||
export default function EditorRoutes(): React.ReactElement {
|
||||
export default function EditorRoutes() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/create" element={<CreateFlowPage />} />
|
@@ -5,28 +5,19 @@ import Grid from '@mui/material/Grid';
|
||||
import Box from '@mui/material/Box';
|
||||
import AlertTitle from '@mui/material/AlertTitle';
|
||||
import Alert from '@mui/material/Alert';
|
||||
import type { IExecutionStep } from 'types';
|
||||
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import ExecutionHeader from 'components/ExecutionHeader';
|
||||
import ExecutionStep from 'components/ExecutionStep';
|
||||
import Container from 'components/Container';
|
||||
import { GET_EXECUTION } from 'graphql/queries/get-execution';
|
||||
import { GET_EXECUTION_STEPS } from 'graphql/queries/get-execution-steps';
|
||||
|
||||
type ExecutionParams = {
|
||||
executionId: string;
|
||||
};
|
||||
|
||||
const EXECUTION_PER_PAGE = 100;
|
||||
|
||||
const getLimitAndOffset = (page: number) => ({
|
||||
const getLimitAndOffset = (page) => ({
|
||||
limit: EXECUTION_PER_PAGE,
|
||||
offset: (page - 1) * EXECUTION_PER_PAGE,
|
||||
});
|
||||
|
||||
export default function Execution(): React.ReactElement {
|
||||
const { executionId } = useParams() as ExecutionParams;
|
||||
export default function Execution() {
|
||||
const { executionId } = useParams();
|
||||
const formatMessage = useFormatMessage();
|
||||
const { data: execution } = useQuery(GET_EXECUTION, {
|
||||
variables: { executionId },
|
||||
@@ -34,12 +25,8 @@ export default function Execution(): React.ReactElement {
|
||||
const { data, loading } = useQuery(GET_EXECUTION_STEPS, {
|
||||
variables: { executionId, ...getLimitAndOffset(1) },
|
||||
});
|
||||
|
||||
const { edges } = data?.getExecutionSteps || {};
|
||||
const executionSteps: IExecutionStep[] = edges?.map(
|
||||
(edge: { node: IExecutionStep }) => edge.node
|
||||
);
|
||||
|
||||
const executionSteps = edges?.map((edge) => edge.node);
|
||||
return (
|
||||
<Container sx={{ py: 3 }}>
|
||||
<ExecutionHeader execution={execution?.getExecution} />
|
@@ -7,27 +7,21 @@ import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Pagination from '@mui/material/Pagination';
|
||||
import PaginationItem from '@mui/material/PaginationItem';
|
||||
import type { IExecution } from 'types';
|
||||
|
||||
import NoResultFound from 'components/NoResultFound';
|
||||
import ExecutionRow from 'components/ExecutionRow';
|
||||
import Container from 'components/Container';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import { GET_EXECUTIONS } from 'graphql/queries/get-executions';
|
||||
|
||||
const EXECUTION_PER_PAGE = 10;
|
||||
|
||||
const getLimitAndOffset = (page: number) => ({
|
||||
const getLimitAndOffset = (page) => ({
|
||||
limit: EXECUTION_PER_PAGE,
|
||||
offset: (page - 1) * EXECUTION_PER_PAGE,
|
||||
});
|
||||
|
||||
export default function Executions(): React.ReactElement {
|
||||
export default function Executions() {
|
||||
const formatMessage = useFormatMessage();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const page = parseInt(searchParams.get('page') || '', 10) || 1;
|
||||
|
||||
const { data, refetch, loading } = useQuery(GET_EXECUTIONS, {
|
||||
variables: getLimitAndOffset(page),
|
||||
fetchPolicy: 'cache-and-network',
|
||||
@@ -35,16 +29,11 @@ export default function Executions(): React.ReactElement {
|
||||
});
|
||||
const getExecutions = data?.getExecutions || {};
|
||||
const { pageInfo, edges } = getExecutions;
|
||||
|
||||
React.useEffect(() => {
|
||||
refetch(getLimitAndOffset(page));
|
||||
}, [refetch, page]);
|
||||
|
||||
const executions: IExecution[] = edges?.map(
|
||||
({ node }: { node: IExecution }) => node
|
||||
);
|
||||
const executions = edges?.map(({ node }) => node);
|
||||
const hasExecutions = executions?.length;
|
||||
|
||||
return (
|
||||
<Box sx={{ py: 3 }}>
|
||||
<Container>
|
@@ -2,16 +2,9 @@ import * as React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import Box from '@mui/material/Box';
|
||||
import Grid from '@mui/material/Grid';
|
||||
|
||||
import Container from 'components/Container';
|
||||
|
||||
type FlowParams = {
|
||||
flowId: string;
|
||||
};
|
||||
|
||||
export default function Flow(): React.ReactElement {
|
||||
const { flowId } = useParams() as FlowParams;
|
||||
|
||||
export default function Flow() {
|
||||
const { flowId } = useParams();
|
||||
return (
|
||||
<Box sx={{ py: 3 }}>
|
||||
<Container>
|
@@ -9,8 +9,6 @@ import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Pagination from '@mui/material/Pagination';
|
||||
import PaginationItem from '@mui/material/PaginationItem';
|
||||
import type { IFlow } from 'types';
|
||||
|
||||
import Can from 'components/Can';
|
||||
import FlowRow from 'components/FlowRow';
|
||||
import NoResultFound from 'components/NoResultFound';
|
||||
@@ -21,15 +19,12 @@ import SearchInput from 'components/SearchInput';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import { GET_FLOWS } from 'graphql/queries/get-flows';
|
||||
import * as URLS from 'config/urls';
|
||||
|
||||
const FLOW_PER_PAGE = 10;
|
||||
|
||||
const getLimitAndOffset = (page: number) => ({
|
||||
const getLimitAndOffset = (page) => ({
|
||||
limit: FLOW_PER_PAGE,
|
||||
offset: (page - 1) * FLOW_PER_PAGE,
|
||||
});
|
||||
|
||||
export default function Flows(): React.ReactElement {
|
||||
export default function Flows() {
|
||||
const formatMessage = useFormatMessage();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const page = parseInt(searchParams.get('page') || '', 10) || 1;
|
||||
@@ -40,7 +35,6 @@ export default function Flows(): React.ReactElement {
|
||||
setLoading(false);
|
||||
},
|
||||
});
|
||||
|
||||
const fetchData = React.useMemo(
|
||||
() =>
|
||||
debounce(
|
||||
@@ -51,43 +45,35 @@ export default function Flows(): React.ReactElement {
|
||||
name,
|
||||
},
|
||||
}),
|
||||
300
|
||||
300,
|
||||
),
|
||||
[page, getFlows]
|
||||
[page, getFlows],
|
||||
);
|
||||
|
||||
React.useEffect(
|
||||
function fetchFlowsOnSearch() {
|
||||
setLoading(true);
|
||||
|
||||
fetchData(flowName);
|
||||
},
|
||||
[fetchData, flowName]
|
||||
[fetchData, flowName],
|
||||
);
|
||||
|
||||
React.useEffect(
|
||||
function resetPageOnSearch() {
|
||||
// reset search params which only consists of `page`
|
||||
setSearchParams({});
|
||||
},
|
||||
[flowName]
|
||||
[flowName],
|
||||
);
|
||||
|
||||
React.useEffect(function cancelDebounceOnUnmount() {
|
||||
return () => {
|
||||
fetchData.cancel();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const { pageInfo, edges } = data?.getFlows || {};
|
||||
|
||||
const flows: IFlow[] = edges?.map(({ node }: { node: IFlow }) => node);
|
||||
const flows = edges?.map(({ node }) => node);
|
||||
const hasFlows = flows?.length;
|
||||
|
||||
const onSearchChange = React.useCallback((event) => {
|
||||
setFlowName(event.target.value);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box sx={{ py: 3 }}>
|
||||
<Container>
|
||||
@@ -130,21 +116,17 @@ export default function Flows(): React.ReactElement {
|
||||
</Grid>
|
||||
|
||||
<Divider sx={{ mt: [2, 0], mb: 2 }} />
|
||||
|
||||
{loading && (
|
||||
<CircularProgress sx={{ display: 'block', margin: '20px auto' }} />
|
||||
)}
|
||||
|
||||
{!loading &&
|
||||
flows?.map((flow) => <FlowRow key={flow.id} flow={flow} />)}
|
||||
|
||||
{!loading && !hasFlows && (
|
||||
<NoResultFound
|
||||
text={formatMessage('flows.noFlows')}
|
||||
to={URLS.CREATE_FLOW}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!loading && pageInfo && pageInfo.totalPages > 1 && (
|
||||
<Pagination
|
||||
sx={{ display: 'flex', justifyContent: 'center', mt: 3 }}
|
@@ -1,13 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
import useCloud from 'hooks/useCloud';
|
||||
import Container from 'components/Container';
|
||||
import ForgotPasswordForm from 'components/ForgotPasswordForm/index.ee';
|
||||
|
||||
export default function ForgotPassword(): React.ReactElement {
|
||||
export default function ForgotPassword() {
|
||||
useCloud({ redirect: true });
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flex: 1, alignItems: 'center' }}>
|
||||
<Container maxWidth="sm">
|
@@ -4,9 +4,7 @@ import Stack from '@mui/material/Stack';
|
||||
import Container from 'components/Container';
|
||||
import LoginForm from 'components/LoginForm';
|
||||
import SsoProviders from 'components/SsoProviders/index.ee';
|
||||
|
||||
|
||||
export default function Login(): React.ReactElement {
|
||||
export default function Login() {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flex: 1, alignItems: 'center' }}>
|
||||
<Container maxWidth="sm">
|
@@ -1,29 +1,22 @@
|
||||
import * as React from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
|
||||
import useAuthentication from 'hooks/useAuthentication';
|
||||
import * as URLS from 'config/urls';
|
||||
|
||||
export default function LoginCallback(): React.ReactElement {
|
||||
export default function LoginCallback() {
|
||||
const navigate = useNavigate();
|
||||
const authentication = useAuthentication();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (authentication.isAuthenticated) {
|
||||
navigate(URLS.DASHBOARD);
|
||||
}
|
||||
}, [authentication.isAuthenticated]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const token = searchParams.get('token');
|
||||
|
||||
if (token) {
|
||||
authentication.updateToken(token);
|
||||
}
|
||||
|
||||
// TODO: handle non-existing token scenario
|
||||
}, []);
|
||||
|
||||
return (<></>);
|
||||
return <></>;
|
||||
}
|
@@ -2,7 +2,6 @@ import * as React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Box from '@mui/material/Box';
|
||||
import Stack from '@mui/material/Stack';
|
||||
|
||||
import useNotifications from 'hooks/useNotifications';
|
||||
import Container from 'components/Container';
|
||||
import NotificationCard from 'components/NotificationCard';
|
||||
@@ -10,29 +9,19 @@ import PageTitle from 'components/PageTitle';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useAutomatischInfo from 'hooks/useAutomatischInfo';
|
||||
import * as URLS from 'config/urls';
|
||||
|
||||
interface INotification {
|
||||
name: string;
|
||||
createdAt: string;
|
||||
documentationUrl: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export default function Updates(): React.ReactElement {
|
||||
export default function Updates() {
|
||||
const navigate = useNavigate();
|
||||
const formatMessage = useFormatMessage();
|
||||
const { notifications } = useNotifications();
|
||||
const { isMation, loading } = useAutomatischInfo();
|
||||
|
||||
React.useEffect(
|
||||
function redirectToHomepageInMation() {
|
||||
if (!loading && isMation) {
|
||||
navigate(URLS.DASHBOARD);
|
||||
}
|
||||
},
|
||||
[loading, isMation]
|
||||
[loading, isMation],
|
||||
);
|
||||
|
||||
return (
|
||||
<Box sx={{ py: 3 }}>
|
||||
<Container>
|
||||
@@ -41,7 +30,7 @@ export default function Updates(): React.ReactElement {
|
||||
</PageTitle>
|
||||
|
||||
<Stack gap={2}>
|
||||
{notifications.map((notification: INotification) => (
|
||||
{notifications.map((notification) => (
|
||||
<NotificationCard
|
||||
key={notification.name}
|
||||
name={`Version ${notification.name}`}
|
@@ -1,34 +1,27 @@
|
||||
import * as React from 'react';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import Grid from '@mui/material/Grid';
|
||||
|
||||
import * as URLS from 'config/urls';
|
||||
import UpgradeFreeTrial from 'components/UpgradeFreeTrial/index.ee';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import Container from 'components/Container';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import useCloud from 'hooks/useCloud';
|
||||
|
||||
function PlanUpgrade() {
|
||||
const isCloud = useCloud();
|
||||
const formatMessage = useFormatMessage();
|
||||
|
||||
// redirect to the initial settings page
|
||||
if (isCloud === false) {
|
||||
return <Navigate to={URLS.SETTINGS} replace={true} />;
|
||||
}
|
||||
|
||||
// render nothing until we know if it's cloud or not
|
||||
// here, `isCloud` is not `false`, but `undefined`
|
||||
if (!isCloud) return <React.Fragment />;
|
||||
|
||||
return (
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Grid container item xs={12} sm={9} md={8}>
|
||||
<Grid item xs={12} sx={{ mb: [2, 5] }}>
|
||||
<PageTitle>
|
||||
{formatMessage('planUpgrade.title')}
|
||||
</PageTitle>
|
||||
<PageTitle>{formatMessage('planUpgrade.title')}</PageTitle>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sx={{ mb: 6 }}>
|
||||
@@ -38,5 +31,4 @@ function PlanUpgrade() {
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export default PlanUpgrade;
|
@@ -9,7 +9,6 @@ import { styled } from '@mui/material/styles';
|
||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
import * as React from 'react';
|
||||
import * as yup from 'yup';
|
||||
|
||||
import Container from 'components/Container';
|
||||
import DeleteAccountDialog from 'components/DeleteAccountDialog/index.ee';
|
||||
import Form from 'components/Form';
|
||||
@@ -18,13 +17,6 @@ import TextField from 'components/TextField';
|
||||
import { UPDATE_CURRENT_USER } from 'graphql/mutations/update-current-user';
|
||||
import useCurrentUser from 'hooks/useCurrentUser';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
|
||||
type TMutationInput = {
|
||||
fullName: string;
|
||||
email: string;
|
||||
password?: string;
|
||||
};
|
||||
|
||||
const validationSchema = yup
|
||||
.object({
|
||||
fullName: yup.string().required(),
|
||||
@@ -35,13 +27,11 @@ const validationSchema = yup
|
||||
.oneOf([yup.ref('password')], 'Passwords must match'),
|
||||
})
|
||||
.required();
|
||||
|
||||
const StyledForm = styled(Form)`
|
||||
display: flex;
|
||||
align-items: end;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
function ProfileSettings() {
|
||||
const [showDeleteAccountConfirmation, setShowDeleteAccountConfirmation] =
|
||||
React.useState(false);
|
||||
@@ -49,19 +39,15 @@ function ProfileSettings() {
|
||||
const currentUser = useCurrentUser();
|
||||
const formatMessage = useFormatMessage();
|
||||
const [updateCurrentUser] = useMutation(UPDATE_CURRENT_USER);
|
||||
|
||||
const handleProfileSettingsUpdate = async (data: any) => {
|
||||
const handleProfileSettingsUpdate = async (data) => {
|
||||
const { fullName, password, email } = data;
|
||||
|
||||
const mutationInput: TMutationInput = {
|
||||
const mutationInput = {
|
||||
fullName,
|
||||
email,
|
||||
};
|
||||
|
||||
if (password) {
|
||||
mutationInput.password = password;
|
||||
}
|
||||
|
||||
await updateCurrentUser({
|
||||
variables: {
|
||||
input: mutationInput,
|
||||
@@ -75,15 +61,13 @@ function ProfileSettings() {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
enqueueSnackbar(formatMessage('profileSettings.updatedProfile'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-update-profile-settings-success'
|
||||
}
|
||||
'data-test': 'snackbar-update-profile-settings-success',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Grid container item xs={12} sm={9} md={8} lg={6}>
|
||||
@@ -209,5 +193,4 @@ function ProfileSettings() {
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export default ProfileSettings;
|
@@ -1,13 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
import useCloud from 'hooks/useCloud';
|
||||
import Container from 'components/Container';
|
||||
import ResetPasswordForm from 'components/ResetPasswordForm/index.ee';
|
||||
|
||||
export default function ResetPassword(): React.ReactElement {
|
||||
export default function ResetPassword() {
|
||||
useCloud({ redirect: true });
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flex: 1, alignItems: 'center' }}>
|
||||
<Container maxWidth="sm">
|
@@ -2,17 +2,14 @@ import * as React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
|
||||
import * as URLS from 'config/urls';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import Container from 'components/Container';
|
||||
import RoleList from 'components/RoleList/index.ee';
|
||||
import ConditionalIconButton from 'components/ConditionalIconButton';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
|
||||
function RolesPage() {
|
||||
const formatMessage = useFormatMessage();
|
||||
|
||||
return (
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Grid container item xs={12} sm={10} md={9}>
|
||||
@@ -47,5 +44,4 @@ function RolesPage() {
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export default RolesPage;
|
@@ -1,13 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
import useCloud from 'hooks/useCloud';
|
||||
import Container from 'components/Container';
|
||||
import SignUpForm from 'components/SignUpForm/index.ee';
|
||||
|
||||
export default function SignUp(): React.ReactElement {
|
||||
export default function SignUp() {
|
||||
useCloud({ redirect: true });
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flex: 1, alignItems: 'center' }}>
|
||||
<Container maxWidth="sm">
|
@@ -6,7 +6,6 @@ import Stack from '@mui/material/Stack';
|
||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
import merge from 'lodash/merge';
|
||||
import * as React from 'react';
|
||||
|
||||
import ColorInput from 'components/ColorInput';
|
||||
import Container from 'components/Container';
|
||||
import Form from 'components/Form';
|
||||
@@ -22,33 +21,16 @@ import {
|
||||
primaryLightColor,
|
||||
primaryMainColor,
|
||||
} from 'styles/theme';
|
||||
|
||||
type UserInterface = {
|
||||
palette: {
|
||||
primary: {
|
||||
dark: string;
|
||||
light: string;
|
||||
main: string;
|
||||
};
|
||||
};
|
||||
title: string;
|
||||
logo: {
|
||||
svgData: string;
|
||||
};
|
||||
};
|
||||
|
||||
const getPrimaryMainColor = (color?: string) => color || primaryMainColor;
|
||||
const getPrimaryDarkColor = (color?: string) => color || primaryDarkColor;
|
||||
const getPrimaryLightColor = (color?: string) => color || primaryLightColor;
|
||||
|
||||
const getPrimaryMainColor = (color) => color || primaryMainColor;
|
||||
const getPrimaryDarkColor = (color) => color || primaryDarkColor;
|
||||
const getPrimaryLightColor = (color) => color || primaryLightColor;
|
||||
const defaultValues = {
|
||||
title: 'Automatisch',
|
||||
'palette.primary.main': primaryMainColor,
|
||||
'palette.primary.dark': primaryDarkColor,
|
||||
'palette.primary.light': primaryLightColor,
|
||||
};
|
||||
|
||||
export default function UserInterface(): React.ReactElement {
|
||||
export default function UserInterface() {
|
||||
const formatMessage = useFormatMessage();
|
||||
const [updateConfig, { loading }] = useMutation(UPDATE_CONFIG);
|
||||
const { config, loading: configLoading } = useConfig([
|
||||
@@ -59,28 +41,22 @@ export default function UserInterface(): React.ReactElement {
|
||||
'logo.svgData',
|
||||
]);
|
||||
const enqueueSnackbar = useEnqueueSnackbar();
|
||||
const configWithDefaults = merge(
|
||||
{},
|
||||
defaultValues,
|
||||
nestObject<UserInterface>(config)
|
||||
);
|
||||
|
||||
const handleUserInterfaceUpdate = async (uiData: Partial<UserInterface>) => {
|
||||
const configWithDefaults = merge({}, defaultValues, nestObject(config));
|
||||
const handleUserInterfaceUpdate = async (uiData) => {
|
||||
try {
|
||||
const input = {
|
||||
title: uiData?.title,
|
||||
'palette.primary.main': getPrimaryMainColor(
|
||||
uiData?.palette?.primary.main
|
||||
uiData?.palette?.primary.main,
|
||||
),
|
||||
'palette.primary.dark': getPrimaryDarkColor(
|
||||
uiData?.palette?.primary.dark
|
||||
uiData?.palette?.primary.dark,
|
||||
),
|
||||
'palette.primary.light': getPrimaryLightColor(
|
||||
uiData?.palette?.primary.light
|
||||
uiData?.palette?.primary.light,
|
||||
),
|
||||
'logo.svgData': uiData?.logo?.svgData,
|
||||
};
|
||||
|
||||
await updateConfig({
|
||||
variables: {
|
||||
input,
|
||||
@@ -90,14 +66,12 @@ export default function UserInterface(): React.ReactElement {
|
||||
},
|
||||
update: async function (cache, { data: { updateConfig } }) {
|
||||
const newConfigWithDefaults = merge({}, defaultValues, updateConfig);
|
||||
|
||||
cache.writeQuery({
|
||||
query: GET_CONFIG,
|
||||
data: {
|
||||
getConfig: newConfigWithDefaults,
|
||||
},
|
||||
});
|
||||
|
||||
cache.writeQuery({
|
||||
query: GET_CONFIG,
|
||||
data: {
|
||||
@@ -107,7 +81,6 @@ export default function UserInterface(): React.ReactElement {
|
||||
keys: ['logo.svgData'],
|
||||
},
|
||||
});
|
||||
|
||||
cache.writeQuery({
|
||||
query: GET_CONFIG,
|
||||
data: {
|
||||
@@ -125,18 +98,16 @@ export default function UserInterface(): React.ReactElement {
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
enqueueSnackbar(formatMessage('userInterfacePage.successfullyUpdated'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-update-user-interface-success'
|
||||
}
|
||||
'data-test': 'snackbar-update-user-interface-success',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error('Failed while updating!');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Grid container item xs={12} sm={10} md={9}>
|
||||
@@ -169,7 +140,7 @@ export default function UserInterface(): React.ReactElement {
|
||||
<ColorInput
|
||||
name="palette.primary.main"
|
||||
label={formatMessage(
|
||||
'userInterfacePage.primaryMainColorFieldLabel'
|
||||
'userInterfacePage.primaryMainColorFieldLabel',
|
||||
)}
|
||||
fullWidth
|
||||
data-test="primary-main-color-input"
|
||||
@@ -178,7 +149,7 @@ export default function UserInterface(): React.ReactElement {
|
||||
<ColorInput
|
||||
name="palette.primary.dark"
|
||||
label={formatMessage(
|
||||
'userInterfacePage.primaryDarkColorFieldLabel'
|
||||
'userInterfacePage.primaryDarkColorFieldLabel',
|
||||
)}
|
||||
fullWidth
|
||||
data-test="primary-dark-color-input"
|
||||
@@ -187,7 +158,7 @@ export default function UserInterface(): React.ReactElement {
|
||||
<ColorInput
|
||||
name="palette.primary.light"
|
||||
label={formatMessage(
|
||||
'userInterfacePage.primaryLightColorFieldLabel'
|
||||
'userInterfacePage.primaryLightColorFieldLabel',
|
||||
)}
|
||||
fullWidth
|
||||
data-test="primary-light-color-input"
|
@@ -2,17 +2,14 @@ import * as React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
|
||||
import * as URLS from 'config/urls';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import Container from 'components/Container';
|
||||
import UserList from 'components/UserList';
|
||||
import ConditionalIconButton from 'components/ConditionalIconButton';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
|
||||
function UsersPage() {
|
||||
const formatMessage = useFormatMessage();
|
||||
|
||||
return (
|
||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Grid container item xs={12} sm={10} md={9}>
|
||||
@@ -47,5 +44,4 @@ function UsersPage() {
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export default UsersPage;
|
Reference in New Issue
Block a user