Passed
Push — dev ( 597815...a2b683 )
by
unknown
06:00
created

resources/assets/js/components/Application/Fit/Fit.tsx   A

Complexity

Total Complexity 6
Complexity/F 0

Size

Lines of Code 169
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 6
eloc 131
mnd 6
bc 6
fnc 0
dl 0
loc 169
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import React, { createRef, useState, MutableRefObject } from "react";
2
import { useIntl, FormattedMessage } from "react-intl";
3
import { FormikProps } from "formik";
4
import { JobPosterQuestion, JobApplicationAnswer } from "../../../models/types";
5
import Question, { QuestionValues } from "./Question";
6
import {
7
  validateAllForms,
8
  submitAllForms,
9
  focusOnElement,
10
} from "../../../helpers/forms";
11
import { fitMessages, navigationMessages } from "../applicationMessages";
12
13
interface FitProps {
14
  applicationId: number;
15
  jobQuestions: JobPosterQuestion[];
16
  jobApplicationAnswers: JobApplicationAnswer[];
17
  handleSubmit: (values: JobApplicationAnswer) => Promise<void>;
18
  handleContinue: () => Promise<void>;
19
  handleQuit: () => void;
20
  handleReturn: () => void;
21
}
22
23
export const Fit: React.FunctionComponent<FitProps> = ({
24
  applicationId,
25
  jobQuestions,
26
  jobApplicationAnswers,
27
  handleSubmit,
28
  handleContinue,
29
  handleQuit,
30
  handleReturn,
31
}) => {
32
  const intl = useIntl();
33
  const [isSubmitting, setIsSubmitting] = useState(false);
34
35
  /**
36
   *
37
   * @param refMap A map of question id to formik Form ref
38
   * @returns A promise that resolves if submission succeeds, and rejects if validation or submission fails.
39
   */
40
  const validateAndSubmit = async (
41
    refMap: Map<number, React.MutableRefObject<FormikProps<QuestionValues>>>,
42
  ): Promise<void> => {
43
    setIsSubmitting(true);
44
    const refs = Array.from(refMap.values());
45
    const formsAreValid = await validateAllForms(refs);
46
    if (formsAreValid) {
47
      try {
48
        await submitAllForms(refs);
49
        setIsSubmitting(false);
50
        return Promise.resolve();
51
      } catch {
52
        setIsSubmitting(false);
53
        return Promise.reject();
54
      }
55
    } else {
56
      Array.from(refMap.entries()).some((ref) => {
57
        const [questionId, formRef] = ref;
58
        if (!formRef.current.isValid) {
59
          focusOnElement(`answer-${questionId}`);
60
          return true;
61
        }
62
        return false;
63
      });
64
      setIsSubmitting(false);
65
      return Promise.reject();
66
    }
67
  };
68
69
  const formRefs = React.useRef<
70
    Map<number, React.MutableRefObject<FormikProps<QuestionValues>>>
71
  >(new Map());
72
73
  return (
74
    <section data-c-container="medium">
75
      <h2 data-c-heading="h2" data-c-margin="top(3) bottom(1)">
76
        {intl.formatMessage(fitMessages.heading)}
77
      </h2>
78
      <p>
79
        <FormattedMessage
80
          id="application.fit.firstParagraph"
81
          defaultMessage="The manager has included a few further questions that they'd like you to answer in order to be considered for this position. Questions that require an answer are marked, while all others are optional."
82
          description="Paragraph for the Fit step in the Application form."
83
        />
84
      </p>
85
      {jobQuestions.map((question, index) => {
86
        if (!formRefs.current.has(question.id)) {
87
          const ref = createRef() as MutableRefObject<
88
            FormikProps<QuestionValues>
89
          >;
90
          formRefs.current.set(question.id, ref);
91
        }
92
93
        const jobApplicationAnswer = jobApplicationAnswers.find(
94
          (appAnswer) => appAnswer.job_poster_question_id === question.id,
95
        );
96
97
        const draftAnswer = {
98
          id: -1,
99
          job_application_id: applicationId,
100
          answer: "",
101
          job_poster_question_id: question.id,
102
        };
103
104
        return (
105
          <Question
106
            key={question.id}
107
            jobApplicationAnswer={jobApplicationAnswer ?? draftAnswer}
108
            index={index}
109
            question={question}
110
            handleSubmit={handleSubmit}
111
            formRef={formRefs.current.get(question.id)}
112
          />
113
        );
114
      })}
115
      <div data-c-container="medium" data-c-padding="tb(2)">
116
        <hr data-c-hr="thin(c1)" data-c-margin="bottom(2)" />
117
        <div data-c-grid="gutter">
118
          <div
119
            data-c-alignment="base(centre) tp(left)"
120
            data-c-grid-item="tp(1of2)"
121
          >
122
            <button
123
              data-c-button="outline(c2)"
124
              data-c-radius="rounded"
125
              type="button"
126
              disabled={isSubmitting}
127
              onClick={(): Promise<void> =>
128
                validateAndSubmit(formRefs.current).then(handleReturn)
129
              }
130
            >
131
              {intl.formatMessage(navigationMessages.return)}
132
            </button>
133
          </div>
134
          <div
135
            data-c-alignment="base(centre) tp(right)"
136
            data-c-grid-item="tp(1of2)"
137
          >
138
            <button
139
              data-c-button="outline(c2)"
140
              data-c-radius="rounded"
141
              type="button"
142
              disabled={isSubmitting}
143
              onClick={(): Promise<void> =>
144
                validateAndSubmit(formRefs.current).then(handleQuit)
145
              }
146
            >
147
              {intl.formatMessage(navigationMessages.quit)}
148
            </button>
149
            <button
150
              data-c-button="solid(c1)"
151
              data-c-radius="rounded"
152
              data-c-margin="left(1)"
153
              type="button"
154
              disabled={isSubmitting}
155
              onClick={(): Promise<void> =>
156
                validateAndSubmit(formRefs.current).then(handleContinue)
157
              }
158
            >
159
              {intl.formatMessage(navigationMessages.continue)}
160
            </button>
161
          </div>
162
        </div>
163
      </div>
164
    </section>
165
  );
166
};
167
168
export default Fit;
169