add business category
This commit is contained in:
@@ -12,6 +12,7 @@ import { AttributeDefinitionsModule } from './modules/attribute-definitions/attr
|
|||||||
import { BusinessAttributeValuesModule } from './modules/business-attribute-values/business-attribute-values.module';
|
import { BusinessAttributeValuesModule } from './modules/business-attribute-values/business-attribute-values.module';
|
||||||
import { BookingsModule } from './modules/bookings/bookings.module';
|
import { BookingsModule } from './modules/bookings/bookings.module';
|
||||||
import { BusinessRegistrationRequestsModule } from './modules/business-registration-requests/business-registration-requests.module';
|
import { BusinessRegistrationRequestsModule } from './modules/business-registration-requests/business-registration-requests.module';
|
||||||
|
import { BusinessServiceRequestsModule } from './modules/business-service-requests/business-service-requests.module';
|
||||||
import { AuthModule } from './modules/auth/auth.module';
|
import { AuthModule } from './modules/auth/auth.module';
|
||||||
import { UsersModule } from './modules/users/users.module';
|
import { UsersModule } from './modules/users/users.module';
|
||||||
|
|
||||||
@@ -27,6 +28,7 @@ import { UsersModule } from './modules/users/users.module';
|
|||||||
BusinessAttributeValuesModule,
|
BusinessAttributeValuesModule,
|
||||||
BookingsModule,
|
BookingsModule,
|
||||||
BusinessRegistrationRequestsModule,
|
BusinessRegistrationRequestsModule,
|
||||||
|
BusinessServiceRequestsModule,
|
||||||
AuthModule,
|
AuthModule,
|
||||||
UsersModule,
|
UsersModule,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class CreateBusinessServiceRequests1789044000000
|
||||||
|
implements MigrationInterface
|
||||||
|
{
|
||||||
|
name = 'CreateBusinessServiceRequests1789044000000';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS "business_service_requests" (
|
||||||
|
"id" uuid NOT NULL DEFAULT gen_random_uuid(),
|
||||||
|
"businessId" uuid NOT NULL,
|
||||||
|
"requestedById" uuid NOT NULL,
|
||||||
|
"status" varchar(20) NOT NULL DEFAULT 'pending',
|
||||||
|
"title" varchar(150) NOT NULL,
|
||||||
|
"description" varchar(500),
|
||||||
|
"price" numeric(12,2) NOT NULL,
|
||||||
|
"durationMinutes" integer,
|
||||||
|
"adminNote" varchar(500),
|
||||||
|
"reviewedById" uuid,
|
||||||
|
"reviewedAt" TIMESTAMPTZ,
|
||||||
|
"serviceTemplateId" uuid,
|
||||||
|
"businessServiceId" uuid,
|
||||||
|
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||||||
|
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||||||
|
CONSTRAINT "PK_business_service_requests" PRIMARY KEY ("id"),
|
||||||
|
CONSTRAINT "FK_bsr_business" FOREIGN KEY ("businessId")
|
||||||
|
REFERENCES "businesses"("id") ON DELETE CASCADE,
|
||||||
|
CONSTRAINT "FK_bsr_requested_by" FOREIGN KEY ("requestedById")
|
||||||
|
REFERENCES "users"("id") ON DELETE CASCADE,
|
||||||
|
CONSTRAINT "FK_bsr_reviewed_by" FOREIGN KEY ("reviewedById")
|
||||||
|
REFERENCES "users"("id") ON DELETE SET NULL,
|
||||||
|
CONSTRAINT "FK_bsr_template" FOREIGN KEY ("serviceTemplateId")
|
||||||
|
REFERENCES "service_templates"("id") ON DELETE SET NULL,
|
||||||
|
CONSTRAINT "FK_bsr_business_service" FOREIGN KEY ("businessServiceId")
|
||||||
|
REFERENCES "business_services"("id") ON DELETE SET NULL
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
await queryRunner.query(`
|
||||||
|
CREATE INDEX IF NOT EXISTS "IDX_bsr_business_status"
|
||||||
|
ON "business_service_requests" ("businessId", "status")
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`DROP TABLE IF EXISTS "business_service_requests"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import {
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
JoinColumn,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { BusinessEntity } from './business.entity';
|
||||||
|
import { BusinessServiceEntity } from './business-service.entity';
|
||||||
|
import { ServiceTemplateEntity } from './service-template.entity';
|
||||||
|
import { UserEntity } from './user.entity';
|
||||||
|
|
||||||
|
export enum BusinessServiceRequestStatus {
|
||||||
|
PENDING = 'pending',
|
||||||
|
APPROVED = 'approved',
|
||||||
|
REJECTED = 'rejected',
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity('business_service_requests')
|
||||||
|
export class BusinessServiceRequestEntity {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => BusinessEntity, { onDelete: 'CASCADE' })
|
||||||
|
@JoinColumn({ name: 'businessId' })
|
||||||
|
business!: BusinessEntity;
|
||||||
|
|
||||||
|
@Column('uuid')
|
||||||
|
businessId!: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => UserEntity, { onDelete: 'CASCADE' })
|
||||||
|
@JoinColumn({ name: 'requestedById' })
|
||||||
|
requestedBy!: UserEntity;
|
||||||
|
|
||||||
|
@Column('uuid')
|
||||||
|
requestedById!: string;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'varchar',
|
||||||
|
length: 20,
|
||||||
|
default: BusinessServiceRequestStatus.PENDING,
|
||||||
|
})
|
||||||
|
status!: BusinessServiceRequestStatus;
|
||||||
|
|
||||||
|
@Column({ length: 150 })
|
||||||
|
title!: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', nullable: true, length: 500 })
|
||||||
|
description!: string | null;
|
||||||
|
|
||||||
|
@Column({ type: 'numeric', precision: 12, scale: 2 })
|
||||||
|
price!: string;
|
||||||
|
|
||||||
|
@Column({ type: 'int', nullable: true })
|
||||||
|
durationMinutes!: number | null;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', nullable: true, length: 500 })
|
||||||
|
adminNote!: string | null;
|
||||||
|
|
||||||
|
@ManyToOne(() => UserEntity, { nullable: true, onDelete: 'SET NULL' })
|
||||||
|
@JoinColumn({ name: 'reviewedById' })
|
||||||
|
reviewedBy!: UserEntity | null;
|
||||||
|
|
||||||
|
@Column({ type: 'uuid', nullable: true })
|
||||||
|
reviewedById!: string | null;
|
||||||
|
|
||||||
|
@Column({ type: 'timestamptz', nullable: true })
|
||||||
|
reviewedAt!: Date | null;
|
||||||
|
|
||||||
|
@ManyToOne(() => ServiceTemplateEntity, { nullable: true, onDelete: 'SET NULL' })
|
||||||
|
@JoinColumn({ name: 'serviceTemplateId' })
|
||||||
|
serviceTemplate!: ServiceTemplateEntity | null;
|
||||||
|
|
||||||
|
@Column({ type: 'uuid', nullable: true })
|
||||||
|
serviceTemplateId!: string | null;
|
||||||
|
|
||||||
|
@ManyToOne(() => BusinessServiceEntity, { nullable: true, onDelete: 'SET NULL' })
|
||||||
|
@JoinColumn({ name: 'businessServiceId' })
|
||||||
|
businessService!: BusinessServiceEntity | null;
|
||||||
|
|
||||||
|
@Column({ type: 'uuid', nullable: true })
|
||||||
|
businessServiceId!: string | null;
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt!: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt!: Date;
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
Post,
|
||||||
|
Query,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
BusinessServiceRequestEntity,
|
||||||
|
BusinessServiceRequestStatus,
|
||||||
|
} from '../../entities/business-service-request.entity';
|
||||||
|
import { UserRole } from '../../entities/user.entity';
|
||||||
|
import { AuthUser } from '../auth/auth.types';
|
||||||
|
import { CurrentUser } from '../auth/decorators/current-user.decorator';
|
||||||
|
import { Roles } from '../auth/decorators/roles.decorator';
|
||||||
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
||||||
|
import { RolesGuard } from '../auth/guards/roles.guard';
|
||||||
|
import { BusinessServiceRequestsService } from './business-service-requests.service';
|
||||||
|
import { CreateBusinessServiceRequestDto } from './dto/create-business-service-request.dto';
|
||||||
|
import { RejectBusinessServiceRequestDto } from './dto/reject-business-service-request.dto';
|
||||||
|
|
||||||
|
@Controller('business-service-requests')
|
||||||
|
export class BusinessServiceRequestsController {
|
||||||
|
constructor(private readonly service: BusinessServiceRequestsService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
|
@Roles(UserRole.BUSINESS_OWNER)
|
||||||
|
create(
|
||||||
|
@CurrentUser() user: AuthUser,
|
||||||
|
@Body() payload: CreateBusinessServiceRequestDto,
|
||||||
|
): Promise<BusinessServiceRequestEntity> {
|
||||||
|
return this.service.create(user.id, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('mine')
|
||||||
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
|
@Roles(UserRole.BUSINESS_OWNER)
|
||||||
|
findMine(
|
||||||
|
@CurrentUser() user: AuthUser,
|
||||||
|
): Promise<BusinessServiceRequestEntity[]> {
|
||||||
|
return this.service.findMine(user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
|
@Roles(UserRole.ADMIN)
|
||||||
|
findAll(
|
||||||
|
@Query('status') status?: BusinessServiceRequestStatus,
|
||||||
|
): Promise<BusinessServiceRequestEntity[]> {
|
||||||
|
return this.service.findAll({ status });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
findOne(
|
||||||
|
@Param('id') id: string,
|
||||||
|
@CurrentUser() user: AuthUser,
|
||||||
|
): Promise<BusinessServiceRequestEntity> {
|
||||||
|
return this.service.findOne(id).then((entity) => {
|
||||||
|
this.service.assertCanView(entity, user);
|
||||||
|
return entity;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post(':id/approve')
|
||||||
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
|
@Roles(UserRole.ADMIN)
|
||||||
|
approve(
|
||||||
|
@Param('id') id: string,
|
||||||
|
@CurrentUser() user: AuthUser,
|
||||||
|
): Promise<BusinessServiceRequestEntity> {
|
||||||
|
return this.service.approve(id, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post(':id/reject')
|
||||||
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
|
@Roles(UserRole.ADMIN)
|
||||||
|
reject(
|
||||||
|
@Param('id') id: string,
|
||||||
|
@CurrentUser() user: AuthUser,
|
||||||
|
@Body() payload: RejectBusinessServiceRequestDto,
|
||||||
|
): Promise<BusinessServiceRequestEntity> {
|
||||||
|
return this.service.reject(id, user, payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { BusinessServiceRequestEntity } from '../../entities/business-service-request.entity';
|
||||||
|
import { BusinessEntity } from '../../entities/business.entity';
|
||||||
|
import { UserEntity } from '../../entities/user.entity';
|
||||||
|
import { AuthModule } from '../auth/auth.module';
|
||||||
|
import { BusinessServicesModule } from '../business-services/business-services.module';
|
||||||
|
import { ServiceTemplatesModule } from '../service-templates/service-templates.module';
|
||||||
|
import { BusinessServiceRequestsController } from './business-service-requests.controller';
|
||||||
|
import { BusinessServiceRequestsService } from './business-service-requests.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
TypeOrmModule.forFeature([
|
||||||
|
BusinessServiceRequestEntity,
|
||||||
|
BusinessEntity,
|
||||||
|
UserEntity,
|
||||||
|
]),
|
||||||
|
AuthModule,
|
||||||
|
ServiceTemplatesModule,
|
||||||
|
BusinessServicesModule,
|
||||||
|
],
|
||||||
|
controllers: [BusinessServiceRequestsController],
|
||||||
|
providers: [BusinessServiceRequestsService],
|
||||||
|
})
|
||||||
|
export class BusinessServiceRequestsModule {}
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
import {
|
||||||
|
BadRequestException,
|
||||||
|
ConflictException,
|
||||||
|
ForbiddenException,
|
||||||
|
Injectable,
|
||||||
|
NotFoundException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import {
|
||||||
|
BusinessServiceRequestEntity,
|
||||||
|
BusinessServiceRequestStatus,
|
||||||
|
} from '../../entities/business-service-request.entity';
|
||||||
|
import { BusinessEntity } from '../../entities/business.entity';
|
||||||
|
import { UserEntity, UserRole } from '../../entities/user.entity';
|
||||||
|
import { AuthUser } from '../auth/auth.types';
|
||||||
|
import { BusinessServicesService } from '../business-services/business-services.service';
|
||||||
|
import { ServiceTemplatesService } from '../service-templates/service-templates.service';
|
||||||
|
import { CreateBusinessServiceRequestDto } from './dto/create-business-service-request.dto';
|
||||||
|
import { RejectBusinessServiceRequestDto } from './dto/reject-business-service-request.dto';
|
||||||
|
|
||||||
|
type ListFilters = {
|
||||||
|
status?: BusinessServiceRequestStatus;
|
||||||
|
};
|
||||||
|
|
||||||
|
function normalizePrice(value: number): string {
|
||||||
|
if (!Number.isFinite(value) || value < 0) {
|
||||||
|
throw new BadRequestException('price must be a non-negative finite number');
|
||||||
|
}
|
||||||
|
return value.toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BusinessServiceRequestsService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(BusinessServiceRequestEntity)
|
||||||
|
private readonly repo: Repository<BusinessServiceRequestEntity>,
|
||||||
|
@InjectRepository(BusinessEntity)
|
||||||
|
private readonly businessRepo: Repository<BusinessEntity>,
|
||||||
|
@InjectRepository(UserEntity)
|
||||||
|
private readonly userRepo: Repository<UserEntity>,
|
||||||
|
private readonly templatesService: ServiceTemplatesService,
|
||||||
|
private readonly businessServicesService: BusinessServicesService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async create(
|
||||||
|
ownerId: string,
|
||||||
|
dto: CreateBusinessServiceRequestDto,
|
||||||
|
): Promise<BusinessServiceRequestEntity> {
|
||||||
|
const business = await this.businessRepo.findOne({ where: { ownerId } });
|
||||||
|
if (!business) {
|
||||||
|
throw new BadRequestException('No business linked to your account');
|
||||||
|
}
|
||||||
|
|
||||||
|
const pending = await this.repo.findOne({
|
||||||
|
where: {
|
||||||
|
businessId: business.id,
|
||||||
|
title: dto.title.trim(),
|
||||||
|
status: BusinessServiceRequestStatus.PENDING,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (pending) {
|
||||||
|
throw new ConflictException('You already have a pending request with this title');
|
||||||
|
}
|
||||||
|
|
||||||
|
const entity = this.repo.create({
|
||||||
|
businessId: business.id,
|
||||||
|
requestedById: ownerId,
|
||||||
|
status: BusinessServiceRequestStatus.PENDING,
|
||||||
|
title: dto.title.trim(),
|
||||||
|
description: dto.description?.trim() ?? null,
|
||||||
|
price: normalizePrice(dto.price),
|
||||||
|
durationMinutes: dto.durationMinutes ?? null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const saved = await this.repo.save(entity);
|
||||||
|
return this.findOne(saved.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
findMine(ownerId: string): Promise<BusinessServiceRequestEntity[]> {
|
||||||
|
return this.repo.find({
|
||||||
|
where: { requestedById: ownerId },
|
||||||
|
relations: { business: true, serviceTemplate: true, businessService: true },
|
||||||
|
order: { createdAt: 'DESC' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
findAll(filters: ListFilters): Promise<BusinessServiceRequestEntity[]> {
|
||||||
|
const qb = this.repo
|
||||||
|
.createQueryBuilder('req')
|
||||||
|
.leftJoinAndSelect('req.business', 'business')
|
||||||
|
.leftJoinAndSelect('req.requestedBy', 'requestedBy')
|
||||||
|
.leftJoinAndSelect('req.serviceTemplate', 'serviceTemplate')
|
||||||
|
.orderBy('req.createdAt', 'DESC');
|
||||||
|
|
||||||
|
if (filters.status) {
|
||||||
|
qb.andWhere('req.status = :status', { status: filters.status });
|
||||||
|
}
|
||||||
|
|
||||||
|
return qb.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOne(id: string): Promise<BusinessServiceRequestEntity> {
|
||||||
|
const entity = await this.repo.findOne({
|
||||||
|
where: { id },
|
||||||
|
relations: {
|
||||||
|
business: true,
|
||||||
|
requestedBy: true,
|
||||||
|
reviewedBy: true,
|
||||||
|
serviceTemplate: true,
|
||||||
|
businessService: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!entity) {
|
||||||
|
throw new NotFoundException('Service request not found');
|
||||||
|
}
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
async approve(id: string, admin: AuthUser): Promise<BusinessServiceRequestEntity> {
|
||||||
|
const request = await this.findOne(id);
|
||||||
|
if (request.status !== BusinessServiceRequestStatus.PENDING) {
|
||||||
|
throw new BadRequestException('Only pending requests can be approved');
|
||||||
|
}
|
||||||
|
|
||||||
|
const business = await this.businessRepo.findOne({
|
||||||
|
where: { id: request.businessId },
|
||||||
|
});
|
||||||
|
if (!business) {
|
||||||
|
throw new NotFoundException('Business not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const template = await this.templatesService.create({
|
||||||
|
title: request.title,
|
||||||
|
categoryId: business.categoryId,
|
||||||
|
description: request.description ?? undefined,
|
||||||
|
defaultDurationMinutes: request.durationMinutes ?? undefined,
|
||||||
|
isActive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const price = Number.parseFloat(request.price);
|
||||||
|
const service = await this.businessServicesService.create(
|
||||||
|
{
|
||||||
|
businessId: request.businessId,
|
||||||
|
serviceTemplateId: template.id,
|
||||||
|
price,
|
||||||
|
durationMinutes: request.durationMinutes ?? undefined,
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
admin,
|
||||||
|
);
|
||||||
|
|
||||||
|
request.status = BusinessServiceRequestStatus.APPROVED;
|
||||||
|
request.serviceTemplateId = template.id;
|
||||||
|
request.businessServiceId = service.id;
|
||||||
|
request.reviewedById = admin.id;
|
||||||
|
request.reviewedAt = new Date();
|
||||||
|
request.adminNote = null;
|
||||||
|
|
||||||
|
const saved = await this.repo.save(request);
|
||||||
|
return this.findOne(saved.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async reject(
|
||||||
|
id: string,
|
||||||
|
admin: AuthUser,
|
||||||
|
dto: RejectBusinessServiceRequestDto,
|
||||||
|
): Promise<BusinessServiceRequestEntity> {
|
||||||
|
const request = await this.findOne(id);
|
||||||
|
if (request.status !== BusinessServiceRequestStatus.PENDING) {
|
||||||
|
throw new BadRequestException('Only pending requests can be rejected');
|
||||||
|
}
|
||||||
|
|
||||||
|
request.status = BusinessServiceRequestStatus.REJECTED;
|
||||||
|
request.adminNote = dto.adminNote?.trim() ?? null;
|
||||||
|
request.reviewedById = admin.id;
|
||||||
|
request.reviewedAt = new Date();
|
||||||
|
|
||||||
|
const saved = await this.repo.save(request);
|
||||||
|
return this.findOne(saved.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertCanView(request: BusinessServiceRequestEntity, user: AuthUser): void {
|
||||||
|
if (user.role === UserRole.ADMIN) return;
|
||||||
|
if (request.requestedById === user.id) return;
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
import { Type } from 'class-transformer';
|
||||||
|
import {
|
||||||
|
IsInt,
|
||||||
|
IsNumber,
|
||||||
|
IsOptional,
|
||||||
|
IsString,
|
||||||
|
Max,
|
||||||
|
MaxLength,
|
||||||
|
Min,
|
||||||
|
MinLength,
|
||||||
|
} from 'class-validator';
|
||||||
|
|
||||||
|
export class CreateBusinessServiceRequestDto {
|
||||||
|
@ApiProperty({ example: 'رنگ موی تخصصی' })
|
||||||
|
@IsString()
|
||||||
|
@MinLength(2)
|
||||||
|
@MaxLength(150)
|
||||||
|
title!: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@MaxLength(500)
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ example: 250000 })
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsNumber()
|
||||||
|
@Min(0)
|
||||||
|
price!: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ example: 45 })
|
||||||
|
@IsOptional()
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
@Max(24 * 60)
|
||||||
|
durationMinutes?: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
import { IsOptional, IsString, MaxLength } from 'class-validator';
|
||||||
|
|
||||||
|
export class RejectBusinessServiceRequestDto {
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@MaxLength(500)
|
||||||
|
adminNote?: string;
|
||||||
|
}
|
||||||
@@ -18,5 +18,6 @@ import { BusinessServicesService } from './business-services.service';
|
|||||||
],
|
],
|
||||||
controllers: [BusinessServicesController],
|
controllers: [BusinessServicesController],
|
||||||
providers: [BusinessServicesService],
|
providers: [BusinessServicesService],
|
||||||
|
exports: [BusinessServicesService],
|
||||||
})
|
})
|
||||||
export class BusinessServicesModule {}
|
export class BusinessServicesModule {}
|
||||||
|
|||||||
@@ -13,5 +13,6 @@ import { ServiceTemplatesService } from './service-templates.service';
|
|||||||
],
|
],
|
||||||
controllers: [ServiceTemplatesController],
|
controllers: [ServiceTemplatesController],
|
||||||
providers: [ServiceTemplatesService],
|
providers: [ServiceTemplatesService],
|
||||||
|
exports: [ServiceTemplatesService],
|
||||||
})
|
})
|
||||||
export class ServiceTemplatesModule {}
|
export class ServiceTemplatesModule {}
|
||||||
|
|||||||
Reference in New Issue
Block a user