Spaces:
Running
Running
File size: 5,522 Bytes
1e2158c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | import React from 'react';
import { motion } from 'framer-motion';
import { Play, GitCompare, Bookmark, Copy, Trash2 } from 'lucide-react';
import { useUserStore } from '@/store/userStore';
interface ScenarioCardProps {
id: string;
name: string;
title: string;
goal: string;
tags?: string[];
badge?: string | null;
badgeColor?: string;
metrics?: {
revenue_change?: number;
profit_change?: number;
risk?: string;
};
confidence?: number;
estimatedRoi?: string;
runtimeMs?: number;
onRun?: (id: string) => void;
onCompare?: (id: string) => void;
onSave?: (id: string) => void;
onClone?: (id: string) => void;
onDelete?: (id: string) => void;
}
const ScenarioCard: React.FC<ScenarioCardProps> = ({
id, name, title, goal, tags = [], badge, badgeColor = 'emerald',
metrics = {}, confidence, estimatedRoi, runtimeMs,
onRun, onCompare, onSave, onClone, onDelete,
}) => {
const { isDark } = useUserStore();
const riskColors: Record<string, string> = {
Low: 'text-emerald-500 bg-emerald-500/10',
Medium: 'text-amber-500 bg-amber-500/10',
High: 'text-red-500 bg-red-500/10',
};
return (
<motion.div
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
className="rounded-2xl border p-5 flex flex-col gap-3 group hover:border-indigo-500/30 transition-all duration-300"
style={{
background: isDark ? 'rgba(255,255,255,0.03)' : '#ffffff',
borderColor: isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)',
boxShadow: isDark ? 'none' : '0 1px 3px rgba(0,0,0,0.04)',
}}
>
{/* Header */}
<div className="flex items-start justify-between">
<div>
<p className="text-xs font-medium" style={{ color: isDark ? '#64748b' : '#94a3b8' }}>{name}</p>
<h4 className="text-sm font-bold mt-0.5" style={{ color: isDark ? '#f8fafc' : '#0f172a' }}>{title}</h4>
</div>
{badge && (
<span className={`text-[10px] font-bold px-2 py-0.5 rounded-full bg-${badgeColor}-500/15 text-${badgeColor}-500`}
style={{
background: badgeColor === 'emerald' ? 'rgba(16,185,129,0.12)' : 'rgba(99,102,241,0.12)',
color: badgeColor === 'emerald' ? '#10b981' : '#6366f1',
}}
>
{badge}
</span>
)}
</div>
{/* Tags */}
<div className="flex flex-wrap gap-1.5">
{tags.map((tag, i) => (
<span
key={i}
className="text-[10px] font-medium px-2 py-0.5 rounded-full"
style={{
background: isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.04)',
color: isDark ? '#94a3b8' : '#64748b',
}}
>
{tag}
</span>
))}
</div>
{/* Description */}
<p className="text-xs leading-relaxed" style={{ color: isDark ? '#94a3b8' : '#64748b' }}>
{goal}
</p>
{/* Metrics */}
<div className="grid grid-cols-3 gap-3 pt-2 border-t" style={{ borderColor: isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)' }}>
{metrics.revenue_change !== undefined && (
<div>
<p className="text-[10px]" style={{ color: isDark ? '#64748b' : '#94a3b8' }}>Revenue</p>
<p className={`text-xs font-bold ${metrics.revenue_change >= 0 ? 'text-emerald-500' : 'text-red-500'}`}>
↑ {Math.abs(metrics.revenue_change)}%
</p>
</div>
)}
{metrics.profit_change !== undefined && (
<div>
<p className="text-[10px]" style={{ color: isDark ? '#64748b' : '#94a3b8' }}>Profit</p>
<p className={`text-xs font-bold ${metrics.profit_change >= 0 ? 'text-emerald-500' : 'text-red-500'}`}>
↑ {Math.abs(metrics.profit_change)}%
</p>
</div>
)}
{metrics.risk && (
<div>
<p className="text-[10px]" style={{ color: isDark ? '#64748b' : '#94a3b8' }}>Risk</p>
<span className={`text-[10px] font-bold px-1.5 py-0.5 rounded-full ${riskColors[metrics.risk] || ''}`}>
{metrics.risk}
</span>
</div>
)}
</div>
{/* Actions */}
<div className="flex gap-2 pt-2">
<button
onClick={() => onRun?.(id)}
className="flex-1 flex items-center justify-center gap-1.5 px-3 py-2 rounded-xl text-xs font-semibold transition-all bg-indigo-600 hover:bg-indigo-700 text-white shadow-sm shadow-indigo-600/20"
>
<Play className="w-3 h-3" /> Run Scenario
</button>
<button
onClick={() => onCompare?.(id)}
className="p-2 rounded-xl border transition-all hover:border-indigo-500/30"
style={{
borderColor: isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.08)',
color: isDark ? '#94a3b8' : '#64748b',
}}
title="Compare"
>
<GitCompare className="w-3.5 h-3.5" />
</button>
<button
onClick={() => onSave?.(id)}
className="p-2 rounded-xl border transition-all hover:border-indigo-500/30"
style={{
borderColor: isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.08)',
color: isDark ? '#94a3b8' : '#64748b',
}}
title="Save"
>
<Bookmark className="w-3.5 h-3.5" />
</button>
</div>
</motion.div>
);
};
export default ScenarioCard;
|