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