mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
Adjusted
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
// modules/admin/pages/assets/ViewDocumentAsset.jsx
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { ArrowLeft, Lock, Globe, FileText } from "lucide-react";
|
||||
|
||||
import { useAssets } from "@/contexts/AdminAssetsContext";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
|
||||
function MetaRow({ label, value }) {
|
||||
if (!value && value !== 0) return null;
|
||||
return (
|
||||
<div className="flex items-start gap-3 py-2">
|
||||
<span className="text-muted-foreground text-sm w-36 shrink-0">{label}</span>
|
||||
<span className="text-sm font-medium break-all">{String(value)}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const PREVIEWABLE = ["pdf", "txt", "html", "htm", "csv", "md"];
|
||||
|
||||
export default function ViewDocumentAsset() {
|
||||
const { assetId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { selectedAsset, loading, fetchAsset } = useAssets();
|
||||
|
||||
useEffect(() => {
|
||||
if (assetId) fetchAsset(assetId);
|
||||
}, [assetId]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-96">
|
||||
<Spinner className="size-8" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!selectedAsset) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-96 gap-3 text-muted-foreground">
|
||||
<p>Asset not found.</p>
|
||||
<Button variant="outline" onClick={() => navigate(-1)}>Go Back</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const a = selectedAsset;
|
||||
const canPreview = PREVIEWABLE.includes((a.extension ?? "").toLowerCase());
|
||||
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto px-4 py-6 space-y-6">
|
||||
|
||||
{/* ── Header ── */}
|
||||
<div className="flex items-center gap-3">
|
||||
<Button variant="ghost" size="icon" onClick={() => navigate(-1)}>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h1 className="text-xl font-semibold truncate">{a.display_name ?? a.original_name}</h1>
|
||||
<p className="text-sm text-muted-foreground">{a.mime_type}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-5 gap-6">
|
||||
|
||||
{/* ── Document preview ── */}
|
||||
<div className="lg:col-span-3">
|
||||
{canPreview && a.file_url ? (
|
||||
<div className="rounded-lg border overflow-hidden bg-white" style={{ height: 600 }}>
|
||||
<iframe
|
||||
src={a.file_url}
|
||||
title={a.display_name ?? a.original_name}
|
||||
className="w-full h-full"
|
||||
// sandbox="allow-scripts allow-same-origin"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-lg border bg-muted/30 flex flex-col items-center justify-center gap-4 py-20">
|
||||
<FileText className="h-16 w-16 text-muted-foreground/40" />
|
||||
<p className="text-muted-foreground text-sm">
|
||||
Preview not available for this file type.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* ── Metadata panel ── */}
|
||||
<div className="lg:col-span-2 space-y-4">
|
||||
<div className="rounded-lg border bg-card p-4 space-y-1">
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground mb-2">File Info</p>
|
||||
<MetaRow label="Original Name" value={a.original_name} />
|
||||
<MetaRow label="Extension" value={a.extension} />
|
||||
<MetaRow label="File Size" value={a.file_size ? `${(a.file_size / 1024).toFixed(1)} KB` : null} />
|
||||
<MetaRow label="MIME Type" value={a.mime_type} />
|
||||
<Separator className="my-2" />
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground mb-2">Storage</p>
|
||||
<MetaRow label="Provider" value={a.storage_provider} />
|
||||
<MetaRow label="Bucket" value={a.storage_bucket} />
|
||||
<MetaRow label="Key" value={a.storage_key} />
|
||||
<Separator className="my-2" />
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground mb-2">Access</p>
|
||||
<div className="flex items-center gap-2 py-1">
|
||||
{a.is_public
|
||||
? <><Globe className="h-3.5 w-3.5 text-green-500" /><Badge variant="outline" className="text-green-600 border-green-400">Public</Badge></>
|
||||
: <><Lock className="h-3.5 w-3.5 text-yellow-500" /><Badge variant="outline" className="text-yellow-600 border-yellow-400">Private</Badge></>
|
||||
}
|
||||
</div>
|
||||
<MetaRow label="Access Level" value={a.access_level} />
|
||||
<MetaRow label="Owner Type" value={a.owner_type} />
|
||||
<MetaRow label="Owner ID" value={a.owner_id} />
|
||||
<Separator className="my-2" />
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground mb-2">Timestamps</p>
|
||||
<MetaRow label="Created By" value={a.createdBy} />
|
||||
<MetaRow label="Created" value={a.createdAt ? new Date(a.createdAt).toLocaleString() : null} />
|
||||
<MetaRow label="Modified" value={a.updatedAt ? new Date(a.updatedAt).toLocaleString() : null} />
|
||||
</div>
|
||||
|
||||
{a.description && (
|
||||
<div className="rounded-lg border bg-card p-4">
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground mb-2">Description</p>
|
||||
<p className="text-sm text-foreground">{a.description}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user