diff --git a/frontend/src/components/admin/analytics/agent-domain-files.tsx b/frontend/src/components/admin/analytics/agent-domain-files.tsx new file mode 100644 index 0000000..aaf8cc8 --- /dev/null +++ b/frontend/src/components/admin/analytics/agent-domain-files.tsx @@ -0,0 +1,564 @@ +'use client'; + +import { useState, useEffect, useRef } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import { api } from '@/lib/api'; +import type { AdminDomainItem, AdminDomainList, AdminDomainQAResponse } from '@/types/api'; + +const PER_PAGE = 20; +const QA_PANEL_WIDTH = 420; + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +function fmtDate(iso: string) { + return new Date(iso).toLocaleDateString(undefined, { + year: 'numeric', month: 'short', day: 'numeric', + }); +} + +function StatusBadge({ status }: { status: string }) { + const color = + status === 'completed' ? '#10b981' : + status === 'failed' ? '#f43f5e' : + status === 'cancelled' ? '#6b7280' : '#f59e0b'; + return ( + + {status.replace(/_/g, ' ')} + + ); +} + +// ── PDF Modal ───────────────────────────────────────────────────────────────── + +function PdfModal({ domain, onClose }: { domain: AdminDomainItem; onClose: () => void }) { + const [blobUrl, setBlobUrl] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + const urlRef = useRef(null); + + useEffect(() => { + let cancelled = false; + setLoading(true); + setError(false); + api.get(`/api/v1/admin/domain-prompts/${domain.domain_id}/pdf`, { + responseType: 'blob', + }).then(res => { + if (cancelled) return; + const url = URL.createObjectURL(res.data); + urlRef.current = url; + setBlobUrl(url); + setLoading(false); + }).catch(() => { + if (!cancelled) { setError(true); setLoading(false); } + }); + return () => { + cancelled = true; + if (urlRef.current) { URL.revokeObjectURL(urlRef.current); urlRef.current = null; } + }; + }, [domain.domain_id]); + + const handleDownload = () => { + if (!blobUrl) return; + const a = document.createElement('a'); + a.href = blobUrl; + a.download = `${domain.domain_name}.pdf`; + a.click(); + }; + + return ( +
e.key === 'Escape' && onClose()} + tabIndex={-1} + style={{ + position: 'fixed', inset: 0, zIndex: 9000, + background: 'rgba(0,0,0,.65)', backdropFilter: 'blur(4px)', + display: 'flex', flexDirection: 'column', + }} + > + {/* Header */} +
+
+ + + + +
+
+ {domain.domain_name} +
+
+ {domain.user_email} +
+
+
+
+ {blobUrl && ( + + )} + +
+
+ {/* Body */} +
+ {loading && ( +
+ Loading PDF… +
+ )} + {error && ( +
+ Failed to load PDF + + Check that MinIO is reachable and the file exists. + +
+ )} + {blobUrl && ( +