Reapply "Add missing admin action files and navigation links"

This reverts commit 4d515bc400.
This commit is contained in:
Simo committed 2026-07-11 20:52:56 +02:00
1 parent 96ed768f14
commit 5b4228261a
338 files changed
+28143 -5948

No files matched your search

+84 -60
View File
@@ -12,13 +12,7 @@ function validImageUrl(url: string): string {
return url;
}
export function MediaPicker({
onSelect,
current,
}: {
onSelect: (url: string) => void;
current?: string;
}) {
export function MediaPicker({ onSelect, current }: { onSelect: (url: string) => void; current?: string }) {
const [open, setOpen] = useState(false);
const [files, setFiles] = useState<MediaFile[]>([]);
const [loading, setLoading] = useState(false);
@@ -64,72 +58,102 @@ export function MediaPicker({
return (
<>
<div style={{ display: "flex", gap: "0.4rem", alignItems: "center" }}>
<button type="button" onClick={() => setOpen(true)} className="btn btn-outline" style={{ fontSize: "0.82rem", padding: "0.3rem 0.7rem" }}>
<button
type="button"
onClick={() => setOpen(true)}
className="btn btn-outline"
style={{ fontSize: "0.82rem", padding: "0.3rem 0.7rem" }}
>
Browse
</button>
{current && (
<span style={{ fontSize: "0.78rem", color: "var(--color-text-muted)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", maxWidth: 200 }}>
<span
style={{
fontSize: "0.78rem",
color: "var(--color-text-muted)",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
maxWidth: 200,
}}
>
{current.split("/").pop()}
</span>
)}
</div>
{open && (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
onClick={() => setOpen(false)}
>
{open && (
<div
onClick={(e) => e.stopPropagation()}
className="bg-[var(--color-surface)] text-[var(--color-text)] rounded-2xl w-[min(90vw,680px)] max-h-[80vh] flex flex-col shadow-xl"
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
onClick={() => setOpen(false)}
>
<div className="flex items-center justify-between px-4 py-3 border-b border-[var(--color-text-muted)]/14">
<strong className="text-base">Media Library</strong>
<div className="flex gap-2 items-center">
<label className="btn btn-primary text-sm cursor-pointer">
{uploading ? "Uploading…" : "Upload"}
<input ref={fileRef} type="file" accept="image/png,image/jpeg,image/gif,image/webp" onChange={handleUpload} className="hidden" multiple />
</label>
<button type="button" onClick={() => setOpen(false)} className="btn text-sm">
Close
</button>
<div
onClick={(e) => e.stopPropagation()}
className="bg-[var(--color-surface)] text-[var(--color-text)] rounded-2xl w-[min(90vw,680px)] max-h-[80vh] flex flex-col shadow-xl"
>
<div className="flex items-center justify-between px-4 py-3 border-b border-[var(--color-text-muted)]/14">
<strong className="text-base">Media Library</strong>
<div className="flex gap-2 items-center">
<label className="btn btn-primary text-sm cursor-pointer">
{uploading ? "Uploading…" : "Upload"}
<input
ref={fileRef}
type="file"
accept="image/png,image/jpeg,image/gif,image/webp"
onChange={handleUpload}
className="hidden"
multiple
/>
</label>
<button type="button" onClick={() => setOpen(false)} className="btn text-sm">
Close
</button>
</div>
</div>
<div className="flex-1 overflow-auto p-3">
{loading ? (
<p className="text-xs text-[var(--color-text-muted)] text-center py-8">Loading…</p>
) : files.length === 0 ? (
<p className="text-xs text-[var(--color-text-muted)] text-center py-8">
No images yet. Upload one above.
</p>
) : (
<div className="grid grid-cols-[repeat(auto-fill,minmax(140px,1fr))] gap-2.5">
{files.map((f) => (
<button
type="button"
key={f.name}
onClick={() => {
onSelect(f.url);
setOpen(false);
}}
className="border-2 border-transparent rounded-xl overflow-hidden cursor-pointer bg-[var(--color-background)] p-0 flex flex-col transition-all duration-150"
style={{ borderColor: f.url === current ? "var(--color-primary)" : "transparent" }}
onMouseEnter={(e) => {
if (f.url !== current)
e.currentTarget.style.borderColor =
"color-mix(in srgb, var(--color-primary) 40%, transparent)";
}}
onMouseLeave={(e) => {
if (f.url !== current) e.currentTarget.style.borderColor = "transparent";
}}
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={validImageUrl(f.url)}
alt={f.name}
className="w-full h-[100px] object-cover block"
/>
<span className="text-xs py-1 text-center overflow-hidden text-ellipsis whitespace-nowrap">
{f.name}
</span>
</button>
))}
</div>
)}
</div>
</div>
<div className="flex-1 overflow-auto p-3">
{loading ? (
<p className="text-xs text-[var(--color-text-muted)] text-center py-8">Loading…</p>
) : files.length === 0 ? (
<p className="text-xs text-[var(--color-text-muted)] text-center py-8">
No images yet. Upload one above.
</p>
) : (
<div className="grid grid-cols-[repeat(auto-fill,minmax(140px,1fr))] gap-2.5">
{files.map((f) => (
<button
type="button"
key={f.name}
onClick={() => { onSelect(f.url); setOpen(false); }}
className="border-2 border-transparent rounded-xl overflow-hidden cursor-pointer bg-[var(--color-background)] p-0 flex flex-col transition-all duration-150"
style={{ borderColor: f.url === current ? "var(--color-primary)" : "transparent" }}
onMouseEnter={(e) => { if (f.url !== current) e.currentTarget.style.borderColor = "color-mix(in srgb, var(--color-primary) 40%, transparent)"; }}
onMouseLeave={(e) => { if (f.url !== current) e.currentTarget.style.borderColor = "transparent"; }}
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={validImageUrl(f.url)}
alt={f.name}
className="w-full h-[100px] object-cover block"
/>
<span className="text-xs py-1 text-center overflow-hidden text-ellipsis whitespace-nowrap">
{f.name}
</span>
</button>
))}
</div>
)}
</div>
</div>
</div>
)}
</>
);