import { ApprovalStatus, ComplianceStatus, ContractStatus, ActivityStatus, Priority, OrganisationType, IndustrySector, ProjectType, UnitOfMeasure, EvidenceType, RiskLikelihood, RiskImpact, NotificationPlatform, NotificationStatus, EmailStatuses, UserRole, } from '../types'; export const formatZAR = (amount: number): string => { return new Intl.NumberFormat('en-ZA', { style: 'currency', currency: 'ZAR', maximumFractionDigits: 0, }).format(amount); }; export const formatCompactZAR = (amount: number): string => { if (amount >= 1_000_000_000) { return `R ${(amount / 1_000_000_000).toFixed(2)}bn`; } if (amount >= 1_000_000) { return `R ${(amount / 1_000_000).toFixed(1)}m`; } if (amount >= 1_000) { return `R ${(amount / 1_000).toFixed(0)}k`; } return formatZAR(amount); }; export const formatDate = (dateStr?: string | null): string => { if (!dateStr) return '—'; try { const d = new Date(dateStr); return d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric', }); } catch { return dateStr; } }; // ========================================== // Dropdown Select Options with Integer Values // ========================================== export const EMAIL_STATUS_OPTIONS = [ { value: EmailStatuses.GeneralError, label: 'General Error' }, { value: EmailStatuses.AuthenticationError, label: 'Authentication Error' }, { value: EmailStatuses.ProtocolError, label: 'Protocol Error' }, { value: EmailStatuses.Connected, label: 'Connected' }, { value: EmailStatuses.Disconnected, label: 'Disconnected' }, { value: EmailStatuses.TooManyConnections, label: 'Too Many Connections' }, { value: EmailStatuses.ConnectionAborted, label: 'Connection Aborted' }, { value: EmailStatuses.Success, label: 'Success' }, ]; export const EMAIL_STATUSES_OPTIONS = EMAIL_STATUS_OPTIONS; export const NOTIFICATION_STATUS_OPTIONS = [ { value: NotificationStatus.Pending, label: 'Pending' }, { value: NotificationStatus.Queued, label: 'Queued' }, { value: NotificationStatus.Processing, label: 'Processing' }, { value: NotificationStatus.Sent, label: 'Sent' }, { value: NotificationStatus.Delivered, label: 'Delivered' }, { value: NotificationStatus.Read, label: 'Read' }, { value: NotificationStatus.Failed, label: 'Failed' }, { value: NotificationStatus.Cancelled, label: 'Cancelled' }, { value: NotificationStatus.Bounced, label: 'Bounced' }, { value: NotificationStatus.Draft, label: 'Draft' }, { value: NotificationStatus.Archived, label: 'Archived' }, ]; export const NOTIFICATION_PLATFORM_OPTIONS = [ { value: NotificationPlatform.Email, label: 'Email' }, { value: NotificationPlatform.SMS, label: 'SMS' }, { value: NotificationPlatform.WhatsApp, label: 'WhatsApp' }, { value: NotificationPlatform.PushNotification, label: 'Push Notification' }, { value: NotificationPlatform.InApp, label: 'In App' }, { value: NotificationPlatform.Teams, label: 'Teams' }, { value: NotificationPlatform.Slack, label: 'Slack' }, { value: NotificationPlatform.Webhook, label: 'Webhook' }, { value: NotificationPlatform.Other, label: 'Other' }, ]; export const EVIDENCE_TYPE_OPTIONS = [ { value: EvidenceType.AttendanceRegister, label: 'Attendance Register' }, { value: EvidenceType.BankStatement, label: 'Bank Statement' }, { value: EvidenceType.CompletionCertificate, label: 'Completion Certificate' }, { value: EvidenceType.FinancialInvoiceOrReceipt, label: 'Financial Invoice Or Receipt' }, { value: EvidenceType.GeotaggedPhoto, label: 'Geotagged Photo' }, { value: EvidenceType.InspectionReport, label: 'Inspection Report' }, { value: EvidenceType.MeetingMinutes, label: 'Meeting Minutes' }, { value: EvidenceType.PayrollOrStipendRegister, label: 'Payroll Or Stipend Register' }, { value: EvidenceType.ProcurementDocument, label: 'Procurement Document' }, { value: EvidenceType.TechnicalSpecificationOrDesign, label: 'Technical Specification Or Design' }, { value: EvidenceType.VideoRecording, label: 'Video Recording' }, { value: EvidenceType.Other, label: 'Other' }, ]; export const PROJECT_TYPE_OPTIONS = [ { value: ProjectType.CapacityBuildingAndTraining, label: 'Capacity Building And Training' }, { value: ProjectType.CapitalExpenditureAndInfrastructure, label: 'Capital Expenditure And Infrastructure' }, { value: ProjectType.CommercializationAndScaling, label: 'Commercialization And Scaling' }, { value: ProjectType.FeasibilityAndTechnicalAssistance, label: 'Feasibility And Technical Assistance' }, { value: ProjectType.OperationalGrantAndSubsidy, label: 'Operational Grant And Subsidy' }, { value: ProjectType.ProductDevelopmentAndPrototyping, label: 'Product Development And Prototyping' }, { value: ProjectType.ResearchAndDevelopment, label: 'Research And Development' }, { value: ProjectType.SocialImpactAndCommunityDevelopment, label: 'Social Impact And Community Development' }, { value: ProjectType.TechnologyTransferAndAdoption, label: 'Technology Transfer And Adoption' }, { value: ProjectType.Innovation, label: 'Innovation' }, { value: ProjectType.Other, label: 'Other' }, ]; export const INDUSTRY_SECTOR_OPTIONS = [ { value: IndustrySector.AgricultureAndAgroProcessing, label: 'Agriculture And Agro Processing' }, { value: IndustrySector.AutomotiveAndTransportEquipment, label: 'Automotive And Transport Equipment' }, { value: IndustrySector.ChemicalsAndPharmaceuticals, label: 'Chemicals And Pharmaceuticals' }, { value: IndustrySector.ConstructionAndInfrastructure, label: 'Construction And Infrastructure' }, { value: IndustrySector.CreativeIndustriesAndMedia, label: 'Creative Industries And Media' }, { value: IndustrySector.DefenseAndAerospace, label: 'Defense And Aerospace' }, { value: IndustrySector.EducationAndSkillsDevelopment, label: 'Education And Skills Development' }, { value: IndustrySector.EnergyAndUtilities, label: 'Energy And Utilities' }, { value: IndustrySector.FinancialServices, label: 'Financial Services' }, { value: IndustrySector.HealthcareAndLifeSciences, label: 'Healthcare And Life Sciences' }, { value: IndustrySector.InformationCommunicationTechnology, label: 'Information Communication Technology' }, { value: IndustrySector.ManufacturingAndIndustrial, label: 'Manufacturing And Industrial' }, { value: IndustrySector.MiningAndMetals, label: 'Mining And Metals' }, { value: IndustrySector.RealEstateAndHousing, label: 'Real Estate And Housing' }, { value: IndustrySector.RetailAndConsumerGoods, label: 'Retail And Consumer Goods' }, { value: IndustrySector.TourismAndHospitality, label: 'Tourism And Hospitality' }, { value: IndustrySector.WaterAndSanitation, label: 'Water And Sanitation' }, { value: IndustrySector.Other, label: 'Other' }, ]; export const ORGANISATION_TYPE_OPTIONS = [ { value: OrganisationType.NonGovernmentalOrganisation, label: 'Non-Governmental Organisation' }, { value: OrganisationType.CivilSocietyOrganisation, label: 'Civil Society Organisation' }, { value: OrganisationType.Trust, label: 'Trust' }, { value: OrganisationType.FaithBasedOrganisation, label: 'Faith-Based Organisation' }, { value: OrganisationType.PrivateLimitedCompany, label: 'Private Limited Company' }, { value: OrganisationType.PublicLimitedCompany, label: 'Public Limited Company' }, { value: OrganisationType.SoleProprietorship, label: 'Sole Proprietorship' }, { value: OrganisationType.Partnership, label: 'Partnership' }, { value: OrganisationType.SocialEnterprise, label: 'Social Enterprise' }, { value: OrganisationType.NationalGovernmentMinistry, label: 'National Government Ministry' }, { value: OrganisationType.ProvincialGovernmentEntity, label: 'Provincial Government Entity' }, { value: OrganisationType.LocalGovernmentMunicipality, label: 'Local Government Municipality' }, { value: OrganisationType.StateOwnedEnterprise, label: 'State-Owned Enterprise' }, { value: OrganisationType.PublicAgency, label: 'Public Agency' }, { value: OrganisationType.PublicUniversity, label: 'Public University' }, { value: OrganisationType.PrivateUniversity, label: 'Private University' }, { value: OrganisationType.ResearchInstitute, label: 'Research Institute' }, { value: OrganisationType.TechnicalVocationalCollege, label: 'Technical Vocational College' }, { value: OrganisationType.Cooperative, label: 'Cooperative' }, { value: OrganisationType.MicrofinanceInstitution, label: 'Microfinance Institution' }, { value: OrganisationType.CommercialBank, label: 'Commercial Bank' }, { value: OrganisationType.DevelopmentFinanceInstitution, label: 'Development Finance Institution' }, { value: OrganisationType.UnitedNationsAgency, label: 'United Nations Agency' }, { value: OrganisationType.IntergovernmentalOrganisation, label: 'Intergovernmental Organisation' }, { value: OrganisationType.InternationalNGO, label: 'International NGO' }, { value: OrganisationType.Consortium, label: 'Consortium' }, { value: OrganisationType.SpecialPurposeVehicle, label: 'Special Purpose Vehicle' }, ]; export const PRIORITY_OPTIONS = [ { value: Priority.Low, label: 'Low' }, { value: Priority.Medium, label: 'Medium' }, { value: Priority.High, label: 'High' }, { value: Priority.Critical, label: 'Critical' }, ]; export const UNIT_OF_MEASURE_OPTIONS = [ { value: UnitOfMeasure.Count, label: 'Count' }, { value: UnitOfMeasure.Percentage, label: 'Percentage' }, { value: UnitOfMeasure.Currency, label: 'Currency' }, { value: UnitOfMeasure.Area, label: 'Area' }, { value: UnitOfMeasure.Weight, label: 'Weight' }, { value: UnitOfMeasure.Volume, label: 'Volume' }, { value: UnitOfMeasure.DurationDays, label: 'Duration (Days)' }, { value: UnitOfMeasure.DurationMonths, label: 'Duration (Months)' }, { value: UnitOfMeasure.Ratio, label: 'Ratio' }, ]; export const CONTRACT_STATUS_OPTIONS = [ { value: ContractStatus.Draft, label: 'Draft' }, { value: ContractStatus.PendingApproval, label: 'Pending Approval' }, { value: ContractStatus.Active, label: 'Active' }, { value: ContractStatus.Expired, label: 'Expired' }, { value: ContractStatus.Terminated, label: 'Terminated' }, { value: ContractStatus.Amended, label: 'Amended' }, ]; export const APPROVAL_STATUS_OPTIONS = [ { value: ApprovalStatus.Pending, label: 'Pending' }, { value: ApprovalStatus.Review, label: 'Review' }, { value: ApprovalStatus.Approved, label: 'Approved' }, { value: ApprovalStatus.Rejected, label: 'Rejected' }, { value: ApprovalStatus.Removed, label: 'Removed' }, ]; export const ACTIVITY_STATUS_OPTIONS = [ { value: ActivityStatus.Active, label: 'Active' }, { value: ActivityStatus.Inactive, label: 'Inactive' }, { value: ActivityStatus.Disabled, label: 'Disabled' }, ]; export const RISK_LIKELIHOOD_OPTIONS = [ { value: RiskLikelihood.Rare, label: 'Rare' }, { value: RiskLikelihood.Unlikely, label: 'Unlikely' }, { value: RiskLikelihood.Possible, label: 'Possible' }, { value: RiskLikelihood.Likely, label: 'Likely' }, { value: RiskLikelihood.AlmostCertain, label: 'Almost Certain' }, ]; export const RISK_IMPACT_OPTIONS = [ { value: RiskImpact.Minor, label: 'Minor' }, { value: RiskImpact.Moderate, label: 'Moderate' }, { value: RiskImpact.Major, label: 'Major' }, { value: RiskImpact.Critical, label: 'Critical' }, { value: RiskImpact.Severe, label: 'Severe' }, ]; // ========================================== // Status Badges & Labels // ========================================== export const getApprovalStatusBadge = (status: ApprovalStatus | number) => { const s = Number(status); switch (s) { case ApprovalStatus.Approved: return { label: 'Approved', className: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30 font-semibold', dot: 'bg-emerald-500', }; case ApprovalStatus.Review: return { label: 'Review', className: 'bg-amber-500/10 text-amber-400 border-amber-500/30 font-semibold', dot: 'bg-amber-500', }; case ApprovalStatus.Rejected: return { label: 'Rejected', className: 'bg-rose-500/10 text-rose-400 border-rose-500/30 font-semibold', dot: 'bg-rose-500', }; case ApprovalStatus.Removed: return { label: 'Removed', className: 'bg-slate-800/80 text-slate-400 border-slate-700/80 font-semibold', dot: 'bg-slate-500', }; case ApprovalStatus.Pending: default: return { label: 'Pending', className: 'bg-blue-500/10 text-blue-400 border-blue-500/30 font-semibold', dot: 'bg-blue-500', }; } }; export const getComplianceStatusBadge = (status: ComplianceStatus | number) => { const s = Number(status); switch (s) { case ComplianceStatus.Exceeded: return { label: 'Exceeded Expectation', className: 'bg-purple-500/15 text-purple-300 border-purple-500/30 font-semibold', dot: 'bg-purple-400', }; case ComplianceStatus.Met: return { label: 'Met', className: 'bg-emerald-500/15 text-emerald-300 border-emerald-500/30 font-semibold', dot: 'bg-emerald-400', }; case ComplianceStatus.NotMet: default: return { label: 'Not Met', className: 'bg-rose-500/15 text-rose-300 border-rose-500/30 font-semibold', dot: 'bg-rose-400', }; } }; export const COMPLIANCE_STATUS_OPTIONS = [ { value: ComplianceStatus.NotMet, label: 'Not Met' }, { value: ComplianceStatus.Met, label: 'Met' }, { value: ComplianceStatus.Exceeded, label: 'Exceeded Expectation' }, ]; export const getContractStatusBadge = (status: ContractStatus | number) => { const s = Number(status); switch (s) { case ContractStatus.Active: return { label: 'Active', className: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30 font-semibold', }; case ContractStatus.PendingApproval: return { label: 'Pending Approval', className: 'bg-amber-500/10 text-amber-400 border-amber-500/30 font-semibold', }; case ContractStatus.Amended: return { label: 'Amended', className: 'bg-indigo-500/10 text-indigo-400 border-indigo-500/30 font-semibold', }; case ContractStatus.Expired: return { label: 'Expired', className: 'bg-slate-800/80 text-slate-400 border-slate-700/80 font-semibold', }; case ContractStatus.Terminated: return { label: 'Terminated', className: 'bg-rose-500/10 text-rose-400 border-rose-500/30 font-semibold', }; case ContractStatus.Draft: default: return { label: 'Draft', className: 'bg-slate-800/80 text-slate-300 border-slate-700/80 font-semibold', }; } }; export const getActivityStatusBadge = (status: ActivityStatus | number | string | boolean | null | undefined) => { if (status === null || status === undefined) { return { label: 'Active', className: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30 font-semibold', }; } if (typeof status === 'boolean') { return status ? { label: 'Active', className: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30 font-semibold' } : { label: 'Inactive', className: 'bg-slate-800/80 text-slate-400 border-slate-700/80 font-semibold' }; } const str = String(status).trim().toLowerCase(); if (str === '1' || str === 'active' || str === 'enabled') { return { label: 'Active', className: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30 font-semibold', }; } if (str === '2' || str === 'inactive') { return { label: 'Inactive', className: 'bg-slate-800/80 text-slate-400 border-slate-700/80 font-semibold', }; } if (str === '3' || str === 'disabled' || str === 'suspended' || str === 'terminated') { return { label: str === 'suspended' ? 'Suspended' : 'Disabled', className: 'bg-rose-500/10 text-rose-400 border-rose-500/30 font-semibold', }; } if (str === 'approved') { return { label: 'Approved', className: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30 font-semibold', }; } if (str === 'pending' || str === 'review') { return { label: 'Pending', className: 'bg-amber-500/10 text-amber-400 border-amber-500/30 font-semibold', }; } const s = Number(status); switch (s) { case ActivityStatus.Active: return { label: 'Active', className: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30 font-semibold', }; case ActivityStatus.Inactive: return { label: 'Inactive', className: 'bg-slate-800/80 text-slate-400 border-slate-700/80 font-semibold', }; case ActivityStatus.Disabled: return { label: 'Disabled', className: 'bg-rose-500/10 text-rose-400 border-rose-500/30 font-semibold', }; default: return { label: String(status), className: 'bg-slate-800/80 text-slate-300 border-slate-700/80 font-semibold', }; } }; /** * Robust Status Badge resolver for Organisation Directory. * Inspects status, Status, activityStatus, approvalStatus, statusId, isActive, or state, * parsing string, boolean, or numeric database representations. */ export const getOrganisationStatusBadge = (statusOrOrg: any) => { let val: any = statusOrOrg; if (statusOrOrg !== null && typeof statusOrOrg === 'object') { val = statusOrOrg.status ?? statusOrOrg.Status ?? statusOrOrg.activityStatus ?? statusOrOrg.ActivityStatus ?? statusOrOrg.approvalStatus ?? statusOrOrg.ApprovalStatus ?? statusOrOrg.statusId ?? statusOrOrg.StatusId ?? statusOrOrg.state ?? statusOrOrg.State ?? (statusOrOrg.isActive !== undefined ? (statusOrOrg.isActive ? 1 : 2) : undefined) ?? (statusOrOrg.IsActive !== undefined ? (statusOrOrg.IsActive ? 1 : 2) : undefined); } return getActivityStatusBadge(val); }; export const getPriorityBadge = (priority: Priority | number) => { const p = Number(priority); switch (p) { case Priority.Critical: return { label: 'Critical', className: 'bg-rose-500/10 text-rose-400 border-rose-500/30 font-semibold' }; case Priority.High: return { label: 'High', className: 'bg-amber-500/10 text-amber-400 border-amber-500/30 font-semibold' }; case Priority.Medium: return { label: 'Medium', className: 'bg-blue-500/10 text-blue-400 border-blue-500/30 font-semibold' }; case Priority.Low: default: return { label: 'Low', className: 'bg-slate-800/80 text-slate-400 border-slate-700/80 font-semibold' }; } }; export const getOrganisationTypeLabel = (type: OrganisationType | number): string => { const opt = ORGANISATION_TYPE_OPTIONS.find((o) => o.value === Number(type)); return opt ? opt.label : 'Organisation'; }; export const getIndustrySectorLabel = (sector: IndustrySector | number): string => { const opt = INDUSTRY_SECTOR_OPTIONS.find((o) => o.value === Number(sector)); return opt ? opt.label : 'Industry Sector'; }; export const getProjectTypeLabel = (type: ProjectType | number): string => { const opt = PROJECT_TYPE_OPTIONS.find((o) => o.value === Number(type)); return opt ? opt.label : 'Project Type'; }; export const getEvidenceTypeLabel = (type: EvidenceType | number): string => { const opt = EVIDENCE_TYPE_OPTIONS.find((o) => o.value === Number(type)); return opt ? opt.label : 'Evidence'; }; export const getUnitOfMeasureLabel = (uom?: UnitOfMeasure | number | null): string => { if (uom === null || uom === undefined) return 'Qty'; const opt = UNIT_OF_MEASURE_OPTIONS.find((o) => o.value === Number(uom)); return opt ? opt.label : 'Qty'; }; export const getNotificationPlatformLabel = (platform?: NotificationPlatform | number | null): string => { const opt = NOTIFICATION_PLATFORM_OPTIONS.find((o) => o.value === Number(platform)); return opt ? opt.label : 'Platform'; }; export const getNotificationStatusLabel = (status?: NotificationStatus | number | null): string => { const opt = NOTIFICATION_STATUS_OPTIONS.find((o) => o.value === Number(status)); return opt ? opt.label : 'Status'; }; export const getEmailStatusLabel = (status?: EmailStatuses | number | null): string => { const opt = EMAIL_STATUS_OPTIONS.find((o) => o.value === Number(status)); return opt ? opt.label : 'Status'; }; export const getEmailStatusBadge = (status?: EmailStatuses | number | null) => { const s = Number(status); switch (s) { case EmailStatuses.Connected: case EmailStatuses.Success: return { label: getEmailStatusLabel(s), className: 'bg-emerald-50 text-emerald-800 border-emerald-300 font-semibold' }; case EmailStatuses.Disconnected: return { label: getEmailStatusLabel(s), className: 'bg-slate-100 text-slate-700 border-slate-300 font-semibold' }; default: return { label: getEmailStatusLabel(s), className: 'bg-rose-50 text-rose-800 border-rose-300 font-semibold' }; } }; export const getNotificationStatusBadge = (status?: NotificationStatus | number | null) => { const s = Number(status); switch (s) { case NotificationStatus.Archived: return { label: 'Archived', className: 'bg-slate-800 text-slate-300 border-slate-700 font-semibold' }; case NotificationStatus.Draft: return { label: 'Draft', className: 'bg-purple-50 text-purple-800 border-purple-300 font-semibold' }; case NotificationStatus.Delivered: case NotificationStatus.Read: return { label: getNotificationStatusLabel(s), className: 'bg-emerald-50 text-emerald-800 border-emerald-300 font-semibold' }; case NotificationStatus.Sent: case NotificationStatus.Queued: case NotificationStatus.Processing: return { label: getNotificationStatusLabel(s), className: 'bg-sky-50 text-sky-800 border-sky-300 font-semibold' }; case NotificationStatus.Pending: return { label: getNotificationStatusLabel(s), className: 'bg-amber-50 text-amber-900 border-amber-300 font-semibold' }; case NotificationStatus.Failed: case NotificationStatus.Cancelled: case NotificationStatus.Bounced: default: return { label: getNotificationStatusLabel(s), className: 'bg-rose-50 text-rose-800 border-rose-300 font-semibold' }; } }; export const getRiskScoreColor = (likelihood: RiskLikelihood | number, impact: RiskImpact | number) => { const score = (Number(likelihood) || 1) * (Number(impact) || 1); if (score >= 15) { return { level: 'Critical', color: 'bg-rose-600 text-white', badge: 'bg-rose-500/20 text-rose-300 border border-rose-500/40 font-bold' }; } if (score >= 10) { return { level: 'High', color: 'bg-orange-500 text-white', badge: 'bg-orange-500/20 text-orange-300 border border-orange-500/40 font-bold' }; } if (score >= 5) { return { level: 'Moderate', color: 'bg-amber-500 text-white', badge: 'bg-amber-500/20 text-amber-300 border border-amber-500/40 font-semibold' }; } return { level: 'Low', color: 'bg-emerald-600 text-white', badge: 'bg-emerald-500/20 text-emerald-300 border border-emerald-500/40 font-semibold' }; }; // ========================================== // User to Email Resolver (UI shows only Email) // ========================================== const USER_EMAIL_CACHE: Record = {}; const EMAIL_TO_USER_ID_CACHE: Record = {}; export const registerUserEmail = (userId: string | number, email: string) => { if (userId && email && typeof email === 'string' && email.trim()) { const cleanId = String(userId).trim(); const cleanEmail = email.trim(); USER_EMAIL_CACHE[cleanId] = cleanEmail; USER_EMAIL_CACHE[cleanId.toLowerCase()] = cleanEmail; EMAIL_TO_USER_ID_CACHE[cleanEmail.toLowerCase()] = cleanId; } }; export const registerUserEmails = (users: Array<{ id: string | number; email?: string | null }>) => { if (!Array.isArray(users)) return; for (const u of users) { if (u && u.id && u.email) { registerUserEmail(u.id, u.email); } } }; export const getUserIdByEmail = (email?: string | null): string | undefined => { if (!email || typeof email !== 'string') return undefined; return EMAIL_TO_USER_ID_CACHE[email.trim().toLowerCase()]; }; export const getUserEmail = (userIdOrEmail?: string | number | null): string => { if (!userIdOrEmail) return '—'; const str = String(userIdOrEmail).trim(); if (!str) return '—'; if (str.includes('@')) return str; if (USER_EMAIL_CACHE[str]) return USER_EMAIL_CACHE[str]; if (USER_EMAIL_CACHE[str.toLowerCase()]) return USER_EMAIL_CACHE[str.toLowerCase()]; // Synchronous fallback from stored auth session in browser try { const storedUserJson = localStorage.getItem('pfm_auth_user'); const storedSub = localStorage.getItem('pfm_user_sub'); if (storedUserJson) { const parsed = JSON.parse(storedUserJson); if (parsed?.email) { const email = String(parsed.email).trim(); if ( (parsed.id && String(parsed.id).toLowerCase() === str.toLowerCase()) || (parsed.sub && String(parsed.sub).toLowerCase() === str.toLowerCase()) || (storedSub && String(storedSub).toLowerCase() === str.toLowerCase()) ) { registerUserEmail(str, email); return email; } } } } catch { // Ignore storage parse errors } return str; }; // ========================================== // User Role Badges & Formatter // ========================================== export const USER_ROLE_OPTIONS = [ { value: 'Admin', label: 'Admin' }, { value: 'Executive', label: 'Executive' }, { value: 'Supervisor', label: 'Supervisor' }, { value: 'Manager', label: 'Manager' }, { value: 'Contributor', label: 'Contributor' }, { value: 'Unassigned', label: 'Unassigned' }, ]; export const getUserRoleBadge = (role?: string | number | UserRole | null) => { if (role === null || role === undefined || role === '' || String(role).trim().toLowerCase() === 'null' || String(role).trim().toLowerCase() === 'unassigned') { return { label: 'Unassigned', className: 'bg-slate-800/80 text-slate-400 border-slate-700/80 shadow-xs', dot: 'bg-slate-500', }; } const roleStr = String(role).trim().toLowerCase(); // 1. Admin - High-privilege Crimson/Rose if (roleStr === 'admin' || roleStr === 'superadmin' || roleStr === '1') { return { label: 'Admin', className: 'bg-rose-500/15 text-rose-300 border-rose-500/30 shadow-xs shadow-rose-500/10 font-semibold', dot: 'bg-rose-400', }; } // 2. Executive - Strategic Amber/Gold if (roleStr === 'executive' || roleStr === '2') { return { label: 'Executive', className: 'bg-amber-500/15 text-amber-300 border-amber-500/30 shadow-xs shadow-amber-500/10 font-semibold', dot: 'bg-amber-400', }; } // 3. Supervisor - Operational Governance Purple/Violet if (roleStr === 'supervisor' || roleStr === '3') { return { label: 'Supervisor', className: 'bg-purple-500/15 text-purple-300 border-purple-500/30 shadow-xs shadow-purple-500/10 font-semibold', dot: 'bg-purple-400', }; } // 4. Manager - Programme & Project Management Blue/Sky if (roleStr === 'manager' || roleStr === 'programmemanager' || roleStr === 'projectmanager' || roleStr === '4') { return { label: 'Manager', className: 'bg-blue-500/15 text-blue-300 border-blue-500/30 shadow-xs shadow-blue-500/10 font-semibold', dot: 'bg-blue-400', }; } // 5. Contributor - Execution & Outreach Emerald/Mint if (roleStr === 'contributor' || roleStr === 'viewer' || roleStr === 'recipientviewer' || roleStr === '5') { return { label: 'Contributor', className: 'bg-emerald-500/15 text-emerald-300 border-emerald-500/30 shadow-xs shadow-emerald-500/10 font-semibold', dot: 'bg-emerald-400', }; } // Fallback for custom formatted role strings const formatted = roleStr.charAt(0).toUpperCase() + roleStr.slice(1); return { label: formatted, className: 'bg-cyan-500/15 text-cyan-300 border-cyan-500/30', dot: 'bg-cyan-400', }; };