Переработан дизайн, добавлена фича SoundLibrary
Deploy / build-and-deploy (push) Successful in 1m14s
Deploy / build-and-deploy (push) Successful in 1m14s
This commit is contained in:
+67
-124
@@ -151,125 +151,92 @@ export function BingoCard({ campaign, initialGrid, currentUserNickname, isAdmin
|
||||
"from-red-600 to-red-500";
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-4 w-full max-w-5xl mx-auto">
|
||||
<div className="flex flex-col items-center gap-2 w-full max-w-lg mx-auto">
|
||||
{/* Header */}
|
||||
<div className="text-center w-full">
|
||||
<h2 className="text-2xl font-bold text-cyan-300 font-mono tracking-wide uppercase">
|
||||
<h2 className="text-xl font-bold text-cyan-300 font-mono tracking-wide uppercase">
|
||||
{campaign.name}
|
||||
</h2>
|
||||
<p className="text-sm text-slate-500 font-mono mt-1">
|
||||
<p className="text-xs text-slate-500 font-mono">
|
||||
{markedCount}/{totalItems} cells marked
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Chaos Meter */}
|
||||
<div className="w-full space-y-1">
|
||||
<div className="flex justify-between text-xs font-mono text-slate-500">
|
||||
<span>{stageLabel}</span>
|
||||
<span>{Math.round(chaosLevel * 100)}% chaos</span>
|
||||
{/* Chaos + Subs compact row */}
|
||||
<div className="w-full flex gap-2 items-stretch">
|
||||
<div className="flex-1 min-w-0 space-y-0.5">
|
||||
<div className="flex justify-between text-[9px] font-mono text-slate-500">
|
||||
<span>{stageLabel}</span>
|
||||
<span>{Math.round(chaosLevel * 100)}% chaos</span>
|
||||
</div>
|
||||
<div className="w-full bg-slate-900/60 rounded-full h-2 overflow-hidden border border-slate-700/30">
|
||||
<div
|
||||
className={cn(
|
||||
"h-full rounded-full transition-all duration-500 ease-out bg-gradient-to-r",
|
||||
chaosStageClass,
|
||||
chaosLevel >= 0.6 && "animate-pulse"
|
||||
)}
|
||||
style={{ width: `${chaosLevel * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full bg-slate-900/60 rounded-full h-3 overflow-hidden border border-slate-700/30">
|
||||
<div
|
||||
className={cn(
|
||||
"h-full rounded-full transition-all duration-500 ease-out bg-gradient-to-r",
|
||||
chaosStageClass,
|
||||
chaosLevel >= 0.6 && "animate-pulse"
|
||||
)}
|
||||
style={{ width: `${chaosLevel * 100}%` }}
|
||||
/>
|
||||
<div className="hidden sm:flex gap-2 text-[9px] font-mono">
|
||||
<div className={cn("px-2 py-1 rounded border", hullHealth < 30 ? "border-red-800/50 text-red-400" : "border-slate-700/30 text-slate-500")}>
|
||||
Hull {Math.round(hullHealth)}%
|
||||
</div>
|
||||
<div className={cn("px-2 py-1 rounded border", reactorTemp > 120 ? "border-orange-800/50 text-orange-400" : "border-slate-700/30 text-slate-500")}>
|
||||
R{Math.round(reactorTemp)}°
|
||||
</div>
|
||||
<div className={cn("px-2 py-1 rounded border", o2Level < 30 ? "border-red-800/50 text-red-400" : "border-slate-700/30 text-slate-500")}>
|
||||
O₂ {Math.round(o2Level)}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sub Status + Grid Row */}
|
||||
<div className="flex gap-4 w-full">
|
||||
{/* Sub Status Panel */}
|
||||
<div className="hidden md:flex flex-col gap-2 w-48 shrink-0">
|
||||
<div className="bg-slate-900/60 border border-slate-700/30 rounded-lg p-3 space-y-2">
|
||||
<h3 className="text-[9px] font-mono text-slate-600 uppercase tracking-wider">Sub Status</h3>
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<div className="flex justify-between text-[9px] font-mono">
|
||||
<span className="text-slate-500">Hull</span>
|
||||
<span className={cn(hullHealth < 30 ? "text-red-400" : "text-slate-400")}>{Math.round(hullHealth)}%</span>
|
||||
</div>
|
||||
<div className="w-full h-1.5 bg-slate-800 rounded-full overflow-hidden">
|
||||
<div className={cn("h-full rounded-full transition-all duration-700", hullHealth < 30 ? "bg-red-500" : "bg-cyan-500")} style={{ width: `${hullHealth}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex justify-between text-[9px] font-mono">
|
||||
<span className="text-slate-500">Reactor</span>
|
||||
<span className={cn(reactorTemp > 120 ? "text-orange-400" : "text-slate-400")}>{Math.round(reactorTemp)}°C</span>
|
||||
</div>
|
||||
<div className="w-full h-1.5 bg-slate-800 rounded-full overflow-hidden">
|
||||
<div className={cn("h-full rounded-full transition-all duration-700", reactorTemp > 120 ? "bg-orange-500 animate-pulse" : "bg-cyan-500")} style={{ width: `${Math.min(reactorTemp / 2, 100)}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex justify-between text-[9px] font-mono">
|
||||
<span className="text-slate-500">O₂</span>
|
||||
<span className={cn(o2Level < 30 ? "text-red-400" : "text-slate-400")}>{Math.round(o2Level)}%</span>
|
||||
</div>
|
||||
<div className="w-full h-1.5 bg-slate-800 rounded-full overflow-hidden">
|
||||
<div className={cn("h-full rounded-full transition-all duration-700", o2Level < 30 ? "bg-red-500" : "bg-cyan-500")} style={{ width: `${o2Level}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-[8px] font-mono text-slate-700 pt-1 border-t border-slate-700/30">
|
||||
{chaosLevel < 0.25 && "All systems nominal"}
|
||||
{chaosLevel >= 0.25 && chaosLevel < 0.5 && "⚠️ Minor breaches detected"}
|
||||
{chaosLevel >= 0.5 && chaosLevel < 0.75 && "🚨 EVACUATE! SUB COMPROMISED"}
|
||||
{chaosLevel >= 0.75 && "☢️ MELTDOWN IN PROGRESS"}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Activity Log */}
|
||||
<div className="bg-slate-900/60 border border-slate-700/30 rounded-lg p-3 flex-1 min-h-0 max-h-60 overflow-y-auto">
|
||||
<h3 className="text-[9px] font-mono text-slate-600 uppercase tracking-wider mb-2 sticky top-0 bg-slate-900/80 pb-1">Crew Log</h3>
|
||||
<div className="space-y-1">
|
||||
{activityLog.length === 0 && (
|
||||
<p className="text-[9px] font-mono text-slate-700 italic">Awaiting chaos...</p>
|
||||
)}
|
||||
{activityLog.map((entry, i) => (
|
||||
<p key={i} className="text-[9px] font-mono text-slate-500 leading-relaxed">{entry}</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bingo Grid */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div
|
||||
className="grid gap-1.5 w-full"
|
||||
style={{ gridTemplateColumns: `repeat(${campaign.gridSize}, 1fr)` }}
|
||||
>
|
||||
{grid.map((cell) => (
|
||||
<BingoCell
|
||||
key={cell.index}
|
||||
item={cell.item}
|
||||
index={cell.index}
|
||||
marked={cell.marked}
|
||||
markCount={cell.markCount}
|
||||
markedBy={cell.markedBy}
|
||||
gridSize={campaign.gridSize}
|
||||
onMark={markItem}
|
||||
onUnmark={unmarkItem}
|
||||
disabled={marking !== null}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{/* Bingo Grid */}
|
||||
<div className="w-full">
|
||||
<div
|
||||
className="grid gap-1"
|
||||
style={{ gridTemplateColumns: `repeat(${campaign.gridSize}, 1fr)` }}
|
||||
>
|
||||
{grid.map((cell) => (
|
||||
<BingoCell
|
||||
key={cell.index}
|
||||
item={cell.item}
|
||||
index={cell.index}
|
||||
marked={cell.marked}
|
||||
markCount={cell.markCount}
|
||||
markedBy={cell.markedBy}
|
||||
gridSize={campaign.gridSize}
|
||||
onMark={markItem}
|
||||
onUnmark={unmarkItem}
|
||||
disabled={marking !== null}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bingo Lines */}
|
||||
{bingoLines.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2 items-center justify-center">
|
||||
<Badge variant="destructive" className="text-sm px-4 py-1 animate-pulse shadow-lg shadow-red-600/30">
|
||||
🏆 BINGO! {bingoLines.length} line(s)
|
||||
</Badge>
|
||||
</div>
|
||||
<Badge variant="destructive" className="text-xs px-3 py-1 animate-pulse shadow-lg shadow-red-600/30">
|
||||
🏆 BINGO! {bingoLines.length} line(s)
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{/* Activity Log */}
|
||||
<div className="w-full bg-slate-900/60 border border-slate-700/30 rounded-lg p-2 max-h-28 overflow-y-auto">
|
||||
<h3 className="text-[9px] font-mono text-slate-600 uppercase tracking-wider mb-1 sticky top-0 bg-slate-900/80 pb-0.5">Crew Log</h3>
|
||||
{activityLog.length === 0 ? (
|
||||
<p className="text-[9px] font-mono text-slate-700 italic">Awaiting chaos...</p>
|
||||
) : (
|
||||
<div className="space-y-0.5">
|
||||
{activityLog.map((entry, i) => (
|
||||
<p key={i} className="text-[9px] font-mono text-slate-500 leading-relaxed">{entry}</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Bingo Celebration Modal */}
|
||||
{showBingo && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm" onClick={() => setShowBingo(false)}>
|
||||
@@ -284,30 +251,6 @@ export function BingoCard({ campaign, initialGrid, currentUserNickname, isAdmin
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Mobile Activity Log */}
|
||||
<div className="md:hidden w-full bg-slate-900/60 border border-slate-700/30 rounded-lg p-3 max-h-40 overflow-y-auto">
|
||||
<h3 className="text-[9px] font-mono text-slate-600 uppercase tracking-wider mb-2">Crew Log</h3>
|
||||
<div className="space-y-1">
|
||||
{activityLog.length === 0 && (
|
||||
<p className="text-[9px] font-mono text-slate-700 italic">Awaiting chaos...</p>
|
||||
)}
|
||||
{activityLog.map((entry, i) => (
|
||||
<p key={i} className="text-[9px] font-mono text-slate-500 leading-relaxed">{entry}</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Legend */}
|
||||
<div className="flex flex-wrap gap-2 justify-center text-[10px] text-slate-600 font-mono">
|
||||
<span>🎺 horn</span>
|
||||
<span>🚨 alarm</span>
|
||||
<span>🌊 flood</span>
|
||||
<span>💥 boom</span>
|
||||
<span>👹 monster</span>
|
||||
<span>💀 death</span>
|
||||
<span>🔥 chaos</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user