import { useMemo } from 'react' import { Link } from 'react-router-dom' import { FileKey, Loader2 } from 'lucide-react' import { Card, CardHeader, CardContent, Badge, Skeleton, Progress } from './ui' import type { Certificate } from '../api/certificates' import type { ProxyHost } from '../api/proxyHosts' interface CertificateStatusCardProps { certificates: Certificate[] hosts: ProxyHost[] isLoading?: boolean } export default function CertificateStatusCard({ certificates, hosts, isLoading }: CertificateStatusCardProps) { const validCount = certificates.filter(c => c.status === 'valid').length const expiringCount = certificates.filter(c => c.status === 'expiring').length const untrustedCount = certificates.filter(c => c.status === 'untrusted').length // Build a set of all domains that have certificates (case-insensitive) // ACME certificates (Let's Encrypt) are auto-managed and don't set certificate_id, // so we match by domain name instead const certifiedDomains = useMemo(() => { const domains = new Set() certificates.forEach(cert => { // Handle missing or undefined domain field if (!cert.domain) return // Certificate domain field can be comma-separated cert.domain.split(',').forEach(d => { const trimmed = d.trim().toLowerCase() if (trimmed) domains.add(trimmed) }) }) return domains }, [certificates]) // Calculate pending hosts: SSL-enabled hosts without any domain covered by a certificate const { pendingCount, totalSSLHosts, hostsWithCerts } = useMemo(() => { const sslHosts = hosts.filter(h => h.ssl_forced && h.enabled) let withCerts = 0 sslHosts.forEach(host => { // Check if any of the host's domains have a certificate const hostDomains = host.domain_names.split(',').map(d => d.trim().toLowerCase()) if (hostDomains.some(domain => certifiedDomains.has(domain))) { withCerts++ } }) return { pendingCount: sslHosts.length - withCerts, totalSSLHosts: sslHosts.length, hostsWithCerts: withCerts, } }, [hosts, certifiedDomains]) const hasProvisioning = pendingCount > 0 const progressPercent = totalSSLHosts > 0 ? Math.round((hostsWithCerts / totalSSLHosts) * 100) : 100 if (isLoading) { return (
) } return (
SSL Certificates
{hasProvisioning && ( Provisioning )}
{certificates.length}
{/* Status breakdown */}
{validCount > 0 && ( {validCount} valid )} {expiringCount > 0 && ( {expiringCount} expiring )} {untrustedCount > 0 && ( {untrustedCount} staging )} {certificates.length === 0 && ( No certificates )}
{/* Pending indicator */} {hasProvisioning && (
{pendingCount} host{pendingCount !== 1 ? 's' : ''} awaiting certificate
{progressPercent}% provisioned
)}
) }