'use client' import { useEffect, useState } from 'react' import { useParams, useRouter } from 'next/navigation' import { getSite, type Site } from '@/lib/api/sites' import { listReplays, formatDuration, type ReplayListItem, type ReplayFilters } from '@/lib/api/replays' import { toast } from 'sonner' import LoadingOverlay from '@/components/LoadingOverlay' function formatDate(dateString: string) { const date = new Date(dateString) return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) } function getFlagEmoji(countryCode: string | null) { if (!countryCode || countryCode.length !== 2) return '🌍' const codePoints = countryCode .toUpperCase() .split('') .map(char => 127397 + char.charCodeAt(0)) return String.fromCodePoint(...codePoints) } function getDeviceEmoji(deviceType: string | null) { switch (deviceType?.toLowerCase()) { case 'mobile': return '📱' case 'tablet': return '📱' default: return '💻' } } export default function ReplaysPage() { const params = useParams() const router = useRouter() const siteId = params.id as string const [site, setSite] = useState(null) const [replays, setReplays] = useState([]) const [loading, setLoading] = useState(true) const [total, setTotal] = useState(0) const [filters, setFilters] = useState({ limit: 20, offset: 0, }) // Load site info and replays useEffect(() => { const init = async () => { try { const siteData = await getSite(siteId) setSite(siteData) } catch (error: unknown) { toast.error('Failed to load site') } finally { setLoading(false) } } init() }, [siteId]) // Load replays when filters change useEffect(() => { const loadReplays = async () => { try { const response = await listReplays(siteId, filters) setReplays(response.replays || []) setTotal(response.total) } catch (error: unknown) { toast.error('Failed to load replays') } } if (site) { loadReplays() } }, [siteId, site, filters]) const handlePageChange = (newOffset: number) => { setFilters(prev => ({ ...prev, offset: newOffset })) } if (loading) return if (!site) return
Site not found
const currentPage = Math.floor((filters.offset || 0) / (filters.limit || 20)) + 1 const totalPages = Math.ceil(total / (filters.limit || 20)) return (

Session Replays {total} recordings

{site.replay_mode === 'disabled' && (
⚠️ Session replay is disabled
)}
{/* Filters */}
{/* Replays List */}
{replays.length === 0 ? (
🎬

No session replays yet

{site.replay_mode === 'disabled' ? 'Enable session replay in settings to start recording visitor sessions.' : 'Recordings will appear here once visitors start interacting with your site.'}

) : ( <> {replays.map((replay) => ( router.push(`/sites/${siteId}/replays/${replay.id}`)} > ))}
Session Entry Page Duration Device Location Date Actions
{replay.is_skeleton_mode && ( Skeleton )} {replay.session_id.substring(0, 8)}...
{replay.entry_page} {formatDuration(replay.duration_ms)}
{getDeviceEmoji(replay.device_type)} {replay.browser || 'Unknown'}
{getFlagEmoji(replay.country)} {replay.country || 'Unknown'} {formatDate(replay.started_at)}
{/* Pagination */} {totalPages > 1 && (
Showing {(filters.offset || 0) + 1} - {Math.min((filters.offset || 0) + (filters.limit || 20), total)} of {total}
Page {currentPage} of {totalPages}
)} )}
) }