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 vote down vote up
// 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 vote down vote up
/**
 * 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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
/**
 * 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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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>
	);
}