From 80ec88f88d05093c40495f984ce7d8cc90ed41cf Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sun, 19 Oct 2025 11:01:39 +0330 Subject: [PATCH 1/3] fix : seeder --- src/db/seeders/index.ts | 4 ++-- src/db/seeders/warrantySeeders.ts | 38 +++++++++++++++---------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/db/seeders/index.ts b/src/db/seeders/index.ts index 5e9e878..1a42c6d 100644 --- a/src/db/seeders/index.ts +++ b/src/db/seeders/index.ts @@ -32,8 +32,8 @@ const seedDatabase = async () => { logger.info("connected to mongodb"); await clearDatabase(logger); await backfillHierarchy(); - await blogCategorySeeder(logger); - await blogSeeder(logger); + // await blogCategorySeeder(logger); + // await blogSeeder(logger); await contractSeeder(logger); await createDefaultAdmin(logger); await seedFaqs(logger); diff --git a/src/db/seeders/warrantySeeders.ts b/src/db/seeders/warrantySeeders.ts index 78b3375..defdaa4 100644 --- a/src/db/seeders/warrantySeeders.ts +++ b/src/db/seeders/warrantySeeders.ts @@ -6,25 +6,25 @@ import { WarrantyModel } from "../../modules/warranty/models/warranty.model"; const warrantyNames = [ "Iran Warranty Co.", - "Pars Guarantee", - "Tehran Warranty Services", - "Safir Warranty", - "Farda Warranty Solutions", - "Aryan Protection", - "Mehr Warranty", - "Saman Warranty Group", - "Persian Warranty Solutions", - "Shahab After Sales", - "Payam Warranty", - "Kavir Protection", - "Tajrish Warranty", - "Sina Warranty", - "Atlas Warranty Services", - "Sepahan Warranty", - "Kish Warranty Group", - "Navid Warranty Solutions", - "Alborz Protection Services", - "Raha Warranty Solutions", + // "Pars Guarantee", + // "Tehran Warranty Services", + // "Safir Warranty", + // "Farda Warranty Solutions", + // "Aryan Protection", + // "Mehr Warranty", + // "Saman Warranty Group", + // "Persian Warranty Solutions", + // "Shahab After Sales", + // "Payam Warranty", + // "Kavir Protection", + // "Tajrish Warranty", + // "Sina Warranty", + // "Atlas Warranty Services", + // "Sepahan Warranty", + // "Kish Warranty Group", + // "Navid Warranty Solutions", + // "Alborz Protection Services", + // "Raha Warranty Solutions", ]; export const seedWarranty = async (logger: Logger) => { From 9027e0ac9c91c67b5a5a0a3aef12ac0841bec056 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Thu, 23 Oct 2025 09:33:54 +0330 Subject: [PATCH 2/3] update: seeder --- src/db/seeders/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/seeders/index.ts b/src/db/seeders/index.ts index 1a42c6d..3798c53 100644 --- a/src/db/seeders/index.ts +++ b/src/db/seeders/index.ts @@ -6,8 +6,8 @@ import mongoose from "mongoose"; import { Logger } from "../../core/logging/logger"; import { connectMongo } from "../connection"; // import { backfillHierarchy } from "./backfillHierarchy"; -import { blogCategorySeeder } from "./blogCategorySeeder"; -import { blogSeeder } from "./blogSeeder"; +// import { blogCategorySeeder } from "./blogCategorySeeder"; +// import { blogSeeder } from "./blogSeeder"; import { contractSeeder } from "./contractSeeder"; // import { seedMedia } from "./mediaSeeder"; import { seedCityAndProvince } from "./iranCityseeder"; From 3c941c9fe89187383ea7d9c1986c292b0e52ac36 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Fri, 24 Oct 2025 20:40:23 +0330 Subject: [PATCH 3/3] add :update category attribute --- src/common/enums/message.enum.ts | 4 ++ .../admin/controllers/category.controller.ts | 12 ++++ .../category/DTO/updateCategoryAtt.dto.ts | 68 +++++++++++++++++++ src/modules/category/category.service.ts | 63 ++++++++++++++++- 4 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/modules/category/DTO/updateCategoryAtt.dto.ts diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index ce23691..79289a1 100644 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -92,6 +92,7 @@ export const enum CategoryMessage { AttAlreadyExist = "ویژگی‌های این دسته بندی قبلا ساخته شده است", VariantCreated = "تنوع‌های دسته بندی با موفقیت ساخته شدند", AttributeCreated = "ویژگی‌های دسته بندی با موفقیت ساخته شدند", + AttributeUpdated = "ویژگی‌های دسته بندی با موفقیت بروزرسانی شدند", NotValidId = "آیدی کتگوری صحیح نمی باشد", MissingColors = "داده‌های رنگ برای تم انتخاب شده موجود نیست.", MissingSizes = "داده‌های سایز برای تم انتخاب شده موجود نیست.", @@ -105,6 +106,9 @@ export const enum CategoryMessage { CategoryNotValidToChose = "کتگوری برای انتخاب مجاز نمی‌باشد", AttributeDeleted = "ویژگی با موفقیت حذف شد", } +export const enum CategoryAttrMessage { + NotValidId = "آیدی صحیح نمی باشد", +} export const enum BrandMessage { NotFound = "برندی با این اسم یافت نشد", diff --git a/src/modules/admin/controllers/category.controller.ts b/src/modules/admin/controllers/category.controller.ts index 655ef2a..539f716 100644 --- a/src/modules/admin/controllers/category.controller.ts +++ b/src/modules/admin/controllers/category.controller.ts @@ -13,6 +13,7 @@ import { CreateCategoryDTO } from "../../category/DTO/CreateCategory.dto"; import { CreateCategoryAttDTO } from "../../category/DTO/createCategoryAtt.dto"; import { CreateCategoryThemeDTO, UpdateCategoryVariantDTO } from "../../category/DTO/createCategoryTheme.dto"; import { UpdateCategoryDTO } from "../../category/DTO/UpdateCategory.dto"; +import { UpdateCategoryAttDTO } from "../../category/DTO/updateCategoryAtt.dto"; import { PermissionEnum } from "../models/Abstraction/IPermission"; @ApiTags("Admin Category") @@ -122,4 +123,15 @@ export class AdminCategoryController extends BaseController { const data = await this.categoryService.createCategoryAttributeS(createAttributeDto, catId); return this.response({ data }, HttpStatus.Created); } + + @ApiOperation("update category attributes ") + @ApiResponse("successful", HttpStatus.Created) + @ApiModel(UpdateCategoryAttDTO) + @ApiAuth() + @ApiBody("the attribute type can be text|select|number|checkbox|input ") + @httpPatch("/attributes", Guard.authAdmin(), ValidationMiddleware.validateInput(UpdateCategoryAttDTO)) + public async updateCategoryAttribute(@requestBody() createAttributeDto: UpdateCategoryAttDTO) { + const data = await this.categoryService.updateCategoryAttributeS(createAttributeDto); + return this.response({ data }, HttpStatus.Created); + } } diff --git a/src/modules/category/DTO/updateCategoryAtt.dto.ts b/src/modules/category/DTO/updateCategoryAtt.dto.ts new file mode 100644 index 0000000..0f2dff9 --- /dev/null +++ b/src/modules/category/DTO/updateCategoryAtt.dto.ts @@ -0,0 +1,68 @@ +import { Expose, Type } from "class-transformer"; +import { ArrayMinSize, IsArray, IsBoolean, IsEnum, IsInt, IsNotEmpty, IsOptional, IsString, ValidateNested } from "class-validator"; + +import { ApiProperty } from "../../../common/decorator/swggerDocs"; +import { IsValidId } from "../../../common/decorator/validation.decorator"; +import { categoryAttType } from "../../../common/enums/category.enum"; +import { CategoryAttributeModel } from "../models/CategoryAttribute.model"; + +class AttributeValue { + @Expose() + @IsNotEmpty() + @IsString() + @ApiProperty({ type: "string", description: "Text of the attribute value", example: "ابریشم" }) + text: string; +} + +export class UpdateCategoryAttDTO { + @Expose() + @IsNotEmpty() + @IsInt() + @IsValidId(CategoryAttributeModel) + @ApiProperty({ type: "number", description: "category attribute id", example: 5 }) + attrId: number; + + @Expose() + @IsNotEmpty() + @ApiProperty({ type: "string", description: "Title of the attribute", example: "جنس" }) + title: string; + + @Expose() + @IsNotEmpty() + @IsEnum(categoryAttType) + @ApiProperty({ type: "string", description: "Type of the attribute", example: "checkbox" }) + type: categoryAttType; + + @Expose() + @IsOptional() + @IsNotEmpty() + @IsBoolean() + @ApiProperty({ type: "boolean", description: "if this be true then attribute can be selected many times", example: false }) + multiple: boolean; + + @Expose() + @IsOptional() + @IsNotEmpty() + @IsString() + @ApiProperty({ type: "string", description: "hint of the attribute", example: "نوع پارچه" }) + hint: string; + + @Expose() + @IsNotEmpty() + @IsBoolean() + @ApiProperty({ type: "boolean", description: "Specifies if the attribute is required", example: true }) + required: boolean; + + @Expose() + @IsNotEmpty() + @IsArray() + @ArrayMinSize(1) + @ValidateNested({ each: true }) + @Type(() => AttributeValue) + @ApiProperty({ + type: "Array", + description: "If the attribute allows multiple values (e.g., checkbox or select), provide the values here", + example: [{ text: "ابریشم" }, { text: "اسفنجی" }], + }) + values: AttributeValue[]; +} diff --git a/src/modules/category/category.service.ts b/src/modules/category/category.service.ts index b97cee4..0964603 100644 --- a/src/modules/category/category.service.ts +++ b/src/modules/category/category.service.ts @@ -23,12 +23,13 @@ import { UpdateCategoryVariantDTO, } from "./DTO/createCategoryTheme.dto"; import { UpdateCategoryDTO } from "./DTO/UpdateCategory.dto"; +import { UpdateCategoryAttDTO } from "./DTO/updateCategoryAtt.dto"; import { IColor } from "./models/Abstraction/IColor"; import { IMeterage } from "./models/Abstraction/IMeterage"; import { ISize } from "./models/Abstraction/ISize"; import { BaseRepository } from "../../common/base/repository"; import { CategoryThemeEnum } from "../../common/enums/category.enum"; -import { CategoryMessage, CommonMessage } from "../../common/enums/message.enum"; +import { CategoryAttrMessage, CategoryMessage, CommonMessage } from "../../common/enums/message.enum"; import { BadRequestError, ForbiddenError } from "../../core/app/app.errors"; import { IOCTYPES } from "../../IOC/ioc.types"; import { ProductDTO } from "../product/DTO/product.dto"; @@ -289,6 +290,66 @@ class CategoryService { } } + async updateCategoryAttributeS(dto: UpdateCategoryAttDTO) { + const session = await startSession(); + session.startTransaction(); + try { + const { attrId, title, type, multiple, required, values, hint } = dto; + + // Validate the existence of the category + const existCategoryAttr = await this.categoryAttRepo.findById(attrId); + if (!existCategoryAttr) { + throw new BadRequestError(CategoryAttrMessage.NotValidId); + } + + // Check if attribute already exists for the category + const existAttribute = await this.categoryAttRepo.model.exists({ + category: existCategoryAttr.category._id, + _id: { $ne: attrId }, + title, + }); + + if (existAttribute) { + throw new BadRequestError(CategoryMessage.AttAlreadyExist); + } + + // Create the category attribute + const updated = await this.categoryAttRepo.model.findByIdAndUpdate( + attrId, + + { + category: existCategoryAttr.category._id, + title, + type, + multiple, + hint, + required, + }, + { session }, + ); + + // delete attribute value + await this.attributeValueRepo.model.deleteMany({ attribute: attrId }, { session }); + + for (const value of values) { + await this.attributeValueRepo.model.create([{ text: value.text, attribute: attrId }], { session }); + } + // Commit the transaction + await session.commitTransaction(); + session.endSession(); + + return { + message: CategoryMessage.AttributeUpdated, + attribute: updated, + }; + } catch (error) { + // Rollback any changes made in the transaction + await session.abortTransaction(); + session.endSession(); + throw error; // Re-throw the error to be handled by the calling errorHandler + } + } + async getCategoryAttributeS(catId: string) { await this.checkCategoryId(catId);