feat(products): add ordered attributes and slim product list api
This commit is contained in:
3
.env
3
.env
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"admin-products.controller.js","sourceRoot":"","sources":["../../../src/modules/catalog/admin-products.controller.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"admin-products.controller.js","sourceRoot":"","sources":["../../../src/modules/catalog/admin-products.controller.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AttributeDataType } from '../enums/attribute-data-type.enum';
|
||||
export declare class ProductAttributeInputDto {
|
||||
displayOrder?: number;
|
||||
attributeId?: string;
|
||||
name?: string;
|
||||
slug?: string;
|
||||
|
||||
@@ -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)(),
|
||||
|
||||
@@ -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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"}
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"}
|
||||
89
dist/modules/catalog/products.controller.d.ts
vendored
89
dist/modules/catalog/products.controller.d.ts
vendored
@@ -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;
|
||||
|
||||
27
dist/modules/catalog/products.controller.js
vendored
27
dist/modules/catalog/products.controller.js
vendored
@@ -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);
|
||||
|
||||
@@ -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"}
|
||||
257
dist/modules/catalog/products.service.d.ts
vendored
257
dist/modules/catalog/products.service.d.ts
vendored
@@ -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;
|
||||
|
||||
130
dist/modules/catalog/products.service.js
vendored
130
dist/modules/catalog/products.service.js
vendored
@@ -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)));
|
||||
}
|
||||
|
||||
2
dist/modules/catalog/products.service.js.map
vendored
2
dist/modules/catalog/products.service.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/tsconfig.build.tsbuildinfo
vendored
2
dist/tsconfig.build.tsbuildinfo
vendored
File diff suppressed because one or more lines are too long
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
|
||||
Reference in New Issue
Block a user