Skip to content

Commit 1a64664

Browse files
committed
PM-3834 Declutter engagement list
1 parent 357800f commit 1a64664

2 files changed

Lines changed: 2 additions & 269 deletions

File tree

src/shared/components/engagement-listing/EngagementCard/index.jsx

Lines changed: 1 addition & 268 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import IconBlackDuration from 'assets/images/icon-black-calendar.svg';
66
import IconBlackLocation from 'assets/images/icon-black-location.svg';
77
import IconBlackPayment from 'assets/images/icon-black-payment.svg';
88
import iconBlackSkills from 'assets/images/icon-skills.png';
9-
import IconTimezone from 'assets/images/icon-timezone.svg';
109

1110
import './style.scss';
1211

@@ -22,12 +21,6 @@ const WORKLOAD_LABELS = {
2221
FRACTIONAL: 'Fractional',
2322
};
2423

25-
const ANTICIPATED_START_LABELS = {
26-
IMMEDIATE: 'Immediate',
27-
FEW_DAYS: 'Few Days',
28-
FEW_WEEKS: 'Few Weeks',
29-
};
30-
3124
const STATUS_LABELS = {
3225
OPEN: 'Open',
3326
PENDING_ASSIGNMENT: 'Pending Assignment',
@@ -36,62 +29,20 @@ const STATUS_LABELS = {
3629
CLOSED: 'Closed',
3730
};
3831

39-
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
40-
const UNKNOWN_SKILL_LABEL = 'Unknown skill';
4132
const DEFAULT_LOCALE = 'en-US';
42-
const SIMPLE_TZ_PATTERN = /^[A-Za-z]{2,6}$/;
43-
const OFFSET_TZ_PATTERN = /^(?:UTC|GMT)?\s*([+-])\s*(\d{1,2})(?::?(\d{2}))?$/i;
44-
const BARE_OFFSET_PATTERN = /^([+-])(\d{2})(?::?(\d{2}))$/;
45-
const TIMEZONE_ABBREVIATION_LONG_NAMES = {
46-
ACDT: 'Australian Central Daylight Time',
47-
ACST: 'Australian Central Standard Time',
48-
AEDT: 'Australian Eastern Daylight Time',
49-
AEST: 'Australian Eastern Standard Time',
50-
AKDT: 'Alaska Daylight Time',
51-
AKST: 'Alaska Standard Time',
52-
AWST: 'Australian Western Standard Time',
53-
BST: 'British Summer Time',
54-
CDT: 'Central Daylight Time',
55-
CEST: 'Central European Summer Time',
56-
CET: 'Central European Standard Time',
57-
CST: 'Central Standard Time',
58-
EDT: 'Eastern Daylight Time',
59-
EEST: 'Eastern European Summer Time',
60-
EET: 'Eastern European Standard Time',
61-
EST: 'Eastern Standard Time',
62-
GMT: 'Greenwich Mean Time',
63-
HST: 'Hawaii-Aleutian Standard Time',
64-
IST: 'India Standard Time',
65-
JST: 'Japan Standard Time',
66-
KST: 'Korea Standard Time',
67-
MDT: 'Mountain Daylight Time',
68-
MST: 'Mountain Standard Time',
69-
NZDT: 'New Zealand Daylight Time',
70-
NZST: 'New Zealand Standard Time',
71-
PDT: 'Pacific Daylight Time',
72-
PST: 'Pacific Standard Time',
73-
SAST: 'South Africa Standard Time',
74-
UTC: 'Coordinated Universal Time',
75-
WEST: 'Western European Summer Time',
76-
WET: 'Western European Standard Time',
77-
};
33+
7834
const REGION_NAME_OVERRIDES = {
7935
UK: 'United Kingdom',
8036
};
8137
const regionDisplayNames = typeof Intl !== 'undefined' && typeof Intl.DisplayNames === 'function'
8238
? new Intl.DisplayNames([DEFAULT_LOCALE], { type: 'region' })
8339
: null;
84-
let timezoneAbbreviationMap;
8540

8641
function asArray(value) {
8742
if (!value) return [];
8843
return Array.isArray(value) ? value : [value];
8944
}
9045

91-
function isUuid(value) {
92-
return typeof value === 'string' && UUID_PATTERN.test(value);
93-
}
94-
9546
function toTitleCase(value) {
9647
return value
9748
.toLowerCase()
@@ -127,21 +78,6 @@ function normalizeLabel(value, normalizedMap) {
12778
return spaced || raw;
12879
}
12980

130-
function normalizeSkillLabel(skill) {
131-
if (!skill) return null;
132-
133-
if (typeof skill === 'object' && skill !== null) {
134-
const label = skill.name || skill.title;
135-
if (label) return String(label);
136-
const skillId = skill.id || skill.value;
137-
if (isUuid(skillId)) return UNKNOWN_SKILL_LABEL;
138-
return skillId ? String(skillId) : null;
139-
}
140-
141-
if (isUuid(skill)) return UNKNOWN_SKILL_LABEL;
142-
return String(skill);
143-
}
144-
14581
function normalizeLocationValue(value) {
14682
if (!value) return null;
14783
if (typeof value === 'object' && value !== null) {
@@ -173,154 +109,6 @@ function normalizeRegionValue(value) {
173109
return trimmed;
174110
}
175111

176-
function getIntlTimeZoneName(timeZone, style) {
177-
if (typeof Intl === 'undefined' || typeof Intl.DateTimeFormat !== 'function') {
178-
return null;
179-
}
180-
181-
try {
182-
const formatter = new Intl.DateTimeFormat(DEFAULT_LOCALE, {
183-
timeZone,
184-
timeZoneName: style,
185-
});
186-
187-
if (typeof formatter.formatToParts !== 'function') {
188-
return null;
189-
}
190-
191-
const parts = formatter.formatToParts(new Date());
192-
const namePart = parts.find(part => part.type === 'timeZoneName');
193-
return namePart && namePart.value ? namePart.value : null;
194-
} catch (error) {
195-
return null;
196-
}
197-
}
198-
199-
function getMomentTimeZoneName(timeZone) {
200-
if (!moment || !moment.tz || !moment.tz.zone) {
201-
return null;
202-
}
203-
204-
if (!moment.tz.zone(timeZone)) {
205-
return null;
206-
}
207-
208-
try {
209-
return moment.tz(new Date(), timeZone).format('z');
210-
} catch (error) {
211-
return null;
212-
}
213-
}
214-
215-
function getTimeZoneAbbreviationMap() {
216-
if (timezoneAbbreviationMap) return timezoneAbbreviationMap;
217-
timezoneAbbreviationMap = new Map();
218-
if (!moment || !moment.tz || typeof moment.tz.names !== 'function') {
219-
return timezoneAbbreviationMap;
220-
}
221-
222-
moment.tz.names().forEach((zoneName) => {
223-
try {
224-
const abbr = moment.tz(new Date(), zoneName).format('z');
225-
if (!abbr) return;
226-
const normalized = abbr.toUpperCase();
227-
if (!timezoneAbbreviationMap.has(normalized)) {
228-
timezoneAbbreviationMap.set(normalized, zoneName);
229-
}
230-
} catch (error) {
231-
// ignore invalid timezone data
232-
}
233-
});
234-
235-
return timezoneAbbreviationMap;
236-
}
237-
238-
function resolveTimeZoneAbbreviationName(abbreviation) {
239-
const normalized = abbreviation.toUpperCase();
240-
if (TIMEZONE_ABBREVIATION_LONG_NAMES[normalized]) {
241-
return TIMEZONE_ABBREVIATION_LONG_NAMES[normalized];
242-
}
243-
244-
const map = getTimeZoneAbbreviationMap();
245-
const zoneName = map.get(normalized);
246-
if (!zoneName) return null;
247-
248-
return (
249-
getIntlTimeZoneName(zoneName, 'long')
250-
|| getIntlTimeZoneName(zoneName, 'longGeneric')
251-
);
252-
}
253-
254-
function formatUtcOffset(sign, hours, minutes) {
255-
const hourValue = Number(hours);
256-
const minuteValue = Number(minutes || 0);
257-
258-
if (Number.isNaN(hourValue) || Number.isNaN(minuteValue)) {
259-
return null;
260-
}
261-
262-
const normalizedHours = String(Math.abs(hourValue)).padStart(2, '0');
263-
const normalizedMinutes = String(Math.abs(minuteValue)).padStart(2, '0');
264-
const suffix = normalizedMinutes !== '00' ? `:${normalizedMinutes}` : '';
265-
266-
return `UTC${sign}${normalizedHours}${suffix}`;
267-
}
268-
269-
function normalizeUtcOffset(value) {
270-
if (!value) return null;
271-
const normalized = String(value).trim();
272-
if (!normalized) return null;
273-
274-
if (/^(utc|gmt)$/i.test(normalized)) {
275-
return 'UTC';
276-
}
277-
278-
const offsetMatch = normalized.match(OFFSET_TZ_PATTERN);
279-
if (offsetMatch) {
280-
return formatUtcOffset(offsetMatch[1], offsetMatch[2], offsetMatch[3]);
281-
}
282-
283-
const bareMatch = normalized.match(BARE_OFFSET_PATTERN);
284-
if (bareMatch) {
285-
return formatUtcOffset(bareMatch[1], bareMatch[2], bareMatch[3]);
286-
}
287-
288-
return null;
289-
}
290-
291-
function normalizeTimezoneValue(value) {
292-
const normalizedValue = normalizeLocationValue(value);
293-
if (!normalizedValue) return null;
294-
295-
const trimmed = normalizedValue.trim();
296-
if (!trimmed) return null;
297-
298-
if (trimmed.toLowerCase() === 'any') {
299-
return 'Any';
300-
}
301-
302-
const longName = getIntlTimeZoneName(trimmed, 'long') || getIntlTimeZoneName(trimmed, 'longGeneric');
303-
if (longName) {
304-
return longName;
305-
}
306-
307-
const offset = normalizeUtcOffset(trimmed);
308-
if (offset) {
309-
return offset;
310-
}
311-
312-
if (SIMPLE_TZ_PATTERN.test(trimmed)) {
313-
return resolveTimeZoneAbbreviationName(trimmed) || trimmed.toUpperCase();
314-
}
315-
316-
const fallbackShortName = getMomentTimeZoneName(trimmed) || getIntlTimeZoneName(trimmed, 'short');
317-
if (fallbackShortName) {
318-
return fallbackShortName;
319-
}
320-
321-
return trimmed;
322-
}
323-
324112
function uniqNormalizedStrings(values) {
325113
const seen = new Set();
326114
return values.reduce((acc, value) => {
@@ -365,10 +153,6 @@ function getWorkloadDisplay(workload) {
365153
return normalizeLabel(workload, WORKLOAD_LABELS);
366154
}
367155

368-
function getAnticipatedStartDisplay(value) {
369-
return normalizeLabel(value, ANTICIPATED_START_LABELS);
370-
}
371-
372156
function getCompensationDisplay(compensationRange) {
373157
if (typeof compensationRange === 'object' && compensationRange !== null) {
374158
const label = compensationRange.name || compensationRange.title;
@@ -406,17 +190,10 @@ function EngagementCard({ engagement }) {
406190
role,
407191
workload,
408192
compensationRange,
409-
skills: engagementSkills,
410-
requiredSkills,
411-
skillsets,
412193
location,
413194
locations: engagementLocations,
414-
timezone,
415-
timezones,
416-
timeZones,
417195
countries,
418196
status,
419-
anticipatedStart,
420197
nanoId,
421198
id,
422199
engagementId,
@@ -431,44 +208,18 @@ function EngagementCard({ engagement }) {
431208
durationWeeks,
432209
durationMonths,
433210
);
434-
const anticipatedStartText = getAnticipatedStartDisplay(anticipatedStart);
435-
436-
const skillsSource = [engagementSkills, requiredSkills, skillsets]
437-
.find(value => Array.isArray(value) && value.length)
438-
|| engagementSkills
439-
|| requiredSkills
440-
|| skillsets;
441-
const skills = Array.from(new Set(
442-
asArray(skillsSource)
443-
.map(normalizeSkillLabel)
444-
.filter(Boolean),
445-
));
446-
const skillsText = skills.length
447-
? skills.slice(0, 2).join(', ')
448-
: 'Not Specified';
449-
const limitedSkillsText = skills.length > 2
450-
? `${skillsText},...`
451-
: skillsText;
452211

453212
const baseLocations = [
454213
...asArray(location),
455214
...asArray(engagementLocations),
456215
]
457216
.map(normalizeRegionValue)
458217
.filter(Boolean);
459-
const timezoneValues = [
460-
...asArray(timezone),
461-
...asArray(timezones),
462-
...asArray(timeZones),
463-
]
464-
.map(normalizeTimezoneValue)
465-
.filter(Boolean);
466218
const countryValues = asArray(countries)
467219
.map(normalizeRegionValue)
468220
.filter(Boolean);
469221
const isAnyValue = value => value.trim().toLowerCase() === 'any';
470222
const hasAnyLocation = [...baseLocations, ...countryValues].some(isAnyValue);
471-
const hasAnyTimezone = timezoneValues.some(isAnyValue);
472223
const filteredBaseLocations = baseLocations.filter(value => !isAnyValue(value));
473224
const filteredCountries = countryValues.filter(value => !isAnyValue(value));
474225
const locations = uniqNormalizedStrings([
@@ -477,15 +228,6 @@ function EngagementCard({ engagement }) {
477228
...filteredCountries,
478229
]);
479230
const locationText = locations.length ? locations.join(', ') : 'Remote';
480-
const filteredTimezones = uniqNormalizedStrings(
481-
timezoneValues.filter(value => !isAnyValue(value)),
482-
);
483-
let timezoneText = 'Not Specified';
484-
if (filteredTimezones.length) {
485-
timezoneText = filteredTimezones.join(', ');
486-
} else if (hasAnyTimezone) {
487-
timezoneText = 'Any';
488-
}
489231

490232
const resolvedEngagementId = nanoId || id || engagementId;
491233
const engagementLink = resolvedEngagementId
@@ -505,12 +247,6 @@ function EngagementCard({ engagement }) {
505247
<div styleName="icon-val">
506248
<img src={iconBlackSkills} alt="role-icon" /> {getRoleDisplay(role)}
507249
</div>
508-
<div styleName="icon-val">
509-
<img src={iconBlackSkills} alt="skills-icon" /> {limitedSkillsText}
510-
</div>
511-
<div styleName="icon-val">
512-
<IconTimezone /> {timezoneText}
513-
</div>
514250
<div styleName="icon-val">
515251
<IconBlackLocation /> {locationText}
516252
</div>
@@ -523,9 +259,6 @@ function EngagementCard({ engagement }) {
523259
<div styleName="icon-val">
524260
<IconBlackDuration /> {durationText}
525261
</div>
526-
<div styleName="icon-val">
527-
<IconBlackDuration /> {`Anticipated start: ${anticipatedStartText}`}
528-
</div>
529262
<div styleName="row-btn">
530263
<a styleName="primary-green-md" href={engagementLink}>
531264
VIEW DETAILS

src/shared/components/engagement-listing/EngagementCard/style.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666

6767
.job-infos {
6868
display: grid;
69-
grid-template-columns: 1.2fr 1fr 1.2fr 1.2fr 1fr 1.3fr 0.9fr 1fr 141px;
69+
grid-template-columns: 1.2fr 1.2fr 1fr 1.3fr 0.9fr 141px;
7070
column-gap: 20px;
7171
align-items: center;
7272

0 commit comments

Comments
 (0)