81 lines
2.8 KiB
TypeScript
81 lines
2.8 KiB
TypeScript
/**
|
|
* 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('No Data')) return 'No Data';
|
|
if (label.includes('High Conviction')) return 'Strong Buy';
|
|
if (label.includes('Speculative')) return 'Speculative';
|
|
if (label.includes('Momentum')) return 'Momentum';
|
|
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{1F7E2}\u{1F7E1}\u{1F534}]/u, '').trim();
|
|
}
|
|
|
|
/**
|
|
* Returns a CSS colour class based on the verdict label content.
|
|
*
|
|
* Signal mapping:
|
|
* 🟢 / High Conviction / Efficient / Attractive → green
|
|
* 🟡 / Speculative / Momentum → yellow
|
|
* Neutral / Hold / no signal → blue (calm, not alarming)
|
|
* 🔴 / Avoid / Reject / REJECT → red
|
|
*/
|
|
export function vClass(
|
|
label: string | null | undefined,
|
|
): 'green' | 'yellow' | 'red' | 'blue' | 'gray' {
|
|
if (!label) return 'gray';
|
|
// Insufficient data is unknown, not a neutral opinion — render gray
|
|
if (label.includes('No Data')) return 'gray';
|
|
if (
|
|
label.startsWith('🟢') ||
|
|
label.includes('High Conviction') ||
|
|
label.includes('Efficient') ||
|
|
label.includes('Attractive')
|
|
)
|
|
return 'green';
|
|
if (label.startsWith('🟡') || label.includes('Speculative') || label.includes('Momentum'))
|
|
return 'yellow';
|
|
if (
|
|
label.startsWith('🔴') ||
|
|
label.includes('Avoid') ||
|
|
label.includes('Reject') ||
|
|
label.includes('REJECT')
|
|
)
|
|
return 'red';
|
|
if (label.includes('Neutral') || label.includes('Hold') || label.includes('BUY')) return 'blue';
|
|
return 'gray';
|
|
}
|
|
|
|
/**
|
|
* 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';
|
|
}
|