diff --git a/src/shared/components/engagement-listing/EngagementCard/index.jsx b/src/shared/components/engagement-listing/EngagementCard/index.jsx index 2378a419e..090d3503c 100644 --- a/src/shared/components/engagement-listing/EngagementCard/index.jsx +++ b/src/shared/components/engagement-listing/EngagementCard/index.jsx @@ -6,7 +6,6 @@ import IconBlackDuration from 'assets/images/icon-black-calendar.svg'; import IconBlackLocation from 'assets/images/icon-black-location.svg'; import IconBlackPayment from 'assets/images/icon-black-payment.svg'; import iconBlackSkills from 'assets/images/icon-skills.png'; -import IconTimezone from 'assets/images/icon-timezone.svg'; import './style.scss'; @@ -22,12 +21,6 @@ const WORKLOAD_LABELS = { FRACTIONAL: 'Fractional', }; -const ANTICIPATED_START_LABELS = { - IMMEDIATE: 'Immediate', - FEW_DAYS: 'Few Days', - FEW_WEEKS: 'Few Weeks', -}; - const STATUS_LABELS = { OPEN: 'Open', PENDING_ASSIGNMENT: 'Pending Assignment', @@ -36,62 +29,20 @@ const STATUS_LABELS = { CLOSED: 'Closed', }; -const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; -const UNKNOWN_SKILL_LABEL = 'Unknown skill'; const DEFAULT_LOCALE = 'en-US'; -const SIMPLE_TZ_PATTERN = /^[A-Za-z]{2,6}$/; -const OFFSET_TZ_PATTERN = /^(?:UTC|GMT)?\s*([+-])\s*(\d{1,2})(?::?(\d{2}))?$/i; -const BARE_OFFSET_PATTERN = /^([+-])(\d{2})(?::?(\d{2}))$/; -const TIMEZONE_ABBREVIATION_LONG_NAMES = { - ACDT: 'Australian Central Daylight Time', - ACST: 'Australian Central Standard Time', - AEDT: 'Australian Eastern Daylight Time', - AEST: 'Australian Eastern Standard Time', - AKDT: 'Alaska Daylight Time', - AKST: 'Alaska Standard Time', - AWST: 'Australian Western Standard Time', - BST: 'British Summer Time', - CDT: 'Central Daylight Time', - CEST: 'Central European Summer Time', - CET: 'Central European Standard Time', - CST: 'Central Standard Time', - EDT: 'Eastern Daylight Time', - EEST: 'Eastern European Summer Time', - EET: 'Eastern European Standard Time', - EST: 'Eastern Standard Time', - GMT: 'Greenwich Mean Time', - HST: 'Hawaii-Aleutian Standard Time', - IST: 'India Standard Time', - JST: 'Japan Standard Time', - KST: 'Korea Standard Time', - MDT: 'Mountain Daylight Time', - MST: 'Mountain Standard Time', - NZDT: 'New Zealand Daylight Time', - NZST: 'New Zealand Standard Time', - PDT: 'Pacific Daylight Time', - PST: 'Pacific Standard Time', - SAST: 'South Africa Standard Time', - UTC: 'Coordinated Universal Time', - WEST: 'Western European Summer Time', - WET: 'Western European Standard Time', -}; + const REGION_NAME_OVERRIDES = { UK: 'United Kingdom', }; const regionDisplayNames = typeof Intl !== 'undefined' && typeof Intl.DisplayNames === 'function' ? new Intl.DisplayNames([DEFAULT_LOCALE], { type: 'region' }) : null; -let timezoneAbbreviationMap; function asArray(value) { if (!value) return []; return Array.isArray(value) ? value : [value]; } -function isUuid(value) { - return typeof value === 'string' && UUID_PATTERN.test(value); -} - function toTitleCase(value) { return value .toLowerCase() @@ -127,21 +78,6 @@ function normalizeLabel(value, normalizedMap) { return spaced || raw; } -function normalizeSkillLabel(skill) { - if (!skill) return null; - - if (typeof skill === 'object' && skill !== null) { - const label = skill.name || skill.title; - if (label) return String(label); - const skillId = skill.id || skill.value; - if (isUuid(skillId)) return UNKNOWN_SKILL_LABEL; - return skillId ? String(skillId) : null; - } - - if (isUuid(skill)) return UNKNOWN_SKILL_LABEL; - return String(skill); -} - function normalizeLocationValue(value) { if (!value) return null; if (typeof value === 'object' && value !== null) { @@ -173,154 +109,6 @@ function normalizeRegionValue(value) { return trimmed; } -function getIntlTimeZoneName(timeZone, style) { - if (typeof Intl === 'undefined' || typeof Intl.DateTimeFormat !== 'function') { - return null; - } - - try { - const formatter = new Intl.DateTimeFormat(DEFAULT_LOCALE, { - timeZone, - timeZoneName: style, - }); - - if (typeof formatter.formatToParts !== 'function') { - return null; - } - - const parts = formatter.formatToParts(new Date()); - const namePart = parts.find(part => part.type === 'timeZoneName'); - return namePart && namePart.value ? namePart.value : null; - } catch (error) { - return null; - } -} - -function getMomentTimeZoneName(timeZone) { - if (!moment || !moment.tz || !moment.tz.zone) { - return null; - } - - if (!moment.tz.zone(timeZone)) { - return null; - } - - try { - return moment.tz(new Date(), timeZone).format('z'); - } catch (error) { - return null; - } -} - -function getTimeZoneAbbreviationMap() { - if (timezoneAbbreviationMap) return timezoneAbbreviationMap; - timezoneAbbreviationMap = new Map(); - if (!moment || !moment.tz || typeof moment.tz.names !== 'function') { - return timezoneAbbreviationMap; - } - - moment.tz.names().forEach((zoneName) => { - try { - const abbr = moment.tz(new Date(), zoneName).format('z'); - if (!abbr) return; - const normalized = abbr.toUpperCase(); - if (!timezoneAbbreviationMap.has(normalized)) { - timezoneAbbreviationMap.set(normalized, zoneName); - } - } catch (error) { - // ignore invalid timezone data - } - }); - - return timezoneAbbreviationMap; -} - -function resolveTimeZoneAbbreviationName(abbreviation) { - const normalized = abbreviation.toUpperCase(); - if (TIMEZONE_ABBREVIATION_LONG_NAMES[normalized]) { - return TIMEZONE_ABBREVIATION_LONG_NAMES[normalized]; - } - - const map = getTimeZoneAbbreviationMap(); - const zoneName = map.get(normalized); - if (!zoneName) return null; - - return ( - getIntlTimeZoneName(zoneName, 'long') - || getIntlTimeZoneName(zoneName, 'longGeneric') - ); -} - -function formatUtcOffset(sign, hours, minutes) { - const hourValue = Number(hours); - const minuteValue = Number(minutes || 0); - - if (Number.isNaN(hourValue) || Number.isNaN(minuteValue)) { - return null; - } - - const normalizedHours = String(Math.abs(hourValue)).padStart(2, '0'); - const normalizedMinutes = String(Math.abs(minuteValue)).padStart(2, '0'); - const suffix = normalizedMinutes !== '00' ? `:${normalizedMinutes}` : ''; - - return `UTC${sign}${normalizedHours}${suffix}`; -} - -function normalizeUtcOffset(value) { - if (!value) return null; - const normalized = String(value).trim(); - if (!normalized) return null; - - if (/^(utc|gmt)$/i.test(normalized)) { - return 'UTC'; - } - - const offsetMatch = normalized.match(OFFSET_TZ_PATTERN); - if (offsetMatch) { - return formatUtcOffset(offsetMatch[1], offsetMatch[2], offsetMatch[3]); - } - - const bareMatch = normalized.match(BARE_OFFSET_PATTERN); - if (bareMatch) { - return formatUtcOffset(bareMatch[1], bareMatch[2], bareMatch[3]); - } - - return null; -} - -function normalizeTimezoneValue(value) { - const normalizedValue = normalizeLocationValue(value); - if (!normalizedValue) return null; - - const trimmed = normalizedValue.trim(); - if (!trimmed) return null; - - if (trimmed.toLowerCase() === 'any') { - return 'Any'; - } - - const longName = getIntlTimeZoneName(trimmed, 'long') || getIntlTimeZoneName(trimmed, 'longGeneric'); - if (longName) { - return longName; - } - - const offset = normalizeUtcOffset(trimmed); - if (offset) { - return offset; - } - - if (SIMPLE_TZ_PATTERN.test(trimmed)) { - return resolveTimeZoneAbbreviationName(trimmed) || trimmed.toUpperCase(); - } - - const fallbackShortName = getMomentTimeZoneName(trimmed) || getIntlTimeZoneName(trimmed, 'short'); - if (fallbackShortName) { - return fallbackShortName; - } - - return trimmed; -} - function uniqNormalizedStrings(values) { const seen = new Set(); return values.reduce((acc, value) => { @@ -365,10 +153,6 @@ function getWorkloadDisplay(workload) { return normalizeLabel(workload, WORKLOAD_LABELS); } -function getAnticipatedStartDisplay(value) { - return normalizeLabel(value, ANTICIPATED_START_LABELS); -} - function getCompensationDisplay(compensationRange) { if (typeof compensationRange === 'object' && compensationRange !== null) { const label = compensationRange.name || compensationRange.title; @@ -406,17 +190,10 @@ function EngagementCard({ engagement }) { role, workload, compensationRange, - skills: engagementSkills, - requiredSkills, - skillsets, location, locations: engagementLocations, - timezone, - timezones, - timeZones, countries, status, - anticipatedStart, nanoId, id, engagementId, @@ -431,24 +208,6 @@ function EngagementCard({ engagement }) { durationWeeks, durationMonths, ); - const anticipatedStartText = getAnticipatedStartDisplay(anticipatedStart); - - const skillsSource = [engagementSkills, requiredSkills, skillsets] - .find(value => Array.isArray(value) && value.length) - || engagementSkills - || requiredSkills - || skillsets; - const skills = Array.from(new Set( - asArray(skillsSource) - .map(normalizeSkillLabel) - .filter(Boolean), - )); - const skillsText = skills.length - ? skills.slice(0, 2).join(', ') - : 'Not Specified'; - const limitedSkillsText = skills.length > 2 - ? `${skillsText},...` - : skillsText; const baseLocations = [ ...asArray(location), @@ -456,19 +215,11 @@ function EngagementCard({ engagement }) { ] .map(normalizeRegionValue) .filter(Boolean); - const timezoneValues = [ - ...asArray(timezone), - ...asArray(timezones), - ...asArray(timeZones), - ] - .map(normalizeTimezoneValue) - .filter(Boolean); const countryValues = asArray(countries) .map(normalizeRegionValue) .filter(Boolean); const isAnyValue = value => value.trim().toLowerCase() === 'any'; const hasAnyLocation = [...baseLocations, ...countryValues].some(isAnyValue); - const hasAnyTimezone = timezoneValues.some(isAnyValue); const filteredBaseLocations = baseLocations.filter(value => !isAnyValue(value)); const filteredCountries = countryValues.filter(value => !isAnyValue(value)); const locations = uniqNormalizedStrings([ @@ -477,15 +228,6 @@ function EngagementCard({ engagement }) { ...filteredCountries, ]); const locationText = locations.length ? locations.join(', ') : 'Remote'; - const filteredTimezones = uniqNormalizedStrings( - timezoneValues.filter(value => !isAnyValue(value)), - ); - let timezoneText = 'Not Specified'; - if (filteredTimezones.length) { - timezoneText = filteredTimezones.join(', '); - } else if (hasAnyTimezone) { - timezoneText = 'Any'; - } const resolvedEngagementId = nanoId || id || engagementId; const engagementLink = resolvedEngagementId @@ -505,12 +247,6 @@ function EngagementCard({ engagement }) {
role-icon {getRoleDisplay(role)}
-
- skills-icon {limitedSkillsText} -
-
- {timezoneText} -
{locationText}
@@ -523,9 +259,6 @@ function EngagementCard({ engagement }) {
{durationText}
-
- {`Anticipated start: ${anticipatedStartText}`} -
VIEW DETAILS diff --git a/src/shared/components/engagement-listing/EngagementCard/style.scss b/src/shared/components/engagement-listing/EngagementCard/style.scss index d7c740e41..b6f4bedae 100644 --- a/src/shared/components/engagement-listing/EngagementCard/style.scss +++ b/src/shared/components/engagement-listing/EngagementCard/style.scss @@ -66,7 +66,7 @@ .job-infos { display: grid; - grid-template-columns: 1.2fr 1fr 1.2fr 1.2fr 1fr 1.3fr 0.9fr 1fr 141px; + grid-template-columns: 1.2fr 1.2fr 1fr 1.3fr 0.9fr 141px; column-gap: 20px; align-items: center;