import { useEffect, useState, type ReactElement } from "react"; import { Database, ShieldCheck, Building2, Package, AlertCircle, Pencil, HardDrive, Files, FileText, Image as ImageIcon, CheckCircle2, Save, Loader2, } from "lucide-react"; import { Layout } from "@/components/layout/Layout"; import { cn } from "@/lib/utils"; import fileAttachmentService, { type StorageStats, type StorageQuota, } from "@/services/file-attachment-service"; import { Modal, FormField, PrimaryButton, // SecondaryButton, } from "@/components/shared"; import { useAppTheme } from "@/hooks/useAppTheme"; // ───────────────────────────────────────────────────────────────────────────── // Helpers // ───────────────────────────────────────────────────────────────────────────── function formatBytes(bytes: number | string): string { const b = typeof bytes === "string" ? parseInt(bytes) : bytes; if (!b || b === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB", "TB"]; const i = Math.floor(Math.log(b) / Math.log(k)); return `${parseFloat((b / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`; } // ───────────────────────────────────────────────────────────────────────────── // Components // ───────────────────────────────────────────────────────────────────────────── interface QuotaEditModalProps { isOpen: boolean; onClose: () => void; quota: StorageQuota; onUpdated: () => void; } const QuotaEditModal = ({ isOpen, onClose, quota, onUpdated }: QuotaEditModalProps) => { const [maxStorageMB, setMaxStorageMB] = useState( Math.floor((typeof quota.max_storage_bytes === 'string' ? parseInt(quota.max_storage_bytes) : quota.max_storage_bytes) / 1024 / 1024) ); const [maxFileMB, setMaxFileMB] = useState( Math.floor(quota.max_file_size_bytes / 1024 / 1024) ); const [isUpdating, setIsUpdating] = useState(false); const handleSubmit = async () => { setIsUpdating(true); try { await fileAttachmentService.updateQuota({ max_storage_bytes: maxStorageMB * 1024 * 1024, max_file_size_bytes: maxFileMB * 1024 * 1024, }); onUpdated(); onClose(); } catch (err) { alert("Failed to update quota"); } finally { setIsUpdating(false); } }; return ( {/* Cancel */} {isUpdating ? : } Save Changes } >
setMaxStorageMB(parseInt(e.target.value) || 0)} placeholder="e.g. 10240 for 10GB" /> setMaxFileMB(parseInt(e.target.value) || 0)} placeholder="e.g. 50 for 50MB" />
); }; const StorageDashboard = (): ReactElement => { const { primaryColor } = useAppTheme(); const [activeTab, setActiveTab] = useState<'stats' | 'quota'>('stats'); const [stats, setStats] = useState(null); const [quota, setQuota] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [isEditModalOpen, setIsEditModalOpen] = useState(false); const loadData = async () => { setLoading(true); try { const [statsRes, quotaRes] = await Promise.all([ fileAttachmentService.getStorageStats(), fileAttachmentService.getQuota(), ]); setStats(statsRes.data); setQuota(quotaRes.data); } catch (err) { setError("Failed to load dashboard data"); } finally { setLoading(false); } }; useEffect(() => { void loadData(); }, []); if (loading) { return (
); } if (error || !stats || !quota) { return (

Error

{error}

Retry
); } return (
{/* Tabs */}
{activeTab === 'stats' && (
{/* Summary Cards */}
Usage

{stats.quota.usage_percent}% capacity

Total Files

{stats.files.total}

Images

{stats.files.images}

DOCs / PDFs

{stats.files.pdfs + stats.files.documents}

{/* Tables Grid */}
{/* Entity Table */}

By Entity Type

{Object.entries(stats.by_entity).map(([name, data]) => ( ))}
Entity File Count Total Size
{name} {data.count} {formatBytes(data.size)}
{/* Module Table */}

By Source Module

{Object.entries(stats.by_module).map(([name, data]) => ( ))}
Module File Count Total Size
{name} {data.count} {formatBytes(data.size)}
)} {activeTab === 'quota' && (

Quota Profile

setIsEditModalOpen(true)} size="default" className="px-4 py-2.5 text-sm" > Edit Quota
{[ { label: "Max Total Storage", value: quota.max_storage_formatted || formatBytes(quota.max_storage_bytes), icon: HardDrive }, { label: "Max Per-File Size", value: quota.max_file_size_formatted || formatBytes(quota.max_file_size_bytes), icon: FileText }, { label: "Currently Used", value: quota.used_storage_formatted || formatBytes(quota.used_storage_bytes), icon: Save }, { label: "File Count", value: `${quota.file_count} items`, icon: Files }, { label: "Last Updated", value: new Date(quota.updated_at).toLocaleString(), icon: CheckCircle2 }, ].map((row) => ( ))}
{row.label} {row.value}

System Security Policy

The following extensions are strictly blocked to prevent malicious execution:

{quota.blocked_extensions?.map(ext => ( {ext} ))}
)}
{isEditModalOpen && ( setIsEditModalOpen(false)} quota={quota} onUpdated={loadData} /> )} ); }; export default StorageDashboard;