mpre ui fixes
This commit is contained in:
183
frontend/src/components/common/StatusMetricsCard.tsx
Normal file
183
frontend/src/components/common/StatusMetricsCard.tsx
Normal file
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* StatusMetricsCard Component
|
||||
* Displays status metrics in a card format with colored left border
|
||||
* Used in table action rows to show page-specific status metrics
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export interface StatusMetricItem {
|
||||
label: string;
|
||||
value: string | number;
|
||||
}
|
||||
|
||||
export interface StatusMetricsCardProps {
|
||||
/** Title for the card */
|
||||
title: string;
|
||||
/** Subtitle text shown below the main count */
|
||||
subtitle?: string;
|
||||
/** Icon to display */
|
||||
icon?: React.ReactNode;
|
||||
/** Color variant - matches page badge colors */
|
||||
color: 'blue' | 'orange' | 'pink' | 'emerald' | 'green' | 'purple' | 'amber' | 'red' | 'indigo' | 'cyan' | 'teal';
|
||||
/** Main count value */
|
||||
count: number;
|
||||
/** Array of metric items for 2-column layout */
|
||||
metrics?: StatusMetricItem[];
|
||||
/** Review count to display */
|
||||
reviewCount?: number;
|
||||
/** Link to review page */
|
||||
reviewLink?: string;
|
||||
/** Custom action button */
|
||||
actionButton?: {
|
||||
label: string;
|
||||
href: string;
|
||||
};
|
||||
}
|
||||
|
||||
const colorClasses: Record<string, { border: string; bg: string; text: string; iconBg: string }> = {
|
||||
blue: {
|
||||
border: 'border-l-blue-500',
|
||||
bg: 'bg-blue-50 dark:bg-blue-500/10',
|
||||
text: 'text-blue-700 dark:text-blue-300',
|
||||
iconBg: 'bg-blue-100 dark:bg-blue-500/20'
|
||||
},
|
||||
orange: {
|
||||
border: 'border-l-orange-500',
|
||||
bg: 'bg-orange-50 dark:bg-orange-500/10',
|
||||
text: 'text-orange-700 dark:text-orange-300',
|
||||
iconBg: 'bg-orange-100 dark:bg-orange-500/20'
|
||||
},
|
||||
pink: {
|
||||
border: 'border-l-pink-500',
|
||||
bg: 'bg-pink-50 dark:bg-pink-500/10',
|
||||
text: 'text-pink-700 dark:text-pink-300',
|
||||
iconBg: 'bg-pink-100 dark:bg-pink-500/20'
|
||||
},
|
||||
emerald: {
|
||||
border: 'border-l-emerald-500',
|
||||
bg: 'bg-emerald-50 dark:bg-emerald-500/10',
|
||||
text: 'text-emerald-700 dark:text-emerald-300',
|
||||
iconBg: 'bg-emerald-100 dark:bg-emerald-500/20'
|
||||
},
|
||||
green: {
|
||||
border: 'border-l-green-500',
|
||||
bg: 'bg-green-50 dark:bg-green-500/10',
|
||||
text: 'text-green-700 dark:text-green-300',
|
||||
iconBg: 'bg-green-100 dark:bg-green-500/20'
|
||||
},
|
||||
purple: {
|
||||
border: 'border-l-purple-500',
|
||||
bg: 'bg-purple-50 dark:bg-purple-500/10',
|
||||
text: 'text-purple-700 dark:text-purple-300',
|
||||
iconBg: 'bg-purple-100 dark:bg-purple-500/20'
|
||||
},
|
||||
amber: {
|
||||
border: 'border-l-amber-500',
|
||||
bg: 'bg-amber-50 dark:bg-amber-500/10',
|
||||
text: 'text-amber-700 dark:text-amber-300',
|
||||
iconBg: 'bg-amber-100 dark:bg-amber-500/20'
|
||||
},
|
||||
red: {
|
||||
border: 'border-l-red-500',
|
||||
bg: 'bg-red-50 dark:bg-red-500/10',
|
||||
text: 'text-red-700 dark:text-red-300',
|
||||
iconBg: 'bg-red-100 dark:bg-red-500/20'
|
||||
},
|
||||
indigo: {
|
||||
border: 'border-l-indigo-500',
|
||||
bg: 'bg-indigo-50 dark:bg-indigo-500/10',
|
||||
text: 'text-indigo-700 dark:text-indigo-300',
|
||||
iconBg: 'bg-indigo-100 dark:bg-indigo-500/20'
|
||||
},
|
||||
cyan: {
|
||||
border: 'border-l-cyan-500',
|
||||
bg: 'bg-cyan-50 dark:bg-cyan-500/10',
|
||||
text: 'text-cyan-700 dark:text-cyan-300',
|
||||
iconBg: 'bg-cyan-100 dark:bg-cyan-500/20'
|
||||
},
|
||||
teal: {
|
||||
border: 'border-l-teal-500',
|
||||
bg: 'bg-teal-50 dark:bg-teal-500/10',
|
||||
text: 'text-teal-700 dark:text-teal-300',
|
||||
iconBg: 'bg-teal-100 dark:bg-teal-500/20'
|
||||
},
|
||||
};
|
||||
|
||||
export default function StatusMetricsCard({
|
||||
title,
|
||||
subtitle,
|
||||
icon,
|
||||
color,
|
||||
count,
|
||||
metrics,
|
||||
reviewCount,
|
||||
reviewLink = '/writer/review',
|
||||
actionButton,
|
||||
}: StatusMetricsCardProps) {
|
||||
const colors = colorClasses[color] || colorClasses.blue;
|
||||
|
||||
return (
|
||||
<div className={`${colors.bg} border-l-4 ${colors.border} rounded-lg px-4 py-3 min-w-[280px]`}>
|
||||
<div className="flex items-start gap-3">
|
||||
{/* Icon */}
|
||||
{icon && (
|
||||
<div className={`${colors.iconBg} rounded-lg p-2 flex-shrink-0`}>
|
||||
<div className={`${colors.text} w-5 h-5`}>
|
||||
{icon}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1 min-w-0">
|
||||
{/* Title and Count Row */}
|
||||
<div className="flex items-center justify-between gap-4 mb-1">
|
||||
<h4 className={`text-sm font-semibold ${colors.text}`}>{title}</h4>
|
||||
<span className="text-2xl font-bold text-gray-900 dark:text-white">{count}</span>
|
||||
</div>
|
||||
|
||||
{/* Subtitle */}
|
||||
{subtitle && (
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mb-2">{subtitle}</p>
|
||||
)}
|
||||
|
||||
{/* 2-column metrics */}
|
||||
{metrics && metrics.length > 0 && (
|
||||
<div className="grid grid-cols-2 gap-x-4 gap-y-1 mt-2 text-sm">
|
||||
{metrics.map((metric, index) => (
|
||||
<div key={index} className="flex justify-between">
|
||||
<span className="text-gray-600 dark:text-gray-400">{metric.label}</span>
|
||||
<span className="font-medium text-gray-900 dark:text-white">{metric.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Review link/button */}
|
||||
{(reviewCount !== undefined || actionButton) && (
|
||||
<div className="mt-3 pt-2 border-t border-gray-200/50 dark:border-gray-700/50 flex items-center justify-between gap-2">
|
||||
{reviewCount !== undefined && (
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">
|
||||
<span className="font-semibold text-gray-900 dark:text-white">{reviewCount}</span> awaiting review
|
||||
</span>
|
||||
)}
|
||||
{actionButton && (
|
||||
<Link
|
||||
to={actionButton.href}
|
||||
className="inline-flex items-center gap-1 px-3 py-1.5 text-sm font-medium text-white bg-emerald-600 hover:bg-emerald-700 rounded-md transition-colors"
|
||||
>
|
||||
{actionButton.label}
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user