chore: compelete danak category service
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { Logger } from "@nestjs/common";
|
import { Logger } from "@nestjs/common";
|
||||||
|
import { hash } from "bcrypt";
|
||||||
import { DataSource, DeepPartial } from "typeorm";
|
import { DataSource, DeepPartial } from "typeorm";
|
||||||
|
|
||||||
import { Role } from "../../src/modules/users/entities/role.entity";
|
import { Role } from "../../src/modules/users/entities/role.entity";
|
||||||
@@ -23,7 +24,9 @@ export const seedAdmin = async (dataSource: DataSource, logger: Logger) => {
|
|||||||
const adminRole = await roleRepo.findOneBy({ name: RoleEnum.ADMIN });
|
const adminRole = await roleRepo.findOneBy({ name: RoleEnum.ADMIN });
|
||||||
if (!adminRole) throw new Error("Role not found");
|
if (!adminRole) throw new Error("Role not found");
|
||||||
|
|
||||||
const admin = userRepo.create({ ...defaultAdmin, role: adminRole });
|
const hashedPassword = await hash(defaultAdmin.password!, 10);
|
||||||
|
|
||||||
|
const admin = userRepo.create({ ...defaultAdmin, password: hashedPassword, role: adminRole });
|
||||||
await userRepo.save(admin);
|
await userRepo.save(admin);
|
||||||
logger.log("admin created successfully");
|
logger.log("admin created successfully");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { ApiProperty } 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { applyDecorators } from "@nestjs/common";
|
||||||
|
import { ApiQuery } from "@nestjs/swagger";
|
||||||
|
|
||||||
|
export function Pagination() {
|
||||||
|
return applyDecorators(
|
||||||
|
ApiQuery({ name: "page", example: 1, required: false, type: "number" }),
|
||||||
|
ApiQuery({ name: "limit", example: 10, required: false, type: "number" }),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -68,6 +68,8 @@ export const enum CommonMessage {
|
|||||||
VALID_FOR_CHOOSE = "معتبر برای انتخاب",
|
VALID_FOR_CHOOSE = "معتبر برای انتخاب",
|
||||||
UPDATE_SUCCESS = "با موفقیت به روز رسانی شد",
|
UPDATE_SUCCESS = "با موفقیت به روز رسانی شد",
|
||||||
CREATED = "با موفقیت ایجاد شد",
|
CREATED = "با موفقیت ایجاد شد",
|
||||||
|
ID_REQUIRED = "شناسه مورد نیاز است",
|
||||||
|
ID_SHOULD_BE_UUID = "شناسه باید یک یو یو آی دی باشد",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum CategoryMessage {
|
export const enum CategoryMessage {
|
||||||
@@ -79,6 +81,40 @@ export const enum CategoryMessage {
|
|||||||
PARENT_ID_REQUIRED = "شناسه والد دسته بندی مورد نیاز است",
|
PARENT_ID_REQUIRED = "شناسه والد دسته بندی مورد نیاز است",
|
||||||
PARENT_ID_SHOULD_BE_UUID = "شناسه والد باید یک یو یو آی دی باشد",
|
PARENT_ID_SHOULD_BE_UUID = "شناسه والد باید یک یو یو آی دی باشد",
|
||||||
TITLE_EXIST = "دسته بندی با این نام قبلا ثبت شده است",
|
TITLE_EXIST = "دسته بندی با این نام قبلا ثبت شده است",
|
||||||
|
IS_ACTIVE_REQUIRED = "وضعیت دسته بندی مورد نیاز است",
|
||||||
|
CAT_ID_SHOULD_BE_UUID = "شناسه دسته بندی باید یک یو یو آی دی باشد",
|
||||||
|
CAT_ID_NOT_EMPTY = "شناسه دسته بندی نمیتواند خالی باشد",
|
||||||
|
CATEGORY_NOT_EXIST = "دسته بندی مورد نظر یافت نشد",
|
||||||
|
IS_ACTIVE_SHOULD_BE_BOOLEAN = "وضعیت دسته بندی باید یک بولین باشد",
|
||||||
|
SEARCH_QUERY_STRING = "رشته جستجو باید یک رشته باشد",
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum ServiceMessage {
|
||||||
|
NAME_REQUIRED = "نام سرویس مورد نیاز است",
|
||||||
|
NAME_STRING = "نام سرویس باید یک رشته باشد",
|
||||||
|
NAME_LENGTH = "نام سرویس باید بین ۳ تا ۵۰ کاراکتر باشد",
|
||||||
|
DESCRIPTION_REQUIRED = "توضیحات سرویس مورد نیاز است",
|
||||||
|
DESCRIPTION_STRING = "توضیحات سرویس باید یک رشته باشد",
|
||||||
|
AUTHOR_REQUIRED = "نویسنده سرویس مورد نیاز است",
|
||||||
|
AUTHOR_STRING = "نویسنده سرویس باید یک رشته باشد",
|
||||||
|
CREATE_DATE_REQUIRED = "تاریخ ایجاد سرویس مورد نیاز است",
|
||||||
|
CREATE_DATE_STRING = "تاریخ ایجاد سرویس باید یک رشته باشد",
|
||||||
|
USER_COUNT_REQUIRED = "تعداد کاربران سرویس مورد نیاز است",
|
||||||
|
USER_COUNT_INT = "تعداد کاربران سرویس باید یک عدد صحیح باشد",
|
||||||
|
SERVICE_LANGUAGE_REQUIRED = "زبان سرویس مورد نیاز است",
|
||||||
|
SOFTWARE_LANGUAGE_REQUIRED = "زبان نرم افزار سرویس مورد نیاز است",
|
||||||
|
SOFTWARE_LANGUAGE_STRING = "زبان نرم افزار سرویس باید یک رشته باشد",
|
||||||
|
META_DESCRIPTION_REQUIRED = "متا توضیحات سرویس مورد نیاز است",
|
||||||
|
LINK_REQUIRED = "لینک سرویس مورد نیاز است",
|
||||||
|
IS_DANAK_SUGGEST_REQUIRED = "آیا سرویس پیشنهادی داناک است؟",
|
||||||
|
IS_DANAK_SUGGEST_BOOLEAN = "وضعیت سرویس پیشنهادی داناک باید یک بولین باشد",
|
||||||
|
ICON_REQUIRED = "آیکون سرویس مورد نیاز است",
|
||||||
|
LINK_SHOULD_BE_URL = "لینک باید یک آدرس یو آر ال باشد",
|
||||||
|
ICON_SHOULD_BE_URL = "آیکون باید یک آدرس یو آر ال باشد",
|
||||||
|
IMAGES_REQUIRED = "تصاویر سرویس مورد نیاز است",
|
||||||
|
DESCRIPTION_LENGTH = "توضیحات سرویس باید بین ۱۰ تا ۵۰۰ کاراکتر باشد",
|
||||||
|
SOFTWARE_LANGUAGE_LENGTH = "زبان نرم افزار سرویس باید بین ۳ تا ۱۵۰ کاراکتر باشد",
|
||||||
|
AUTHOR_LENGTH = "نویسنده سرویس باید بین 3 تا 100 کاراکتر باشد",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum AnnouncementMessage {
|
export const enum AnnouncementMessage {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class RoleGuard implements CanActivate {
|
|||||||
|
|
||||||
if (!user) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS);
|
if (!user) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS);
|
||||||
|
|
||||||
const hasRequiredRole = requiredRole.includes(user.role.name);
|
const hasRequiredRole = requiredRole.includes(user.role as unknown as RoleEnum);
|
||||||
if (!hasRequiredRole) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS);
|
if (!hasRequiredRole) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -98,11 +98,14 @@ export class AuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//****************** */
|
//****************** */
|
||||||
async requestLoginOtp(requestOtpDto: RequestOtpDto) {
|
async requestLoginOtp(requestOtpDto: RequestOtpDto, isAdmin: boolean = false) {
|
||||||
const { phone } = requestOtpDto;
|
const { phone } = requestOtpDto;
|
||||||
const user = await this.usersService.findOneWithPhone(phone);
|
const user = await this.usersService.findOneWithPhone(phone);
|
||||||
if (!user) throw new BadRequestException(AuthMessage.PHONE_NOT_FOUND);
|
if (!user) throw new BadRequestException(AuthMessage.PHONE_NOT_FOUND);
|
||||||
|
|
||||||
|
//check the if the method call is from admin or not
|
||||||
|
if (isAdmin && user.role.name !== RoleEnum.ADMIN) throw new BadRequestException(AuthMessage.NOT_ADMIN);
|
||||||
|
|
||||||
const existCode = await this.otpService.checkExistOtp(phone, "LOGIN");
|
const existCode = await this.otpService.checkExistOtp(phone, "LOGIN");
|
||||||
if (existCode) {
|
if (existCode) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
|
import { Type } from "class-transformer";
|
||||||
|
import { IsOptional, IsString, IsUUID } from "class-validator";
|
||||||
|
|
||||||
|
import { PaginationDto } from "../../../common/DTO/pagination.dto";
|
||||||
|
import { CategoryMessage } from "../../../common/enums/message.enum";
|
||||||
|
|
||||||
|
export class CategorySearchQueryDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsUUID("4", { message: CategoryMessage.PARENT_ID_SHOULD_BE_UUID })
|
||||||
|
@ApiPropertyOptional({ description: "Parent category id", example: "8b1e8b1e-8b1e-8b1e-8b1e-8b1e8b1e8b1e" })
|
||||||
|
parentId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CategoryListSearchQueryDto extends PaginationDto {
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
// @IsBoolean({ message: CategoryMessage.IS_ACTIVE_SHOULD_BE_BOOLEAN })
|
||||||
|
@ApiPropertyOptional({ description: "Category status", example: 1 })
|
||||||
|
isActive?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString({ message: CategoryMessage.SEARCH_QUERY_STRING })
|
||||||
|
@ApiPropertyOptional({ description: "Search query", example: "search query" })
|
||||||
|
q?: string;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length } from "class-validator";
|
import { IsBoolean, IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length } from "class-validator";
|
||||||
|
|
||||||
import { CategoryMessage } from "../../../common/enums/message.enum";
|
import { CategoryMessage } from "../../../common/enums/message.enum";
|
||||||
|
|
||||||
@@ -15,6 +15,11 @@ export class CreateCategoryDto {
|
|||||||
@ApiProperty({ description: "Category icon", example: "https://example.com/icon.png" })
|
@ApiProperty({ description: "Category icon", example: "https://example.com/icon.png" })
|
||||||
icon: string;
|
icon: string;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: CategoryMessage.IS_ACTIVE_REQUIRED })
|
||||||
|
@IsBoolean({ message: CategoryMessage.IS_ACTIVE_REQUIRED })
|
||||||
|
@ApiProperty({ description: "Category status", example: true })
|
||||||
|
isActive: boolean;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsNotEmpty({ message: CategoryMessage.PARENT_ID_REQUIRED })
|
@IsNotEmpty({ message: CategoryMessage.PARENT_ID_REQUIRED })
|
||||||
@IsUUID("4", { message: CategoryMessage.PARENT_ID_SHOULD_BE_UUID })
|
@IsUUID("4", { message: CategoryMessage.PARENT_ID_SHOULD_BE_UUID })
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsBoolean, IsDateString, IsEnum, IsInt, IsNotEmpty, IsString, IsUUID, IsUrl, Length, MaxLength } from "class-validator";
|
||||||
|
|
||||||
|
import { CategoryMessage, ServiceMessage } from "../../../common/enums/message.enum";
|
||||||
|
import { ServicesLanguage } from "../enums/services-language.enum";
|
||||||
|
|
||||||
|
export class CreateServiceDto {
|
||||||
|
@IsNotEmpty({ message: CategoryMessage.CAT_ID_NOT_EMPTY })
|
||||||
|
@IsUUID("4", { message: CategoryMessage.CAT_ID_SHOULD_BE_UUID })
|
||||||
|
@ApiProperty({ description: "The category id of the service", example: "00000000-0000-0000-0000-000000000000" })
|
||||||
|
categoryId: string;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: ServiceMessage.NAME_REQUIRED })
|
||||||
|
@IsString({ message: ServiceMessage.NAME_STRING })
|
||||||
|
@Length(3, 50, { message: ServiceMessage.NAME_LENGTH })
|
||||||
|
@ApiProperty({ description: "The name of the service", example: "Service Name" })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: ServiceMessage.DESCRIPTION_REQUIRED })
|
||||||
|
@IsString({ message: ServiceMessage.DESCRIPTION_STRING })
|
||||||
|
@Length(10, 500, { message: ServiceMessage.DESCRIPTION_LENGTH })
|
||||||
|
@ApiProperty({ description: "The description of the service", example: "Service Description" })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: ServiceMessage.AUTHOR_REQUIRED })
|
||||||
|
@IsString({ message: ServiceMessage.AUTHOR_STRING })
|
||||||
|
@Length(3, 100, { message: ServiceMessage.AUTHOR_LENGTH })
|
||||||
|
@ApiProperty({ description: "The author of the service", example: "danak corp" })
|
||||||
|
author: string;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: ServiceMessage.CREATE_DATE_REQUIRED })
|
||||||
|
@IsDateString({}, { message: ServiceMessage.CREATE_DATE_STRING })
|
||||||
|
@ApiProperty({ description: "The create date of the service", example: "2021-08-01T00:00:00.000Z" })
|
||||||
|
createDate: Date;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: ServiceMessage.USER_COUNT_REQUIRED })
|
||||||
|
@IsInt({ message: ServiceMessage.USER_COUNT_INT })
|
||||||
|
@ApiProperty({ description: "The user count of the service", example: 1000 })
|
||||||
|
userCount: number;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: ServiceMessage.SERVICE_LANGUAGE_REQUIRED })
|
||||||
|
@IsEnum(ServicesLanguage)
|
||||||
|
@ApiProperty({ enum: ServicesLanguage, description: "The language of the service", example: ServicesLanguage.ENGLISH })
|
||||||
|
serviceLanguage: ServicesLanguage;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: ServiceMessage.SOFTWARE_LANGUAGE_REQUIRED })
|
||||||
|
@IsString({ message: ServiceMessage.SOFTWARE_LANGUAGE_STRING })
|
||||||
|
@MaxLength(150, { message: ServiceMessage.SOFTWARE_LANGUAGE_LENGTH })
|
||||||
|
@ApiProperty({ description: "The software language of the service", example: "Python" })
|
||||||
|
softwareLanguage: string;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: ServiceMessage.META_DESCRIPTION_REQUIRED })
|
||||||
|
@IsString({ message: ServiceMessage.META_DESCRIPTION_REQUIRED })
|
||||||
|
@ApiProperty({ description: "The meta description of the service", example: "Service Meta Description" })
|
||||||
|
metaDescription: string;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: ServiceMessage.LINK_REQUIRED })
|
||||||
|
@IsUrl({ protocols: ["http", "https"], require_protocol: true }, { message: ServiceMessage.LINK_SHOULD_BE_URL })
|
||||||
|
@ApiProperty({ description: "The status of the service", example: "https://example.com" })
|
||||||
|
link: string;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: ServiceMessage.IS_DANAK_SUGGEST_REQUIRED })
|
||||||
|
@IsBoolean({ message: ServiceMessage.IS_DANAK_SUGGEST_BOOLEAN })
|
||||||
|
@ApiProperty({ description: "Is the service a Danak suggest?", example: true })
|
||||||
|
isDanakSuggest: boolean;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: ServiceMessage.ICON_REQUIRED })
|
||||||
|
@IsUrl({ protocols: ["http", "https"], require_protocol: true }, { message: ServiceMessage.ICON_SHOULD_BE_URL })
|
||||||
|
@ApiProperty({ description: "The icon url of the service", example: "https://example.com/icon.png" })
|
||||||
|
icon: string;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: ServiceMessage.IMAGES_REQUIRED })
|
||||||
|
@IsString({ each: true })
|
||||||
|
@ApiProperty({ description: "The images of the service", example: ["https://example.com/image1.png", "https://example.com/image2.png"] })
|
||||||
|
images: string[];
|
||||||
|
}
|
||||||
@@ -1,18 +1,61 @@
|
|||||||
import { Body, Controller, Post } from "@nestjs/common";
|
import { Body, Controller, Get, HttpCode, HttpStatus, Param, Post, Query } from "@nestjs/common";
|
||||||
import { ApiOperation, ApiTags } from "@nestjs/swagger";
|
import { ApiOperation, ApiTags } from "@nestjs/swagger";
|
||||||
|
|
||||||
|
import { CategoryListSearchQueryDto, CategorySearchQueryDto } from "./DTO/category-search-query.dto";
|
||||||
import { CreateCategoryDto } from "./DTO/create-category.dto";
|
import { CreateCategoryDto } from "./DTO/create-category.dto";
|
||||||
|
import { CreateServiceDto } from "./DTO/create-service.dto";
|
||||||
import { DanakServicesService } from "./providers/danak-services.service";
|
import { DanakServicesService } from "./providers/danak-services.service";
|
||||||
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||||
|
import { Pagination } from "../../common/decorators/pagination.decorator";
|
||||||
|
import { Roles } from "../../common/decorators/roles.decorator";
|
||||||
|
import { ParamDto } from "../../common/DTO/param.dto";
|
||||||
|
import { RoleEnum } from "../users/enums/role.enum";
|
||||||
|
|
||||||
@Controller("services")
|
@Controller("services")
|
||||||
@ApiTags("Danak-Services")
|
@ApiTags("Danak-Services")
|
||||||
export class DanakServicesController {
|
export class DanakServicesController {
|
||||||
constructor(private readonly danakServicesService: DanakServicesService) {}
|
constructor(private readonly danakServicesService: DanakServicesService) {}
|
||||||
|
|
||||||
|
//
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
|
@Roles(RoleEnum.ADMIN)
|
||||||
@ApiOperation({ summary: "Create a new service category" })
|
@ApiOperation({ summary: "Create a new service category" })
|
||||||
@Post("category")
|
@Post("category")
|
||||||
createCategory(@Body() createDto: CreateCategoryDto) {
|
createCategory(@Body() createDto: CreateCategoryDto) {
|
||||||
return this.danakServicesService.createCategory(createDto);
|
return this.danakServicesService.createCategory(createDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@Roles(RoleEnum.ADMIN)
|
||||||
|
@ApiOperation({ summary: "Get all service categories" })
|
||||||
|
@Get("category")
|
||||||
|
getCategories(@Query() queryDto: CategorySearchQueryDto) {
|
||||||
|
return this.danakServicesService.getCategories(queryDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@Roles(RoleEnum.ADMIN)
|
||||||
|
@ApiOperation({ summary: "Get all service categories" })
|
||||||
|
@Pagination()
|
||||||
|
@Get("category-list")
|
||||||
|
getCategoryList(@Query() queryDto: CategoryListSearchQueryDto) {
|
||||||
|
return this.danakServicesService.getCategoryList(queryDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@Roles(RoleEnum.ADMIN)
|
||||||
|
@ApiOperation({ summary: "toggle status of categories" })
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Post("category/toggle-status/:id")
|
||||||
|
toggleCategoryStatus(@Param() paramDto: ParamDto) {
|
||||||
|
return this.danakServicesService.toggleCategoryStatus(paramDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@Roles(RoleEnum.ADMIN)
|
||||||
|
@ApiOperation({ summary: "create new danak services" })
|
||||||
|
@Post()
|
||||||
|
createService(@Body() createDto: CreateServiceDto) {
|
||||||
|
return this.danakServicesService.createService(createDto);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ export class DanakServiceCategory extends BaseEntity {
|
|||||||
@Column({ type: "varchar", length: 150, nullable: false })
|
@Column({ type: "varchar", length: 150, nullable: false })
|
||||||
icon: string;
|
icon: string;
|
||||||
|
|
||||||
|
@Column({ type: "boolean", default: true })
|
||||||
|
isActive: boolean;
|
||||||
|
|
||||||
@ManyToOne(() => DanakServiceCategory, (category) => category.children)
|
@ManyToOne(() => DanakServiceCategory, (category) => category.children)
|
||||||
@JoinColumn({ name: "parentId" })
|
@JoinColumn({ name: "parentId" })
|
||||||
parent: DanakServiceCategory;
|
parent: DanakServiceCategory;
|
||||||
@@ -19,7 +22,7 @@ export class DanakServiceCategory extends BaseEntity {
|
|||||||
children: DanakServiceCategory[];
|
children: DanakServiceCategory[];
|
||||||
|
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
parentId: string;
|
parentId: string | null;
|
||||||
|
|
||||||
@OneToMany(() => DanakService, (danakService) => danakService.category)
|
@OneToMany(() => DanakService, (danakService) => danakService.category)
|
||||||
danakService: DanakService[];
|
danakService: DanakService[];
|
||||||
|
|||||||
@@ -8,9 +8,12 @@ import { ServicesLanguage } from "../enums/services-language.enum";
|
|||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class DanakService extends BaseEntity {
|
export class DanakService extends BaseEntity {
|
||||||
@Column({ type: "varchar", length: 255, nullable: false })
|
@Column({ type: "varchar", length: 100, nullable: false })
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
|
@Column({ type: "boolean", nullable: false, default: false })
|
||||||
|
isDanakSuggest: boolean;
|
||||||
|
|
||||||
@Column({ type: "text", nullable: false })
|
@Column({ type: "text", nullable: false })
|
||||||
description: string;
|
description: string;
|
||||||
|
|
||||||
@@ -41,7 +44,7 @@ export class DanakService extends BaseEntity {
|
|||||||
@Column({ type: "varchar", length: 255, nullable: false })
|
@Column({ type: "varchar", length: 255, nullable: false })
|
||||||
icon: string;
|
icon: string;
|
||||||
|
|
||||||
@OneToMany(() => DanakServiceImage, (danakServiceImage) => danakServiceImage.danakService, { eager: true })
|
@OneToMany(() => DanakServiceImage, (danakServiceImage) => danakServiceImage.danakService, { eager: true, cascade: true })
|
||||||
images: DanakServiceImage[];
|
images: DanakServiceImage[];
|
||||||
|
|
||||||
@ManyToOne(() => DanakServiceCategory, (danakServiceCategory) => danakServiceCategory.danakService, {
|
@ManyToOne(() => DanakServiceCategory, (danakServiceCategory) => danakServiceCategory.danakService, {
|
||||||
|
|||||||
@@ -1,17 +1,27 @@
|
|||||||
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
import { BadRequestException, Injectable } from "@nestjs/common";
|
||||||
|
import { FindOptionsWhere, IsNull } from "typeorm";
|
||||||
|
|
||||||
|
import { ParamDto } from "../../../common/DTO/param.dto";
|
||||||
import { CategoryMessage, CommonMessage } from "../../../common/enums/message.enum";
|
import { CategoryMessage, CommonMessage } from "../../../common/enums/message.enum";
|
||||||
|
import { PaginationUtils } from "../../utils/providers/pagination.utils";
|
||||||
|
import { CategoryListSearchQueryDto, CategorySearchQueryDto } from "../DTO/category-search-query.dto";
|
||||||
import { CreateCategoryDto } from "../DTO/create-category.dto";
|
import { CreateCategoryDto } from "../DTO/create-category.dto";
|
||||||
|
import { CreateServiceDto } from "../DTO/create-service.dto";
|
||||||
|
import { DanakServiceCategory } from "../entities/danak-service-category.entity";
|
||||||
import { DanakServicesCategoryRepository } from "../repositories/danak-services-category.repository";
|
import { DanakServicesCategoryRepository } from "../repositories/danak-services-category.repository";
|
||||||
|
import { DanakServicesRepository } from "../repositories/danak-services.repository";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DanakServicesService {
|
export class DanakServicesService {
|
||||||
private readonly logger = new Logger(DanakServicesService.name);
|
// private readonly logger = new Logger(DanakServicesService.name);
|
||||||
constructor(private readonly danakServicesCategoryRepository: DanakServicesCategoryRepository) {}
|
constructor(
|
||||||
/********** */
|
private readonly danakServicesRepository: DanakServicesRepository,
|
||||||
|
private readonly danakServicesCategoryRepository: DanakServicesCategoryRepository,
|
||||||
|
) {}
|
||||||
|
/******************************************** */
|
||||||
|
|
||||||
async createCategory(createDto: CreateCategoryDto) {
|
async createCategory(createDto: CreateCategoryDto) {
|
||||||
this.logger.log(`Creating a new category with title: ${createDto.title}`);
|
const existCategory = await this.danakServicesCategoryRepository.findOneByTitle(createDto.title);
|
||||||
const existCategory = await this.danakServicesCategoryRepository.findOneBy({ title: createDto.title });
|
|
||||||
if (existCategory) throw new BadRequestException(CategoryMessage.TITLE_EXIST);
|
if (existCategory) throw new BadRequestException(CategoryMessage.TITLE_EXIST);
|
||||||
const category = this.danakServicesCategoryRepository.create(createDto);
|
const category = this.danakServicesCategoryRepository.create(createDto);
|
||||||
await this.danakServicesCategoryRepository.save(category);
|
await this.danakServicesCategoryRepository.save(category);
|
||||||
@@ -20,5 +30,81 @@ export class DanakServicesService {
|
|||||||
category,
|
category,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/********** */
|
/******************************************** */
|
||||||
|
async getCategories(queryDto: CategorySearchQueryDto) {
|
||||||
|
const where: FindOptionsWhere<DanakServiceCategory> = {
|
||||||
|
isActive: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (queryDto.parentId === undefined) {
|
||||||
|
where.parentId = IsNull();
|
||||||
|
} else {
|
||||||
|
// If parentId is provided, use it
|
||||||
|
where.parentId = queryDto.parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const categories = await this.danakServicesCategoryRepository.find({
|
||||||
|
where,
|
||||||
|
order: { createdAt: "ASC" },
|
||||||
|
});
|
||||||
|
|
||||||
|
return { categories };
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************** */
|
||||||
|
async getCategoryList(queryDto: CategoryListSearchQueryDto) {
|
||||||
|
const { limit, skip } = PaginationUtils(queryDto);
|
||||||
|
const queryBuilder = this.danakServicesCategoryRepository
|
||||||
|
.createQueryBuilder("category")
|
||||||
|
.loadRelationCountAndMap("category.servicesCount", "category.danakService")
|
||||||
|
.loadRelationCountAndMap("category.childrenCount", "category.children");
|
||||||
|
|
||||||
|
if (queryDto.isActive !== undefined) {
|
||||||
|
queryBuilder.andWhere("category.isActive = :isActive", { isActive: queryDto.isActive === 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryDto.q) {
|
||||||
|
queryBuilder.andWhere("category.title LIKE :q", { q: `%${queryDto.q}%` });
|
||||||
|
}
|
||||||
|
|
||||||
|
queryBuilder
|
||||||
|
.leftJoinAndSelect("category.parent", "parent")
|
||||||
|
.select(["category", "parent.id", "parent.title"])
|
||||||
|
.orderBy("category.createdAt", "DESC");
|
||||||
|
|
||||||
|
const [categories, count] = await queryBuilder.skip(skip).take(limit).getManyAndCount();
|
||||||
|
|
||||||
|
return { categories, count };
|
||||||
|
}
|
||||||
|
/******************************************** */
|
||||||
|
|
||||||
|
async toggleCategoryStatus(paramDto: ParamDto) {
|
||||||
|
const category = await this.danakServicesCategoryRepository.findOneById(paramDto.id);
|
||||||
|
if (!category) throw new BadRequestException(CategoryMessage.CATEGORY_NOT_EXIST);
|
||||||
|
category.isActive = !category.isActive;
|
||||||
|
await this.danakServicesCategoryRepository.save(category);
|
||||||
|
return {
|
||||||
|
message: CommonMessage.UPDATE_SUCCESS,
|
||||||
|
isActive: category.isActive,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/******************************************** */
|
||||||
|
async createService(createDto: CreateServiceDto) {
|
||||||
|
const category = await this.danakServicesCategoryRepository.findOneById(createDto.categoryId);
|
||||||
|
if (!category) throw new BadRequestException(CategoryMessage.CATEGORY_NOT_EXIST);
|
||||||
|
|
||||||
|
const images = createDto.images.map((image) => {
|
||||||
|
return { imageUrl: image };
|
||||||
|
});
|
||||||
|
|
||||||
|
const service = this.danakServicesRepository.create({ ...createDto, category, images });
|
||||||
|
await this.danakServicesRepository.save(service);
|
||||||
|
return {
|
||||||
|
message: CommonMessage.CREATED,
|
||||||
|
service,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************** */
|
||||||
|
/******************************************** */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,4 +9,12 @@ export class DanakServicesCategoryRepository extends Repository<DanakServiceCate
|
|||||||
constructor(@InjectRepository(DanakServiceCategory) danakServicesCategoryRepository: Repository<DanakServiceCategory>) {
|
constructor(@InjectRepository(DanakServiceCategory) danakServicesCategoryRepository: Repository<DanakServiceCategory>) {
|
||||||
super(danakServicesCategoryRepository.target, danakServicesCategoryRepository.manager, danakServicesCategoryRepository.queryRunner);
|
super(danakServicesCategoryRepository.target, danakServicesCategoryRepository.manager, danakServicesCategoryRepository.queryRunner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async findOneByTitle(title: string): Promise<DanakServiceCategory | null> {
|
||||||
|
return this.findOneBy({ title });
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOneById(id: string): Promise<DanakServiceCategory | null> {
|
||||||
|
return this.findOneBy({ id });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
|||||||
import slugify from "slugify";
|
import slugify from "slugify";
|
||||||
import { In, Not } from "typeorm";
|
import { In, Not } from "typeorm";
|
||||||
|
|
||||||
import { RoleRepository } from "../repositories/roles.repository";
|
|
||||||
// import { roles } from "../../../../database/seeders/role.seeder";
|
// import { roles } from "../../../../database/seeders/role.seeder";
|
||||||
import { CommonMessage, UserMessage } from "../../../common/enums/message.enum";
|
import { CommonMessage, UserMessage } from "../../../common/enums/message.enum";
|
||||||
import { CompleteRegistrationDto } from "../../auth/DTO/complete-register.dto";
|
import { CompleteRegistrationDto } from "../../auth/DTO/complete-register.dto";
|
||||||
@@ -12,6 +11,7 @@ import { CreateUserGroupDto } from "../DTO/user-group.dto";
|
|||||||
import { User } from "../entities/user.entity";
|
import { User } from "../entities/user.entity";
|
||||||
import { RoleEnum } from "../enums/role.enum";
|
import { RoleEnum } from "../enums/role.enum";
|
||||||
import { ValidityType } from "../enums/validity-type.enum";
|
import { ValidityType } from "../enums/validity-type.enum";
|
||||||
|
import { RoleRepository } from "../repositories/roles.repository";
|
||||||
import { UserRepository } from "../repositories/users.repository";
|
import { UserRepository } from "../repositories/users.repository";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { PaginationDto } from "../../../common/DTO/pagination.dto";
|
||||||
|
|
||||||
|
export function PaginationUtils(paginationData: PaginationDto) {
|
||||||
|
const { limit = 10, page = 1 } = paginationData;
|
||||||
|
|
||||||
|
// const pageN = Number.parseInt(page) || 1;
|
||||||
|
// const limitN = Number.parseInt(limit) || 10;
|
||||||
|
|
||||||
|
const skip = (page - 1) * limit;
|
||||||
|
|
||||||
|
return {
|
||||||
|
// page: page,
|
||||||
|
// limit: limit > 50 ? 50 : limit,
|
||||||
|
limit: limit,
|
||||||
|
skip: skip,
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user