Passed
Push — task/application-profile-react... ( 0b591d...202f17 )
by
unknown
06:19
created

  C

Complexity

Conditions 9

Size

Total Lines 16
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 16
rs 6.6666
c 0
b 0
f 0
cc 9
1
import React, { FunctionComponent, ChangeEvent, useState } from "react";
2
import { FormattedMessage, useIntl } from "react-intl";
3
import {
4
  GocClassification,
5
  ProfileBasicInformation as ProfileBasicInformationProp,
6
  GCEmployeeStatusName
7
} from "../../models/types";
8
import { myBasicInformationMessages } from "../Application/applicationMessages";
9
import { removeDuplicatesById } from "../../helpers/queries";
10
import SelectInput from "../Form/SelectInput";
11
import { FastField, Formik, Field, Form } from "formik";
12
import messages, {
13
  citizenshipDeclaration,
14
  veteranStatus,
15
  gcEmployeeStatus,
16
} from "../Application/BasicInfo/basicInfoMessages";
17
import { basicInfoMessages } from "../Application/applicationMessages";
18
import {
19
  CitizenshipId,
20
  VeteranId,
21
  currentGcEmployeeId,
22
} from "../../models/lookupConstants";
23
24
export interface ProfileBasicInformationProps {
25
  gocClassifications: GocClassification[];
26
  basicInformation: ProfileBasicInformationProp;
27
  name: string;
28
  email: string;
29
}
30
31
export interface ClassificationDropdownsProps {
32
  gocClassifications: GocClassification[];
33
  selectedItem?: GocClassification;
34
}
35
36
export interface GcExperienceProps {
37
  gocClassifications: GocClassification[];
38
  previousExperienceProp: GocClassification[];
39
  currentGcClassification: GocClassification;
40
  wasGcEmployee: GCEmployeeStatusName;
41
}
42
43
44
45
const GcExperience: FunctionComponent<GcExperienceProps> = ({
46
  previousExperienceProp,
47
  currentGcClassification,
48
  gocClassifications,
49
  wasGcEmployee,
50
}) => {
51
  const intl = useIntl();
52
53
  const [previousExperience, setPreviousExperience] = useState<
54
    GocClassification[]
55
  >(previousExperienceProp);
56
57
  const removeExperience = function (gocClassification: GocClassification) {
58
    setPreviousExperience(
59
      previousExperience.filter((experience) => {
60
        return !(
61
          experience.classification.key ==
62
            gocClassification.classification.key &&
63
          experience.level == gocClassification.level
64
        );
65
      }),
66
    );
67
  };
68
69
  const createPreviousExperienceDropdowns = (
70
    previousGcExperience: GocClassification[],
71
  ) => {
72
    return previousGcExperience.map((experience) => {
73
      return (
74
        <>
75
          <li>
76
            <div data-c-grid="gutter top">
77
              <ClassificationDropdowns
78
                gocClassifications={gocClassifications}
79
                selectedItem={experience}
80
                data-c-grid-item="base(2of2) tl(2of3)"
81
              />
82
              <div data-c-grid-item="base(1of1) tl(1of3)" data-c-input="select">
83
                <button
84
                  data-c-button="solid(c1)"
85
                  onClick={() => removeExperience(experience)}
86
                >
87
                  <i className="fa fa-trash" /> Remove
88
                </button>
89
                <span>
90
                  {intl.formatMessage(myBasicInformationMessages.inputError)}
91
                </span>
92
              </div>
93
            </div>
94
          </li>
95
        </>
96
      );
97
    });
98
  };
99
100
  const addExperience = function () {
101
    // If the user has not populated their most recent experience, prevent adding a second empty row
102
    if (
103
      previousExperience.filter((experience) => {
104
        return experience.classification.id == 0;
105
      }).length > 0
106
    ) {
107
      return;
108
    }
109
110
    setPreviousExperience([
111
      ...previousExperience,
112
      {
113
        classification: {
114
          id: 0,
115
          key: "",
116
          name: {
117
            en: "",
118
            fr: "",
119
          },
120
        },
121
        level: 0,
122
        order: 0,
123
      },
124
    ]);
125
  };
126
127
  if (wasGcEmployee == "no") return <></>;
128
129
  if (wasGcEmployee == "previous") {
130
    return (
131
      <>
132
        <label htmlFor="SEL2">
133
          {intl.formatMessage(myBasicInformationMessages.addPreviousGcExperience)}
134
        </label>
135
        <div id="list-previous-gov-class">
136
          <ol>{createPreviousExperienceDropdowns(previousExperience)}</ol>
137
        </div>
138
        <button data-c-button="solid(c1)" onClick={addExperience}>
139
          {intl.formatMessage(myBasicInformationMessages.addClassification)}
140
        </button>
141
      </>
142
    )
143
  }
144
145
  // if wasGcEmployee == "yes" (the only remainig option)
146
  else {
147
    return (
148
      <>
149
        <label htmlFor="SEL2">
150
          {intl.formatMessage(
151
            myBasicInformationMessages.currentClassificationAndLevel,
152
          )}
153
        </label>
154
        <ClassificationDropdowns
155
          selectedItem={currentGcClassification}
156
          gocClassifications={gocClassifications}
157
        />
158
159
        <label htmlFor="SEL2">
160
          {intl.formatMessage(myBasicInformationMessages.addPreviousGcExperience)}
161
        </label>
162
        <div id="list-previous-gov-class">
163
          <ol>{createPreviousExperienceDropdowns(previousExperience)}</ol>
164
        </div>
165
        <button data-c-button="solid(c1)" onClick={addExperience}>
166
          {intl.formatMessage(myBasicInformationMessages.addClassification)}
167
        </button>
168
      </>
169
    );
170
  }
171
172
};
173
174
const ClassificationDropdowns: FunctionComponent<ClassificationDropdownsProps> = ({
175
  gocClassifications,
176
  selectedItem,
177
}) => {
178
  const intl = useIntl();
179
180
  const safeParseInt = function (str: string | null): number {
181
    if (str == null) return 0;
182
    else if (typeof str == "string") return parseInt(str);
183
    else return -1;
184
  };
185
186
  const getInitialSelectedClassification = (): string | null => {
187
    if (selectedItem?.classification.id) {
188
      return selectedItem?.classification.id.toString();
189
    }
190
    return null;
191
  };
192
193
  const [selectedClassification, setSelectedClassification] = useState<
194
    string | null
195
  >(getInitialSelectedClassification());
196
197
  const handleSelectedClassification = function (
198
    e: ChangeEvent<HTMLSelectElement>,
199
  ) {
200
    setSelectedClassification(e.target.value);
201
  };
202
203
  const uniqueClassifications = removeDuplicatesById(
204
    gocClassifications.map((item) => ({
205
      id: item.classification.id,
206
      key: item.classification.key,
207
    })),
208
  );
209
210
  function getLevelsOfClassification(
211
    classificationKey: number | null,
212
  ): string[] {
213
    const correspondingGocClassifications = gocClassifications.filter(
214
      (item) => item.classification.id == classificationKey,
215
    );
216
217
    let correspondingLevels: string[] = [];
218
    correspondingGocClassifications.forEach(function (
219
      correspondingGocClassification: GocClassification,
220
    ) {
221
      correspondingLevels.push(correspondingGocClassification.level.toString());
222
    });
223
224
    return correspondingLevels;
225
  }
226
227
  return (
228
    <>
229
      <div data-c-grid-item="base(1of3)" data-c-grid="gutter top">
230
        <div data-c-grid-item="base(1of1) tl(1of2)" data-c-input="select">
231
          <div>
232
            <i className="fas fa-caret-down" />
233
            <select
234
              defaultValue={selectedItem?.classification.id}
235
              required
236
              id="SEL2"
237
              onChange={handleSelectedClassification}
238
            >
239
              <option></option>
240
              {uniqueClassifications.map((item) => (
241
                <option key={item.id} value={item.id}>
242
                  {item.key}
243
                </option>
244
              ))}
245
              ;
246
            </select>
247
          </div>
248
          <span>
249
            {intl.formatMessage(myBasicInformationMessages.inputError)}
250
          </span>
251
        </div>
252
        <div data-c-grid-item="base(1of1) tl(1of2)" data-c-input="select">
253
          <div>
254
            <i className="fas fa-caret-down" />
255
            <select defaultValue={selectedItem?.level} required id="SEL2">
256
              <option></option>
257
              {getLevelsOfClassification(
258
                safeParseInt(selectedClassification),
259
              ).map((item) => (
260
                <option key={item} value={item}>
261
                  {item}
262
                </option>
263
              ))}
264
            </select>
265
          </div>
266
          <span>
267
            {intl.formatMessage(myBasicInformationMessages.inputError)}
268
          </span>
269
        </div>
270
      </div>
271
    </>
272
  );
273
};
274
275
export const ProfileBasicInformation: React.FC<ProfileBasicInformationProps> = ({
276
  gocClassifications,
277
  basicInformation,
278
  name,
279
  email,
280
}) => {
281
  const intl = useIntl();
282
283
  const getInitialEmployeeState = (): GCEmployeeStatusName => {
284
    if (basicInformation.current_classification && basicInformation.previous_classifications.length > 0) {
285
      return "yes";
286
    }
287
    else if (basicInformation.current_classification && basicInformation.previous_classifications.length == 0) {
288
      return "previous";
289
    }
290
    else {
291
      return "no";
292
    }
293
  };
294
295
  const [currentGcEmployee, setCurrentGcEmployee] = useState<GCEmployeeStatusName>(
296
    getInitialEmployeeState(),
297
  );
298
299
  const onChangeSetCurrentGCEmployee = (e : React.ChangeEvent<HTMLSelectElement>, field : any) => {
300
    if (field.value == 1) setCurrentGcEmployee("yes")
301
    if (field.value == 2) setCurrentGcEmployee("no")
302
    if (field.value == 3) setCurrentGcEmployee("previous")
303
  }
304
305
  let initialValues = {
306
    citizenship: basicInformation.citizenship_status.id,
307
    veteranStatus: basicInformation.citizenship_status.id,
308
    currentGcEmployeeStatus: basicInformation.current_gc_employee.id,
309
  };
310
311
  return (
312
    <>
313
      <Formik
314
        initialValues={initialValues}
315
        onSubmit={(values): void => {
316
          const basicInformationValues = {
317
            ...values,
318
          };
319
        }}
320
      >
321
        {({ errors, values, touched, setValues }) => (
322
          <Form>
323
            <h2 data-c-heading="h2" data-c-margin="bottom(1)">
324
              {intl.formatMessage(myBasicInformationMessages.heading)}
325
            </h2>
326
            <p data-c-margin="bottom(1)">
327
              <FormattedMessage
328
                id="profile.experience.preamble"
329
                defaultMessage="This profile is also shared when you submit a job application."
330
                description="First section of text on the 'My Basic Information' of the Application Timeline."
331
              />
332
            </p>
333
            <div>
334
              <p>
335
                {intl.formatMessage(myBasicInformationMessages.name)}:{" "}
336
                <b data-c-color="c1"> {name} </b>{" "}
337
              </p>
338
              <p>
339
                {intl.formatMessage(myBasicInformationMessages.personalEmail)}:{" "}
340
                <b data-c-color="c1"> {email} </b>{" "}
341
              </p>
342
              <p>
343
                {intl.formatMessage(myBasicInformationMessages.toChangeGoTo)}:{" "}
344
                <a data-c-color="c1" href="#">
345
                  {intl.formatMessage(
346
                    myBasicInformationMessages.accountSettings,
347
                  )}
348
                </a>
349
              </p>
350
            </div>
351
            <div data-c-grid-item="base(1of3)">
352
              <FastField
353
                id="citizenship"
354
                name="citizenship"
355
                component={SelectInput}
356
                required
357
                label={intl.formatMessage(basicInfoMessages.citizenshipLabel)}
358
                grid="base(1of1)"
359
                nullSelection={intl.formatMessage(messages.nullSelectOption)}
360
                options={Object.values(CitizenshipId).map((id: number): {
361
                  value: number;
362
                  label: string;
363
                } => ({
364
                  value: id,
365
                  label: intl.formatMessage(citizenshipDeclaration(id)),
366
                }))}
367
              />
368
            </div>
369
            <div data-c-grid-item="base(1of3)">
370
              <FastField
371
                id="veteranStatus"
372
                name="veteranStatus"
373
                component={SelectInput}
374
                required
375
                label={intl.formatMessage(basicInfoMessages.veteranStatusLabel)}
376
                grid="base(1of1)"
377
                nullSelection={intl.formatMessage(messages.nullSelectOption)}
378
                options={Object.values(VeteranId).map((id: number): {
379
                  value: number;
380
                  label: string;
381
                } => ({
382
                  value: id,
383
                  label: intl.formatMessage(veteranStatus(id)),
384
                }))}
385
              />
386
            </div>
387
            <h2 data-c-heading="h2" data-c-margin="bottom(1)">
388
              {intl.formatMessage(myBasicInformationMessages.heading)}
389
            </h2>
390
391
            <FastField required name="currentGcEmployeeStatus" placeholder="F">
392
              {({ field }) => (
393
                <div data-c-grid-item="base(1of3)">
394
                  <div data-c-input="select" data-c-grid-item="base(1of1)" data-c-required="true" >
395
                    <label htmlFor="currentGcEmployeeStatus">
396
                      {intl.formatMessage(
397
                        myBasicInformationMessages.isGCEmployee,
398
                      )}
399
                    </label>
400
                    <div>
401
                      <i className="fa fa-caret-down" />
402
                      <select {...field}
403
                        onChange={e => onChangeSetCurrentGCEmployee(e, field)}
404
                        id="currentGcEmployeeStatus"
405
                      >
406
                        <option value=""></option>
407
                        {Object.values(currentGcEmployeeId).map(i =>
408
                          <option key={i} value={i}>{intl.formatMessage(gcEmployeeStatus(i))}</option>
409
                        )}
410
                      </select>
411
                    </div>
412
                  </div>
413
                </div>
414
              )}
415
            </FastField>
416
417
            <GcExperience
418
              gocClassifications={gocClassifications}
419
              previousExperienceProp={basicInformation.previous_classifications}
420
              currentGcClassification={basicInformation.current_classification}
421
              wasGcEmployee={currentGcEmployee}
422
            />
423
          </Form>
424
        )}
425
      </Formik>
426
    </>
427
  );
428
};
429
430
export default ProfileBasicInformation;
431