Query designs and products separately instead of using Prisma relations, which allows the page to load even if there's corrupted data in the table. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
96 lines
3.1 KiB
TypeScript
96 lines
3.1 KiB
TypeScript
import Link from 'next/link';
|
|
import { prisma } from '@/lib/prisma';
|
|
import AdminNav from '@/components/AdminNav';
|
|
|
|
export default async function DesignsPage() {
|
|
let designs = [];
|
|
try {
|
|
designs = await prisma.designApproval.findMany({
|
|
select: {
|
|
id: true,
|
|
customerName: true,
|
|
customerEmail: true,
|
|
designPreviewUrl: true,
|
|
status: true,
|
|
createdAt: true,
|
|
productId: true,
|
|
},
|
|
orderBy: { createdAt: 'desc' },
|
|
});
|
|
|
|
// Fetch products separately to avoid relation issues
|
|
const productIds = [...new Set(designs.map(d => d.productId))];
|
|
const products = await prisma.product.findMany({
|
|
where: { id: { in: productIds } },
|
|
select: { id: true, name: true },
|
|
});
|
|
const productMap = Object.fromEntries(products.map(p => [p.id, p]));
|
|
|
|
// Add product to each design
|
|
designs = designs.map(d => ({
|
|
...d,
|
|
product: productMap[d.productId],
|
|
messages: [],
|
|
})) as any;
|
|
} catch (err) {
|
|
console.error('Error fetching designs:', err);
|
|
}
|
|
|
|
const pending = designs.filter((d) => d.status === 'PENDING');
|
|
const approved = designs.filter((d) => d.status === 'APPROVED');
|
|
const rejected = designs.filter((d) => d.status === 'REJECTED');
|
|
|
|
const DesignList = ({ items, status }: { items: typeof designs; status: string }) => (
|
|
<div className="mt-8">
|
|
<h2 className="font-display text-lg">{status}</h2>
|
|
{items.length === 0 ? (
|
|
<p className="mt-4 text-sm text-muted">No designs.</p>
|
|
) : (
|
|
<div className="mt-4 divide-y divide-line border-t border-line">
|
|
{items.map((d) => {
|
|
const last = d.messages[0];
|
|
return (
|
|
<Link
|
|
key={d.id}
|
|
href={`/admin/designs/${d.id}`}
|
|
className="flex items-center gap-4 py-4 hover:bg-surface"
|
|
>
|
|
<img
|
|
src={d.designPreviewUrl}
|
|
alt="design preview"
|
|
className="h-14 w-14 border border-line object-cover"
|
|
/>
|
|
<div className="flex-1">
|
|
<p className="font-medium">{d.customerName || d.customerEmail}</p>
|
|
<p className="truncate text-sm text-muted">
|
|
{last ? (
|
|
<>
|
|
{last.sender === 'ADMIN' ? 'You: ' : ''}
|
|
{last.body}
|
|
</>
|
|
) : (
|
|
'No messages yet'
|
|
)}
|
|
</p>
|
|
</div>
|
|
<span className="tag-label">{d.product.name}</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<div className="mx-auto max-w-4xl px-6 py-12">
|
|
<AdminNav />
|
|
<h1 className="font-display text-3xl">Design Approvals</h1>
|
|
|
|
<DesignList items={pending} status={`Pending (${pending.length})`} />
|
|
<DesignList items={approved} status={`Approved (${approved.length})`} />
|
|
<DesignList items={rejected} status={`Rejected (${rejected.length})`} />
|
|
</div>
|
|
);
|
|
}
|