chore: add service list fetch for admin with filters

This commit is contained in:
mahyargdz
2025-01-29 14:43:48 +03:30
parent 7ad744930b
commit b1d0c3c882
7 changed files with 70 additions and 37 deletions
@@ -1,9 +1,9 @@
import { ApiPropertyOptional } from "@nestjs/swagger"; import { ApiPropertyOptional } from "@nestjs/swagger";
import { IsEnum, IsOptional, IsString } from "class-validator"; import { Type } from "class-transformer";
import { IsOptional, IsString } from "class-validator";
import { PaginationDto } from "../../../common/DTO/pagination.dto"; import { PaginationDto } from "../../../common/DTO/pagination.dto";
import { CategoryMessage } from "../../../common/enums/message.enum"; import { CategoryMessage } from "../../../common/enums/message.enum";
import { ServiceStatus } from "../enums/service-status.enum";
export class DanakServicesSearchQueryDto extends PaginationDto { export class DanakServicesSearchQueryDto extends PaginationDto {
@IsOptional() @IsOptional()
@@ -12,12 +12,18 @@ export class DanakServicesSearchQueryDto extends PaginationDto {
categoryId?: string; categoryId?: string;
@IsOptional() @IsOptional()
@IsEnum(ServiceStatus) @Type(() => Number)
@ApiPropertyOptional({ description: "services status", enum: ServiceStatus, example: ServiceStatus.ACTIVE }) // @IsBoolean({ message: CategoryMessage.IS_ACTIVE_SHOULD_BE_BOOLEAN })
status?: ServiceStatus; @ApiPropertyOptional({ description: "service status", example: 1 })
isActive?: number;
@IsOptional() @IsOptional()
@IsString({ message: CategoryMessage.SEARCH_QUERY_STRING }) @IsString({ message: CategoryMessage.SEARCH_QUERY_STRING })
@ApiPropertyOptional({ description: "Search query", example: "search query" }) @ApiPropertyOptional({ description: "Search query", example: "search query" })
q?: string; q?: string;
@IsOptional()
@Type(() => Number)
@ApiPropertyOptional({ description: "is danak suggest this service", example: 1 })
isDanakSuggest?: number;
} }
@@ -70,6 +70,15 @@ export class DanakServicesController {
return this.danakServicesService.getServicesList(queryDto); return this.danakServicesService.getServicesList(queryDto);
} }
@AuthGuards()
@Roles(RoleEnum.ADMIN)
@ApiOperation({ summary: "toggle status of danak service => admin route" })
@HttpCode(HttpStatus.OK)
@Post("toggle-status/:id")
toggleServiceStatus(@Param() paramDto: ParamDto) {
return this.danakServicesService.toggleServiceStatus(paramDto);
}
@AuthGuards() @AuthGuards()
@Roles(RoleEnum.USER) @Roles(RoleEnum.USER)
@ApiOperation({ summary: "get all user purchased danak services" }) @ApiOperation({ summary: "get all user purchased danak services" })
@@ -6,7 +6,6 @@ import { BaseEntity } from "../../../common/entities/base.entity";
import { Announcement } from "../../announcements/entities/announcement.entity"; import { Announcement } from "../../announcements/entities/announcement.entity";
import { Ticket } from "../../tickets/entities/ticket.entity"; import { Ticket } from "../../tickets/entities/ticket.entity";
import { User } from "../../users/entities/user.entity"; import { User } from "../../users/entities/user.entity";
import { ServiceStatus } from "../enums/service-status.enum";
import { ServicesLanguage } from "../enums/services-language.enum"; import { ServicesLanguage } from "../enums/services-language.enum";
@Entity() @Entity()
@@ -38,8 +37,8 @@ export class DanakService extends BaseEntity {
@Column({ type: "text", nullable: false }) @Column({ type: "text", nullable: false })
metaDescription: string; metaDescription: string;
@Column({ type: "enum", enum: ServiceStatus, nullable: false, default: ServiceStatus.IN_ACTIVE }) @Column({ type: "boolean", default: false })
status: ServiceStatus; isActive: boolean;
@Column({ type: "varchar", length: 255, nullable: false }) @Column({ type: "varchar", length: 255, nullable: false })
link: string; link: string;
@@ -100,6 +100,17 @@ export class DanakServicesService {
isActive: category.isActive, isActive: category.isActive,
}; };
} }
async toggleServiceStatus(paramDto: ParamDto) {
const service = await this.danakServicesRepository.findServiceById(paramDto.id);
if (!service) throw new BadRequestException(ServiceMessage.SERVICE_NOT_EXIST);
service.isActive = !service.isActive;
await this.danakServicesRepository.save(service);
return {
message: CommonMessage.UPDATE_SUCCESS,
isActive: service.isActive,
};
}
/******************************************** */ /******************************************** */
async createService(createDto: CreateServiceDto) { async createService(createDto: CreateServiceDto) {
const category = await this.danakServicesCategoryRepository.findOneById(createDto.categoryId); const category = await this.danakServicesCategoryRepository.findOneById(createDto.categoryId);
@@ -131,9 +142,14 @@ export class DanakServicesService {
.skip(skip) .skip(skip)
.take(limit); .take(limit);
if (queryDto.status) { if (queryDto.isActive) {
queryBuilder.andWhere("service.status = :status", { status: queryDto.status }); queryBuilder.andWhere("service.isActive = :isActive", { isActive: queryDto.isActive === 1 });
} }
if (queryDto.isDanakSuggest) {
queryBuilder.andWhere("service.isDanakSuggest = :isDanakSuggest", { isDanakSuggest: queryDto.isDanakSuggest === 1 });
}
if (queryDto.categoryId) { if (queryDto.categoryId) {
queryBuilder.andWhere("service.categoryId = :categoryId", { categoryId: queryDto.categoryId }); queryBuilder.andWhere("service.categoryId = :categoryId", { categoryId: queryDto.categoryId });
} }
@@ -13,4 +13,8 @@ export class DanakServicesRepository extends Repository<DanakService> {
async findOneByName(name: string): Promise<DanakService | null> { async findOneByName(name: string): Promise<DanakService | null> {
return this.findOneBy({ name }); return this.findOneBy({ name });
} }
async findServiceById(id: string): Promise<DanakService | null> {
return this.findOneBy({ id });
}
} }
@@ -0,0 +1,20 @@
import { ApiProperty } from "@nestjs/swagger";
import { IFile } from "../../utils/interfaces/IFile";
export class UploadSingleFileDto {
@ApiProperty({ type: "string", format: "binary", nullable: false })
file: IFile;
}
export class UploadMultipleFileDto {
@ApiProperty({
required: true,
type: "array",
items: {
type: "string",
format: "binary",
},
})
files: IFile[];
}
+6 -27
View File
@@ -2,7 +2,9 @@ import { FileInterceptor, FilesInterceptor } from "@nest-lab/fastify-multer";
import { Controller, Post, UploadedFile, UploadedFiles, UseInterceptors } from "@nestjs/common"; import { Controller, Post, UploadedFile, UploadedFiles, UseInterceptors } from "@nestjs/common";
import { ApiBody, ApiConsumes, ApiOperation, ApiTags } from "@nestjs/swagger"; import { ApiBody, ApiConsumes, ApiOperation, ApiTags } from "@nestjs/swagger";
import { UploadMultipleFileDto, UploadSingleFileDto } from "./DTO/upload-file.dto";
import { UploaderService } from "./uploader.service"; import { UploaderService } from "./uploader.service";
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { IFile } from "../utils/interfaces/IFile"; import { IFile } from "../utils/interfaces/IFile";
@ApiTags("Uploader") @ApiTags("Uploader")
@@ -10,45 +12,22 @@ import { IFile } from "../utils/interfaces/IFile";
export class UploaderController { export class UploaderController {
constructor(private readonly uploaderService: UploaderService) {} constructor(private readonly uploaderService: UploaderService) {}
@AuthGuards()
@ApiOperation({ summary: "Uploads a single file" }) @ApiOperation({ summary: "Uploads a single file" })
@ApiConsumes("multipart/form-data") @ApiConsumes("multipart/form-data")
@UseInterceptors(FileInterceptor("file")) @UseInterceptors(FileInterceptor("file"))
@ApiBody({ @ApiBody({ type: UploadSingleFileDto })
required: true,
schema: {
type: "object",
properties: {
file: {
type: "string",
format: "binary",
},
},
},
})
@Post("single-file") @Post("single-file")
async uploadFile(@UploadedFile() file: IFile) { async uploadFile(@UploadedFile() file: IFile) {
console.log(file); console.log(file);
return await this.uploaderService.upload(file); return await this.uploaderService.upload(file);
} }
@AuthGuards()
@ApiOperation({ summary: "Uploads multiple files" }) @ApiOperation({ summary: "Uploads multiple files" })
@ApiConsumes("multipart/form-data") @ApiConsumes("multipart/form-data")
@UseInterceptors(FilesInterceptor("files")) @UseInterceptors(FilesInterceptor("files"))
@ApiBody({ @ApiBody({ type: UploadMultipleFileDto })
required: true,
schema: {
type: "object",
properties: {
files: {
type: "array",
items: {
type: "string",
format: "binary",
},
},
},
},
})
@Post("multi-file") @Post("multi-file")
async uploadFiles(@UploadedFiles() files: IFile[]) { async uploadFiles(@UploadedFiles() files: IFile[]) {
console.log({ files }); console.log({ files });