Refactor code
This commit is contained in:
@ -8,6 +8,7 @@ import ArtworkVariants from "@/components/artworks/single/ArtworkVariants";
|
||||
import DeleteArtworkButton from "@/components/artworks/single/DeleteArtworkButton";
|
||||
import EditArtworkForm from "@/components/artworks/single/EditArtworkForm";
|
||||
|
||||
// Single artwork edit page.
|
||||
export default async function ArtworkSinglePage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params;
|
||||
|
||||
@ -16,30 +17,30 @@ export default async function ArtworkSinglePage({ params }: { params: Promise<{
|
||||
const categories = await getCategoriesWithTags();
|
||||
const tags = await getTags();
|
||||
|
||||
if (!item) return <div>Artwork with this id not found</div>
|
||||
if (!item) return <div>Artwork with this id not found</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold mb-4">Edit artwork</h1>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<div className="space-y-6">
|
||||
{item ? <EditArtworkForm artwork={item} tags={tags} categories={categories} /> : 'Artwork not found...'}
|
||||
<EditArtworkForm artwork={item} tags={tags} categories={categories} />
|
||||
<div>
|
||||
{item && <DeleteArtworkButton artworkId={item.id} />}
|
||||
<DeleteArtworkButton artworkId={item.id} />
|
||||
</div>
|
||||
<div>
|
||||
{item && <ArtworkTimelapse artworkId={item.id} timelapse={item.timelapse} />}
|
||||
<ArtworkTimelapse artworkId={item.id} timelapse={item.timelapse} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
{item && <ArtworkColors colors={item.colors} artworkId={item.id} />}
|
||||
<ArtworkColors colors={item.colors} artworkId={item.id} />
|
||||
</div>
|
||||
<div>
|
||||
{item && <ArtworkDetails artwork={item} />}
|
||||
<ArtworkDetails artwork={item} />
|
||||
</div>
|
||||
<div>
|
||||
{item && <ArtworkVariants artworkId={item.id} variants={item.variants} />}
|
||||
<ArtworkVariants artworkId={item.id} variants={item.variants} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { ArtworkColorProcessor } from "@/components/artworks/ArtworkColorProcessor";
|
||||
import { ArtworksTable } from "@/components/artworks/ArtworksTable";
|
||||
|
||||
// Admin artworks list page.
|
||||
export default async function ArtworksPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import EditCategoryForm from "@/components/categories/EditCategoryForm";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// Edit category page.
|
||||
export default async function PortfolioCategoriesEditPage({ params }: { params: { id: string } }) {
|
||||
const { id } = await params;
|
||||
const category = await prisma.artCategory.findUnique({
|
||||
where: {
|
||||
id,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -15,4 +16,4 @@ export default async function PortfolioCategoriesEditPage({ params }: { params:
|
||||
{category && <EditCategoryForm category={category} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import NewCategoryForm from "@/components/categories/NewCategoryForm";
|
||||
|
||||
// Create a new category page.
|
||||
export default function PortfolioCategoriesNewPage() {
|
||||
return (
|
||||
<div>
|
||||
@ -7,4 +8,4 @@ export default function PortfolioCategoriesNewPage() {
|
||||
<NewCategoryForm />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import { PlusCircleIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Suspense } from "react";
|
||||
|
||||
// Admin categories management page.
|
||||
export default async function CategoriesPage() {
|
||||
const items = await getCategoriesWithCount();
|
||||
|
||||
@ -11,7 +12,10 @@ export default async function CategoriesPage() {
|
||||
<div>
|
||||
<div className="flex gap-4 justify-between pb-8">
|
||||
<h1 className="text-2xl font-bold mb-4">Art Categories</h1>
|
||||
<Link href="/categories/new" className="flex gap-2 items-center cursor-pointer bg-primary hover:bg-primary/90 text-primary-foreground px-4 py-2 rounded">
|
||||
<Link
|
||||
href="/categories/new"
|
||||
className="flex gap-2 items-center cursor-pointer bg-primary hover:bg-primary/90 text-primary-foreground px-4 py-2 rounded"
|
||||
>
|
||||
<PlusCircleIcon className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all text-primary-foreground" /> Add new category
|
||||
</Link>
|
||||
</div>
|
||||
@ -24,4 +28,4 @@ export default async function CategoriesPage() {
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { listCommissionCustomCardImages } from "@/actions/commissions/customCards/images";
|
||||
import EditCustomCardForm from "@/components/commissions/customCards/EditCustomCardForm";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
// Edit custom commission card page.
|
||||
export default async function CommissionCustomCardEditPage({
|
||||
params,
|
||||
}: {
|
||||
@ -10,7 +10,7 @@ export default async function CommissionCustomCardEditPage({
|
||||
}) {
|
||||
const { id } = await params;
|
||||
|
||||
const [card, options, extras, images, tags] = await Promise.all([
|
||||
const [card, options, extras, tags] = await Promise.all([
|
||||
prisma.commissionCustomCard.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
@ -21,7 +21,6 @@ export default async function CommissionCustomCardEditPage({
|
||||
}),
|
||||
prisma.commissionOption.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }),
|
||||
prisma.commissionExtra.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }),
|
||||
listCommissionCustomCardImages(),
|
||||
prisma.tag.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }),
|
||||
]);
|
||||
|
||||
@ -38,7 +37,6 @@ export default async function CommissionCustomCardEditPage({
|
||||
card={card}
|
||||
allOptions={options}
|
||||
allExtras={extras}
|
||||
images={images}
|
||||
allTags={tags}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import { listCommissionCustomCardImages } from "@/actions/commissions/customCards/images";
|
||||
import NewCustomCardForm from "@/components/commissions/customCards/NewCustomCardForm";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// New custom commission card page.
|
||||
export default async function CommissionCustomCardsNewPage() {
|
||||
const [options, extras, images, tags] = await Promise.all([
|
||||
const [options, extras, tags] = await Promise.all([
|
||||
prisma.commissionOption.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }),
|
||||
prisma.commissionExtra.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }),
|
||||
listCommissionCustomCardImages(),
|
||||
prisma.tag.findMany({ orderBy: [{ sortIndex: "asc" }, { name: "asc" }] }),
|
||||
]);
|
||||
|
||||
@ -15,7 +14,7 @@ export default async function CommissionCustomCardsNewPage() {
|
||||
<div className="flex gap-4 justify-between pb-8">
|
||||
<h1 className="text-2xl font-bold mb-4">New Custom Commission Card</h1>
|
||||
</div>
|
||||
<NewCustomCardForm options={options} extras={extras} images={images} tags={tags} />
|
||||
<NewCustomCardForm options={options} extras={extras} tags={tags} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { PlusCircleIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
// Custom commission cards list page.
|
||||
export default async function CommissionCustomCardsPage() {
|
||||
const cards = await prisma.commissionCustomCard.findMany({
|
||||
include: {
|
||||
|
||||
@ -2,6 +2,7 @@ import { listCommissionExamples } from "@/actions/commissions/examples";
|
||||
import { getActiveGuidelines } from "@/actions/commissions/guidelines/getGuidelines";
|
||||
import GuidelinesEditor from "@/components/commissions/guidelines/Editor";
|
||||
|
||||
// Admin page for editing commission guidelines.
|
||||
export default async function CommissionGuidelinesPage() {
|
||||
const [{ markdown, exampleImageUrl }, examples] = await Promise.all([
|
||||
getActiveGuidelines(),
|
||||
|
||||
@ -4,6 +4,7 @@ import { prisma } from "@/lib/prisma";
|
||||
|
||||
import type { BoardItem, ColumnsState } from "@/types/Board";
|
||||
|
||||
// Admin kanban page for commission requests.
|
||||
export default async function CommissionsBoardPage() {
|
||||
const requests = await prisma.commissionRequest.findMany({
|
||||
where: {
|
||||
|
||||
@ -2,6 +2,7 @@ import { getCommissionRequestById } from "@/actions/commissions/requests/getComm
|
||||
import { CommissionRequestEditor } from "@/components/commissions/requests/CommissionRequestEditor";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
// Admin page for editing a single commission request.
|
||||
export default async function CommissionRequestPage({
|
||||
params,
|
||||
}: {
|
||||
@ -20,7 +21,7 @@ export default async function CommissionRequestPage({
|
||||
Submitted: {new Date(request.createdAt).toLocaleString()} · ID: {request.id}
|
||||
</p>
|
||||
</div>
|
||||
<CommissionRequestEditor request={request as any} />
|
||||
<CommissionRequestEditor request={request} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import RequestsTable from "@/components/commissions/requests/RequestsTable";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// Server-rendered commissions list page.
|
||||
export default async function CommissionPage() {
|
||||
const items = await prisma.commissionRequest.findMany({
|
||||
include: {
|
||||
@ -15,11 +16,11 @@ export default async function CommissionPage() {
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold">Commission Requests</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
List of all incomming requests via website.
|
||||
List of all incoming requests via website.
|
||||
</p>
|
||||
</div>
|
||||
<RequestsTable requests={items} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import EditTypeForm from "@/components/commissions/types/EditTypeForm";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// Edit commission type page.
|
||||
export default async function CommissionTypesEditPage({ params }: { params: { id: string } }) {
|
||||
const { id } = await params;
|
||||
const commissionType = await prisma.commissionType.findUnique({
|
||||
@ -13,7 +14,7 @@ export default async function CommissionTypesEditPage({ params }: { params: { id
|
||||
customInputs: { include: { customInput: true }, orderBy: { sortIndex: "asc" } },
|
||||
tags: true,
|
||||
},
|
||||
})
|
||||
});
|
||||
const tags = await prisma.tag.findMany({
|
||||
orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
|
||||
});
|
||||
@ -22,13 +23,10 @@ export default async function CommissionTypesEditPage({ params }: { params: { id
|
||||
});
|
||||
const extras = await prisma.commissionExtra.findMany({
|
||||
orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
|
||||
})
|
||||
// const customInputs = await prisma.commissionCustomInput.findMany({
|
||||
// orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
|
||||
// })
|
||||
});
|
||||
|
||||
if (!commissionType) {
|
||||
return <div>Type not found</div>
|
||||
return <div>Type not found</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { ExtraListClient } from "@/components/commissions/extras/ExtraListClient";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// Admin page for managing commission extras.
|
||||
export default async function CommissionTypesExtrasPage() {
|
||||
const extras = await prisma.commissionExtra.findMany({
|
||||
orderBy: [{ createdAt: "asc" }, { name: "asc" }],
|
||||
});
|
||||
|
||||
return <ExtraListClient extras={extras} />;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import NewTypeForm from "@/components/commissions/types/NewTypeForm";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// Create new commission type page.
|
||||
export default async function CommissionTypesNewPage() {
|
||||
const tags = await prisma.tag.findMany({
|
||||
orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
|
||||
@ -10,10 +11,10 @@ export default async function CommissionTypesNewPage() {
|
||||
});
|
||||
const extras = await prisma.commissionExtra.findMany({
|
||||
orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
|
||||
})
|
||||
});
|
||||
const customInputs = await prisma.commissionCustomInput.findMany({
|
||||
orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
|
||||
})
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -27,6 +28,5 @@ export default async function CommissionTypesNewPage() {
|
||||
tags={tags}
|
||||
/>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { OptionsListClient } from "@/components/commissions/options/OptionsListClient";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// Admin page for managing commission options.
|
||||
export default async function CommissionTypesOptionsPage() {
|
||||
const options = await prisma.commissionOption.findMany({
|
||||
orderBy: [{ createdAt: "asc" }, { name: "asc" }],
|
||||
});
|
||||
|
||||
return <OptionsListClient options={options} />;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { PlusCircleIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
// Commission types list page.
|
||||
export default async function CommissionTypesPage() {
|
||||
const types = await prisma.commissionType.findMany({
|
||||
include: {
|
||||
@ -17,11 +18,18 @@ export default async function CommissionTypesPage() {
|
||||
<div>
|
||||
<div className="flex gap-4 justify-between pb-8">
|
||||
<h1 className="text-2xl font-bold mb-4">Commission Types</h1>
|
||||
<Link href="/commissions/types/new" className="flex gap-2 items-center cursor-pointer bg-primary hover:bg-primary/90 text-primary-foreground px-4 py-2 rounded">
|
||||
<Link
|
||||
href="/commissions/types/new"
|
||||
className="flex gap-2 items-center cursor-pointer bg-primary hover:bg-primary/90 text-primary-foreground px-4 py-2 rounded"
|
||||
>
|
||||
<PlusCircleIcon className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all text-primary-foreground" /> Add new Type
|
||||
</Link>
|
||||
</div>
|
||||
{types && types.length > 0 ? <ListTypes types={types} /> : <p className="text-muted-foreground italic">No types found.</p>}
|
||||
{types && types.length > 0 ? (
|
||||
<ListTypes types={types} />
|
||||
) : (
|
||||
<p className="text-muted-foreground italic">No types found.</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,25 +3,15 @@ import Footer from "@/components/global/Footer";
|
||||
import MobileSidebar from "@/components/global/MobileSidebar";
|
||||
import ModeToggle from "@/components/global/ModeToggle";
|
||||
import Sidebar from "@/components/global/Sidebar";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
// Main admin layout with sidebar, header actions, and footer.
|
||||
export default function AdminLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
children: ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
// <div className="flex flex-col min-h-screen min-w-screen">
|
||||
// <header className="sticky top-0 z-50 h-14 w-full border-b bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/60 px-4 py-2">
|
||||
// <Header />
|
||||
// </header>
|
||||
// <main className="container mx-auto px-4 py-8">
|
||||
// {children}
|
||||
// </main>
|
||||
// <footer className="mt-auto px-4 py-2 h-14 border-t bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/60">
|
||||
// <Footer />
|
||||
// </footer>
|
||||
// <Toaster />
|
||||
// </div>
|
||||
<div className="min-h-screen w-full">
|
||||
<div className="flex min-h-screen w-full">
|
||||
<aside className="hidden md:flex md:w-64 md:flex-col md:border-r md:bg-background">
|
||||
|
||||
@ -6,14 +6,7 @@ import { StatusPill } from "@/components/home/StatusPill";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
|
||||
function fmtDate(d: Date) {
|
||||
return new Intl.DateTimeFormat("de-DE", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
}).format(d);
|
||||
}
|
||||
|
||||
// Admin dashboard summary page.
|
||||
export default async function HomePage() {
|
||||
const data = await getAdminDashboard();
|
||||
|
||||
@ -28,9 +21,6 @@ export default async function HomePage() {
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{/* <Button asChild variant="secondary">
|
||||
<Link href="/artworks/new">Add artwork</Link>
|
||||
</Button> */}
|
||||
<Button asChild>
|
||||
<Link href="/commissions/requests">Review requests</Link>
|
||||
</Button>
|
||||
@ -80,48 +70,6 @@ export default async function HomePage() {
|
||||
</section>
|
||||
|
||||
<section className="grid gap-4 lg:grid-cols-3">
|
||||
{/* Artwork status */}
|
||||
{/* <Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Artwork status</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2">
|
||||
<StatusPill label="Published" value={data.artworks.published} />
|
||||
<StatusPill label="Unpublished" value={data.artworks.unpublished} />
|
||||
<StatusPill label="NSFW" value={data.artworks.nsfw} />
|
||||
<StatusPill label="Set as header" value={data.artworks.header} />
|
||||
</CardContent>
|
||||
</Card> */}
|
||||
|
||||
{/* Color pipeline */}
|
||||
{/* <Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Color pipeline</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2">
|
||||
<StatusPill
|
||||
label="Pending"
|
||||
value={data.artworks.colorStatus.PENDING}
|
||||
/>
|
||||
<StatusPill
|
||||
label="Processing"
|
||||
value={data.artworks.colorStatus.PROCESSING}
|
||||
/>
|
||||
<StatusPill
|
||||
label="Ready"
|
||||
value={data.artworks.colorStatus.READY}
|
||||
/>
|
||||
<StatusPill
|
||||
label="Failed"
|
||||
value={data.artworks.colorStatus.FAILED}
|
||||
/>
|
||||
<div className="pt-2 text-sm text-muted-foreground">
|
||||
Tip: keep “Failed” near zero—those typically need a re-run or file
|
||||
fix.
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card> */}
|
||||
|
||||
{/* Commissions status */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
@ -153,84 +101,6 @@ export default async function HomePage() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
</section>
|
||||
|
||||
{/* Recent activity */}
|
||||
{/* <section className="grid gap-4 lg:grid-cols-2">
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between">
|
||||
<CardTitle className="text-base">Recent artworks</CardTitle>
|
||||
<Button asChild variant="ghost" size="sm">
|
||||
<Link href="/artworks">Open</Link>
|
||||
</Button>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{data.artworks.recent.length === 0 ? (
|
||||
<div className="text-sm text-muted-foreground">No artworks yet.</div>
|
||||
) : (
|
||||
<ul className="space-y-2">
|
||||
{data.artworks.recent.map((a) => (
|
||||
<li
|
||||
key={a.id}
|
||||
className="flex items-center justify-between gap-3 rounded-md border px-3 py-2"
|
||||
>
|
||||
<div className="min-w-0">
|
||||
<div className="truncate font-medium">{a.name}</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{fmtDate(a.createdAt)} · {a.colorStatus}
|
||||
{a.published ? " · published" : " · draft"}
|
||||
{a.needsWork ? " · needs work" : ""}
|
||||
</div>
|
||||
</div>
|
||||
<Button asChild variant="secondary" size="sm">
|
||||
<Link href={`/artworks/${a.slug}`}>Open</Link>
|
||||
</Button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between">
|
||||
<CardTitle className="text-base">Recent commission requests</CardTitle>
|
||||
<Button asChild variant="ghost" size="sm">
|
||||
<Link href="/commissions/requests">Open</Link>
|
||||
</Button>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{data.commissions.recent.length === 0 ? (
|
||||
<div className="text-sm text-muted-foreground">
|
||||
No commission requests yet.
|
||||
</div>
|
||||
) : (
|
||||
<ul className="space-y-2">
|
||||
{data.commissions.recent.map((r) => (
|
||||
<li
|
||||
key={r.id}
|
||||
className="flex items-center justify-between gap-3 rounded-md border px-3 py-2"
|
||||
>
|
||||
<div className="min-w-0">
|
||||
<div className="truncate font-medium">
|
||||
{r.customerName}{" "}
|
||||
<span className="text-muted-foreground">
|
||||
({r.customerEmail})
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{fmtDate(r.createdAt)} · {r.status}
|
||||
</div>
|
||||
</div>
|
||||
<Button asChild variant="secondary" size="sm">
|
||||
<Link href={`/commissions/requests/${r.id}`}>Open</Link>
|
||||
</Button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</section> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import EditTagForm from "@/components/tags/EditTagForm";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// Edit tag page.
|
||||
export default async function PortfolioTagsEditPage({ params }: { params: { id: string } }) {
|
||||
const { id } = await params;
|
||||
const tag = await prisma.tag.findUnique({
|
||||
@ -15,8 +16,8 @@ export default async function PortfolioTagsEditPage({ params }: { params: { id:
|
||||
},
|
||||
},
|
||||
aliases: true
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
const categories = await prisma.artCategory.findMany({
|
||||
include: { tagLinks: true },
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import NewTagForm from "@/components/tags/NewTagForm";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// Create a new tag page.
|
||||
export default async function PortfolioTagsNewPage() {
|
||||
const categories = await prisma.artCategory.findMany({
|
||||
include: { tagLinks: true },
|
||||
|
||||
@ -4,6 +4,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { PlusCircleIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
// Admin tags management page.
|
||||
export default async function ArtTagsPage() {
|
||||
const items = await prisma.tag.findMany({
|
||||
include: {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { getLatestTos } from "@/actions/tos/getTos";
|
||||
import TosEditor from "@/components/tos/Editor";
|
||||
|
||||
// Admin page for editing Terms of Service.
|
||||
export default async function TosPage() {
|
||||
const markdown = await getLatestTos();
|
||||
|
||||
@ -14,4 +15,4 @@ export default async function TosPage() {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import UploadBulkImageForm from "@/components/uploads/UploadBulkImageForm";
|
||||
|
||||
// Bulk image upload page.
|
||||
export default function UploadsBulkPage() {
|
||||
return (
|
||||
<div><UploadBulkImageForm /></div>
|
||||
<div>
|
||||
<UploadBulkImageForm />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import UploadImageForm from "@/components/uploads/UploadImageForm";
|
||||
|
||||
// Single image upload page.
|
||||
export default function UploadsSinglePage() {
|
||||
return (
|
||||
<div><UploadImageForm /></div>
|
||||
<div>
|
||||
<UploadImageForm />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import { CreateUserForm } from "@/components/users/CreateUserForm";
|
||||
import { auth } from "@/lib/auth";
|
||||
import type { SessionWithRole } from "@/types/auth";
|
||||
import { headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
// Admin-only user creation page.
|
||||
export default async function NewUserPage() {
|
||||
const session = await auth.api.getSession({ headers: await headers() });
|
||||
const role = (session as any)?.user?.role;
|
||||
const role = (session as SessionWithRole)?.user?.role;
|
||||
|
||||
if (!session) redirect("/login");
|
||||
if (role !== "admin") redirect("/");
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import { UsersTable } from "@/components/users/UsersTable";
|
||||
import { auth } from "@/lib/auth";
|
||||
import type { SessionWithRole } from "@/types/auth";
|
||||
import { headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
// Admin users list page.
|
||||
export default async function UsersPage() {
|
||||
const session = await auth.api.getSession({ headers: await headers() });
|
||||
const role = (session as any)?.user?.role as string | undefined;
|
||||
const role = (session as SessionWithRole)?.user?.role;
|
||||
|
||||
if (!session) redirect("/login");
|
||||
if (role !== "admin") redirect("/");
|
||||
|
||||
Reference in New Issue
Block a user