skyderby/skyderby

View on GitHub
app/javascript/components/Events/SpeedSkydivingCompetition/Show/Scoreboard/CompetitorForm/index.tsx

Summary

Maintainability
F
5 days
Test Coverage
import React from 'react'
import { Formik, Field, FormikHelpers, FieldProps } from 'formik'
import toast from 'react-hot-toast'
import { ValueType } from 'react-select'

import Modal from 'components/ui/Modal'
import { useI18n } from 'components/TranslationsProvider'
import ProfileSelect from 'components/ProfileSelect'
import CountrySelect from 'components/CountrySelect'
import CategorySelect from './CategorySelect'
import validationSchema from './validationSchema'
import styles from './styles.module.scss'
import {
  EditCompetitorMutation,
  NewCompetitorMutation
} from 'api/speedSkydivingCompetitions/competitors'
import RequestErrorToast from 'components/RequestErrorToast'

interface CompetitorData {
  profileId: number | null
  profileAttributes: {
    name: string
    countryId: number | null
  }
  categoryId: number | null
  assignedNumber: number | null
}

interface FormData extends CompetitorData {
  newProfile: 'true' | 'false'
}

type CompetitorFormProps = {
  eventId: number
  initialValues?: Partial<CompetitorData>
  mutation: NewCompetitorMutation | EditCompetitorMutation
  onHide: () => void
}

const defaultInitialValues: FormData = {
  profileId: null,
  profileAttributes: {
    name: '',
    countryId: null
  },
  categoryId: null,
  assignedNumber: null,
  newProfile: 'false'
}

const CompetitorForm = ({
  eventId,
  initialValues = {},
  mutation,
  onHide: hide
}: CompetitorFormProps) => {
  const { t } = useI18n()

  const handleSubmit = async (values: FormData, formikBag: FormikHelpers<FormData>) => {
    const { newProfile, profileId, profileAttributes, ...params } = values
    const competitorParams = {
      ...params,
      ...(newProfile === 'true'
        ? { profileAttributes, profileId: null }
        : { profileId, profileAttributes: null })
    }

    mutation.mutate(competitorParams, {
      onSuccess: () => hide(),
      onSettled: () => formikBag.setSubmitting(false),
      onError: error => {
        toast.error(<RequestErrorToast response={error.response} />)
      }
    })
  }

  return (
    <Modal isShown={true} onHide={hide} title="New competitor" size="sm">
      <Formik
        initialValues={{ ...defaultInitialValues, ...initialValues }}
        validationSchema={validationSchema}
        onSubmit={handleSubmit}
      >
        {({ handleSubmit, isSubmitting, touched, errors }) => (
          <form onSubmit={handleSubmit}>
            <Modal.Body>
              <div className={styles.inputGroup}>
                <label className={styles.radioInput}>
                  <Field type="radio" name="newProfile" value="false" />
                  <span>{t('competitors.form.select_profile')}</span>
                </label>
                <Field name="profileId">
                  {({
                    field: { name, ...props },
                    form: { setFieldValue },
                    meta: { touched, error }
                  }: FieldProps) => (
                    <>
                      <ProfileSelect
                        isInvalid={touched && error}
                        {...props}
                        menuPortalTarget={document.getElementById('dropdowns-root')}
                        onChange={(option: ValueType<{ value: number }, false>) => {
                          if (option === null) {
                            setFieldValue(name, null)
                          } else {
                            setFieldValue(name, option.value)
                          }
                        }}
                      />
                    </>
                  )}
                </Field>
              </div>

              <div className={styles.inputGroup}>
                <label className={styles.radioInput}>
                  <Field type="radio" name="newProfile" value="true" />
                  <span>{t('competitors.form.create_profile')}</span>
                </label>
                <div className={styles.newProfileGroup}>
                  <label>{t('activerecord.attributes.profile.name')}</label>
                  <Field
                    name="profileAttributes[name]"
                    data-invalid={
                      errors?.profileAttributes?.name && touched?.profileAttributes?.name
                    }
                    className={styles.input}
                  />

                  <label>{t('activerecord.attributes.profile.country')}</label>
                  <Field name="profileAttributes[countryId]">
                    {({
                      field: { name, ...props },
                      form: { setFieldValue },
                      meta: { touched, error }
                    }: FieldProps) => (
                      <CountrySelect
                        isInvalid={touched && error}
                        {...props}
                        onChange={(option: ValueType<{ value: number }, false>) => {
                          if (option === null) {
                            setFieldValue(name, null)
                          } else {
                            setFieldValue(name, option.value)
                          }
                        }}
                      />
                    )}
                  </Field>
                </div>
              </div>

              <hr />

              <div className={styles.inputGroup}>
                <label htmlFor="assignedValue">
                  {t('activerecord.attributes.event/competitor.assigned_number')}
                </label>
                <Field
                  name="assignedNumber"
                  id="assignedValue"
                  className={styles.input}
                />
              </div>

              <div className={styles.inputGroup}>
                <label>{t('activerecord.attributes.event/competitor.section')}</label>
                <Field name="categoryId">
                  {({
                    field: { name, ...props },
                    form: { setFieldValue },
                    meta: { touched, error }
                  }: FieldProps) => (
                    <>
                      <CategorySelect
                        isInvalid={touched && error}
                        eventId={eventId}
                        {...props}
                        menuPortalTarget={document.getElementById('dropdowns-root')}
                        onChange={(option: ValueType<{ value: number }, false>) => {
                          if (option === null) {
                            setFieldValue(name, null)
                          } else {
                            setFieldValue(name, option.value)
                          }
                        }}
                      />
                    </>
                  )}
                </Field>
              </div>
            </Modal.Body>

            <Modal.Footer>
              <button
                className={styles.primaryButton}
                type="submit"
                disabled={isSubmitting}
              >
                {t('general.save')}
              </button>
              <button
                className={styles.secondaryButton}
                type="button"
                disabled={isSubmitting}
                data-dismiss="modal"
                onClick={hide}
              >
                {t('general.cancel')}
              </button>
            </Modal.Footer>
          </form>
        )}
      </Formik>
    </Modal>
  )
}

export default CompetitorForm