Passed
Push — dev ( 63db4c...b626e2 )
by
unknown
05:07
created

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

Complexity

Total Complexity 14
Complexity/F 0

Size

Lines of Code 336
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 14
eloc 277
mnd 14
bc 14
fnc 0
dl 0
loc 336
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
rs 10
1
/* eslint-disable camelcase */
2
import * as React from "react";
3
import { useIntl } from "react-intl";
4
import { Formik, Form, FastField } from "formik";
5
import * as Yup from "yup";
6
import messages, {
7
  citizenshipDeclaration,
8
  veteranStatus,
9
  languageRequirementDescription,
10
  languageRequirementLabel,
11
} from "./basicInfoMessages";
12
import {
13
  basicInfoMessages,
14
  navigationMessages,
15
  educationRequirementMessages,
16
} from "../applicationMessages";
17
import SelectInput from "../../Form/SelectInput";
18
import {
19
  CitizenshipId,
20
  VeteranId,
21
  LanguageRequirementId,
22
} from "../../../models/lookupConstants";
23
import { validationMessages } from "../../Form/Messages";
24
import {
25
  Job,
26
  ApplicationNormalized,
27
  Classification,
28
} from "../../../models/types";
29
import CheckboxInput from "../../Form/CheckboxInput";
30
import textToParagraphs from "../../../helpers/textToParagraphs";
31
import { getLocale, localizeField } from "../../../helpers/localize";
32
33
interface BasicInfoProps {
34
  application: ApplicationNormalized;
35
  job: Job;
36
  classification: Classification | null;
37
  handleContinue: (values: ApplicationNormalized) => Promise<void>;
38
  handleReturn: (values: ApplicationNormalized) => Promise<void>;
39
  handleQuit: (values: ApplicationNormalized) => Promise<void>;
40
}
41
42
export interface BasicInfoFormValues {
43
  citizenship: number | "";
44
  veteranStatus: number | "";
45
  languageRequirement: boolean;
46
  languageTest: boolean;
47
  educationRequirement: boolean;
48
}
49
50
export const BasicInfo: React.FunctionComponent<BasicInfoProps> = ({
51
  application,
52
  job,
53
  classification,
54
  handleContinue,
55
  handleReturn,
56
  handleQuit,
57
}) => {
58
  const intl = useIntl();
59
  const locale = getLocale(intl.locale);
60
61
  const initialValues: BasicInfoFormValues = {
62
    citizenship: application?.citizenship_declaration_id
63
      ? application.citizenship_declaration_id
64
      : "",
65
    veteranStatus: application?.veteran_status_id
66
      ? application.veteran_status_id
67
      : "",
68
    languageRequirement: application?.language_requirement_confirmed
69
      ? application.language_requirement_confirmed
70
      : false,
71
    languageTest: application?.language_test_confirmed
72
      ? application.language_test_confirmed
73
      : false,
74
    educationRequirement: application?.education_requirement_confirmed
75
      ? application.education_requirement_confirmed
76
      : false,
77
  };
78
79
  const jobEducationReq = localizeField(locale, job, "education");
80
  const defaultEducationReq = classification
81
    ? classification.education_requirements[locale]
82
    : intl.formatMessage(educationRequirementMessages.missingClassification);
83
  // If the job is using the default education requirements (for its classification) then we
84
  //  can predictably style it, by setting the right lines to bold. Otherwise, all we can do is
85
  //  split it into paragraphs.
86
  const educationRequirements =
87
    jobEducationReq === null || jobEducationReq === defaultEducationReq
88
      ? textToParagraphs(
89
          defaultEducationReq,
90
          {},
91
          {
92
            0: { "data-c-font-weight": "bold" },
93
            5: { "data-c-font-weight": "bold" },
94
          },
95
        )
96
      : textToParagraphs(jobEducationReq);
97
98
  const validationSchema = Yup.object().shape({
99
    citizenship: Yup.number()
100
      .oneOf(
101
        Object.values(CitizenshipId),
102
        intl.formatMessage(validationMessages.invalidSelection),
103
      )
104
      .required(intl.formatMessage(validationMessages.required)),
105
    veteranStatus: Yup.number()
106
      .oneOf(
107
        Object.values(VeteranId),
108
        intl.formatMessage(validationMessages.invalidSelection),
109
      )
110
      .required(intl.formatMessage(validationMessages.required)),
111
    languageRequirement: Yup.boolean()
112
      .required(intl.formatMessage(validationMessages.required))
113
      .oneOf([true], intl.formatMessage(validationMessages.required)),
114
    languageTest: Yup.boolean().notRequired(),
115
    educationRequirement: Yup.boolean()
116
      .required(intl.formatMessage(validationMessages.required))
117
      .oneOf([true], intl.formatMessage(validationMessages.required)),
118
  });
119
120
  const updateApplication = (
121
    oldApplication: ApplicationNormalized,
122
    values: BasicInfoFormValues,
123
  ): ApplicationNormalized => {
124
    const editedApplication: ApplicationNormalized = {
125
      ...oldApplication,
126
      citizenship_declaration_id: values.citizenship
127
        ? Number(values.citizenship)
128
        : null,
129
      veteran_status_id: values.veteranStatus
130
        ? Number(values.veteranStatus)
131
        : null,
132
      language_requirement_confirmed: values.languageRequirement,
133
      language_test_confirmed: values.languageTest,
134
      education_requirement_confirmed: values.educationRequirement,
135
    };
136
    return editedApplication;
137
  };
138
139
  return (
140
    <div data-c-container="medium" data-c-padding="tb(2)">
141
      <h2 data-c-heading="h2" data-c-margin="top(3) bottom(1)">
142
        {intl.formatMessage(basicInfoMessages.heading)}
143
      </h2>
144
      <Formik
145
        initialValues={initialValues}
146
        validationSchema={validationSchema}
147
        onSubmit={(values, { setSubmitting }): void => {
148
          // Save data to application object, then navigate to the next step
149
          const basicInfoFormValues: BasicInfoFormValues = {
150
            ...values,
151
          };
152
153
          handleContinue(
154
            updateApplication(application, basicInfoFormValues),
155
          ).finally(() => {
156
            setSubmitting(false);
157
          });
158
        }}
159
      >
160
        {({ isSubmitting, values }): React.ReactElement => (
161
          <Form>
162
            <div data-c-grid="gutter(all, 1)">
163
              {/* Citizenship Declaration */}
164
              <FastField
165
                id="citizenship"
166
                name="citizenship"
167
                component={SelectInput}
168
                required
169
                label={intl.formatMessage(basicInfoMessages.citizenshipLabel)}
170
                grid="base(1of1)"
171
                nullSelection={intl.formatMessage(messages.nullSelectOption)}
172
                options={Object.values(CitizenshipId).map((id: number): {
173
                  value: number;
174
                  label: string;
175
                } => ({
176
                  value: id,
177
                  label: intl.formatMessage(citizenshipDeclaration(id)),
178
                }))}
179
              />
180
              {/* Veteran Status */}
181
              <FastField
182
                id="veteranStatus"
183
                name="veteranStatus"
184
                component={SelectInput}
185
                required
186
                label={intl.formatMessage(basicInfoMessages.veteranStatusLabel)}
187
                grid="base(1of1)"
188
                nullSelection={intl.formatMessage(messages.nullSelectOption)}
189
                options={Object.values(VeteranId).map((id: number): {
190
                  value: number;
191
                  label: string;
192
                } => ({
193
                  value: id,
194
                  label: intl.formatMessage(veteranStatus(id)),
195
                }))}
196
              />
197
            </div>
198
            {/* Language Requirement */}
199
            <h3
200
              data-c-heading="h3"
201
              data-c-margin="top(2) bottom(1)"
202
              data-c-padding="bottom(1)"
203
            >
204
              {intl.formatMessage(
205
                basicInfoMessages.languageRequirementsHeading,
206
              )}
207
            </h3>
208
            <p>
209
              {job.language_requirement_id &&
210
                intl.formatMessage(
211
                  languageRequirementDescription(job.language_requirement_id),
212
                )}
213
            </p>
214
            <div data-c-margin="left(2)">
215
              <FastField
216
                id="languageRequirement"
217
                name="languageRequirement"
218
                component={CheckboxInput}
219
                required
220
                label={
221
                  job.language_requirement_id &&
222
                  intl.formatMessage(
223
                    languageRequirementLabel(job.language_requirement_id),
224
                  )
225
                }
226
              />
227
            </div>
228
            {/* Language Test (only displayed for bilingual language requirements) */}
229
            {((job.language_requirement_id &&
230
              job.language_requirement_id ===
231
                LanguageRequirementId.bilingualAdvanced) ||
232
              job.language_requirement_id ===
233
                LanguageRequirementId.bilingualIntermediate) && (
234
              <div data-c-margin="left(2)">
235
                <FastField
236
                  id="languageTest"
237
                  name="languageTest"
238
                  component={CheckboxInput}
239
                  required
240
                  label={intl.formatMessage(messages.languageTestLabel)}
241
                />
242
              </div>
243
            )}
244
            {/* Education Requirement */}
245
            <h3
246
              data-c-heading="h3"
247
              data-c-margin="top(2) bottom(1)"
248
              data-c-padding="bottom(1)"
249
            >
250
              {intl.formatMessage(messages.educationRequirementHeader)}
251
            </h3>
252
            <p data-c-margin="bottom(1)">
253
              {intl.formatMessage(messages.meetEducationRequirement)}
254
            </p>
255
            <div
256
              data-c-background="gray(20)"
257
              data-c-radius="rounded"
258
              data-c-padding="all(1)"
259
              data-c-margin="bottom(1)"
260
            >
261
              {educationRequirements}
262
            </div>
263
            <div data-c-margin="left(2)">
264
              <FastField
265
                id="educationRequirement"
266
                name="educationRequirement"
267
                component={CheckboxInput}
268
                required
269
                label={intl.formatMessage(messages.educationRequirementLabel)}
270
              />
271
            </div>
272
            <hr data-c-hr="thin(c1)" data-c-margin="bottom(2)" />
273
            <div data-c-grid="gutter">
274
              <div
275
                data-c-alignment="base(centre) tp(left)"
276
                data-c-grid-item="tp(1of2)"
277
              >
278
                <button
279
                  data-c-button="outline(c2)"
280
                  data-c-radius="rounded"
281
                  type="button"
282
                  disabled={isSubmitting}
283
                  onClick={(): void => {
284
                    const basicInfoFormValues: BasicInfoFormValues = {
285
                      ...values,
286
                    };
287
                    // Method should save the current data and return user to the previous step
288
                    handleReturn(
289
                      updateApplication(application, basicInfoFormValues),
290
                    );
291
                  }}
292
                >
293
                  {intl.formatMessage(navigationMessages.return)}
294
                </button>
295
              </div>
296
              <div
297
                data-c-alignment="base(centre) tp(right)"
298
                data-c-grid-item="tp(1of2)"
299
              >
300
                <button
301
                  data-c-button="outline(c2)"
302
                  data-c-radius="rounded"
303
                  type="button"
304
                  disabled={isSubmitting}
305
                  onClick={(): void => {
306
                    const basicInfoFormValues: BasicInfoFormValues = {
307
                      ...values,
308
                    };
309
                    // Method should save the current data and return user to My Applications page
310
                    handleQuit(
311
                      updateApplication(application, basicInfoFormValues),
312
                    );
313
                  }}
314
                >
315
                  {intl.formatMessage(navigationMessages.quit)}
316
                </button>
317
                <button
318
                  data-c-button="solid(c1)"
319
                  data-c-radius="rounded"
320
                  data-c-margin="left(1)"
321
                  type="submit"
322
                  disabled={isSubmitting}
323
                >
324
                  {intl.formatMessage(navigationMessages.continue)}
325
                </button>
326
              </div>
327
            </div>
328
          </Form>
329
        )}
330
      </Formik>
331
    </div>
332
  );
333
};
334
335
export default BasicInfo;
336