2022-10-21 13:14:17 +08:00
|
|
|
import type { AdminConsoleKey } from '@logto/phrases';
|
|
|
|
import type { Application } from '@logto/schemas';
|
2023-09-04 16:05:30 +08:00
|
|
|
import { conditional } from '@silverhand/essentials';
|
2022-10-21 13:14:17 +08:00
|
|
|
import type { KeyboardEvent } from 'react';
|
2023-09-04 16:05:30 +08:00
|
|
|
import { useContext, useRef } from 'react';
|
2022-06-27 20:10:27 +08:00
|
|
|
import { Controller, FormProvider, useForm } from 'react-hook-form';
|
2022-06-29 10:26:04 +08:00
|
|
|
import { toast } from 'react-hot-toast';
|
2022-06-27 20:10:27 +08:00
|
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
import useSWR from 'swr';
|
|
|
|
|
2023-09-11 12:03:41 +08:00
|
|
|
import { GuideContext } from '@/components/Guide';
|
2022-11-29 18:44:50 +08:00
|
|
|
import MultiTextInputField from '@/components/MultiTextInputField';
|
2023-06-16 22:26:13 +08:00
|
|
|
import Button from '@/ds-components/Button';
|
|
|
|
import {
|
|
|
|
convertRhfErrorMessage,
|
|
|
|
createValidatorForRhf,
|
|
|
|
} from '@/ds-components/MultiTextInput/utils';
|
2022-10-21 13:14:17 +08:00
|
|
|
import type { RequestError } from '@/hooks/use-api';
|
|
|
|
import useApi from '@/hooks/use-api';
|
|
|
|
import type { GuideForm } from '@/types/guide';
|
2023-06-13 11:28:41 +08:00
|
|
|
import { trySubmitSafe } from '@/utils/form';
|
2023-02-15 10:26:22 +08:00
|
|
|
import { uriValidator } from '@/utils/validator';
|
2022-06-27 20:10:27 +08:00
|
|
|
|
|
|
|
import * as styles from './index.module.scss';
|
|
|
|
|
|
|
|
type Props = {
|
|
|
|
name: 'redirectUris' | 'postLogoutRedirectUris';
|
2023-09-04 16:05:30 +08:00
|
|
|
/** The default value of the input field when there's no data. */
|
|
|
|
defaultValue?: string;
|
2022-06-27 20:10:27 +08:00
|
|
|
};
|
|
|
|
|
2023-09-04 16:05:30 +08:00
|
|
|
function UriInputField({ name, defaultValue }: Props) {
|
2022-06-27 20:10:27 +08:00
|
|
|
const methods = useForm<Partial<GuideForm>>();
|
|
|
|
const {
|
|
|
|
control,
|
|
|
|
getValues,
|
|
|
|
handleSubmit,
|
|
|
|
reset,
|
|
|
|
formState: { isSubmitting },
|
|
|
|
} = methods;
|
2023-09-11 12:03:41 +08:00
|
|
|
const { app } = useContext(GuideContext);
|
|
|
|
const appId = app?.id;
|
|
|
|
const { data, mutate } = useSWR<Application, RequestError>(appId && `api/applications/${appId}`);
|
2022-06-27 20:10:27 +08:00
|
|
|
|
|
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
|
|
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
|
|
|
const api = useApi();
|
2023-09-04 16:05:30 +08:00
|
|
|
const title: AdminConsoleKey =
|
|
|
|
name === 'redirectUris'
|
|
|
|
? 'application_details.redirect_uri'
|
|
|
|
: 'application_details.post_sign_out_redirect_uri';
|
2022-06-27 20:10:27 +08:00
|
|
|
|
2023-06-13 11:28:41 +08:00
|
|
|
const onSubmit = trySubmitSafe(async (value: string[]) => {
|
2023-09-11 12:03:41 +08:00
|
|
|
if (!appId) {
|
|
|
|
return;
|
|
|
|
}
|
2022-06-27 20:10:27 +08:00
|
|
|
const updatedApp = await api
|
2023-02-10 01:34:23 +08:00
|
|
|
.patch(`api/applications/${appId}`, {
|
2022-06-27 20:10:27 +08:00
|
|
|
json: {
|
|
|
|
oidcClientMetadata: {
|
|
|
|
[name]: value.filter(Boolean),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.json<Application>();
|
|
|
|
void mutate(updatedApp);
|
2022-06-29 10:26:04 +08:00
|
|
|
toast.success(t('general.saved'));
|
2022-06-27 20:10:27 +08:00
|
|
|
|
|
|
|
// Reset form to set 'isDirty' to false
|
|
|
|
reset(getValues());
|
2023-06-13 11:28:41 +08:00
|
|
|
});
|
2022-06-27 20:10:27 +08:00
|
|
|
|
|
|
|
const onKeyPress = (event: KeyboardEvent<HTMLInputElement>, value: string[]) => {
|
|
|
|
if (event.key === 'Enter') {
|
|
|
|
event.preventDefault();
|
|
|
|
void handleSubmit(async () => onSubmit(value))();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-09-04 16:05:30 +08:00
|
|
|
const clientMetadata = data?.oidcClientMetadata[name];
|
|
|
|
const defaultValueArray = clientMetadata?.length
|
|
|
|
? clientMetadata
|
|
|
|
: conditional(defaultValue && [defaultValue]);
|
|
|
|
|
2022-06-27 20:10:27 +08:00
|
|
|
return (
|
|
|
|
<FormProvider {...methods}>
|
2023-09-04 16:05:30 +08:00
|
|
|
<form className={styles.form}>
|
2022-11-29 18:44:50 +08:00
|
|
|
<Controller
|
|
|
|
name={name}
|
|
|
|
control={control}
|
2023-09-04 16:05:30 +08:00
|
|
|
defaultValue={defaultValueArray}
|
2022-11-29 18:44:50 +08:00
|
|
|
rules={{
|
|
|
|
validate: createValidatorForRhf({
|
2023-09-06 23:23:21 +08:00
|
|
|
required: t('errors.required_field_missing_plural', { field: t(title) }),
|
2022-11-29 18:44:50 +08:00
|
|
|
pattern: {
|
|
|
|
verify: (value) => !value || uriValidator(value),
|
|
|
|
message: t('errors.invalid_uri_format'),
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
}}
|
|
|
|
render={({ field: { onChange, value = [] }, fieldState: { error, isDirty } }) => {
|
|
|
|
const errorObject = convertRhfErrorMessage(error?.message);
|
2022-06-27 20:10:27 +08:00
|
|
|
|
2022-11-29 18:44:50 +08:00
|
|
|
return (
|
|
|
|
<div ref={ref} className={styles.wrapper}>
|
2023-09-06 22:55:11 +08:00
|
|
|
<MultiTextInputField
|
|
|
|
isRequired={name === 'redirectUris'}
|
|
|
|
formFieldClassName={styles.field}
|
|
|
|
title={title}
|
|
|
|
value={value}
|
|
|
|
error={errorObject}
|
|
|
|
className={styles.multiTextInput}
|
|
|
|
onChange={onChange}
|
|
|
|
onKeyPress={(event) => {
|
|
|
|
onKeyPress(event, value);
|
|
|
|
}}
|
|
|
|
/>
|
2022-11-29 18:44:50 +08:00
|
|
|
<Button
|
|
|
|
className={styles.saveButton}
|
|
|
|
disabled={!isDirty}
|
|
|
|
isLoading={isSubmitting}
|
|
|
|
title="general.save"
|
|
|
|
type="primary"
|
|
|
|
onClick={handleSubmit(async () => onSubmit(value))}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}}
|
|
|
|
/>
|
2022-06-27 20:10:27 +08:00
|
|
|
</form>
|
|
|
|
</FormProvider>
|
|
|
|
);
|
2023-03-22 16:45:10 +08:00
|
|
|
}
|
2022-06-27 20:10:27 +08:00
|
|
|
|
|
|
|
export default UriInputField;
|