feat build

This commit is contained in:
mahmood
2026-09-18 17:30:32 +03:30
parent ffd952d754
commit 062fe80a68
341 changed files with 461 additions and 561 deletions
+71 -7
View File
@@ -88488,10 +88488,16 @@ var require_admin_products_service = __commonJS({
category: p.category ? { id: p.category.id, title: p.category.title, collection: { title: p.category.title } } : null,
// legacy grid slot reads `ProductImage`
ProductImage: p.images.map((i) => ({ id: i.id, image: i.url, full: i.url, url: i.url })),
images: p.images.map((i) => ({ id: i.id, url: i.url, sortOrder: i.sortOrder })),
images: p.images.map((i) => ({ id: i.id, url: i.url, sortOrder: i.sortOrder, role: i.role })),
// The order builder's grid reads `thumb.full` and `variants_all` directly
// off each row, so both travel with the list as they did in legacy.
thumb: p.images[0] ? { full: p.images[0].url } : null,
// off each row, so both travel with the list as they did in legacy. Legacy's
// grid thumbnail was always the featured image (تصویر شاخص) when one was
// set — fall back to the first gallery image only if no featured image
// was uploaded, rather than picking whichever image happens to sort first.
thumb: (() => {
const featured = p.images.find((i) => i.role === "FEATURED") ?? p.images[0];
return featured ? { full: featured.url } : null;
})(),
variants_all: p.variants.map((v) => this.variantRow(v)),
variants_count: p.variants.length,
// legacy min_gram / max_gram: the product's lightest and heaviest variant,
@@ -88521,7 +88527,14 @@ var require_admin_products_service = __commonJS({
brand_id: p.brandId,
cats: p.categoryId ? [p.categoryId] : [],
ciny_ids: p.collections.map((c) => c.collectionId),
variants_all: p.variants.map((v) => this.variantRow(v))
variants_all: p.variants.map((v) => this.variantRow(v)),
// The 4 image slots the product form edits, split back out by role —
// mirrors the shape AddProduct.vue posts (indicator_id / mobile_pic /
// images / images_mobile).
indicator_id: p.images.find((i) => i.role === "FEATURED")?.url ?? null,
mobile_pic: p.images.find((i) => i.role === "FEATURED_MOBILE")?.url ?? null,
images_gallery: p.images.filter((i) => i.role === "GALLERY").map((i) => i.url),
images_mobile: p.images.filter((i) => i.role === "GALLERY_MOBILE").map((i) => i.url)
};
}
variantRow(v) {
@@ -88629,6 +88642,35 @@ var require_admin_products_service = __commonJS({
isActive: v.is_active !== void 0 ? Boolean(v.is_active) : true
};
}
/**
* Legacy had 4 distinct upload slots (تصویر شاخص / تصویر شاخص موبایل /
* گالری تصاویر سایت / گالری تصاویر موبایل — AddProduct.vue's `indicator_id`,
* `mobile_pic`, `images`, and a mobile gallery). The form posts one URL each
* for the two featured slots and one URL array each for the two galleries;
* this flattens all four into the role-tagged rows ProductImage.create wants.
*/
imageCreateData(data) {
const rows = [];
const single = (v, role) => {
const url = typeof v === "string" ? v : String(v?.url ?? "");
if (url)
rows.push({ url, sortOrder: 0, role });
};
const gallery = (v, role) => {
if (!Array.isArray(v))
return;
v.forEach((item, i) => {
const url = typeof item === "string" ? item : String(item?.url ?? "");
if (url)
rows.push({ url, sortOrder: i, role });
});
};
single(data.indicator_id, "FEATURED");
single(data.mobile_pic, "FEATURED_MOBILE");
gallery(data.images, "GALLERY");
gallery(data.images_mobile, "GALLERY_MOBILE");
return rows;
}
/** Legacy `POST product/create`. */
async create(data) {
const title = String(data.title ?? "").trim();
@@ -88637,6 +88679,7 @@ var require_admin_products_service = __commonJS({
const cats = Array.isArray(data.cats) ? data.cats : [];
const cinyIds = Array.isArray(data.ciny_ids) ? data.ciny_ids : [];
const variants = Array.isArray(data.variants) ? data.variants : [];
const images = this.imageCreateData(data);
return this.prisma.product.create({
data: {
title,
@@ -88647,9 +88690,10 @@ var require_admin_products_service = __commonJS({
brandId: data.brand_id || null,
categoryId: cats[0] ?? null,
collections: cinyIds.length ? { create: cinyIds.map((collectionId) => ({ collectionId })) } : void 0,
variants: variants.length ? { create: variants.map((v) => this.variantCreateData(v)) } : void 0
variants: variants.length ? { create: variants.map((v) => this.variantCreateData(v)) } : void 0,
images: images.length ? { create: images } : void 0
},
include: { variants: true }
include: { variants: true, images: true }
});
}
/** Legacy `PUT product/update/{id}`. */
@@ -88699,6 +88743,15 @@ var require_admin_products_service = __commonJS({
data: { isActive: false }
});
}
if (["indicator_id", "mobile_pic", "images", "images_mobile"].some((k) => k in data)) {
const images = this.imageCreateData(data);
await this.prisma.productImage.deleteMany({ where: { productId: id } });
if (images.length) {
await this.prisma.productImage.createMany({
data: images.map((img) => ({ ...img, productId: id }))
});
}
}
return this.findOne(id);
}
/**
@@ -93248,7 +93301,18 @@ async function bootstrap() {
res.setHeader("Content-Type", mime);
}
});
await app.listen(process.env.PORT ?? 3e3, "0.0.0.0");
const port = Number(process.env.PORT ?? 3e3);
try {
await app.listen(port, "0.0.0.0");
} catch (err) {
if (err.code === "EADDRINUSE") {
console.error(`
[api] Port ${port} is already in use. Free it or change API_PORT in the repo-root .env (and PORT in apps/api/.env to match) \u2014 the API will not fall back to a different port.
`);
process.exit(1);
}
throw err;
}
}
bootstrap();
/*! Bundled license information: