fix: wrap GameOverScreen in ErrorBoundary and add defensive checks for day 30 blank page bug
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 2m25s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 2m25s
- Wrap GameOverScreen in ErrorBoundary in page.tsx to prevent blank page on render errors - Add defensive Number.isFinite checks in GameOverScreen for all numeric props - Add regression test for day 30 → game-over flow (day30-blank-page.test.ts) Fixes #375
This commit is contained in:
@@ -17,6 +17,12 @@ export function GameOverScreen({ day, hour, insightGained, totalInsight }: GameO
|
||||
useGameStore.getState().startNewLoop();
|
||||
};
|
||||
|
||||
// Defensive: ensure all values are valid numbers (guard against render-time edge cases)
|
||||
const safeDay = Number.isFinite(day) ? day : 0;
|
||||
const safeHour = Number.isFinite(hour) ? hour : 0;
|
||||
const safeInsightGained = Number.isFinite(insightGained) ? insightGained : 0;
|
||||
const safeTotalInsight = Number.isFinite(totalInsight) ? totalInsight : 0;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 game-overlay flex items-center justify-center z-50">
|
||||
<Card className="bg-gray-900 border-gray-600 max-w-md w-full mx-4 shadow-2xl">
|
||||
@@ -32,19 +38,19 @@ export function GameOverScreen({ day, hour, insightGained, totalInsight }: GameO
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="p-3 bg-gray-800 rounded">
|
||||
<div className="text-xl font-bold text-amber-400 game-mono">{fmt(insightGained)}</div>
|
||||
<div className="text-xl font-bold text-amber-400 game-mono">{fmt(safeInsightGained)}</div>
|
||||
<div className="text-xs text-gray-400">Insight Gained</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-800 rounded">
|
||||
<div className="text-xl font-bold text-blue-400 game-mono">{day}</div>
|
||||
<div className="text-xl font-bold text-blue-400 game-mono">{safeDay}</div>
|
||||
<div className="text-xs text-gray-400">Day Reached</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-800 rounded">
|
||||
<div className="text-xl font-bold text-purple-400 game-mono">{formatHour(hour)}</div>
|
||||
<div className="text-xl font-bold text-purple-400 game-mono">{formatHour(safeHour)}</div>
|
||||
<div className="text-xs text-gray-400">Hour</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-800 rounded">
|
||||
<div className="text-xl font-bold text-green-400 game-mono">{fmt(totalInsight)}</div>
|
||||
<div className="text-xl font-bold text-green-400 game-mono">{fmt(safeTotalInsight)}</div>
|
||||
<div className="text-xs text-gray-400">Total Insight</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+9
-1
@@ -169,7 +169,15 @@ export default function ManaLoopGame() {
|
||||
useEffect(() => { setMounted(true); }, []); // eslint-disable-line react-hooks/set-state-in-effect
|
||||
|
||||
if (gameOver) {
|
||||
return <GameOverScreen day={day} hour={hour} insightGained={loopInsight} totalInsight={insight} />;
|
||||
return (
|
||||
<ErrorBoundary
|
||||
onReset={() => {
|
||||
useGameStore.getState().resetGame();
|
||||
}}
|
||||
>
|
||||
<GameOverScreen day={day} hour={hour} insightGained={loopInsight} totalInsight={insight} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
if (!mounted) return <div className="p-4 text-center text-gray-400">Loading...</div>;
|
||||
|
||||
Reference in New Issue
Block a user