diff --git a/app/contracts/page.tsx b/app/contracts/page.tsx
index fbcf36f..6976d9e 100644
--- a/app/contracts/page.tsx
+++ b/app/contracts/page.tsx
@@ -7,622 +7,5 @@ export default function ContractsPage() {
}>
-'use client'
-
-import { useEffect, useMemo, useRef, useState } from 'react'
-import { useSearchParams } from 'next/navigation'
-import Link from 'next/link'
-import { WatchedContract, Network } from '@/types'
-import { getContracts, getAlerts } from '@/lib/storage'
-import {
- refreshContracts,
- syncDeleteContract,
- syncSaveContract,
- getSyncStatuses,
- onSyncStatusesChange,
- type SyncStatus
-} from '@/lib/contractSync'
-import {
- loadContractPrefs,
- saveContractPrefs,
- resolveContractPrefs,
- sortContracts,
- SortOption,
- ContractViewMode
-} from '@/lib/contractSort'
-import ContractCard from '@/components/ContractCard'
-import EmptyState from '@/components/EmptyState'
-import ContractsSkeleton from '@/components/ContractsSkeleton'
-import { useToast } from '@/components/ToastProvider'
-import { getContracts, getAlerts, deleteContract } from '@/lib/storage'
-import { refreshContracts, syncSaveContract } from '@/lib/contractSync'
-import ContractCard from '@/components/ContractCard'
-import EmptyState from '@/components/EmptyState'
-import ContractsSkeleton from '@/components/ContractsSkeleton'
-import { networkDotColors } from '@/components/NetworkBadge'
-
-type NetworkFilter = 'all' | Network
-
-const PAGE_SIZE = 12
-const HIGHLIGHT_DURATION_MS = 6000
-
-const NETWORK_LABELS: Record
= {
- mainnet: 'Mainnet',
- testnet: 'Testnet',
- futurenet: 'Futurenet',
-}
-
-const NETWORK_FILTERS: { value: NetworkFilter; label: string }[] = [
- { value: 'all', label: 'All Networks' },
- { value: 'mainnet', label: 'Mainnet' },
- { value: 'testnet', label: 'Testnet' },
- { value: 'futurenet', label: 'Futurenet' },
-]
-
-const SORT_OPTIONS: { value: SortOption; label: string }[] = [
- { value: 'newest', label: 'Newest first' },
- { value: 'oldest', label: 'Oldest first' },
- { value: 'label-asc', label: 'Label A–Z' },
- { value: 'label-desc', label: 'Label Z–A' },
-]
-
-const VIEW_MODES: ContractViewMode[] = ['flat', 'grouped']
-
-export default function ContractsPage() {
- const searchParams = useSearchParams()
- const { showToast } = useToast()
-
- const [allContracts, setAllContracts] = useState([])
- const [syncStatuses, setSyncStatuses] = useState>({})
- const [networkFilter, setNetworkFilter] = useState('all')
- const [sortBy, setSortBy] = useState('newest')
- const [search, setSearch] = useState(() => searchParams?.get('q') ?? '')
- const [mounted, setMounted] = useState(false)
- const [viewMode, setViewMode] = useState('flat')
- const [page, setPage] = useState(1)
- const [highlightedId, setHighlightedId] = useState(null)
- const highlightTimer = useRef | null>(null)
- const [selectionMode, setSelectionMode] = useState(false)
- const [selectedIds, setSelectedIds] = useState([])
- const [confirmOpen, setConfirmOpen] = useState(false)
-
- useEffect(() => {
- const all = getContracts()
- setAllContracts(all)
- setMounted(true)
- refreshContracts().then((r) => setAllContracts(r.contracts))
-
- setSyncStatuses(getSyncStatuses())
- return onSyncStatusesChange(() => {
- setSyncStatuses(getSyncStatuses())
- })
- }, [])
-
- useEffect(() => {
- const initial = resolveContractPrefs({
- view: searchParams?.get('view'),
- sort: searchParams?.get('sort'),
- })
- setViewMode(initial.viewMode)
- setSortBy(initial.sortBy)
- }, [searchParams])
-
- useEffect(() => {
- try {
- const id = sessionStorage.getItem('txwatch_last_created_contract')
- if (id) {
- setHighlightedId(id)
- sessionStorage.removeItem('txwatch_last_created_contract')
- if (highlightTimer.current) clearTimeout(highlightTimer.current)
- highlightTimer.current = setTimeout(() => {
- setHighlightedId(null)
- highlightTimer.current = null
- }, HIGHLIGHT_DURATION_MS)
- }
- } catch {
- // ignore
- }
- return () => {
- if (highlightTimer.current) {
- clearTimeout(highlightTimer.current)
- highlightTimer.current = null
- }
- }
- }, [])
-
- useEffect(() => {
- setPage(1)
- }, [networkFilter, search])
-
- const networkFiltered = useMemo(() => {
- if (networkFilter === 'all') {
- return allContracts
- }
- return allContracts.filter((contract) => contract.network === networkFilter)
- }, [allContracts, networkFilter])
-
- const filtered = useMemo(() => {
- const query = search.trim().toLowerCase()
- if (!query) return networkFiltered
-
- return networkFiltered.filter((contract) => {
- return (
- contract.label.toLowerCase().includes(query) ||
- contract.contract_id.toLowerCase().includes(query)
- )
- })
- }, [networkFiltered, search])
-
- const sorted = useMemo(() => sortContracts(filtered, sortBy), [filtered, sortBy])
- const totalPages = Math.max(1, Math.ceil(sorted.length / PAGE_SIZE))
- const paginated = useMemo(
- () => sorted.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE),
- [page, sorted]
- )
-
- useEffect(() => {
- if (page > totalPages) {
- setPage(totalPages)
- }
- }, [page, totalPages])
-
- useEffect(() => {
- if (!highlightedId) return
- const index = sorted.findIndex((contract) => contract.id === highlightedId)
- if (index === -1) return
- const targetPage = Math.floor(index / PAGE_SIZE) + 1
- setPage((current) => (current === targetPage ? current : targetPage))
- }, [highlightedId, sorted])
-
- useEffect(() => {
- if (!highlightedId) return
- const el = document.getElementById(`contract-${highlightedId}`)
- if (el && typeof el.scrollIntoView === 'function') {
- el.scrollIntoView({ behavior: 'smooth', block: 'center' })
- }
- }, [highlightedId, page, paginated])
-
- const grouped = useMemo(() => {
- const groups: Record = {
- mainnet: [],
- testnet: [],
- futurenet: [],
- }
- for (const contract of sorted) {
- groups[contract.network].push(contract)
- }
- return groups
- }, [sorted])
-
- const selectedContracts = useMemo(
- () => allContracts.filter((contract) => selectedIds.includes(contract.id)),
- [allContracts, selectedIds]
- )
-
- const selectedAlertCount = useMemo(() => {
- if (selectedIds.length === 0) return 0
- const alerts = getAlerts()
- return alerts.filter((alert) => selectedIds.includes(alert.contract_id)).length
- }, [selectedIds])
-
- const allFilteredSelected =
- filtered.length > 0 && filtered.every((contract) => selectedIds.includes(contract.id))
-
- const toggleSelectionMode = () => {
- setSelectionMode((prev) => {
- if (prev) setSelectedIds([])
- return !prev
- })
- }
-
- const toggleSelected = (id: string) => {
- setSelectedIds((prev) =>
- prev.includes(id) ? prev.filter((value) => value !== id) : [...prev, id]
- )
- }
-
- const toggleSelectAll = () => {
- if (allFilteredSelected) {
- const filteredIds = new Set(filtered.map((contract) => contract.id))
- setSelectedIds((prev) => prev.filter((id) => !filteredIds.has(id)))
- } else {
- setSelectedIds((prev) => {
- const next = new Set(prev)
- for (const contract of filtered) next.add(contract.id)
- return Array.from(next)
- })
- }
- }
-
- const handleDeleteSelected = async () => {
- setConfirmOpen(false)
- setSelectionMode(false)
-
- const idsToDelete = [...selectedIds]
- setSelectedIds([])
-
- // Optimistic local UI removal
- setAllContracts((prev) => prev.filter((contract) => !idsToDelete.includes(contract.id)))
-
- const results = await Promise.allSettled(
- idsToDelete.map(id => syncDeleteContract(id))
- )
-
- let failCount = 0
- results.forEach((res) => {
- if (res.status === 'rejected' || (res.status === 'fulfilled' && !res.value.ok)) {
- failCount++
- }
- })
-
- if (failCount > 0) {
- showToast(`${failCount} of ${idsToDelete.length} could not be deleted on the server`, 'error')
- // Restore failed deletions from truth
- refreshContracts().then((r) => setAllContracts(r.contracts))
- } else {
- showToast(`Deleted ${idsToDelete.length} contract(s)`, 'success')
- }
- }
-
- const handlePauseSelected = () => {
- for (const id of selectedIds) {
- const contract = allContracts.find(c => c.id === id)
- if (contract) {
- syncSaveContract({ ...contract, enabled: false }, false)
- }
- }
- setAllContracts((prev) => prev.map((c) => selectedIds.includes(c.id) ? { ...c, enabled: false } : c))
- setSelectedIds([])
- setSelectionMode(false)
- }
-
- const handlePauseSelected = () => {
- for (const id of selectedIds) {
- const contract = allContracts.find(c => c.id === id)
- if (contract) {
- syncSaveContract({ ...contract, enabled: false }, false)
- }
- }
- setAllContracts((prev) => prev.map((c) => selectedIds.includes(c.id) ? { ...c, enabled: false } : c))
- setSelectedIds([])
- setSelectionMode(false)
- }
-
- if (!mounted) return
-
- const hasAnyContracts = allContracts.length > 0
- const hasFilteredContracts = filtered.length > 0
-
- return (
-
-
-
-
Contracts
-
{filtered.length} registered
-
-
-
-
-
-
-
-
-
setSearch(event.target.value)}
- placeholder="Search by label or contract ID"
- className="w-full rounded-lg border border-zinc-700 bg-zinc-900 py-2 pl-10 pr-3 text-sm text-zinc-100 placeholder:text-zinc-500 focus:border-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500/20"
- />
-
-
- {search && (
-
- )}
-
-
-
-
-
- {NETWORK_FILTERS.map((filter) => (
-
- ))}
-
-
-
-
-
-
-
-
- {hasAnyContracts && (
-
-
-
-
-
-
- )}
-
-
- {VIEW_MODES.map((mode) => {
- const isActive = viewMode === mode
- return (
-
- )
- })}
-
-
-
-
-
-
- {selectionMode && (
-
-
-
- {selectedIds.length} selected
-
-
-
-
-
-
- )}
-
- {!hasAnyContracts ? (
-
- Add contract
-
- }
- />
- ) : !hasFilteredContracts ? (
-
- ) : viewMode === 'flat' ? (
-
- {paginated.map((contract) => (
-
- ))}
-
- ) : (
- description="Try adjusting your search or filter."
- />
- ) : viewMode === 'grouped' ? (
-
- {(Object.keys(grouped) as Network[]).map((network) => {
- const contracts = grouped[network]
- if (contracts.length === 0) return null
- return (
-
-
- {NETWORK_LABELS[network]}
- {contracts.length}
-
-
- {contracts.map((contract) => (
-
- ))}
-
-
- )
- })}
-
- ) : (
-
- {paginated.map((contract) => (
-
- ))}
-
- )}
-
- {viewMode === 'flat' && totalPages > 1 && (
-
-