140 lines
3.6 KiB
TypeScript
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();
|
|
});
|