chore: add slider module
This commit is contained in:
@@ -23,6 +23,7 @@ import { IndustriesModule } from "./modules/industries/industries.module";
|
||||
import { InvoicesModule } from "./modules/invoices/invoices.module";
|
||||
import { NotificationModule } from "./modules/notifications/notifications.module";
|
||||
import { PaymentsModule } from "./modules/payments/payments.module";
|
||||
import { SlidersModule } from "./modules/sliders/sliders.module";
|
||||
import { TicketsModule } from "./modules/tickets/tickets.module";
|
||||
import { UploaderModule } from "./modules/uploader/uploader.module";
|
||||
import { UsersModule } from "./modules/users/users.module";
|
||||
@@ -59,6 +60,7 @@ import { UtilsModule } from "./modules/utils/utils.module";
|
||||
AnnouncementsModule,
|
||||
CriticismModule,
|
||||
PaymentsModule,
|
||||
SlidersModule,
|
||||
],
|
||||
})
|
||||
export class AppModule implements NestModule {
|
||||
|
||||
@@ -650,3 +650,19 @@ export const enum BusinessMessage {
|
||||
DOMAIN_SETUP_TROUBLESHOOTING_5 = "- برخی از ارائهدهندگان DNS ممکن است نیاز به تنظیمات خاص برای رکوردهای TXT داشته باشند",
|
||||
DOMAIN_SETUP_TROUBLESHOOTING_6 = "- اگر از Cloudflare استفاده میکنید، مطمئن شوید که رکوردهای TXT را بدون پروکسی تنظیم کردهاید",
|
||||
}
|
||||
|
||||
export const enum SliderMessage {
|
||||
TITLE_REQUIRED = "عنوان اسلایدر الزامی است",
|
||||
TITLE_STRING = "عنوان اسلایدر باید یک رشته باشد",
|
||||
TITLE_MAX_LENGTH = "عنوان اسلایدر باید حداکثر ۱۰۰ کاراکتر باشد",
|
||||
DESCRIPTION_STRING = "توضیحات اسلایدر باید یک رشته باشد",
|
||||
DESCRIPTION_MAX_LENGTH = "توضیحات اسلایدر باید حداکثر ۲۵۵ کاراکتر باشد",
|
||||
IMAGE_URL_REQUIRED = "آدرس تصویر اسلایدر الزامی است",
|
||||
IMAGE_URL_STRING = "آدرس تصویر اسلایدر باید یک رشته باشد",
|
||||
IMAGE_URL_MAX_LENGTH = "آدرس تصویر اسلایدر باید حداکثر ۲۵۵ کاراکتر باشد",
|
||||
LINK_STRING = "لینک اسلایدر باید یک رشته باشد",
|
||||
LINK_MAX_LENGTH = "لینک اسلایدر باید حداکثر ۲۵۵ کاراکتر باشد",
|
||||
ORDER_NUMBER = "ترتیب اسلایدر باید یک عدد باشد",
|
||||
IS_ACTIVE_SHOULD_BE_A_BOOLEAN = "وضعیت فعال بودن اسلایدر باید یک مقدار بولی باشد",
|
||||
SLIDER_NOT_FOUND = "SLIDER_NOT_FOUND",
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Company } from "../../companies/entities/company.entity";
|
||||
import { Criticism } from "../../criticisms/entities/criticism.entity";
|
||||
import { Industry } from "../../industries/entities/industry.entity";
|
||||
import { Invoice } from "../../invoices/entities/invoice.entity";
|
||||
import { Slider } from "../../sliders/entities/slider.entity";
|
||||
import { TicketCategory } from "../../tickets/entities/ticket-category.entity";
|
||||
import { Ticket } from "../../tickets/entities/ticket.entity";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
@@ -65,6 +66,9 @@ export class Business extends BaseEntity {
|
||||
@OneToMany(() => Criticism, (criticism) => criticism.business)
|
||||
criticisms = new Collection<Criticism>(this);
|
||||
|
||||
@OneToMany(() => Slider, (slider) => slider.business)
|
||||
sliders = new Collection<Slider>(this);
|
||||
|
||||
@OneToMany(() => User, (user) => user.business)
|
||||
users = new Collection<User>(this);
|
||||
|
||||
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||
import { IsBoolean, IsNotEmpty, IsNumber, IsOptional, IsString, MaxLength } from "class-validator";
|
||||
|
||||
import { SliderMessage } from "../../../common/enums/message.enum";
|
||||
|
||||
export class CreateSliderDto {
|
||||
@ApiProperty({ description: "Title of the slider", example: "Summer Sale" })
|
||||
@IsNotEmpty({ message: SliderMessage.TITLE_REQUIRED })
|
||||
@IsString({ message: SliderMessage.TITLE_STRING })
|
||||
@MaxLength(150, { message: SliderMessage.TITLE_MAX_LENGTH })
|
||||
title: string;
|
||||
|
||||
@ApiPropertyOptional({ description: "Description of the slider", example: "Get up to 50% off" })
|
||||
@IsOptional()
|
||||
@IsString({ message: SliderMessage.DESCRIPTION_STRING })
|
||||
@MaxLength(255, { message: SliderMessage.DESCRIPTION_MAX_LENGTH })
|
||||
description?: string;
|
||||
|
||||
@ApiProperty({ description: "URL of the slider image", example: "https://example.com/image.jpg" })
|
||||
@IsNotEmpty({ message: SliderMessage.IMAGE_URL_REQUIRED })
|
||||
@IsString({ message: SliderMessage.IMAGE_URL_STRING })
|
||||
@MaxLength(255, { message: SliderMessage.IMAGE_URL_MAX_LENGTH })
|
||||
imageUrl: string;
|
||||
|
||||
@ApiPropertyOptional({ description: "Link URL for the slider", example: "https://example.com/sale" })
|
||||
@IsOptional()
|
||||
@IsString({ message: SliderMessage.LINK_STRING })
|
||||
@MaxLength(255, { message: SliderMessage.LINK_MAX_LENGTH })
|
||||
link?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: "Order of the slider", example: 1, default: 0 })
|
||||
@IsOptional()
|
||||
@IsNumber({}, { message: SliderMessage.ORDER_NUMBER })
|
||||
order?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: "Whether the slider is active", example: true, default: true })
|
||||
@IsOptional()
|
||||
@IsBoolean({ message: SliderMessage.IS_ACTIVE_SHOULD_BE_A_BOOLEAN })
|
||||
isActive?: boolean;
|
||||
}
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
import { PartialType } from "@nestjs/swagger";
|
||||
|
||||
import { CreateSliderDto } from "./create-slider.dto";
|
||||
|
||||
export class UpdateSliderDto extends PartialType(CreateSliderDto) {}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { Entity, EntityRepositoryType, ManyToOne, Opt, Property } from "@mikro-orm/core";
|
||||
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
import { Business } from "../../businesses/entities/business.entity";
|
||||
import { SliderRepository } from "../repositories/sliders.repository";
|
||||
|
||||
@Entity({ repository: () => SliderRepository })
|
||||
export class Slider extends BaseEntity {
|
||||
@Property({ type: "varchar", length: 150 })
|
||||
title: string;
|
||||
|
||||
@Property({ type: "varchar", length: 255, nullable: true })
|
||||
description?: string;
|
||||
|
||||
@Property({ type: "varchar", length: 255 })
|
||||
imageUrl: string;
|
||||
|
||||
@Property({ type: "varchar", length: 255, nullable: true })
|
||||
link?: string;
|
||||
|
||||
@Property({ type: "int", default: 0 })
|
||||
order: number & Opt;
|
||||
|
||||
@Property({ type: "boolean", default: true })
|
||||
isActive: boolean & Opt;
|
||||
|
||||
@ManyToOne(() => Business, { deleteRule: "restrict" })
|
||||
business!: Business;
|
||||
|
||||
//-----------------------------
|
||||
|
||||
[EntityRepositoryType]?: SliderRepository;
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
import { EntityManager } from "@mikro-orm/postgresql";
|
||||
import { BadRequestException, Injectable } from "@nestjs/common";
|
||||
|
||||
import { PaginationDto } from "../../../common/DTO/pagination.dto";
|
||||
import { CommonMessage, SliderMessage } from "../../../common/enums/message.enum";
|
||||
import { Business } from "../../businesses/entities/business.entity";
|
||||
import { CreateSliderDto } from "../DTO/create-slider.dto";
|
||||
import { UpdateSliderDto } from "../DTO/update-slider.dto";
|
||||
import { SliderRepository } from "../repositories/sliders.repository";
|
||||
|
||||
@Injectable()
|
||||
export class SlidersService {
|
||||
constructor(
|
||||
private readonly slidersRepository: SliderRepository,
|
||||
private readonly em: EntityManager,
|
||||
) {}
|
||||
|
||||
async create(createSliderDto: CreateSliderDto, business: Business) {
|
||||
const slider = this.slidersRepository.create({ ...createSliderDto, business });
|
||||
await this.em.persistAndFlush(slider);
|
||||
return {
|
||||
message: CommonMessage.CREATED,
|
||||
slider,
|
||||
};
|
||||
}
|
||||
//*************************** */
|
||||
async getSlidersList(queryDto: PaginationDto, businessId: string) {
|
||||
const [sliders, count] = await this.slidersRepository.getSlidersListForAdmin(queryDto, businessId);
|
||||
return {
|
||||
sliders,
|
||||
count,
|
||||
paginate: true,
|
||||
};
|
||||
}
|
||||
//************************* */
|
||||
|
||||
async getSliderListPublic(businessId: string) {
|
||||
const sliders = await this.slidersRepository.find({ isActive: true, deletedAt: null, business: { id: businessId } });
|
||||
return { sliders };
|
||||
}
|
||||
//************************* */
|
||||
async getSliderById(id: string, businessId: string) {
|
||||
const slider = await this.slidersRepository.findOne({ id, business: { id: businessId }, deletedAt: null });
|
||||
if (!slider) throw new BadRequestException(SliderMessage.SLIDER_NOT_FOUND);
|
||||
|
||||
return {
|
||||
slider,
|
||||
};
|
||||
}
|
||||
//****************** */
|
||||
async update(id: string, updateSliderDto: UpdateSliderDto, businessId: string) {
|
||||
const { slider } = await this.getSliderById(id, businessId);
|
||||
|
||||
this.slidersRepository.assign(slider, updateSliderDto);
|
||||
|
||||
await this.em.persistAndFlush(slider);
|
||||
return {
|
||||
message: CommonMessage.UPDATE_SUCCESS,
|
||||
};
|
||||
}
|
||||
//****************** */
|
||||
async remove(id: string, businessId: string) {
|
||||
const { slider } = await this.getSliderById(id, businessId);
|
||||
|
||||
slider.deletedAt = new Date();
|
||||
await this.em.flush();
|
||||
|
||||
return {
|
||||
message: CommonMessage.DELETED,
|
||||
};
|
||||
}
|
||||
//****************** */
|
||||
|
||||
async toggleSliderStatus(id: string, businessId: string) {
|
||||
const { slider } = await this.getSliderById(id, businessId);
|
||||
|
||||
slider.isActive = !slider.isActive;
|
||||
|
||||
await this.em.flush();
|
||||
return {
|
||||
message: CommonMessage.UPDATE_SUCCESS,
|
||||
};
|
||||
}
|
||||
//****************** */
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { EntityRepository } from "@mikro-orm/postgresql";
|
||||
|
||||
import { PaginationDto } from "../../../common/DTO/pagination.dto";
|
||||
import { PaginationUtils } from "../../utils/providers/pagination.utils";
|
||||
import { Slider } from "../entities/slider.entity";
|
||||
|
||||
export class SliderRepository extends EntityRepository<Slider> {
|
||||
async getSlidersListForAdmin(queryDto: PaginationDto, businessId: string) {
|
||||
const { skip, limit } = PaginationUtils(queryDto);
|
||||
|
||||
return this.findAndCount({ deletedAt: null, business: { id: businessId } }, { offset: skip, limit, orderBy: { order: "ASC" } });
|
||||
}
|
||||
}
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Query, UseInterceptors } from "@nestjs/common";
|
||||
import { ApiOperation } from "@nestjs/swagger";
|
||||
|
||||
import { CreateSliderDto } from "./DTO/create-slider.dto";
|
||||
import { UpdateSliderDto } from "./DTO/update-slider.dto";
|
||||
import { SlidersService } from "./providers/sliders.service";
|
||||
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||
import { BusinessDec } from "../../common/decorators/business.decorator";
|
||||
import { SkipAuth } from "../../common/decorators/skip-auth.decorator";
|
||||
import { PaginationDto } from "../../common/DTO/pagination.dto";
|
||||
import { ParamDto } from "../../common/DTO/param.dto";
|
||||
import { BusinessInterceptor } from "../../core/interceptors/business.interceptor";
|
||||
import { Business } from "../businesses/entities/business.entity";
|
||||
|
||||
@Controller("sliders")
|
||||
@AuthGuards()
|
||||
@UseInterceptors(BusinessInterceptor)
|
||||
export class SlidersController {
|
||||
constructor(private readonly slidersService: SlidersService) {}
|
||||
|
||||
@ApiOperation({ summary: "create slider (admin route)" })
|
||||
@Post()
|
||||
create(@Body() createSliderDto: CreateSliderDto, @BusinessDec() business: Business) {
|
||||
return this.slidersService.create(createSliderDto, business);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "Get all sliders (admin route)" })
|
||||
@Get()
|
||||
getSlidersList(@Query() queryDto: PaginationDto, @BusinessDec("id") businessId: string) {
|
||||
return this.slidersService.getSlidersList(queryDto, businessId);
|
||||
}
|
||||
|
||||
@Get("list/public")
|
||||
@SkipAuth()
|
||||
@ApiOperation({ summary: "get all slider public" })
|
||||
getSliderListPublic(@BusinessDec("id") businessId: string) {
|
||||
return this.slidersService.getSliderListPublic(businessId);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "Get a slider by id (admin route)" })
|
||||
@Get(":id")
|
||||
getSliderById(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
|
||||
return this.slidersService.getSliderById(paramDto.id, businessId);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "Update a slider (admin route)" })
|
||||
@Patch(":id")
|
||||
update(@Param() paramDto: ParamDto, @Body() updateSliderDto: UpdateSliderDto, @BusinessDec("id") businessId: string) {
|
||||
return this.slidersService.update(paramDto.id, updateSliderDto, businessId);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "Delete a slider (admin route)" })
|
||||
@Delete(":id")
|
||||
remove(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
|
||||
return this.slidersService.remove(paramDto.id, businessId);
|
||||
}
|
||||
}
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
import { MikroOrmModule } from "@mikro-orm/nestjs";
|
||||
import { Module } from "@nestjs/common";
|
||||
|
||||
import { Slider } from "./entities/slider.entity";
|
||||
import { SlidersService } from "./providers/sliders.service";
|
||||
import { SlidersController } from "./sliders.controller";
|
||||
import { BusinessesModule } from "../businesses/businesses.module";
|
||||
|
||||
@Module({
|
||||
imports: [MikroOrmModule.forFeature([Slider]), BusinessesModule],
|
||||
controllers: [SlidersController],
|
||||
providers: [SlidersService],
|
||||
exports: [SlidersService],
|
||||
})
|
||||
export class SlidersModule {}
|
||||
@@ -94,22 +94,22 @@ export class UsersService {
|
||||
|
||||
/*******************************/
|
||||
|
||||
async updateUser(id: string, updateData: Partial<User>, em: EntityManager) {
|
||||
const user = await em.findOne(User, { id, deletedAt: null });
|
||||
async updateUser(userId: string, updateData: Partial<User>, em: EntityManager) {
|
||||
const user = await em.findOne(User, { id: userId, deletedAt: null });
|
||||
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
||||
|
||||
if (updateData.phone) {
|
||||
const existingUser = await em.findOne(User, { phone: updateData.phone, deletedAt: null, id: { $ne: id } });
|
||||
const existingUser = await em.findOne(User, { phone: updateData.phone, deletedAt: null, id: { $ne: userId } });
|
||||
if (existingUser) throw new BadRequestException(UserMessage.PHONE_EXIST);
|
||||
}
|
||||
|
||||
if (updateData.email) {
|
||||
const existingUser = await em.findOne(User, { email: updateData.email, deletedAt: null, id: { $ne: id } });
|
||||
const existingUser = await em.findOne(User, { email: updateData.email, deletedAt: null, id: { $ne: userId } });
|
||||
if (existingUser) throw new BadRequestException(UserMessage.EMAIL_EXIST);
|
||||
}
|
||||
|
||||
if (updateData.nationalCode) {
|
||||
const existingUser = await em.findOne(User, { nationalCode: updateData.nationalCode, deletedAt: null, id: { $ne: id } });
|
||||
const existingUser = await em.findOne(User, { nationalCode: updateData.nationalCode, deletedAt: null, id: { $ne: userId } });
|
||||
if (existingUser) throw new BadRequestException(UserMessage.NATIONAL_CODE_EXIST);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user