Files
doormile_crm/src/components/StatCard.jsx

100 lines
4.0 KiB
JavaScript

import { Card } from '@astryxdesign/core/Card';
// ==============================|| STAT / KPI CARD ||============================== //
export default function StatCard({ title, value, icon: Icon, color = 'primary', trend, caption, size = 'md' }) {
const trendUp = typeof trend === 'number' ? trend >= 0 : null;
const isCompact = size === 'sm';
// Map theme colors to CSS values
const colorMap = {
primary: { main: '#C01227', light: 'rgba(192, 18, 39, 0.04)', border: 'rgba(192, 18, 39, 0.1)' },
secondary: { main: '#475569', light: 'rgba(71, 85, 105, 0.04)', border: 'rgba(71, 85, 105, 0.1)' },
success: { main: '#10b981', light: 'rgba(16, 185, 129, 0.04)', border: 'rgba(16, 185, 129, 0.1)' },
warning: { main: '#f59e0b', light: 'rgba(245, 158, 11, 0.04)', border: 'rgba(245, 158, 11, 0.1)' },
error: { main: '#ef4444', light: 'rgba(239, 68, 68, 0.04)', border: 'rgba(239, 68, 68, 0.1)' },
info: { main: '#3b82f6', light: 'rgba(59, 130, 246, 0.04)', border: 'rgba(59, 130, 246, 0.1)' }
};
const themeColor = colorMap[color] || colorMap.primary;
return (
<Card
style={{
height: '100%',
position: 'relative',
overflow: 'hidden',
borderRadius: isCompact ? '12px' : '16px',
padding: isCompact ? '16px' : '24px',
border: `1px solid ${themeColor.border}`,
boxShadow: '0 4px 20px rgba(0,0,0,0.02)',
background: `linear-gradient(135deg, #ffffff 0%, ${themeColor.light} 100%)`,
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
cursor: 'pointer',
boxSizing: 'border-box'
}}
className="stat-card"
>
{Icon && (
<div style={{ position: 'absolute', right: '-12px', bottom: '-12px', opacity: 0.04, transform: 'rotate(-15deg)', pointerEvents: 'none', color: themeColor.main }}>
<Icon size={isCompact ? 64 : 96} />
</div>
)}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: isCompact ? '10px' : '16px', position: 'relative', zIndex: 2 }}>
<span style={{ fontWeight: 700, fontSize: '0.75rem', color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
{title}
</span>
{Icon && (
<div
style={{
width: isCompact ? '28px' : '36px',
height: isCompact ? '28px' : '36px',
borderRadius: '8px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'rgba(192, 18, 39, 0.08)',
color: '#C01227'
}}
>
<Icon size={isCompact ? 14 : 18} />
</div>
)}
</div>
<div style={{ flexGrow: 1, position: 'relative', zIndex: 2, marginBottom: (trendUp !== null || caption) ? (isCompact ? '10px' : '16px') : '0' }}>
<span style={{ fontWeight: 800, fontSize: isCompact ? '1.5rem' : '2rem', color: '#1e293b', lineHeight: 1, display: 'block' }}>
{value}
</span>
</div>
{(trendUp !== null || caption) && (
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: '8px', paddingTop: isCompact ? '8px' : '12px', borderTop: '1px dashed #e2e8f0', position: 'relative', zIndex: 2 }}>
{trendUp !== null && (
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '2px',
backgroundColor: trendUp ? '#E3F6EC' : '#FEEAE9',
padding: '2px 6px',
borderRadius: '4px'
}}
>
<span style={{ fontSize: '11px', fontWeight: 700, color: trendUp ? '#00773B' : '#A82216' }}>
{trendUp ? '↑' : '↓'} {Math.abs(trend)}%
</span>
</div>
)}
{caption && (
<span style={{ fontSize: '0.75rem', color: '#64748b', fontWeight: 600 }}>
{caption}
</span>
)}
</div>
)}
</Card>
);
}