/** * Transform backend confidence score to 0–1 sentiment scale. * CRITICAL: backend score = confidence in predicted label (always high). * POSITIVE + 0.95 → 0.95 (very positive) * NEGATIVE + 0.95 → 0.05 (very negative) */ export function effectiveSentiment(label, score) { return label === 'POSITIVE' ? score : 1 - score } /** * Map 0–1 sentiment to color: red(0) → grey(0.5) → green(1) */ export function sentimentColor(value) { if (value <= 0.5) { const t = value * 2 // 0→1 in [0, 0.5] const r = Math.round(239 + (156 - 239) * t) const g = Math.round(68 + (163 - 68) * t) const b = Math.round(68 + (163 - 68) * t) return `rgb(${r},${g},${b})` } else { const t = (value - 0.5) * 2 // 0→1 in [0.5, 1] const r = Math.round(156 + (34 - 156) * t) const g = Math.round(163 + (197 - 163) * t) const b = Math.round(163 + (94 - 163) * t) return `rgb(${r},${g},${b})` } } /** * Returns { color, isDisagree } based on quadrant: * high stars + high sentiment = green * low stars + low sentiment = red * off-diagonal = amber (isDisagree: true) * Star rating is 0–10. Threshold: 5 for stars, 0.5 for sentiment. */ export function quadrantColor(starRating, sentimentScore) { const highStars = starRating >= 5 const highSentiment = sentimentScore >= 0.5 if (highStars && highSentiment) return { color: '#22c55e', isDisagree: false } if (!highStars && !highSentiment) return { color: '#ef4444', isDisagree: false } return { color: '#f59e0b', isDisagree: true } } /** * Compare first 20% vs last 20% average sentiment. * Returns "improved" / "declined" / "remained stable" */ export function arcTitle(sortedReviews) { if (!sortedReviews || sortedReviews.length < 5) return 'remained stable' const n = sortedReviews.length const slice = Math.max(1, Math.floor(n * 0.2)) const avg = (arr) => arr.reduce((s, r) => s + effectiveSentiment(r.sentiment_label, r.sentiment_score), 0) / arr.length const firstAvg = avg(sortedReviews.slice(0, slice)) const lastAvg = avg(sortedReviews.slice(n - slice)) const delta = lastAvg - firstAvg if (delta > 0.05) return 'improved' if (delta < -0.05) return 'declined' return 'remained stable' }