formik#FormikErrors TypeScript Examples
The following examples show how to use
formik#FormikErrors.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: useInitialRequiredErrors.tsx From SQForm with MIT License | 7 votes |
// Until Formik exposes the validationSchema (again) via useFormikContext(), the solution has to be handled at the Form declaration level
// There's a few open PR's on this issue, here's one for reference: https://github.com/formium/formik/pull/2933
export function useInitialRequiredErrors<Values>(
validationSchema: AnyObjectSchema | undefined,
initialValues: FormikValues
): FormikErrors<Values> {
const initialRequiredErrorsRef = React.useRef(
Object.entries(validationSchema || {}).reduce((acc, [key, value]) => {
if (value?._exclusive?.required && !_getHasValue(initialValues[key])) {
return {...acc, [key]: 'Required'};
}
return acc;
}, {})
);
return initialRequiredErrorsRef.current;
}
Example #2
Source File: formikValidateJsonSchema.ts From amplication with Apache License 2.0 | 6 votes |
/**
* A function to validate a form based on a given JSON Schema.
* When using formik validation, do no use any DOM validation attributes to avoid the default browser validation and error messages
* @param values
* The data to be validated
* @param validationSchema
* The JSON schema for validation
* @returns
* FormikErrors object
* @example
* <Formik
* initialValues={INITIAL_VALUES}
* validate={(values: ValuesType) => {
* return validate<ValuesType>(values, FORM_SCHEMA);
* }}
* onSubmit={handleSubmit}
* >
* */
export function validate<T>(
values: T,
validationSchema: object
): FormikErrors<T> {
const errors: FormikErrors<T> = {};
const ajv = new Ajv({ allErrors: true });
let isValid = ajv.validate(validationSchema, values);
if (!isValid && ajv.errors) {
for (const error of ajv.errors) {
//remove the first dot from dataPath
const fieldName = error.instancePath.substring(1).replaceAll("/", ".");
set(errors, fieldName, error.message);
}
}
return errors;
}
Example #3
Source File: Quorum.tsx From homebase-app with MIT License | 6 votes |
validateForm = (values: QuorumSettings) => {
const errors: FormikErrors<QuorumSettings> = {};
Object.keys(values).forEach((key) => {
if ((values[key as keyof QuorumSettings] as number | string) === "") {
errors[key as keyof QuorumSettings] = "Required";
}
if (Number(values[key as keyof QuorumSettings]) < 0) {
errors[key as keyof QuorumSettings] = "Cannot be negative";
}
});
if (values.minQuorumAmount <= 0) {
errors.minQuorumAmount = "Must be greater than 0";
}
if (values.maxQuorumAmount >= 100) {
errors.maxQuorumAmount = "Must be lower than 100";
}
if (values.minQuorumAmount > values.maxQuorumAmount) {
errors.maxQuorumAmount = "Must be greater than Min. Quorum amount";
}
if (
values.quorumThreshold >= values.maxQuorumAmount ||
values.quorumThreshold <= values.minQuorumAmount
) {
errors.quorumThreshold = "Must be between Min and Max Quorum amounts";
}
if (values.quorumChange > values.quorumMaxChange) {
errors.quorumChange = "Cannot be greater than Max Quorum Change";
}
return errors;
}
Example #4
Source File: utils.ts From assisted-ui-lib with Apache License 2.0 | 6 votes |
getFormikErrorFields = <FormikValues>(
errors: FormikErrors<FormikValues>,
touched: FormikTouched<FormikValues>,
) => Object.keys(errors).filter((field) => touched[field])
Example #5
Source File: MaterialEdit.tsx From mayoor with MIT License | 6 votes |
getFormikValidate = (t: TFunction) => (values: { name: string; price: number }) => {
const errors: FormikErrors<{ name: string; price: number }> = {};
if (!values.name) {
errors.name = t('material_name_empty');
}
if (!values.price) {
errors.price = t('material_price_empty');
}
return errors;
}
Example #6
Source File: DaoSettings.tsx From homebase-app with MIT License | 5 votes |
validateForm = (values: OrgSettings) => {
const errors: FormikErrors<OrgSettings> = {};
if (!values.name) {
errors.name = "Required";
}
if (!values.symbol) {
errors.symbol = "Required";
}
if (!values.description) {
errors.description = "Required";
}
if (!values.administrator) {
errors.administrator = "Required";
}
if (values.administrator && isInvalidKtOrTzAddress(values.administrator)) {
errors.administrator = "Invalid address";
}
if (!values.guardian) {
errors.guardian = "Required";
}
if (values.guardian && isInvalidKtOrTzAddress(values.guardian)) {
errors.guardian = "Invalid address";
}
if (!values.governanceToken.address) {
errors.governanceToken = {
...errors.governanceToken,
address: "Required",
};
}
if (
values.governanceToken.address &&
validateContractAddress(values.governanceToken.address) !== 3
) {
errors.governanceToken = {
...errors.governanceToken,
address: "Invalid address",
};
}
if (!values.governanceToken.tokenId) {
errors.governanceToken = {
...errors.governanceToken,
tokenId: "Required",
};
}
if (!values.governanceToken.tokenMetadata) {
errors.governanceToken = {
...errors.governanceToken,
address: "Could not find token",
};
}
return errors;
}
Example #7
Source File: Governance.tsx From homebase-app with MIT License | 5 votes |
validateForm = (values: VotingSettings) => {
const errors: FormikErrors<VotingSettings> = {};
Object.keys(values).forEach((key) => {
if ((values[key as keyof VotingSettings] as number | string) === "") {
errors[key as keyof VotingSettings] = "Required";
}
if (Number(values[key as keyof VotingSettings]) < 0) {
errors[key as keyof VotingSettings] = "Cannot be negative";
}
});
if (!values.votingBlocks || Number(values.votingBlocks) <= 0) {
errors.votingBlocks = "Must be greater than 0";
}
if (!values.proposalFlushBlocks || Number(values.proposalFlushBlocks) <= 0) {
errors.proposalFlushBlocks = "Must be greater than 0";
}
if (
!values.proposalExpiryBlocks ||
Number(values.proposalExpiryBlocks) <= 0
) {
errors.proposalExpiryBlocks = "Must be greater than 0";
}
if (values.proposeStakeRequired <= 0) {
errors.proposeStakeRequired = "Must be greater than 0";
}
if (values.maxXtzAmount <= 0) {
errors.maxXtzAmount = "Must be greater than 0";
}
if (values.minXtzAmount > values.maxXtzAmount) {
errors.maxXtzAmount = "Must be greater than Min. XTZ amount";
}
return errors;
}
Example #8
Source File: ChangePassword.tsx From mayoor with MIT License | 5 votes |
ChangePassword: React.FC = () => {
const { t } = useTranslation();
const [changePassword, { loading }] = useMutation<
ChangePasswordMutation,
ChangePasswordMutationVariables
>(CHANGE_PASSWORD_MUTATION);
const formik = useFormik<FormValues>({
initialValues: {
oldPassword: '',
newPassword: '',
newPasswordRepeat: '',
},
validate: (values) => {
const errors: FormikErrors<FormValues> = {};
if (values.newPassword !== values.newPasswordRepeat) {
errors.newPasswordRepeat = t('new_passwords_must_match');
}
return errors;
},
onSubmit: async ({ oldPassword, newPassword }) => {
try {
await changePassword({ variables: { oldPassword, newPassword } });
message.success(t('pwd_changed'));
formik.resetForm();
} catch (err) {
if (err instanceof ApolloError) {
if (err.graphQLErrors[0].extensions?.code === 'INVALID_PASSWORD') {
formik.setErrors({
oldPassword: t('old_pwd_incorrect'),
});
} else {
message.error(t('error_changing_pwd'));
}
}
}
},
});
const getPasswordField = (name: keyof FormValues, label: string) => {
const errorMessage = formik.touched[name] && formik.errors[name];
const status = errorMessage ? 'error' : '';
return (
<FormItemStyled validateStatus={status} help={errorMessage}>
<Input
prefix={<LockFilled />}
placeholder={label}
name={name}
onChange={formik.handleChange}
value={formik.values[name]}
type="password"
/>
</FormItemStyled>
);
};
return (
<form onSubmit={formik.handleSubmit}>
<h4>{t('Change your password')}</h4>
{getPasswordField('oldPassword', t('Old Password'))}
<Row gutter={16}>
<Col span={12}>{getPasswordField('newPassword', t('New Password'))}</Col>
<Col span={12}>
{getPasswordField('newPasswordRepeat', t('Repeat New Password'))}
</Col>
</Row>
<Button type="primary" htmlType="submit" loading={loading}>
{t('Change password')}
</Button>
</form>
);
}
Example #9
Source File: EntityFieldForm.tsx From amplication with Apache License 2.0 | 4 votes |
EntityFieldForm = ({
onSubmit,
defaultValues = {},
isDisabled,
applicationId,
entityDisplayName,
}: Props) => {
const initialValues = useMemo(() => {
const sanitizedDefaultValues = omit(
defaultValues,
NON_INPUT_GRAPHQL_PROPERTIES
);
return {
...INITIAL_VALUES,
...sanitizedDefaultValues,
};
}, [defaultValues]);
function onKeyDown(keyEvent: any) {
if ((keyEvent.charCode || keyEvent.keyCode) === 13) {
keyEvent.preventDefault();
}
}
return (
<Formik
initialValues={initialValues}
validate={(values: Values) => {
const errors: FormikErrors<Values> = validate<Values>(
values,
FORM_SCHEMA
);
//validate the field dynamic properties
const schema = getSchemaForDataType(values.dataType);
const propertiesError = validate<Object>(values.properties, schema);
// Ignore related field ID error
if ("relatedFieldId" in propertiesError) {
// @ts-ignore
delete propertiesError.relatedFieldId;
}
if (!isEmpty(propertiesError)) {
errors.properties = propertiesError;
}
return errors;
}}
enableReinitialize
onSubmit={onSubmit}
>
{(formik) => {
const schema = getSchemaForDataType(formik.values.dataType);
return (
<Form childrenAsBlocks onKeyDown={onKeyDown}>
{!isDisabled && <FormikAutoSave debounceMS={1000} />}
<DisplayNameField
name="displayName"
label="Display Name"
disabled={isDisabled}
required
/>
<NameField name="name" disabled={isDisabled} required />
<OptionalDescriptionField
name="description"
label="Description"
disabled={isDisabled}
/>
<div>
<ToggleField
name="unique"
label="Unique Field"
disabled={isDisabled}
/>
</div>
<div>
<ToggleField
name="required"
label="Required Field"
disabled={isDisabled}
/>
</div>
<div>
<ToggleField
name="searchable"
label="Searchable"
disabled={isDisabled}
/>
</div>
{!SYSTEM_DATA_TYPES.has(formik.values.dataType) && (
<DataTypeSelectField label="Data Type" disabled={isDisabled} />
)}
<SchemaFields
schema={schema}
isDisabled={isDisabled}
applicationId={applicationId}
entityDisplayName={entityDisplayName}
/>
</Form>
);
}}
</Formik>
);
}
Example #10
Source File: useForm.tsx From houston with MIT License | 4 votes |
/**
* Hook implemation of IFormAdapter
* @param IUseFormParams
*/
export default function useForm<Values = Record<string, never>>({
onSubmit,
onSubmitWithErrors,
validationSchema,
initialValues,
validateOnMount = true
}: IUseFormParams<Values>): IFormAdapter<Values> {
const promiseRef = useRef<{ promise?: Promise<any> }>({}).current;
const handlers = useRef<{ [key: string]: (value: any) => void }>({}).current;
const submitData = useRef(new Subject<{ model: Partial<Values>; formikHelpers: FormikHelpers<Values> }>()).current;
const onSubmitRef = useRef<typeof onSubmit>(onSubmit);
useObservable(() => {
return submitData.pipe(
switchMap(({ model, formikHelpers }) => {
const result$ = onSubmitRef?.current?.(model as Values, formikHelpers);
const result = of(true).pipe(
switchMap(() => (!result$ ? of(null) : result$)),
catchError(() => of(null)),
share()
);
promiseRef.promise = result.toPromise();
return result;
})
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const formik = useFormik<Partial<Values>>({
validateOnMount,
initialValues: initialValues ?? {},
validationSchema: validationSchema ? () => validationSchema(yup) : null,
onSubmit: (model, formikHelpers) => {
onSubmitRef.current = onSubmit;
submitData.next({ model, formikHelpers });
return new Promise(resolve => setTimeout(() => resolve(promiseRef.promise), 500));
}
});
useEffect(() => {
if (!formik.submitCount || formik.isValid) return;
onSubmitWithErrors && onSubmitWithErrors(formik.errors, formik.values);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [formik.submitCount]);
const handleChange = useRef((field: string) => {
if (!handlers[field]) {
handlers[field] = (value: any) => {
formik.setFieldTouched(field, true, false);
formik.setFieldValue(field, value, false);
};
}
return handlers[field];
}).current;
const handleSubmit = useCallback(e => formik.handleSubmit(e), [formik]);
const getFieldValue = useCallback((name: string) => formik.getFieldMeta(name).value, [formik]);
const setFieldValue = useCallback((name: string, value: any) => {
formik.setFieldTouched(name, true, false);
formik.setFieldValue(name, value, true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const getFieldError = useCallback(
(name: string) => {
const field = formik.getFieldMeta(name);
return field.touched || (formik.submitCount > 0 && !field.value) ? field.error : '';
},
[formik]
);
const setErrors = useCallback((errors: FormikErrors<Partial<Values>>) => formik.setErrors(errors), [formik]);
return {
handleSubmit,
handleChange,
handleReset: () => formik.resetForm({ values: initialValues }),
setValues: formik.setValues,
setErrors: setErrors,
getFieldValue: getFieldValue,
setFieldValue: setFieldValue,
getFieldError: getFieldError,
setFieldTouched: formik.setFieldTouched,
reset: values => formik.resetForm({ values: values === undefined ? initialValues : values }),
initialValues: formik.initialValues,
values: formik.values,
isSubmitting: formik.isSubmitting,
isValid: formik.isValid,
errors: formik.errors
};
}
Example #11
Source File: SystemSettings.tsx From nextclade with MIT License | 4 votes |
export function SystemSettings() {
const { t } = useTranslationSafe()
const [numThreads, setNumThreads] = useRecoilState(numThreadsAtom)
const resetNumThreads = useResetRecoilState(numThreadsAtom)
const guess = useGuessNumThreads(numThreads)
const handleValidate = useCallback((values: SettingsFormValues): FormikErrors<SettingsFormValues> => {
const errors: FormikErrors<SettingsFormValues> = {}
const { numThreads } = values
if (!Number.isInteger(numThreads) || numThreads < 0 || numThreads > 1000) {
errors.numThreads = 'Should be a positive integer from 1 to 1000'
}
return errors
}, [])
const setNumThreadsDebounced = useMemo(
() => debounce(setNumThreads, 500, { leading: false, trailing: true }), // prettier-ignore
[setNumThreads],
)
const handleSubmit = useCallback(
(values: SettingsFormValues, { setSubmitting }: FormikHelpers<SettingsFormValues>) => {
setNumThreadsDebounced(values.numThreads)
setSubmitting(false)
},
[setNumThreadsDebounced],
)
const initialValues = useMemo(() => ({ numThreads }), [numThreads])
const onReset = useCallback(() => ({ numThreads }), [numThreads])
const memoryAvailable = useMemo(() => {
return guess.memoryAvailable ? prettyBytes.format(guess.memoryAvailable) : t('unsupported')
}, [guess.memoryAvailable, t])
const memoryAvailablePerThread = useMemo(() => {
return guess.memoryAvailable ? prettyBytes.format(guess.memoryAvailable / numThreads) : t('unsupported')
}, [guess.memoryAvailable, numThreads, t])
return (
<Formik initialValues={initialValues} validate={handleValidate} onSubmit={handleSubmit} onReset={onReset}>
{({ values, errors, touched, handleChange, handleBlur, resetForm }) => (
<Form>
<FormikAutoSubmit />
<FormGroup>
<Label className="d-block w-100">
<NumericInput
id="numThreads"
min={1}
max={1000}
className={classNames('d-inline', errors?.numThreads && 'border-danger')}
type="number"
identifier="settings-num-threads-input"
value={values.numThreads}
onChange={handleChange}
onBlur={handleBlur}
/>
<span className="d-inline">
<span className="mx-3">{t('Number of CPU threads')}</span>
<span className="mx-auto">
<ButtonTransparent
className="my-0"
type="button"
title={t('Reset to default')}
// eslint-disable-next-line react-perf/jsx-no-new-function-as-prop
onClick={() => {
resetNumThreads()
resetForm()
}}
>
<MdRefresh /> {t('Reset')}
</ButtonTransparent>
</span>
</span>
{touched.numThreads && errors?.numThreads && <p className="text-danger">{errors.numThreads}</p>}
{guess.numThreads && guess.memoryAvailable && (
<Alert className="mt-2 p-1" color="primary" isOpen fade={false}>
<TableSlim borderless className="small mb-1">
<tbody>
<tr>
<td>{t('Memory available*')}</td>
<td>{memoryAvailable}</td>
</tr>
<tr>
<td>{t('Memory per CPU thread')}</td>
<td>{memoryAvailablePerThread}</td>
</tr>
<tr>
<td>{t('Recommended number of CPU threads**')}</td>
<td>{guess.numThreads ?? t('unsupported')}</td>
</tr>
<tr>
<td colSpan={2} className="small">
{t('* Current value. This amount can change depending on load')}
</td>
</tr>
<tr>
<td colSpan={2} className="small">
{t('** {{appName}} requires at least {{memoryRequired}} of memory per thread', {
appName: PROJECT_NAME,
memoryRequired: prettyBytes.format(MEMORY_BYTES_PER_THREAD_MINIMUM),
})}
</td>
</tr>
</tbody>
</TableSlim>
</Alert>
)}
</Label>
</FormGroup>
</Form>
)}
</Formik>
)
}
Example #12
Source File: CreateCustomTypeModal.tsx From slice-machine with Apache License 2.0 | 4 votes |
CreateCustomTypeModal: React.FC = () => {
const { createCustomType, closeCreateCustomTypeModal } =
useSliceMachineActions();
const {
customTypeIds,
isCreateCustomTypeModalOpen,
isCreatingCustomType,
customTypeLabels,
} = useSelector((store: SliceMachineStoreType) => ({
customTypeIds: selectAllCustomTypeIds(store),
customTypeLabels: selectAllCustomTypeLabels(store),
isCreateCustomTypeModalOpen: isModalOpen(
store,
ModalKeysEnum.CREATE_CUSTOM_TYPE
),
isCreatingCustomType: isLoading(store, LoadingKeysEnum.CREATE_CUSTOM_TYPE),
}));
const createCustomTypeAndTrack = ({
id,
label,
repeatable,
}: {
id: string;
label: string;
repeatable: boolean;
}) => {
const name = label || id;
void Tracker.get().trackCreateCustomType({
id,
name,
repeatable,
});
createCustomType(id, name, repeatable);
};
return (
<ModalFormCard
dataCy="create-ct-modal"
isOpen={isCreateCustomTypeModalOpen}
widthInPx="530px"
formId="create-custom-type"
buttonLabel={"Create"}
close={closeCreateCustomTypeModal}
onSubmit={createCustomTypeAndTrack}
isLoading={isCreatingCustomType}
initialValues={{
repeatable: true,
id: "",
label: "",
}}
validate={({ id, label }) => {
const errors: FormikErrors<{
repeatable: boolean;
id: string;
label: string;
}> = {};
if (!label || !label.length) {
errors.label = "Cannot be empty.";
}
if (!errors.label && customTypeLabels.includes(label)) {
errors.label = "Custom Type name is already taken.";
}
if (!id || !id.length) {
errors.id = "ID cannot be empty.";
}
if (!errors.id && id && !/^[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*$/.exec(id)) {
errors.id = "Invalid id: No special characters allowed.";
}
if (
!errors.id &&
id &&
customTypeIds
.map((customTypeId) => customTypeId.toLowerCase())
.includes(id)
) {
errors.id = `ID "${id}" exists already.`;
}
return Object.keys(errors).length > 0 ? errors : undefined;
}}
content={{
title: "Create a new custom type",
}}
>
{({ errors }) => (
<Box>
<SelectRepeatable />
<InputBox
name="label"
label="Custom Type Name"
dataCy="ct-name-input"
placeholder="My Custom Type"
error={errors.label}
/>
<InputBox
name="id"
dataCy="ct-id-input"
label="Custom Type ID"
placeholder="my-custom-type"
error={errors.id}
/>
</Box>
)}
</ModalFormCard>
);
}
Example #13
Source File: CustomerForm.tsx From mayoor with MIT License | 4 votes |
CustomerForm: React.FC<Props> = (props) => {
const { t } = useTranslation();
return (
<Formik<UserFormValues>
initialValues={props.initialValues}
onSubmit={async (values, { resetForm }) => {
await props.onSubmit(values, resetForm);
}}
validate={(values) => {
const errors: FormikErrors<UserFormValues> = {};
if (!values.name) {
errors.name = t('missing_company_name');
}
return errors;
}}
>
{({ values, setFieldValue, handleChange, handleSubmit }) => (
<StyledForm onSubmit={handleSubmit}>
<Row gutter={32}>
<Col xs={24} md={12}>
<FormInput
name="name"
label={t('Company name')}
icon={<ContactsOutlined />}
/>
<Row gutter={16}>
<Col span={12}>
<IdentificationNumberInput />
</Col>
<Col span={12}>
<FormInput
name="taxIdentificationNumber"
label={t('Tax identification number')}
icon={<HddOutlined />}
/>
</Col>
</Row>
<StyledDivider orientation="left">{t('Contact person')}</StyledDivider>
<FormInput
name="personName"
label={t('Contact person name')}
icon={<UserOutlined />}
/>
<Row gutter={16}>
<Col span={12}>
<FormInput
name="email"
label={t('Email')}
icon={<MailOutlined />}
/>
</Col>
<Col span={12}>
<FormInput
name="phone"
label={t('Phone')}
icon={<PhoneOutlined />}
/>
</Col>
</Row>
</Col>
<Col xs={24} md={12}>
<Checkbox
name="allowedBankPayments"
onClick={() =>
setFieldValue(
'allowedBankPayments',
!values.allowedBankPayments,
)
}
checked={values.allowedBankPayments}
>
{t('Allow bank payments')}
</Checkbox>
<StyledFormItem label={t('Note')}>
<Input.TextArea
rows={4}
name="note"
placeholder={t('customer_note_placeholder')}
onChange={handleChange}
value={values.note || ''}
/>
</StyledFormItem>
</Col>
</Row>
<Row gutter={32}>
{values.addresses
.sort(({ isPrimary }) => (isPrimary ? -1 : 1))
.map((_, i) => (
<Col xs={24} md={12} key={i}>
<StyledDivider orientation="left">
{i === 0 ? t('Shipping address') : t('Billing address')}
</StyledDivider>
<StyledFormItem>
<Input
name={`addresses.${i}.street`}
prefix={<EnvironmentOutlined />}
placeholder={t('Street')}
onChange={handleChange}
value={values.addresses[i].street || ''}
/>
</StyledFormItem>
<Row gutter={12}>
<Col span={16}>
<StyledFormItem>
<Input
name={`addresses.${i}.city`}
prefix={<HomeOutlined />}
placeholder={t('City')}
onChange={handleChange}
value={values.addresses[i].city || ''}
/>
</StyledFormItem>
</Col>
<Col span={8}>
<StyledFormItem>
<Input
name={`addresses.${i}.postNumber`}
prefix={<NumberOutlined />}
placeholder={t('Post Number')}
onChange={handleChange}
value={values.addresses[i].postNumber || ''}
/>
</StyledFormItem>
</Col>
</Row>
</Col>
))}
</Row>
{props.submitButton}
</StyledForm>
)}
</Formik>
);
}
Example #14
Source File: LoginForm.tsx From mayoor with MIT License | 4 votes |
LoginForm: React.FC = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const [login, { loading }] = useMutation<LoginMutation, LoginMutationVariables>(LOGIN_MUTATION);
const { errors, handleSubmit, values, handleChange, isValid, setErrors, touched } = useFormik<
FormValues
>({
initialValues: {
username: '',
password: '',
},
validate: (values) => {
const errors: FormikErrors<FormValues> = {};
if (!values.password) {
errors.password = t('password_required');
}
if (!values.username) {
errors.username = t('username_required');
}
return errors;
},
onSubmit: async ({ username, password }) => {
try {
const result = await login({ variables: { email: username, password } });
if (result.data?.login) {
dispatch({
type: 'SET_CURRENT_USER',
user: result.data.login.user,
});
localStorage.setItem('auth-token', result.data.login.token);
}
} catch (err) {
if (err instanceof ApolloError) {
if (err.graphQLErrors[0].extensions?.code === 'USER_NOT_FOUND') {
setErrors({
username: t('user_not_found'),
});
}
if (err.graphQLErrors[0].extensions?.code === 'INVALID_PASSWORD') {
setErrors({
password: t('invalid_password'),
});
}
}
}
},
});
return (
<CenteredWrapper>
<S.LoginWrapper onSubmit={handleSubmit}>
<S.Logo src={LogoImage} />
<S.FormItemStyled
validateStatus={touched.username && errors.username ? 'error' : ''}
help={touched.username && errors.username}
>
<Input
prefix={<UserOutlined />}
placeholder={t('Username')}
name="username"
onChange={handleChange}
value={values.username}
data-test-id="login-username"
/>
</S.FormItemStyled>
<S.FormItemStyled
validateStatus={touched.password && errors.password ? 'error' : ''}
help={touched.password && errors.password}
>
<Input
prefix={<LockFilled />}
placeholder={t('Password')}
name="password"
onChange={handleChange}
value={values.password}
type="password"
data-test-id="login-password"
/>
</S.FormItemStyled>
<Button
icon={<LoginOutlined />}
loading={loading}
disabled={!isValid}
htmlType="submit"
data-test-id="login-submit-button"
>
{t('Log In')}
</Button>
<S.LanguageSwitchWrapper>
<LanguageSwitch />
</S.LanguageSwitchWrapper>
</S.LoginWrapper>
</CenteredWrapper>
);
}