Coursemology/coursemology2

View on GitHub
client/app/bundles/course/survey/containers/SurveyFormDialogue/SurveyForm.jsx

Summary

Maintainability
C
1 day
Test Coverage
import useEmitterFactory from 'react-emitter-factory';
import { Controller, useForm } from 'react-hook-form';
import { injectIntl } from 'react-intl';
import { yupResolver } from '@hookform/resolvers/yup';
import PropTypes from 'prop-types';
import * as yup from 'yup';

import ErrorText from 'lib/components/core/ErrorText';
import FormCheckboxField from 'lib/components/form/fields/CheckboxField';
import FormDateTimePickerField from 'lib/components/form/fields/DateTimePickerField';
import FormRichTextField from 'lib/components/form/fields/RichTextField';
import FormTextField from 'lib/components/form/fields/TextField';
import formTranslations from 'lib/translations/form';

import translations from '../../translations';

const validationSchema = yup.object({
  title: yup.string().required(formTranslations.required),
  description: yup.string(),
  start_at: yup
    .date()
    .nullable()
    .typeError(formTranslations.invalidDate)
    .required(formTranslations.required),
  end_at: yup
    .date()
    .nullable()
    .typeError(formTranslations.invalidDate)
    .min(yup.ref('start_at'), translations.startEndValidationError)
    .when('allow_response_after_end', {
      is: true,
      then: yup
        .date()
        .min(yup.ref('start_at'), translations.startEndValidationError)
        .typeError(formTranslations.invalidDate)
        .required(formTranslations.required),
    }),
  bonus_end_at: yup
    .date()
    .nullable()
    .typeError(formTranslations.invalidDate)
    .min(yup.ref('start_at'), translations.bonusEndValidationError)
    .max(yup.ref('end_at'), translations.bonusEndValidationError),
  base_exp: yup
    .number()
    .typeError(formTranslations.required)
    .required(formTranslations.required),
  time_bonus_exp: yup
    .number()
    .nullable(true)
    .transform((_, val) => (val === Number(val) ? val : null)),
  allow_response_after_end: yup.bool(),
  allow_modify_after_submit: yup.bool(),
  has_todo: yup.bool(),
  anonymous: yup.bool(),
});

const SurveyForm = (props) => {
  const { intl, onSubmit, disabled, disableAnonymousToggle, initialValues } =
    props;
  const {
    control,
    handleSubmit,
    setError,
    formState: { errors, isDirty },
  } = useForm({
    defaultValues: initialValues,
    resolver: yupResolver(validationSchema),
  });

  useEmitterFactory(
    props,
    {
      isDirty,
    },
    [isDirty],
  );

  return (
    <form
      className="space-y-5"
      id="survey-form"
      noValidate
      onSubmit={handleSubmit((data) => onSubmit(data, setError))}
    >
      <ErrorText errors={errors} />
      <Controller
        control={control}
        name="title"
        render={({ field, fieldState }) => (
          <FormTextField
            disabled={disabled}
            field={field}
            fieldState={fieldState}
            fullWidth
            InputLabelProps={{
              shrink: true,
            }}
            label={intl.formatMessage(translations.title)}
            required
            variant="standard"
          />
        )}
      />

      <Controller
        control={control}
        name="description"
        render={({ field, fieldState }) => (
          <FormRichTextField
            disabled={disabled}
            field={field}
            fieldState={fieldState}
            fullWidth
            InputLabelProps={{
              shrink: true,
            }}
            label={intl.formatMessage(translations.description)}
            multiline
            rows={2}
            variant="standard"
          />
        )}
      />

      <div className="flex space-x-4">
        <Controller
          control={control}
          name="start_at"
          render={({ field, fieldState }) => (
            <FormDateTimePickerField
              disabled={disabled}
              field={field}
              fieldState={fieldState}
              label={intl.formatMessage(translations.startsAt)}
            />
          )}
        />

        <Controller
          control={control}
          name="end_at"
          render={({ field, fieldState }) => (
            <FormDateTimePickerField
              disabled={disabled}
              field={field}
              fieldState={fieldState}
              label={intl.formatMessage(translations.endsAt)}
            />
          )}
        />

        <Controller
          control={control}
          name="bonus_end_at"
          render={({ field, fieldState }) => (
            <FormDateTimePickerField
              disabled={disabled}
              field={field}
              fieldState={fieldState}
              label={intl.formatMessage(translations.bonusEndsAt)}
            />
          )}
        />
      </div>

      <div className="flex space-x-4">
        <Controller
          control={control}
          name="base_exp"
          render={({ field, fieldState }) => (
            <FormTextField
              disabled={disabled}
              field={field}
              fieldState={fieldState}
              fullWidth
              InputLabelProps={{
                shrink: true,
              }}
              label={intl.formatMessage(translations.basePoints)}
              onWheel={(event) => event.currentTarget.blur()}
              type="number"
              variant="standard"
            />
          )}
        />

        <Controller
          control={control}
          name="time_bonus_exp"
          render={({ field, fieldState }) => (
            <FormTextField
              disabled={disabled}
              field={field}
              fieldState={fieldState}
              fullWidth
              InputLabelProps={{
                shrink: true,
              }}
              label={intl.formatMessage(translations.bonusPoints)}
              onWheel={(event) => event.currentTarget.blur()}
              type="number"
              variant="standard"
            />
          )}
        />
      </div>

      <Controller
        control={control}
        name="has_todo"
        render={({ field, fieldState }) => (
          <FormCheckboxField
            description={intl.formatMessage(translations.hasTodoHint)}
            disabled={disabled}
            field={field}
            fieldState={fieldState}
            label={intl.formatMessage(translations.hasTodo)}
          />
        )}
      />

      <Controller
        control={control}
        name="allow_response_after_end"
        render={({ field, fieldState }) => (
          <FormCheckboxField
            disabled={disabled}
            field={field}
            fieldState={fieldState}
            label={intl.formatMessage(translations.allowResponseAfterEnd)}
          />
        )}
      />

      <Controller
        control={control}
        name="allow_modify_after_submit"
        render={({ field, fieldState }) => (
          <FormCheckboxField
            description={intl.formatMessage(
              translations.allowModifyAfterSubmitHint,
            )}
            disabled={disabled}
            field={field}
            fieldState={fieldState}
            label={intl.formatMessage(translations.allowModifyAfterSubmit)}
          />
        )}
      />

      <Controller
        control={control}
        name="anonymous"
        render={({ field, fieldState }) => (
          <FormCheckboxField
            description={
              disableAnonymousToggle
                ? intl.formatMessage(translations.hasStudentResponse)
                : intl.formatMessage(translations.anonymousHint)
            }
            disabled={disabled || disableAnonymousToggle}
            field={field}
            fieldState={fieldState}
            label={intl.formatMessage(translations.anonymous)}
          />
        )}
      />
    </form>
  );
};

SurveyForm.propTypes = {
  disabled: PropTypes.bool,
  disableAnonymousToggle: PropTypes.bool,
  initialValues: PropTypes.object.isRequired,
  intl: PropTypes.object.isRequired,
  onSubmit: PropTypes.func.isRequired,
};

export default injectIntl(SurveyForm);