feat: add support plan but not tested
This commit is contained in:
+5
-4
@@ -7,13 +7,13 @@ import { ConfigModule } from "@nestjs/config";
|
|||||||
import { ThrottlerModule } from "@nestjs/throttler";
|
import { ThrottlerModule } from "@nestjs/throttler";
|
||||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||||
import { MailerModule } from "@nestjs-modules/mailer";
|
import { MailerModule } from "@nestjs-modules/mailer";
|
||||||
import { TelegrafModule } from "nestjs-telegraf";
|
// import { TelegrafModule } from "nestjs-telegraf";
|
||||||
|
|
||||||
import { bullMqConfig } from "./configs/bullmq.config";
|
import { bullMqConfig } from "./configs/bullmq.config";
|
||||||
import { cacheConfig } from "./configs/cache.config";
|
import { cacheConfig } from "./configs/cache.config";
|
||||||
import { mailerConfig } from "./configs/mailer.config";
|
import { mailerConfig } from "./configs/mailer.config";
|
||||||
import { rateLimitConfig } from "./configs/rateLimit.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 { databaseConfigs } from "./configs/typeorm.config";
|
||||||
import { HTTPLogger } from "./core/middlewares/logger.middleware";
|
import { HTTPLogger } from "./core/middlewares/logger.middleware";
|
||||||
import { AddressModule } from "./modules/address/address.module";
|
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 { ReferralsModule } from "./modules/referrals/referrals.module";
|
||||||
import { SettingModule } from "./modules/settings/settings.module";
|
import { SettingModule } from "./modules/settings/settings.module";
|
||||||
import { SubscriptionsModule } from "./modules/subscriptions/subscriptions.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 { TicketsModule } from "./modules/tickets/tickets.module";
|
||||||
import { UploaderModule } from "./modules/uploader/uploader.module";
|
import { UploaderModule } from "./modules/uploader/uploader.module";
|
||||||
import { UsersModule } from "./modules/users/users.module";
|
import { UsersModule } from "./modules/users/users.module";
|
||||||
import { WalletsModule } from "./modules/wallets/wallets.module";
|
import { WalletsModule } from "./modules/wallets/wallets.module";
|
||||||
import { MonitoringModule } from "./monitoring/monitoring.module";
|
import { MonitoringModule } from "./monitoring/monitoring.module";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
TelegrafModule.forRootAsync(telegrafConfig()),
|
// TelegrafModule.forRootAsync(telegrafConfig()),
|
||||||
MailerModule.forRootAsync(mailerConfig()),
|
MailerModule.forRootAsync(mailerConfig()),
|
||||||
BullModule.forRootAsync(bullMqConfig()),
|
BullModule.forRootAsync(bullMqConfig()),
|
||||||
ThrottlerModule.forRootAsync(rateLimitConfig()),
|
ThrottlerModule.forRootAsync(rateLimitConfig()),
|
||||||
@@ -84,6 +84,7 @@ import { MonitoringModule } from "./monitoring/monitoring.module";
|
|||||||
LandingModule,
|
LandingModule,
|
||||||
ReferralsModule,
|
ReferralsModule,
|
||||||
DiscountsModule,
|
DiscountsModule,
|
||||||
|
SupportPlansModule,
|
||||||
],
|
],
|
||||||
controllers: [],
|
controllers: [],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ export const enum CommonMessage {
|
|||||||
PaymentTypeQueryNotEmpty = "نوع پرداخت نمیتواند خالی باشد",
|
PaymentTypeQueryNotEmpty = "نوع پرداخت نمیتواند خالی باشد",
|
||||||
SEARCH_QUERY_STRING = "رشته جستجو باید یک رشته باشد",
|
SEARCH_QUERY_STRING = "رشته جستجو باید یک رشته باشد",
|
||||||
UPDATED = "با موفقیت آپدیت شد",
|
UPDATED = "با موفقیت آپدیت شد",
|
||||||
|
IS_ACTIVE_SHOULD_BE_1_0 = "وضعیت باید یکی از مقادیر ۰ و ۱ باشد",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum CategoryMessage {
|
export const enum CategoryMessage {
|
||||||
@@ -776,3 +777,32 @@ export const enum ReferralMessage {
|
|||||||
INVALID_OR_INACTIVE_REFERRAL_CODE = "کد ارجاع نامعتبر یا غیرفعال است",
|
INVALID_OR_INACTIVE_REFERRAL_CODE = "کد ارجاع نامعتبر یا غیرفعال است",
|
||||||
USER_ALREADY_REFERRED = "کاربر قبلاً ارجاع داده شده است",
|
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 = "پلن پشتیبانی با موفقیت به روز رسانی شد",
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function databaseConfigs(): TypeOrmModuleAsyncOptions {
|
|||||||
username: configService.getOrThrow<string>("DB_USER"),
|
username: configService.getOrThrow<string>("DB_USER"),
|
||||||
password: configService.getOrThrow<string>("DB_PASS"),
|
password: configService.getOrThrow<string>("DB_PASS"),
|
||||||
autoLoadEntities: true,
|
autoLoadEntities: true,
|
||||||
synchronize: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
|
synchronize: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : false,
|
||||||
logging: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
|
logging: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
|
||||||
migrationsTableName: "typeorm_migrations",
|
migrationsTableName: "typeorm_migrations",
|
||||||
migrationsRun: false,
|
migrationsRun: false,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class CreateAdsDto {
|
|||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
|
|
||||||
@IsNotEmpty({ message: AdsMessage.IMAGE_URL_REQUIRED })
|
@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" })
|
@ApiProperty({ description: "The URL of the image to be displayed", example: "https://test.com/pic.png" })
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
|
|
||||||
|
|||||||
@@ -105,10 +105,10 @@ export class DanakServicesService {
|
|||||||
|
|
||||||
async updateCategory(categoryId: string, updateDto: UpdateDanakServiceCategoryDto) {
|
async updateCategory(categoryId: string, updateDto: UpdateDanakServiceCategoryDto) {
|
||||||
const queryRunner = this.dataSource.createQueryRunner();
|
const queryRunner = this.dataSource.createQueryRunner();
|
||||||
await queryRunner.connect();
|
|
||||||
await queryRunner.startTransaction();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
await queryRunner.connect();
|
||||||
|
await queryRunner.startTransaction();
|
||||||
const category = await queryRunner.manager.findOne(DanakServiceCategory, { where: { id: categoryId } });
|
const category = await queryRunner.manager.findOne(DanakServiceCategory, { where: { id: categoryId } });
|
||||||
if (!category) throw new BadRequestException(CategoryMessage.CATEGORY_NOT_EXIST);
|
if (!category) throw new BadRequestException(CategoryMessage.CATEGORY_NOT_EXIST);
|
||||||
|
|
||||||
@@ -386,6 +386,7 @@ export class DanakServicesService {
|
|||||||
description: true,
|
description: true,
|
||||||
createDate: true,
|
createDate: true,
|
||||||
icon: true,
|
icon: true,
|
||||||
|
slug: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
isDanakSuggest: true,
|
isDanakSuggest: true,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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[];
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { PartialType } from "@nestjs/swagger";
|
||||||
|
|
||||||
|
import { CreateSupportPlanDto } from "./create-support-plan.dto";
|
||||||
|
|
||||||
|
export class UpdateSupportPlanDto extends PartialType(CreateSupportPlanDto) {}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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", // ظرفیت ارسال تیکت
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export enum SupportPlanFeatureType {
|
||||||
|
TEXT = "text",
|
||||||
|
BOOLEAN = "boolean",
|
||||||
|
// NUMBER = "number",
|
||||||
|
// DATE = "date",
|
||||||
|
// TIME = "time",
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export enum UserSupportPlanStatus {
|
||||||
|
ACTIVE = "ACTIVE",
|
||||||
|
INACTIVE = "INACTIVE",
|
||||||
|
CANCELLED = "CANCELLED",
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<SupportPlan> {
|
||||||
|
constructor(@InjectRepository(SupportPlan) supportPlanRepo: Repository<SupportPlan>) {
|
||||||
|
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 } });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<UserSupportPlan> {
|
||||||
|
constructor(@InjectRepository(UserSupportPlan) userSupportPlanRepo: Repository<UserSupportPlan>) {
|
||||||
|
super(userSupportPlanRepo.target, userSupportPlanRepo.manager, userSupportPlanRepo.queryRunner);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -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 {}
|
||||||
@@ -19,4 +19,5 @@ export enum PermissionEnum {
|
|||||||
BANK_ACCOUNTS = "bank_accounts",
|
BANK_ACCOUNTS = "bank_accounts",
|
||||||
PAYMENTS = "payments",
|
PAYMENTS = "payments",
|
||||||
MANAGE_SSO_CLIENTS = "manage_sso_clients",
|
MANAGE_SSO_CLIENTS = "manage_sso_clients",
|
||||||
|
SUPPORT_PLAN = "support_plan",
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user