Passed
Push — dev ( 1db7a6...e8a28c )
by
unknown
05:02
created

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

Complexity

Total Complexity 14
Complexity/F 0

Size

Lines of Code 338
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

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