Passed
Push — feature/experience-step-functi... ( 88816e...5abe99 )
by Tristan
04:16
created

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

Complexity

Total Complexity 6
Complexity/F 0

Size

Lines of Code 168
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 6
eloc 132
mnd 6
bc 6
fnc 0
dl 0
loc 168
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 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
  /** List of job poster questions. */
16
  jobQuestions: JobPosterQuestion[];
17
  jobApplicationAnswers: JobApplicationAnswer[];
18
  handleSubmit: (values: JobApplicationAnswer) => Promise<void>;
19
  handleContinue: () => void;
20
  handleQuit: () => void;
21
  handleReturn: () => void;
22
}
23
24
export const Fit: React.FunctionComponent<FitProps> = ({
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
        return (
94
          <Question
95
            key={question.id}
96
            jobApplicationAnswer={
97
              jobApplicationAnswers.find(
98
                (appAnswer) =>
99
                  appAnswer.job_poster_questions_id === question.id,
100
              ) ?? {
101
                id: 0,
102
                job_application_id: 0,
103
                answer: "",
104
                job_poster_questions_id: 0,
105
              }
106
            }
107
            index={index}
108
            question={question}
109
            handleSubmit={handleSubmit}
110
            formRef={formRefs.current.get(question.id)}
111
          />
112
        );
113
      })}
114
      <div data-c-container="medium" data-c-padding="tb(2)">
115
        <hr data-c-hr="thin(c1)" data-c-margin="bottom(2)" />
116
        <div data-c-grid="gutter">
117
          <div
118
            data-c-alignment="base(centre) tp(left)"
119
            data-c-grid-item="tp(1of2)"
120
          >
121
            <button
122
              data-c-button="outline(c2)"
123
              data-c-radius="rounded"
124
              type="button"
125
              disabled={isSubmitting}
126
              onClick={(): Promise<void> =>
127
                validateAndSubmit(formRefs.current).then(handleReturn)
128
              }
129
            >
130
              {intl.formatMessage(navigationMessages.return)}
131
            </button>
132
          </div>
133
          <div
134
            data-c-alignment="base(centre) tp(right)"
135
            data-c-grid-item="tp(1of2)"
136
          >
137
            <button
138
              data-c-button="outline(c2)"
139
              data-c-radius="rounded"
140
              type="button"
141
              disabled={isSubmitting}
142
              onClick={(): Promise<void> =>
143
                validateAndSubmit(formRefs.current).then(handleQuit)
144
              }
145
            >
146
              {intl.formatMessage(navigationMessages.quit)}
147
            </button>
148
            <button
149
              data-c-button="solid(c1)"
150
              data-c-radius="rounded"
151
              data-c-margin="left(1)"
152
              type="button"
153
              disabled={isSubmitting}
154
              onClick={(): Promise<void> =>
155
                validateAndSubmit(formRefs.current).then(handleContinue)
156
              }
157
            >
158
              {intl.formatMessage(navigationMessages.continue)}
159
            </button>
160
          </div>
161
        </div>
162
      </div>
163
    </section>
164
  );
165
};
166
167
export default Fit;
168