diff --git a/packages/console/src/components/TextInput/index.module.scss b/packages/console/src/components/TextInput/index.module.scss index 034ad9391..07d94d6af 100644 --- a/packages/console/src/components/TextInput/index.module.scss +++ b/packages/console/src/components/TextInput/index.module.scss @@ -72,3 +72,9 @@ } } } + +.errorMessage { + font: var(--font-body-medium); + color: var(--color-error); + margin-top: _.unit(1); +} diff --git a/packages/console/src/components/TextInput/index.tsx b/packages/console/src/components/TextInput/index.tsx index 09c132fd8..5d09b15b0 100644 --- a/packages/console/src/components/TextInput/index.tsx +++ b/packages/console/src/components/TextInput/index.tsx @@ -5,27 +5,31 @@ import * as styles from './index.module.scss'; type Props = HTMLProps & { hasError?: boolean; + errorMessage?: string; icon?: ReactNode; }; const TextInput = ( - { hasError = false, icon, disabled, className, readOnly, ...rest }: Props, + { hasError = false, errorMessage, icon, disabled, className, readOnly, ...rest }: Props, reference: ForwardedRef ) => { return ( -
- {icon && {icon}} - -
+ <> +
+ {icon && {icon}} + +
+ {hasError && errorMessage &&
{errorMessage}
} + ); }; diff --git a/packages/console/src/pages/ApiResourceDetails/index.tsx b/packages/console/src/pages/ApiResourceDetails/index.tsx index 69906d556..d6d2a5d13 100644 --- a/packages/console/src/pages/ApiResourceDetails/index.tsx +++ b/packages/console/src/pages/ApiResourceDetails/index.tsx @@ -46,7 +46,7 @@ const ApiResourceDetails = () => { handleSubmit, register, reset, - formState: { isSubmitting }, + formState: { isSubmitting, errors }, } = useForm({ defaultValues: data, }); @@ -152,7 +152,10 @@ const ApiResourceDetails = () => { title="admin_console.api_resources.api_name" className={styles.textField} > - + { > diff --git a/packages/console/src/pages/ApplicationDetails/components/Settings.tsx b/packages/console/src/pages/ApplicationDetails/components/Settings.tsx index fdbe7ae3f..f309f1347 100644 --- a/packages/console/src/pages/ApplicationDetails/components/Settings.tsx +++ b/packages/console/src/pages/ApplicationDetails/components/Settings.tsx @@ -18,7 +18,11 @@ type Props = { }; const Settings = ({ oidcConfig }: Props) => { - const { control, register } = useFormContext(); + const { + control, + register, + formState: { errors }, + } = useFormContext(); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const uriPatternRules: MultiTextInputRule = { @@ -35,7 +39,7 @@ const Settings = ({ oidcConfig }: Props) => { title="admin_console.application_details.application_name" className={styles.textField} > - + diff --git a/packages/console/src/pages/Applications/components/CreateForm/index.tsx b/packages/console/src/pages/Applications/components/CreateForm/index.tsx index bc68ec605..ef232ca8f 100644 --- a/packages/console/src/pages/Applications/components/CreateForm/index.tsx +++ b/packages/console/src/pages/Applications/components/CreateForm/index.tsx @@ -121,7 +121,7 @@ const CreateForm = ({ onClose }: Props) => { )} - + diff --git a/packages/console/src/pages/SignInExperience/components/BrandingForm.tsx b/packages/console/src/pages/SignInExperience/components/BrandingForm.tsx index 5d7aedf02..5f7cbe2b7 100644 --- a/packages/console/src/pages/SignInExperience/components/BrandingForm.tsx +++ b/packages/console/src/pages/SignInExperience/components/BrandingForm.tsx @@ -8,13 +8,19 @@ import FormField from '@/components/FormField'; import RadioGroup, { Radio } from '@/components/RadioGroup'; import Switch from '@/components/Switch'; import TextInput from '@/components/TextInput'; +import { uriValidator } from '@/utilities/validator'; import { SignInExperienceForm } from '../types'; import * as styles from './index.module.scss'; const BrandingForm = () => { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - const { watch, register, control } = useFormContext(); + const { + watch, + register, + control, + formState: { errors }, + } = useFormContext(); const isDarkModeEnabled = watch('branding.isDarkModeEnabled'); const style = watch('branding.style'); @@ -66,15 +72,47 @@ const BrandingForm = () => { /> - + { + if (uriValidator({ verifyBlank: false })(value)) { + return true; + } + + return t('errors.invalid_uri_format'); + }, + })} + hasError={Boolean(errors.branding?.logoUrl)} + errorMessage={errors.branding?.logoUrl?.message} + /> {isDarkModeEnabled && ( - + { + if (!value) { + return true; + } + + if (uriValidator({ verifyBlank: false })(value)) { + return true; + } + + return t('errors.invalid_uri_format'); + }, + })} + hasError={Boolean(errors.branding?.darkLogoUrl)} + errorMessage={errors.branding?.darkLogoUrl?.message} + /> )} - + ); diff --git a/packages/console/src/pages/SignInExperience/components/TermsForm.tsx b/packages/console/src/pages/SignInExperience/components/TermsForm.tsx index 3ebf9253d..64ec56235 100644 --- a/packages/console/src/pages/SignInExperience/components/TermsForm.tsx +++ b/packages/console/src/pages/SignInExperience/components/TermsForm.tsx @@ -11,7 +11,11 @@ import * as styles from './index.module.scss'; const TermsForm = () => { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - const { watch, register } = useFormContext(); + const { + watch, + register, + formState: { errors }, + } = useFormContext(); const enabled = watch('termsOfUse.enabled'); return ( @@ -28,7 +32,10 @@ const TermsForm = () => { title="admin_console.sign_in_exp.terms_of_use.terms_of_use" tooltip="admin_console.sign_in_exp.terms_of_use.terms_of_use_tip" > - + ); diff --git a/packages/console/src/pages/UserDetails/index.tsx b/packages/console/src/pages/UserDetails/index.tsx index 543fe3865..77e9c37f3 100644 --- a/packages/console/src/pages/UserDetails/index.tsx +++ b/packages/console/src/pages/UserDetails/index.tsx @@ -28,6 +28,7 @@ import Reset from '@/icons/Reset'; import * as detailsStyles from '@/scss/details.module.scss'; import * as modalStyles from '@/scss/modal.module.scss'; import { safeParseJson } from '@/utilities/json'; +import { uriValidator } from '@/utilities/validator'; import CreateSuccess from './components/CreateSuccess'; import DeleteForm from './components/DeleteForm'; @@ -60,7 +61,7 @@ const UserDetails = () => { register, control, reset, - formState: { isSubmitting }, + formState: { isSubmitting, errors }, getValues, } = useForm(); @@ -221,7 +222,23 @@ const UserDetails = () => { title="admin_console.user_details.field_avatar" className={styles.textField} > - + { + if (!value) { + return true; + } + + if (uriValidator({ verifyBlank: true })(value)) { + return true; + } + + return t('errors.invalid_uri_format'); + }, + })} + hasError={Boolean(errors.avatar)} + errorMessage={errors.avatar?.message} + /> { const { handleSubmit, register, - formState: { isSubmitting }, + formState: { isSubmitting, errors }, } = useForm(); + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + const api = useApi(); const onSubmit = handleSubmit(async (data) => { @@ -53,13 +57,40 @@ const CreateForm = ({ onClose }: Props) => { >
- + - + - +
diff --git a/packages/phrases/src/locales/en.ts b/packages/phrases/src/locales/en.ts index bbd647cfa..cd382be48 100644 --- a/packages/phrases/src/locales/en.ts +++ b/packages/phrases/src/locales/en.ts @@ -121,6 +121,9 @@ const translation = { required_field_missing: 'Please enter {{field}}', required_field_missing_plural: 'You have to enter at least one {{field}}', more_details: 'More details', + username_pattern_error: + 'Username should only contain letters, numbers, or underscore and should not start with a number.', + password_pattern_error: 'Password requires a minimum of 6 characters.', }, tab_sections: { overview: 'Overview', diff --git a/packages/phrases/src/locales/zh-cn.ts b/packages/phrases/src/locales/zh-cn.ts index 95dca1cf8..b38189098 100644 --- a/packages/phrases/src/locales/zh-cn.ts +++ b/packages/phrases/src/locales/zh-cn.ts @@ -121,6 +121,8 @@ const translation = { required_field_missing: '请输入{{field}}', required_field_missing_plural: '{{field}}不能全部为空', more_details: '查看详情', + username_pattern_error: '用户名只能包含英文字母、数字或下划线,且不以数字开头。', + password_pattern_error: '密码应不少于 6 位。', }, tab_sections: { overview: '概览',