diff --git a/src/app.module.ts b/src/app.module.ts index 443884e..9a2ad30 100755 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -7,13 +7,13 @@ import { ConfigModule } from "@nestjs/config"; import { ThrottlerModule } from "@nestjs/throttler"; import { TypeOrmModule } from "@nestjs/typeorm"; import { MailerModule } from "@nestjs-modules/mailer"; -import { TelegrafModule } from "nestjs-telegraf"; +// import { TelegrafModule } from "nestjs-telegraf"; import { bullMqConfig } from "./configs/bullmq.config"; import { cacheConfig } from "./configs/cache.config"; import { mailerConfig } from "./configs/mailer.config"; import { rateLimitConfig } from "./configs/rateLimit.config"; -import { telegrafConfig } from "./configs/telegraf.config"; +// import { telegrafConfig } from "./configs/telegraf.config"; import { databaseConfigs } from "./configs/typeorm.config"; import { HTTPLogger } from "./core/middlewares/logger.middleware"; import { AddressModule } from "./modules/address/address.module"; @@ -35,15 +35,15 @@ import { PaymentsModule } from "./modules/payments/payments.module"; import { ReferralsModule } from "./modules/referrals/referrals.module"; import { SettingModule } from "./modules/settings/settings.module"; import { SubscriptionsModule } from "./modules/subscriptions/subscriptions.module"; +import { SupportPlansModule } from "./modules/support-plans/support-plans.module"; import { TicketsModule } from "./modules/tickets/tickets.module"; import { UploaderModule } from "./modules/uploader/uploader.module"; import { UsersModule } from "./modules/users/users.module"; import { WalletsModule } from "./modules/wallets/wallets.module"; import { MonitoringModule } from "./monitoring/monitoring.module"; - @Module({ imports: [ - TelegrafModule.forRootAsync(telegrafConfig()), + // TelegrafModule.forRootAsync(telegrafConfig()), MailerModule.forRootAsync(mailerConfig()), BullModule.forRootAsync(bullMqConfig()), ThrottlerModule.forRootAsync(rateLimitConfig()), @@ -84,6 +84,7 @@ import { MonitoringModule } from "./monitoring/monitoring.module"; LandingModule, ReferralsModule, DiscountsModule, + SupportPlansModule, ], controllers: [], }) diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index 6926222..4908c18 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -120,6 +120,7 @@ export const enum CommonMessage { PaymentTypeQueryNotEmpty = "نوع پرداخت نمی‌تواند خالی باشد", SEARCH_QUERY_STRING = "رشته جستجو باید یک رشته باشد", UPDATED = "با موفقیت آپدیت شد", + IS_ACTIVE_SHOULD_BE_1_0 = "وضعیت باید یکی از مقادیر ۰ و ۱ باشد", } export const enum CategoryMessage { @@ -776,3 +777,32 @@ export const enum ReferralMessage { INVALID_OR_INACTIVE_REFERRAL_CODE = "کد ارجاع نامعتبر یا غیرفعال است", USER_ALREADY_REFERRED = "کاربر قبلاً ارجاع داده شده است", } + +export const enum SupportPlanMessage { + NAME_REQUIRED = "نام الزامی است", + NAME_STRING = "نام باید یک رشته باشد", + NAME_MAX_LENGTH = "حداکثر طول نام باید ۱۵۰ کاراکتر باشد", + DESCRIPTION_REQUIRED = "توضیحات الزامی است", + DESCRIPTION_STRING = "توضیحات باید یک رشته باشد", + DESCRIPTION_MAX_LENGTH = "حداکثر طول توضیحات باید ۱۰۰۰ کاراکتر باشد", + PRICE_REQUIRED = "قیمت الزامی است", + PRICE_NUMBER = "قیمت باید یک عدد باشد", + PRICE_MIN = "قیمت باید بیشتر از ۱۰۰۰۰ باشد", + DURATION_REQUIRED = "مدت الزامی است", + DURATION_NUMBER = "مدت باید یک عدد باشد", + DURATION_MIN = "مدت باید بیشتر از ۰ باشد", + IS_ACTIVE_REQUIRED = "وضعیت فعال بودن الزامی است", + IS_ACTIVE_BOOLEAN = "وضعیت فعال بودن باید یک بولین باشد", + SUPPORT_PLAN_CREATED = "پلن پشتیبانی با موفقیت ایجاد شد", + FEATURE_KEY_REQUIRED = "کلید ویژگی الزامی است", + FEATURE_VALUE_REQUIRED = "مقدار ویژگی الزامی است", + FEATURE_VALUE_STRING = "مقدار ویژگی باید یک رشته باشد", + FEATURE_TYPE_REQUIRED = "نوع ویژگی الزامی است", + FEATURE_ICON_URL = "آدرس آیکون ویژگی باید یک URL معتبر باشد", + FEATURE_ICON_REQUIRED = "آیکون ویژگی الزامی است", + FEATURES_ARRAY = "ویژگی‌ها باید یک آرایه باشد", + FEATURES_VALIDATE_NESTED = "ویژگی‌ها باید یک آرایه از ویژگی‌ها باشد", + SUPPORT_PLAN_NOT_FOUND = "پلن پشتیبانی مورد نظر یافت نشد", + SUPPORT_PLAN_DELETED = "پلن پشتیبانی با موفقیت حذف شد", + SUPPORT_PLAN_UPDATED = "پلن پشتیبانی با موفقیت به روز رسانی شد", +} diff --git a/src/configs/typeorm.config.ts b/src/configs/typeorm.config.ts index 8d8378f..9391ab2 100755 --- a/src/configs/typeorm.config.ts +++ b/src/configs/typeorm.config.ts @@ -13,7 +13,7 @@ export function databaseConfigs(): TypeOrmModuleAsyncOptions { username: configService.getOrThrow("DB_USER"), password: configService.getOrThrow("DB_PASS"), autoLoadEntities: true, - synchronize: configService.getOrThrow("NODE_ENV") == "production" ? false : true, + synchronize: configService.getOrThrow("NODE_ENV") == "production" ? false : false, logging: configService.getOrThrow("NODE_ENV") == "production" ? false : true, migrationsTableName: "typeorm_migrations", migrationsRun: false, diff --git a/src/modules/ads/DTO/create-ads.dto.ts b/src/modules/ads/DTO/create-ads.dto.ts index c363b60..749ba86 100755 --- a/src/modules/ads/DTO/create-ads.dto.ts +++ b/src/modules/ads/DTO/create-ads.dto.ts @@ -36,7 +36,7 @@ export class CreateAdsDto { isActive: boolean; @IsNotEmpty({ message: AdsMessage.IMAGE_URL_REQUIRED }) - @IsUrl({ protocols: ["http", "https"], require_protocol: true }, { message: AdsMessage.IMAGE_SHOULD_BE_URL }) + @IsUrl({ protocols: ["https"], require_protocol: true }, { message: AdsMessage.IMAGE_SHOULD_BE_URL }) @ApiProperty({ description: "The URL of the image to be displayed", example: "https://test.com/pic.png" }) imageUrl: string; diff --git a/src/modules/danak-services/providers/danak-services.service.ts b/src/modules/danak-services/providers/danak-services.service.ts index 6f7c6d3..22203fb 100755 --- a/src/modules/danak-services/providers/danak-services.service.ts +++ b/src/modules/danak-services/providers/danak-services.service.ts @@ -105,10 +105,10 @@ export class DanakServicesService { async updateCategory(categoryId: string, updateDto: UpdateDanakServiceCategoryDto) { const queryRunner = this.dataSource.createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); try { + await queryRunner.connect(); + await queryRunner.startTransaction(); const category = await queryRunner.manager.findOne(DanakServiceCategory, { where: { id: categoryId } }); if (!category) throw new BadRequestException(CategoryMessage.CATEGORY_NOT_EXIST); @@ -386,6 +386,7 @@ export class DanakServicesService { description: true, createDate: true, icon: true, + slug: true, createdAt: true, isActive: true, isDanakSuggest: true, diff --git a/src/modules/support-plans/DTO/create-support-plan-feature.dto.ts b/src/modules/support-plans/DTO/create-support-plan-feature.dto.ts new file mode 100644 index 0000000..672223d --- /dev/null +++ b/src/modules/support-plans/DTO/create-support-plan-feature.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsEnum, IsNotEmpty, IsOptional, IsString, IsUrl } from "class-validator"; + +import { SupportPlanMessage } from "../../../common/enums/message.enum"; +import { SupportPlanFeatureKey } from "../enums/support-plan-feature-key.enum"; +import { SupportPlanFeatureType } from "../enums/support-plan-feature-type.enum"; + +export class CreateSupportPlanFeatureDto { + @IsNotEmpty({ message: SupportPlanMessage.FEATURE_KEY_REQUIRED }) + @IsEnum(SupportPlanFeatureKey) + @ApiProperty({ description: "The key of the feature", enum: SupportPlanFeatureKey, example: SupportPlanFeatureKey.LEARNING_DOCS }) + featureKey: SupportPlanFeatureKey; + + @IsNotEmpty({ message: SupportPlanMessage.FEATURE_VALUE_REQUIRED }) + @IsString({ message: SupportPlanMessage.FEATURE_VALUE_STRING }) + @ApiProperty({ description: "The value of the feature", example: "true" }) + featureValue: string; + + @IsNotEmpty({ message: SupportPlanMessage.FEATURE_TYPE_REQUIRED }) + @IsEnum(SupportPlanFeatureType) + @ApiProperty({ description: "The type of the feature", enum: SupportPlanFeatureType, example: SupportPlanFeatureType.BOOLEAN }) + featureType: SupportPlanFeatureType; + + @IsOptional() + @IsNotEmpty({ message: SupportPlanMessage.FEATURE_ICON_REQUIRED }) + @IsUrl({ protocols: ["https"] }, { message: SupportPlanMessage.FEATURE_ICON_URL }) + @ApiProperty({ description: "The icon of the feature", example: "https://example.com/icon.png" }) + icon?: string; +} diff --git a/src/modules/support-plans/DTO/create-support-plan.dto.ts b/src/modules/support-plans/DTO/create-support-plan.dto.ts new file mode 100644 index 0000000..e5d86c8 --- /dev/null +++ b/src/modules/support-plans/DTO/create-support-plan.dto.ts @@ -0,0 +1,58 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { Type } from "class-transformer"; +import { + ArrayMinSize, + IsArray, + IsBoolean, + IsNotEmpty, + IsNumber, + IsOptional, + IsString, + MaxLength, + Min, + ValidateNested, +} from "class-validator"; + +import { CreateSupportPlanFeatureDto } from "./create-support-plan-feature.dto"; +import { SupportPlanMessage } from "../../../common/enums/message.enum"; + +export class CreateSupportPlanDto { + @IsNotEmpty({ message: SupportPlanMessage.NAME_REQUIRED }) + @IsString({ message: SupportPlanMessage.NAME_STRING }) + @MaxLength(150, { message: SupportPlanMessage.NAME_MAX_LENGTH }) + @ApiProperty({ description: "The name of the support plan" }) + name: string; + + @IsOptional() + @IsNotEmpty({ message: SupportPlanMessage.DESCRIPTION_REQUIRED }) + @IsString({ message: SupportPlanMessage.DESCRIPTION_STRING }) + @MaxLength(1000, { message: SupportPlanMessage.DESCRIPTION_MAX_LENGTH }) + @ApiProperty({ description: "The description of the support plan" }) + description: string; + + @IsNotEmpty({ message: SupportPlanMessage.PRICE_REQUIRED }) + @IsNumber({}, { message: SupportPlanMessage.PRICE_NUMBER }) + @Min(10000, { message: SupportPlanMessage.PRICE_MIN }) + @ApiProperty({ description: "The price of the support plan" }) + price: number; + + @IsNotEmpty({ message: SupportPlanMessage.DURATION_REQUIRED }) + @IsNumber({}, { message: SupportPlanMessage.DURATION_NUMBER }) + @Min(1, { message: SupportPlanMessage.DURATION_MIN }) + @ApiProperty({ description: "The duration of the support plan" }) + duration: number; + + @IsOptional() + @IsNotEmpty({ message: SupportPlanMessage.IS_ACTIVE_REQUIRED }) + @IsBoolean({ message: SupportPlanMessage.IS_ACTIVE_BOOLEAN }) + @ApiProperty({ description: "The status of the support plan" }) + isActive: boolean; + + @IsNotEmpty({ message: SupportPlanMessage.FEATURES_VALIDATE_NESTED }) + @IsArray({ message: SupportPlanMessage.FEATURES_VALIDATE_NESTED }) + @ArrayMinSize(9, { message: SupportPlanMessage.FEATURES_VALIDATE_NESTED }) + @ValidateNested({ each: true }) + @Type(() => CreateSupportPlanFeatureDto) + @ApiProperty({ description: "The features of the support plan", type: [CreateSupportPlanFeatureDto] }) + features: CreateSupportPlanFeatureDto[]; +} diff --git a/src/modules/support-plans/DTO/get-support-plan-list-query.dto.ts b/src/modules/support-plans/DTO/get-support-plan-list-query.dto.ts new file mode 100644 index 0000000..647a4ad --- /dev/null +++ b/src/modules/support-plans/DTO/get-support-plan-list-query.dto.ts @@ -0,0 +1,19 @@ +import { ApiPropertyOptional } from "@nestjs/swagger"; +import { Type } from "class-transformer"; +import { IsIn, IsOptional, IsString } from "class-validator"; + +import { PaginationDto } from "../../../common/DTO/pagination.dto"; +import { CommonMessage } from "../../../common/enums/message.enum"; + +export class GetSupportPlanListQueryDto extends PaginationDto { + @IsOptional() + @Type(() => Number) + @IsIn([1, 0], { message: CommonMessage.IS_ACTIVE_SHOULD_BE_1_0 }) + @ApiPropertyOptional({ description: "status", example: 1 }) + isActive?: number; + + @IsOptional() + @IsString({ message: CommonMessage.SEARCH_QUERY_STRING }) + @ApiPropertyOptional({ description: "Search query", example: "search query" }) + q?: string; +} diff --git a/src/modules/support-plans/DTO/update-support-plan-feature.dto.ts b/src/modules/support-plans/DTO/update-support-plan-feature.dto.ts new file mode 100644 index 0000000..c304c7b --- /dev/null +++ b/src/modules/support-plans/DTO/update-support-plan-feature.dto.ts @@ -0,0 +1,5 @@ +import { PartialType } from "@nestjs/swagger"; + +import { CreateSupportPlanDto } from "./create-support-plan.dto"; + +export class UpdateSupportPlanDto extends PartialType(CreateSupportPlanDto) {} diff --git a/src/modules/support-plans/entities/support-plan-feature.entity.ts b/src/modules/support-plans/entities/support-plan-feature.entity.ts new file mode 100644 index 0000000..13b9429 --- /dev/null +++ b/src/modules/support-plans/entities/support-plan-feature.entity.ts @@ -0,0 +1,24 @@ +import { Column, Entity, ManyToOne } from "typeorm"; + +import { SupportPlan } from "./support-plan.entity"; +import { BaseEntity } from "../../../common/entities/base.entity"; +import { SupportPlanFeatureKey } from "../enums/support-plan-feature-key.enum"; +import { SupportPlanFeatureType } from "../enums/support-plan-feature-type.enum"; + +@Entity() +export class SupportPlanFeature extends BaseEntity { + @ManyToOne(() => SupportPlan, (plan) => plan.features, { nullable: false, onDelete: "CASCADE" }) + supportPlan: SupportPlan; + + @Column({ type: "enum", enum: SupportPlanFeatureKey, nullable: false }) + featureKey: SupportPlanFeatureKey; + + @Column({ type: "varchar", length: 255, nullable: false }) + featureValue: string; + + @Column({ type: "enum", enum: SupportPlanFeatureType, nullable: false }) + featureType: SupportPlanFeatureType; + + @Column({ type: "varchar", length: 255, nullable: true }) + icon: string | null; +} diff --git a/src/modules/support-plans/entities/support-plan.entity.ts b/src/modules/support-plans/entities/support-plan.entity.ts new file mode 100644 index 0000000..3d65c51 --- /dev/null +++ b/src/modules/support-plans/entities/support-plan.entity.ts @@ -0,0 +1,29 @@ +import { Decimal } from "decimal.js"; +import { Column, DeleteDateColumn, Entity, OneToMany } from "typeorm"; + +import { SupportPlanFeature } from "./support-plan-feature.entity"; +import { BaseEntity } from "../../../common/entities/base.entity"; +import { DecimalTransformer } from "../../../common/transformers/decimal.transformer"; +@Entity() +export class SupportPlan extends BaseEntity { + @Column({ type: "varchar", length: 150, nullable: false }) + name: string; + + @Column({ type: "text", nullable: true }) + description: string | null; + + @Column({ type: "decimal", precision: 16, scale: 2, nullable: false, transformer: new DecimalTransformer() }) + price: Decimal; + + @Column({ type: "int", nullable: false }) + duration: number; //days + + @Column({ type: "boolean", default: true }) + isActive: boolean; + + @OneToMany(() => SupportPlanFeature, (feature) => feature.supportPlan, { cascade: true }) + features: SupportPlanFeature[]; + + @DeleteDateColumn({ nullable: true }) + deletedAt?: Date; +} diff --git a/src/modules/support-plans/entities/user-support-plan.entity.ts b/src/modules/support-plans/entities/user-support-plan.entity.ts new file mode 100644 index 0000000..3b219a5 --- /dev/null +++ b/src/modules/support-plans/entities/user-support-plan.entity.ts @@ -0,0 +1,24 @@ +import { Column, Entity, ManyToOne } from "typeorm"; + +import { SupportPlan } from "./support-plan.entity"; +import { BaseEntity } from "../../../common/entities/base.entity"; +import { User } from "../../users/entities/user.entity"; +import { UserSupportPlanStatus } from "../enums/user-support-plan-status.enum"; + +@Entity() +export class UserSupportPlan extends BaseEntity { + @ManyToOne(() => User, { nullable: false, onDelete: "CASCADE" }) + user: User; + + @ManyToOne(() => SupportPlan, { nullable: false, onDelete: "CASCADE" }) + supportPlan: SupportPlan; + + @Column({ type: "timestamptz", nullable: false }) + startDate: Date; + + @Column({ type: "timestamptz", nullable: false }) + endDate: Date; + + @Column({ type: "enum", enum: UserSupportPlanStatus, default: UserSupportPlanStatus.INACTIVE }) + status: UserSupportPlanStatus; +} diff --git a/src/modules/support-plans/enums/support-plan-feature-key.enum.ts b/src/modules/support-plans/enums/support-plan-feature-key.enum.ts new file mode 100644 index 0000000..1c587d2 --- /dev/null +++ b/src/modules/support-plans/enums/support-plan-feature-key.enum.ts @@ -0,0 +1,11 @@ +export enum SupportPlanFeatureKey { + LEARNING_DOCS = "learning_docs", // راهنما و مستندات آموزشی + TICKET_SUPPORT = "ticket_support", // ارسال تیکت + RESPONSE_TIME = "response_time", // زمان پاسخگویی به تیکت + PHONE_SUPPORT = "phone_support", // پاسخگویی تلفنی + BACKUP_VERSION = "backup_version", // نسخه بکاپ + TECHNICAL_EXPERT_ACCESS = "technical_expert_access", // دسترسی به متخصصین فنی + ON_SITE_SUPPORT = "on_site_support", // پشتیبانی در محل + ON_SITE_TRAINING = "on_site_training", // آموزش در محل + TICKET_LIMIT = "ticket_limit", // ظرفیت ارسال تیکت +} diff --git a/src/modules/support-plans/enums/support-plan-feature-type.enum.ts b/src/modules/support-plans/enums/support-plan-feature-type.enum.ts new file mode 100644 index 0000000..dee1a93 --- /dev/null +++ b/src/modules/support-plans/enums/support-plan-feature-type.enum.ts @@ -0,0 +1,7 @@ +export enum SupportPlanFeatureType { + TEXT = "text", + BOOLEAN = "boolean", + // NUMBER = "number", + // DATE = "date", + // TIME = "time", +} diff --git a/src/modules/support-plans/enums/user-support-plan-status.enum.ts b/src/modules/support-plans/enums/user-support-plan-status.enum.ts new file mode 100644 index 0000000..9051da4 --- /dev/null +++ b/src/modules/support-plans/enums/user-support-plan-status.enum.ts @@ -0,0 +1,5 @@ +export enum UserSupportPlanStatus { + ACTIVE = "ACTIVE", + INACTIVE = "INACTIVE", + CANCELLED = "CANCELLED", +} diff --git a/src/modules/support-plans/providers/support-plans.service.ts b/src/modules/support-plans/providers/support-plans.service.ts new file mode 100644 index 0000000..5da97db --- /dev/null +++ b/src/modules/support-plans/providers/support-plans.service.ts @@ -0,0 +1,62 @@ +import { BadRequestException, Injectable } from "@nestjs/common"; + +import { SupportPlanMessage } from "../../../common/enums/message.enum"; +import { CreateSupportPlanDto } from "../DTO/create-support-plan.dto"; +import { GetSupportPlanListQueryDto } from "../DTO/get-support-plan-list-query.dto"; +import { UpdateSupportPlanDto } from "../DTO/update-support-plan-feature.dto"; +import { SupportPlanRepository } from "../repositories/support-plan.repository"; +@Injectable() +export class SupportPlansService { + constructor( + private readonly supportPlanRepo: SupportPlanRepository, + // private readonly userSupportPlanRepo: UserSupportPlanRepository, + ) {} + + async createSupportPlan(createSupportPlanDto: CreateSupportPlanDto) { + const supportPlan = this.supportPlanRepo.create(createSupportPlanDto); + await this.supportPlanRepo.save(supportPlan); + return { + message: SupportPlanMessage.SUPPORT_PLAN_CREATED, + supportPlan, + }; + } + //***************************************** */ + async getSupportPlansList(queryDto: GetSupportPlanListQueryDto) { + const [supportPlans, count] = await this.supportPlanRepo.getSupportPlansListAdmin(queryDto); + return { + supportPlans, + count, + paginate: true, + }; + } + //***************************************** */ + async getSupportPlanById(id: string) { + const supportPlan = await this.supportPlanRepo.getSupportPlanById(id); + if (!supportPlan) throw new BadRequestException(SupportPlanMessage.SUPPORT_PLAN_NOT_FOUND); + + return { + supportPlan, + }; + } + //***************************************** */ + async deleteSupportPlanById(id: string) { + const supportPlan = await this.supportPlanRepo.getSupportPlanById(id); + if (!supportPlan) throw new BadRequestException(SupportPlanMessage.SUPPORT_PLAN_NOT_FOUND); + await this.supportPlanRepo.softDelete(id); + + return { + message: SupportPlanMessage.SUPPORT_PLAN_DELETED, + }; + } + //***************************************** */ + async updateSupportPlanById(id: string, updateSupportPlanDto: UpdateSupportPlanDto) { + const supportPlan = await this.supportPlanRepo.getSupportPlanById(id); + if (!supportPlan) throw new BadRequestException(SupportPlanMessage.SUPPORT_PLAN_NOT_FOUND); + + await this.supportPlanRepo.save({ ...supportPlan, ...updateSupportPlanDto }); + + return { + message: SupportPlanMessage.SUPPORT_PLAN_UPDATED, + }; + } +} diff --git a/src/modules/support-plans/repositories/support-plan.repository.ts b/src/modules/support-plans/repositories/support-plan.repository.ts new file mode 100644 index 0000000..e99e3e5 --- /dev/null +++ b/src/modules/support-plans/repositories/support-plan.repository.ts @@ -0,0 +1,35 @@ +import { Injectable } from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { IsNull, Repository } from "typeorm"; + +import { PaginationUtils } from "../../utils/providers/pagination.utils"; +import { GetSupportPlanListQueryDto } from "../DTO/get-support-plan-list-query.dto"; +import { SupportPlan } from "../entities/support-plan.entity"; + +@Injectable() +export class SupportPlanRepository extends Repository { + constructor(@InjectRepository(SupportPlan) supportPlanRepo: Repository) { + super(supportPlanRepo.target, supportPlanRepo.manager, supportPlanRepo.queryRunner); + } + + //***************************************** */ + async getSupportPlansListAdmin(queryDto: GetSupportPlanListQueryDto) { + const { limit, skip } = PaginationUtils(queryDto); + const query = this.createQueryBuilder("supportPlan").where("supportPlan.deletedAt IS NULL"); + + if (queryDto.q) { + query.andWhere("supportPlan.name ILIKE :q OR supportPlan.description ILIKE :q", { q: `%${queryDto.q}%` }); + } + + if (queryDto.isActive !== undefined) { + query.andWhere("supportPlan.isActive = :isActive", { isActive: queryDto.isActive === 1 }); + } + + query.skip(skip).take(limit).orderBy("supportPlan.createdAt", "DESC"); + return query.getManyAndCount(); + } + //***************************************** */ + async getSupportPlanById(id: string) { + return this.findOne({ where: { id, deletedAt: IsNull() }, relations: { features: true } }); + } +} diff --git a/src/modules/support-plans/repositories/user-support-plan.repository.ts b/src/modules/support-plans/repositories/user-support-plan.repository.ts new file mode 100644 index 0000000..8521095 --- /dev/null +++ b/src/modules/support-plans/repositories/user-support-plan.repository.ts @@ -0,0 +1,12 @@ +import { Injectable } from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; + +import { UserSupportPlan } from "../entities/user-support-plan.entity"; + +@Injectable() +export class UserSupportPlanRepository extends Repository { + constructor(@InjectRepository(UserSupportPlan) userSupportPlanRepo: Repository) { + super(userSupportPlanRepo.target, userSupportPlanRepo.manager, userSupportPlanRepo.queryRunner); + } +} diff --git a/src/modules/support-plans/support-plans.controller.ts b/src/modules/support-plans/support-plans.controller.ts new file mode 100644 index 0000000..85fed19 --- /dev/null +++ b/src/modules/support-plans/support-plans.controller.ts @@ -0,0 +1,55 @@ +import { Body, Controller, Delete, Get, Param, Post, Query } from "@nestjs/common"; +import { ApiOperation } from "@nestjs/swagger"; + +import { CreateSupportPlanDto } from "./DTO/create-support-plan.dto"; +import { GetSupportPlanListQueryDto } from "./DTO/get-support-plan-list-query.dto"; +import { SupportPlansService } from "./providers/support-plans.service"; +import { AdminRoute } from "../../common/decorators/admin.decorator"; +import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; +import { ParamDto } from "../../common/DTO/param.dto"; + +@Controller("support-plans") +@AuthGuards() +export class SupportPlansController { + constructor(private readonly supportPlansService: SupportPlansService) {} + + @AdminRoute() + // @PermissionsDec(PermissionEnum.SUPPORT_PLAN) + @ApiOperation({ summary: "Create a new support plan (admin)" }) + @Post() + createSupportPlan(@Body() createSupportPlanDto: CreateSupportPlanDto) { + return this.supportPlansService.createSupportPlan(createSupportPlanDto); + } + + @AdminRoute() + // @PermissionsDec(PermissionEnum.SUPPORT_PLAN) + @ApiOperation({ summary: "Get all support plans (admin)" }) + @Get("list") + getSupportPlansList(@Query() queryDto: GetSupportPlanListQueryDto) { + return this.supportPlansService.getSupportPlansList(queryDto); + } + + @AdminRoute() + // @PermissionsDec(PermissionEnum.SUPPORT_PLAN) + @ApiOperation({ summary: "Get a support plan by id (admin)" }) + @Get(":id") + getSupportPlanById(@Param() paramDto: ParamDto) { + return this.supportPlansService.getSupportPlanById(paramDto.id); + } + + @AdminRoute() + // @PermissionsDec(PermissionEnum.SUPPORT_PLAN) + @ApiOperation({ summary: "Delete a support plan by id (admin)" }) + @Delete(":id") + deleteSupportPlanById(@Param() paramDto: ParamDto) { + return this.supportPlansService.deleteSupportPlanById(paramDto.id); + } + + // @AdminRoute() + // @PermissionsDec(PermissionEnum.SUPPORT_PLAN) + // @ApiOperation({ summary: "Update a support plan by id (admin)" }) + // @Patch(":id") + // updateSupportPlanById(@Param() paramDto: ParamDto, @Body() updateSupportPlanDto: UpdateSupportPlanDto) { + // return this.supportPlansService.updateSupportPlanById(paramDto.id, updateSupportPlanDto); + // } +} diff --git a/src/modules/support-plans/support-plans.module.ts b/src/modules/support-plans/support-plans.module.ts new file mode 100644 index 0000000..49d9e17 --- /dev/null +++ b/src/modules/support-plans/support-plans.module.ts @@ -0,0 +1,18 @@ +import { Module } from "@nestjs/common"; +import { TypeOrmModule } from "@nestjs/typeorm"; + +import { SupportPlanFeature } from "./entities/support-plan-feature.entity"; +import { SupportPlan } from "./entities/support-plan.entity"; +import { UserSupportPlan } from "./entities/user-support-plan.entity"; +import { SupportPlansService } from "./providers/support-plans.service"; +import { SupportPlanRepository } from "./repositories/support-plan.repository"; +import { UserSupportPlanRepository } from "./repositories/user-support-plan.repository"; +import { SupportPlansController } from "./support-plans.controller"; + +@Module({ + imports: [TypeOrmModule.forFeature([SupportPlan, SupportPlanFeature, UserSupportPlan])], + controllers: [SupportPlansController], + providers: [SupportPlansService, SupportPlanRepository, UserSupportPlanRepository], + exports: [SupportPlansService], +}) +export class SupportPlansModule {} diff --git a/src/modules/users/enums/permission.enum.ts b/src/modules/users/enums/permission.enum.ts index 1ab953a..a2e4323 100755 --- a/src/modules/users/enums/permission.enum.ts +++ b/src/modules/users/enums/permission.enum.ts @@ -19,4 +19,5 @@ export enum PermissionEnum { BANK_ACCOUNTS = "bank_accounts", PAYMENTS = "payments", MANAGE_SSO_CLIENTS = "manage_sso_clients", + SUPPORT_PLAN = "support_plan", }