Passed
Push — feature/add-2fa-support ( 23e818...85b1f3 )
by Chris
24:19 queued 11:16
created

resources/assets/js/components/JobBuilder/Review/JobReview.tsx   A

Complexity

Total Complexity 22
Complexity/F 0

Size

Lines of Code 862
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 710
dl 0
loc 862
rs 9.89
c 0
b 0
f 0
wmc 22
mnd 22
bc 22
fnc 0
bpm 0
cpm 0
noi 0
1
import React, { useState, useRef } from "react";
2
import {
3
  injectIntl,
4
  WrappedComponentProps,
5
  FormattedMessage,
6
  defineMessages,
7
} from "react-intl";
8
import {
9
  JobPosterKeyTask,
10
  Criteria,
11
  Job,
12
  Skill,
13
  Department,
14
  Manager,
15
} from "../../../models/types";
16
import {
17
  jobBuilderDetails,
18
  jobBuilderTasks,
19
  jobBuilderImpact,
20
  jobBuilderSkills,
21
  managerEditProfile,
22
  jobBuilderEnv,
23
} from "../../../helpers/routes";
24
import {
25
  find,
26
  mapToObject,
27
  hasKey,
28
  getId,
29
  notEmpty,
30
} from "../../../helpers/queries";
31
import {
32
  provinceName,
33
  securityClearance,
34
  languageRequirement,
35
  languageRequirementDescription,
36
  languageRequirementContext,
37
} from "../../../models/localizedConstants";
38
import {
39
  CriteriaTypeId,
40
  LanguageRequirementId,
41
} from "../../../models/lookupConstants";
42
import Criterion from "../Criterion";
43
import JobWorkEnv from "../JobWorkEnv";
44
import JobWorkCulture from "../JobWorkCulture";
45
import Modal from "../../Modal";
46
import { textToParagraphs } from "../../../helpers/textToParagraphs";
47
import { useUrlHash, Link } from "../../../helpers/router";
48
49
interface JobReviewSectionProps {
50
  title: string;
51
  isSubsection?: boolean;
52
  link: string;
53
  linkLabel: string;
54
  description?: string;
55
}
56
57
const messages = defineMessages({
58
  titleHeading: {
59
    id: "jobBuilder.review.jobPageHeading",
60
    defaultMessage: "Job Page Heading",
61
    description: "Section title.",
62
  },
63
  infoEditLink: {
64
    id: "jobBuilder.review.infoEditLink",
65
    defaultMessage: "Edit This in Step 01: Job Info",
66
    description: "Link to edit job details.",
67
  },
68
  impactEditLink: {
69
    id: "jobBuilder.review.impactEditLink",
70
    defaultMessage: "Edit This in Step 03: Impact",
71
    description: "Link to edit impact statements.",
72
  },
73
  tasksEditLink: {
74
    id: "jobBuilder.review.tasksEditLink",
75
    defaultMessage: "Edit This in Step 04: Tasks",
76
    description: "Link to edit tasks.",
77
  },
78
  skillsEditLink: {
79
    id: "jobBuilder.review.skillsEditLink",
80
    defaultMessage: "Edit This in Step 05: Skills",
81
    description: "Link to edit skills.",
82
  },
83
  workEnvEditLink: {
84
    id: "jobBuilder.review.workEnvEditLink",
85
    defaultMessage: "Edit This in Step 02: Work Environment",
86
    description: "Link to edit work environment.",
87
  },
88
  managerProfileLink: {
89
    id: "jobBuilder.review.managerProfileLink",
90
    defaultMessage: "Edit This in Your Profile",
91
    description: "Link to edit a manager's profile.",
92
  },
93
  nullProvince: {
94
    id: "jobBuilder.review.nullProvince",
95
    defaultMessage: "MISSING PROVINCE",
96
    description: "Error text for missing province information.",
97
  },
98
  basicHeading: {
99
    id: "jobBuilder.review.basicInformationHeading",
100
    defaultMessage: "Basic Information",
101
    description: "Heading for Basic Information section",
102
  },
103
  impactHeading: {
104
    id: "jobBuilder.review.impactHeading",
105
    defaultMessage: "Impact",
106
    description: "Heading for Impact section",
107
  },
108
  tasksHeading: {
109
    id: "jobBuilder.review.tasksHeading",
110
    defaultMessage: "Tasks",
111
    description: "Heading for Tasks section",
112
  },
113
  criteriaSection: {
114
    id: "jobBuilder.review.criteriaSection",
115
    defaultMessage: "Criteria",
116
    description: "Title for criteria section",
117
  },
118
  educationalHeading: {
119
    id: "jobBuilder.review.educationalHeading",
120
    defaultMessage: "Education Requirements",
121
    description: "Heading for Educational section",
122
  },
123
  skillsHeading: {
124
    id: "jobBuilder.review.skillsHeading",
125
    defaultMessage: "Skills You Need to Have",
126
    description: "Heading for Skills section",
127
  },
128
  assetHeading: {
129
    id: "jobBuilder.review.assetHeading",
130
    defaultMessage: "Skills That Are Nice to Have",
131
    description: "Heading for Asset Skills section",
132
  },
133
  languageHeading: {
134
    id: "jobBuilder.review.languageHeading",
135
    defaultMessage: "Language Requirements",
136
    description: "Heading for Language section",
137
  },
138
  cultureSection: {
139
    id: "jobBuilder.review.cultureSection",
140
    defaultMessage: "Environment & Culture",
141
    description: "Title for culture section",
142
  },
143
  managerHeading: {
144
    id: "jobBuilder.review.managerHeading",
145
    defaultMessage: "Manager Information",
146
    description: "Heading for Manager section",
147
  },
148
  workCultureHeading: {
149
    id: "jobBuilder.review.workCultureHeading",
150
    defaultMessage: "Work Culture",
151
    description: "Heading for Work Culture section",
152
  },
153
  workEnvHeading: {
154
    id: "jobBuilder.review.workEnvHeading",
155
    defaultMessage: "Work Environment",
156
    description: "Heading for Work Environment section",
157
  },
158
  workEnvDescription: {
159
    id: "jobBuilder.review.workDescription",
160
    defaultMessage:
161
      "Please note that some Work Environment information is only presented to the applicant after they've clicked the \"View the team's work environment and culture\" button that appears on the job poster.",
162
    description: "A note about the information in the work description section",
163
  },
164
  otherInfoHeading: {
165
    id: "jobBuilder.review.otherInfoHeading",
166
    defaultMessage: "Other Team Information",
167
    description: "Heading for other info section",
168
  },
169
});
170
171
const JobReviewSection: React.FunctionComponent<JobReviewSectionProps> = ({
172
  title,
173
  isSubsection,
174
  link,
175
  linkLabel,
176
  description,
177
  children,
178
}): React.ReactElement => {
179
  return (
180
    <>
181
      <div
182
        data-c-margin={
183
          isSubsection ? "tb(normal)" : "top(triple) bottom(normal)"
184
        }
185
      >
186
        <div data-c-grid="gutter middle">
187
          <div
188
            data-c-grid-item="tp(1of2)"
189
            data-c-alignment="base(centre) tp(left)"
190
          >
191
            {isSubsection ? (
192
              <h5 data-c-font-weight="bold" data-c-font-size="h5">
193
                {title}
194
              </h5>
195
            ) : (
196
              <h4 data-c-colour="c2" data-c-font-size="h4">
197
                {title}
198
              </h4>
199
            )}
200
          </div>
201
          <div
202
            data-c-grid-item="tp(1of2)"
203
            data-c-alignment="base(centre) tp(right)"
204
          >
205
            <Link href={link} title={linkLabel}>
206
              <i data-c-colour="c2" className="fas fa-edit" />
207
              {linkLabel}
208
            </Link>
209
          </div>
210
        </div>
211
      </div>
212
      {description && <p data-c-margin="bottom(normal)">{description}</p>}
213
      <div
214
        data-c-border="all(thin, solid, grey)"
215
        data-c-padding="normal"
216
        data-c-radius="rounded"
217
      >
218
        {children}
219
      </div>
220
    </>
221
  );
222
};
223
224
const sectionTitle = (title: string): React.ReactElement => {
225
  return (
226
    <div data-c-margin="top(triple) bottom(normal)">
227
      <div data-c-grid="gutter middle">
228
        <div
229
          data-c-grid-item="base(1of1)"
230
          data-c-alignment="base(centre) tp(left)"
231
        >
232
          <h4 data-c-colour="c2" data-c-font-size="h4">
233
            {title}
234
          </h4>
235
        </div>
236
      </div>
237
    </div>
238
  );
239
};
240
241
const languageRequirementIcons = (
242
  languageRequirementId: number,
243
): React.ReactElement => {
244
  const enIcon = <img src="/images/icon_english_requirement.svg" alt="" />;
245
  const frIcon = <img src="/images/icon_french_requirement.svg" alt="" />;
246
  switch (languageRequirementId) {
247
    case LanguageRequirementId.bilingualIntermediate:
248
    case LanguageRequirementId.bilingualAdvanced:
249
      return (
250
        <>
251
          {enIcon}
252
          &amp;&nbsp;&nbsp;
253
          {frIcon}
254
        </>
255
      );
256
    case LanguageRequirementId.englishOrFrench:
257
      return (
258
        <>
259
          {enIcon}
260
          <FormattedMessage
261
            id="jobBuilder.review.or"
262
            defaultMessage="or"
263
            description="Displayed between language icons for the English Or French option."
264
          />
265
          &nbsp;&nbsp;
266
          {frIcon}
267
        </>
268
      );
269
    case LanguageRequirementId.english:
270
      return enIcon;
271
    case LanguageRequirementId.french:
272
      return frIcon;
273
    default:
274
      return enIcon;
275
  }
276
};
277
278
const renderManagerSection = (
279
  manager: Manager | null,
280
  managerDeptName: string,
281
  locale: "en" | "fr",
282
): React.ReactElement => {
283
  if (manager === null) {
284
    return (
285
      <p>
286
        <FormattedMessage
287
          id="jobBuilder.review.managerDataLoading"
288
          defaultMessage="Manager data is loading..."
289
          description="Placeholder text as Manager data loads."
290
        />
291
      </p>
292
    );
293
  }
294
  const managerTranslation = manager[locale];
295
  if (managerTranslation) {
296
    return (
297
      <>
298
        <p data-c-margin="bottom(normal)">{manager.name}</p>
299
        <p
300
          data-c-margin={`${managerTranslation.about_me && "{bottom(normal)"}`}
301
        >
302
          <FormattedMessage
303
            id="jobBuilder.review.managerPosition"
304
            defaultMessage="{position} at {department}"
305
            description="Description of the Manager's position & department."
306
            values={{
307
              position: managerTranslation.position,
308
              department: managerDeptName,
309
            }}
310
          />
311
        </p>
312
        {managerTranslation.about_me && <p>{managerTranslation.about_me}</p>}
313
      </>
314
    );
315
  }
316
  return (
317
    <p>
318
      <FormattedMessage
319
        id="jobBuilder.review.managerIncomplete"
320
        defaultMessage="Please complete your manager profile."
321
        description="Note that the Manager's profile is incomplete and should be edited before continuing."
322
      />
323
    </p>
324
  );
325
};
326
327
interface JobReviewProps {
328
  job: Job;
329
  manager: Manager | null;
330
  tasks: JobPosterKeyTask[];
331
  criteria: Criteria[];
332
  // List of all possible skills.
333
  skills: Skill[];
334
  // List of all possible departments.
335
  departments: Department[];
336
  validForSubmission?: boolean;
337
  handleSubmit: (job: Job) => Promise<void>;
338
  handleContinue: () => void;
339
  handleReturn: () => void;
340
}
341
342
export const JobReview: React.FunctionComponent<JobReviewProps &
343
  WrappedComponentProps> = ({
344
  job,
345
  manager,
346
  tasks,
347
  criteria,
348
  skills,
349
  departments,
350
  validForSubmission,
351
  handleSubmit,
352
  handleContinue,
353
  handleReturn,
354
  intl,
355
}): React.ReactElement => {
356
  // Scroll to element specified in the url hash, if possible
357
  useUrlHash();
358
359
  const [isModalVisible, setIsModalVisible] = useState(false);
360
  const modalId = "job-review-modal";
361
  const modalParentRef = useRef<HTMLDivElement>(null);
362
363
  const { locale } = intl;
364
  if (locale !== "en" && locale !== "fr") {
365
    throw new Error("Unknown intl.locale");
366
  }
367
368
  const getDeptName = (departmentId: number | null): string => {
369
    const department =
370
      departmentId !== null ? find(departments, departmentId) : null;
371
    return department !== null ? department[locale].name : "MISSING DEPARTMENT";
372
  };
373
  const departmentName = getDeptName(job.department_id);
374
  const managerDeptName = manager ? getDeptName(manager.department_id) : "";
375
376
  // Map the skills into a dictionary for quicker access
377
  const skillsById = mapToObject(skills, getId);
378
  const getSkillOfCriteria = (criterion: Criteria): Skill | null => {
379
    return hasKey(skillsById, criterion.skill_id)
380
      ? skillsById[criterion.skill_id]
381
      : null;
382
  };
383
  const essentialCriteria = criteria.filter(
384
    (criterion): boolean =>
385
      criterion.criteria_type_id === CriteriaTypeId.Essential,
386
  );
387
  const assetCriteria = criteria.filter(
388
    (criterion): boolean => criterion.criteria_type_id === CriteriaTypeId.Asset,
389
  );
390
391
  const selectedEnvOptions: string[] = job.work_env_features
392
    ? Object.entries(job.work_env_features)
393
        .map(([feature, selected]): string | null =>
394
          selected ? feature : null,
395
        )
396
        .filter(notEmpty)
397
    : [];
398
399
  return (
400
    <>
401
      <div
402
        data-c-container="form"
403
        data-c-padding="top(triple) bottom(triple)"
404
        ref={modalParentRef}
405
      >
406
        <h3
407
          data-c-font-size="h3"
408
          data-c-font-weight="bold"
409
          data-c-margin="bottom(double)"
410
        >
411
          <FormattedMessage
412
            id="jobBuilder.review.reviewYourPoster"
413
            defaultMessage="Review Your Job Poster for:"
414
            description="Title for Review Job Poster section."
415
          />{" "}
416
          <span data-c-colour="c2">{job[locale].title}</span>
417
        </h3>
418
        <p>
419
          <FormattedMessage
420
            id="jobBuilder.review.headsUp"
421
            defaultMessage="Just a heads up! We've rearranged some of your information to help you
422
            understand how an applicant will see it once published."
423
            description="Description under primary title of review section"
424
          />
425
        </p>
426
        <JobReviewSection
427
          title={intl.formatMessage(messages.titleHeading)}
428
          linkLabel={intl.formatMessage(messages.infoEditLink)}
429
          link={jobBuilderDetails(locale, job.id)}
430
        >
431
          <p data-c-font-weight="bold" data-c-margin="bottom(half)">
432
            {job[locale].title}
433
          </p>
434
          <p data-c-margin="bottom(normal)">{departmentName}</p>
435
          <p data-c-margin="bottom(half)">
436
            <i
437
              data-c-colour="c2"
438
              className="fas fa-map-marker-alt"
439
              title="Location Icon."
440
            >
441
              &nbsp;&nbsp;
442
            </i>
443
            {job[locale].city},{" "}
444
            {job.province_id !== null
445
              ? intl.formatMessage(provinceName(job.province_id))
446
              : intl.formatMessage(messages.nullProvince)}
447
          </p>
448
          <p>
449
            <i
450
              data-c-colour="c2"
451
              className="fas fa-home"
452
              title="Remote Work Icon."
453
            >
454
              &nbsp;&nbsp;
455
            </i>
456
            {job.remote_work_allowed ? (
457
              <FormattedMessage
458
                id="jobBuilder.review.remoteAllowed"
459
                defaultMessage="Remote Work Allowed"
460
                description="Text displayed when remote work is allowed."
461
              />
462
            ) : (
463
              <FormattedMessage
464
                id="jobBuilder.review.remoteNotAllowed"
465
                defaultMessage="Remote Work Not Allowed"
466
                description="Text displayed when remote work is not allowed."
467
              />
468
            )}
469
          </p>
470
        </JobReviewSection>
471
        <JobReviewSection
472
          title={intl.formatMessage(messages.basicHeading)}
473
          linkLabel={intl.formatMessage(messages.infoEditLink)}
474
          link={jobBuilderDetails(locale, job.id)}
475
        >
476
          <div data-c-grid="gutter">
477
            <div data-c-grid-item="tp(1of2)">
478
              <p data-c-font-weight="bold" data-c-margin="bottom(quarter)">
479
                <FormattedMessage
480
                  id="jobBuilder.review.averageAnnualSalary"
481
                  defaultMessage="Annual Salary Range"
482
                  description="Label for salary information."
483
                />
484
              </p>
485
              <p>
486
                <FormattedMessage
487
                  id="jobBuilder.review.tCAdds"
488
                  defaultMessage="Talent Cloud will add this."
489
                  description="Information will be added placeholder."
490
                />
491
              </p>
492
            </div>
493
            <div data-c-grid-item="tp(1of2)">
494
              <p data-c-font-weight="bold" data-c-margin="bottom(quarter)">
495
                <FormattedMessage
496
                  id="jobBuilder.review.languageProfile"
497
                  defaultMessage="Language Profile"
498
                  description="Information will be added placeholder."
499
                />
500
              </p>
501
              <p>
502
                {job.language_requirement_id
503
                  ? intl.formatMessage(
504
                      languageRequirement(job.language_requirement_id),
505
                    )
506
                  : ""}
507
              </p>
508
            </div>
509
            <div data-c-grid-item="tp(1of2)">
510
              <p data-c-font-weight="bold" data-c-margin="bottom(quarter)">
511
                <FormattedMessage
512
                  id="jobBuilder.review.duration"
513
                  defaultMessage="Duration"
514
                  description="Label for duration of Term"
515
                />
516
              </p>
517
              <p>
518
                <FormattedMessage
519
                  id="jobBuilder.review.months"
520
                  defaultMessage="{termMonths, plural, =0 {No Months} one {# Month} other {# Months}}"
521
                  description="Length of term in months"
522
                  values={{ termMonths: job.term_qty }}
523
                />
524
              </p>
525
            </div>
526
            <div data-c-grid-item="tp(1of2)">
527
              <p data-c-font-weight="bold" data-c-margin="bottom(quarter)">
528
                <FormattedMessage
529
                  id="jobBuilder.review.securityClearance"
530
                  defaultMessage="Security Clearance"
531
                  description="Label for Security Clearance info"
532
                />
533
              </p>
534
              <p>
535
                {job.security_clearance_id
536
                  ? intl.formatMessage(
537
                      securityClearance(job.security_clearance_id),
538
                    )
539
                  : ""}
540
              </p>
541
            </div>
542
            <div data-c-grid-item="tp(1of2)">
543
              <p data-c-font-weight="bold" data-c-margin="bottom(quarter)">
544
                <FormattedMessage
545
                  id="jobBuilder.review.targetStartDate"
546
                  defaultMessage="Target Start Date"
547
                  description="Label for start date info"
548
                />
549
              </p>
550
              <p>
551
                <FormattedMessage
552
                  id="jobBuilder.review.comesLater"
553
                  defaultMessage="This comes later."
554
                  description="Placeholder for information that comes later"
555
                />
556
              </p>
557
            </div>
558
            <div data-c-grid-item="tp(1of2)">
559
              <p data-c-font-weight="bold" data-c-margin="bottom(quarter)">
560
                <FormattedMessage
561
                  id="jobBuilder.review.GovernmentClass"
562
                  defaultMessage="Government Classification"
563
                  description="Placeholder for information that comes later"
564
                />
565
              </p>
566
              <p>
567
                {job.classification_code}-0{job.classification_level}
568
              </p>
569
            </div>
570
          </div>
571
        </JobReviewSection>
572
        <JobReviewSection
573
          title={intl.formatMessage(messages.impactHeading)}
574
          linkLabel={intl.formatMessage(messages.impactEditLink)}
575
          link={jobBuilderImpact(locale, job.id)}
576
        >
577
          <p data-c-margin="bottom(normal)">{job[locale].dept_impact}</p>
578
          <p data-c-margin="bottom(normal)">{job[locale].team_impact}</p>
579
          <p>{job[locale].hire_impact}</p>
580
        </JobReviewSection>
581
        <JobReviewSection
582
          title={intl.formatMessage(messages.tasksHeading)}
583
          linkLabel={intl.formatMessage(messages.tasksEditLink)}
584
          link={jobBuilderTasks(locale, job.id)}
585
        >
586
          <ul>
587
            {tasks.map(
588
              (task: JobPosterKeyTask): React.ReactElement => (
589
                <li key={task.id}>{task[locale].description}</li>
590
              ),
591
            )}
592
          </ul>
593
        </JobReviewSection>
594
        {sectionTitle(intl.formatMessage(messages.criteriaSection))}
595
        <JobReviewSection
596
          title={intl.formatMessage(messages.educationalHeading)}
597
          isSubsection
598
          linkLabel={intl.formatMessage(messages.infoEditLink)}
599
          link={jobBuilderDetails(locale, job.id)}
600
        >
601
          {textToParagraphs(job[locale].education || "")}
602
        </JobReviewSection>
603
        <JobReviewSection
604
          title={intl.formatMessage(messages.skillsHeading)}
605
          isSubsection
606
          linkLabel={intl.formatMessage(messages.skillsEditLink)}
607
          link={jobBuilderSkills(locale, job.id)}
608
        >
609
          {essentialCriteria.length === 0 ? (
610
            <p>
611
              <FormattedMessage
612
                id="jobBuilder.review.skills.nullState"
613
                defaultMessage="You haven't added any Nice to Have skills to this poster."
614
                description="The text displayed for skills when you haven't added any skills."
615
              />
616
            </p>
617
          ) : (
618
            essentialCriteria.map((criterion): React.ReactElement | null => {
619
              const skill = getSkillOfCriteria(criterion);
620
              if (skill === null) {
621
                return null;
622
              }
623
              return (
624
                <Criterion
625
                  criterion={criterion}
626
                  skill={skill}
627
                  key={criterion.id}
628
                />
629
              );
630
            })
631
          )}
632
        </JobReviewSection>
633
        <JobReviewSection
634
          title={intl.formatMessage(messages.assetHeading)}
635
          isSubsection
636
          linkLabel={intl.formatMessage(messages.skillsEditLink)}
637
          link={jobBuilderSkills(locale, job.id)}
638
        >
639
          {assetCriteria.length === 0 ? (
640
            <p>
641
              <FormattedMessage
642
                id="jobBuilder.review.skills.nullState"
643
                defaultMessage="You haven't added any Nice to Have skills to this poster."
644
                description="The text displayed for skills when you haven't added any skills."
645
              />
646
            </p>
647
          ) : (
648
            assetCriteria.map((criterion): React.ReactElement | null => {
649
              const skill = getSkillOfCriteria(criterion);
650
              if (skill === null) {
651
                return null;
652
              }
653
              return (
654
                <Criterion
655
                  criterion={criterion}
656
                  skill={skill}
657
                  key={criterion.id}
658
                />
659
              );
660
            })
661
          )}
662
        </JobReviewSection>
663
        <JobReviewSection
664
          title={intl.formatMessage(messages.languageHeading)}
665
          linkLabel={intl.formatMessage(messages.infoEditLink)}
666
          link={jobBuilderDetails(locale, job.id)}
667
        >
668
          {/** TODO: get lang data from job */}
669
          {job.language_requirement_id && (
670
            <>
671
              <p
672
                className="job-builder-review-language"
673
                data-c-margin="bottom(normal)"
674
              >
675
                {languageRequirementIcons(job.language_requirement_id)}
676
                {intl.formatMessage(
677
                  languageRequirement(job.language_requirement_id),
678
                )}
679
              </p>
680
              <p data-c-margin="bottom(normal)">
681
                {intl.formatMessage(
682
                  languageRequirementDescription(job.language_requirement_id),
683
                )}
684
              </p>
685
              <p data-c-margin="top(normal)">
686
                {intl.formatMessage(
687
                  languageRequirementContext(job.language_requirement_id),
688
                )}
689
              </p>
690
            </>
691
          )}
692
        </JobReviewSection>
693
        {sectionTitle(intl.formatMessage(messages.cultureSection))}
694
        <JobReviewSection
695
          title={intl.formatMessage(messages.managerHeading)}
696
          isSubsection
697
          linkLabel={intl.formatMessage(messages.managerProfileLink)}
698
          link={managerEditProfile(locale)}
699
        >
700
          {renderManagerSection(manager, managerDeptName, locale)}
701
        </JobReviewSection>
702
        <JobReviewSection
703
          title={intl.formatMessage(messages.workCultureHeading)}
704
          isSubsection
705
          linkLabel={intl.formatMessage(messages.workEnvEditLink)}
706
          link={jobBuilderEnv(locale, job.id)}
707
        >
708
          {job[locale].culture_summary && <p>{job[locale].culture_summary}</p>}
709
          {job[locale].culture_special && <p>{job[locale].culture_special}</p>}
710
        </JobReviewSection>
711
        <JobReviewSection
712
          title={intl.formatMessage(messages.workEnvHeading)}
713
          isSubsection
714
          linkLabel={intl.formatMessage(messages.workEnvEditLink)}
715
          link={jobBuilderEnv(locale, job.id)}
716
          description={intl.formatMessage(messages.workEnvDescription)}
717
        >
718
          <JobWorkEnv
719
            teamSize={job.team_size || 0}
720
            selectedEnvOptions={selectedEnvOptions}
721
          />
722
        </JobReviewSection>
723
        <JobReviewSection
724
          title={intl.formatMessage(messages.otherInfoHeading)}
725
          isSubsection
726
          linkLabel={intl.formatMessage(messages.infoEditLink)}
727
          link={jobBuilderDetails(locale, job.id)}
728
        >
729
          <JobWorkCulture job={job} />
730
        </JobReviewSection>
731
        <div data-c-grid="gutter">
732
          <div data-c-grid-item="base(1of1)">
733
            <hr data-c-margin="top(normal) bottom(normal)" />
734
          </div>
735
          <div
736
            data-c-alignment="base(centre) tp(left)"
737
            data-c-grid-item="tp(1of2)"
738
          >
739
            <button
740
              data-c-button="outline(c2)"
741
              data-c-radius="rounded"
742
              type="button"
743
              onClick={(): void => handleReturn()}
744
            >
745
              <FormattedMessage
746
                id="jobBuilder.review.button.return"
747
                defaultMessage="Save &amp; Return to Skills"
748
                description="Label of 'Previous Step' button."
749
              />
750
            </button>
751
          </div>
752
          <div
753
            data-c-alignment="base(centre) tp(right)"
754
            data-c-grid-item="tp(1of2)"
755
          >
756
            {/* Modal trigger, same as last step. */}
757
            <button
758
              id="submit"
759
              data-c-button="solid(c2)"
760
              data-c-radius="rounded"
761
              type="button"
762
              disabled={!validForSubmission}
763
              onClick={(): void => setIsModalVisible(true)}
764
            >
765
              <FormattedMessage
766
                id="jobBuilder.review.button.submit"
767
                defaultMessage="Looks good!"
768
                description="Label of Job Review Submission Button"
769
              />
770
            </button>
771
          </div>
772
        </div>
773
      </div>
774
      <div data-c-dialog-overlay={isModalVisible ? "active" : ""} />
775
      <Modal
776
        id={modalId}
777
        visible={isModalVisible}
778
        onModalCancel={(): void => setIsModalVisible(false)}
779
        onModalConfirm={async (): Promise<void> => {
780
          try {
781
            await handleSubmit(job);
782
            handleContinue();
783
          } catch {
784
            setIsModalVisible(false);
785
          }
786
        }}
787
        parentElement={modalParentRef.current}
788
      >
789
        <Modal.Header>
790
          <div
791
            data-c-background="c1(100)"
792
            data-c-border="bottom(thin, solid, black)"
793
            data-c-padding="normal"
794
          >
795
            <h5
796
              data-c-colour="white"
797
              data-c-font-size="h4"
798
              id={`${modalId}-title`}
799
            >
800
              <FormattedMessage
801
                id="jobBuilder.review.confirm.title"
802
                defaultMessage="Congrats! Are You Ready to Submit?"
803
                description="Title of Submit Confirmation modal."
804
              />
805
            </h5>
806
          </div>
807
        </Modal.Header>
808
        <Modal.Body>
809
          <div data-c-padding="normal">
810
            <p data-c-margin="bottom(normal)">
811
              <FormattedMessage
812
                id="jobBuilder.review.readyTosubmit"
813
                defaultMessage="If you're ready to submit your poster, click the Submit button below."
814
                description="Instructions on how to submit"
815
              />
816
            </p>
817
            <p data-c-font-weight="bold" data-c-margin="bottom(normal)">
818
              <FormattedMessage
819
                id="jobBuilder.review.whatHappens"
820
                defaultMessage="What happens next?"
821
                description="Rhtorical question"
822
              />
823
            </p>
824
            <p data-c-margin="bottom(normal)">
825
              <FormattedMessage
826
                id="jobBuilder.review.sendYourDraft"
827
                defaultMessage="Talent Cloud will send your draft to your department's HR advisor who will notify you with comments."
828
                description="Information about next steps"
829
              />
830
            </p>
831
            <p>
832
              <FormattedMessage
833
                id="jobBuilder.review.meantime"
834
                defaultMessage="In the meantime, feel free to go ahead and create a screening plan for your selection process. Alternatively, you can wait for comments to come back from HR before you take the next step."
835
                description="Suggestion on what to do while you wait."
836
              />
837
            </p>
838
          </div>
839
        </Modal.Body>
840
        <Modal.Footer>
841
          <Modal.FooterCancelBtn>
842
            <FormattedMessage
843
              id="jobBuilder.review.confirm.cancel"
844
              defaultMessage="Cancel"
845
              description="Cancel button of Job Review confirmation modal."
846
            />
847
          </Modal.FooterCancelBtn>
848
          <Modal.FooterConfirmBtn>
849
            <FormattedMessage
850
              id="jobBuilder.review.confirm.submit"
851
              defaultMessage="Yes, Submit"
852
              description="Submit button of Job Review confirmation modal."
853
            />
854
          </Modal.FooterConfirmBtn>
855
        </Modal.Footer>
856
      </Modal>
857
    </>
858
  );
859
};
860
861
export default injectIntl(JobReview);
862