since3-portal/modules/shared/ui/views/documents-panel.tsx
2026-09-29 22:56:37 +02:00

96 lines
4.5 KiB
TypeScript

import Link from "next/link"
import { notFound } from "next/navigation"
import { ChevronRightIcon, FolderIcon } from "lucide-react"
import { z } from "zod"
import { getClientId } from "@/modules/shared/queries/profile"
import { getDocuments, getFolder, getFolders } from "@/modules/shared/queries/documents"
import { CreateFolderDialog, DeleteFolderDialog } from "@/modules/shared/ui/components/folder-dialogs"
import { DocumentsBrowser } from "@/modules/shared/ui/components/documents-browser"
function EmptyState({ children }: { children: React.ReactNode }) {
return <p className="rounded-lg border border-border bg-surface/50 p-8 text-center text-muted-foreground">{children}</p>
}
export async function DocumentsPanel({
profileId,
isAdmin,
basePath,
folderId,
}: {
profileId: string
isAdmin: boolean
basePath: string
folderId?: string
}) {
const clientId = await getClientId(profileId)
if (!clientId) {
return (
<div className="flex w-full flex-col gap-6 md:gap-8">
<h1 className="text-2xl font-semibold md:text-3xl">Dokumente</h1>
<EmptyState>
{isAdmin
? "Für diesen Client sind noch keine Stammdaten hinterlegt. Bitte zuerst die Stammdaten ausfüllen."
: "Sobald eure Stammdaten hinterlegt sind, könnt ihr hier Dokumente ablegen."}
</EmptyState>
</div>
)
}
if (folderId) {
if (!z.string().uuid().safeParse(folderId).success) notFound()
const folder = await getFolder(clientId, folderId)
if (!folder) notFound()
const documents = await getDocuments(clientId, folder.id)
return (
<div className="flex flex-col gap-6 w-full">
<div className="flex flex-wrap items-center justify-between gap-3">
<nav aria-label="Pfad" className="flex min-w-0 items-center gap-1.5 text-2xl font-semibold md:text-3xl">
<Link href={basePath} className="text-muted-foreground hover:text-foreground">Dokumente</Link>
<ChevronRightIcon className="size-5 shrink-0 text-muted-foreground md:size-6" aria-hidden />
<span className="truncate">{folder.name}</span>
</nav>
{isAdmin && documents.length === 0 && (
<DeleteFolderDialog profileId={profileId} folderId={folder.id} folderName={folder.name} redirectTo={basePath} />
)}
</div>
<DocumentsBrowser profileId={profileId} folderId={folder.id} isAdmin={isAdmin} documents={documents} />
</div>
)
}
const folders = await getFolders(clientId)
return (
<div className="flex flex-col gap-6 w-full">
<div className="flex flex-wrap items-center justify-between gap-3">
<h1 className="text-2xl font-semibold md:text-3xl">Dokumente</h1>
{isAdmin && <CreateFolderDialog profileId={profileId} />}
</div>
{folders.length === 0 ? (
<EmptyState>
{isAdmin ? "Noch keine Ordner. Lege einen Ordner an, damit Dateien hochgeladen werden können." : "Noch keine Ordner vorhanden."}
</EmptyState>
) : (
<ul className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
{folders.map((folder) => (
<li key={folder.id}>
<Link
href={`${basePath}?folder=${folder.id}`}
className="flex flex-col items-center gap-2 rounded-lg border border-border bg-background p-4 text-center sm:p-5 transition-colors hover:border-ink-300 hover:bg-surface focus-visible:ring-2 focus-visible:ring-ring outline-none"
>
<FolderIcon className="size-10 sm:size-12 fill-accent-400/20 stroke-[1.25] text-accent-500" aria-hidden />
<span className="w-full truncate font-medium">{folder.name}</span>
<span className="text-label text-muted-foreground">
{folder.documentCount} {folder.documentCount === 1 ? "Datei" : "Dateien"}
</span>
</Link>
</li>
))}
</ul>
)}
</div>
)
}