import { DeleteOutlined, EyeOutlined, SearchOutlined } from '@ant-design/icons'; import { Button, Card, Input, Popconfirm, Select, Space, Table, Tag, Typography } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { useMemo, useState } from 'react'; import { buildDomainHealth } from '../../frontend/domain-model.js'; import { useI18n } from '../../frontend/i18n/react'; import type { DnsCredential, Domain, SendEvent } from '../../frontend/types'; import { StatusTag } from '../../components/common/StatusTag'; interface DomainsPageProps { domains: Domain[]; events: SendEvent[]; dnsCredentials: DnsCredential[]; actionLoading?: boolean; onViewDetail: (domain: Domain) => void; onApplyDns: (domain: Domain) => void; onCheck: (domain: Domain) => void; onSendTest: (domain: Domain) => void; onDelete: (domain: Domain) => void; onAddDomain: () => void; } export default function DomainsPage({ domains, events, dnsCredentials, actionLoading, onViewDetail, onApplyDns, onCheck, onSendTest, onDelete, onAddDomain }: DomainsPageProps) { const { t } = useI18n(); const [query, setQuery] = useState(''); const [status, setStatus] = useState(); const credentialName = new Map(dnsCredentials.map((item) => [item.id, item.name])); const filtered = useMemo(() => { return domains.filter((domain) => { const health = buildDomainHealth(domain); const matchesQuery = !query || domain.domain.includes(query) || domain.senderHost.includes(query); const matchesStatus = !status || health.status === status; return matchesQuery && matchesStatus; }); }, [domains, query, status]); const columns: ColumnsType = [ { title: t('domains.domain'), dataIndex: 'domain', fixed: 'left', width: 190, render: (value, domain) => ( ) }, { title: t('domains.senderHost'), dataIndex: 'senderHost', width: 190, ellipsis: true }, { title: t('domains.sendingIp'), dataIndex: 'sendingIp', width: 140 }, { title: t('domains.dnsApi'), dataIndex: 'dnsCredentialId', width: 150, render: (value: number | null) => value ? {credentialName.get(value) || value} : {t('common.manual')} }, recordColumn('DKIM', 'dkim'), recordColumn('SPF', 'spf'), recordColumn('DMARC', 'dmarc'), { title: t('domains.smtp'), width: 110, render: (_, domain) => {domain.status?.verified ? t('domains.sendable') : t('domains.waitingVerify')} }, { title: t('domains.lastSent'), width: 180, render: (_, domain) => { const event = events.find((item) => item.domain === domain.domain); return event ? new Date(event.createdAt).toLocaleString() : t('common.notFound'); } }, { title: t('domains.overallStatus'), width: 130, render: (_, domain) => { const health = buildDomainHealth(domain); return {domainHealthLabel(health.status, t)}; } }, { title: t('domains.actions'), width: 360, fixed: 'right', render: (_, domain) => ( onDelete(domain)}>