File size: 3,928 Bytes
d3ad7d5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | import { useEffect, useState } from 'react';
import { Database, HardDrive, RefreshCw, Download } from 'lucide-react';
import './CachePanel.css';
interface Dataset {
variable: string;
query_type: string;
start_date: string;
end_date: string;
lat_bounds: [number, number];
lon_bounds: [number, number];
file_size_bytes: number;
path: string;
}
interface CacheData {
datasets: Dataset[];
total_size_bytes: number;
}
function formatBytes(bytes: number): string {
if (bytes < 1024) return bytes + ' B';
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
}
async function downloadDataset(path: string) {
try {
const resp = await fetch(`/api/cache/download?path=${encodeURIComponent(path)}`);
if (!resp.ok) throw new Error('Download failed');
const blob = await resp.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = path.split('/').pop() + '.zip';
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
} catch (err) {
console.error('Download error:', err);
}
}
export default function CachePanel() {
const [cache, setCache] = useState<CacheData | null>(null);
const [loading, setLoading] = useState(false);
const fetchCache = async () => {
setLoading(true);
try {
const res = await fetch('/api/cache');
if (res.ok) setCache(await res.json());
} catch { /* ignore */ }
setLoading(false);
};
useEffect(() => { fetchCache(); }, []);
return (
<div className="cache-panel">
<div className="cache-header">
<div className="cache-title">
<Database size={16} />
<span>Cached Datasets</span>
</div>
<button className="cache-refresh" onClick={fetchCache} disabled={loading} title="Refresh">
<RefreshCw size={14} className={loading ? 'spin' : ''} />
</button>
</div>
{cache && cache.datasets.length > 0 ? (
<>
<div className="cache-summary">
<HardDrive size={13} />
<span>{cache.datasets.length} datasets · {formatBytes(cache.total_size_bytes)}</span>
</div>
<ul className="cache-list">
{cache.datasets.map((ds, i) => (
<li key={i} className="cache-item">
<div className="cache-item-row">
<div>
<div className="cache-var">{ds.variable}</div>
<div className="cache-meta">
{ds.start_date} → {ds.end_date} · {formatBytes(ds.file_size_bytes)}
</div>
</div>
<button
className="cache-dl-btn"
onClick={() => downloadDataset(ds.path)}
title="Download as ZIP"
>
<Download size={13} />
</button>
</div>
</li>
))}
</ul>
</>
) : (
<div className="cache-empty">No cached datasets yet</div>
)}
</div>
);
}
|