Files
craft2prints/src/app/admin/designs/page.tsx
T
AndymickandClaude Haiku 4.5 0d2e874f14 Fetch designs without relations to avoid database corruption issues
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>
2026-07-18 19:11:19 +01:00

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>
);
}