Files
DynamoDNS/client/src/components/domain/domain-table.tsx
T
alphaeusmote be64a9b1c3 Checkpoint
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9111ef36-26c8-4085-84ca-a35dc1fec1b5
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7083d608-d6d3-4a6a-9a27-6286c5109627/79fe97ba-9acb-4084-b598-67aa27403958.jpg
2025-04-10 11:53:22 +00:00

257 lines
8.7 KiB
TypeScript

import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { Domain, Provider } from "@shared/schema";
import { Link } from "wouter";
import { useOrganization } from "@/context/organization-context";
import { Pagination } from "@/components/shared/pagination";
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { formatDistanceToNow } from "date-fns";
import { Cloud, Home, MoreVertical, Loader2, CircleHelp } from "lucide-react";
interface DomainTableProps {
onManageDomain?: (domain: Domain) => void;
onDeleteDomain?: (domain: Domain) => void;
onAddDomain?: () => void;
}
export function DomainTable({ onManageDomain, onDeleteDomain, onAddDomain }: DomainTableProps) {
const { currentOrganization } = useOrganization();
const [currentPage, setCurrentPage] = useState(1);
const pageSize = 5;
const { data: domains = [], isLoading } = useQuery<Domain[]>({
queryKey: ["/api/domains", currentOrganization?.id],
enabled: !!currentOrganization,
});
// Get providers to display provider names
const { data: providers = [] } = useQuery<Provider[]>({
queryKey: ["/api/providers"],
});
const getProviderName = (providerId: string | null | undefined) => {
// Add enhanced debugging to help track down the issue
console.log("Provider lookup - ID provided:", providerId, typeof providerId);
console.log("Available providers:", providers.map(p => ({ id: p.id, name: p.name })));
// Handle completely empty providerId cases
if (providerId === null || providerId === undefined || providerId === "") {
console.log("Provider is empty/null");
return "None";
}
// Check if the provider exists in our providers list
// The providerId from the database will be a string UUID
const provider = providers.find(p => p.id === providerId);
if (provider) {
console.log("Found provider:", provider.name);
return provider.name;
} else {
console.log("Provider not found for ID:", providerId);
// If we have a providerId but can't find the provider (might be deleted)
return "Unknown Provider";
}
};
const getProviderIcon = (providerId: string | null | undefined) => {
// Handle completely empty providerId cases
if (!providerId || providerId === "") {
return <CircleHelp className="mr-2 text-muted-foreground" size={16} />;
}
// The providerId from the database will be a string UUID
const provider = providers.find(p => p.id === providerId);
if (!provider) {
return <CircleHelp className="mr-2 text-warning" size={16} />;
}
switch (provider.type) {
case "cloudflare":
return <Cloud className="mr-2 text-primary" size={16} />;
case "route53":
return <Home className="mr-2 text-primary" size={16} />;
default:
return <CircleHelp className="mr-2 text-primary" size={16} />;
}
};
const getStatusBadge = (domain: Domain) => {
if (!domain.isActive) {
return (
<span className="px-2 py-1 text-xs font-semibold rounded-full bg-muted/30 text-muted-foreground">
Inactive
</span>
);
}
// If updated within the last hour, show "Active"
if (domain.lastUpdated && new Date(domain.lastUpdated).getTime() > Date.now() - 3600000) {
return (
<span className="px-2 py-1 text-xs font-semibold rounded-full bg-success/10 text-success">
Active
</span>
);
}
// If updated more than 24 hours ago, show "Update Pending"
if (domain.lastUpdated && new Date(domain.lastUpdated).getTime() < Date.now() - 86400000) {
return (
<span className="px-2 py-1 text-xs font-semibold rounded-full bg-warning/10 text-warning">
Update Pending
</span>
);
}
return (
<span className="px-2 py-1 text-xs font-semibold rounded-full bg-success/10 text-success">
Active
</span>
);
};
// Calculate pagination
const startIndex = (currentPage - 1) * pageSize;
const endIndex = startIndex + pageSize;
const paginatedDomains = domains.slice(startIndex, endIndex);
const totalPages = Math.ceil(domains.length / pageSize);
if (isLoading) {
return (
<div className="flex justify-center items-center py-8">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
</div>
);
}
if (domains.length === 0) {
return (
<div className="text-center py-8 border rounded-lg">
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-primary/10">
<Home className="h-6 w-6 text-primary" />
</div>
<h3 className="mt-4 text-lg font-medium">No domains found</h3>
<p className="mt-2 text-sm text-muted-foreground">
Get started by adding your first domain.
</p>
<div className="mt-6">
<Button onClick={onAddDomain}>Add Domain</Button>
</div>
</div>
);
}
return (
<div className="bg-card rounded-lg shadow-sm border border-border">
<div className="p-5 border-b border-border flex justify-between items-center">
<h2 className="text-lg font-semibold">Managed Domains</h2>
<Button onClick={onAddDomain}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="mr-1"
>
<path d="M5 12h14"></path>
<path d="M12 5v14"></path>
</svg>
Add Domain
</Button>
</div>
<div className="overflow-x-auto">
<Table>
<TableHeader>
<TableRow>
<TableHead>Domain</TableHead>
<TableHead>Provider</TableHead>
<TableHead>Records</TableHead>
<TableHead>Last Update</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{paginatedDomains.map(domain => (
<TableRow key={domain.id}>
<TableCell className="font-medium">{domain.name}</TableCell>
<TableCell>
<div className="flex items-center">
{getProviderIcon(domain.providerId)}
{getProviderName(domain.providerId)}
</div>
</TableCell>
<TableCell>
<Link href={`/dns-records?domainId=${domain.id}`}>
<div className="text-primary hover:underline cursor-pointer">View Records</div>
</Link>
</TableCell>
<TableCell>
{domain.lastUpdated
? formatDistanceToNow(new Date(domain.lastUpdated), { addSuffix: true })
: "Never"}
</TableCell>
<TableCell>{getStatusBadge(domain)}</TableCell>
<TableCell className="text-right">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<MoreVertical className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => onManageDomain && onManageDomain(domain)}
>
Manage
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => onDeleteDomain && onDeleteDomain(domain)}
className="text-destructive focus:text-destructive"
>
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
<div className="p-4 border-t border-border">
<Pagination
currentPage={currentPage}
totalPages={totalPages}
onPageChange={setCurrentPage}
itemLabel="domains"
totalItems={domains.length}
itemsPerPage={pageSize}
/>
</div>
</div>
);
}