"use client" import { useState, useMemo } from "react" import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query" import { useToast } from "@/components/ui/use-toast" import { Trans, useLingui } from "@lingui/react/macro" import { Button } from "@/components/ui/button" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog" import { Card, CardContent, CardHeader, CardTitle, CardDescription, } from "@/components/ui/card" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger, DropdownMenuCheckboxItem, } from "@/components/ui/dropdown-menu" import { Badge } from "@/components/ui/badge" import { Input } from "@/components/ui/input" import { getDomains, deleteDomain, refreshDomain, getStatusBadgeColor, getStatusLabel, formatDate, type Domain, } from "@/lib/domains" import { MoreHorizontal, Plus, RefreshCw, Globe, AlertTriangle, CheckCircle2, Clock, FilterIcon, LayoutGridIcon, LayoutListIcon, Tag, } from "lucide-react" import { DomainDialog } from "./domain-dialog" import { Link } from "@/components/router" import { useBrowserStorage } from "@/lib/utils" type ViewMode = "table" | "grid" type StatusFilter = "all" | "active" | "expiring" | "expired" | "unknown" | "paused" type DisplayOptions = { showSSL: boolean showRegistrar: boolean showExpiryDate: boolean showTags: boolean } // Days left badge component - big and visible function DaysLeftBadge({ days, label = "days" }: { days: number | undefined; label?: string }) { if (days === undefined || days === null) return - const isCritical = days >= 0 && days <= 7 const isWarning = days >= 0 && days <= 30 const isExpired = days < 0 const colorClass = isExpired ? "bg-red-500/15 text-red-600 border-red-500/30" : isCritical ? "bg-red-500/15 text-red-600 border-red-500/30" : isWarning ? "bg-yellow-500/15 text-yellow-600 border-yellow-500/30" : "bg-green-500/15 text-green-600 border-green-500/30" return (
{isExpired ? Math.abs(days) : days} {isExpired ? "EXPIRED" : days === 1 ? "DAY" : label.toUpperCase()}
) } export default function DomainsTable() { const { t } = useLingui() const { toast } = useToast() const queryClient = useQueryClient() const [dialogOpen, setDialogOpen] = useState(false) const [editingDomain, setEditingDomain] = useState(null) const [deleteConfirmId, setDeleteConfirmId] = useState(null) const [filter, setFilter] = useState("") const [statusFilter, setStatusFilter] = useState("all") const [tagFilter, setTagFilter] = useState("all") const [viewMode, setViewMode] = useBrowserStorage( "domainsViewMode", window.innerWidth < 1024 ? "grid" : "table" ) const [displayOptions, setDisplayOptions] = useBrowserStorage( "domainsDisplayOptions", { showSSL: true, showRegistrar: true, showExpiryDate: true, showTags: true } ) const { data: domains = [], isLoading } = useQuery({ queryKey: ["domains"], queryFn: getDomains, }) // Filter by status first const statusFilteredDomains = useMemo(() => { if (statusFilter === "all") return domains return domains.filter((d) => d.status === statusFilter) }, [domains, statusFilter]) // Then filter by search text and tags const filteredDomains = useMemo(() => { let result = statusFilteredDomains if (filter) { const f = filter.toLowerCase() result = result.filter( (d) => d.domain_name.toLowerCase().includes(f) || (d.registrar_name || "").toLowerCase().includes(f) ) } if (tagFilter !== "all") { result = result.filter((d) => d.tags?.includes(tagFilter)) } return result }, [statusFilteredDomains, filter, tagFilter]) // Extract all unique tags const allTags = useMemo(() => { const tagSet = new Set() domains.forEach((d) => d.tags?.forEach((tag) => tagSet.add(tag))) return Array.from(tagSet).sort() }, [domains]) const statusCounts = useMemo(() => { const total = domains.length const active = domains.filter((d) => d.status === "active").length const expiring = domains.filter((d) => d.status === "expiring").length const expired = domains.filter((d) => d.status === "expired").length const unknown = domains.filter((d) => d.status === "unknown").length const paused = domains.filter((d) => d.status === "paused").length return { total, active, expiring, expired, unknown, paused } }, [domains]) const deleteMutation = useMutation({ mutationFn: deleteDomain, onSuccess: () => { toast({ title: "Domain deleted successfully" }) queryClient.invalidateQueries({ queryKey: ["domains"] }) }, }) const refreshMutation = useMutation({ mutationFn: refreshDomain, onSuccess: () => { toast({ title: "Domain refresh started" }) queryClient.invalidateQueries({ queryKey: ["domains"] }) }, }) const handleEdit = (domain: Domain) => { setEditingDomain(domain) setDialogOpen(true) } const handleAdd = () => { setEditingDomain(null) setDialogOpen(true) } const handleDelete = (id: string) => { setDeleteConfirmId(id) } const handleRefresh = (id: string) => { refreshMutation.mutate(id) } const getStatusIcon = (status: string) => { switch (status) { case "active": return case "expiring": return case "expired": return default: return } } if (isLoading) { return (
Loading...
) } return ( <>
{/* Title row */}
Domain Monitoring Track domain expiry dates and watch domains for purchase ({statusCounts.active} {statusCounts.expiring > 0 && ( <> {" "} {statusCounts.expiring}{" "} )} {statusCounts.expired > 0 && ( <> {" "} {statusCounts.expired}{" "} )} {statusCounts.paused > 0 && ( <> {" "} {statusCounts.paused}{" "} )} / {statusCounts.total})
{/* Quick status filters */}
{[ { key: "all", label: `All ${statusCounts.total}`, color: "bg-primary" }, { key: "active", label: `Active ${statusCounts.active}`, color: "bg-green-500" }, { key: "expiring", label: `Expiring ${statusCounts.expiring}`, color: "bg-yellow-500" }, { key: "expired", label: `Expired ${statusCounts.expired}`, color: "bg-red-500" }, { key: "unknown", label: `Unknown ${statusCounts.unknown}`, color: "bg-gray-400" }, { key: "paused", label: `Paused ${statusCounts.paused}`, color: "bg-gray-400" }, ].map((s) => ( ))}
{/* Filter row */}
setFilter(e.target.value)} value={filter} className="w-full" />
{allTags.length > 0 && ( All Tags {allTags.map((tag) => ( {tag} ))} )} {/* Layout */} Layout setViewMode(v as ViewMode)}> Table Grid {/* Status Filter */} Status setStatusFilter(v as StatusFilter)}> All ({statusCounts.total}) Active ({statusCounts.active}) Expiring ({statusCounts.expiring}) Expired ({statusCounts.expired}) Unknown ({statusCounts.unknown}) {statusCounts.paused > 0 && ( Paused ({statusCounts.paused}) )} {/* Display Options */} Display Columns setDisplayOptions({ ...displayOptions, showSSL: checked })} > SSL Info setDisplayOptions({ ...displayOptions, showRegistrar: checked })} > Registrar setDisplayOptions({ ...displayOptions, showExpiryDate: checked })} > Expiry Date setDisplayOptions({ ...displayOptions, showTags: checked })} > Tags
{filteredDomains.length === 0 ? (
{filter || statusFilter !== "all" ? ( "No domains match your filters." ) : (

No domains tracked. Add domains to monitor their expiry dates or track domains you want to buy.

)}
) : viewMode === "table" ? (
Domain Status {displayOptions.showExpiryDate && Expiry} Days Left {displayOptions.showRegistrar && Registrar} {displayOptions.showSSL && SSL Expiry} {displayOptions.showTags && Tags} Actions {filteredDomains.map((domain) => ( {domain.favicon_url && ( (e.currentTarget.style.display = "none")} /> )} {domain.domain_name}
{getStatusIcon(domain.status)} {getStatusLabel(domain.status)}
{displayOptions.showExpiryDate && ( {domain.expiry_date ? formatDate(domain.expiry_date) : "Unknown"} )} {displayOptions.showRegistrar && ( {domain.registrar_name || "Unknown"} )} {displayOptions.showSSL && ( {domain.ssl_valid_to ? ( ) : ( - )} )} {displayOptions.showTags && (
{domain.tags?.map((tag: string) => ( {tag} ))}
)} handleEdit(domain)}> Edit handleRefresh(domain.id)} disabled={refreshMutation.isPending} > Refresh Visit handleDelete(domain.id)} className="text-destructive" > Delete
))}
) : (
{filteredDomains.map((domain) => (
{domain.favicon_url && ( (e.currentTarget.style.display = "none")} /> )}
{domain.domain_name}
handleEdit(domain)}>Edit handleRefresh(domain.id)} disabled={refreshMutation.isPending}> Refresh handleDelete(domain.id)} className="text-destructive"> Delete
{getStatusIcon(domain.status)} {getStatusLabel(domain.status)}
{displayOptions.showTags && domain.tags && domain.tags.length > 0 && (
{domain.tags.map((tag: string) => ( {tag} ))}
)}
{displayOptions.showExpiryDate && ( {domain.expiry_date ? formatDate(domain.expiry_date) : "Unknown"} )} {displayOptions.showRegistrar && ( {domain.registrar_name || "Unknown"} )}
{displayOptions.showSSL && domain.ssl_valid_to && ( )}
))}
)}
setDeleteConfirmId(null)}> Delete Domain Are you sure you want to delete this domain? This action cannot be undone. Cancel { if (deleteConfirmId) { deleteMutation.mutate(deleteConfirmId) setDeleteConfirmId(null) } }} className="bg-destructive text-destructive-foreground hover:bg-destructive/90" > Delete ) }