Passed
Push — feature/timeline-myfit-step ( 6743fb )
by Yonathan
05:13
created

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

Complexity

Total Complexity 8
Complexity/F 0

Size

Lines of Code 209
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 8
eloc 160
mnd 8
bc 8
fnc 0
dl 0
loc 209
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
rs 10
1
/* eslint-disable @typescript-eslint/camelcase */
2
import React, { createRef, MutableRefObject } from "react";
3
import { FormattedMessage } from "react-intl";
4
import _ from "lodash";
5
import { FormikProps } from "formik";
6
import { JobPosterQuestion, JobApplicationAnswer } from "../../../models/types";
7
import Question, { QuestionValues } from "./Question";
8
import { navigate } from "../../../helpers/router";
9
10
interface MyFitProps {
11
  /** List of job poster questions. */
12
  jobQuestions: JobPosterQuestion[];
13
  appAnswers: JobApplicationAnswer[];
14
  handleContinue: (values: JobApplicationAnswer[]) => void;
15
  handleQuit: () => void;
16
  handleReturn: () => void;
17
}
18
19
interface MyFitValues {
20
  id: number;
21
  jobPosterQuestionsId: number;
22
  jobApplicationId: number;
23
  answer: string;
24
}
25
26
export const MyFit: React.FunctionComponent<MyFitProps> = ({
27
  jobQuestions,
28
  appAnswers,
29
  handleContinue,
30
  handleQuit,
31
  handleReturn,
32
}) => {
33
  const focusOnElement = (id: string): void => {
34
    const element = document.getElementById(id);
35
    if (element) {
36
      element.focus();
37
    }
38
  };
39
40
  const validateAllForms = async (
41
    refs: React.MutableRefObject<
42
      React.MutableRefObject<FormikProps<QuestionValues>>[]
43
    >,
44
  ): Promise<boolean> => {
45
    for (let i = 0; i < refs.current.length; i += 1) {
46
      let ref = refs.current[i].current;
47
      // eslint-disable-next-line no-await-in-loop
48
      const isFormValid = await refs.current[i].current
49
        .validateForm()
50
        .then(() => {
51
          ref = refs.current[i].current;
52
          if (!_.isEmpty(ref.errors) && !ref.isSubmitting && !ref.isValid) {
53
            return false;
54
          }
55
          return true;
56
        });
57
      if (!isFormValid) {
58
        focusOnElement(`answer-${ref.values.id}`);
59
        break;
60
      }
61
    }
62
63
    const invalidForm = refs.current.some(
64
      (ref: MutableRefObject<FormikProps<QuestionValues>>) =>
65
        !ref.current.isValid,
66
    );
67
68
    return invalidForm ? Promise.resolve(false) : Promise.resolve(true);
69
  };
70
71
  const submitAllForms = async (
72
    refs: React.MutableRefObject<
73
      React.MutableRefObject<FormikProps<QuestionValues>>[]
74
    >,
75
  ): Promise<void[]> => {
76
    return Promise.all(
77
      refs.current.map((ref: MutableRefObject<FormikProps<QuestionValues>>) =>
78
        // TODO: Might need make one mass submission by combining all values into an array.
79
        ref.current.submitForm(),
80
      ),
81
    );
82
  };
83
84
  const validateAndSubmit = (
85
    refs: React.MutableRefObject<
86
      React.MutableRefObject<FormikProps<QuestionValues>>[]
87
    >,
88
    nextStep: string,
89
  ): Promise<void> =>
90
    validateAllForms(refs).then((response) => {
91
      if (response) {
92
        submitAllForms(refs).finally(() => {
93
          // navigate(nextStepUrl);
94
          console.log("navigate to next step");
95
        });
96
      }
97
    });
98
99
  const formRefs = React.useRef<
100
    MutableRefObject<FormikProps<QuestionValues>>[]
101
  >([]);
102
103
  return (
104
    <section data-c-container="medium">
105
      <h2 data-c-heading="h2" data-c-margin="top(3) bottom(1)">
106
        <FormattedMessage
107
          id="application.myfit.heading"
108
          defaultMessage="My Fit"
109
          description="Heading for the My Fit step in the Application Timeline"
110
        />
111
      </h2>
112
      <p>
113
        <FormattedMessage
114
          id="application.myfit.firstParagraph"
115
          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."
116
          description="Paragraph for the My Fit step in the Application Timeline"
117
        />
118
      </p>
119
      {jobQuestions.map((question, index) => {
120
        formRefs.current = [
121
          ...formRefs.current,
122
          formRefs.current[index] || createRef(),
123
        ];
124
125
        return (
126
          <Question
127
            key={question.id}
128
            applicationAnswer={
129
              appAnswers.find(
130
                (appAnswer) =>
131
                  appAnswer.job_poster_questions_id === question.id,
132
              ) ?? {
133
                id: 0,
134
                job_application_id: 0,
135
                answer: { en: "", fr: "" },
136
                job_poster_questions_id: 0,
137
              }
138
            }
139
            index={index}
140
            question={question}
141
            handleSubmit={async () => {}}
142
            formRef={formRefs.current[index]}
143
          />
144
        );
145
      })}
146
      <div data-c-container="medium" data-c-padding="tb(2)">
147
        <hr data-c-hr="thin(c1)" data-c-margin="bottom(2)" />
148
        <div data-c-grid="gutter">
149
          <div
150
            data-c-alignment="base(centre) tp(left)"
151
            data-c-grid-item="tp(1of2)"
152
          >
153
            <button
154
              data-c-button="outline(c2)"
155
              data-c-radius="rounded"
156
              type="button"
157
              onClick={() =>
158
                validateAndSubmit(formRefs, "ReplaceWithUrlOfPreviousStep")
159
              }
160
            >
161
              <FormattedMessage
162
                id="application.returnButtonLabel"
163
                defaultMessage="Save & Return to Previous Step"
164
                description="The text displayed on the Save & Return button of the Applicant Timeline form."
165
              />
166
            </button>
167
          </div>
168
          <div
169
            data-c-alignment="base(centre) tp(right)"
170
            data-c-grid-item="tp(1of2)"
171
          >
172
            <button
173
              data-c-button="outline(c2)"
174
              data-c-radius="rounded"
175
              type="button"
176
              onClick={() =>
177
                validateAndSubmit(formRefs, "ReplaceWithUrlOfMyApplications")
178
              }
179
            >
180
              <FormattedMessage
181
                id="application.quitButtonLabel"
182
                defaultMessage="Save & Quit"
183
                description="The text displayed on the Save & Return button of the Applicant Timeline form."
184
              />
185
            </button>
186
            <button
187
              data-c-button="solid(c1)"
188
              data-c-radius="rounded"
189
              data-c-margin="left(1)"
190
              type="button"
191
              onClick={() =>
192
                validateAndSubmit(formRefs, "ReplaceWithUrlOfNextStep")
193
              }
194
            >
195
              <FormattedMessage
196
                id="application.submitButtonLabel"
197
                defaultMessage="Save & Continue"
198
                description="The text displayed on the submit button for the Job Details form."
199
              />
200
            </button>
201
          </div>
202
        </div>
203
      </div>
204
    </section>
205
  );
206
};
207
208
export default MyFit;
209