"use client"; import { useQuery } from "@tanstack/react-query"; import { useState } from "react"; import { useTranslations } from "next-intl"; import { AlertTriangle, ArrowUpDown, MoreHorizontal } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { DataTable, ExtendedColumnDef } from "@app/components/ui/data-table"; import { Button } from "@app/components/ui/button"; import { Badge } from "@app/components/ui/badge"; import { Switch } from "@app/components/ui/switch"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@app/components/ui/dropdown-menu"; import CopyToClipboard from "@app/components/CopyToClipboard"; import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; import CustomDnsRecordCredenza from "@app/components/CustomDnsRecordCredenza"; import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert"; import { usePaidStatus } from "@app/hooks/usePaidStatus"; import { tierMatrix } from "@server/lib/billing/tierMatrix"; import { domainQueries } from "@app/lib/queries"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; import { CustomDnsRecordRow } from "@server/routers/customDnsRecord/types"; type Props = { orgId: string; domainId: string; baseDomain: string; // The DNS server only serves custom records for verified domains verified: boolean; }; function formatValue(record: CustomDnsRecordRow, value: string): string { if (record.type === "MX") { return `${record.priority} ${value}`; } if (record.type === "SRV") { return `${record.priority} ${record.weight} ${record.port} ${value}`; } return value; } export default function CustomDnsRecordsTable({ orgId, domainId, baseDomain, verified }: Props) { const t = useTranslations(); const api = createApiClient(useEnvContext()); const { isPaidUser } = usePaidStatus(); const isDisabled = !isPaidUser(tierMatrix.customDnsRecords); const { data: records = [], isRefetching, refetch } = useQuery(domainQueries.listCustomDnsRecords({ orgId, domainId })); const [editorOpen, setEditorOpen] = useState(false); const [editingRecord, setEditingRecord] = useState(null); const [deleteOpen, setDeleteOpen] = useState(false); const [deletingRecord, setDeletingRecord] = useState(null); function openEditor(record: CustomDnsRecordRow | null) { setEditingRecord(record); setEditorOpen(true); } async function toggleEnabled(record: CustomDnsRecordRow, enabled: boolean) { try { await api.post( `/org/${orgId}/domain/${domainId}/custom-dns-record/${record.customDnsRecordId}`, { enabled } ); refetch(); } catch (e) { toast({ variant: "destructive", title: t("customDnsRecordSaveError"), description: formatAxiosError(e) }); } } async function deleteRecord() { if (!deletingRecord) { return; } try { await api.delete( `/org/${orgId}/domain/${domainId}/custom-dns-record/${deletingRecord.customDnsRecordId}` ); toast({ title: t("customDnsRecordDeleted") }); setDeleteOpen(false); setDeletingRecord(null); refetch(); } catch (e) { toast({ variant: "destructive", title: t("customDnsRecordDeleteError"), description: formatAxiosError(e) }); } } const columns: ExtendedColumnDef[] = [ { accessorKey: "fullDomain", enableHiding: false, friendlyName: t("name"), header: ({ column }) => { return ( ); }, cell: ({ row }) => { const fullDomain = row.original.fullDomain; return ( ); } }, { accessorKey: "type", friendlyName: t("type"), header: ({ column }) => { return ( ); }, cell: ({ row }) => { return {row.original.type}; } }, { accessorKey: "values", friendlyName: t("value"), header: () => {t("value")}, cell: ({ row }) => { const record = row.original; return (
{record.values.map((value) => ( {formatValue(record, value)} ))}
); } }, { accessorKey: "ttl", friendlyName: t("TTL"), header: ({ column }) => { return ( ); } }, { accessorKey: "enabled", friendlyName: t("enabled"), header: () => {t("enabled")}, cell: ({ row }) => { const record = row.original; return ( toggleEnabled(record, checked) } /> ); } }, { id: "actions", enableHiding: false, header: () => , cell: ({ row }) => { const record = row.original; return (
{ setDeletingRecord(record); setDeleteOpen(true); }} > {t("delete")}
); } } ]; return ( <> {!verified && ( {t("customDnsRecordsUnverifiedTitle")} {t("customDnsRecordsUnverifiedDescription")} )} {deletingRecord && !isDisabled && ( { setDeleteOpen(open); if (!open) { setDeletingRecord(null); } }} string={deletingRecord.fullDomain} title={t("customDnsRecordDeleteTitle")} dialog={

{t("customDnsRecordDeleteQuestion", { type: deletingRecord.type, name: deletingRecord.fullDomain })}

} buttonText={t("customDnsRecordDeleteConfirm")} onConfirm={deleteRecord} /> )} refetch()} /> openEditor(null)} addButtonDisabled={isDisabled} onRefresh={refetch} isRefreshing={isRefetching} enableColumnVisibility={true} stickyLeftColumn="fullDomain" stickyRightColumn="actions" /> ); }