import { useState, useEffect, useCallback } from "react" import { createFileRoute, Link } from "@tanstack/react-router" import { authClient } from "@/lib/auth-client" import { Search, Star, ExternalLink, ChevronDown, ChevronRight, Trash2, Clock, Globe, } from "lucide-react" import type { BrowserSession, BrowserSessionTab } from "@/db/schema" export const Route = createFileRoute("/sessions")({ component: BrowserSessionsPage, ssr: false, }) interface SessionWithTabs extends BrowserSession { tabs?: BrowserSessionTab[] } interface PaginationInfo { page: number limit: number total: number totalPages: number } function getDomain(url: string): string { try { return new URL(url).hostname.replace("www.", "") } catch { return url } } function formatDate(date: Date | string): string { const d = typeof date === "string" ? new Date(date) : date return d.toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric", }) } function SessionCard({ session, onToggleFavorite, onDelete, }: { session: SessionWithTabs onToggleFavorite: (id: string, isFavorite: boolean) => void onDelete: (id: string) => void }) { const [expanded, setExpanded] = useState(false) const [tabs, setTabs] = useState([]) const [loading, setLoading] = useState(false) const loadTabs = useCallback(async () => { if (tabs.length > 0) return setLoading(true) try { const res = await fetch(`/api/browser-sessions/${session.id}`) if (res.ok) { const data = await res.json() setTabs(data.tabs || []) } } catch (error) { console.error("Failed to load tabs:", error) } finally { setLoading(false) } }, [session.id, tabs.length]) const handleExpand = () => { if (!expanded) { loadTabs() } setExpanded(!expanded) } return (

{session.name}

{formatDate(session.captured_at)} | {session.browser} | {session.tab_count} tabs
{expanded && (
{loading ? (

Loading tabs...

) : tabs.length === 0 ? (

No tabs found

) : ( )}
)}
) } function BrowserSessionsPage() { const { data: session, isPending: authPending } = authClient.useSession() const [sessions, setSessions] = useState([]) const [pagination, setPagination] = useState({ page: 1, limit: 50, total: 0, totalPages: 0, }) const [search, setSearch] = useState("") const [loading, setLoading] = useState(true) const fetchSessions = useCallback( async (page = 1, searchQuery = "") => { setLoading(true) try { const body = { action: "list" as const, page, limit: pagination.limit, search: searchQuery || undefined, } const res = await fetch("/api/browser-sessions", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }) if (res.ok) { const data = await res.json() setSessions(data.sessions || []) setPagination(data.pagination) } } catch (error) { console.error("Failed to fetch sessions:", error) } finally { setLoading(false) } }, [pagination.limit], ) useEffect(() => { if (session?.user) { fetchSessions(1, search) } }, [session?.user, fetchSessions, search]) const handleToggleFavorite = async (id: string, isFavorite: boolean) => { try { const res = await fetch(`/api/browser-sessions/${id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ is_favorite: isFavorite }), }) if (res.ok) { setSessions((prev) => prev.map((s) => (s.id === id ? { ...s, is_favorite: isFavorite } : s)), ) } } catch (error) { console.error("Failed to update favorite:", error) } } const handleDelete = async (id: string) => { try { const res = await fetch(`/api/browser-sessions/${id}`, { method: "DELETE", }) if (res.ok) { setSessions((prev) => prev.filter((s) => s.id !== id)) setPagination((prev) => ({ ...prev, total: prev.total - 1 })) } } catch (error) { console.error("Failed to delete session:", error) } } if (authPending) { return (

Loading...

) } if (!session?.user) { return (

Sign in to view your browser sessions

Sign in
) } // Group sessions by date const sessionsByDate = sessions.reduce( (acc, s) => { const date = formatDate(s.captured_at) if (!acc[date]) acc[date] = [] acc[date].push(s) return acc }, {} as Record, ) return (

Browser Sessions

{pagination.total} sessions saved

{/* Search */}
setSearch(e.target.value)} placeholder="Search sessions..." className="w-full pl-10 pr-4 py-2.5 bg-zinc-900 border border-zinc-800 rounded-xl text-white placeholder-zinc-600 focus:outline-none focus:border-zinc-700 focus:ring-1 focus:ring-zinc-700" />
{/* Sessions list */} {loading ? (
Loading sessions...
) : sessions.length === 0 ? (
{search ? `No sessions found for "${search}"` : "No sessions yet"}
) : (
{Object.entries(sessionsByDate).map(([date, dateSessions]) => (

{date}

{dateSessions.map((s) => ( ))}
))}
)} {/* Pagination */} {pagination.totalPages > 1 && ( )}
) }