Files
gridpilot.gg/apps/website/components/dev/sections/ReplaySection.tsx
2026-01-18 16:18:18 +01:00

173 lines
4.9 KiB
TypeScript

'use client';
import React, { useState, useEffect } from 'react';
import { Play, Copy, Trash2, Download, Clock } from 'lucide-react';
import { getGlobalReplaySystem } from '@/lib/infrastructure/ErrorReplay';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
import { Icon } from '@/ui/Icon';
import { IconButton } from '@/ui/IconButton';
import { Button } from '@/ui/Button';
interface ReplayEntry {
id: string;
timestamp: string;
error: string;
type: string;
}
export function ReplaySection() {
const [replays, setReplays] = useState<ReplayEntry[]>([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
loadReplays();
}, []);
const loadReplays = () => {
const system = getGlobalReplaySystem();
const index = system.getReplayIndex();
setReplays(index);
};
const handleReplay = async (replayId: string) => {
setLoading(true);
try {
const system = getGlobalReplaySystem();
await system.replay(replayId);
} catch (error) {
console.error('Replay failed:', error);
} finally {
setLoading(false);
}
};
const handleExport = (replayId: string) => {
const system = getGlobalReplaySystem();
const data = system.exportReplay(replayId, 'json');
const blob = new Blob([data], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `replay_${replayId}.json`;
a.click();
URL.revokeObjectURL(url);
};
const handleCopy = async (replayId: string) => {
const system = getGlobalReplaySystem();
const data = system.exportReplay(replayId, 'json');
try {
await navigator.clipboard.writeText(data);
console.log('Replay data copied to clipboard');
} catch (err) {
console.error('Failed to copy:', err);
}
};
const handleDelete = (replayId: string) => {
if (confirm('Delete this replay?')) {
const system = getGlobalReplaySystem();
system.deleteReplay(replayId);
loadReplays();
}
};
const handleClearAll = () => {
if (confirm('Clear all replays?')) {
const system = getGlobalReplaySystem();
system.clearAll();
loadReplays();
}
};
return (
<Stack gap={2}>
<Box display="flex" alignItems="center" justifyContent="between">
<Text size="xs" weight="semibold" color="text-gray-400">Error Replay</Text>
<Box display="flex" gap={1}>
<IconButton
icon={Clock}
onClick={loadReplays}
variant="ghost"
size="sm"
title="Refresh"
/>
<IconButton
icon={Trash2}
onClick={handleClearAll}
variant="ghost"
size="sm"
title="Clear All"
color="rgb(239, 68, 68)"
/>
</Box>
</Box>
{replays.length === 0 ? (
<Box textAlign="center" py={2}>
<Text size="xs" color="text-gray-500">No replays available</Text>
</Box>
) : (
<Stack gap={1}>
{replays.map((replay) => (
<Box
key={replay.id}
bg="bg-deep-graphite"
border
borderColor="border-charcoal-outline"
rounded="md"
p={2}
>
<Box mb={1}>
<Text size="xs" font="mono" weight="bold" color="text-red-400" block truncate>
{replay.type}
</Text>
<Text size="xs" color="text-gray-300" block truncate>{replay.error}</Text>
<Text size="xs" color="text-gray-500" block>
{new Date(replay.timestamp).toLocaleTimeString()}
</Text>
</Box>
<Box display="flex" gap={1} mt={1}>
<Button
variant="primary"
onClick={() => handleReplay(replay.id)}
disabled={loading}
size="sm"
icon={<Icon icon={Play} size={3} />}
>
Replay
</Button>
<Button
variant="secondary"
onClick={() => handleExport(replay.id)}
size="sm"
icon={<Icon icon={Download} size={3} />}
>
Export
</Button>
<Button
variant="secondary"
onClick={() => handleCopy(replay.id)}
size="sm"
icon={<Icon icon={Copy} size={3} />}
>
Copy
</Button>
<IconButton
icon={Trash2}
onClick={() => handleDelete(replay.id)}
variant="secondary"
size="sm"
/>
</Box>
</Box>
))}
</Stack>
)}
</Stack>
);
}