diff --git a/src/app/admin/anfragen/page.tsx b/src/app/admin/anfragen/page.tsx index 4774a6b..724fe70 100644 --- a/src/app/admin/anfragen/page.tsx +++ b/src/app/admin/anfragen/page.tsx @@ -1,12 +1,25 @@ -import Link from "next/link"; import { prisma } from "@/lib/prisma"; import { insensitiveContains } from "@/lib/prisma-search"; import { BackLink } from "@/components/back-link"; import { formatDate } from "@/lib/format"; +import { + AdminBadge, + AdminEmptyState, + AdminPageHeader, + AdminSearchForm, + type AdminBadgeTone, +} from "@/components/admin-list"; import { updateBusinessInquiryStatusAction } from "../actions"; import { getLocale } from "@/i18n/locale"; import { getDictionary } from "@/i18n/get-dictionary"; +const STATUS_TONES: Record = { + NEU: "pending", + IN_BEARBEITUNG: "pending", + ABGESCHLOSSEN: "positive", + ABGELEHNT: "neutral", +}; + export default async function AdminInquiriesPage(props: PageProps<"/admin/anfragen">) { const t = getDictionary(await getLocale()); const statusLabels: Record = { @@ -35,46 +48,40 @@ export default async function AdminInquiriesPage(props: PageProps<"/admin/anfrag return (
-

{t.adminInquiriesPage.title}

+ -
- - - {query ? ( - - {t.adminInquiriesPage.resetFilter} - - ) : null} -
+
{inquiries.map((inquiry) => (
-

{inquiry.companyName}

+
+

{inquiry.companyName}

+ + {statusLabels[inquiry.status] ?? inquiry.status} + +

{inquiry.contactPerson} · {inquiry.email} {inquiry.phone ? ` · ${inquiry.phone}` : ""}

-

{formatDate(inquiry.createdAt)}

+

{formatDate(inquiry.createdAt)}

-
@@ -107,9 +117,10 @@ export default async function AdminInquiriesPage(props: PageProps<"/admin/anfrag
))} {inquiries.length === 0 ? ( -

- {query ? t.adminInquiriesPage.noResultsFiltered : t.adminInquiriesPage.noInquiriesYet} -

+ ) : null}
diff --git a/src/app/admin/audit-log/page.tsx b/src/app/admin/audit-log/page.tsx index 4235eee..5efb3ab 100644 --- a/src/app/admin/audit-log/page.tsx +++ b/src/app/admin/audit-log/page.tsx @@ -1,6 +1,7 @@ import { prisma } from "@/lib/prisma"; import { BackLink } from "@/components/back-link"; import { formatDateTime } from "@/lib/format"; +import { AdminEmptyState, AdminPageHeader } from "@/components/admin-list"; import { getLocale } from "@/i18n/locale"; import { getDictionary } from "@/i18n/get-dictionary"; @@ -16,8 +17,7 @@ export default async function AdminAuditLogPage() { return (
-

{t.adminAuditLogPage.title}

-

{t.adminAuditLogPage.intro(RECENT_LIMIT)}

+
@@ -33,7 +33,7 @@ export default async function AdminAuditLogPage() { {entries.map((entry) => ( - +
{formatDateTime(entry.createdAt)}{formatDateTime(entry.createdAt)} {entry.adminEmail} {entry.action} @@ -45,7 +45,7 @@ export default async function AdminAuditLogPage() { ))}
- {entries.length === 0 ?

{t.adminAuditLogPage.noEntriesYet}

: null} + {entries.length === 0 ? : null}
); diff --git a/src/app/admin/benutzer/page.tsx b/src/app/admin/benutzer/page.tsx index 7e6d04b..33b225c 100644 --- a/src/app/admin/benutzer/page.tsx +++ b/src/app/admin/benutzer/page.tsx @@ -1,10 +1,18 @@ -import Link from "next/link"; import { prisma } from "@/lib/prisma"; import { insensitiveContains } from "@/lib/prisma-search"; import { BackLink } from "@/components/back-link"; import { ConfirmForm } from "@/components/confirm-form"; import { getCurrentAdmin } from "@/lib/auth"; import { formatDate } from "@/lib/format"; +import { + AdminActionButton, + AdminActionLink, + AdminBadge, + AdminEmptyState, + AdminPageHeader, + AdminRowActions, + AdminSearchForm, +} from "@/components/admin-list"; import { blockCustomerAction, unblockCustomerAction, deleteCustomerAction } from "../actions"; import { getLocale } from "@/i18n/locale"; import { getDictionary } from "@/i18n/get-dictionary"; @@ -32,37 +40,21 @@ export default async function AdminBenutzerPage(props: PageProps<"/admin/benutze }), ]); + const newUser = { href: "/admin/benutzer/neu", label: t.adminUsersPage.newUser }; + return (
-
-

{t.adminUsersPage.title}

- - {t.adminUsersPage.newUser} - -
+ - -
- - - {query ? ( - - {t.adminUsersPage.resetFilter} - - ) : null} -
+
@@ -89,37 +81,43 @@ export default async function AdminBenutzerPage(props: PageProps<"/admin/benutze {isSelf ? {t.adminUsersPage.youSuffix} : null} - - - + + + ); @@ -142,9 +138,10 @@ export default async function AdminBenutzerPage(props: PageProps<"/admin/benutze
{customer.email}{roleLabels[customer.role] ?? customer.role} - {customer.blockedAt ? ( - {t.adminUsersPage.statusBlocked} - ) : isTemporarilyLocked ? ( - {t.adminUsersPage.statusTemporarilyLocked} + {customer.role === "ADMIN" ? ( + {roleLabels.ADMIN} ) : ( - {t.adminUsersPage.statusActive} + (roleLabels[customer.role] ?? customer.role) )} {customer.twoFactorEnabled ? t.adminUsersPage.statusActive : "—"}{formatDate(customer.createdAt)} -
- - {t.adminUsersPage.edit} - + {customer.blockedAt ? ( + {t.adminUsersPage.statusBlocked} + ) : isTemporarilyLocked ? ( + {t.adminUsersPage.statusTemporarilyLocked} + ) : ( + {t.adminUsersPage.statusActive} + )} +
+ {customer.twoFactorEnabled ? ( + {t.adminUsersPage.statusActive} + ) : ( + — + )} + {formatDate(customer.createdAt)} + + {t.adminUsersPage.edit} {!isSelf ? ( customer.blockedAt ? (
- + {t.adminUsersPage.unblock}
) : (
- + {t.adminUsersPage.block}
) ) : null} @@ -129,12 +127,10 @@ export default async function AdminBenutzerPage(props: PageProps<"/admin/benutze confirmMessage={t.adminUsersPage.deleteConfirm(`${customer.firstName} ${customer.lastName}`)} > - + {t.adminUsersPage.delete} ) : null} - +
{customers.length === 0 ? ( -

- {query ? t.adminUsersPage.noResultsFiltered : t.adminUsersPage.noUsersYet} -

+ ) : null}
diff --git a/src/app/admin/bewertungen/page.tsx b/src/app/admin/bewertungen/page.tsx index 442acef..57b13e0 100644 --- a/src/app/admin/bewertungen/page.tsx +++ b/src/app/admin/bewertungen/page.tsx @@ -3,6 +3,15 @@ import { prisma } from "@/lib/prisma"; 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 { insensitiveContains } from "@/lib/prisma-search"; import { approveReviewAction, deleteReviewAction } from "../actions"; import { getLocale } from "@/i18n/locale"; @@ -34,28 +43,16 @@ export default async function AdminBewertungenPage(props: PageProps<"/admin/bewe return (
-

{t.adminReviewsPage.title}

+ -
- - - {query ? ( - - {t.adminReviewsPage.resetFilter} - - ) : null} -
+
@@ -74,34 +71,34 @@ export default async function AdminBewertungenPage(props: PageProps<"/admin/bewe {reviews.map((review) => ( - ))}
- + {review.product.name} {review.customer.firstName} {review.customer.lastName} - {"★".repeat(review.rating)} - {"☆".repeat(5 - review.rating)} + + + {review.rating} / 5 {review.comment ?? "—"} {formatDate(review.createdAt)} {review.isApproved ? ( - {t.adminReviewsPage.statusPublished} + {t.adminReviewsPage.statusPublished} ) : ( - {t.adminReviewsPage.statusPending} + + {t.adminReviewsPage.statusPending} + )} -
+ {!review.isApproved ? (
- + {t.adminReviewsPage.approve}
) : null} - + {t.adminReviewsPage.delete} -
+
{reviews.length === 0 ? ( -

- {query ? t.adminReviewsPage.noResultsFiltered : t.adminReviewsPage.noReviewsYet} -

+ ) : null}
diff --git a/src/app/admin/chargen/page.tsx b/src/app/admin/chargen/page.tsx index 6dcfd09..9098e80 100644 --- a/src/app/admin/chargen/page.tsx +++ b/src/app/admin/chargen/page.tsx @@ -1,8 +1,17 @@ -import Link from "next/link"; import { prisma } from "@/lib/prisma"; import { BackLink } from "@/components/back-link"; import { ConfirmForm } from "@/components/confirm-form"; import { formatDate } from "@/lib/format"; +import { Button } from "@/components/ui"; +import { AdminField, AdminSelect } from "@/components/admin-form"; +import { + AdminActionButton, + AdminActionLink, + AdminActionNote, + AdminEmptyState, + AdminPageHeader, + AdminRowActions, +} from "@/components/admin-list"; import { createBatchAction, deleteBatchAction } from "../actions"; import { getLocale } from "@/i18n/locale"; import { getDictionary } from "@/i18n/get-dictionary"; @@ -20,7 +29,7 @@ export default async function AdminBatchesPage() { return (
-

{t.adminBatchesPage.title}

+
@@ -41,84 +50,70 @@ export default async function AdminBatchesPage() { - - - - - + + + + ))}
{batch.harvest.type} {batch.harvest.year} {formatDate(batch.fillDate)}{formatDate(batch.bestBeforeDate)}{batch.glassCount}{batch.remainingGlasses} - - {t.adminBatchesPage.edit} - + {formatDate(batch.fillDate)}{formatDate(batch.bestBeforeDate)}{batch.glassCount} + {batch.remainingGlasses} + + + {t.adminBatchesPage.edit} {batch._count.products === 0 && batch._count.orderItems === 0 ? ( - + {t.adminBatchesPage.delete} ) : ( // Statt den Knopf wortlos wegzulassen: der Grund steht da. - {t.adminBatchesPage.notDeletable} + {t.adminBatchesPage.notDeletable} )} +
- {batches.length === 0 ?

{t.adminBatchesPage.noBatchesYet}

: null} + {batches.length === 0 ? : null}
-

{t.adminBatchesPage.newBatchHeading}

+

{t.adminBatchesPage.newBatchHeading}

-
- - +
+ + + {harvests.map((h) => ( + + ))} +
-
- - +
+ +
-
- - +
+ + +
+
+
-
diff --git a/src/app/admin/ernten/page.tsx b/src/app/admin/ernten/page.tsx index 3f6aa25..551cb8c 100644 --- a/src/app/admin/ernten/page.tsx +++ b/src/app/admin/ernten/page.tsx @@ -1,8 +1,18 @@ -import Link from "next/link"; import { prisma } from "@/lib/prisma"; import { BackLink } from "@/components/back-link"; import { ConfirmForm } from "@/components/confirm-form"; import { formatDate } from "@/lib/format"; +import { Button } from "@/components/ui"; +import { AdminField, AdminSwitch, AdminTextarea } from "@/components/admin-form"; +import { + AdminActionButton, + AdminActionLink, + AdminActionNote, + AdminBadge, + AdminEmptyState, + AdminPageHeader, + AdminRowActions, +} from "@/components/admin-list"; import { createHarvestAction, toggleHarvestPublishedAction, deleteHarvestAction } from "../actions"; import { ApiCoreSyncPanel, ApiCoreSyncResult } from "@/components/apicore-sync-panel"; import { getLocale } from "@/i18n/locale"; @@ -19,7 +29,7 @@ export default async function AdminHarvestsPage(props: PageProps<"/admin/ernten" return (
-

{t.adminHarvestsPage.title}

+ @@ -41,74 +51,64 @@ export default async function AdminHarvestsPage(props: PageProps<"/admin/ernten" {harvests.map((harvest) => ( {harvest.type} - {harvest.year} - {formatDate(harvest.harvestDate)} + {harvest.year} + {formatDate(harvest.harvestDate)} {harvest.location} - {harvest._count.batches} - {harvest._count.products} + {harvest._count.batches} + {harvest._count.products} - - {harvest.isPublished ? t.adminHarvestsPage.statusPublished : t.adminHarvestsPage.statusDraft} - + {harvest.isPublished ? ( + {t.adminHarvestsPage.statusPublished} + ) : ( + {t.adminHarvestsPage.statusDraft} + )} - - - {t.adminHarvestsPage.edit} - -
+ + + {t.adminHarvestsPage.edit} + {harvest.isPublished ? null : } - + {harvest._count.batches === 0 && harvest._count.products === 0 ? ( - + {t.adminHarvestsPage.delete} ) : ( // Statt den Knopf wortlos wegzulassen: der Grund steht da. - {t.adminHarvestsPage.notDeletable} + {t.adminHarvestsPage.notDeletable} )} +
))} - {harvests.length === 0 ?

{t.adminHarvestsPage.noHarvestsYet}

: null} + {harvests.length === 0 ? : null}
-

{t.adminHarvestsPage.newHarvestHeading}

+

{t.adminHarvestsPage.newHarvestHeading}

-
- - +
+ + +
+ + + + +
+
- - -