Files
AndymickandClaude Haiku 4.5 9c7999b8d7 Fix: Set Classic Tee photoRecolorable to false for proper image display
The Classic Tee product was set to photoRecolorable: true, which applied a
color tint/filter to the product images instead of displaying them normally.
Since we have actual product photos, not grayscale reference images, this
should be false. Colors are still selectable and saved with orders.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-19 11:43:35 +01:00

215 lines
5.7 KiB
TypeScript

import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const categories = [
{ name: 'Apparel', slug: 'APPAREL' },
{ name: 'Drinkware', slug: 'DRINKWARE' },
{ name: 'Phone case', slug: 'PHONE_CASE' },
];
const products: Array<{
slug: string;
name: string;
category: string;
description: string;
basePrice: number;
colors: string[];
sizes: string[];
mockup: string;
printArea: { xPct: number; yPct: number; wPct: number; hPct: number };
imageUrl: string | null;
imageUrlBack: string | null;
photoRecolorable: boolean;
}> = [
{
slug: 'classic-tee',
name: 'Classic Tee',
category: 'APPAREL',
description:
'Heavyweight 220gsm combed cotton. Your artwork, printed dead-center on the chest.',
basePrice: 2900,
colors: ['#FFFFFF', '#17181C', '#2B4C3F', '#8A8578', '#1EA7E0', '#E5227E', '#F5871F', '#5B2C86', '#7A1F1F', '#D8D2C2'],
sizes: ['S', 'M', 'L', 'XL', 'XXL'],
mockup: 'tshirt',
printArea: { xPct: 0.32, yPct: 0.27, wPct: 0.36, hPct: 0.34 },
imageUrl: '/seed/tee-front.png',
imageUrlBack: '/seed/tee-back.png',
photoRecolorable: false,
},
{
slug: 'heavyweight-hoodie',
name: 'Heavyweight Hoodie',
category: 'APPAREL',
description: 'Brushed-fleece interior, boxy fit. Built for a bold front print.',
basePrice: 5400,
colors: ['#17181C', '#8A8578', '#2B4C3F', '#7A1F1F', '#1EA7E0', '#D8D2C2', '#5B2C86'],
sizes: ['S', 'M', 'L', 'XL', 'XXL'],
mockup: 'hoodie',
printArea: { xPct: 0.33, yPct: 0.34, wPct: 0.34, hPct: 0.28 },
imageUrl: null,
imageUrlBack: null,
photoRecolorable: false,
},
{
slug: 'ceramic-mug',
name: 'Ceramic Mug',
category: 'DRINKWARE',
description: '11oz glossy ceramic. Dishwasher-safe, wraparound print.',
basePrice: 1800,
colors: ['#FFFFFF', '#17181C', '#1EA7E0', '#E5227E', '#F5871F', '#5B2C86'],
sizes: [],
mockup: 'mug',
printArea: { xPct: 0.22, yPct: 0.32, wPct: 0.56, hPct: 0.28 },
imageUrl: null,
imageUrlBack: null,
photoRecolorable: false,
},
{
slug: 'travel-tumbler',
name: 'Travel Tumbler',
category: 'DRINKWARE',
description: '16oz double-wall stainless steel, keeps drinks cold for 24 hours.',
basePrice: 3200,
colors: ['#17181C', '#8A8578', '#C9A227', '#1EA7E0', '#E5227E', '#2B4C3F'],
sizes: [],
mockup: 'tumbler',
printArea: { xPct: 0.26, yPct: 0.28, wPct: 0.48, hPct: 0.36 },
imageUrl: null,
imageUrlBack: null,
photoRecolorable: false,
},
{
slug: 'snap-phone-case',
name: 'Snap Phone Case',
category: 'PHONE_CASE',
description: 'Slim polycarbonate snap case with a matte finish.',
basePrice: 2200,
colors: ['#FFFFFF', '#17181C', '#2B4C3F', '#E5227E', '#1EA7E0', '#F5871F'],
sizes: [],
mockup: 'phonecase',
printArea: { xPct: 0.28, yPct: 0.16, wPct: 0.44, hPct: 0.5 },
imageUrl: null,
imageUrlBack: null,
photoRecolorable: false,
},
{
slug: 'tough-phone-case',
name: 'Tough Phone Case',
category: 'PHONE_CASE',
description: 'Dual-layer shock-resistant case with raised edges.',
basePrice: 2900,
colors: ['#17181C', '#8A8578', '#5B2C86', '#7A1F1F'],
sizes: [],
mockup: 'phonecase',
printArea: { xPct: 0.3, yPct: 0.18, wPct: 0.4, hPct: 0.46 },
imageUrl: null,
imageUrlBack: null,
photoRecolorable: false,
},
{
slug: 'shaker-bottle',
name: 'Shaker Bottle',
category: 'DRINKWARE',
description: '20oz insulated shaker bottle with a secure flip-top lid. Your artwork, printed on the body.',
basePrice: 1500,
colors: ['#FFFFFF'],
sizes: [],
mockup: 'tumbler',
printArea: { xPct: 0.32, yPct: 0.36, wPct: 0.36, hPct: 0.38 },
imageUrl: '/seed/shaker-bottle.png',
imageUrlBack: null,
photoRecolorable: false,
},
];
const shippingRates = [
{
service: 'Special Delivery Guaranteed by 9am',
basePrice: 895,
pricePerKg: 0,
estimatedDays: 1,
isInternational: false,
},
{
service: 'Royal Mail 24®',
basePrice: 495,
pricePerKg: 0,
estimatedDays: 1,
isInternational: false,
},
{
service: 'Royal Mail 48®',
basePrice: 295,
pricePerKg: 0,
estimatedDays: 3,
isInternational: false,
},
{
service: 'International Signed',
basePrice: 1495,
pricePerKg: 300,
estimatedDays: 14,
isInternational: true,
},
{
service: 'International Economy',
basePrice: 1095,
pricePerKg: 200,
estimatedDays: 21,
isInternational: true,
},
];
async function main() {
for (const c of categories) {
await prisma.category.upsert({
where: { slug: c.slug },
update: {},
create: c,
});
}
for (const p of products) {
const data = {
name: p.name,
category: p.category,
description: p.description,
basePrice: p.basePrice,
colors: JSON.stringify(p.colors),
sizes: p.sizes.length > 0 ? JSON.stringify(p.sizes) : null,
mockup: p.mockup,
printArea: JSON.stringify(p.printArea),
imageUrl: p.imageUrl,
imageUrlBack: p.imageUrlBack,
photoRecolorable: p.photoRecolorable,
};
await prisma.product.upsert({
where: { slug: p.slug },
update: data, // keeps these 6 starter products in sync when the seed file changes
create: { slug: p.slug, ...data },
});
}
for (const rate of shippingRates) {
await prisma.shippingRate.upsert({
where: { service: rate.service },
update: { ...rate },
create: { ...rate },
});
}
console.log(
`Seeded ${categories.length} categories, ${products.length} products, and ${shippingRates.length} shipping rates.`
);
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});