restauarant list in sup
This commit is contained in:
Vendored
+1
-1
@@ -7,6 +7,6 @@
|
|||||||
"source.fixAll.eslint": "explicit"
|
"source.fixAll.eslint": "explicit"
|
||||||
},
|
},
|
||||||
"[typescript]": {
|
"[typescript]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Controller, Get, Post, Body, Patch, Param, UseGuards, Delete } from '@nestjs/common';
|
import { Controller, Get, Post, Body, Patch, Param, UseGuards, Delete, Query } from '@nestjs/common';
|
||||||
import { RestaurantsService } from '../providers/restaurants.service';
|
import { RestaurantsService } from '../providers/restaurants.service';
|
||||||
import { CreateRestaurantDto } from '../dto/create-restaurant.dto';
|
import { CreateRestaurantDto } from '../dto/create-restaurant.dto';
|
||||||
import { UpdateRestaurantDto } from '../dto/update-restaurant.dto';
|
import { UpdateRestaurantDto } from '../dto/update-restaurant.dto';
|
||||||
|
import { FindRestaurantsDto } from '../dto/find-restaurants.dto';
|
||||||
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
||||||
import {
|
import {
|
||||||
ApiBearerAuth,
|
ApiBearerAuth,
|
||||||
@@ -13,6 +14,8 @@ import {
|
|||||||
ApiHeader,
|
ApiHeader,
|
||||||
} from '@nestjs/swagger';
|
} from '@nestjs/swagger';
|
||||||
import { RestId } from 'src/common/decorators';
|
import { RestId } from 'src/common/decorators';
|
||||||
|
import { API_HEADER_SLUG } from 'src/common/constants';
|
||||||
|
import { SuperAdminAuthGuard } from 'src/modules/auth/guards/superAdminAuth.guard';
|
||||||
|
|
||||||
@ApiTags('restaurants')
|
@ApiTags('restaurants')
|
||||||
@Controller()
|
@Controller()
|
||||||
@@ -21,20 +24,14 @@ export class RestaurantsController {
|
|||||||
|
|
||||||
@Get('public/restaurants/:slug')
|
@Get('public/restaurants/:slug')
|
||||||
@ApiOperation({ summary: 'Get restaurant specification by slug' })
|
@ApiOperation({ summary: 'Get restaurant specification by slug' })
|
||||||
@ApiHeader({
|
@ApiHeader(API_HEADER_SLUG)
|
||||||
name: 'X-Slug',
|
|
||||||
required: true,
|
|
||||||
schema: {
|
|
||||||
type: 'string',
|
|
||||||
default: 'zhivan',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@ApiParam({ name: 'slug', required: true, description: 'Restaurant slug' })
|
@ApiParam({ name: 'slug', required: true, description: 'Restaurant slug' })
|
||||||
@ApiNotFoundResponse({ description: 'Restaurant not found' })
|
@ApiNotFoundResponse({ description: 'Restaurant not found' })
|
||||||
findBySlug(@Param('slug') slug: string) {
|
findBySlug(@Param('slug') slug: string) {
|
||||||
return this.restaurantsService.getRestaurantSpecification(slug);
|
return this.restaurantsService.getRestaurantSpecification(slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Admin Endpoints */
|
||||||
@UseGuards(AdminAuthGuard)
|
@UseGuards(AdminAuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Post('admin/restaurants')
|
@Post('admin/restaurants')
|
||||||
@@ -44,13 +41,6 @@ export class RestaurantsController {
|
|||||||
return this.restaurantsService.create(createRestaurantDto);
|
return this.restaurantsService.create(createRestaurantDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Get('admin/restaurants')
|
|
||||||
// @UseGuards(AdminAuthGuard)
|
|
||||||
// @ApiBearerAuth()
|
|
||||||
// @ApiOperation({ summary: 'Get all restaurants' })
|
|
||||||
// findAll() {
|
|
||||||
// return this.restaurantsService.findAll();
|
|
||||||
// }
|
|
||||||
|
|
||||||
@UseGuards(AdminAuthGuard)
|
@UseGuards(AdminAuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@@ -86,4 +76,13 @@ export class RestaurantsController {
|
|||||||
remove(@Param('id') id: string) {
|
remove(@Param('id') id: string) {
|
||||||
return this.restaurantsService.remove(id);
|
return this.restaurantsService.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Super Admin Endpoints */
|
||||||
|
// @UseGuards(SuperAdminAuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiOperation({ summary: 'Get all restaurants with pagination and filters' })
|
||||||
|
@Get('super-admin/restaurants')
|
||||||
|
findAll(@Query() dto: FindRestaurantsDto) {
|
||||||
|
return this.restaurantsService.findAll(dto);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import {
|
||||||
|
IsOptional,
|
||||||
|
IsString,
|
||||||
|
IsNumber,
|
||||||
|
Min,
|
||||||
|
IsIn,
|
||||||
|
IsEnum,
|
||||||
|
IsBoolean,
|
||||||
|
} from 'class-validator';
|
||||||
|
import { Type, Transform } from 'class-transformer';
|
||||||
|
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
import { PlanEnum } from '../interface/plan.interface';
|
||||||
|
|
||||||
|
const sortOrderOptions = ['asc', 'desc'] as const;
|
||||||
|
type SortOrder = (typeof sortOrderOptions)[number];
|
||||||
|
|
||||||
|
export class FindRestaurantsDto {
|
||||||
|
@ApiPropertyOptional({ default: 1, minimum: 1 })
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsNumber()
|
||||||
|
@Min(1)
|
||||||
|
page: number = 1;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ default: 10, minimum: 1 })
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsNumber()
|
||||||
|
@Min(1)
|
||||||
|
limit: number = 10;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Search by name, slug, domain, or address',
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
search?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Filter by active status',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Transform(({ value }) => {
|
||||||
|
if (value === 'true') return true;
|
||||||
|
if (value === 'false') return false;
|
||||||
|
return value;
|
||||||
|
})
|
||||||
|
@IsBoolean()
|
||||||
|
isActive?: boolean;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
enum: PlanEnum,
|
||||||
|
description: 'Filter by plan type',
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(PlanEnum)
|
||||||
|
plan?: PlanEnum;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ default: 'createdAt' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
orderBy: string = 'createdAt';
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
enum: sortOrderOptions,
|
||||||
|
default: 'desc',
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsIn(sortOrderOptions)
|
||||||
|
order: SortOrder = 'desc';
|
||||||
|
}
|
||||||
|
|
||||||
@@ -5,6 +5,8 @@ import { Restaurant } from '../entities/restaurant.entity';
|
|||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { RestRepository } from '../repositories/rest.repository';
|
import { RestRepository } from '../repositories/rest.repository';
|
||||||
import { RestMessage } from 'src/common/enums/message.enum';
|
import { RestMessage } from 'src/common/enums/message.enum';
|
||||||
|
import { FindRestaurantsDto } from '../dto/find-restaurants.dto';
|
||||||
|
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RestaurantsService {
|
export class RestaurantsService {
|
||||||
@@ -24,8 +26,16 @@ export class RestaurantsService {
|
|||||||
return restaurant;
|
return restaurant;
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll() {
|
async findAll(dto: FindRestaurantsDto): Promise<PaginatedResult<Restaurant>> {
|
||||||
return this.restRepository.findAll();
|
return this.restRepository.findAllPaginated({
|
||||||
|
page: dto.page,
|
||||||
|
limit: dto.limit,
|
||||||
|
search: dto.search,
|
||||||
|
isActive: dto.isActive,
|
||||||
|
plan: dto.plan,
|
||||||
|
orderBy: dto.orderBy,
|
||||||
|
order: dto.order,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async findBySlug(slug: string): Promise<Restaurant> {
|
async findBySlug(slug: string): Promise<Restaurant> {
|
||||||
|
|||||||
@@ -1,10 +1,79 @@
|
|||||||
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
|
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
|
||||||
|
import { FilterQuery } from '@mikro-orm/core';
|
||||||
import { Restaurant } from '../entities/restaurant.entity';
|
import { Restaurant } from '../entities/restaurant.entity';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
||||||
|
import { PlanEnum } from '../interface/plan.interface';
|
||||||
|
|
||||||
|
type FindRestaurantsOpts = {
|
||||||
|
page?: number;
|
||||||
|
limit?: number;
|
||||||
|
search?: string;
|
||||||
|
isActive?: boolean;
|
||||||
|
plan?: PlanEnum;
|
||||||
|
orderBy?: string;
|
||||||
|
order?: 'asc' | 'desc';
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RestRepository extends EntityRepository<Restaurant> {
|
export class RestRepository extends EntityRepository<Restaurant> {
|
||||||
constructor(readonly em: EntityManager) {
|
constructor(readonly em: EntityManager) {
|
||||||
super(em, Restaurant);
|
super(em, Restaurant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find restaurants with pagination and optional filters.
|
||||||
|
* Supports: search (name/slug/domain/address), isActive, plan, ordering.
|
||||||
|
*/
|
||||||
|
async findAllPaginated(opts: FindRestaurantsOpts = {}): Promise<PaginatedResult<Restaurant>> {
|
||||||
|
const {
|
||||||
|
page = 1,
|
||||||
|
limit = 10,
|
||||||
|
search,
|
||||||
|
isActive,
|
||||||
|
plan,
|
||||||
|
orderBy = 'createdAt',
|
||||||
|
order = 'desc',
|
||||||
|
} = opts;
|
||||||
|
|
||||||
|
const offset = (page - 1) * limit;
|
||||||
|
|
||||||
|
const where: FilterQuery<Restaurant> = {};
|
||||||
|
|
||||||
|
if (typeof isActive === 'boolean') {
|
||||||
|
where.isActive = isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plan) {
|
||||||
|
where.plan = plan;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
const pattern = `%${search}%`;
|
||||||
|
where.$or = [
|
||||||
|
{ name: { $ilike: pattern } },
|
||||||
|
{ slug: { $ilike: pattern } },
|
||||||
|
{ domain: { $ilike: pattern } },
|
||||||
|
{ address: { $ilike: pattern } },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const [data, total] = await this.findAndCount(where, {
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(total / limit);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
meta: {
|
||||||
|
total,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
totalPages,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user