added frontent viewer for snapshot
This commit is contained in:
@@ -0,0 +1,74 @@
|
|||||||
|
import { File, Folder } from 'lucide-react';
|
||||||
|
|
||||||
|
export function SnapshotFilesPanel({ files, loading, onClose, snapshot }) {
|
||||||
|
if (!snapshot) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="overflow-hidden rounded-md border border-zinc-800 bg-zinc-900/70">
|
||||||
|
<div className="flex items-center justify-between gap-3 border-b border-zinc-800 px-4 py-3">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-sm font-semibold">Snapshot Contents</h2>
|
||||||
|
<p className="mt-1 font-mono text-xs text-zinc-500">{snapshot.id.slice(0, 8)}</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="h-8 rounded-md border border-zinc-700 px-2.5 text-xs text-zinc-100 hover:border-zinc-500"
|
||||||
|
onClick={onClose}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full min-w-[680px] text-left text-sm">
|
||||||
|
<thead className="border-b border-zinc-800 text-xs uppercase tracking-wide text-zinc-500">
|
||||||
|
<tr>
|
||||||
|
<th className="px-4 py-3 font-medium">Path</th>
|
||||||
|
<th className="px-4 py-3 font-medium">Type</th>
|
||||||
|
<th className="px-4 py-3 font-medium">Size</th>
|
||||||
|
<th className="px-4 py-3 font-medium">Modified</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="divide-y divide-zinc-800">
|
||||||
|
{files.map((entry) => (
|
||||||
|
<tr key={`${entry.type}:${entry.path}`}>
|
||||||
|
<td className="px-4 py-3">
|
||||||
|
<span className="inline-flex items-center gap-2 font-mono text-zinc-100">
|
||||||
|
{entry.type === 'dir' ? <Folder className="h-4 w-4 text-cyan-300" /> : <File className="h-4 w-4 text-zinc-400" />}
|
||||||
|
{entry.path}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-3 text-zinc-300">{entry.type}</td>
|
||||||
|
<td className="px-4 py-3 text-zinc-300">{entry.type === 'dir' ? '-' : formatBytes(entry.size)}</td>
|
||||||
|
<td className="px-4 py-3 text-zinc-300">{formatTime(entry.mtime)}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
{!files.length ? (
|
||||||
|
<tr>
|
||||||
|
<td className="px-4 py-8 text-center text-zinc-500" colSpan="4">
|
||||||
|
{loading ? 'Loading snapshot contents...' : 'No files found.'}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
) : null}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatBytes(value) {
|
||||||
|
if (!value) return '0 B';
|
||||||
|
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB'];
|
||||||
|
let size = Number(value);
|
||||||
|
let unitIndex = 0;
|
||||||
|
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||||
|
size /= 1024;
|
||||||
|
unitIndex += 1;
|
||||||
|
}
|
||||||
|
return `${size.toFixed(unitIndex === 0 ? 0 : 1)} ${units[unitIndex]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(value) {
|
||||||
|
if (!value) return '-';
|
||||||
|
return new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'medium' }).format(new Date(value));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user