Passed
Push — feature/classification-educati... ( 76b71d )
by
unknown
06:50
created

resources/assets/js/components/Application/ExperienceAccordions/BaseExperienceAccordion.tsx   A

Complexity

Total Complexity 6
Complexity/F 0

Size

Lines of Code 332
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 6
eloc 269
mnd 6
bc 6
fnc 0
dl 0
loc 332
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import React, { ReactElement, useState } from "react";
2
import { FormattedMessage, useIntl, IntlShape } from "react-intl";
3
import { accordionMessages } from "../applicationMessages";
4
import {
5
  Locales,
6
  getLocale,
7
  localizeFieldNonNull,
8
} from "../../../helpers/localize";
9
import { readableDate } from "../../../helpers/dates";
10
import { ExperienceSkill, Skill } from "../../../models/types";
11
import { getId, mapToObject, hasKey } from "../../../helpers/queries";
12
13
export const titleBarDateRange = (
14
  startDate: Date,
15
  endDate: Date | null,
16
  isActive: boolean,
17
  intl: IntlShape,
18
  locale: Locales,
19
): React.ReactElement => {
20
  let dateRange;
21
22
  if (isActive || endDate === null) {
23
    dateRange = intl.formatMessage(accordionMessages.dateRangeCurrent, {
24
      startDate: readableDate(locale, startDate),
25
    });
26
  } else {
27
    dateRange = intl.formatMessage(accordionMessages.dateRange, {
28
      startDate: readableDate(locale, startDate),
29
      endDate: readableDate(locale, endDate),
30
    });
31
  }
32
33
  return (
34
    <p data-c-margin="top(quarter)" data-c-colour="c1" data-c-font-size="small">
35
      {dateRange}
36
    </p>
37
  );
38
};
39
40
interface BaseExperienceAccordionProps {
41
  iconClass: string;
42
  title: ReactElement | string;
43
  relevantSkills: ExperienceSkill[];
44
  skills: Skill[];
45
  irrelevantSkillCount: number;
46
  isEducationJustification: boolean;
47
  details: ReactElement;
48
  showSkillDetails: boolean;
49
  showButtons: boolean;
50
  handleDelete: () => Promise<void>;
51
  handleEdit: () => void;
52
}
53
54
export const BaseExperienceAccordion: React.FC<BaseExperienceAccordionProps> = ({
55
  iconClass,
56
  title,
57
  relevantSkills,
58
  skills,
59
  irrelevantSkillCount,
60
  isEducationJustification,
61
  details,
62
  showSkillDetails,
63
  showButtons,
64
  handleDelete,
65
  handleEdit,
66
}) => {
67
  const intl = useIntl();
68
  const locale = getLocale(intl.locale);
69
  const [isExpanded, setIsExpanded] = useState(false);
70
  const [isDeleting, setIsDeleting] = useState(false);
71
72
  const relevantSkillCount = relevantSkills.length;
73
  const skillsById = mapToObject(skills, getId);
74
75
  return (
76
    <div
77
      data-c-accordion
78
      data-c-background="white(100)"
79
      data-c-card=""
80
      data-c-margin="bottom(.5)"
81
      className={`${isExpanded && "active"}`}
82
    >
83
      <button
84
        tabIndex={0}
85
        aria-expanded={isExpanded}
86
        data-c-accordion-trigger
87
        type="button"
88
        onClick={(): void => {
89
          setIsExpanded(!isExpanded);
90
        }}
91
      >
92
        <div data-c-grid="">
93
          <div data-c-grid-item="base(1of4) tl(1of6) equal-col">
94
            <div className="experience-type-indicator">
95
              <i
96
                className={`fas ${iconClass}`}
97
                data-c-color="c1"
98
                data-c-font-size="h4"
99
              />
100
            </div>
101
          </div>
102
          <div data-c-grid-item="base(3of4) tl(5of6)">
103
            <div data-c-padding="all(1)">
104
              <div data-c-grid="middle">
105
                <div data-c-grid-item="tl(3of4)">{title}</div>
106
                <div data-c-grid-item="tl(1of4)" data-c-align="base(left)">
107
                  <FormattedMessage
108
                    id="application.experienceAccordion.skillCount"
109
                    defaultMessage="{skillCount, plural, =0 {No related skills} one {# related skill} other {# related skills}} {isEducationJustification, select, true {/ Education Requirement} false {}}"
110
                    description="Displays the number of required skills this relates to, and whether it's used to meed education requirements."
111
                    values={{
112
                      skillCount: relevantSkillCount,
113
                      isEducationJustification,
114
                    }}
115
                  />
116
                </div>
117
              </div>
118
            </div>
119
          </div>
120
        </div>
121
        <span data-c-visibility="invisible">
122
          {intl.formatMessage(accordionMessages.expand)}
123
        </span>
124
        <i
125
          aria-hidden="true"
126
          className="fas fa-angle-down"
127
          data-c-accordion-add=""
128
          data-c-colour="black"
129
        />
130
        <i
131
          aria-hidden="true"
132
          className="fas fa-angle-up"
133
          data-c-accordion-remove=""
134
          data-c-colour="black"
135
        />
136
      </button>
137
      <div
138
        aria-hidden="true"
139
        data-c-accordion-content=""
140
        data-c-background="gray(10)"
141
        data-c-padding="bottom(2)"
142
      >
143
        <hr data-c-hr="thin(gray)" data-c-margin="bottom(2)" />
144
        <div data-c-padding="lr(2)">
145
          <div data-c-grid="gutter(all, 1)">
146
            <div data-c-grid-item="base(1of1)">
147
              <div data-c-grid="gutter(all, 1)">
148
                <div data-c-grid-item="base(1of1)">
149
                  <h4 data-c-color="c2" data-c-font-weight="bold">
150
                    <FormattedMessage
151
                      id="application.experienceAccordion.detailsTitle"
152
                      defaultMessage="Details of this Experience"
153
                      description="Subtitle of the details section."
154
                    />
155
                  </h4>
156
                </div>
157
                {details}
158
              </div>
159
            </div>
160
            <div data-c-grid-item="base(1of1)">
161
              <h4
162
                data-c-color="c2"
163
                data-c-font-weight="bold"
164
                data-c-margin="top(1) bottom(.5)"
165
              >
166
                <FormattedMessage
167
                  id="application.experienceAccordion.skillsTitle"
168
                  defaultMessage="Skills for this Job"
169
                  description="Subtitle of the skills section."
170
                />
171
              </h4>
172
              <div data-c-grid="gutter(all, 1)">
173
                {showSkillDetails ? (
174
                  relevantSkills.map((experienceSkill) => {
175
                    const skill = hasKey(skillsById, experienceSkill.skill_id)
176
                      ? skillsById[experienceSkill.skill_id]
177
                      : null;
178
                    if (skill === null) {
179
                      return null;
180
                    }
181
182
                    return (
183
                      <div key={skill.id} data-c-grid-item="base(1of1)">
184
                        <p>
185
                          <span
186
                            data-c-tag="c1"
187
                            data-c-radius="pill"
188
                            data-c-font-size="small"
189
                          >
190
                            {localizeFieldNonNull(locale, skill, "name")}
191
                          </span>
192
                        </p>
193
                        <p data-c-font-style="italic" data-c-margin="top(.5)">
194
                          {experienceSkill.justification}
195
                        </p>
196
                      </div>
197
                    );
198
                  })
199
                ) : (
200
                  <div data-c-grid-item="base(1of1)">
201
                    {relevantSkills.map((experienceSkill) => {
202
                      const skill = hasKey(skillsById, experienceSkill.skill_id)
203
                        ? skillsById[experienceSkill.skill_id]
204
                        : null;
205
                      if (skill === null) {
206
                        return null;
207
                      }
208
                      return (
209
                        <span
210
                          key={skill.id}
211
                          data-c-tag="c1"
212
                          data-c-radius="pill"
213
                          data-c-font-size="small"
214
                        >
215
                          {localizeFieldNonNull(locale, skill, "name")}
216
                        </span>
217
                      );
218
                    })}
219
                  </div>
220
                )}
221
                {irrelevantSkillCount > 0 && (
222
                  <div data-c-grid-item="base(1of1)">
223
                    <p
224
                      data-c-font-size="small"
225
                      data-c-color="gray"
226
                      data-c-margin="bottom(1)"
227
                    >
228
                      <FormattedMessage
229
                        id="application.experienceAccordion.irrelevantSkillCount"
230
                        defaultMessage="There {skillCount, plural, one {is <b>#</b> other unrelated skill} other {are <b>#</b> other unrelated skills}} attached to this experience. You can see {skillCount, plural, one {it} other {them}} on your profile."
231
                        description="Say how many skills unrelated to this job are associated with this experience."
232
                        values={{
233
                          skillCount: irrelevantSkillCount,
234
                          b: (...chunks) => (
235
                            <span data-c-font-weight="bold">{chunks}</span>
236
                          ),
237
                        }}
238
                      />
239
                    </p>
240
                  </div>
241
                )}
242
                {irrelevantSkillCount === 0 && relevantSkillCount === 0 && (
243
                  <div data-c-grid-item="base(1of1)">
244
                    <p data-c-color="gray" data-c-margin="bottom(1)">
245
                      <FormattedMessage
246
                        id="application.experienceAccordion.noSkills"
247
                        defaultMessage="You don't have any skills attached to this experience."
248
                        description="Message to show if experience has no associated skills at all."
249
                      />
250
                    </p>
251
                  </div>
252
                )}
253
              </div>
254
            </div>
255
            {isEducationJustification && (
256
              <div data-c-grid-item="base(1of1)">
257
                <h4
258
                  data-c-color="c2"
259
                  data-c-font-weight="bold"
260
                  data-c-margin="bottom(.5)"
261
                >
262
                  <i
263
                    className="fas fa-check-circle"
264
                    data-c-margin="right(.25)"
265
                    data-c-color="go"
266
                  />
267
                  <FormattedMessage
268
                    id="application.experienceAccordion.educationRequirement"
269
                    defaultMessage="Education Requirement"
270
                  />
271
                </h4>
272
                <p data-c-margin="bottom(1)">
273
                  <FormattedMessage
274
                    id="application.experienceAccordion.educationRequirmentDescription"
275
                    defaultMessage="You've selected this experience as an indicator of how you meet the education requirements for this job."
276
                    description="Explanation of what it means that this experience meets an education requirement."
277
                  />
278
                </p>
279
              </div>
280
            )}
281
          </div>
282
        </div>
283
        {showButtons && (
284
          <div data-c-padding="top(1) lr(2)">
285
            <div data-c-grid="gutter(all, 1) middle">
286
              <div
287
                data-c-grid-item="tp(1of2)"
288
                data-c-align="base(center) tp(left)"
289
              >
290
                <button
291
                  data-c-button="outline(c1)"
292
                  data-c-radius="rounded"
293
                  type="button"
294
                  disabled={isDeleting}
295
                  onClick={(): void => {
296
                    setIsDeleting(true);
297
                    handleDelete().finally(() => {
298
                      setIsDeleting(false);
299
                    });
300
                  }}
301
                >
302
                  <FormattedMessage
303
                    id="application.experienceAccordion.deleteButton"
304
                    defaultMessage="Delete Experience"
305
                  />
306
                </button>
307
              </div>
308
              <div
309
                data-c-grid-item="tp(1of2)"
310
                data-c-align="base(center) tp(right)"
311
              >
312
                <button
313
                  data-c-button="solid(c1)"
314
                  data-c-radius="rounded"
315
                  type="button"
316
                  disabled={isDeleting}
317
                  onClick={handleEdit}
318
                >
319
                  <FormattedMessage
320
                    id="application.experienceAccordion.editButton"
321
                    defaultMessage="Edit Experience"
322
                  />
323
                </button>
324
              </div>
325
            </div>
326
          </div>
327
        )}
328
      </div>
329
    </div>
330
  );
331
};
332