Passed
Push — dev ( 0b1244...c6594a )
by
unknown
06:00
created

resources/assets/js/components/JobBuilder/Intro/JobIntro.tsx   A

Complexity

Total Complexity 7
Complexity/F 0

Size

Lines of Code 499
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 7
eloc 386
mnd 7
bc 7
fnc 0
dl 0
loc 499
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import React, { useState } from "react";
2
import { FormattedMessage, defineMessages, useIntl } from "react-intl";
3
import { Form, Formik, FastField } from "formik";
4
import * as Yup from "yup";
5
import nprogress from "nprogress";
6
import get from "lodash/get";
7
import { validationMessages } from "../../Form/Messages";
8
import { Job, Department, Manager, User } from "../../../models/types";
9
import { emptyJob } from "../../../models/jobUtil";
10
import TextInput from "../../Form/TextInput";
11
import { accountSettings } from "../../../helpers/routes";
12
import { localizeFieldNonNull, getLocale } from "../../../helpers/localize";
13
14
const pageMessages = defineMessages({
15
  explanationBoldText: {
16
    id: "jobBuilder.intro.explanation.boldText",
17
    defaultMessage: "confirm that your personal information below is correct.",
18
    description: "Bold text portion of the JPB Intro explanation.",
19
  },
20
  emailLinkTitle: {
21
    id: "jobBuilder.intro.emailLinkTitle",
22
    defaultMessage: "Email Talent Cloud",
23
    description: "Title of the Talent Cloud email link.",
24
  },
25
  emailLinkText: {
26
    id: "jobBuilder.intro.emailLinkText",
27
    defaultMessage: "Talent Cloud",
28
    description: "Visible text of the Talent Cloud email link.",
29
  },
30
});
31
32
const formMessages = defineMessages({
33
  jobTitleLabelEN: {
34
    id: "jobBuilder.intro.jobTitleLabelEN",
35
    defaultMessage: "{name}'s Position (English)",
36
    description: "The label displayed on the manager position input.",
37
  },
38
  jobTitlePlaceholderEN: {
39
    id: "jobBuilder.intro.jobTitlePlaceholderEN",
40
    defaultMessage: "e.g. Design Manager",
41
    description: "The placeholder displayed on the Manager Position input.",
42
  },
43
  jobTitleLabelFR: {
44
    id: "jobBuilder.intro.jobTitleLabelFR",
45
    defaultMessage: "{name}'s Position (Français)",
46
    description: "The label displayed on the Manager Position input.",
47
  },
48
  jobTitlePlaceholderFR: {
49
    id: "jobBuilder.intro.jobTitlePlaceholderFR",
50
    defaultMessage: "e.g. Gestionnaire de la conception",
51
    description: "The placeholder displayed on the Manager Position input.",
52
  },
53
  departmentNullSelection: {
54
    id: "jobBuilder.intro.departmentNullSelection",
55
    defaultMessage: "Select a department...",
56
    description:
57
      "The default selection option displayed on the department select box.",
58
  },
59
  accountSettingsLinkTitle: {
60
    id: "jobBuilder.intro.accountSettingsLinkTitle",
61
    defaultMessage: "Visit Account Settings page.",
62
    description: "Title of the account settings page link.",
63
  },
64
  accountSettingsLinkText: {
65
    id: "jobBuilder.intro.accountSettingsLinkText",
66
    defaultMessage: "account settings",
67
    description: "Visible text of the account settings link.",
68
  },
69
  divisionLabelEN: {
70
    id: "jobBuilder.intro.divisionLabelEN",
71
    defaultMessage: "{name}'s Division (English)",
72
    description: "The label displayed on the division input.",
73
  },
74
  divisionPlaceholderEN: {
75
    id: "jobBuilder.intro.divisionPlaceholderEN",
76
    defaultMessage: "e.g. Digital Change",
77
    description: "The placeholder displayed on the division input.",
78
  },
79
  divisionLabelFR: {
80
    id: "jobBuilder.intro.divisionLabelFR",
81
    defaultMessage: "{name}'s Division (Français)",
82
    description: "The label displayed on the division input.",
83
  },
84
  divisionPlaceholderFR: {
85
    id: "jobBuilder.intro.divisionPlaceholderFR",
86
    defaultMessage: "e.g. Changement numérique",
87
    description: "The placeholder displayed on the division input.",
88
  },
89
});
90
91
// shape of values used in Form
92
interface JobIntroValues {
93
  managerPositionEn: string;
94
  managerPositionFr: string;
95
  department: number | "";
96
  divisionEN: string;
97
  divisionFR: string;
98
}
99
100
interface JobIntroProps {
101
  // If not null, used to prepopulate form values
102
  job: Job | null;
103
  // The manager of this job. Used to prepopulate values.
104
  manager: Manager;
105
  // List of known department options.
106
  departments: Department[];
107
  // The user related to the manager of this job.
108
  user: User;
109
  // Runs after successful validation.
110
  // It must (asynchronously) return the resulting job, if successful.
111
  handleSubmit: (job: Job, manager: Manager) => Promise<Job>;
112
  // Continues to next step in JobBuilder.
113
  handleContinue: (chosenLang: "en" | "fr", job: Job) => void;
114
  // // Continues the JobBuilder in French.
115
  // handleContinueFr: (job: Job) => void;
116
}
117
118
const initializeValues = (
119
  job: Job | null,
120
  manager: Manager,
121
  user: User,
122
): JobIntroValues => {
123
  let department: number | "" = "";
124
  if (user.department_id !== null) {
125
    department = user.department_id;
126
  }
127
128
  const managerDivision = {
129
    en: get(manager, "division.en", ""),
130
    fr: get(manager, "division.fr", ""),
131
  };
132
133
  let divisionEN = "";
134
  if (job !== null && job.division.en) {
135
    divisionEN = job.division.en;
136
  } else if (managerDivision.en) {
137
    divisionEN = managerDivision.en;
138
  }
139
140
  let divisionFR = "";
141
  if (job !== null && job.division.fr) {
142
    divisionFR = job.division.fr;
143
  } else if (managerDivision.fr) {
144
    divisionFR = managerDivision.fr;
145
  }
146
147
  return {
148
    managerPositionEn: get(manager, "position.en", ""),
149
    managerPositionFr: get(manager, "position.fr", ""),
150
    department,
151
    divisionEN,
152
    divisionFR,
153
  };
154
};
155
156
const updateJobWithValues = (
157
  job: Job,
158
  values: JobIntroValues,
159
  locale: string,
160
  departmentId: number | null,
161
): Job => ({
162
  ...job,
163
  chosen_lang: locale,
164
  department_id: departmentId,
165
  division: {
166
    ...job.division,
167
    en: values.divisionEN || null,
168
    fr: values.divisionFR || null,
169
  },
170
});
171
172
const updateManagerWithValues = (
173
  manager: Manager,
174
  values: JobIntroValues,
175
): Manager => ({
176
  ...manager,
177
  division: {
178
    ...manager.division,
179
    en: values.divisionEN || null,
180
    fr: values.divisionFR || null,
181
  },
182
  position: {
183
    ...manager.position,
184
    en: values.managerPositionEn || null,
185
    fr: values.managerPositionFr || null,
186
  },
187
});
188
189
const JobIntro: React.FunctionComponent<JobIntroProps> = ({
190
  job,
191
  manager,
192
  user,
193
  departments,
194
  handleSubmit,
195
  handleContinue,
196
}: JobIntroProps): React.ReactElement => {
197
  const intl = useIntl();
198
  const locale = getLocale(intl.locale);
199
  const initialValues: JobIntroValues = initializeValues(job, manager, user);
200
  const [languageSelection, setLanguageSelection] = useState(locale);
201
202
  const getDepartmentName = (): string | undefined => {
203
    const departmentName = departments.find(
204
      (department) => department.id === user.department_id,
205
    );
206
    return departmentName
207
      ? localizeFieldNonNull(locale, departmentName, "name")
208
      : undefined;
209
  };
210
211
  const introSchema = Yup.object().shape({
212
    managerPositionEn: Yup.string().required(
213
      intl.formatMessage(validationMessages.required),
214
    ),
215
    managerPositionFr: Yup.string().required(
216
      intl.formatMessage(validationMessages.required),
217
    ),
218
    divisionEN: Yup.string().required(
219
      intl.formatMessage(validationMessages.required),
220
    ),
221
    divisionFR: Yup.string().required(
222
      intl.formatMessage(validationMessages.required),
223
    ),
224
  });
225
226
  return (
227
    <>
228
      <div data-c-container="form" data-c-padding="top(triple) bottom(triple)">
229
        <h3
230
          data-c-font-size="h3"
231
          data-c-font-weight="bold"
232
          data-c-margin="bottom(normal)"
233
        >
234
          <FormattedMessage
235
            id="jobBuilder.intro.welcome"
236
            defaultMessage="Welcome to the Job Poster Builder"
237
            description="Header of Job Poster Builder Intro Step"
238
          />
239
        </h3>
240
        <p data-c-margin="bottom(double)">
241
          <FormattedMessage
242
            id="jobBuilder.intro.explanation"
243
            defaultMessage="This tool will help you create a job poster that attracts the right talent. Before we get started on your job poster, take some time to {boldText}"
244
            description="Explanation of Job Poster Builder Intro Step"
245
            values={{
246
              boldText: (
247
                <span data-c-font-weight="bold">
248
                  {intl.formatMessage(pageMessages.explanationBoldText)}
249
                </span>
250
              ),
251
            }}
252
          />
253
        </p>
254
        <Formik
255
          enableReinitialize
256
          initialValues={initialValues}
257
          validationSchema={introSchema}
258
          onSubmit={(values, { setSubmitting }): void => {
259
            const updatedJob = updateJobWithValues(
260
              job || emptyJob(),
261
              values,
262
              languageSelection,
263
              user.department_id,
264
            );
265
            const updatedManager = updateManagerWithValues(manager, values);
266
            nprogress.start();
267
            handleSubmit(updatedJob, updatedManager)
268
              .then((newJob: Job): void => {
269
                if (languageSelection === "fr") {
270
                  handleContinue("fr", newJob);
271
                } else {
272
                  handleContinue("en", newJob);
273
                }
274
              })
275
              .finally((): void => {
276
                nprogress.done();
277
                setSubmitting(false); // Required by Formik to finish the submission cycle
278
              });
279
          }}
280
        >
281
          {({ isSubmitting, submitForm }): React.ReactElement => (
282
            <>
283
              <Form id="form" data-c-margin="bottom(normal)">
284
                <div data-c-grid="gutter">
285
                  <h4 data-c-font-size="h4" data-c-grid-item="base(1of1)">
286
                    <FormattedMessage
287
                      id="jobBuilder.intro.formTitle"
288
                      defaultMessage="{name}'s Profile Information"
289
                      description="The title of the profile information form."
290
                      values={{
291
                        name: manager.first_name,
292
                      }}
293
                    />
294
                  </h4>
295
                  <p data-c-grid-item="base(1of1)">
296
                    <FormattedMessage
297
                      id="jobBuilder.intro.formDescription"
298
                      defaultMessage="This information is used on the Job Poster to help applicants learn more about who they'll be working with."
299
                      description="Explanation of why the profile information is collected."
300
                    />
301
                  </p>
302
                  <FastField
303
                    type="text"
304
                    id="managerPositionEn"
305
                    name="managerPositionEn"
306
                    label={intl.formatMessage(formMessages.jobTitleLabelEN, {
307
                      name: manager.first_name,
308
                    })}
309
                    placeholder={intl.formatMessage(
310
                      formMessages.jobTitlePlaceholderEN,
311
                    )}
312
                    required
313
                    grid="tl(1of2)"
314
                    component={TextInput}
315
                  />
316
                  <FastField
317
                    type="text"
318
                    id="managerPositionFr"
319
                    name="managerPositionFr"
320
                    label={intl.formatMessage(formMessages.jobTitleLabelFR, {
321
                      name: manager.first_name,
322
                    })}
323
                    placeholder={intl.formatMessage(
324
                      formMessages.jobTitlePlaceholderFR,
325
                    )}
326
                    required
327
                    grid="tl(1of2)"
328
                    component={TextInput}
329
                  />
330
                  <h4 data-c-font-size="h4" data-c-grid-item="base(1of1)">
331
                    <FormattedMessage
332
                      id="jobBuilder.intro.departmentHeader"
333
                      defaultMessage="{name}'s Department Information"
334
                      description="The label displayed on the department select box."
335
                      values={{ name: manager.first_name }}
336
                    />
337
                  </h4>
338
                  <p data-c-grid-item="base(1of1)">
339
                    <FormattedMessage
340
                      id="jobBuilder.intro.departmentLabel"
341
                      defaultMessage="Department"
342
                      description="The label displayed on the department select box."
343
                    />
344
                    :{" "}
345
                    {getDepartmentName && (
346
                      <span id="department" data-c-font-weight="bold">
347
                        {getDepartmentName()}
348
                      </span>
349
                    )}
350
                    <span
351
                      data-c-margin="top(quarter)"
352
                      data-c-font-size="small"
353
                      style={{ display: "block" }}
354
                    >
355
                      <FormattedMessage
356
                        id="jobBuilder.intro.changeDepartment"
357
                        defaultMessage="To change your department, please contact {email}. To learn more visit your {accountSettings}."
358
                        values={{
359
                          email: (
360
                            <a
361
                              href="mailto:[email protected]"
362
                              title={intl.formatMessage(
363
                                pageMessages.emailLinkTitle,
364
                              )}
365
                            >
366
                              {intl.formatMessage(pageMessages.emailLinkText)}
367
                            </a>
368
                          ),
369
                          accountSettings: (
370
                            <a
371
                              href={accountSettings(locale)}
372
                              title={intl.formatMessage(
373
                                formMessages.accountSettingsLinkTitle,
374
                              )}
375
                              target="_blank"
376
                              rel="noopener noreferrer"
377
                            >
378
                              {intl.formatMessage(
379
                                formMessages.accountSettingsLinkText,
380
                              )}
381
                            </a>
382
                          ),
383
                        }}
384
                      />
385
                    </span>
386
                  </p>
387
                  <FastField
388
                    type="text"
389
                    id="divisionEN"
390
                    name="divisionEN"
391
                    label={intl.formatMessage(formMessages.divisionLabelEN, {
392
                      name: manager.first_name,
393
                    })}
394
                    placeholder={intl.formatMessage(
395
                      formMessages.divisionPlaceholderEN,
396
                    )}
397
                    required
398
                    grid="tl(1of2)"
399
                    component={TextInput}
400
                  />
401
                  <FastField
402
                    type="text"
403
                    id="divisionFR"
404
                    name="divisionFR"
405
                    label={intl.formatMessage(formMessages.divisionLabelFR, {
406
                      name: manager.first_name,
407
                    })}
408
                    placeholder={intl.formatMessage(
409
                      formMessages.divisionPlaceholderFR,
410
                    )}
411
                    required
412
                    grid="tl(1of2)"
413
                    component={TextInput}
414
                  />
415
                </div>
416
              </Form>
417
              <p data-c-margin="bottom(double)">
418
                <FormattedMessage
419
                  id="jobBuilder.intro.completeInLanguage"
420
                  defaultMessage="Complete the job poster in the language of your choice. We will handle translation."
421
                  description="Instructions at bottom of form on language choice for job poster builder."
422
                />{" "}
423
                <FormattedMessage
424
                  id="jobBuilder.intro.contactUs"
425
                  defaultMessage="We’ve also provided instructions and examples to help guide you through the process but if you still have questions, contact {link}."
426
                  description="Subtitle 2 of Job Poster Builder Intro Step"
427
                  values={{
428
                    link: (
429
                      <a
430
                        href="mailto:[email protected]"
431
                        title={intl.formatMessage(pageMessages.emailLinkTitle)}
432
                      >
433
                        {intl.formatMessage(pageMessages.emailLinkText)}
434
                      </a>
435
                    ),
436
                  }}
437
                />
438
              </p>
439
              {/* This button continues the user in English. */}
440
              <button
441
                form="form"
442
                data-c-button="solid(c1)"
443
                data-c-radius="rounded"
444
                data-c-margin="right(normal) bottom(normal)"
445
                type="button"
446
                disabled={isSubmitting}
447
                onClick={(): void => {
448
                  /** FIXME:
449
                   * This is a race condition, since setLanguageSelection is asynchronous.
450
                   * I have to find a way to handle 2 submit buttons in formik without a race condition somewhere :(
451
                   * For now, the setState always happens faster than the validation check, so it works.
452
                   * See https://github.com/jaredpalmer/formik/issues/214
453
                   * -- Tristan
454
                   */
455
                  setLanguageSelection("en");
456
                  submitForm();
457
                }}
458
              >
459
                <FormattedMessage
460
                  id="jobBuilder.intro.continueButtonLabelEN"
461
                  defaultMessage="Continue in English"
462
                  description="Label displayed on submit button for continuation of job builder in english."
463
                />
464
              </button>
465
              {/* This button switches the user to French and continues the process in French.  */}
466
              <button
467
                form="form"
468
                data-c-button="solid(c1)"
469
                data-c-radius="rounded"
470
                type="button"
471
                disabled={isSubmitting}
472
                onClick={(): void => {
473
                  /** FIXME:
474
                   * This is a race condition, since setLanguageSelection is asynchronous.
475
                   * I have to find a way to handle 2 submit buttons in formik without a race condition somewhere :(
476
                   * For now, the setState always happens faster than the validation check, so it works.
477
                   * See https://github.com/jaredpalmer/formik/issues/214
478
                   * -- Tristan
479
                   */
480
                  setLanguageSelection("fr");
481
                  submitForm();
482
                }}
483
              >
484
                <FormattedMessage
485
                  id="jobBuilder.intro.continueButtonLabelFR"
486
                  defaultMessage="Continuer en Français"
487
                  description="Label displayed on submit button for continuation of job builder in french."
488
                />
489
              </button>
490
            </>
491
          )}
492
        </Formik>
493
      </div>
494
    </>
495
  );
496
};
497
498
export default JobIntro;
499