ticket
This commit is contained in:
Executable
+22
@@ -0,0 +1,22 @@
|
|||||||
|
import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
|
import { Type } from "class-transformer";
|
||||||
|
import { IsNotEmpty, IsNumber, IsOptional, Max, Min } from "class-validator";
|
||||||
|
|
||||||
|
export class PaginationDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsNumber()
|
||||||
|
@Min(1)
|
||||||
|
@Type(() => Number)
|
||||||
|
@ApiPropertyOptional({ type: "number", required: false })
|
||||||
|
page?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsNumber()
|
||||||
|
@Min(1)
|
||||||
|
@Max(50)
|
||||||
|
@Type(() => Number)
|
||||||
|
@ApiPropertyOptional({ type: "number", required: false })
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
Executable
+19
@@ -0,0 +1,19 @@
|
|||||||
|
import { ApiProperty, PartialType } from "@nestjs/swagger";
|
||||||
|
import { IsNotEmpty, IsUUID } from "class-validator";
|
||||||
|
|
||||||
|
import { CommonMessage } from "../enums/message.enum";
|
||||||
|
|
||||||
|
export class ParamDto {
|
||||||
|
@IsNotEmpty({ message: CommonMessage.ID_REQUIRED })
|
||||||
|
@IsUUID("4", { message: CommonMessage.ID_SHOULD_BE_UUID })
|
||||||
|
@ApiProperty({ description: "Id of the entity", example: "8b1e8b1e-8b1e-8b1e-8b1e-8b1e8b1e8b1e" })
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OptionalParamDto extends PartialType(ParamDto) {}
|
||||||
|
|
||||||
|
export class ParamNumberIdDto {
|
||||||
|
@IsNotEmpty({ message: CommonMessage.ID_REQUIRED })
|
||||||
|
@ApiProperty({ description: "Id of the entity", example: "20" })
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
@@ -71,6 +71,54 @@ export const enum AuthMessage {
|
|||||||
INVALID_SLUG = 'اسلاگ نامعتبر است',
|
INVALID_SLUG = 'اسلاگ نامعتبر است',
|
||||||
INVALID_OR_EXPIRED_TOKEN = 'توکن نامعتبر یا منقضی شده است',
|
INVALID_OR_EXPIRED_TOKEN = 'توکن نامعتبر یا منقضی شده است',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum TicketMessageEnum {
|
||||||
|
TITLE_REQUIRED = "عنوان تیکت مورد نیاز است",
|
||||||
|
TITLE_STRING = "عنوان تیکت باید یک رشته باشد",
|
||||||
|
TITLE_LENGTH = "عنوان تیکت باید بین ۳ تا ۱۵۰ کاراکتر باشد",
|
||||||
|
SUBJECT_REQUIRED = "موضوع تیکت اجباری میباشد",
|
||||||
|
SUBJECT_STRING = "موضوع تیکت باید رشته باشد",
|
||||||
|
SUBJECT_LENGTH = "موضوع تیکت باید بین ۳ تا ۱۵۰ کاراکتر باشد",
|
||||||
|
PRIORITY_REQUIRED = "اولویت تیکت اجباری میباشد",
|
||||||
|
PRIORITY_INVALID = "اولویت تیکت باید یکی از مقادیر LOW، MEDIUM یا HIGH باشد",
|
||||||
|
CATEGORY_ID_REQUIRED = "شناسه دستهبندی نمیتواند خالی باشد",
|
||||||
|
CATEGORY_ID_SHOULD_BE_UUID = "شناسه دستهبندی باید یک UUID معتبر باشد",
|
||||||
|
DANAK_SERVICE_ID_REQUIRED = "شناسه سرویس داناک اجباری است",
|
||||||
|
DANAK_SERVICE_ID_SHOULD_BE_UUID = "شناسه سرویس داناک باید یک UUID معتبر باشد",
|
||||||
|
MESSAGE_REQUIRED = "متن پیام تیکت اجباری است",
|
||||||
|
MESSAGE_STRING = "متن پیام باید یک رشته باشد",
|
||||||
|
MESSAGE_LENGTH = "متن پیام باید بین ۵ تا ۵۰۰۰ کاراکتر باشد",
|
||||||
|
CATEGORY_EXIST = "دستهبندی با این نام قبلاً ثبت شده است",
|
||||||
|
CATEGORY_CREATED = "دستهبندی با موفقیت ایجاد شد",
|
||||||
|
CATEGORY_NOT_FOUND = "دستهبندی یافت نشد",
|
||||||
|
USER_GROUP_ID_NOT_EMPTY = "شناسه گروه کاربری نمیتواند خالی باشد",
|
||||||
|
USER_GROUP_ID_SHOULD_BE_UUID = "شناسه گروه کاربری باید یک UUID معتبر باشد",
|
||||||
|
IS_ACTIVE_REQUIRED = "وضعیت تیکت مورد نیاز است",
|
||||||
|
IS_ACTIVE_SHOULD_BE_BOOLEAN = "وضعیت تیکت باید یک مقدار بولی (true/false) باشد",
|
||||||
|
CREATED = "تیکت با موفقیت ساخته شد",
|
||||||
|
DESCRIPTION_REQUIRED = "توضیحات تیکت مورد نیاز است",
|
||||||
|
DESCRIPTION_STRING = "توضیحات تیکت باید یک رشته باشد",
|
||||||
|
DESCRIPTION_LENGTH = "توضیحات تیکت باید بین ۳ تا ۵۰۰ کاراکتر باشد",
|
||||||
|
CONTENT_REQUIRED = "محتوای پیام تیکت مورد نیاز است",
|
||||||
|
ATTACHMENT_REQUIRED = "آدرس فایل پیوست تیکت مورد نیاز است",
|
||||||
|
ATTACHMENT_SHOULD_BE_URL = "آدرس فایل پیوست تیکت باید یوآرال باشد",
|
||||||
|
CONTENT_STRING = "محتوای پیام تیکت باید یک رشته باشد",
|
||||||
|
MESSAGE_CREATED = "پیام تیکت با موفقیت ایجاد شد",
|
||||||
|
TICKET_NOT_FOUND = "تیکت مورد نظر یافت نشد",
|
||||||
|
CLOSED = "تیکت با موفقیت بسته شد",
|
||||||
|
TICKET_CLOSED = "تیکت قبلا بسته شده است",
|
||||||
|
ERROR_IN_TICKET_MSG_CREATION = "خطا در ایجاد پیام تیکت",
|
||||||
|
SERVICE_NOT_FOUND = "سرویس مورد نظر یافت نشد",
|
||||||
|
TICKET_NOT_ASSIGNED = "تیکت به کارشناسی اختصاص داده نشده است",
|
||||||
|
TICKET_NOT_ASSIGNED_TO_USER = "تیکت به شما اختصاص داده نشده است",
|
||||||
|
REFERRED = "REFERRED",
|
||||||
|
NO_ACTIVE_SUPPORT_PLAN = "شما پلن پشتیبانی فعال ندارید",
|
||||||
|
TICKET_LIMIT_NOT_FOUND = "محدودیت تعداد تیکت در پلن پشتیبانی شما یافت نشد",
|
||||||
|
INVALID_TICKET_LIMIT = "محدودیت تعداد تیکت در پلن پشتیبانی شما نامعتبر است",
|
||||||
|
TICKET_LIMIT_EXCEEDED = "شما به محدودیت تعداد تیکت در پلن پشتیبانی خود رسیدهاید",
|
||||||
|
OPENED = "تیکت با موفقیت باز شد",
|
||||||
|
}
|
||||||
|
|
||||||
export const enum productMessage {
|
export const enum productMessage {
|
||||||
NOT_FOUND = 'کالایی با این مشخصات یافت نشد',
|
NOT_FOUND = 'کالایی با این مشخصات یافت نشد',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { OrderStatus, StatusTransitionRef } from '../interface/order.interface';
|
import type { OrderStatus } from '../interface/order.interface';
|
||||||
|
|
||||||
export class OrderCreatedEvent {
|
export class OrderCreatedEvent {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -17,6 +17,5 @@ export class OrderStatusChangedEvent {
|
|||||||
public readonly: string,
|
public readonly: string,
|
||||||
public readonly previousStatus: OrderStatus,
|
public readonly previousStatus: OrderStatus,
|
||||||
public readonly newStatus: OrderStatus,
|
public readonly newStatus: OrderStatus,
|
||||||
public readonly changedBy: StatusTransitionRef,
|
|
||||||
) { }
|
) { }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { User } from '../../user/entities/user.entity';
|
|||||||
|
|
||||||
import { PaymentMethodEnum, PaymentStatusEnum } from '../../payment/interface/payment';
|
import { PaymentMethodEnum, PaymentStatusEnum } from '../../payment/interface/payment';
|
||||||
|
|
||||||
import { PaymentMethod } from '../../payment/entities/payment-method.entity';
|
|
||||||
import { PaymentsService } from '../../payment/services/payments.service';
|
import { PaymentsService } from '../../payment/services/payments.service';
|
||||||
|
|
||||||
import { OrderRepository } from '../repositories/order.repository';
|
import { OrderRepository } from '../repositories/order.repository';
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ import {
|
|||||||
ApiHeader,
|
ApiHeader,
|
||||||
} from '@nestjs/swagger';
|
} from '@nestjs/swagger';
|
||||||
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
||||||
import { PaymentMethodService } from '../services/payment-method.service';
|
import { CreatePaymentMethodDto } from '../dto/create-payment-method.dto';
|
||||||
import { CreatePaymentMethodDto } from '../dto/create-payment-method.dto';
|
|
||||||
import { UpdatePaymentMethodDto } from '../dto/update-payment-method.dto';
|
import { UpdatePaymentMethodDto } from '../dto/update-payment-method.dto';
|
||||||
import { VerifyPaymentDto } from '../dto/verify-payment.dto';
|
import { VerifyPaymentDto } from '../dto/verify-payment.dto';
|
||||||
import { PaymentChartDto } from '../dto/payment-chart.dto';
|
import { PaymentChartDto } from '../dto/payment-chart.dto';
|
||||||
@@ -26,31 +25,30 @@ import { Permission } from 'src/common/enums/permission.enum';
|
|||||||
export class PaymentsController {
|
export class PaymentsController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly paymentsService: PaymentsService,
|
private readonly paymentsService: PaymentsService,
|
||||||
private readonly paymentMethodService: PaymentMethodService,
|
) { }
|
||||||
) { }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@UseGuards(AuthGuard)
|
// @UseGuards(AuthGuard)
|
||||||
@ApiBearerAuth()
|
// @ApiBearerAuth()
|
||||||
@Get('public/payments/pay-order/:orderId')
|
// @Get('public/payments/pay-order/:orderId')
|
||||||
@ApiOperation({ summary: 'Pay for an order' })
|
// @ApiOperation({ summary: 'Pay for an order' })
|
||||||
@ApiParam({ name: 'orderId', type: 'string', description: 'Order ID' })
|
// @ApiParam({ name: 'orderId', type: 'string', description: 'Order ID' })
|
||||||
|
|
||||||
payAnOrder(@Param('orderId') orderId: string) {
|
// payAnOrder(@Param('orderId') orderId: string) {
|
||||||
return this.paymentsService.payOrder(orderId);
|
// return this.paymentsService.payOrder(orderId);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Post('public/payments/verify')
|
// @Post('public/payments/verify')
|
||||||
@ApiOperation({ summary: 'Verify a payment' })
|
// @ApiOperation({ summary: 'Verify a payment' })
|
||||||
|
|
||||||
@ApiBody({ type: VerifyPaymentDto })
|
// @ApiBody({ type: VerifyPaymentDto })
|
||||||
@ApiNotFoundResponse({ description: 'Payment not found' })
|
// @ApiNotFoundResponse({ description: 'Payment not found' })
|
||||||
verifyPayment(@Body() verifyPaymentDto: VerifyPaymentDto) {
|
// verifyPayment(@Body() verifyPaymentDto: VerifyPaymentDto) {
|
||||||
return this.paymentsService.verifyOnlinePayment(verifyPaymentDto.authority, verifyPaymentDto.orderId);
|
// return this.paymentsService.verifyOnlinePayment(verifyPaymentDto.authority, verifyPaymentDto.orderId);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ApiProperty } from "@nestjs/swagger";
|
|||||||
import { ArrayMinSize, IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length } from "class-validator";
|
import { ArrayMinSize, IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length } from "class-validator";
|
||||||
|
|
||||||
import { TicketMessageEnum, UserMessage } from "../../../common/enums/message.enum";
|
import { TicketMessageEnum, UserMessage } from "../../../common/enums/message.enum";
|
||||||
import { TicketPriority } from "../enums/ticket-priority.enum";
|
|
||||||
|
|
||||||
export class CreateTicketDto {
|
export class CreateTicketDto {
|
||||||
// @IsNotEmpty({ message: TicketMessageEnum.TITLE_REQUIRED })
|
// @IsNotEmpty({ message: TicketMessageEnum.TITLE_REQUIRED })
|
||||||
@@ -17,10 +17,6 @@ export class CreateTicketDto {
|
|||||||
@ApiProperty({ description: "Subject of the ticket", example: "Subject" })
|
@ApiProperty({ description: "Subject of the ticket", example: "Subject" })
|
||||||
subject: string;
|
subject: string;
|
||||||
|
|
||||||
@IsNotEmpty({ message: TicketMessageEnum.PRIORITY_REQUIRED })
|
|
||||||
@IsEnum(TicketPriority, { message: TicketMessageEnum.PRIORITY_INVALID })
|
|
||||||
@ApiProperty({ enum: TicketPriority, example: TicketPriority.LOW, description: "LOW, MEDIUM, HIGH" })
|
|
||||||
priority: TicketPriority;
|
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsNotEmpty({ message: TicketMessageEnum.DANAK_SERVICE_ID_REQUIRED })
|
@IsNotEmpty({ message: TicketMessageEnum.DANAK_SERVICE_ID_REQUIRED })
|
||||||
@@ -48,8 +44,8 @@ export class CreateTicketDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class CreateTicketByAdminDto extends CreateTicketDto {
|
export class CreateTicketByAdminDto extends CreateTicketDto {
|
||||||
@IsNotEmpty({ message: UserMessage.USER_ID_REQUIRED })
|
@IsNotEmpty({ message: "UserMessage.USER_ID_REQUIRED" })
|
||||||
@IsUUID("4", { message: UserMessage.USER_ID_SHOULD_BE_A_UUID })
|
@IsUUID("4", { message:" UserMessage.USER_ID_SHOULD_BE_A_UUID" })
|
||||||
@ApiProperty({ description: "User ID of the ticket", example: "123e4567-e89b-12d3-a456-426614174000" })
|
@ApiProperty({ description: "User ID of the ticket", example: "123e4567-e89b-12d3-a456-426614174000" })
|
||||||
userId: string;
|
userId: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
import { CategoryListSearchQueryDto } from "../../danak-services/DTO/category-search-query.dto";
|
|
||||||
|
|
||||||
export class SearchTicketCategoryDto extends CategoryListSearchQueryDto {}
|
|
||||||
|
export class SearchTicketCategoryDto {}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export class SearchTicketQueryDto extends PaginationDto {
|
|||||||
status?: TicketStatus;
|
status?: TicketStatus;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsUUID("4", { message: UserMessage.USER_ID_SHOULD_BE_A_UUID })
|
@IsUUID("4", { message:" UserMessage.USER_ID_SHOULD_BE_A_UUID" })
|
||||||
@ApiPropertyOptional({ description: "user id", example: "123e4567-e89b-12d3-a456-426614174000" })
|
@ApiPropertyOptional({ description: "user id", example: "123e4567-e89b-12d3-a456-426614174000" })
|
||||||
userId?: string;
|
userId?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
|||||||
import { Injectable } from "@nestjs/common";
|
|
||||||
import { InjectRepository } from "@nestjs/typeorm";
|
|
||||||
import { Repository } from "typeorm";
|
|
||||||
|
|
||||||
import { TicketMessage } from "../entities/ticket-message.entity";
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class TicketMessagesRepository extends Repository<TicketMessage> {
|
|
||||||
constructor(@InjectRepository(TicketMessage) ticketMessagesRepository: Repository<TicketMessage>) {
|
|
||||||
super(ticketMessagesRepository.target, ticketMessagesRepository.manager, ticketMessagesRepository.queryRunner);
|
|
||||||
}
|
|
||||||
|
|
||||||
async findMessagesByTicketId(ticketId: string): Promise<TicketMessage[]> {
|
|
||||||
return this.find({
|
|
||||||
relations: {
|
|
||||||
author: {
|
|
||||||
roles: true,
|
|
||||||
},
|
|
||||||
attachments: true,
|
|
||||||
// ticket: true,
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
content: true,
|
|
||||||
attachments: {
|
|
||||||
id: true,
|
|
||||||
attachmentUrl: true,
|
|
||||||
},
|
|
||||||
createdAt: true,
|
|
||||||
author: {
|
|
||||||
id: true,
|
|
||||||
firstName: true,
|
|
||||||
lastName: true,
|
|
||||||
roles: {
|
|
||||||
id: true,
|
|
||||||
name: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
where: { ticket: { id: ticketId } },
|
|
||||||
order: { createdAt: "ASC" },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +1,13 @@
|
|||||||
import { Injectable } from "@nestjs/common";
|
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
|
||||||
import { InjectRepository } from "@nestjs/typeorm";
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Repository } from "typeorm";
|
import { Ticket } from '../entities/ticket.entity';
|
||||||
|
|
||||||
import { PaginationUtils } from "../../utils/providers/pagination.utils";
|
|
||||||
import { SearchTicketQueryDto } from "../DTO/search-ticket-query.dto";
|
|
||||||
import { Ticket } from "../entities/ticket.entity";
|
|
||||||
import { TicketStatus } from "../enums/ticket-status.enum";
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TicketsRepository extends Repository<Ticket> {
|
export class TicketRepository extends EntityRepository<Ticket> {
|
||||||
constructor(@InjectRepository(Ticket) ticketsRepository: Repository<Ticket>) {
|
constructor(
|
||||||
super(ticketsRepository.target, ticketsRepository.manager, ticketsRepository.queryRunner);
|
readonly em: EntityManager,
|
||||||
|
) {
|
||||||
|
super(em, Ticket);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findTicketById(id: string, userId?: string): Promise<Ticket | null> {
|
|
||||||
return this.findOne({
|
|
||||||
where: { id, ...(userId && { user: { id: userId } }) },
|
|
||||||
relations: {
|
|
||||||
user: true,
|
|
||||||
category: true,
|
|
||||||
assignedTo: true,
|
|
||||||
danakService: true,
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
numericId: true,
|
|
||||||
danakService: {
|
|
||||||
id: true,
|
|
||||||
name: true,
|
|
||||||
},
|
|
||||||
subject: true,
|
|
||||||
priority: true,
|
|
||||||
status: true,
|
|
||||||
createdAt: true,
|
|
||||||
user: {
|
|
||||||
id: true,
|
|
||||||
firstName: true,
|
|
||||||
lastName: true,
|
|
||||||
},
|
|
||||||
assignedTo: {
|
|
||||||
id: true,
|
|
||||||
firstName: true,
|
|
||||||
lastName: true,
|
|
||||||
},
|
|
||||||
category: {
|
|
||||||
id: true,
|
|
||||||
title: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//******************************** */
|
|
||||||
|
|
||||||
async findTicketsList(queryDto: SearchTicketQueryDto, userId: string) {
|
|
||||||
const { limit, skip } = PaginationUtils(queryDto);
|
|
||||||
|
|
||||||
const queryBuilder = this.createQueryBuilder("ticket")
|
|
||||||
.leftJoinAndSelect("ticket.category", "category")
|
|
||||||
.leftJoinAndSelect("ticket.danakService", "danakService")
|
|
||||||
.where("ticket.user.id = :userId", { userId })
|
|
||||||
.orderBy("ticket.createdAt", "DESC")
|
|
||||||
.select([
|
|
||||||
"ticket.id",
|
|
||||||
"ticket.subject",
|
|
||||||
"ticket.status",
|
|
||||||
"ticket.priority",
|
|
||||||
"ticket.numericId",
|
|
||||||
"ticket.createdAt",
|
|
||||||
"ticket.updatedAt",
|
|
||||||
"category.id",
|
|
||||||
"category.title",
|
|
||||||
"danakService.id",
|
|
||||||
"danakService.name",
|
|
||||||
])
|
|
||||||
.skip(skip)
|
|
||||||
.take(limit);
|
|
||||||
|
|
||||||
if (queryDto.status) {
|
|
||||||
queryBuilder.andWhere("ticket.status = :status", {
|
|
||||||
status: queryDto.status,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
queryBuilder.andWhere("ticket.status != :closedStatus", {
|
|
||||||
closedStatus: TicketStatus.CLOSED,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return queryBuilder.getManyAndCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
async findTicketsListForAdmin(queryDto: SearchTicketQueryDto, adminId: string, isSuperAdmin: boolean) {
|
|
||||||
const { limit, skip } = PaginationUtils(queryDto);
|
|
||||||
|
|
||||||
const queryBuilder = this.createQueryBuilder("ticket")
|
|
||||||
.leftJoinAndSelect("ticket.category", "category")
|
|
||||||
.leftJoinAndSelect("ticket.user", "user")
|
|
||||||
.leftJoinAndSelect("ticket.assignedTo", "assignedTo")
|
|
||||||
.leftJoinAndSelect("ticket.danakService", "danakService")
|
|
||||||
.orderBy("ticket.createdAt", "DESC");
|
|
||||||
|
|
||||||
if (!isSuperAdmin) {
|
|
||||||
queryBuilder.andWhere("ticket.assignedToId = :adminId", { adminId });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryDto.status) {
|
|
||||||
queryBuilder.andWhere("ticket.status = :status", {
|
|
||||||
status: queryDto.status,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryDto.userId) {
|
|
||||||
queryBuilder.andWhere("user.id = :userId", { userId: queryDto.userId });
|
|
||||||
}
|
|
||||||
|
|
||||||
return queryBuilder.skip(skip).take(limit).getManyAndCount();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,137 +4,133 @@ import { ApiOperation, ApiTags } from "@nestjs/swagger";
|
|||||||
import { CreateTicketCategoryDto } from "./DTO/create-ticket-category.dto";
|
import { CreateTicketCategoryDto } from "./DTO/create-ticket-category.dto";
|
||||||
import { CreateTicketMessageDto } from "./DTO/create-ticket-message.dto";
|
import { CreateTicketMessageDto } from "./DTO/create-ticket-message.dto";
|
||||||
import { CreateTicketByAdminDto, CreateTicketDto } from "./DTO/create-ticket.dto";
|
import { CreateTicketByAdminDto, CreateTicketDto } from "./DTO/create-ticket.dto";
|
||||||
import { ReferTicketDto } from "./DTO/refer-ticket.dto";
|
import { SearchTicketCategoryDto } from "./DTO/search-ticket-category.dto";
|
||||||
import { SearchTicketCategoryDto } from "./DTO/search-ticket-category.dto";
|
|
||||||
import { SearchTicketQueryDto } from "./DTO/search-ticket-query.dto";
|
import { SearchTicketQueryDto } from "./DTO/search-ticket-query.dto";
|
||||||
import { UpdateTicketCategoryDto } from "./DTO/update-ticket-category.dto";
|
import { TicketsService } from "./providers/tickets.service";
|
||||||
import { TicketsService } from "./providers/tickets.service";
|
|
||||||
import { AdminRoute } from "../../common/decorators/admin.decorator";
|
|
||||||
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
|
||||||
import { PermissionsDec } from "../../common/decorators/permission.decorator";
|
|
||||||
import { IUserIpAndHeaders, UserDec, UserIpAndHeaders } from "../../common/decorators/user.decorator";
|
|
||||||
import { ParamDto } from "../../common/DTO/param.dto";
|
import { ParamDto } from "../../common/DTO/param.dto";
|
||||||
import { PermissionEnum } from "../users/enums/permission.enum";
|
|
||||||
@Controller("tickets")
|
@Controller("tickets")
|
||||||
@ApiTags("Tickets")
|
@ApiTags("Tickets")
|
||||||
@AuthGuards()
|
|
||||||
export class TicketsController {
|
export class TicketsController {
|
||||||
constructor(private readonly ticketsService: TicketsService) {}
|
constructor(private readonly ticketsService: TicketsService) {}
|
||||||
|
|
||||||
//----------------- ticket categories -------------------
|
//----------------- ticket categories -------------------
|
||||||
|
|
||||||
@ApiOperation({ summary: "Create ticket category => admin route" })
|
// @ApiOperation({ summary: "Create ticket category => admin route" })
|
||||||
@PermissionsDec(PermissionEnum.TICKETS)
|
// @PermissionsDec(PermissionEnum.TICKETS)
|
||||||
@Post("category")
|
// @Post("category")
|
||||||
createTicketCategory(@Body() createDto: CreateTicketCategoryDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
// createTicketCategory(@Body() createDto: CreateTicketCategoryDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
return this.ticketsService.createTicketCategory(createDto, userIpAndHeaders);
|
// return this.ticketsService.createTicketCategory(createDto, userIpAndHeaders);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({ summary: "Update ticket category => admin route" })
|
// @ApiOperation({ summary: "Update ticket category => admin route" })
|
||||||
@PermissionsDec(PermissionEnum.TICKETS)
|
// @PermissionsDec(PermissionEnum.TICKETS)
|
||||||
@Patch("category/:id")
|
// @Patch("category/:id")
|
||||||
updateCategory(
|
// updateCategory(
|
||||||
@Param() paramDto: ParamDto,
|
// @Param() paramDto: ParamDto,
|
||||||
@Body() updateCategoryDto: UpdateTicketCategoryDto,
|
// @Body() updateCategoryDto: UpdateTicketCategoryDto,
|
||||||
@UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders,
|
// @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders,
|
||||||
) {
|
// ) {
|
||||||
return this.ticketsService.updateCategory(paramDto, updateCategoryDto, userIpAndHeaders);
|
// return this.ticketsService.updateCategory(paramDto, updateCategoryDto, userIpAndHeaders);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({ summary: "Get ticket categories " })
|
// @ApiOperation({ summary: "Get ticket categories " })
|
||||||
@Get("categories")
|
// @Get("categories")
|
||||||
getTicketCategories() {
|
// getTicketCategories() {
|
||||||
return this.ticketsService.getTicketCategories();
|
// return this.ticketsService.getTicketCategories();
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({ summary: "Get ticket categories list => admin route" })
|
// @ApiOperation({ summary: "Get ticket categories list => admin route" })
|
||||||
@AdminRoute()
|
// @AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.TICKETS)
|
// @PermissionsDec(PermissionEnum.TICKETS)
|
||||||
@Get("categories-list")
|
// @Get("categories-list")
|
||||||
getCategoriesList(@Query() queryDto: SearchTicketCategoryDto) {
|
// getCategoriesList(@Query() queryDto: SearchTicketCategoryDto) {
|
||||||
return this.ticketsService.getCategoriesList(queryDto);
|
// return this.ticketsService.getCategoriesList(queryDto);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({ summary: "toggle status of categories => admin route" })
|
// @ApiOperation({ summary: "toggle status of categories => admin route" })
|
||||||
@AdminRoute()
|
// @AdminRoute()
|
||||||
@HttpCode(HttpStatus.OK)
|
// @HttpCode(HttpStatus.OK)
|
||||||
@PermissionsDec(PermissionEnum.TICKETS)
|
// @PermissionsDec(PermissionEnum.TICKETS)
|
||||||
@Post("category/toggle-status/:id")
|
// @Post("category/toggle-status/:id")
|
||||||
toggleCategoryStatus(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
// toggleCategoryStatus(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
return this.ticketsService.toggleCategoryStatus(paramDto, userIpAndHeaders);
|
// return this.ticketsService.toggleCategoryStatus(paramDto, userIpAndHeaders);
|
||||||
}
|
// }
|
||||||
|
|
||||||
//----------------- ticket and ticket messages -------------------
|
//----------------- ticket and ticket messages -------------------
|
||||||
|
|
||||||
@ApiOperation({ summary: "create ticket ==> user route" })
|
// @ApiOperation({ summary: "create ticket ==> user route" })
|
||||||
@Post()
|
// @Post()
|
||||||
createTicket(@Body() createDto: CreateTicketDto, @UserDec("id") userId: string) {
|
// createTicket(@Body() createDto: CreateTicketDto, @UserDec("id") userId: string) {
|
||||||
return this.ticketsService.createTicket(createDto, userId);
|
// return this.ticketsService.createTicket(createDto, userId);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({ summary: "create ticket by admin" })
|
// @ApiOperation({ summary: "create ticket by admin" })
|
||||||
@AdminRoute()
|
// @AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.TICKETS)
|
// @PermissionsDec(PermissionEnum.TICKETS)
|
||||||
@Post("admin")
|
// @Post("admin")
|
||||||
createTicketByAdmin(@Body() createDto: CreateTicketByAdminDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
// createTicketByAdmin(@Body() createDto: CreateTicketByAdminDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
return this.ticketsService.createTicketByAdmin(createDto, userIpAndHeaders);
|
// return this.ticketsService.createTicketByAdmin(createDto, userIpAndHeaders);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({ summary: "Get all tickets of user ==> user route" })
|
// @ApiOperation({ summary: "Get all tickets of user ==> user route" })
|
||||||
@Get()
|
// @Get()
|
||||||
getTickets(@Query() queryDto: SearchTicketQueryDto, @UserDec("id") userId: string) {
|
// getTickets(@Query() queryDto: SearchTicketQueryDto, @UserDec("id") userId: string) {
|
||||||
return this.ticketsService.getTickets(queryDto, userId);
|
// return this.ticketsService.getTickets(queryDto, userId);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({ summary: "Get all tickets ==> admin route" })
|
// @ApiOperation({ summary: "Get all tickets ==> admin route" })
|
||||||
@AdminRoute()
|
// @AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.TICKETS)
|
// @PermissionsDec(PermissionEnum.TICKETS)
|
||||||
@Get("admin")
|
// @Get("admin")
|
||||||
getAllTickets(@Query() queryDto: SearchTicketQueryDto, @UserDec("id") adminId: string) {
|
// getAllTickets(@Query() queryDto: SearchTicketQueryDto, @UserDec("id") adminId: string) {
|
||||||
return this.ticketsService.getTicketForAdmin(queryDto, adminId);
|
// return this.ticketsService.getTicketForAdmin(queryDto, adminId);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({ summary: "create ticket messages" })
|
// @ApiOperation({ summary: "create ticket messages" })
|
||||||
@Post(":id/messages")
|
// @Post(":id/messages")
|
||||||
createTicketMessage(
|
// createTicketMessage(
|
||||||
@Param() paramDto: ParamDto,
|
// @Param() paramDto: ParamDto,
|
||||||
@Body() createDto: CreateTicketMessageDto,
|
// @Body() createDto: CreateTicketMessageDto,
|
||||||
@UserDec("id") userId: string,
|
// @UserDec("id") userId: string,
|
||||||
@UserDec("isAdmin") isAdmin: boolean,
|
// @UserDec("isAdmin") isAdmin: boolean,
|
||||||
) {
|
// ) {
|
||||||
return this.ticketsService.createTicketMessage(paramDto.id, createDto, userId, isAdmin);
|
// return this.ticketsService.createTicketMessage(paramDto.id, createDto, userId, isAdmin);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({ summary: "Get all ticket messages of user" })
|
// @ApiOperation({ summary: "Get all ticket messages of user" })
|
||||||
@Get(":id/messages")
|
// @Get(":id/messages")
|
||||||
getTicketMessages(@Param() paramDto: ParamDto, @UserDec("id") userId: string, @UserDec("isAdmin") isAdmin: boolean) {
|
// getTicketMessages(@Param() paramDto: ParamDto, @UserDec("id") userId: string, @UserDec("isAdmin") isAdmin: boolean) {
|
||||||
return this.ticketsService.getTicketMessages(paramDto.id, userId, isAdmin);
|
// return this.ticketsService.getTicketMessages(paramDto.id, userId, isAdmin);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({ summary: "close ticket by user or admin" })
|
// @ApiOperation({ summary: "close ticket by user or admin" })
|
||||||
@HttpCode(HttpStatus.OK)
|
// @HttpCode(HttpStatus.OK)
|
||||||
@Post(":id/close")
|
// @Post(":id/close")
|
||||||
closedTicketByUser(
|
// closedTicketByUser(
|
||||||
@Param() paramDto: ParamDto,
|
// @Param() paramDto: ParamDto,
|
||||||
@UserDec("isAdmin") isAdmin: boolean,
|
// @UserDec("isAdmin") isAdmin: boolean,
|
||||||
@UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders,
|
// @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders,
|
||||||
) {
|
// ) {
|
||||||
return this.ticketsService.closeTicketByUser(paramDto.id, isAdmin, userIpAndHeaders);
|
// return this.ticketsService.closeTicketByUser(paramDto.id, isAdmin, userIpAndHeaders);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({ summary: "open ticket by admin" })
|
// @ApiOperation({ summary: "open ticket by admin" })
|
||||||
@AdminRoute()
|
// @AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.TICKETS)
|
// @PermissionsDec(PermissionEnum.TICKETS)
|
||||||
@Post(":id/open")
|
// @Post(":id/open")
|
||||||
openTicketByAdmin(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
// openTicketByAdmin(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
return this.ticketsService.openTicketByAdmin(paramDto.id, userIpAndHeaders);
|
// return this.ticketsService.openTicketByAdmin(paramDto.id, userIpAndHeaders);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({ summary: "referer ticket ==> admin route" })
|
// @ApiOperation({ summary: "referer ticket ==> admin route" })
|
||||||
@AdminRoute()
|
// @AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.TICKETS)
|
// @PermissionsDec(PermissionEnum.TICKETS)
|
||||||
@Post(":id/refer")
|
// @Post(":id/refer")
|
||||||
referTicket(@Param() paramDto: ParamDto, @Body() referDto: ReferTicketDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
// referTicket(@Param() paramDto: ParamDto, @Body() referDto: ReferTicketDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
return this.ticketsService.referTicket(paramDto.id, referDto, userIpAndHeaders);
|
// return this.ticketsService.referTicket(paramDto.id, referDto, userIpAndHeaders);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import { Ticket } from "./entities/ticket.entity";
|
|||||||
|
|
||||||
import { TicketsService } from "./providers/tickets.service";
|
import { TicketsService } from "./providers/tickets.service";
|
||||||
|
|
||||||
import { TicketMessagesRepository } from "./repositories/tickets-message.repository";
|
import { TicketRepository } from "./repositories/tickets.repository";
|
||||||
import { TicketsRepository } from "./repositories/tickets.repository";
|
|
||||||
import { TicketsController } from "./tickets.controller";
|
import { TicketsController } from "./tickets.controller";
|
||||||
import { MikroOrmModule } from "@mikro-orm/nestjs";
|
import { MikroOrmModule } from "@mikro-orm/nestjs";
|
||||||
|
|
||||||
@@ -18,7 +17,7 @@ import { MikroOrmModule } from "@mikro-orm/nestjs";
|
|||||||
MikroOrmModule.forFeature([Ticket]),
|
MikroOrmModule.forFeature([Ticket]),
|
||||||
BullModule.registerQueue({ name: TICKET.QUEUE_NAME }),
|
BullModule.registerQueue({ name: TICKET.QUEUE_NAME }),
|
||||||
],
|
],
|
||||||
providers: [TicketsService, TicketsRepository, TicketMessagesRepository],
|
providers: [TicketsService, TicketRepository],
|
||||||
controllers: [TicketsController],
|
controllers: [TicketsController],
|
||||||
exports: [TicketsService],
|
exports: [TicketsService],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { IsOptional, IsString, IsNumber, Min, IsIn, IsDateString } from 'class-validator';
|
import { IsOptional, IsString, IsNumber, Min, IsIn, IsDateString } from 'class-validator';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
import { WalletTransactionType, WalletTransactionReason } from '../interface/wallet';
|
|
||||||
|
|
||||||
// Define the valid sort directions
|
// Define the valid sort directions
|
||||||
const sortOrderOptions = ['asc', 'desc'] as const;
|
const sortOrderOptions = ['asc', 'desc'] as const;
|
||||||
@@ -40,27 +39,12 @@ export class FindWalletTransactionsDto {
|
|||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
limit?: number = 10;
|
limit?: number = 10;
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter by transaction type
|
|
||||||
*/
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
description: 'Transaction type filter',
|
|
||||||
enum: WalletTransactionType,
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsIn(Object.values(WalletTransactionType))
|
|
||||||
type?: WalletTransactionType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter by transaction reason
|
* Filter by transaction reason
|
||||||
*/
|
*/
|
||||||
@ApiPropertyOptional({
|
|
||||||
description: 'Transaction reason filter',
|
|
||||||
enum: WalletTransactionReason,
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsIn(Object.values(WalletTransactionReason))
|
|
||||||
reason?: WalletTransactionReason;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter transactions from this date onwards
|
* Filter transactions from this date onwards
|
||||||
|
|||||||
@@ -18,10 +18,9 @@ export class UsersSeeder {
|
|||||||
phone: normalizedPhone, // Use normalized phone
|
phone: normalizedPhone, // Use normalized phone
|
||||||
firstName: userData.firstName,
|
firstName: userData.firstName,
|
||||||
lastName: userData.lastName,
|
lastName: userData.lastName,
|
||||||
birthDate: new Date(userData.birthDate),
|
|
||||||
marriageDate: new Date(userData.marriageDate),
|
|
||||||
isActive: userData.isActive,
|
isActive: userData.isActive,
|
||||||
gender: userData.gender,
|
gender: userData.gender,
|
||||||
|
maxCredit: 0
|
||||||
});
|
});
|
||||||
em.persist(user);
|
em.persist(user);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user