1import { useState, useEffect } from 'react';
2import { apiFetch } from '../../../lib/api';
4function parseJwt(token) {
6 const payload = token.split('.')[1];
7 return JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')));
13export default function DomainStats({ domains }) {
14 const [pendingRequests, setPendingRequests] = useState(0);
15 const token = localStorage.getItem('token');
16 const jwtPayload = token ? parseJwt(token) : null;
17 const isRootTenant = jwtPayload?.is_msp || false;
19 const activeDomains = domains.filter(d => d.status === 'active').length;
21 // Count registered vs DNS-only domains
22 const registeredDomains = domains.filter(d => d.metadata?.domain_type === 'registered').length;
23 const dnsOnlyDomains = domains.filter(d => d.metadata?.domain_type === 'dns_only').length;
25 // Count domains with Cloudflare enrichment
26 const cloudflareEnrichedDomains = domains.filter(d =>
27 d.metadata?.has_cloudflare_dns || d.metadata?.cloudflare_zone_id
30 // Count locked domains
31 const lockedDomains = domains.filter(d => d.metadata?.locked === true).length;
33 const expiringSoon = domains.filter(d => {
34 if (!d.expiration_date || d.metadata?.domain_type === 'dns_only') return false;
35 const days = Math.ceil((new Date(d.expiration_date) - new Date()) / (1000 * 60 * 60 * 24));
36 return days <= 30 && days > 0;
39 // Find most recent update time
40 const lastSync = domains.length > 0
41 ? domains.reduce((latest, d) => {
42 const updated = new Date(d.updated_at);
43 return updated > latest ? updated : latest;
47 // Load pending requests count for root tenant
54 const loadPendingCount = async () => {
56 const res = await apiFetch('/domain-requests?status=pending');
58 const data = await res.json();
59 const count = data.total || (Array.isArray(data) ? data.length : data.requests?.length || 0);
60 setPendingRequests(count);
62 console.error('Error loading pending requests:', err);
67 <div className="tab-stats">
68 <div className="stat-card">
69 <span className="stat-label">Total Domains</span>
70 <span className="stat-value">{domains.length}</span>
72 <small style={{ fontSize: '0.75em', color: '#666', marginTop: '4px', display: 'block' }}>
73 Last synced: {lastSync.toLocaleTimeString()}
77 <div className="stat-card">
78 <span className="stat-label">Registered</span>
79 <span className="stat-value">{registeredDomains}</span>
80 <small style={{ fontSize: '0.75em', color: '#666', marginTop: '4px', display: 'block' }}>
84 <div className="stat-card">
85 <span className="stat-label">DNS Only</span>
86 <span className="stat-value">{dnsOnlyDomains}</span>
87 <small style={{ fontSize: '0.75em', color: '#666', marginTop: '4px', display: 'block' }}>
91 <div className="stat-card" style={{ background: cloudflareEnrichedDomains > 0 ? '#e3f2fd' : undefined }}>
92 <span className="stat-label">☁️ Cloudflare DNS</span>
93 <span className="stat-value" style={{ color: cloudflareEnrichedDomains > 0 ? '#1976d2' : undefined }}>
94 {cloudflareEnrichedDomains}
96 <small style={{ fontSize: '0.75em', color: '#666', marginTop: '4px', display: 'block' }}>
100 <div className="stat-card" style={{ background: lockedDomains > 0 ? '#e8f5e9' : undefined }}>
101 <span className="stat-label">🔒 Locked</span>
102 <span className="stat-value" style={{ color: lockedDomains > 0 ? '#388e3c' : undefined }}>
105 <small style={{ fontSize: '0.75em', color: '#666', marginTop: '4px', display: 'block' }}>
109 <div className="stat-card" style={{ background: expiringSoon > 0 ? '#fff3e0' : undefined }}>
110 <span className="stat-label">⚠️ Expiring Soon</span>
111 <span className="stat-value" style={{ color: expiringSoon > 0 ? '#e65100' : undefined }}>
114 <small style={{ fontSize: '0.75em', color: '#666', marginTop: '4px', display: 'block' }}>
119 <div className="stat-card" style={{ background: pendingRequests > 0 ? '#fff3e0' : undefined }}>
120 <span className="stat-label">Pending Requests</span>
121 <span className="stat-value" style={{ color: pendingRequests > 0 ? '#e65100' : undefined }}>
124 {pendingRequests > 0 && (
125 <small style={{ fontSize: '0.75em', color: '#e65100', marginTop: '4px', display: 'block' }}>