mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
wip: course layout and func to context
Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Search, CheckCircle2 } from "lucide-react";
|
||||
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetDescription,
|
||||
} from "@/components/ui/sheet";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
import { useAssets } from "@/contexts/AdminAssetsContext";
|
||||
|
||||
// ─── Empty ────────────────────────────────────────────────────────────────────
|
||||
|
||||
function EmptyState({ fileType }) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-48 gap-2">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
No {fileType} assets found.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Asset Card ───────────────────────────────────────────────────────────────
|
||||
|
||||
function AssetCard({ asset, selected, onSelect }) {
|
||||
const thumb = asset.thumbnail_url ?? asset.file_url;
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onSelect(asset)}
|
||||
className={[
|
||||
"relative rounded-lg border-2 overflow-hidden transition-all text-left w-full",
|
||||
"hover:border-primary/60 hover:shadow-sm",
|
||||
selected
|
||||
? "border-primary ring-2 ring-primary/20"
|
||||
: "border-border",
|
||||
].join(" ")}
|
||||
>
|
||||
{/* Thumbnail */}
|
||||
<div className="aspect-video bg-muted w-full overflow-hidden">
|
||||
{thumb ? (
|
||||
<img
|
||||
src={thumb}
|
||||
alt={asset.display_name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<span className="text-xs text-muted-foreground">No preview</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Name */}
|
||||
<div className="p-2">
|
||||
<p className="text-xs font-medium truncate">{asset.display_name}</p>
|
||||
</div>
|
||||
|
||||
{/* Selected checkmark */}
|
||||
{selected && (
|
||||
<div className="absolute top-1.5 right-1.5">
|
||||
<CheckCircle2 className="h-5 w-5 text-primary fill-white" />
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Sheet ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export function AssetPickerSheet({ open, onOpenChange, fileType, onSelect }) {
|
||||
const { fetchAssets, assets, pagination, loading } = useAssets();
|
||||
const [search, setSearch] = useState("");
|
||||
const [page, setPage] = useState(1);
|
||||
const [selected, setSelected] = useState(null);
|
||||
|
||||
const LIMIT = 12;
|
||||
|
||||
// ── Fetch on open / search / page change ──────────────────────────────────
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
fetchAssets({
|
||||
page,
|
||||
limit: LIMIT,
|
||||
filters: [
|
||||
...(fileType ? [{ id: "file_type", value: [fileType] }] : []),
|
||||
...(search ? [{ id: "display_name", value: [search] }] : []),
|
||||
],
|
||||
});
|
||||
}, [open, page, search, fileType]);
|
||||
|
||||
// ── Reset on close ────────────────────────────────────────────────────────
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
setSearch("");
|
||||
setPage(1);
|
||||
setSelected(null);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const handleSelect = (asset) => {
|
||||
setSelected(asset.asset_id);
|
||||
onSelect(asset);
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
const label = fileType
|
||||
? `${fileType.charAt(0).toUpperCase()}${fileType.slice(1)}s`
|
||||
: "Assets";
|
||||
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={onOpenChange}>
|
||||
<SheetContent side="right" className="w-full sm:max-w-lg flex flex-col gap-0 p-0">
|
||||
|
||||
{/* ── Header ── */}
|
||||
<SheetHeader className="px-6 pt-6 pb-4 border-b">
|
||||
<SheetTitle>Select {label}</SheetTitle>
|
||||
<SheetDescription>
|
||||
Click an asset to attach it.
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
|
||||
{/* ── Search ── */}
|
||||
<div className="px-6 py-3 border-b">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder={`Search ${label.toLowerCase()}…`}
|
||||
value={search}
|
||||
onChange={(e) => { setSearch(e.target.value); setPage(1); }}
|
||||
className="pl-9"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Grid ── */}
|
||||
<div className="flex-1 overflow-y-auto px-6 py-4">
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center h-48">
|
||||
<Spinner className="h-5 w-5" />
|
||||
</div>
|
||||
) : !assets.length ? (
|
||||
<EmptyState fileType={fileType ?? "asset"} />
|
||||
) : (
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{assets.map((asset) => (
|
||||
<AssetCard
|
||||
key={asset.asset_id}
|
||||
asset={asset}
|
||||
selected={selected === asset.asset_id}
|
||||
onSelect={handleSelect}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* ── Pagination ── */}
|
||||
{pagination.totalPages > 1 && (
|
||||
<div className="flex items-center justify-between px-6 py-3 border-t text-sm">
|
||||
<span className="text-muted-foreground">
|
||||
Page {pagination.page} of {pagination.totalPages}
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
disabled={!pagination.hasPrevPage || loading}
|
||||
onClick={() => setPage((p) => p - 1)}
|
||||
className="px-3 py-1 rounded border text-xs disabled:opacity-40 hover:bg-muted transition-colors"
|
||||
>
|
||||
Prev
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!pagination.hasNextPage || loading}
|
||||
onClick={() => setPage((p) => p + 1)}
|
||||
className="px-3 py-1 rounded border text-xs disabled:opacity-40 hover:bg-muted transition-colors"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user