1
|
|
|
/* eslint-disable camelcase */ |
2
|
|
|
/* eslint-disable @typescript-eslint/camelcase */ |
3
|
|
|
import * as React from "react"; |
4
|
|
|
import { FormattedMessage, useIntl, defineMessages } from "react-intl"; |
5
|
|
|
import { |
6
|
|
|
Skill, |
7
|
|
|
ExperienceEducation, |
8
|
|
|
ExperienceWork, |
9
|
|
|
ExperienceCommunity, |
10
|
|
|
Experience, |
11
|
|
|
ExperiencePersonal, |
12
|
|
|
ExperienceAward, |
13
|
|
|
ExperienceSkill, |
14
|
|
|
Criteria, |
15
|
|
|
} from "../../../models/types"; |
16
|
|
|
import { localizeFieldNonNull, getLocale } from "../../../helpers/localize"; |
17
|
|
|
import { |
18
|
|
|
SkillTypeId, |
19
|
|
|
CriteriaTypeId, |
20
|
|
|
getKeyByValue, |
21
|
|
|
ClassificationId, |
22
|
|
|
} from "../../../models/lookupConstants"; |
23
|
|
|
import EducationExperienceModal, { |
24
|
|
|
messages as educationMessages, |
25
|
|
|
EducationType, |
26
|
|
|
EducationStatus, |
27
|
|
|
EducationExperienceSubmitData, |
28
|
|
|
} from "../ExperienceModals/EducationExperienceModal"; |
29
|
|
|
|
30
|
|
|
import WorkExperienceModal, { |
31
|
|
|
messages as workMessages, |
32
|
|
|
WorkExperienceSubmitData, |
33
|
|
|
} from "../ExperienceModals/WorkExperienceModal"; |
34
|
|
|
import CommunityExperienceModal, { |
35
|
|
|
messages as communityMessages, |
36
|
|
|
CommunityExperienceSubmitData, |
37
|
|
|
} from "../ExperienceModals/CommunityExperienceModal"; |
38
|
|
|
import PersonalExperienceModal, { |
39
|
|
|
messages as personalMessages, |
40
|
|
|
PersonalExperienceSubmitData, |
41
|
|
|
} from "../ExperienceModals/PersonalExperienceModal"; |
42
|
|
|
import AwardExperienceModal, { |
43
|
|
|
messages as awardMessages, |
44
|
|
|
AwardRecipientType, |
45
|
|
|
AwardRecognitionType, |
46
|
|
|
AwardExperienceSubmitData, |
47
|
|
|
} from "../ExperienceModals/AwardExperienceModal"; |
48
|
|
|
import ExperienceEducationAccordion from "../ExperienceAccordions/ExperienceEducationAccordion"; |
49
|
|
|
import ExperienceWorkAccordion from "../ExperienceAccordions/ExperienceWorkAccordion"; |
50
|
|
|
import ExperienceCommunityAccordion from "../ExperienceAccordions/ExperienceCommunityAccordion"; |
51
|
|
|
import ExperiencePersonalAccordion from "../ExperienceAccordions/ExperiencePersonalAccordion"; |
52
|
|
|
import ExperienceAwardAccordion from "../ExperienceAccordions/ExperienceAwardAccordion"; |
53
|
|
|
import { |
54
|
|
|
getSkillOfCriteria, |
55
|
|
|
getSkillsOfExperience, |
56
|
|
|
getDisconnectedRequiredSkills, |
57
|
|
|
} from "../helpers"; |
58
|
|
|
import { navigationMessages, experienceMessages } from "../applicationMessages"; |
59
|
|
|
|
60
|
|
|
const messages = defineMessages({ |
61
|
|
|
educationTypeMissing: { |
62
|
|
|
id: "application.experience.educationTypeMissing", |
63
|
|
|
defaultMessage: "Education type not found", |
64
|
|
|
description: "Error message for when the education type cannot be found.", |
65
|
|
|
}, |
66
|
|
|
educationStatusMissing: { |
67
|
|
|
id: "application.experience.educationStatusMissing", |
68
|
|
|
defaultMessage: "Education status not found", |
69
|
|
|
description: "Error message for when the education status cannot be found.", |
70
|
|
|
}, |
71
|
|
|
awardRecipientMissing: { |
72
|
|
|
id: "application.experience.awardRecipientMissing", |
73
|
|
|
defaultMessage: "Award recipient not found", |
74
|
|
|
description: "Error message for when the award recipient cannot be found.", |
75
|
|
|
}, |
76
|
|
|
awardRecognitionMissing: { |
77
|
|
|
id: "application.experience.awardRecognitionMissing", |
78
|
|
|
defaultMessage: "Award recognition not found", |
79
|
|
|
description: |
80
|
|
|
"Error message for when the award recognition cannot be found.", |
81
|
|
|
}, |
82
|
|
|
errorRenderingExperience: { |
83
|
|
|
id: "application.experience.errorRenderingExperience", |
84
|
|
|
defaultMessage: "Experience failed to render (experience type missing).", |
85
|
|
|
description: "Error message displayed when experience fails to render.", |
86
|
|
|
}, |
87
|
|
|
}); |
88
|
|
|
|
89
|
|
|
export type ExperienceSubmitData = |
90
|
|
|
| EducationExperienceSubmitData |
91
|
|
|
| WorkExperienceSubmitData |
92
|
|
|
| CommunityExperienceSubmitData |
93
|
|
|
| PersonalExperienceSubmitData |
94
|
|
|
| AwardExperienceSubmitData; |
95
|
|
|
|
96
|
|
|
interface ExperienceProps { |
97
|
|
|
experiences: Experience[]; |
98
|
|
|
educationStatuses: EducationStatus[]; |
99
|
|
|
educationTypes: EducationType[]; |
100
|
|
|
experienceSkills: ExperienceSkill[]; |
101
|
|
|
criteria: Criteria[]; |
102
|
|
|
skills: Skill[]; |
103
|
|
|
jobId: number; |
104
|
|
|
jobClassificationId: number; |
105
|
|
|
recipientTypes: AwardRecipientType[]; |
106
|
|
|
recognitionTypes: AwardRecognitionType[]; |
107
|
|
|
handleSubmitExperience: (data: ExperienceSubmitData) => Promise<void>; |
108
|
|
|
handleDeleteExperience: ( |
109
|
|
|
id: number, |
110
|
|
|
type: Experience["type"], |
111
|
|
|
) => Promise<void>; |
112
|
|
|
handleContinue: () => void; |
113
|
|
|
handleQuit: () => void; |
114
|
|
|
handleReturn: () => void; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
const MyExperience: React.FunctionComponent<ExperienceProps> = ({ |
118
|
|
|
experiences, |
119
|
|
|
educationStatuses, |
120
|
|
|
educationTypes, |
121
|
|
|
experienceSkills, |
122
|
|
|
criteria, |
123
|
|
|
skills, |
124
|
|
|
handleSubmitExperience, |
125
|
|
|
handleDeleteExperience, |
126
|
|
|
jobId, |
127
|
|
|
jobClassificationId, |
128
|
|
|
recipientTypes, |
129
|
|
|
recognitionTypes, |
130
|
|
|
handleContinue, |
131
|
|
|
handleQuit, |
132
|
|
|
handleReturn, |
133
|
|
|
}) => { |
134
|
|
|
const intl = useIntl(); |
135
|
|
|
const locale = getLocale(intl.locale); |
136
|
|
|
|
137
|
|
|
const jobClassification = getKeyByValue( |
138
|
|
|
ClassificationId, |
139
|
|
|
jobClassificationId, |
140
|
|
|
); |
141
|
|
|
|
142
|
|
|
const [experienceData, setExperienceData] = React.useState< |
143
|
|
|
| (Experience & { |
144
|
|
|
savedOptionalSkills: Skill[]; |
145
|
|
|
savedRequiredSkills: Skill[]; |
146
|
|
|
}) |
147
|
|
|
| null |
148
|
|
|
>(null); |
149
|
|
|
|
150
|
|
|
const [isModalVisible, setIsModalVisible] = React.useState<{ |
151
|
|
|
id: Experience["type"] | ""; |
152
|
|
|
visible: boolean; |
153
|
|
|
}>({ |
154
|
|
|
id: "", |
155
|
|
|
visible: false, |
156
|
|
|
}); |
157
|
|
|
|
158
|
|
|
const filteredSkills = criteria.reduce( |
159
|
|
|
(result, criterion): { essential: Skill[]; asset: Skill[] } => { |
160
|
|
|
const skillOfCriterion = getSkillOfCriteria(criterion, skills); |
161
|
|
|
if (skillOfCriterion) { |
162
|
|
|
if (criterion.criteria_type_id === CriteriaTypeId.Essential) { |
163
|
|
|
result.essential.push(skillOfCriterion); |
164
|
|
|
} |
165
|
|
|
if (criterion.criteria_type_id === CriteriaTypeId.Asset) { |
166
|
|
|
result.asset.push(skillOfCriterion); |
167
|
|
|
} |
168
|
|
|
} |
169
|
|
|
return result; |
170
|
|
|
}, |
171
|
|
|
{ essential: [], asset: [] } as { essential: Skill[]; asset: Skill[] }, |
172
|
|
|
); |
173
|
|
|
|
174
|
|
|
const essentialSkills = filteredSkills.essential; |
175
|
|
|
const assetSkills = filteredSkills.asset; |
176
|
|
|
|
177
|
|
|
getDisconnectedRequiredSkills(experiences, experienceSkills, essentialSkills); |
178
|
|
|
|
179
|
|
|
const [ |
180
|
|
|
disconnectedRequiredSkills, |
181
|
|
|
setDisconnectedRequiredSkills, |
182
|
|
|
] = React.useState<Skill[]>( |
183
|
|
|
getDisconnectedRequiredSkills( |
184
|
|
|
experiences, |
185
|
|
|
experienceSkills, |
186
|
|
|
essentialSkills, |
187
|
|
|
), |
188
|
|
|
); |
189
|
|
|
|
190
|
|
|
const openModal = (id: Experience["type"]): void => { |
191
|
|
|
setIsModalVisible({ id, visible: true }); |
192
|
|
|
}; |
193
|
|
|
|
194
|
|
|
const closeModal = (): void => { |
195
|
|
|
setDisconnectedRequiredSkills( |
196
|
|
|
getDisconnectedRequiredSkills( |
197
|
|
|
experiences, |
198
|
|
|
experienceSkills, |
199
|
|
|
essentialSkills, |
200
|
|
|
), |
201
|
|
|
); |
202
|
|
|
setExperienceData(null); |
203
|
|
|
setIsModalVisible({ id: "", visible: false }); |
204
|
|
|
}; |
205
|
|
|
|
206
|
|
|
const submitExperience = (data: ExperienceSubmitData): Promise<void> => |
207
|
|
|
handleSubmitExperience(data).then(closeModal); |
208
|
|
|
|
209
|
|
|
const editExperience = ( |
210
|
|
|
experience: Experience, |
211
|
|
|
savedOptionalSkills: Skill[], |
212
|
|
|
savedRequiredSkills: Skill[], |
213
|
|
|
): void => { |
214
|
|
|
setExperienceData({ |
215
|
|
|
...experience, |
216
|
|
|
savedOptionalSkills, |
217
|
|
|
savedRequiredSkills, |
218
|
|
|
}); |
219
|
|
|
setIsModalVisible({ id: experience.type, visible: true }); |
220
|
|
|
}; |
221
|
|
|
|
222
|
|
|
const deleteExperience = (experience: Experience): Promise<void> => |
223
|
|
|
handleDeleteExperience(experience.id, experience.type).then(closeModal); |
224
|
|
|
|
225
|
|
|
const softSkills = [...assetSkills, ...essentialSkills].filter( |
226
|
|
|
(skill) => skill.skill_type_id === SkillTypeId.Soft, |
227
|
|
|
); |
228
|
|
|
|
229
|
|
|
const modalButtons: { |
230
|
|
|
[key: string]: { id: Experience["type"]; title: string; icon: string }; |
231
|
|
|
} = { |
232
|
|
|
education: { |
233
|
|
|
id: "experience_education", |
234
|
|
|
title: intl.formatMessage(educationMessages.modalTitle), |
235
|
|
|
icon: "fas fa-book", |
236
|
|
|
}, |
237
|
|
|
work: { |
238
|
|
|
id: "experience_work", |
239
|
|
|
title: intl.formatMessage(workMessages.modalTitle), |
240
|
|
|
icon: "fas fa-briefcase", |
241
|
|
|
}, |
242
|
|
|
community: { |
243
|
|
|
id: "experience_community", |
244
|
|
|
title: intl.formatMessage(communityMessages.modalTitle), |
245
|
|
|
icon: "fas fa-people-carry", |
246
|
|
|
}, |
247
|
|
|
personal: { |
248
|
|
|
id: "experience_personal", |
249
|
|
|
title: intl.formatMessage(personalMessages.modalTitle), |
250
|
|
|
icon: "fas fa-mountain", |
251
|
|
|
}, |
252
|
|
|
award: { |
253
|
|
|
id: "experience_award", |
254
|
|
|
title: intl.formatMessage(awardMessages.modalTitle), |
255
|
|
|
icon: "fas fa-trophy", |
256
|
|
|
}, |
257
|
|
|
}; |
258
|
|
|
|
259
|
|
|
const modalButton = ({ id, title, icon }): React.ReactElement => ( |
260
|
|
|
<div key={id} data-c-grid-item="base(1of2) tp(1of3) tl(1of5)"> |
261
|
|
|
<button |
262
|
|
|
className="application-experience-trigger" |
263
|
|
|
data-c-card |
264
|
|
|
data-c-background="c1(100)" |
265
|
|
|
data-c-radius="rounded" |
266
|
|
|
title={title} |
267
|
|
|
data-c-dialog-id={id} |
268
|
|
|
data-c-dialog-action="open" |
269
|
|
|
type="button" |
270
|
|
|
onClick={(): void => openModal(id)} |
271
|
|
|
> |
272
|
|
|
<i className={icon} aria-hidden="true" /> |
273
|
|
|
<span data-c-font-size="regular" data-c-font-weight="bold"> |
274
|
|
|
{title} |
275
|
|
|
</span> |
276
|
|
|
</button> |
277
|
|
|
</div> |
278
|
|
|
); |
279
|
|
|
|
280
|
|
|
const modalRoot = document.getElementById("modal-root"); |
281
|
|
|
|
282
|
|
|
const experienceAccordion = ( |
283
|
|
|
experience: Experience, |
284
|
|
|
irrelevantSkillCount: number, |
285
|
|
|
relevantSkills: ExperienceSkill[], |
286
|
|
|
handleEdit: () => void, |
287
|
|
|
handleDelete: () => void, |
288
|
|
|
): React.ReactElement => { |
289
|
|
|
switch (experience.type) { |
290
|
|
|
case "experience_education": |
291
|
|
|
return ( |
292
|
|
|
<ExperienceEducationAccordion |
293
|
|
|
key={`${experience.id}-${experience.type}`} |
294
|
|
|
areaOfStudy={experience.area_of_study} |
295
|
|
|
educationType={localizeFieldNonNull( |
296
|
|
|
locale, |
297
|
|
|
experience, |
298
|
|
|
"education_type", |
299
|
|
|
)} |
300
|
|
|
endDate={experience.end_date} |
301
|
|
|
handleDelete={handleDelete} |
302
|
|
|
handleEdit={handleEdit} |
303
|
|
|
institution={experience.institution} |
304
|
|
|
irrelevantSkillCount={irrelevantSkillCount} |
305
|
|
|
isActive={experience.is_active} |
306
|
|
|
isEducationJustification={experience.is_education_requirement} |
307
|
|
|
relevantSkills={relevantSkills} |
308
|
|
|
skills={skills} |
309
|
|
|
showButtons |
310
|
|
|
showSkillDetails |
311
|
|
|
startDate={experience.start_date} |
312
|
|
|
status={localizeFieldNonNull( |
313
|
|
|
locale, |
314
|
|
|
experience, |
315
|
|
|
"education_status", |
316
|
|
|
)} |
317
|
|
|
thesisTitle={experience.thesis_title} |
318
|
|
|
/> |
319
|
|
|
); |
320
|
|
|
case "experience_work": |
321
|
|
|
return ( |
322
|
|
|
<ExperienceWorkAccordion |
323
|
|
|
key={`${experience.id}-${experience.type}`} |
324
|
|
|
endDate={experience.end_date} |
325
|
|
|
group={experience.group} |
326
|
|
|
handleDelete={handleDelete} |
327
|
|
|
handleEdit={handleEdit} |
328
|
|
|
irrelevantSkillCount={irrelevantSkillCount} |
329
|
|
|
isActive={experience.is_active} |
330
|
|
|
isEducationJustification={experience.is_education_requirement} |
331
|
|
|
organization={experience.organization} |
332
|
|
|
relevantSkills={relevantSkills} |
333
|
|
|
skills={skills} |
334
|
|
|
showButtons |
335
|
|
|
showSkillDetails |
336
|
|
|
startDate={experience.start_date} |
337
|
|
|
title={experience.title} |
338
|
|
|
/> |
339
|
|
|
); |
340
|
|
|
case "experience_community": |
341
|
|
|
return ( |
342
|
|
|
<ExperienceCommunityAccordion |
343
|
|
|
key={`${experience.id}-${experience.type}`} |
344
|
|
|
endDate={experience.end_date} |
345
|
|
|
group={experience.group} |
346
|
|
|
handleDelete={handleDelete} |
347
|
|
|
handleEdit={handleEdit} |
348
|
|
|
irrelevantSkillCount={irrelevantSkillCount} |
349
|
|
|
isActive={experience.is_active} |
350
|
|
|
isEducationJustification={experience.is_education_requirement} |
351
|
|
|
project={experience.project} |
352
|
|
|
relevantSkills={relevantSkills} |
353
|
|
|
skills={skills} |
354
|
|
|
showButtons |
355
|
|
|
showSkillDetails |
356
|
|
|
startDate={experience.start_date} |
357
|
|
|
title={experience.title} |
358
|
|
|
/> |
359
|
|
|
); |
360
|
|
|
case "experience_personal": |
361
|
|
|
return ( |
362
|
|
|
<ExperiencePersonalAccordion |
363
|
|
|
key={`${experience.id}-${experience.type}`} |
364
|
|
|
description={experience.description} |
365
|
|
|
endDate={experience.end_date} |
366
|
|
|
handleDelete={handleDelete} |
367
|
|
|
handleEdit={handleEdit} |
368
|
|
|
irrelevantSkillCount={irrelevantSkillCount} |
369
|
|
|
isActive={experience.is_active} |
370
|
|
|
isEducationJustification={experience.is_education_requirement} |
371
|
|
|
isShareable={experience.is_shareable} |
372
|
|
|
relevantSkills={relevantSkills} |
373
|
|
|
skills={skills} |
374
|
|
|
showButtons |
375
|
|
|
showSkillDetails |
376
|
|
|
startDate={experience.start_date} |
377
|
|
|
title={experience.title} |
378
|
|
|
/> |
379
|
|
|
); |
380
|
|
|
case "experience_award": |
381
|
|
|
return ( |
382
|
|
|
<ExperienceAwardAccordion |
383
|
|
|
key={`${experience.id}-${experience.type}`} |
384
|
|
|
awardedDate={experience.awarded_date} |
385
|
|
|
handleDelete={handleDelete} |
386
|
|
|
handleEdit={handleEdit} |
387
|
|
|
irrelevantSkillCount={irrelevantSkillCount} |
388
|
|
|
isEducationJustification={experience.is_education_requirement} |
389
|
|
|
issuer={experience.issued_by} |
390
|
|
|
recipient={localizeFieldNonNull( |
391
|
|
|
locale, |
392
|
|
|
experience, |
393
|
|
|
"award_recipient_type", |
394
|
|
|
)} |
395
|
|
|
relevantSkills={relevantSkills} |
396
|
|
|
skills={skills} |
397
|
|
|
scope={localizeFieldNonNull( |
398
|
|
|
locale, |
399
|
|
|
experience, |
400
|
|
|
"award_recognition_type", |
401
|
|
|
)} |
402
|
|
|
showButtons |
403
|
|
|
showSkillDetails |
404
|
|
|
title={experience.title} |
405
|
|
|
/> |
406
|
|
|
); |
407
|
|
|
default: |
408
|
|
|
return ( |
409
|
|
|
<div |
410
|
|
|
data-c-background="gray(10)" |
411
|
|
|
data-c-radius="rounded" |
412
|
|
|
data-c-border="all(thin, solid, gray)" |
413
|
|
|
data-c-margin="top(1)" |
414
|
|
|
data-c-padding="all(1)" |
415
|
|
|
> |
416
|
|
|
<div data-c-align="base(center)"> |
417
|
|
|
<p data-c-color="stop"> |
418
|
|
|
{intl.formatMessage(messages.errorRenderingExperience)} |
419
|
|
|
</p> |
420
|
|
|
</div> |
421
|
|
|
</div> |
422
|
|
|
); |
423
|
|
|
} |
424
|
|
|
}; |
425
|
|
|
|
426
|
|
|
return ( |
427
|
|
|
<> |
428
|
|
|
<div data-c-container="medium"> |
429
|
|
|
<h2 data-c-heading="h2" data-c-margin="top(3) bottom(1)"> |
430
|
|
|
{intl.formatMessage(experienceMessages.heading)} |
431
|
|
|
</h2> |
432
|
|
|
<p data-c-margin="bottom(1)"> |
433
|
|
|
<FormattedMessage |
434
|
|
|
id="application.experience.preamble" |
435
|
|
|
defaultMessage="Use the buttons below to add experiences you want to share with the manager. Experiences you have added in the past also appear below, and you can edit them to link them to skills required for this job when necessary." |
436
|
|
|
description="First section of text on the experience step of the Application Timeline." |
437
|
|
|
/> |
438
|
|
|
</p> |
439
|
|
|
<div data-c-grid="gutter(all, 1)"> |
440
|
|
|
{essentialSkills.length > 0 && ( |
441
|
|
|
<div data-c-grid-item="tl(1of2)"> |
442
|
|
|
<p data-c-margin="bottom(.5)"> |
443
|
|
|
<FormattedMessage |
444
|
|
|
id="application.experience.essentialSkillsListIntro" |
445
|
|
|
description="Text before the list of essential skills on the experience step of the Application Timeline." |
446
|
|
|
defaultMessage="This job <span>requires</span> the following skills:" |
447
|
|
|
values={{ |
448
|
|
|
span: (chunks): React.ReactElement => ( |
449
|
|
|
<span data-c-font-weight="bold" data-c-color="c2"> |
450
|
|
|
{chunks} |
451
|
|
|
</span> |
452
|
|
|
), |
453
|
|
|
}} |
454
|
|
|
/> |
455
|
|
|
</p> |
456
|
|
|
<ul data-c-margin="bottom(1)"> |
457
|
|
|
{essentialSkills.map((skill) => ( |
458
|
|
|
<li key={skill.id}> |
459
|
|
|
{localizeFieldNonNull(locale, skill, "name")} |
460
|
|
|
</li> |
461
|
|
|
))} |
462
|
|
|
</ul> |
463
|
|
|
</div> |
464
|
|
|
)} |
465
|
|
|
{assetSkills.length > 0 && ( |
466
|
|
|
<div data-c-grid-item="tl(1of2)"> |
467
|
|
|
<p data-c-margin="bottom(.5)"> |
468
|
|
|
<FormattedMessage |
469
|
|
|
id="application.experience.assetSkillsListIntro" |
470
|
|
|
defaultMessage="These skills are beneficial, but not required:" |
471
|
|
|
description="Text before the list of asset skills on the experience step of the Application Timeline." |
472
|
|
|
/> |
473
|
|
|
</p> |
474
|
|
|
<ul data-c-margin="bottom(1)"> |
475
|
|
|
{assetSkills.map((skill) => ( |
476
|
|
|
<li key={skill.id}> |
477
|
|
|
{localizeFieldNonNull(locale, skill, "name")} |
478
|
|
|
</li> |
479
|
|
|
))} |
480
|
|
|
</ul> |
481
|
|
|
</div> |
482
|
|
|
)} |
483
|
|
|
</div> |
484
|
|
|
<p data-c-color="gray" data-c-margin="bottom(2)"> |
485
|
|
|
<FormattedMessage |
486
|
|
|
id="application.experience.softSkillsList" |
487
|
|
|
defaultMessage="Don't forget, {skill} will be evaluated later in the hiring process." |
488
|
|
|
description="List of soft skills that will be evaluated later." |
489
|
|
|
values={{ |
490
|
|
|
skill: ( |
491
|
|
|
<> |
492
|
|
|
{softSkills.map((skill, index) => { |
493
|
|
|
const and = " and "; |
494
|
|
|
const lastElement = index === softSkills.length - 1; |
495
|
|
|
return ( |
496
|
|
|
<> |
497
|
|
|
{lastElement && softSkills.length > 1 && and} |
498
|
|
|
<span key={skill.id} data-c-font-weight="bold"> |
499
|
|
|
{localizeFieldNonNull(locale, skill, "name")} |
500
|
|
|
</span> |
501
|
|
|
{!lastElement && softSkills.length > 2 && ", "} |
502
|
|
|
</> |
503
|
|
|
); |
504
|
|
|
})} |
505
|
|
|
</> |
506
|
|
|
), |
507
|
|
|
}} |
508
|
|
|
/> |
509
|
|
|
</p> |
510
|
|
|
{/* Experience Modal Buttons */} |
511
|
|
|
<div data-c-grid="gutter(all, 1)"> |
512
|
|
|
{Object.keys(modalButtons).map((id) => modalButton(modalButtons[id]))} |
513
|
|
|
</div> |
514
|
|
|
{/* Experience Accordion List */} |
515
|
|
|
{experiences && experiences.length > 0 ? ( |
516
|
|
|
<div className="experience-list" data-c-margin="top(2)"> |
517
|
|
|
<div data-c-accordion-group> |
518
|
|
|
{experiences.map((experience) => { |
519
|
|
|
const savedOptionalSkills = getSkillsOfExperience( |
520
|
|
|
experienceSkills, |
521
|
|
|
experience, |
522
|
|
|
assetSkills, |
523
|
|
|
); |
524
|
|
|
const savedRequiredSkills = getSkillsOfExperience( |
525
|
|
|
experienceSkills, |
526
|
|
|
experience, |
527
|
|
|
essentialSkills, |
528
|
|
|
); |
529
|
|
|
const relevantSkills: ExperienceSkill[] = |
530
|
|
|
savedRequiredSkills.map((skill) => { |
531
|
|
|
const experienceSkill = experienceSkills.find( |
532
|
|
|
({ experience_id, experience_type, skill_id }) => |
533
|
|
|
experience_id === experience.experienceable_id && |
534
|
|
|
skill_id === skill.id && |
535
|
|
|
experience_type === experience.type, |
536
|
|
|
); |
537
|
|
|
return { |
538
|
|
|
id: experienceSkill?.id ?? 0, |
539
|
|
|
skill_id: experienceSkill?.skill_id ?? 0, |
540
|
|
|
experience_id: experienceSkill?.experience_id ?? 0, |
541
|
|
|
experience_type: experienceSkill?.experience_type ?? "", |
542
|
|
|
justification: experienceSkill?.justification ?? "", |
543
|
|
|
created_at: experienceSkill?.created_at ?? new Date(), |
544
|
|
|
updated_at: experienceSkill?.updated_at ?? new Date(), |
545
|
|
|
}; |
546
|
|
|
}) ?? []; |
547
|
|
|
|
548
|
|
|
// Number of skills attached to Experience but are not part of the jobs skill criteria. |
549
|
|
|
const irrelevantSkillCount = |
550
|
|
|
experienceSkills.filter( |
551
|
|
|
(experienceSkill) => |
552
|
|
|
experienceSkill.experience_id === experience.id && |
553
|
|
|
experienceSkill.experience_type === experience.type, |
554
|
|
|
).length - |
555
|
|
|
(savedOptionalSkills.length + savedRequiredSkills.length); |
556
|
|
|
|
557
|
|
|
return experienceAccordion( |
558
|
|
|
experience, |
559
|
|
|
irrelevantSkillCount, |
560
|
|
|
relevantSkills, |
561
|
|
|
() => |
562
|
|
|
editExperience( |
563
|
|
|
experience, |
564
|
|
|
savedOptionalSkills, |
565
|
|
|
savedRequiredSkills, |
566
|
|
|
), |
567
|
|
|
() => deleteExperience(experience), |
568
|
|
|
); |
569
|
|
|
})} |
570
|
|
|
</div> |
571
|
|
|
</div> |
572
|
|
|
) : ( |
573
|
|
|
<div |
574
|
|
|
data-c-background="gray(10)" |
575
|
|
|
data-c-radius="rounded" |
576
|
|
|
data-c-border="all(thin, solid, gray)" |
577
|
|
|
data-c-margin="top(2)" |
578
|
|
|
data-c-padding="all(1)" |
579
|
|
|
> |
580
|
|
|
<div data-c-align="base(center)"> |
581
|
|
|
<p data-c-color="gray"> |
582
|
|
|
<FormattedMessage |
583
|
|
|
id="application.experience.noExperiences" |
584
|
|
|
defaultMessage="Looks like you don't have any experience added yet. Use the buttons above to add experience. Don't forget that experience will always be saved to your profile so that you can use it on future applications!" |
585
|
|
|
description="Message displayed when application has no experiences." |
586
|
|
|
/> |
587
|
|
|
</p> |
588
|
|
|
</div> |
589
|
|
|
</div> |
590
|
|
|
)} |
591
|
|
|
{disconnectedRequiredSkills && disconnectedRequiredSkills.length > 0 && ( |
592
|
|
|
<p data-c-color="stop" data-c-margin="top(2)"> |
593
|
|
|
<FormattedMessage |
594
|
|
|
id="application.experience.unconnectedSkills" |
595
|
|
|
defaultMessage="The following required skill(s) are not connected to your experience:" |
596
|
|
|
description="Message showing list of required skills that are not connected to a experience." |
597
|
|
|
/>{" "} |
598
|
|
|
{disconnectedRequiredSkills.map((skill) => ( |
599
|
|
|
<> |
600
|
|
|
<span |
601
|
|
|
data-c-tag="stop" |
602
|
|
|
data-c-radius="pill" |
603
|
|
|
data-c-font-size="small" |
604
|
|
|
> |
605
|
|
|
{localizeFieldNonNull(locale, skill, "name")} |
606
|
|
|
</span>{" "} |
607
|
|
|
</> |
608
|
|
|
))} |
609
|
|
|
</p> |
610
|
|
|
)} |
611
|
|
|
</div> |
612
|
|
|
<div data-c-container="medium" data-c-padding="tb(2)"> |
613
|
|
|
<hr data-c-hr="thin(c1)" data-c-margin="bottom(2)" /> |
614
|
|
|
<div data-c-grid="gutter"> |
615
|
|
|
<div |
616
|
|
|
data-c-alignment="base(centre) tp(left)" |
617
|
|
|
data-c-grid-item="tp(1of2)" |
618
|
|
|
> |
619
|
|
|
<button |
620
|
|
|
data-c-button="outline(c2)" |
621
|
|
|
data-c-radius="rounded" |
622
|
|
|
type="button" |
623
|
|
|
onClick={(): void => handleReturn()} |
624
|
|
|
> |
625
|
|
|
{intl.formatMessage(navigationMessages.return)} |
626
|
|
|
</button> |
627
|
|
|
</div> |
628
|
|
|
<div |
629
|
|
|
data-c-alignment="base(centre) tp(right)" |
630
|
|
|
data-c-grid-item="tp(1of2)" |
631
|
|
|
> |
632
|
|
|
<button |
633
|
|
|
data-c-button="outline(c2)" |
634
|
|
|
data-c-radius="rounded" |
635
|
|
|
type="button" |
636
|
|
|
onClick={(): void => handleQuit()} |
637
|
|
|
> |
638
|
|
|
{intl.formatMessage(navigationMessages.quit)} |
639
|
|
|
</button> |
640
|
|
|
<button |
641
|
|
|
data-c-button="solid(c1)" |
642
|
|
|
data-c-radius="rounded" |
643
|
|
|
data-c-margin="left(1)" |
644
|
|
|
type="button" |
645
|
|
|
onClick={(): void => handleContinue()} |
646
|
|
|
> |
647
|
|
|
{intl.formatMessage(navigationMessages.continue)} |
648
|
|
|
</button> |
649
|
|
|
</div> |
650
|
|
|
</div> |
651
|
|
|
</div> |
652
|
|
|
|
653
|
|
|
<div data-c-dialog-overlay={isModalVisible.visible ? "active" : ""} /> |
654
|
|
|
<EducationExperienceModal |
655
|
|
|
educationStatuses={educationStatuses} |
656
|
|
|
educationTypes={educationTypes} |
657
|
|
|
experienceEducation={experienceData as ExperienceEducation} |
658
|
|
|
experienceableId={experienceData?.experienceable_id ?? 0} |
659
|
|
|
experienceableType={ |
660
|
|
|
experienceData?.experienceable_type ?? "application" |
661
|
|
|
} |
662
|
|
|
jobId={jobId} |
663
|
|
|
jobClassification={jobClassification} |
664
|
|
|
modalId={modalButtons.education.id} |
665
|
|
|
onModalCancel={closeModal} |
666
|
|
|
onModalConfirm={submitExperience} |
667
|
|
|
optionalSkills={assetSkills} |
668
|
|
|
parentElement={modalRoot} |
669
|
|
|
requiredSkills={essentialSkills} |
670
|
|
|
savedOptionalSkills={experienceData?.savedOptionalSkills ?? []} |
671
|
|
|
savedRequiredSkills={experienceData?.savedRequiredSkills ?? []} |
672
|
|
|
visible={ |
673
|
|
|
isModalVisible.visible && |
674
|
|
|
isModalVisible.id === modalButtons.education.id |
675
|
|
|
} |
676
|
|
|
/> |
677
|
|
|
<WorkExperienceModal |
678
|
|
|
experienceWork={experienceData as ExperienceWork} |
679
|
|
|
experienceableId={experienceData?.experienceable_id ?? 0} |
680
|
|
|
experienceableType={ |
681
|
|
|
experienceData?.experienceable_type ?? "application" |
682
|
|
|
} |
683
|
|
|
jobId={jobId} |
684
|
|
|
jobClassification={jobClassification} |
685
|
|
|
modalId={modalButtons.work.id} |
686
|
|
|
onModalCancel={closeModal} |
687
|
|
|
onModalConfirm={submitExperience} |
688
|
|
|
optionalSkills={assetSkills} |
689
|
|
|
parentElement={modalRoot} |
690
|
|
|
requiredSkills={essentialSkills} |
691
|
|
|
savedOptionalSkills={experienceData?.savedOptionalSkills ?? []} |
692
|
|
|
savedRequiredSkills={experienceData?.savedRequiredSkills ?? []} |
693
|
|
|
visible={ |
694
|
|
|
isModalVisible.visible && isModalVisible.id === modalButtons.work.id |
695
|
|
|
} |
696
|
|
|
/> |
697
|
|
|
<CommunityExperienceModal |
698
|
|
|
experienceCommunity={experienceData as ExperienceCommunity} |
699
|
|
|
experienceableId={experienceData?.experienceable_id ?? 0} |
700
|
|
|
experienceableType={ |
701
|
|
|
experienceData?.experienceable_type ?? "application" |
702
|
|
|
} |
703
|
|
|
jobId={jobId} |
704
|
|
|
jobClassification={jobClassification} |
705
|
|
|
modalId={modalButtons.community.id} |
706
|
|
|
onModalCancel={closeModal} |
707
|
|
|
onModalConfirm={submitExperience} |
708
|
|
|
optionalSkills={assetSkills} |
709
|
|
|
parentElement={modalRoot} |
710
|
|
|
requiredSkills={essentialSkills} |
711
|
|
|
savedOptionalSkills={experienceData?.savedOptionalSkills ?? []} |
712
|
|
|
savedRequiredSkills={experienceData?.savedRequiredSkills ?? []} |
713
|
|
|
visible={ |
714
|
|
|
isModalVisible.visible && |
715
|
|
|
isModalVisible.id === modalButtons.community.id |
716
|
|
|
} |
717
|
|
|
/> |
718
|
|
|
<PersonalExperienceModal |
719
|
|
|
experiencePersonal={experienceData as ExperiencePersonal} |
720
|
|
|
experienceableId={experienceData?.experienceable_id ?? 0} |
721
|
|
|
experienceableType={ |
722
|
|
|
experienceData?.experienceable_type ?? "application" |
723
|
|
|
} |
724
|
|
|
jobId={jobId} |
725
|
|
|
jobClassification={jobClassification} |
726
|
|
|
modalId={modalButtons.personal.id} |
727
|
|
|
onModalCancel={closeModal} |
728
|
|
|
onModalConfirm={submitExperience} |
729
|
|
|
optionalSkills={assetSkills} |
730
|
|
|
parentElement={modalRoot} |
731
|
|
|
requiredSkills={essentialSkills} |
732
|
|
|
savedOptionalSkills={experienceData?.savedOptionalSkills ?? []} |
733
|
|
|
savedRequiredSkills={experienceData?.savedRequiredSkills ?? []} |
734
|
|
|
visible={ |
735
|
|
|
isModalVisible.visible && |
736
|
|
|
isModalVisible.id === modalButtons.personal.id |
737
|
|
|
} |
738
|
|
|
/> |
739
|
|
|
<AwardExperienceModal |
740
|
|
|
experienceAward={experienceData as ExperienceAward} |
741
|
|
|
experienceableId={experienceData?.experienceable_id ?? 0} |
742
|
|
|
experienceableType={ |
743
|
|
|
experienceData?.experienceable_type ?? "application" |
744
|
|
|
} |
745
|
|
|
jobId={jobId} |
746
|
|
|
jobClassification={jobClassification} |
747
|
|
|
modalId={modalButtons.award.id} |
748
|
|
|
onModalCancel={closeModal} |
749
|
|
|
onModalConfirm={submitExperience} |
750
|
|
|
optionalSkills={assetSkills} |
751
|
|
|
parentElement={modalRoot} |
752
|
|
|
recipientTypes={recipientTypes} |
753
|
|
|
recognitionTypes={recognitionTypes} |
754
|
|
|
requiredSkills={essentialSkills} |
755
|
|
|
savedOptionalSkills={experienceData?.savedOptionalSkills ?? []} |
756
|
|
|
savedRequiredSkills={experienceData?.savedRequiredSkills ?? []} |
757
|
|
|
visible={ |
758
|
|
|
isModalVisible.visible && isModalVisible.id === modalButtons.award.id |
759
|
|
|
} |
760
|
|
|
/> |
761
|
|
|
</> |
762
|
|
|
); |
763
|
|
|
}; |
764
|
|
|
|
765
|
|
|
export default MyExperience; |
766
|
|
|
|