feat: introduce application auth clients tab in the admin panel (#1423)
* feat: introduce application auth clients tab in the admin panel * feat: introduce improvements * feat: use loading state returned from useMutation * feat: use error returned by useMutation hook
This commit is contained in:
1
packages/types/index.d.ts
vendored
1
packages/types/index.d.ts
vendored
@@ -462,6 +462,7 @@ type AppAuthClient = {
|
|||||||
appConfigId: string;
|
appConfigId: string;
|
||||||
authDefaults: string;
|
authDefaults: string;
|
||||||
formattedAuthDefaults: IJSONObject;
|
formattedAuthDefaults: IJSONObject;
|
||||||
|
active: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Notification = {
|
type Notification = {
|
||||||
|
@@ -0,0 +1,104 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { IField } from '@automatisch/types';
|
||||||
|
import LoadingButton from '@mui/lab/LoadingButton';
|
||||||
|
import Alert from '@mui/material/Alert';
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
import DialogContentText from '@mui/material/DialogContentText';
|
||||||
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
|
import { FieldValues, SubmitHandler } from 'react-hook-form';
|
||||||
|
import type { UseFormProps } from 'react-hook-form';
|
||||||
|
import type { ApolloError } from '@apollo/client';
|
||||||
|
|
||||||
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
import InputCreator from 'components/InputCreator';
|
||||||
|
import Switch from 'components/Switch';
|
||||||
|
import TextField from 'components/TextField';
|
||||||
|
|
||||||
|
import { Form } from './style';
|
||||||
|
|
||||||
|
type AdminApplicationAuthClientDialogProps = {
|
||||||
|
title: string;
|
||||||
|
authFields?: IField[];
|
||||||
|
defaultValues: UseFormProps['defaultValues'];
|
||||||
|
loading: boolean;
|
||||||
|
submitting: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
error?: ApolloError;
|
||||||
|
submitHandler: SubmitHandler<FieldValues>;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function AdminApplicationAuthClientDialog(
|
||||||
|
props: AdminApplicationAuthClientDialogProps
|
||||||
|
): React.ReactElement {
|
||||||
|
const {
|
||||||
|
error,
|
||||||
|
onClose,
|
||||||
|
title,
|
||||||
|
loading,
|
||||||
|
submitHandler,
|
||||||
|
authFields,
|
||||||
|
submitting,
|
||||||
|
defaultValues,
|
||||||
|
disabled = false,
|
||||||
|
} = props;
|
||||||
|
const formatMessage = useFormatMessage();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={true} onClose={onClose}>
|
||||||
|
<DialogTitle>{title}</DialogTitle>
|
||||||
|
{error && (
|
||||||
|
<Alert
|
||||||
|
severity="error"
|
||||||
|
sx={{ mt: 1, fontWeight: 500, wordBreak: 'break-all' }}
|
||||||
|
>
|
||||||
|
{error.message}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
<DialogContent>
|
||||||
|
{loading ? (
|
||||||
|
<CircularProgress
|
||||||
|
data-test="search-for-app-loader"
|
||||||
|
sx={{ display: 'block', margin: '20px auto' }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<DialogContentText tabIndex={-1} component="div">
|
||||||
|
<Form
|
||||||
|
onSubmit={submitHandler}
|
||||||
|
defaultValues={defaultValues}
|
||||||
|
render={({ formState: { isDirty } }) => (
|
||||||
|
<>
|
||||||
|
<Switch
|
||||||
|
name="active"
|
||||||
|
label={formatMessage('authClient.inputActive')}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
required={true}
|
||||||
|
name="name"
|
||||||
|
label={formatMessage('authClient.inputName')}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
{authFields?.map((field: IField) => (
|
||||||
|
<InputCreator key={field.key} schema={field} />
|
||||||
|
))}
|
||||||
|
<LoadingButton
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
sx={{ boxShadow: 2 }}
|
||||||
|
loading={submitting}
|
||||||
|
disabled={disabled || !isDirty}
|
||||||
|
>
|
||||||
|
{formatMessage('authClient.buttonSubmit')}
|
||||||
|
</LoadingButton>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
></Form>
|
||||||
|
</DialogContentText>
|
||||||
|
)}
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
import BaseForm from 'components/Form';
|
||||||
|
|
||||||
|
export const Form = styled(BaseForm)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: theme.spacing(2),
|
||||||
|
paddingTop: theme.spacing(1),
|
||||||
|
}));
|
@@ -0,0 +1,89 @@
|
|||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import Card from '@mui/material/Card';
|
||||||
|
import CardActionArea from '@mui/material/CardActionArea';
|
||||||
|
import CardContent from '@mui/material/CardContent';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Chip from '@mui/material/Chip';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
|
||||||
|
import * as URLS from 'config/urls';
|
||||||
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
import useAppAuthClients from 'hooks/useAppAuthClients.ee';
|
||||||
|
|
||||||
|
import NoResultFound from 'components/NoResultFound';
|
||||||
|
|
||||||
|
type AdminApplicationAuthClientsProps = {
|
||||||
|
appKey: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function AdminApplicationAuthClients(
|
||||||
|
props: AdminApplicationAuthClientsProps
|
||||||
|
): React.ReactElement {
|
||||||
|
const { appKey } = props;
|
||||||
|
const formatMessage = useFormatMessage();
|
||||||
|
const { appAuthClients, loading } = useAppAuthClients({ appKey });
|
||||||
|
|
||||||
|
if (loading)
|
||||||
|
return <CircularProgress sx={{ display: 'block', margin: '20px auto' }} />;
|
||||||
|
|
||||||
|
if (!appAuthClients?.length) {
|
||||||
|
return (
|
||||||
|
<NoResultFound
|
||||||
|
to={URLS.ADMIN_APP_AUTH_CLIENTS_CREATE(appKey)}
|
||||||
|
text={formatMessage('adminAppsAuthClients.noAuthClients')}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortedAuthClients = appAuthClients.slice().sort((a, b) => {
|
||||||
|
if (a.id < b.id) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a.id > b.id) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{sortedAuthClients.map((client) => (
|
||||||
|
<Card sx={{ mb: 1 }} key={client.id}>
|
||||||
|
<CardActionArea
|
||||||
|
component={Link}
|
||||||
|
to={URLS.ADMIN_APP_AUTH_CLIENT(appKey, client.id)}
|
||||||
|
>
|
||||||
|
<CardContent>
|
||||||
|
<Stack direction="row" justifyContent="space-between">
|
||||||
|
<Typography variant="h6" noWrap>
|
||||||
|
{client.name}
|
||||||
|
</Typography>
|
||||||
|
<Chip
|
||||||
|
size="small"
|
||||||
|
color={client?.active ? 'success' : 'info'}
|
||||||
|
variant={client?.active ? 'filled' : 'outlined'}
|
||||||
|
label={formatMessage(
|
||||||
|
client?.active
|
||||||
|
? 'adminAppsAuthClients.statusActive'
|
||||||
|
: 'adminAppsAuthClients.statusInactive'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</CardContent>
|
||||||
|
</CardActionArea>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
<Stack justifyContent="flex-end" direction="row">
|
||||||
|
<Link to={URLS.ADMIN_APP_AUTH_CLIENTS_CREATE(appKey)}>
|
||||||
|
<Button variant="contained" sx={{ mt: 2 }} component="div">
|
||||||
|
{formatMessage('createAuthClient.button')}
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</Stack>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AdminApplicationAuthClients;
|
@@ -0,0 +1,112 @@
|
|||||||
|
import React, { useCallback, useMemo } from 'react';
|
||||||
|
import type { IApp } from '@automatisch/types';
|
||||||
|
import { FieldValues, SubmitHandler } from 'react-hook-form';
|
||||||
|
import { useMutation } from '@apollo/client';
|
||||||
|
import { CREATE_APP_CONFIG } from 'graphql/mutations/create-app-config';
|
||||||
|
import { CREATE_APP_AUTH_CLIENT } from 'graphql/mutations/create-app-auth-client';
|
||||||
|
|
||||||
|
import useAppConfig from 'hooks/useAppConfig.ee';
|
||||||
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
|
||||||
|
import AdminApplicationAuthClientDialog from 'components/AdminApplicationAuthClientDialog';
|
||||||
|
|
||||||
|
type AdminApplicationCreateAuthClientProps = {
|
||||||
|
appKey: string;
|
||||||
|
application: IApp;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function AdminApplicationCreateAuthClient(
|
||||||
|
props: AdminApplicationCreateAuthClientProps
|
||||||
|
): React.ReactElement {
|
||||||
|
const { appKey, application, onClose } = props;
|
||||||
|
const { auth } = application;
|
||||||
|
const formatMessage = useFormatMessage();
|
||||||
|
const { appConfig, loading: loadingAppConfig } = useAppConfig(appKey);
|
||||||
|
const [
|
||||||
|
createAppConfig,
|
||||||
|
{ loading: loadingCreateAppConfig, error: createAppConfigError },
|
||||||
|
] = useMutation(CREATE_APP_CONFIG, {
|
||||||
|
refetchQueries: ['GetAppConfig'],
|
||||||
|
context: { autoSnackbar: false },
|
||||||
|
});
|
||||||
|
const [
|
||||||
|
createAppAuthClient,
|
||||||
|
{ loading: loadingCreateAppAuthClient, error: createAppAuthClientError },
|
||||||
|
] = useMutation(CREATE_APP_AUTH_CLIENT, {
|
||||||
|
refetchQueries: ['GetAppAuthClients'],
|
||||||
|
context: { autoSnackbar: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
const submitHandler: SubmitHandler<FieldValues> = async (values) => {
|
||||||
|
let appConfigId = appConfig?.id;
|
||||||
|
|
||||||
|
if (!appConfigId) {
|
||||||
|
const { data: appConfigData } = await createAppConfig({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
key: appKey,
|
||||||
|
allowCustomConnection: false,
|
||||||
|
shared: false,
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
appConfigId = appConfigData.createAppConfig.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name, active, ...formattedAuthDefaults } = values;
|
||||||
|
|
||||||
|
await createAppAuthClient({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
appConfigId,
|
||||||
|
name,
|
||||||
|
active,
|
||||||
|
formattedAuthDefaults,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAuthFieldsDefaultValues = useCallback(() => {
|
||||||
|
if (!auth?.fields) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const defaultValues: {
|
||||||
|
[key: string]: any;
|
||||||
|
} = {};
|
||||||
|
auth.fields.forEach((field) => {
|
||||||
|
if (field.value || field.type !== 'string') {
|
||||||
|
defaultValues[field.key] = field.value;
|
||||||
|
} else if (field.type === 'string') {
|
||||||
|
defaultValues[field.key] = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return defaultValues;
|
||||||
|
}, [auth?.fields]);
|
||||||
|
|
||||||
|
const defaultValues = useMemo(
|
||||||
|
() => ({
|
||||||
|
name: '',
|
||||||
|
active: false,
|
||||||
|
...getAuthFieldsDefaultValues(),
|
||||||
|
}),
|
||||||
|
[getAuthFieldsDefaultValues]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AdminApplicationAuthClientDialog
|
||||||
|
onClose={onClose}
|
||||||
|
error={createAppConfigError || createAppAuthClientError}
|
||||||
|
title={formatMessage('createAuthClient.title')}
|
||||||
|
loading={loadingAppConfig}
|
||||||
|
submitHandler={submitHandler}
|
||||||
|
authFields={auth?.fields}
|
||||||
|
submitting={loadingCreateAppConfig || loadingCreateAppAuthClient}
|
||||||
|
defaultValues={defaultValues}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@@ -0,0 +1,95 @@
|
|||||||
|
import React, { useCallback, useMemo } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import type { IApp } from '@automatisch/types';
|
||||||
|
import { FieldValues, SubmitHandler } from 'react-hook-form';
|
||||||
|
import { useMutation } from '@apollo/client';
|
||||||
|
import { UPDATE_APP_AUTH_CLIENT } from 'graphql/mutations/update-app-auth-client';
|
||||||
|
|
||||||
|
import useAppAuthClient from 'hooks/useAppAuthClient.ee';
|
||||||
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
import AdminApplicationAuthClientDialog from 'components/AdminApplicationAuthClientDialog';
|
||||||
|
|
||||||
|
type AdminApplicationUpdateAuthClientProps = {
|
||||||
|
application: IApp;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function AdminApplicationUpdateAuthClient(
|
||||||
|
props: AdminApplicationUpdateAuthClientProps
|
||||||
|
): React.ReactElement {
|
||||||
|
const { application, onClose } = props;
|
||||||
|
const { auth } = application;
|
||||||
|
const authFields = auth?.fields?.map((field) => ({
|
||||||
|
...field,
|
||||||
|
required: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const formatMessage = useFormatMessage();
|
||||||
|
|
||||||
|
const { clientId } = useParams();
|
||||||
|
const { appAuthClient, loading: loadingAuthClient } =
|
||||||
|
useAppAuthClient(clientId);
|
||||||
|
const [updateAppAuthClient, { loading: loadingUpdateAppAuthClient, error }] =
|
||||||
|
useMutation(UPDATE_APP_AUTH_CLIENT, {
|
||||||
|
refetchQueries: ['GetAppAuthClients'],
|
||||||
|
context: { autoSnackbar: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
const submitHandler: SubmitHandler<FieldValues> = async (values) => {
|
||||||
|
if (!appAuthClient) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { name, active, ...formattedAuthDefaults } = values;
|
||||||
|
await updateAppAuthClient({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
id: appAuthClient.id,
|
||||||
|
name,
|
||||||
|
active,
|
||||||
|
formattedAuthDefaults,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAuthFieldsDefaultValues = useCallback(() => {
|
||||||
|
if (!authFields) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const defaultValues: {
|
||||||
|
[key: string]: any;
|
||||||
|
} = {};
|
||||||
|
authFields.forEach((field) => {
|
||||||
|
if (field.value || field.type !== 'string') {
|
||||||
|
defaultValues[field.key] = field.value;
|
||||||
|
} else if (field.type === 'string') {
|
||||||
|
defaultValues[field.key] = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return defaultValues;
|
||||||
|
}, [auth?.fields]);
|
||||||
|
|
||||||
|
const defaultValues = useMemo(
|
||||||
|
() => ({
|
||||||
|
name: appAuthClient?.name || '',
|
||||||
|
active: appAuthClient?.active || false,
|
||||||
|
...getAuthFieldsDefaultValues(),
|
||||||
|
}),
|
||||||
|
[appAuthClient, getAuthFieldsDefaultValues]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AdminApplicationAuthClientDialog
|
||||||
|
onClose={onClose}
|
||||||
|
error={error}
|
||||||
|
title={formatMessage('updateAuthClient.title')}
|
||||||
|
loading={loadingAuthClient}
|
||||||
|
submitHandler={submitHandler}
|
||||||
|
authFields={authFields}
|
||||||
|
submitting={loadingUpdateAppAuthClient}
|
||||||
|
defaultValues={defaultValues}
|
||||||
|
disabled={!appAuthClient}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@@ -17,7 +17,7 @@ type AppAuthClientsDialogProps = {
|
|||||||
|
|
||||||
export default function AppAuthClientsDialog(props: AppAuthClientsDialogProps) {
|
export default function AppAuthClientsDialog(props: AppAuthClientsDialogProps) {
|
||||||
const { appKey, onClientClick, onClose } = props;
|
const { appKey, onClientClick, onClose } = props;
|
||||||
const { appAuthClients } = useAppAuthClients(appKey);
|
const { appAuthClients } = useAppAuthClients({ appKey, active: true });
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
|
|
||||||
React.useEffect(
|
React.useEffect(
|
||||||
|
@@ -107,6 +107,10 @@ export const ADMIN_APP_SETTINGS = (appKey: string) =>
|
|||||||
`${ADMIN_SETTINGS}/apps/${appKey}/settings`;
|
`${ADMIN_SETTINGS}/apps/${appKey}/settings`;
|
||||||
export const ADMIN_APP_AUTH_CLIENTS = (appKey: string) =>
|
export const ADMIN_APP_AUTH_CLIENTS = (appKey: string) =>
|
||||||
`${ADMIN_SETTINGS}/apps/${appKey}/auth-clients`;
|
`${ADMIN_SETTINGS}/apps/${appKey}/auth-clients`;
|
||||||
|
export const ADMIN_APP_AUTH_CLIENT = (appKey: string, id: string) =>
|
||||||
|
`${ADMIN_SETTINGS}/apps/${appKey}/auth-clients/${id}`;
|
||||||
|
export const ADMIN_APP_AUTH_CLIENTS_CREATE = (appKey: string) =>
|
||||||
|
`${ADMIN_SETTINGS}/apps/${appKey}/auth-clients/create`;
|
||||||
|
|
||||||
export const DASHBOARD = FLOWS;
|
export const DASHBOARD = FLOWS;
|
||||||
|
|
||||||
|
12
packages/web/src/graphql/mutations/create-app-auth-client.ts
Normal file
12
packages/web/src/graphql/mutations/create-app-auth-client.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const CREATE_APP_AUTH_CLIENT = gql`
|
||||||
|
mutation CreateAppAuthClient($input: CreateAppAuthClientInput) {
|
||||||
|
createAppAuthClient(input: $input) {
|
||||||
|
id
|
||||||
|
appConfigId
|
||||||
|
name
|
||||||
|
active
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
12
packages/web/src/graphql/mutations/update-app-auth-client.ts
Normal file
12
packages/web/src/graphql/mutations/update-app-auth-client.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const UPDATE_APP_AUTH_CLIENT = gql`
|
||||||
|
mutation UpdateAppAuthClient($input: UpdateAppAuthClientInput) {
|
||||||
|
updateAppAuthClient(input: $input) {
|
||||||
|
id
|
||||||
|
appConfigId
|
||||||
|
name
|
||||||
|
active
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
@@ -1,28 +1,26 @@
|
|||||||
import { useLazyQuery } from '@apollo/client';
|
import { useLazyQuery } from '@apollo/client';
|
||||||
import { AppConfig } from '@automatisch/types';
|
import { AppAuthClient } from '@automatisch/types';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { GET_APP_AUTH_CLIENT } from 'graphql/queries/get-app-auth-client.ee';
|
import { GET_APP_AUTH_CLIENT } from 'graphql/queries/get-app-auth-client.ee';
|
||||||
|
|
||||||
type QueryResponse = {
|
type QueryResponse = {
|
||||||
getAppAuthClient: AppConfig;
|
getAppAuthClient: AppAuthClient;
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function useAppAuthClient(id: string) {
|
export default function useAppAuthClient(id?: string) {
|
||||||
const [
|
const [getAppAuthClient, { data, loading }] =
|
||||||
getAppAuthClient,
|
useLazyQuery<QueryResponse>(GET_APP_AUTH_CLIENT);
|
||||||
{
|
|
||||||
data,
|
|
||||||
loading
|
|
||||||
}
|
|
||||||
] = useLazyQuery<QueryResponse>(GET_APP_AUTH_CLIENT);
|
|
||||||
const appAuthClient = data?.getAppAuthClient;
|
const appAuthClient = data?.getAppAuthClient;
|
||||||
|
|
||||||
React.useEffect(function fetchUponId() {
|
React.useEffect(
|
||||||
if (!id) return;
|
function fetchUponId() {
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
getAppAuthClient({ variables: { id } });
|
getAppAuthClient({ variables: { id } });
|
||||||
}, [id]);
|
},
|
||||||
|
[id]
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
appAuthClient,
|
appAuthClient,
|
||||||
|
@@ -6,25 +6,33 @@ import { GET_APP_AUTH_CLIENTS } from 'graphql/queries/get-app-auth-clients.ee';
|
|||||||
|
|
||||||
type QueryResponse = {
|
type QueryResponse = {
|
||||||
getAppAuthClients: AppAuthClient[];
|
getAppAuthClients: AppAuthClient[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function useAppAuthClient(appKey: string) {
|
export default function useAppAuthClient({
|
||||||
const [
|
appKey,
|
||||||
getAppAuthClients,
|
active,
|
||||||
|
}: {
|
||||||
|
appKey: string;
|
||||||
|
active?: boolean;
|
||||||
|
}) {
|
||||||
|
const [getAppAuthClients, { data, loading }] = useLazyQuery<QueryResponse>(
|
||||||
|
GET_APP_AUTH_CLIENTS,
|
||||||
{
|
{
|
||||||
data,
|
context: { autoSnackbar: false },
|
||||||
loading
|
|
||||||
}
|
}
|
||||||
] = useLazyQuery<QueryResponse>(GET_APP_AUTH_CLIENTS, {
|
);
|
||||||
context: { autoSnackbar: false },
|
|
||||||
});
|
|
||||||
const appAuthClients = data?.getAppAuthClients;
|
const appAuthClients = data?.getAppAuthClients;
|
||||||
|
|
||||||
React.useEffect(function fetchUponAppKey() {
|
React.useEffect(
|
||||||
if (!appKey) return;
|
function fetchUponAppKey() {
|
||||||
|
if (!appKey) return;
|
||||||
|
|
||||||
getAppAuthClients({ variables: { appKey, active: true } });
|
getAppAuthClients({
|
||||||
}, [appKey]);
|
variables: { appKey, ...(typeof active === 'boolean' && { active }) },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[appKey]
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
appAuthClients,
|
appAuthClients,
|
||||||
|
@@ -256,5 +256,14 @@
|
|||||||
"adminAppsSettings.shared": "Shared",
|
"adminAppsSettings.shared": "Shared",
|
||||||
"adminAppsSettings.disabled": "Disabled",
|
"adminAppsSettings.disabled": "Disabled",
|
||||||
"adminAppsSettings.save": "Save",
|
"adminAppsSettings.save": "Save",
|
||||||
"adminAppsSettings.successfullySaved": "Settings have been saved."
|
"adminAppsSettings.successfullySaved": "Settings have been saved.",
|
||||||
|
"adminAppsAuthClients.noAuthClients": "You don't have any auth clients yet.",
|
||||||
|
"adminAppsAuthClients.statusActive": "Active",
|
||||||
|
"adminAppsAuthClients.statusInactive": "Inactive",
|
||||||
|
"createAuthClient.button": "Create auth client",
|
||||||
|
"createAuthClient.title": "Create auth client",
|
||||||
|
"authClient.buttonSubmit": "Submit",
|
||||||
|
"authClient.inputName": "Name",
|
||||||
|
"authClient.inputActive": "Active",
|
||||||
|
"updateAuthClient.title": "Update auth client"
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@ import {
|
|||||||
Routes,
|
Routes,
|
||||||
useParams,
|
useParams,
|
||||||
useMatch,
|
useMatch,
|
||||||
|
useNavigate,
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||||
@@ -23,6 +24,9 @@ import AppIcon from 'components/AppIcon';
|
|||||||
import Container from 'components/Container';
|
import Container from 'components/Container';
|
||||||
import PageTitle from 'components/PageTitle';
|
import PageTitle from 'components/PageTitle';
|
||||||
import AdminApplicationSettings from 'components/AdminApplicationSettings';
|
import AdminApplicationSettings from 'components/AdminApplicationSettings';
|
||||||
|
import AdminApplicationAuthClients from 'components/AdminApplicationAuthClients';
|
||||||
|
import AdminApplicationCreateAuthClient from 'components/AdminApplicationCreateAuthClient';
|
||||||
|
import AdminApplicationUpdateAuthClient from 'components/AdminApplicationUpdateAuthClient';
|
||||||
|
|
||||||
type AdminApplicationParams = {
|
type AdminApplicationParams = {
|
||||||
appKey: string;
|
appKey: string;
|
||||||
@@ -32,6 +36,7 @@ export default function AdminApplication(): React.ReactElement | null {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md'));
|
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md'));
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const connectionsPathMatch = useMatch({
|
const connectionsPathMatch = useMatch({
|
||||||
path: URLS.ADMIN_APP_CONNECTIONS_PATTERN,
|
path: URLS.ADMIN_APP_CONNECTIONS_PATTERN,
|
||||||
@@ -51,79 +56,104 @@ export default function AdminApplication(): React.ReactElement | null {
|
|||||||
|
|
||||||
const app = data?.getApp || {};
|
const app = data?.getApp || {};
|
||||||
|
|
||||||
|
const goToAuthClientsPage = () => navigate('auth-clients');
|
||||||
|
|
||||||
if (loading) return null;
|
if (loading) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
<>
|
||||||
<Grid container item xs={12} sm={10} md={9}>
|
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||||
<Grid container sx={{ mb: 3 }} alignItems="center">
|
<Grid container item xs={12} sm={10} md={9}>
|
||||||
<Grid item xs="auto" sx={{ mr: 3 }}>
|
<Grid container sx={{ mb: 3 }} alignItems="center">
|
||||||
<AppIcon
|
<Grid item xs="auto" sx={{ mr: 3 }}>
|
||||||
url={app.iconUrl}
|
<AppIcon
|
||||||
color={app.primaryColor}
|
url={app.iconUrl}
|
||||||
name={app.name}
|
color={app.primaryColor}
|
||||||
/>
|
name={app.name}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs>
|
||||||
|
<PageTitle>{app.name}</PageTitle>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs>
|
<Grid container>
|
||||||
<PageTitle>{app.name}</PageTitle>
|
<Grid item xs>
|
||||||
</Grid>
|
<Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 2 }}>
|
||||||
</Grid>
|
<Tabs
|
||||||
<Grid container>
|
variant={matchSmallScreens ? 'fullWidth' : undefined}
|
||||||
<Grid item xs>
|
value={
|
||||||
<Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 2 }}>
|
settingsPathMatch?.pattern?.path ||
|
||||||
<Tabs
|
connectionsPathMatch?.pattern?.path ||
|
||||||
variant={matchSmallScreens ? 'fullWidth' : undefined}
|
authClientsPathMatch?.pattern?.path
|
||||||
value={
|
}
|
||||||
settingsPathMatch?.pattern?.path ||
|
>
|
||||||
connectionsPathMatch?.pattern?.path ||
|
<Tab
|
||||||
authClientsPathMatch?.pattern?.path
|
label={formatMessage('adminApps.settings')}
|
||||||
}
|
to={URLS.ADMIN_APP_SETTINGS(appKey)}
|
||||||
>
|
value={URLS.ADMIN_APP_SETTINGS_PATTERN}
|
||||||
<Tab
|
component={Link}
|
||||||
label={formatMessage('adminApps.settings')}
|
/>
|
||||||
to={URLS.ADMIN_APP_SETTINGS(appKey)}
|
<Tab
|
||||||
value={URLS.ADMIN_APP_SETTINGS_PATTERN}
|
label={formatMessage('adminApps.authClients')}
|
||||||
component={Link}
|
to={URLS.ADMIN_APP_AUTH_CLIENTS(appKey)}
|
||||||
/>
|
value={URLS.ADMIN_APP_AUTH_CLIENTS_PATTERN}
|
||||||
<Tab
|
component={Link}
|
||||||
label={formatMessage('adminApps.authClients')}
|
/>
|
||||||
to={URLS.ADMIN_APP_AUTH_CLIENTS(appKey)}
|
<Tab
|
||||||
value={URLS.ADMIN_APP_AUTH_CLIENTS_PATTERN}
|
label={formatMessage('adminApps.connections')}
|
||||||
component={Link}
|
to={URLS.ADMIN_APP_CONNECTIONS(appKey)}
|
||||||
/>
|
value={URLS.ADMIN_APP_CONNECTIONS_PATTERN}
|
||||||
<Tab
|
disabled={!app.supportsConnections}
|
||||||
label={formatMessage('adminApps.connections')}
|
component={Link}
|
||||||
to={URLS.ADMIN_APP_CONNECTIONS(appKey)}
|
/>
|
||||||
value={URLS.ADMIN_APP_CONNECTIONS_PATTERN}
|
</Tabs>
|
||||||
disabled={!app.supportsConnections}
|
</Box>
|
||||||
component={Link}
|
|
||||||
/>
|
|
||||||
</Tabs>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route
|
<Route
|
||||||
path={`/settings/*`}
|
path={`/settings/*`}
|
||||||
element={<AdminApplicationSettings appKey={appKey} />}
|
element={<AdminApplicationSettings appKey={appKey} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path={`/auth-clients/*`}
|
path={`/auth-clients/*`}
|
||||||
element={<div>Auth clients</div>}
|
element={<AdminApplicationAuthClients appKey={appKey} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path={`/connections/*`}
|
path={`/connections/*`}
|
||||||
element={<div>App connections</div>}
|
element={<div>App connections</div>}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/"
|
path="/"
|
||||||
element={
|
element={
|
||||||
<Navigate to={URLS.ADMIN_APP_SETTINGS(appKey)} replace />
|
<Navigate to={URLS.ADMIN_APP_SETTINGS(appKey)} replace />
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Container>
|
||||||
</Container>
|
<Routes>
|
||||||
|
<Route
|
||||||
|
path="/auth-clients/create"
|
||||||
|
element={
|
||||||
|
<AdminApplicationCreateAuthClient
|
||||||
|
application={app}
|
||||||
|
onClose={goToAuthClientsPage}
|
||||||
|
appKey={appKey}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/auth-clients/:clientId"
|
||||||
|
element={
|
||||||
|
<AdminApplicationUpdateAuthClient
|
||||||
|
application={app}
|
||||||
|
onClose={goToAuthClientsPage}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Routes>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user