pretty much ready to 'ship'

This commit is contained in:
cottongin
2025-10-30 17:18:30 -04:00
parent 7bb3aabd72
commit 8f3a12ad76
10 changed files with 518 additions and 190 deletions

View File

@@ -0,0 +1,81 @@
import React from 'react';
/**
* PopularityBadge - Display popularity score with upvotes, downvotes, and ratio
*
* @param {number} upvotes - Number of upvotes
* @param {number} downvotes - Number of downvotes
* @param {number} popularityScore - Net score (upvotes - downvotes)
* @param {string} size - Size variant: 'sm', 'md', or 'lg' (default: 'sm')
* @param {boolean} showRatio - Show the upvote/downvote ratio (default: false)
* @param {boolean} showNet - Show the net score (default: true)
* @param {boolean} showCounts - Show individual counts (default: true)
*/
function PopularityBadge({
upvotes = 0,
downvotes = 0,
popularityScore = 0,
size = 'sm',
showRatio = false,
showNet = true,
showCounts = true
}) {
// Don't show badge if no votes
if (upvotes === 0 && downvotes === 0) {
return null;
}
const total = upvotes + downvotes;
const ratio = total > 0 ? ((upvotes / total) * 100).toFixed(0) : 0;
// Determine color based on net score
const isPositive = popularityScore > 0;
const isNeutral = popularityScore === 0;
const colorClasses = isNeutral
? 'bg-gray-100 dark:bg-gray-700/50 text-gray-700 dark:text-gray-300'
: isPositive
? 'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300'
: 'bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300';
const sizeClasses = {
sm: 'text-xs px-2 py-1',
md: 'text-sm px-3 py-1.5',
lg: 'text-base px-4 py-2'
}[size];
return (
<span
className={`inline-flex items-center gap-1 rounded font-semibold ${colorClasses} ${sizeClasses}`}
title="Cumulative popularity across all sessions"
>
{showCounts && (
<>
<span className="whitespace-nowrap">👍 {upvotes}</span>
<span className="text-gray-400 dark:text-gray-500">|</span>
<span className="whitespace-nowrap">👎 {downvotes}</span>
</>
)}
{showNet && showCounts && (
<span className="text-gray-400 dark:text-gray-500">|</span>
)}
{showNet && (
<span className="whitespace-nowrap">
{popularityScore > 0 ? '+' : ''}{popularityScore}
</span>
)}
{showRatio && (
<>
<span className="text-gray-400 dark:text-gray-500">|</span>
<span className="whitespace-nowrap">{ratio}%</span>
</>
)}
</span>
);
}
export default PopularityBadge;