"use client"; /* eslint-disable @next/next/no-img-element */ import type { MediaKind } from "@prisma/client"; import { FileType2, Grid2x2, ImageIcon, LayoutList, LoaderCircle, Trash2, Upload } from "lucide-react"; import Link from "next/link"; import { useEffect, useMemo, useState } from "react"; import { useFormStatus } from "react-dom"; import { useSearchParams } from "next/navigation"; import type { MediaAssetView } from "@/lib/media"; import { MotionFade } from "@/components/motion-fade"; import { AppCard } from "@/components/ui/app-card"; import { Button } from "@/components/ui/button"; import { CardContent } from "@/components/ui/card"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table"; import { cn } from "@/lib/utils"; import { createMediaAssetAction, deleteMediaAssetAction } from "@/app/_admin/media/actions"; const copy = { addMedia: "Upload Media File", addMediaDescription: "Datei hochladen und direkt in die Media Library uebernehmen.", addMediaHint: "PNG, JPG, GIF oder PDF bis 5 MB", label: "Bezeichnung", kind: "Typ", image: "Image", document: "Document", upload: "Upload", uploading: "Uploading...", selectFile: "Datei auswaehlen", empty: "Noch keine Bilder vorhanden.", grid: "Grid", list: "List", detailsButton: "Details", open: "Open", delete: "Delete", deleteConfirm: "Delete this media file?", usages: "Verwendungen", source: "Quelle", fileName: "Dateiname", url: "URL", mimeType: "MIME Type", size: "Dateigroesse", createdAt: "Erstellt", close: "Close", details: "Bilddetails", detailsDescription: "Metadaten und Verwendungen der ausgewaehlten Datei.", }; function formatFileSize(size: number | null) { if (!size) { return "—"; } if (size < 1024) { return `${size} B`; } if (size < 1024 * 1024) { return `${(size / 1024).toFixed(1)} KB`; } return `${(size / (1024 * 1024)).toFixed(1)} MB`; } function formatCreatedAt(value: string | Date) { const date = value instanceof Date ? value : new Date(value); return new Intl.DateTimeFormat("de-DE", { dateStyle: "medium", timeStyle: "short", }).format(date); } function getAbsoluteUrl(path: string) { if (/^https?:\/\//.test(path)) { return path; } if (typeof window === "undefined") { return path; } return new URL(path, window.location.origin).toString(); } function UploadSubmitButton() { const { pending } = useFormStatus(); return ( ); } function MediaUploadDialog({ open, onOpenChange, }: { open: boolean; onOpenChange: (open: boolean) => void; }) { const [kind, setKind] = useState("IMAGE"); const [label, setLabel] = useState(""); const [isLabelDirty, setIsLabelDirty] = useState(false); useEffect(() => { if (!open) { setKind("IMAGE"); setLabel(""); setIsLabelDirty(false); } }, [open]); function handleFileChange(event: React.ChangeEvent) { const file = event.target.files?.[0]; if (!file || isLabelDirty) { return; } setLabel(file.name.replace(/\.[^.]+$/, "")); } return ( {copy.addMedia} {copy.addMediaDescription}
{ setLabel(event.target.value); setIsLabelDirty(true); }} placeholder={copy.label} />
{[ { value: "IMAGE" as const, label: copy.image, icon: ImageIcon }, { value: "DOCUMENT" as const, label: copy.document, icon: FileType2 }, ].map((option) => { const Icon = option.icon; const isActive = kind === option.value; return ( ); })}

{copy.addMediaHint}

); } function MediaDetailsDialog({ asset, open, onOpenChange, }: { asset: MediaAssetView | null; open: boolean; onOpenChange: (open: boolean) => void; }) { if (!asset) { return null; } const absoluteUrl = getAbsoluteUrl(asset.url); return (
{asset.label}
{asset.label} {copy.detailsDescription}
{copy.kind} {asset.kind} {copy.source} {asset.source} {copy.fileName} {asset.fileName || "—"} {copy.mimeType} {asset.mimeType || "—"} {copy.size} {formatFileSize(asset.size)} {copy.createdAt} {formatCreatedAt(asset.createdAt)}
{asset.usages.length > 0 ? (

{asset.usages.length} {copy.usages}

{asset.usages.map((usage) => (

{usage.usageType} / {usage.entityType} / {usage.fieldKey}

))}
) : null}

{copy.url}

{absoluteUrl}

); } export function MediaLibraryManager({ mediaAssets, }: { mediaAssets: MediaAssetView[]; }) { const searchParams = useSearchParams(); const [isUploadDialogOpen, setIsUploadDialogOpen] = useState(false); const [selectedAssetId, setSelectedAssetId] = useState(null); const [viewMode, setViewMode] = useState<"grid" | "list">("grid"); const imageAssets = useMemo( () => mediaAssets.filter((asset) => asset.kind === "IMAGE"), [mediaAssets], ); const selectedAsset = imageAssets.find((asset) => asset.id === selectedAssetId) ?? null; useEffect(() => { if (searchParams.has("success")) { setIsUploadDialogOpen(false); setSelectedAssetId(null); } }, [searchParams]); return (
{imageAssets.length > 0 ? (
) : null} {imageAssets.length > 0 ? ( viewMode === "grid" ? (
{imageAssets.map((asset, index) => ( ))}
) : (
{imageAssets.map((asset, index) => (

{asset.label}

{formatFileSize(asset.size)}

{formatCreatedAt(asset.createdAt)}

))}
) ) : ( {copy.empty} )} { if (!open) { setSelectedAssetId(null); } }} />
); }