import Link from "next/link"; import { prisma } from "@/lib/prisma"; import type { Prisma } from "@/generated/prisma/client"; import { BackLink } from "@/components/back-link"; import { ConfirmForm } from "@/components/confirm-form"; import { formatDate } from "@/lib/format"; import { StarRating } from "@/components/star-rating"; import { AdminActionButton, AdminBadge, AdminEmptyState, AdminPageHeader, AdminRowActions, AdminSearchForm, } from "@/components/admin-list"; import { AdminArchiveTabs } from "@/components/admin-archive"; import { insensitiveContains } from "@/lib/prisma-search"; import { isArchiveView } from "@/lib/archive"; import { approvedReviewWhere, pendingReviewWhere, rejectedReviewWhere } from "@/lib/review-status"; import { approveReviewAction, deleteReviewAction, rejectReviewAction } from "../actions"; import { getLocale } from "@/i18n/locale"; import { getDictionary } from "@/i18n/get-dictionary"; import type { Dictionary } from "@/i18n/dictionary-type"; const MAX_ROWS = 100; const BASE_PATH = "/admin/bewertungen"; const REVIEW_INCLUDE = { product: { select: { name: true, slug: true } }, customer: { select: { firstName: true, lastName: true } }, } satisfies Prisma.ReviewInclude; type AdminReview = Prisma.ReviewGetPayload<{ include: typeof REVIEW_INCLUDE }>; type ReviewTexts = Dictionary["adminReviewsPage"]; function searchWhere(query: string): Prisma.ReviewWhereInput { if (!query) return {}; return { OR: [ { product: { name: insensitiveContains(query) } }, { customer: { firstName: insensitiveContains(query) } }, { customer: { lastName: insensitiveContains(query) } }, ], }; } export default async function AdminBewertungenPage(props: PageProps<"/admin/bewertungen">) { const t = getDictionary(await getLocale()); const s = t.adminReviewsPage; const { q, ansicht } = await props.searchParams; const query = typeof q === "string" ? q.trim() : ""; const isArchive = isArchiveView(ansicht); const filter = searchWhere(query); const findReviews = (where: Prisma.ReviewWhereInput, orderBy: Prisma.ReviewOrderByWithRelationInput) => prisma.review.findMany({ where: { ...filter, ...where }, include: REVIEW_INCLUDE, orderBy, take: MAX_ROWS }); // Offene: aelteste zuerst, sie warten am laengsten. Archiv: zuletzt entschiedene oben. const [pending, approved, rejected, pendingCount, decidedCount] = await Promise.all([ isArchive ? Promise.resolve([]) : findReviews(pendingReviewWhere(), { createdAt: "asc" }), isArchive ? findReviews(approvedReviewWhere(), { updatedAt: "desc" }) : Promise.resolve([]), isArchive ? findReviews(rejectedReviewWhere(), { rejectedAt: "desc" }) : Promise.resolve([]), prisma.review.count({ where: { ...filter, ...pendingReviewWhere() } }), prisma.review.count({ where: { ...filter, NOT: pendingReviewWhere() } }), ]); return (
{isArchive ?

{t.adminArchive.autoRuleReviews}

: null} {isArchive ? (
) : ( )}
); } function PendingTable({ reviews, query, s }: { reviews: AdminReview[]; query: string; s: ReviewTexts }) { return (
{reviews.map((review) => ( ))}
{s.colProduct} {s.colCustomer} {s.colRating} {s.colComment} {s.colDate} {s.colStatus}
{review.customer.firstName} {review.customer.lastName} {review.comment ?? "—"} {formatDate(review.createdAt)} {s.statusPending}
{reviews.length === 0 ? ( ) : null}
); } function DecidedColumn({ title, reviews, decision, s, }: { title: string; reviews: AdminReview[]; decision: "approved" | "rejected"; s: ReviewTexts; }) { return (

{title} {reviews.length}

{reviews.length === 0 ? (

{s.emptyColumn}

) : ( )}
); } function ProductLink({ review }: { review: AdminReview }) { return ( {review.product.name} ); } function Stars({ rating }: { rating: number }) { return ( {rating} / 5 ); } /** Freischalten und Ablehnen jeweils nur dort, wo es den Stand aendert; Loeschen immer. */ function ReviewActions({ review, decision, s, }: { review: AdminReview; decision: "pending" | "approved" | "rejected"; s: ReviewTexts; }) { return ( {decision !== "approved" ? (
{s.approve}
) : null} {decision !== "rejected" ? ( {s.reject} ) : null} {s.delete}
); }