This commit is contained in:
2025-11-25 15:49:08 +03:30
parent 6f76b36a09
commit e6fb28b87e
4 changed files with 58 additions and 49 deletions
@@ -36,21 +36,17 @@ export class CategoryController {
@UseGuards(AdminAuthGuard) @UseGuards(AdminAuthGuard)
@ApiBearerAuth() @ApiBearerAuth()
@Post('admin/categories') @Post('admin/categories')
create(@Body() dto: CreateCategoryDto) { create(@Body() dto: CreateCategoryDto, @RestId() restId: string) {
return this.categoryService.create(dto); return this.categoryService.create(restId, dto);
} }
@ApiOperation({ summary: 'my restaurant categories' }) @ApiOperation({ summary: 'my restaurant categories' })
@ApiOkResponse({ description: 'List of categories' }) @ApiOkResponse({ description: 'List of categories' })
@ApiQuery({ name: 'restId', required: false, type: String })
@ApiQuery({ name: 'isActive', required: false, type: Boolean })
@UseGuards(AdminAuthGuard) @UseGuards(AdminAuthGuard)
@ApiBearerAuth() @ApiBearerAuth()
@Get('admin/categories') @Get('admin/categories')
findAll(@RestId() restId: string, @Query('isActive') isActive?: string) { findAll(@RestId() restId: string) {
// convert isActive from query string to boolean when provided return this.categoryService.findAllByRestaurantId(restId);
const isActiveBool: boolean | undefined = typeof isActive !== 'undefined' ? isActive === 'true' : undefined;
return this.categoryService.findAll({ restId, isActive: isActiveBool });
} }
@UseGuards(AdminAuthGuard) @UseGuards(AdminAuthGuard)
@@ -1,6 +1,7 @@
import { Entity, Property, Collection, OneToMany } from '@mikro-orm/core'; import { Entity, Property, Collection, OneToMany, ManyToOne } from '@mikro-orm/core';
import { Food } from './food.entity'; import { Food } from './food.entity';
import { BaseEntity } from '../../../common/entities/base.entity'; import { BaseEntity } from '../../../common/entities/base.entity';
import { Restaurant } from 'src/modules/restaurants/entities/restaurant.entity';
@Entity({ tableName: 'categories' }) @Entity({ tableName: 'categories' })
export class Category extends BaseEntity { export class Category extends BaseEntity {
@@ -13,8 +14,8 @@ export class Category extends BaseEntity {
@Property({ default: true }) @Property({ default: true })
isActive: boolean = true; isActive: boolean = true;
@Property({ nullable: true }) @ManyToOne(() => Restaurant)
restId?: string; restaurant!: Restaurant;
@Property({ nullable: true }) @Property({ nullable: true })
avatarUrl?: string; avatarUrl?: string;
+35 -28
View File
@@ -3,7 +3,7 @@ import { CreateCategoryDto } from '../dto/create-category.dto';
import { UpdateCategoryDto } from '../dto/update-category.dto'; import { UpdateCategoryDto } from '../dto/update-category.dto';
import { CategoryRepository } from '../repositories/category.repository'; import { CategoryRepository } from '../repositories/category.repository';
import { EntityManager } from '@mikro-orm/postgresql'; import { EntityManager } from '@mikro-orm/postgresql';
import { RequiredEntityData, FilterQuery } from '@mikro-orm/core'; import { RequiredEntityData } from '@mikro-orm/core';
import { Category } from '../entities/category.entity'; import { Category } from '../entities/category.entity';
import { CategoryMessage, RestMessage } from 'src/common/enums/message.enum'; import { CategoryMessage, RestMessage } from 'src/common/enums/message.enum';
import { Restaurant } from 'src/modules/restaurants/entities/restaurant.entity'; import { Restaurant } from 'src/modules/restaurants/entities/restaurant.entity';
@@ -15,12 +15,15 @@ export class CategoryService {
private readonly em: EntityManager, private readonly em: EntityManager,
) {} ) {}
async create(dto: CreateCategoryDto): Promise<Category> { async create(restId: string, dto: CreateCategoryDto): Promise<Category> {
const restaurant = await this.em.findOne(Restaurant, { id: restId });
if (!restaurant) {
throw new NotFoundException(RestMessage.NOT_FOUND);
}
const data: RequiredEntityData<Category> = { const data: RequiredEntityData<Category> = {
title: dto.title, title: dto.title,
isActive: dto.isActive ?? true, isActive: dto.isActive ?? true,
//TODO restaurant: restaurant,
restId: '01KA35CFFN0F6VA04DF0W6KCE3',
avatarUrl: dto.avatarUrl, avatarUrl: dto.avatarUrl,
}; };
@@ -34,37 +37,41 @@ export class CategoryService {
if (!restaurant || !restaurant.id) { if (!restaurant || !restaurant.id) {
throw new NotFoundException(RestMessage.NOT_FOUND); throw new NotFoundException(RestMessage.NOT_FOUND);
} }
return this.categoryRepository.find({ restId: restaurant.id.toString() }); return this.categoryRepository.find({ restaurant: restaurant });
} }
async findAll(opts?: { restId?: string; isActive?: boolean }): Promise<Category[]> { async findAllByRestaurantId(restId: string): Promise<Category[]> {
const where: FilterQuery<Category> = {}; return this.categoryRepository.find({ restaurant: { id: restId } });
if (opts?.restId) where.restId = opts.restId;
if (opts && typeof opts.isActive === 'boolean') where.isActive = opts.isActive;
const cats = await this.categoryRepository.find(where, { populate: ['foods'] });
// Return plain objects to avoid circular references during JSON serialization
return cats.map(cat => ({
id: cat.id,
title: cat.title,
isActive: cat.isActive,
restId: cat.restId,
avatarUrl: cat.avatarUrl,
createdAt: cat.createdAt,
updatedAt: cat.updatedAt,
foods: cat.foods.getItems().map(f => ({ id: f.id, title: f.title })),
})) as unknown as Category[];
} }
// async findAll(opts?: { restId?: string; isActive?: boolean }): Promise<Category[]> {
// const where: FilterQuery<Category> = {};
// if (opts?.restId) where.restId = opts.restId;
// if (opts && typeof opts.isActive === 'boolean') where.isActive = opts.isActive;
// const cats = await this.categoryRepository.find(where, { populate: ['foods'] });
// // Return plain objects to avoid circular references during JSON serialization
// return cats.map(cat => ({
// id: cat.id,
// title: cat.title,
// isActive: cat.isActive,
// restId: cat.restId,
// avatarUrl: cat.avatarUrl,
// createdAt: cat.createdAt,
// updatedAt: cat.updatedAt,
// foods: cat.foods.getItems().map(f => ({ id: f.id, title: f.title })),
// })) as unknown as Category[];
// }
async findOne(restId: string, id: string): Promise<Category> { async findOne(restId: string, id: string): Promise<Category> {
const cat = await this.categoryRepository.findOne({ id, restId }, { populate: ['foods'] }); const cat = await this.categoryRepository.findOne({ id, restaurant: { id: restId } }, { populate: ['foods'] });
if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND); if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND);
return { return {
id: cat.id, id: cat.id,
title: cat.title, title: cat.title,
isActive: cat.isActive, isActive: cat.isActive,
restId: cat.restId, restaurant: cat.restaurant,
avatarUrl: cat.avatarUrl, avatarUrl: cat.avatarUrl,
createdAt: cat.createdAt, createdAt: cat.createdAt,
updatedAt: cat.updatedAt, updatedAt: cat.updatedAt,
@@ -73,14 +80,14 @@ export class CategoryService {
} }
async findRestaurantCategories(restId: string): Promise<Category> { async findRestaurantCategories(restId: string): Promise<Category> {
const cat = await this.categoryRepository.findOne({ restId }, { populate: ['foods'] }); const cat = await this.categoryRepository.findOne({ restaurant: { id: restId } }, { populate: ['foods'] });
if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND); if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND);
return { return {
id: cat.id, id: cat.id,
title: cat.title, title: cat.title,
isActive: cat.isActive, isActive: cat.isActive,
restId: cat.restId, restaurant: cat.restaurant,
avatarUrl: cat.avatarUrl, avatarUrl: cat.avatarUrl,
createdAt: cat.createdAt, createdAt: cat.createdAt,
updatedAt: cat.updatedAt, updatedAt: cat.updatedAt,
@@ -88,7 +95,7 @@ export class CategoryService {
} }
async update(restId: string, id: string, dto: UpdateCategoryDto): Promise<Category> { async update(restId: string, id: string, dto: UpdateCategoryDto): Promise<Category> {
const cat = await this.categoryRepository.findOne({ id, restId }); const cat = await this.categoryRepository.findOne({ id, restaurant: { id: restId } });
if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND); if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND);
this.em.assign(cat, dto); this.em.assign(cat, dto);
await this.em.persistAndFlush(cat); await this.em.persistAndFlush(cat);
@@ -96,7 +103,7 @@ export class CategoryService {
} }
async remove(restId: string, id: string): Promise<void> { async remove(restId: string, id: string): Promise<void> {
const cat = await this.categoryRepository.findOne({ id, restId }); const cat = await this.categoryRepository.findOne({ id, restaurant: { id: restId } });
if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND); if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND);
cat.deletedAt = new Date(); cat.deletedAt = new Date();
await this.em.persistAndFlush(cat); await this.em.persistAndFlush(cat);
+15 -10
View File
@@ -235,23 +235,28 @@ export class DatabaseSeeder extends Seeder {
// 6. Create Categories (Farsi) // 6. Create Categories (Farsi)
const categories = [ const categories = [
{ title: 'غذای اصلی', restId: zhivan?.id }, { title: 'غذای اصلی', restaurant: zhivan },
{ title: 'پیش غذا', restId: zhivan?.id }, { title: 'پیش غذا', restaurant: zhivan },
{ title: 'دسر', restId: zhivan?.id }, { title: 'دسر', restaurant: zhivan },
{ title: 'نوشیدنی', restId: zhivan?.id }, { title: 'نوشیدنی', restaurant: zhivan },
{ title: 'غذای اصلی', restId: boote?.id }, { title: 'غذای اصلی', restaurant: boote },
{ title: 'پیش غذا', restId: boote?.id }, { title: 'پیش غذا', restaurant: boote },
{ title: 'دسر', restId: boote?.id }, { title: 'دسر', restaurant: boote },
{ title: 'نوشیدنی', restId: boote?.id }, { title: 'نوشیدنی', restaurant: boote },
]; ];
const createdCategories: Category[] = []; const createdCategories: Category[] = [];
for (const catData of categories) { for (const catData of categories) {
const existing = await em.findOne(Category, { title: catData.title, restId: catData.restId }); if (!catData.restaurant) continue;
const existing = await em.findOne(Category, {
title: catData.title,
restaurant: catData.restaurant,
});
if (!existing) { if (!existing) {
const category = em.create(Category, { const category = em.create(Category, {
title: catData.title, title: catData.title,
restId: catData.restId, restaurant: catData.restaurant,
isActive: true, isActive: true,
}); });
em.persist(category); em.persist(category);