/** * Verdict label helpers — convert long verdict strings to short display values * and derive CSS colour classes from emoji prefixes. */ /** * Converts a long verdict label into a short display string. * e.g. "🟢 BUY (High Conviction)" → "Strong" */ export function verdictShort(label: string | null | undefined): string { if (!label) return '—'; if (label.includes('High Conviction')) return 'Strong'; if (label.includes('Speculative')) return 'Speculative'; if (label.includes('BUY')) return 'Buy'; if (label.includes('Efficient')) return 'Efficient'; if (label.includes('Attractive')) return 'Attractive'; if (label.includes('Neutral')) return 'Hold'; if (label.includes('REJECT')) return 'Reject'; if (label.includes('Avoid')) return 'Avoid'; return label.replace(/[🟢🟡🔴]/u, '').trim(); } /** * Returns a CSS colour class ('green' | 'yellow' | 'red') based on * the emoji prefix of a verdict label. */ export function vClass(label: string | null | undefined): 'green' | 'yellow' | 'red' { if (label?.startsWith('🟢')) return 'green'; if (label?.startsWith('🟡')) return 'yellow'; return 'red'; } /** * Returns a CSS colour class for a portfolio advice string based on its emoji prefix. * 🟢 → 'green', 🟡 → 'yellow', 🟠 → 'orange', 🔴 → 'red', else 'gray'. */ export function advClass( advice: string | null | undefined, ): 'green' | 'yellow' | 'orange' | 'red' | 'gray' { if (advice?.includes('🟢')) return 'green'; if (advice?.includes('🟡')) return 'yellow'; if (advice?.includes('🟠')) return 'orange'; if (advice?.includes('🔴')) return 'red'; return 'gray'; } /** * Returns 'green' for non-negative G/L percentage, 'red' otherwise. * Accepts string (e.g. "12.5") or number. */ export function glClass(pct: string | number | null | undefined): 'green' | 'red' { return parseFloat(String(pct ?? 0)) >= 0 ? 'green' : 'red'; }