Passed
Push — task/ci-browser-test-actions ( 30e209...126bd6 )
by
unknown
03:55
created

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

Complexity

Total Complexity 6
Complexity/F 0

Size

Lines of Code 170
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

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