Files
backend/scripts/seed-website-slot-defaults.ts
T

140 lines
3.6 KiB
TypeScript

/**
* Seed curated homepage images into website slots for mana-collection.
* Creates missing cells and fills empty imageUrl with storefront defaults.
* Never overwrites an existing uploaded imageUrl.
*
* Usage: npm run seed:website-slots
*/
import { PrismaClient } from '@prisma/client';
import { WEBSITE_SLOT_DEFS } from '../src/website-slots/website-slot.catalog';
const prisma = new PrismaClient();
type DefaultRow = {
slotKey: string;
sortOrder: number;
nameFa: string;
nameEn: string;
subtext: string;
link: string;
imageUrl: string;
};
function collectDefaults(): DefaultRow[] {
const rows: DefaultRow[] = [];
for (const def of WEBSITE_SLOT_DEFS) {
if (def.kind === 'fixed' && def.defaultImage) {
rows.push({
slotKey: def.slug,
sortOrder: 0,
nameFa: def.defaultNameFa || def.labelFa,
nameEn: def.defaultNameEn || def.labelEn,
subtext: def.defaultSubtext || '',
link: def.defaultLink || '',
imageUrl: def.defaultImage,
});
continue;
}
if (def.kind === 'fixed-set' && def.cellLabels?.length) {
def.cellLabels.forEach((cell, index) => {
if (!cell.defaultImage) return;
rows.push({
slotKey: def.slug,
sortOrder: index,
nameFa: cell.defaultNameFa || cell.labelFa,
nameEn: cell.defaultNameEn || cell.labelEn,
subtext: cell.defaultSubtext || '',
link: cell.defaultLink || '',
imageUrl: cell.defaultImage,
});
});
}
}
return rows;
}
async function main() {
const store = await prisma.store.findFirst({
where: { slug: 'mana-collection' },
select: { id: true, slug: true },
});
if (!store) {
throw new Error('Store mana-collection not found. Run seed:mana-store first.');
}
const defaults = collectDefaults();
let created = 0;
let filled = 0;
let skipped = 0;
for (const row of defaults) {
const existing = await prisma.websiteSlotItem.findFirst({
where: {
storeId: store.id,
slotKey: row.slotKey,
sortOrder: row.sortOrder,
},
});
if (!existing) {
await prisma.websiteSlotItem.create({
data: {
storeId: store.id,
slotKey: row.slotKey,
sortOrder: row.sortOrder,
nameFa: row.nameFa,
nameEn: row.nameEn,
subtext: row.subtext,
linkText: '',
link: row.link,
imageUrl: row.imageUrl,
},
});
created += 1;
continue;
}
const shouldReplaceImage =
!existing.imageUrl ||
// Replace legacy CDN category icons with curated transparent assets.
(row.slotKey === 'category-strip' &&
!existing.imageUrl.includes('/images/transparent/')) ||
((row.slotKey === 'opening-frame' || row.slotKey === 'sticky-story') &&
!existing.imageUrl.includes('/images/landscapes/'));
if (shouldReplaceImage) {
await prisma.websiteSlotItem.update({
where: { id: existing.id },
data: {
imageUrl: row.imageUrl,
nameFa: existing.nameFa || row.nameFa,
nameEn: existing.nameEn || row.nameEn,
subtext: existing.subtext || row.subtext,
link: existing.link || row.link,
},
});
filled += 1;
continue;
}
skipped += 1;
}
console.log('Seeded website slot defaults:', {
store: store.slug,
created,
filledEmptyImages: filled,
alreadyHadImages: skipped,
totalDefaults: defaults.length,
});
}
main()
.catch((err) => {
console.error(err);
process.exitCode = 1;
})
.finally(async () => {
await prisma.$disconnect();
});