feat(products): add ordered attributes and slim product list api

This commit is contained in:
2026-03-29 12:06:37 +03:00
parent c4ed7e6fc8
commit 0aab640e81
23 changed files with 544 additions and 315 deletions

3
.env
View File

@@ -16,5 +16,4 @@ SMS_WSDL_URL=http://payammatni.com/webservice/send.php?wsdl
SMS_USERNAME=engel5960
SMS_PASSWORD=replace-me
SMS_NUMBER=80008
OTP_TTL_SECONDS=120
OTP_TTL_SECONDS=120

View File

@@ -15,6 +15,7 @@ export declare class AdminProductsController {
}): Promise<{
attributes: {
id: string;
displayOrder: number;
attributeId: string;
name: string;
slug: string;
@@ -86,36 +87,17 @@ export declare class AdminProductsController {
createdAt: Date;
updatedAt: Date;
}>;
findAll(filters: FilterProductsDto): Promise<{
findAll(filters: FilterProductsDto, productType?: string): Promise<{
items: {
attributes: {
id: string;
attributeId: string;
name: string;
slug: string;
dataType: import("./enums/attribute-data-type.enum").AttributeDataType;
unit: string | null;
options: string[];
isFilterable: boolean;
isVisible: boolean;
valueText: string | null;
valueNumber: number | null;
valueBoolean: boolean | null;
valueJson: string[] | Record<string, unknown> | null;
}[];
meta: import("./entities/product-meta.entity").ProductMeta | {
shortDescription: string | null;
description: string | null;
seo: {
title: string;
description: string | null;
} | undefined;
share: {
title: string;
description: string | null;
imageUrl: string | null | undefined;
};
};
status?: import("./enums/product-status.enum").ProductStatus | undefined;
createdAt?: Date | undefined;
updatedAt?: Date | undefined;
id: string;
sku: string;
title: string;
slug: string;
technicalCode: string;
brand: string;
brandInfo: {
id: string;
name: string;
@@ -123,6 +105,15 @@ export declare class AdminProductsController {
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
} | null;
type: import("./enums/product-type.enum").ProductType;
featured: boolean;
basePriceUSD: number;
salePriceUSD: number | null;
stock: number;
averageRating: number;
reviewsCount: number;
mainImageUrl: string | null;
shortDescription: string | null;
primaryCategory: {
id: string;
name: string;
@@ -130,36 +121,19 @@ export declare class AdminProductsController {
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
} | null;
categories: {
attributes: {
id: string;
displayOrder: number;
attributeId: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
dataType: import("./enums/attribute-data-type.enum").AttributeDataType;
unit: string | null;
valueText: string | null;
valueNumber: number | null;
valueBoolean: boolean | null;
valueJson: string[] | Record<string, unknown> | null;
}[];
id: string;
sku: string;
title: string;
slug: string;
technicalCode: string;
brand: string;
brandEntity?: import("./entities/brand.entity").Brand | null;
basePriceUSD: number;
salePriceUSD?: number | null;
stock: number;
featured: boolean;
type: import("./enums/product-type.enum").ProductType;
status: import("./enums/product-status.enum").ProductStatus;
mainImageUrl?: string | null;
threeDModelUrl?: string | null;
imageGalleryUrls: string[];
tags: string[];
averageRating: number;
reviewsCount: number;
attributeValues: import("./entities/product-attribute-value.entity").ProductAttributeValue[];
reviews: import("./entities/product-review.entity").ProductReview[];
createdAt: Date;
updatedAt: Date;
}[];
meta: {
total: number;
@@ -183,6 +157,7 @@ export declare class AdminProductsController {
findOne(id: string): Promise<{
attributes: {
id: string;
displayOrder: number;
attributeId: string;
name: string;
slug: string;
@@ -261,6 +236,7 @@ export declare class AdminProductsController {
}): Promise<{
attributes: {
id: string;
displayOrder: number;
attributeId: string;
name: string;
slug: string;

View File

@@ -37,8 +37,8 @@ let AdminProductsController = class AdminProductsController {
create(dto, files) {
return this.productsService.create(dto, files);
}
findAll(filters) {
return this.productsService.findAdmin(filters);
findAll(filters, productType) {
return this.productsService.findAdmin(filters, productType);
}
checkSlug(query) {
return this.productsService.checkSlugAvailability(query.slug, query.excludeId);
@@ -83,8 +83,9 @@ __decorate([
(0, common_1.Get)(),
(0, swagger_1.ApiOperation)({ summary: 'List all products for admin panel, including drafts' }),
__param(0, (0, common_1.Query)()),
__param(1, (0, common_1.Headers)('x-product-type')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [filter_products_dto_1.FilterProductsDto]),
__metadata("design:paramtypes", [filter_products_dto_1.FilterProductsDto, String]),
__metadata("design:returntype", void 0)
], AdminProductsController.prototype, "findAll", null);
__decorate([
@@ -159,6 +160,12 @@ exports.AdminProductsController = AdminProductsController = __decorate([
(0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, roles_guard_1.RolesGuard, permissions_guard_1.PermissionsGuard),
(0, roles_decorator_1.Roles)(user_role_enum_1.UserRole.ADMIN),
(0, permissions_decorator_1.Permissions)('products.manage'),
(0, swagger_1.ApiHeader)({
name: 'x-product-type',
required: false,
description: 'Optional product type filter header. Falls back to query param `type` if omitted.',
enum: ['Industrial', 'Automotive'],
}),
(0, common_1.Controller)('admin/products'),
__metadata("design:paramtypes", [products_service_1.ProductsService])
], AdminProductsController);

View File

@@ -1 +1 @@
{"version":3,"file":"admin-products.controller.js","sourceRoot":"","sources":["../../../src/modules/catalog/admin-products.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAYwB;AACxB,+DAAiE;AACjE,6CAMyB;AACzB,yFAA4E;AAC5E,6EAAgE;AAChE,6EAAyE;AACzE,iEAA6D;AAC7D,kEAA6D;AAC7D,kEAAyD;AACzD,yEAAmE;AACnE,iEAA4D;AAC5D,iFAA2E;AAC3E,mEAA8D;AAC9D,mFAA6E;AAC7E,iEAA4D;AAC5D,yDAAqD;AAQ9C,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAA7B,YAA6B,eAAgC;QAAhC,oBAAe,GAAf,eAAe,CAAiB;IAAG,CAAC;IAajE,MAAM,CACI,GAAqB,EAE7B,KAIC;QAED,OAAO,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC;IAID,OAAO,CAAU,OAA0B;QACzC,OAAO,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IAID,SAAS,CAAU,KAA0B;QAC3C,OAAO,IAAI,CAAC,eAAe,CAAC,qBAAqB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACjF,CAAC;IAID,WAAW,CAAU,OAAgC;QACnD,OAAO,IAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACxD,CAAC;IAID,YAAY,CACS,QAAgB,EAC3B,GAA6B;QAErC,OAAO,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC1D,CAAC;IAID,YAAY,CAAoB,QAAgB;QAC9C,OAAO,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC;IAID,OAAO,CAAc,EAAU;QAC7B,OAAO,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAC/C,CAAC;IAaD,MAAM,CACS,EAAU,EACf,GAAqB,EAE7B,KAIC;QAED,OAAO,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IACrD,CAAC;IAID,MAAM,CAAc,EAAU;QAC5B,OAAO,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;CACF,CAAA;AA9FY,0DAAuB;AAclC;IAXC,IAAA,aAAI,GAAE;IACN,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,kCAAkC,EAAE,CAAC;IAC7D,IAAA,qBAAW,EAAC,qBAAqB,CAAC;IAClC,IAAA,iBAAO,EAAC,EAAE,IAAI,EAAE,qCAAgB,EAAE,CAAC;IACnC,IAAA,wBAAe,EACd,IAAA,wCAAqB,EAAC;QACpB,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE;QAClC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE;QAChC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,EAAE;KACjC,CAAC,CACH;IAEE,WAAA,IAAA,aAAI,GAAE,CAAA;IACN,WAAA,IAAA,sBAAa,GAAE,CAAA;;qCADH,qCAAgB;;qDAS9B;AAID;IAFC,IAAA,YAAG,GAAE;IACL,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,qDAAqD,EAAE,CAAC;IACxE,WAAA,IAAA,cAAK,GAAE,CAAA;;qCAAU,uCAAiB;;sDAE1C;AAID;IAFC,IAAA,YAAG,EAAC,YAAY,CAAC;IACjB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,iEAAiE,EAAE,CAAC;IAClF,WAAA,IAAA,cAAK,GAAE,CAAA;;qCAAQ,4CAAmB;;wDAE5C;AAID;IAFC,IAAA,YAAG,EAAC,cAAc,CAAC;IACnB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,qCAAqC,EAAE,CAAC;IACpD,WAAA,IAAA,cAAK,GAAE,CAAA;;qCAAU,oDAAuB;;0DAEpD;AAID;IAFC,IAAA,cAAK,EAAC,mBAAmB,CAAC;IAC1B,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,iCAAiC,EAAE,CAAC;IAE1D,WAAA,IAAA,cAAK,EAAC,UAAU,CAAC,CAAA;IACjB,WAAA,IAAA,aAAI,GAAE,CAAA;;6CAAM,sDAAwB;;2DAGtC;AAID;IAFC,IAAA,eAAM,EAAC,mBAAmB,CAAC;IAC3B,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC;IACvC,WAAA,IAAA,cAAK,EAAC,UAAU,CAAC,CAAA;;;;2DAE9B;AAID;IAFC,IAAA,YAAG,EAAC,KAAK,CAAC;IACV,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,iCAAiC,EAAE,CAAC;IACpD,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;sDAEnB;AAaD;IAXC,IAAA,cAAK,EAAC,KAAK,CAAC;IACZ,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,kCAAkC,EAAE,CAAC;IAC7D,IAAA,qBAAW,EAAC,qBAAqB,CAAC;IAClC,IAAA,iBAAO,EAAC,EAAE,IAAI,EAAE,qCAAgB,EAAE,CAAC;IACnC,IAAA,wBAAe,EACd,IAAA,wCAAqB,EAAC;QACpB,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE;QAClC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE;QAChC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,EAAE;KACjC,CAAC,CACH;IAEE,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IACX,WAAA,IAAA,aAAI,GAAE,CAAA;IACN,WAAA,IAAA,sBAAa,GAAE,CAAA;;6CADH,qCAAgB;;qDAS9B;AAID;IAFC,IAAA,eAAM,EAAC,KAAK,CAAC;IACb,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,iCAAiC,EAAE,CAAC;IACrD,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;qDAElB;kCA7FU,uBAAuB;IANnC,IAAA,iBAAO,EAAC,gBAAgB,CAAC;IACzB,IAAA,uBAAa,GAAE;IACf,IAAA,kBAAS,EAAC,6BAAY,EAAE,wBAAU,EAAE,oCAAgB,CAAC;IACrD,IAAA,uBAAK,EAAC,yBAAQ,CAAC,KAAK,CAAC;IACrB,IAAA,mCAAW,EAAC,iBAAiB,CAAC;IAC9B,IAAA,mBAAU,EAAC,gBAAgB,CAAC;qCAEmB,kCAAe;GADlD,uBAAuB,CA8FnC"}
{"version":3,"file":"admin-products.controller.js","sourceRoot":"","sources":["../../../src/modules/catalog/admin-products.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAawB;AACxB,+DAAiE;AACjE,6CAOyB;AACzB,yFAA4E;AAC5E,6EAAgE;AAChE,6EAAyE;AACzE,iEAA6D;AAC7D,kEAA6D;AAC7D,kEAAyD;AACzD,yEAAmE;AACnE,iEAA4D;AAC5D,iFAA2E;AAC3E,mEAA8D;AAC9D,mFAA6E;AAC7E,iEAA4D;AAC5D,yDAAqD;AAc9C,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAA7B,YAA6B,eAAgC;QAAhC,oBAAe,GAAf,eAAe,CAAiB;IAAG,CAAC;IAajE,MAAM,CACI,GAAqB,EAE7B,KAIC;QAED,OAAO,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC;IAID,OAAO,CACI,OAA0B,EACR,WAAoB;QAE/C,OAAO,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC9D,CAAC;IAID,SAAS,CAAU,KAA0B;QAC3C,OAAO,IAAI,CAAC,eAAe,CAAC,qBAAqB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACjF,CAAC;IAID,WAAW,CAAU,OAAgC;QACnD,OAAO,IAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACxD,CAAC;IAID,YAAY,CACS,QAAgB,EAC3B,GAA6B;QAErC,OAAO,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC1D,CAAC;IAID,YAAY,CAAoB,QAAgB;QAC9C,OAAO,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC;IAID,OAAO,CAAc,EAAU;QAC7B,OAAO,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAC/C,CAAC;IAaD,MAAM,CACS,EAAU,EACf,GAAqB,EAE7B,KAIC;QAED,OAAO,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IACrD,CAAC;IAID,MAAM,CAAc,EAAU;QAC5B,OAAO,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;CACF,CAAA;AAjGY,0DAAuB;AAclC;IAXC,IAAA,aAAI,GAAE;IACN,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,kCAAkC,EAAE,CAAC;IAC7D,IAAA,qBAAW,EAAC,qBAAqB,CAAC;IAClC,IAAA,iBAAO,EAAC,EAAE,IAAI,EAAE,qCAAgB,EAAE,CAAC;IACnC,IAAA,wBAAe,EACd,IAAA,wCAAqB,EAAC;QACpB,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE;QAClC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE;QAChC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,EAAE;KACjC,CAAC,CACH;IAEE,WAAA,IAAA,aAAI,GAAE,CAAA;IACN,WAAA,IAAA,sBAAa,GAAE,CAAA;;qCADH,qCAAgB;;qDAS9B;AAID;IAFC,IAAA,YAAG,GAAE;IACL,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,qDAAqD,EAAE,CAAC;IAE9E,WAAA,IAAA,cAAK,GAAE,CAAA;IACP,WAAA,IAAA,gBAAO,EAAC,gBAAgB,CAAC,CAAA;;qCADR,uCAAiB;;sDAIpC;AAID;IAFC,IAAA,YAAG,EAAC,YAAY,CAAC;IACjB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,iEAAiE,EAAE,CAAC;IAClF,WAAA,IAAA,cAAK,GAAE,CAAA;;qCAAQ,4CAAmB;;wDAE5C;AAID;IAFC,IAAA,YAAG,EAAC,cAAc,CAAC;IACnB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,qCAAqC,EAAE,CAAC;IACpD,WAAA,IAAA,cAAK,GAAE,CAAA;;qCAAU,oDAAuB;;0DAEpD;AAID;IAFC,IAAA,cAAK,EAAC,mBAAmB,CAAC;IAC1B,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,iCAAiC,EAAE,CAAC;IAE1D,WAAA,IAAA,cAAK,EAAC,UAAU,CAAC,CAAA;IACjB,WAAA,IAAA,aAAI,GAAE,CAAA;;6CAAM,sDAAwB;;2DAGtC;AAID;IAFC,IAAA,eAAM,EAAC,mBAAmB,CAAC;IAC3B,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC;IACvC,WAAA,IAAA,cAAK,EAAC,UAAU,CAAC,CAAA;;;;2DAE9B;AAID;IAFC,IAAA,YAAG,EAAC,KAAK,CAAC;IACV,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,iCAAiC,EAAE,CAAC;IACpD,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;sDAEnB;AAaD;IAXC,IAAA,cAAK,EAAC,KAAK,CAAC;IACZ,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,kCAAkC,EAAE,CAAC;IAC7D,IAAA,qBAAW,EAAC,qBAAqB,CAAC;IAClC,IAAA,iBAAO,EAAC,EAAE,IAAI,EAAE,qCAAgB,EAAE,CAAC;IACnC,IAAA,wBAAe,EACd,IAAA,wCAAqB,EAAC;QACpB,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE;QAClC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE;QAChC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,EAAE;KACjC,CAAC,CACH;IAEE,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IACX,WAAA,IAAA,aAAI,GAAE,CAAA;IACN,WAAA,IAAA,sBAAa,GAAE,CAAA;;6CADH,qCAAgB;;qDAS9B;AAID;IAFC,IAAA,eAAM,EAAC,KAAK,CAAC;IACb,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,iCAAiC,EAAE,CAAC;IACrD,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;qDAElB;kCAhGU,uBAAuB;IAZnC,IAAA,iBAAO,EAAC,gBAAgB,CAAC;IACzB,IAAA,uBAAa,GAAE;IACf,IAAA,kBAAS,EAAC,6BAAY,EAAE,wBAAU,EAAE,oCAAgB,CAAC;IACrD,IAAA,uBAAK,EAAC,yBAAQ,CAAC,KAAK,CAAC;IACrB,IAAA,mCAAW,EAAC,iBAAiB,CAAC;IAC9B,IAAA,mBAAS,EAAC;QACT,IAAI,EAAE,gBAAgB;QACtB,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,mFAAmF;QAChG,IAAI,EAAE,CAAC,YAAY,EAAE,YAAY,CAAC;KACnC,CAAC;IACD,IAAA,mBAAU,EAAC,gBAAgB,CAAC;qCAEmB,kCAAe;GADlD,uBAAuB,CAiGnC"}

View File

@@ -1,5 +1,6 @@
import { AttributeDataType } from '../enums/attribute-data-type.enum';
export declare class ProductAttributeInputDto {
displayOrder?: number;
attributeId?: string;
name?: string;
slug?: string;

View File

@@ -16,6 +16,7 @@ const class_validator_1 = require("class-validator");
const json_transform_util_1 = require("../../../common/utils/json-transform.util");
const attribute_data_type_enum_1 = require("../enums/attribute-data-type.enum");
class ProductAttributeInputDto {
displayOrder;
attributeId;
name;
slug;
@@ -35,6 +36,14 @@ class ProductAttributeInputDto {
overrideUnit;
}
exports.ProductAttributeInputDto = ProductAttributeInputDto;
__decorate([
(0, swagger_1.ApiPropertyOptional)({ example: 0 }),
(0, class_validator_1.IsOptional)(),
(0, class_transformer_1.Type)(() => Number),
(0, class_validator_1.IsInt)(),
(0, class_validator_1.Min)(0),
__metadata("design:type", Number)
], ProductAttributeInputDto.prototype, "displayOrder", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)(),
(0, class_validator_1.IsOptional)(),

View File

@@ -1 +1 @@
{"version":3,"file":"product-attribute-input.dto.js","sourceRoot":"","sources":["../../../../src/modules/catalog/dto/product-attribute-input.dto.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"product-attribute-input.dto.js","sourceRoot":"","sources":["../../../../src/modules/catalog/dto/product-attribute-input.dto.ts"],"names":[],"mappings":"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"}

View File

@@ -4,6 +4,7 @@ export declare class ProductAttributeValue {
id: string;
product: Product;
attribute: AttributeDefinition;
displayOrder: number;
valueText?: string | null;
valueNumber?: number | null;
valueBoolean?: boolean | null;

View File

@@ -17,6 +17,7 @@ let ProductAttributeValue = class ProductAttributeValue {
id;
product;
attribute;
displayOrder;
valueText;
valueNumber;
valueBoolean;
@@ -41,6 +42,10 @@ __decorate([
}),
__metadata("design:type", attribute_definition_entity_1.AttributeDefinition)
], ProductAttributeValue.prototype, "attribute", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'display_order', type: 'int', default: 0 }),
__metadata("design:type", Number)
], ProductAttributeValue.prototype, "displayOrder", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'value_text', type: 'varchar', length: 255, nullable: true }),
__metadata("design:type", Object)

View File

@@ -1 +1 @@
{"version":3,"file":"product-attribute-value.entity.js","sourceRoot":"","sources":["../../../../src/modules/catalog/entities/product-attribute-value.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAKiB;AACjB,+EAAoE;AACpE,qDAA2C;AAGpC,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAEhC,EAAE,CAAS;IAKX,OAAO,CAAU;IAMjB,SAAS,CAAsB;IAG/B,SAAS,CAAiB;IAc1B,WAAW,CAAiB;IAG5B,YAAY,CAAkB;IAG9B,SAAS,CAA6C;IAGtD,YAAY,CAAiB;CAC9B,CAAA;AAxCY,sDAAqB;AAEhC;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;iDACpB;AAKX;IAHC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,wBAAO,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,eAAe,EAAE;QAC9D,QAAQ,EAAE,SAAS;KACpB,CAAC;8BACO,wBAAO;sDAAC;AAMjB;IAJC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,iDAAmB,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE;QACrE,KAAK,EAAE,IAAI;QACX,QAAQ,EAAE,SAAS;KACpB,CAAC;8BACS,iDAAmB;wDAAC;AAG/B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wDACnD;AAc1B;IAZC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,cAAc;QACpB,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,EAAE;QACb,KAAK,EAAE,CAAC;QACR,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE;YACX,EAAE,EAAE,CAAC,KAAqB,EAAE,EAAE,CAAC,KAAK;YACpC,IAAI,EAAE,CAAC,KAAqB,EAAE,EAAE,CAC9B,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC/D;KACF,CAAC;;0DAC0B;AAG5B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2DACrC;AAG9B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wDACR;AAGtD;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2DAClD;gCAvClB,qBAAqB;IADjC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC;GAChC,qBAAqB,CAwCjC"}
{"version":3,"file":"product-attribute-value.entity.js","sourceRoot":"","sources":["../../../../src/modules/catalog/entities/product-attribute-value.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAKiB;AACjB,+EAAoE;AACpE,qDAA2C;AAGpC,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAEhC,EAAE,CAAS;IAKX,OAAO,CAAU;IAMjB,SAAS,CAAsB;IAG/B,YAAY,CAAS;IAGrB,SAAS,CAAiB;IAc1B,WAAW,CAAiB;IAG5B,YAAY,CAAkB;IAG9B,SAAS,CAA6C;IAGtD,YAAY,CAAiB;CAC9B,CAAA;AA3CY,sDAAqB;AAEhC;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;iDACpB;AAKX;IAHC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,wBAAO,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,eAAe,EAAE;QAC9D,QAAQ,EAAE,SAAS;KACpB,CAAC;8BACO,wBAAO;sDAAC;AAMjB;IAJC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,iDAAmB,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE;QACrE,KAAK,EAAE,IAAI;QACX,QAAQ,EAAE,SAAS;KACpB,CAAC;8BACS,iDAAmB;wDAAC;AAG/B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;2DACtC;AAGrB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wDACnD;AAc1B;IAZC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,cAAc;QACpB,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,EAAE;QACb,KAAK,EAAE,CAAC;QACR,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE;YACX,EAAE,EAAE,CAAC,KAAqB,EAAE,EAAE,CAAC,KAAK;YACpC,IAAI,EAAE,CAAC,KAAqB,EAAE,EAAE,CAC9B,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC/D;KACF,CAAC;;0DAC0B;AAG5B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2DACrC;AAG9B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wDACR;AAGtD;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2DAClD;gCA1ClB,qBAAqB;IADjC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC;GAChC,qBAAqB,CA2CjC"}

View File

@@ -4,36 +4,17 @@ import { ProductsService } from './products.service';
export declare class ProductsController {
private readonly productsService;
constructor(productsService: ProductsService);
findAll(filters: FilterProductsDto): Promise<{
findAll(filters: FilterProductsDto, productType?: string): Promise<{
items: {
attributes: {
id: string;
attributeId: string;
name: string;
slug: string;
dataType: import("./enums/attribute-data-type.enum").AttributeDataType;
unit: string | null;
options: string[];
isFilterable: boolean;
isVisible: boolean;
valueText: string | null;
valueNumber: number | null;
valueBoolean: boolean | null;
valueJson: string[] | Record<string, unknown> | null;
}[];
meta: import("./entities/product-meta.entity").ProductMeta | {
shortDescription: string | null;
description: string | null;
seo: {
title: string;
description: string | null;
} | undefined;
share: {
title: string;
description: string | null;
imageUrl: string | null | undefined;
};
};
status?: import("./enums/product-status.enum").ProductStatus | undefined;
createdAt?: Date | undefined;
updatedAt?: Date | undefined;
id: string;
sku: string;
title: string;
slug: string;
technicalCode: string;
brand: string;
brandInfo: {
id: string;
name: string;
@@ -41,6 +22,15 @@ export declare class ProductsController {
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
} | null;
type: import("./enums/product-type.enum").ProductType;
featured: boolean;
basePriceUSD: number;
salePriceUSD: number | null;
stock: number;
averageRating: number;
reviewsCount: number;
mainImageUrl: string | null;
shortDescription: string | null;
primaryCategory: {
id: string;
name: string;
@@ -48,36 +38,19 @@ export declare class ProductsController {
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
} | null;
categories: {
attributes: {
id: string;
displayOrder: number;
attributeId: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
dataType: import("./enums/attribute-data-type.enum").AttributeDataType;
unit: string | null;
valueText: string | null;
valueNumber: number | null;
valueBoolean: boolean | null;
valueJson: string[] | Record<string, unknown> | null;
}[];
id: string;
sku: string;
title: string;
slug: string;
technicalCode: string;
brand: string;
brandEntity?: import("./entities/brand.entity").Brand | null;
basePriceUSD: number;
salePriceUSD?: number | null;
stock: number;
featured: boolean;
type: import("./enums/product-type.enum").ProductType;
status: import("./enums/product-status.enum").ProductStatus;
mainImageUrl?: string | null;
threeDModelUrl?: string | null;
imageGalleryUrls: string[];
tags: string[];
averageRating: number;
reviewsCount: number;
attributeValues: import("./entities/product-attribute-value.entity").ProductAttributeValue[];
reviews: import("./entities/product-review.entity").ProductReview[];
createdAt: Date;
updatedAt: Date;
}[];
meta: {
total: number;
@@ -85,10 +58,11 @@ export declare class ProductsController {
limit: number;
};
}>;
findBySlug(slug: string): Promise<{
findBySlug(slug: string, productType?: string): Promise<{
approvedReviews: import("./entities/product-review.entity").ProductReview[];
attributes: {
id: string;
displayOrder: number;
attributeId: string;
name: string;
slug: string;
@@ -165,10 +139,11 @@ export declare class ProductsController {
message: string;
reviewId: string;
}>;
findOne(id: string): Promise<{
findOne(id: string, productType?: string): Promise<{
approvedReviews: import("./entities/product-review.entity").ProductReview[];
attributes: {
id: string;
displayOrder: number;
attributeId: string;
name: string;
slug: string;

View File

@@ -23,11 +23,11 @@ let ProductsController = class ProductsController {
constructor(productsService) {
this.productsService = productsService;
}
findAll(filters) {
return this.productsService.findPublic(filters);
findAll(filters, productType) {
return this.productsService.findPublic(filters, productType);
}
findBySlug(slug) {
return this.productsService.findPublicOneBySlug(slug);
findBySlug(slug, productType) {
return this.productsService.findPublicOneBySlug(slug, productType);
}
findApprovedReviews(id) {
return this.productsService.findApprovedReviews(id);
@@ -35,8 +35,8 @@ let ProductsController = class ProductsController {
createReview(id, dto) {
return this.productsService.createReview(id, dto);
}
findOne(id) {
return this.productsService.findPublicOne(id);
findOne(id, productType) {
return this.productsService.findPublicOne(id, productType);
}
};
exports.ProductsController = ProductsController;
@@ -44,16 +44,18 @@ __decorate([
(0, common_1.Get)(),
(0, swagger_1.ApiOperation)({ summary: 'List published products for storefront' }),
__param(0, (0, common_1.Query)()),
__param(1, (0, common_1.Headers)('x-product-type')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [filter_products_dto_1.FilterProductsDto]),
__metadata("design:paramtypes", [filter_products_dto_1.FilterProductsDto, String]),
__metadata("design:returntype", void 0)
], ProductsController.prototype, "findAll", null);
__decorate([
(0, common_1.Get)('slug/:slug'),
(0, swagger_1.ApiOperation)({ summary: 'Get one published product by slug' }),
__param(0, (0, common_1.Param)('slug')),
__param(1, (0, common_1.Headers)('x-product-type')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", void 0)
], ProductsController.prototype, "findBySlug", null);
__decorate([
@@ -78,12 +80,19 @@ __decorate([
(0, common_1.Get)(':id'),
(0, swagger_1.ApiOperation)({ summary: 'Get one published product with approved reviews summary' }),
__param(0, (0, common_1.Param)('id')),
__param(1, (0, common_1.Headers)('x-product-type')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", void 0)
], ProductsController.prototype, "findOne", null);
exports.ProductsController = ProductsController = __decorate([
(0, swagger_1.ApiTags)('Products'),
(0, swagger_1.ApiHeader)({
name: 'x-product-type',
required: false,
description: 'Optional product type filter header. Falls back to query param `type` if omitted.',
enum: ['Industrial', 'Automotive'],
}),
(0, common_1.Controller)('products'),
__metadata("design:paramtypes", [products_service_1.ProductsService])
], ProductsController);

View File

@@ -1 +1 @@
{"version":3,"file":"products.controller.js","sourceRoot":"","sources":["../../../src/modules/catalog/products.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA2E;AAC3E,6CAAiE;AACjE,+EAAyE;AACzE,mEAA8D;AAC9D,yDAAqD;AAI9C,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IACA;IAA7B,YAA6B,eAAgC;QAAhC,oBAAe,GAAf,eAAe,CAAiB;IAAG,CAAC;IAIjE,OAAO,CAAU,OAA0B;QACzC,OAAO,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAClD,CAAC;IAID,UAAU,CAAgB,IAAY;QACpC,OAAO,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACxD,CAAC;IAID,mBAAmB,CAAc,EAAU;QACzC,OAAO,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC;IAKD,YAAY,CAAc,EAAU,EAAU,GAA2B;QACvE,OAAO,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACpD,CAAC;IAID,OAAO,CAAc,EAAU;QAC7B,OAAO,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAChD,CAAC;CACF,CAAA;AAjCY,gDAAkB;AAK7B;IAFC,IAAA,YAAG,GAAE;IACL,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,wCAAwC,EAAE,CAAC;IAC3D,WAAA,IAAA,cAAK,GAAE,CAAA;;qCAAU,uCAAiB;;iDAE1C;AAID;IAFC,IAAA,YAAG,EAAC,YAAY,CAAC;IACjB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,mCAAmC,EAAE,CAAC;IACnD,WAAA,IAAA,cAAK,EAAC,MAAM,CAAC,CAAA;;;;oDAExB;AAID;IAFC,IAAA,YAAG,EAAC,aAAa,CAAC;IAClB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,qCAAqC,EAAE,CAAC;IAC5C,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;6DAE/B;AAKD;IAHC,IAAA,aAAI,EAAC,aAAa,CAAC;IACnB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,6BAA6B,EAAE,CAAC;IACxD,IAAA,iBAAO,EAAC,EAAE,IAAI,EAAE,kDAAsB,EAAE,CAAC;IAC5B,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IAAc,WAAA,IAAA,aAAI,GAAE,CAAA;;6CAAM,kDAAsB;;sDAExE;AAID;IAFC,IAAA,YAAG,EAAC,KAAK,CAAC;IACV,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,yDAAyD,EAAE,CAAC;IAC5E,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;iDAEnB;6BAhCU,kBAAkB;IAF9B,IAAA,iBAAO,EAAC,UAAU,CAAC;IACnB,IAAA,mBAAU,EAAC,UAAU,CAAC;qCAEyB,kCAAe;GADlD,kBAAkB,CAiC9B"}
{"version":3,"file":"products.controller.js","sourceRoot":"","sources":["../../../src/modules/catalog/products.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAoF;AACpF,6CAA4E;AAC5E,+EAAyE;AACzE,mEAA8D;AAC9D,yDAAqD;AAU9C,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IACA;IAA7B,YAA6B,eAAgC;QAAhC,oBAAe,GAAf,eAAe,CAAiB;IAAG,CAAC;IAIjE,OAAO,CACI,OAA0B,EACR,WAAoB;QAE/C,OAAO,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC/D,CAAC;IAID,UAAU,CACO,IAAY,EACA,WAAoB;QAE/C,OAAO,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACrE,CAAC;IAID,mBAAmB,CAAc,EAAU;QACzC,OAAO,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC;IAKD,YAAY,CAAc,EAAU,EAAU,GAA2B;QACvE,OAAO,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACpD,CAAC;IAID,OAAO,CAAc,EAAU,EAA6B,WAAoB;QAC9E,OAAO,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;IAC7D,CAAC;CACF,CAAA;AAvCY,gDAAkB;AAK7B;IAFC,IAAA,YAAG,GAAE;IACL,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,wCAAwC,EAAE,CAAC;IAEjE,WAAA,IAAA,cAAK,GAAE,CAAA;IACP,WAAA,IAAA,gBAAO,EAAC,gBAAgB,CAAC,CAAA;;qCADR,uCAAiB;;iDAIpC;AAID;IAFC,IAAA,YAAG,EAAC,YAAY,CAAC;IACjB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,mCAAmC,EAAE,CAAC;IAE5D,WAAA,IAAA,cAAK,EAAC,MAAM,CAAC,CAAA;IACb,WAAA,IAAA,gBAAO,EAAC,gBAAgB,CAAC,CAAA;;;;oDAG3B;AAID;IAFC,IAAA,YAAG,EAAC,aAAa,CAAC;IAClB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,qCAAqC,EAAE,CAAC;IAC5C,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;6DAE/B;AAKD;IAHC,IAAA,aAAI,EAAC,aAAa,CAAC;IACnB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,6BAA6B,EAAE,CAAC;IACxD,IAAA,iBAAO,EAAC,EAAE,IAAI,EAAE,kDAAsB,EAAE,CAAC;IAC5B,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IAAc,WAAA,IAAA,aAAI,GAAE,CAAA;;6CAAM,kDAAsB;;sDAExE;AAID;IAFC,IAAA,YAAG,EAAC,KAAK,CAAC;IACV,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,yDAAyD,EAAE,CAAC;IAC5E,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IAAc,WAAA,IAAA,gBAAO,EAAC,gBAAgB,CAAC,CAAA;;;;iDAE1D;6BAtCU,kBAAkB;IAR9B,IAAA,iBAAO,EAAC,UAAU,CAAC;IACnB,IAAA,mBAAS,EAAC;QACT,IAAI,EAAE,gBAAgB;QACtB,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,mFAAmF;QAChG,IAAI,EAAE,CAAC,YAAY,EAAE,YAAY,CAAC;KACnC,CAAC;IACD,IAAA,mBAAU,EAAC,UAAU,CAAC;qCAEyB,kCAAe;GADlD,kBAAkB,CAuC9B"}

View File

@@ -17,6 +17,7 @@ import { Product } from './entities/product.entity';
import { ProductReview } from './entities/product-review.entity';
import { AttributeDataType } from './enums/attribute-data-type.enum';
import { ProductStatus } from './enums/product-status.enum';
import { ProductType } from './enums/product-type.enum';
export declare class ProductsService {
private readonly productsRepository;
private readonly productMetaRepository;
@@ -34,6 +35,7 @@ export declare class ProductsService {
}): Promise<{
attributes: {
id: string;
displayOrder: number;
attributeId: string;
name: string;
slug: string;
@@ -65,21 +67,21 @@ export declare class ProductsService {
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
} | null;
primaryCategory: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
} | null;
categories: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
}[];
id: string;
sku: string;
@@ -92,7 +94,7 @@ export declare class ProductsService {
salePriceUSD?: number | null;
stock: number;
featured: boolean;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
status: ProductStatus;
mainImageUrl?: string | null;
threeDModelUrl?: string | null;
@@ -105,80 +107,53 @@ export declare class ProductsService {
createdAt: Date;
updatedAt: Date;
}>;
findPublic(filters: FilterProductsDto): Promise<{
findPublic(filters: FilterProductsDto, headerProductType?: string): Promise<{
items: {
attributes: {
id: string;
attributeId: string;
name: string;
slug: string;
dataType: AttributeDataType;
unit: string | null;
options: string[];
isFilterable: boolean;
isVisible: boolean;
valueText: string | null;
valueNumber: number | null;
valueBoolean: boolean | null;
valueJson: string[] | Record<string, unknown> | null;
}[];
meta: ProductMeta | {
shortDescription: string | null;
description: string | null;
seo: {
title: string;
description: string | null;
} | undefined;
share: {
title: string;
description: string | null;
imageUrl: string | null | undefined;
};
};
brandInfo: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
} | null;
primaryCategory: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
} | null;
categories: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
}[];
status?: ProductStatus | undefined;
createdAt?: Date | undefined;
updatedAt?: Date | undefined;
id: string;
sku: string;
title: string;
slug: string;
technicalCode: string;
brand: string;
brandEntity?: Brand | null;
basePriceUSD: number;
salePriceUSD?: number | null;
stock: number;
brandInfo: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: ProductType;
} | null;
type: ProductType;
featured: boolean;
type: import("./enums/product-type.enum").ProductType;
status: ProductStatus;
mainImageUrl?: string | null;
threeDModelUrl?: string | null;
imageGalleryUrls: string[];
tags: string[];
basePriceUSD: number;
salePriceUSD: number | null;
stock: number;
averageRating: number;
reviewsCount: number;
attributeValues: ProductAttributeValue[];
reviews: ProductReview[];
createdAt: Date;
updatedAt: Date;
mainImageUrl: string | null;
shortDescription: string | null;
primaryCategory: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: ProductType;
} | null;
attributes: {
id: string;
displayOrder: number;
attributeId: string;
name: string;
slug: string;
dataType: AttributeDataType;
unit: string | null;
valueText: string | null;
valueNumber: number | null;
valueBoolean: boolean | null;
valueJson: string[] | Record<string, unknown> | null;
}[];
}[];
meta: {
total: number;
@@ -186,80 +161,53 @@ export declare class ProductsService {
limit: number;
};
}>;
findAdmin(filters: FilterProductsDto): Promise<{
findAdmin(filters: FilterProductsDto, headerProductType?: string): Promise<{
items: {
attributes: {
id: string;
attributeId: string;
name: string;
slug: string;
dataType: AttributeDataType;
unit: string | null;
options: string[];
isFilterable: boolean;
isVisible: boolean;
valueText: string | null;
valueNumber: number | null;
valueBoolean: boolean | null;
valueJson: string[] | Record<string, unknown> | null;
}[];
meta: ProductMeta | {
shortDescription: string | null;
description: string | null;
seo: {
title: string;
description: string | null;
} | undefined;
share: {
title: string;
description: string | null;
imageUrl: string | null | undefined;
};
};
brandInfo: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
} | null;
primaryCategory: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
} | null;
categories: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
}[];
status?: ProductStatus | undefined;
createdAt?: Date | undefined;
updatedAt?: Date | undefined;
id: string;
sku: string;
title: string;
slug: string;
technicalCode: string;
brand: string;
brandEntity?: Brand | null;
basePriceUSD: number;
salePriceUSD?: number | null;
stock: number;
brandInfo: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: ProductType;
} | null;
type: ProductType;
featured: boolean;
type: import("./enums/product-type.enum").ProductType;
status: ProductStatus;
mainImageUrl?: string | null;
threeDModelUrl?: string | null;
imageGalleryUrls: string[];
tags: string[];
basePriceUSD: number;
salePriceUSD: number | null;
stock: number;
averageRating: number;
reviewsCount: number;
attributeValues: ProductAttributeValue[];
reviews: ProductReview[];
createdAt: Date;
updatedAt: Date;
mainImageUrl: string | null;
shortDescription: string | null;
primaryCategory: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: ProductType;
} | null;
attributes: {
id: string;
displayOrder: number;
attributeId: string;
name: string;
slug: string;
dataType: AttributeDataType;
unit: string | null;
valueText: string | null;
valueNumber: number | null;
valueBoolean: boolean | null;
valueJson: string[] | Record<string, unknown> | null;
}[];
}[];
meta: {
total: number;
@@ -268,10 +216,11 @@ export declare class ProductsService {
};
}>;
checkSlugAvailability(slug: string, excludeId?: string): Promise<boolean>;
findPublicOne(id: string): Promise<{
findPublicOne(id: string, headerProductType?: string): Promise<{
approvedReviews: ProductReview[];
attributes: {
id: string;
displayOrder: number;
attributeId: string;
name: string;
slug: string;
@@ -303,21 +252,21 @@ export declare class ProductsService {
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
} | null;
primaryCategory: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
} | null;
categories: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
}[];
id: string;
sku: string;
@@ -330,7 +279,7 @@ export declare class ProductsService {
salePriceUSD?: number | null;
stock: number;
featured: boolean;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
status: ProductStatus;
mainImageUrl?: string | null;
threeDModelUrl?: string | null;
@@ -343,10 +292,11 @@ export declare class ProductsService {
createdAt: Date;
updatedAt: Date;
}>;
findPublicOneBySlug(slug: string): Promise<{
findPublicOneBySlug(slug: string, headerProductType?: string): Promise<{
approvedReviews: ProductReview[];
attributes: {
id: string;
displayOrder: number;
attributeId: string;
name: string;
slug: string;
@@ -378,21 +328,21 @@ export declare class ProductsService {
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
} | null;
primaryCategory: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
} | null;
categories: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
}[];
id: string;
sku: string;
@@ -405,7 +355,7 @@ export declare class ProductsService {
salePriceUSD?: number | null;
stock: number;
featured: boolean;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
status: ProductStatus;
mainImageUrl?: string | null;
threeDModelUrl?: string | null;
@@ -421,6 +371,7 @@ export declare class ProductsService {
findAdminOne(id: string): Promise<{
attributes: {
id: string;
displayOrder: number;
attributeId: string;
name: string;
slug: string;
@@ -452,21 +403,21 @@ export declare class ProductsService {
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
} | null;
primaryCategory: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
} | null;
categories: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
}[];
id: string;
sku: string;
@@ -479,7 +430,7 @@ export declare class ProductsService {
salePriceUSD?: number | null;
stock: number;
featured: boolean;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
status: ProductStatus;
mainImageUrl?: string | null;
threeDModelUrl?: string | null;
@@ -516,6 +467,7 @@ export declare class ProductsService {
}): Promise<{
attributes: {
id: string;
displayOrder: number;
attributeId: string;
name: string;
slug: string;
@@ -547,21 +499,21 @@ export declare class ProductsService {
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
} | null;
primaryCategory: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
} | null;
categories: {
id: string;
name: string;
slug: string;
imageUrl: string | null | undefined;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
}[];
id: string;
sku: string;
@@ -574,7 +526,7 @@ export declare class ProductsService {
salePriceUSD?: number | null;
stock: number;
featured: boolean;
type: import("./enums/product-type.enum").ProductType;
type: ProductType;
status: ProductStatus;
mainImageUrl?: string | null;
threeDModelUrl?: string | null;
@@ -608,6 +560,11 @@ export declare class ProductsService {
private syncAttributeValues;
private resolveAttributeDefinition;
private serializeProduct;
private serializeProductListItem;
private sortAttributes;
private mergeProductTypeFilter;
private buildPublicProductWhere;
private normalizeProductType;
private deleteGallery;
private deleteModel;
private replaceFile;

View File

@@ -25,6 +25,7 @@ const product_meta_entity_1 = require("./entities/product-meta.entity");
const product_entity_1 = require("./entities/product.entity");
const product_review_entity_1 = require("./entities/product-review.entity");
const product_status_enum_1 = require("./enums/product-status.enum");
const product_type_enum_1 = require("./enums/product-type.enum");
let ProductsService = class ProductsService {
productsRepository;
productMetaRepository;
@@ -86,11 +87,11 @@ let ProductsService = class ProductsService {
product.attributeValues = await this.syncAttributeValues(product, dto.attributes ?? []);
return this.serializeProduct(await this.findOneById(product.id));
}
async findPublic(filters) {
return this.findAll(filters, false);
async findPublic(filters, headerProductType) {
return this.findAll(this.mergeProductTypeFilter(filters, headerProductType), false);
}
async findAdmin(filters) {
return this.findAll(filters, true);
async findAdmin(filters, headerProductType) {
return this.findAll(this.mergeProductTypeFilter(filters, headerProductType), true);
}
async checkSlugAvailability(slug, excludeId) {
const query = this.productsRepository
@@ -102,9 +103,9 @@ let ProductsService = class ProductsService {
const existing = await query.getOne();
return !existing;
}
async findPublicOne(id) {
async findPublicOne(id, headerProductType) {
const product = await this.productsRepository.findOne({
where: { id, status: product_status_enum_1.ProductStatus.PUBLISHED },
where: this.buildPublicProductWhere({ id }, headerProductType),
relations: {
primaryCategory: true,
categories: true,
@@ -122,9 +123,9 @@ let ProductsService = class ProductsService {
approvedReviews,
};
}
async findPublicOneBySlug(slug) {
async findPublicOneBySlug(slug, headerProductType) {
const product = await this.productsRepository.findOne({
where: { slug, status: product_status_enum_1.ProductStatus.PUBLISHED },
where: this.buildPublicProductWhere({ slug }, headerProductType),
relations: {
primaryCategory: true,
categories: true,
@@ -402,7 +403,7 @@ let ProductsService = class ProductsService {
this.applyAttributeFilters(query, filters.attributes);
const [items, total] = await query.getManyAndCount();
return {
items: items.map((item) => this.serializeProduct(item, false)),
items: items.map((item) => this.serializeProductListItem(item, includeUnpublished)),
meta: {
total,
page,
@@ -560,11 +561,12 @@ let ProductsService = class ProductsService {
product: { id: product.id },
});
const results = [];
for (const input of inputs) {
for (const [index, input] of inputs.entries()) {
const attribute = await this.resolveAttributeDefinition(input);
const value = this.productAttributeValuesRepository.create({
product,
attribute,
displayOrder: input.displayOrder ?? index,
valueText: input.valueText ?? null,
valueNumber: input.valueNumber ?? null,
valueBoolean: input.valueBoolean ?? null,
@@ -609,6 +611,7 @@ let ProductsService = class ProductsService {
}));
}
serializeProduct(product, includeMeta = true) {
const orderedAttributes = this.sortAttributes(product.attributeValues);
const meta = product.meta ?? null;
const shortDescription = meta?.shortDescription ?? null;
const share = {
@@ -659,8 +662,9 @@ let ProductsService = class ProductsService {
},
}
: {}),
attributes: (product.attributeValues ?? []).map((item) => ({
attributes: orderedAttributes.map((item) => ({
id: item.id,
displayOrder: item.displayOrder ?? 0,
attributeId: item.attribute?.id,
name: item.attribute?.name,
slug: item.attribute?.slug,
@@ -680,6 +684,110 @@ let ProductsService = class ProductsService {
})),
};
}
serializeProductListItem(product, includeAdminFields) {
const meta = product.meta ?? null;
const orderedAttributes = this.sortAttributes(product.attributeValues).filter((item) => item.attribute?.isVisible ?? true);
return {
id: product.id,
sku: product.sku,
title: product.title,
slug: product.slug,
technicalCode: product.technicalCode,
brand: product.brand,
brandInfo: product.brandEntity
? {
id: product.brandEntity.id,
name: product.brandEntity.name,
slug: product.brandEntity.slug,
imageUrl: product.brandEntity.imageUrl,
type: product.brandEntity.type,
}
: null,
type: product.type,
featured: product.featured,
basePriceUSD: product.basePriceUSD,
salePriceUSD: product.salePriceUSD ?? null,
stock: product.stock,
averageRating: product.averageRating,
reviewsCount: product.reviewsCount,
mainImageUrl: product.mainImageUrl ?? null,
shortDescription: meta?.shortDescription ?? null,
primaryCategory: product.primaryCategory
? {
id: product.primaryCategory.id,
name: product.primaryCategory.name,
slug: product.primaryCategory.slug,
imageUrl: product.primaryCategory.imageUrl,
type: product.primaryCategory.type,
}
: null,
attributes: orderedAttributes.slice(0, 2).map((item) => ({
id: item.id,
displayOrder: item.displayOrder ?? 0,
attributeId: item.attribute?.id,
name: item.attribute?.name,
slug: item.attribute?.slug,
dataType: item.attribute?.dataType,
unit: item.overrideUnit ?? item.attribute?.unit ?? null,
valueText: item.valueText ?? item.attribute?.defaultValueText ?? null,
valueNumber: item.valueNumber ??
(item.attribute?.defaultValueNumber !== null &&
item.attribute?.defaultValueNumber !== undefined
? Number(item.attribute.defaultValueNumber)
: null),
valueBoolean: item.valueBoolean ?? item.attribute?.defaultValueBoolean ?? null,
valueJson: item.valueJson ?? item.attribute?.defaultValueJson ?? null,
})),
...(includeAdminFields
? {
status: product.status,
createdAt: product.createdAt,
updatedAt: product.updatedAt,
}
: {}),
};
}
sortAttributes(attributes) {
return [...(attributes ?? [])].sort((left, right) => {
const orderDiff = (left.displayOrder ?? 0) - (right.displayOrder ?? 0);
if (orderDiff !== 0) {
return orderDiff;
}
return left.attribute?.name?.localeCompare(right.attribute?.name ?? '') ?? 0;
});
}
mergeProductTypeFilter(filters, headerProductType) {
if (filters.type) {
return filters;
}
const normalizedType = this.normalizeProductType(headerProductType);
if (!normalizedType) {
return filters;
}
return {
...filters,
type: normalizedType,
};
}
buildPublicProductWhere(where, headerProductType) {
const normalizedType = this.normalizeProductType(headerProductType);
return {
...where,
status: product_status_enum_1.ProductStatus.PUBLISHED,
...(normalizedType ? { type: normalizedType } : {}),
};
}
normalizeProductType(headerProductType) {
if (!headerProductType) {
return undefined;
}
const normalizedValue = headerProductType.trim().toLowerCase();
const matchedType = Object.values(product_type_enum_1.ProductType).find((type) => type.toLowerCase() === normalizedValue);
if (!matchedType) {
throw new common_1.BadRequestException(`Invalid x-product-type header. Allowed values: ${Object.values(product_type_enum_1.ProductType).join(', ')}`);
}
return matchedType;
}
async deleteGallery(imageUrls) {
await Promise.all((imageUrls ?? []).map((url) => this.storageService.deletePublicFileByUrl(url)));
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -58,12 +58,19 @@ Query params:
- `page`
- `limit`
Optional request header:
- `x-product-type`: `Industrial | Automotive`
Example:
```http
GET /api/products?search=skf&type=bearing&featured=true&page=1&limit=20
GET /api/products?search=skf&featured=true&page=1&limit=20
x-product-type: Industrial
```
List response items are intentionally lightweight and include only fields needed for product cards / list pages. The `attributes` array is limited to the first 2 visible attributes, sorted by `displayOrder`.
### `GET /api/products/:id`
Returns one published product with category and up to 10 approved reviews.
@@ -113,6 +120,10 @@ Query params:
- `page`
- `limit`
Optional request header:
- `x-product-type`: `Industrial | Automotive`
### `GET /api/admin/products/:id`
Returns one product for admin panel.
@@ -138,6 +149,7 @@ Fields:
- `status`
- `categoryId`
- `attributes` as JSON string
- each item in `attributes` can include `displayOrder`
- `tags` as JSON string array
- `existingMainImageUrl`
- `existingGalleryUrls` as JSON string array

View File

@@ -3,6 +3,7 @@ import {
Controller,
Delete,
Get,
Headers,
Param,
Patch,
Post,
@@ -16,6 +17,7 @@ import {
ApiBearerAuth,
ApiBody,
ApiConsumes,
ApiHeader,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
@@ -38,6 +40,12 @@ import { ProductsService } from './products.service';
@UseGuards(JwtAuthGuard, RolesGuard, PermissionsGuard)
@Roles(UserRole.ADMIN)
@Permissions('products.manage')
@ApiHeader({
name: 'x-product-type',
required: false,
description: 'Optional product type filter header. Falls back to query param `type` if omitted.',
enum: ['Industrial', 'Automotive'],
})
@Controller('admin/products')
export class AdminProductsController {
constructor(private readonly productsService: ProductsService) {}
@@ -67,8 +75,11 @@ export class AdminProductsController {
@Get()
@ApiOperation({ summary: 'List all products for admin panel, including drafts' })
findAll(@Query() filters: FilterProductsDto) {
return this.productsService.findAdmin(filters);
findAll(
@Query() filters: FilterProductsDto,
@Headers('x-product-type') productType?: string,
) {
return this.productsService.findAdmin(filters, productType);
}
@Get('check-slug')

View File

@@ -4,17 +4,26 @@ import {
IsArray,
IsBoolean,
IsEnum,
IsInt,
IsNumber,
IsObject,
IsOptional,
IsString,
IsUUID,
MaxLength,
Min,
} from 'class-validator';
import { parseJsonValue } from '../../../common/utils/json-transform.util';
import { AttributeDataType } from '../enums/attribute-data-type.enum';
export class ProductAttributeInputDto {
@ApiPropertyOptional({ example: 0 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(0)
displayOrder?: number;
@ApiPropertyOptional()
@IsOptional()
@IsUUID()

View File

@@ -23,6 +23,9 @@ export class ProductAttributeValue {
})
attribute: AttributeDefinition;
@Column({ name: 'display_order', type: 'int', default: 0 })
displayOrder: number;
@Column({ name: 'value_text', type: 'varchar', length: 255, nullable: true })
valueText?: string | null;

View File

@@ -1,24 +1,36 @@
import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common';
import { ApiBody, ApiOperation, ApiTags } from '@nestjs/swagger';
import { Body, Controller, Get, Headers, Param, Post, Query } from '@nestjs/common';
import { ApiBody, ApiHeader, ApiOperation, ApiTags } from '@nestjs/swagger';
import { CreateProductReviewDto } from './dto/create-product-review.dto';
import { FilterProductsDto } from './dto/filter-products.dto';
import { ProductsService } from './products.service';
@ApiTags('Products')
@ApiHeader({
name: 'x-product-type',
required: false,
description: 'Optional product type filter header. Falls back to query param `type` if omitted.',
enum: ['Industrial', 'Automotive'],
})
@Controller('products')
export class ProductsController {
constructor(private readonly productsService: ProductsService) {}
@Get()
@ApiOperation({ summary: 'List published products for storefront' })
findAll(@Query() filters: FilterProductsDto) {
return this.productsService.findPublic(filters);
findAll(
@Query() filters: FilterProductsDto,
@Headers('x-product-type') productType?: string,
) {
return this.productsService.findPublic(filters, productType);
}
@Get('slug/:slug')
@ApiOperation({ summary: 'Get one published product by slug' })
findBySlug(@Param('slug') slug: string) {
return this.productsService.findPublicOneBySlug(slug);
findBySlug(
@Param('slug') slug: string,
@Headers('x-product-type') productType?: string,
) {
return this.productsService.findPublicOneBySlug(slug, productType);
}
@Get(':id/reviews')
@@ -36,7 +48,7 @@ export class ProductsController {
@Get(':id')
@ApiOperation({ summary: 'Get one published product with approved reviews summary' })
findOne(@Param('id') id: string) {
return this.productsService.findPublicOne(id);
findOne(@Param('id') id: string, @Headers('x-product-type') productType?: string) {
return this.productsService.findPublicOne(id, productType);
}
}

View File

@@ -25,6 +25,7 @@ import { Product } from './entities/product.entity';
import { ProductReview } from './entities/product-review.entity';
import { AttributeDataType } from './enums/attribute-data-type.enum';
import { ProductStatus } from './enums/product-status.enum';
import { ProductType } from './enums/product-type.enum';
@Injectable()
export class ProductsService {
@@ -113,12 +114,12 @@ export class ProductsService {
return this.serializeProduct(await this.findOneById(product.id));
}
async findPublic(filters: FilterProductsDto) {
return this.findAll(filters, false);
async findPublic(filters: FilterProductsDto, headerProductType?: string) {
return this.findAll(this.mergeProductTypeFilter(filters, headerProductType), false);
}
async findAdmin(filters: FilterProductsDto) {
return this.findAll(filters, true);
async findAdmin(filters: FilterProductsDto, headerProductType?: string) {
return this.findAll(this.mergeProductTypeFilter(filters, headerProductType), true);
}
async checkSlugAvailability(slug: string, excludeId?: string) {
@@ -135,9 +136,9 @@ export class ProductsService {
return !existing;
}
async findPublicOne(id: string) {
async findPublicOne(id: string, headerProductType?: string) {
const product = await this.productsRepository.findOne({
where: { id, status: ProductStatus.PUBLISHED },
where: this.buildPublicProductWhere({ id }, headerProductType),
relations: {
primaryCategory: true,
categories: true,
@@ -159,9 +160,9 @@ export class ProductsService {
};
}
async findPublicOneBySlug(slug: string) {
async findPublicOneBySlug(slug: string, headerProductType?: string) {
const product = await this.productsRepository.findOne({
where: { slug, status: ProductStatus.PUBLISHED },
where: this.buildPublicProductWhere({ slug }, headerProductType),
relations: {
primaryCategory: true,
categories: true,
@@ -528,7 +529,7 @@ export class ProductsService {
const [items, total] = await query.getManyAndCount();
return {
items: items.map((item) => this.serializeProduct(item, false)),
items: items.map((item) => this.serializeProductListItem(item, includeUnpublished)),
meta: {
total,
page,
@@ -742,11 +743,12 @@ export class ProductsService {
});
const results: ProductAttributeValue[] = [];
for (const input of inputs) {
for (const [index, input] of inputs.entries()) {
const attribute = await this.resolveAttributeDefinition(input);
const value = this.productAttributeValuesRepository.create({
product,
attribute,
displayOrder: input.displayOrder ?? index,
valueText: input.valueText ?? null,
valueNumber: input.valueNumber ?? null,
valueBoolean: input.valueBoolean ?? null,
@@ -803,6 +805,7 @@ export class ProductsService {
}
private serializeProduct(product: Product, includeMeta = true) {
const orderedAttributes = this.sortAttributes(product.attributeValues);
const meta = product.meta ?? null;
const shortDescription = meta?.shortDescription ?? null;
const share = {
@@ -854,8 +857,9 @@ export class ProductsService {
},
}
: {}),
attributes: (product.attributeValues ?? []).map((item) => ({
attributes: orderedAttributes.map((item) => ({
id: item.id,
displayOrder: item.displayOrder ?? 0,
attributeId: item.attribute?.id,
name: item.attribute?.name,
slug: item.attribute?.slug,
@@ -878,6 +882,137 @@ export class ProductsService {
};
}
private serializeProductListItem(product: Product, includeAdminFields: boolean) {
const meta = product.meta ?? null;
const orderedAttributes = this.sortAttributes(product.attributeValues).filter(
(item) => item.attribute?.isVisible ?? true,
);
return {
id: product.id,
sku: product.sku,
title: product.title,
slug: product.slug,
technicalCode: product.technicalCode,
brand: product.brand,
brandInfo: product.brandEntity
? {
id: product.brandEntity.id,
name: product.brandEntity.name,
slug: product.brandEntity.slug,
imageUrl: product.brandEntity.imageUrl,
type: product.brandEntity.type,
}
: null,
type: product.type,
featured: product.featured,
basePriceUSD: product.basePriceUSD,
salePriceUSD: product.salePriceUSD ?? null,
stock: product.stock,
averageRating: product.averageRating,
reviewsCount: product.reviewsCount,
mainImageUrl: product.mainImageUrl ?? null,
shortDescription: meta?.shortDescription ?? null,
primaryCategory: product.primaryCategory
? {
id: product.primaryCategory.id,
name: product.primaryCategory.name,
slug: product.primaryCategory.slug,
imageUrl: product.primaryCategory.imageUrl,
type: product.primaryCategory.type,
}
: null,
attributes: orderedAttributes.slice(0, 2).map((item) => ({
id: item.id,
displayOrder: item.displayOrder ?? 0,
attributeId: item.attribute?.id,
name: item.attribute?.name,
slug: item.attribute?.slug,
dataType: item.attribute?.dataType,
unit: item.overrideUnit ?? item.attribute?.unit ?? null,
valueText: item.valueText ?? item.attribute?.defaultValueText ?? null,
valueNumber:
item.valueNumber ??
(item.attribute?.defaultValueNumber !== null &&
item.attribute?.defaultValueNumber !== undefined
? Number(item.attribute.defaultValueNumber)
: null),
valueBoolean:
item.valueBoolean ?? item.attribute?.defaultValueBoolean ?? null,
valueJson: item.valueJson ?? item.attribute?.defaultValueJson ?? null,
})),
...(includeAdminFields
? {
status: product.status,
createdAt: product.createdAt,
updatedAt: product.updatedAt,
}
: {}),
};
}
private sortAttributes(attributes?: ProductAttributeValue[]) {
return [...(attributes ?? [])].sort((left, right) => {
const orderDiff = (left.displayOrder ?? 0) - (right.displayOrder ?? 0);
if (orderDiff !== 0) {
return orderDiff;
}
return left.attribute?.name?.localeCompare(right.attribute?.name ?? '') ?? 0;
});
}
private mergeProductTypeFilter(
filters: FilterProductsDto,
headerProductType?: string,
): FilterProductsDto {
if (filters.type) {
return filters;
}
const normalizedType = this.normalizeProductType(headerProductType);
if (!normalizedType) {
return filters;
}
return {
...filters,
type: normalizedType,
};
}
private buildPublicProductWhere(
where: Pick<Product, 'id'> | Pick<Product, 'slug'>,
headerProductType?: string,
) {
const normalizedType = this.normalizeProductType(headerProductType);
return {
...where,
status: ProductStatus.PUBLISHED,
...(normalizedType ? { type: normalizedType } : {}),
};
}
private normalizeProductType(headerProductType?: string): ProductType | undefined {
if (!headerProductType) {
return undefined;
}
const normalizedValue = headerProductType.trim().toLowerCase();
const matchedType = Object.values(ProductType).find(
(type) => type.toLowerCase() === normalizedValue,
);
if (!matchedType) {
throw new BadRequestException(
`Invalid x-product-type header. Allowed values: ${Object.values(ProductType).join(', ')}`,
);
}
return matchedType;
}
private async deleteGallery(imageUrls: string[]) {
await Promise.all(
(imageUrls ?? []).map((url) => this.storageService.deletePublicFileByUrl(url)),