lint
This commit is contained in:
@@ -23,7 +23,7 @@ export function cacheConfig(): CacheModuleAsyncOptions {
|
|||||||
keyvRedisStore.client.on('connect', () => {
|
keyvRedisStore.client.on('connect', () => {
|
||||||
logger.log('✅ Redis connected successfully.');
|
logger.log('✅ Redis connected successfully.');
|
||||||
});
|
});
|
||||||
keyvRedisStore.client.on('error', (error) => {
|
keyvRedisStore.client.on('error', error => {
|
||||||
logger.error('❌ Redis connection error:', error);
|
logger.error('❌ Redis connection error:', error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const dataBaseConfig: MikroOrmModuleAsyncOptions = {
|
|||||||
const encodedPassword = encodeURIComponent(DB_PASS);
|
const encodedPassword = encodeURIComponent(DB_PASS);
|
||||||
const isProduction = configService.getOrThrow<string>('NODE_ENV') === 'production';
|
const isProduction = configService.getOrThrow<string>('NODE_ENV') === 'production';
|
||||||
|
|
||||||
return ({
|
return {
|
||||||
// entities: ['dist/**/*.entity.js'],
|
// entities: ['dist/**/*.entity.js'],
|
||||||
entitiesTs: ['src/**/*.entity.ts'],
|
entitiesTs: ['src/**/*.entity.ts'],
|
||||||
driver: PostgreSqlDriver,
|
driver: PostgreSqlDriver,
|
||||||
@@ -102,7 +102,7 @@ const dataBaseConfig: MikroOrmModuleAsyncOptions = {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -174,5 +174,4 @@ export class AdminService {
|
|||||||
}
|
}
|
||||||
return this.em.removeAndFlush(adminRole);
|
return this.em.removeAndFlush(adminRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { SetMetadata } from '@nestjs/common';
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
|
||||||
export const PERMISSIONS_KEY = 'permissions';
|
export const PERMISSIONS_KEY = 'permissions';
|
||||||
|
|
||||||
export const Permissions = (...permissions: string[]) =>
|
export const Permissions = (...permissions: string[]) => SetMetadata(PERMISSIONS_KEY, permissions);
|
||||||
SetMetadata(PERMISSIONS_KEY, permissions);
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { User } from '../../users/entities/user.entity';
|
import type { User } from '../../users/entities/user.entity';
|
||||||
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
|
import type { Restaurant } from '../../restaurants/entities/restaurant.entity';
|
||||||
|
|
||||||
export interface UserLoginResponse {
|
export interface UserLoginResponse {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -34,4 +34,3 @@ export class UserLoginTransformer {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,14 +22,28 @@ export class CartController {
|
|||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({ summary: 'Get cart for current user and restaurant' })
|
@ApiOperation({ summary: 'Get cart for current user and restaurant' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
async findOne(@UserId() userId: string, @RestId() restaurantId: string) {
|
async findOne(@UserId() userId: string, @RestId() restaurantId: string) {
|
||||||
return this.cartService.getOrCreateCart(userId, restaurantId);
|
return this.cartService.getOrCreateCart(userId, restaurantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('items/:foodId/increment')
|
@Post('items/:foodId/increment')
|
||||||
@ApiOperation({ summary: 'Increment item quantity in cart by 1' })
|
@ApiOperation({ summary: 'Increment item quantity in cart by 1' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiParam({ name: 'foodId', description: 'Food ID to increment in cart' })
|
@ApiParam({ name: 'foodId', description: 'Food ID to increment in cart' })
|
||||||
async incrementItem(@UserId() userId: string, @RestId() restaurantId: string, @Param('foodId') foodId: string) {
|
async incrementItem(@UserId() userId: string, @RestId() restaurantId: string, @Param('foodId') foodId: string) {
|
||||||
return this.cartService.incrementItem(userId, restaurantId, foodId);
|
return this.cartService.incrementItem(userId, restaurantId, foodId);
|
||||||
@@ -37,7 +51,14 @@ export class CartController {
|
|||||||
|
|
||||||
@Post('items/:foodId/decrement')
|
@Post('items/:foodId/decrement')
|
||||||
@ApiOperation({ summary: 'Decrement item quantity in cart by 1 (removes item if quantity reaches 0)' })
|
@ApiOperation({ summary: 'Decrement item quantity in cart by 1 (removes item if quantity reaches 0)' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiParam({ name: 'foodId', description: 'Food ID to decrement in cart' })
|
@ApiParam({ name: 'foodId', description: 'Food ID to decrement in cart' })
|
||||||
async decrementItem(@UserId() userId: string, @RestId() restaurantId: string, @Param('foodId') foodId: string) {
|
async decrementItem(@UserId() userId: string, @RestId() restaurantId: string, @Param('foodId') foodId: string) {
|
||||||
return this.cartService.decrementItem(userId, restaurantId, foodId);
|
return this.cartService.decrementItem(userId, restaurantId, foodId);
|
||||||
@@ -45,7 +66,14 @@ export class CartController {
|
|||||||
|
|
||||||
@Post('items/bulk')
|
@Post('items/bulk')
|
||||||
@ApiOperation({ summary: 'Bulk add items to cart (increments quantity if items exist)' })
|
@ApiOperation({ summary: 'Bulk add items to cart (increments quantity if items exist)' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiBody({ type: BulkAddItemsToCartDto })
|
@ApiBody({ type: BulkAddItemsToCartDto })
|
||||||
bulkAddItems(
|
bulkAddItems(
|
||||||
@UserId() userId: string,
|
@UserId() userId: string,
|
||||||
@@ -57,7 +85,14 @@ export class CartController {
|
|||||||
|
|
||||||
@Patch('items/:foodId')
|
@Patch('items/:foodId')
|
||||||
@ApiOperation({ summary: 'Update item quantity in cart' })
|
@ApiOperation({ summary: 'Update item quantity in cart' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiParam({ name: 'foodId', description: 'Food ID in the cart' })
|
@ApiParam({ name: 'foodId', description: 'Food ID in the cart' })
|
||||||
@ApiBody({ type: UpdateItemQuantityDto })
|
@ApiBody({ type: UpdateItemQuantityDto })
|
||||||
async updateItemQuantity(
|
async updateItemQuantity(
|
||||||
@@ -71,7 +106,14 @@ export class CartController {
|
|||||||
|
|
||||||
@Delete('items/:foodId')
|
@Delete('items/:foodId')
|
||||||
@ApiOperation({ summary: 'Remove item from cart' })
|
@ApiOperation({ summary: 'Remove item from cart' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiParam({ name: 'foodId', description: 'Food ID in the cart' })
|
@ApiParam({ name: 'foodId', description: 'Food ID in the cart' })
|
||||||
async removeItem(@UserId() userId: string, @RestId() restaurantId: string, @Param('foodId') foodId: string) {
|
async removeItem(@UserId() userId: string, @RestId() restaurantId: string, @Param('foodId') foodId: string) {
|
||||||
return this.cartService.removeItem(userId, restaurantId, foodId);
|
return this.cartService.removeItem(userId, restaurantId, foodId);
|
||||||
@@ -79,14 +121,28 @@ export class CartController {
|
|||||||
|
|
||||||
@Delete()
|
@Delete()
|
||||||
@ApiOperation({ summary: 'Clear entire cart' })
|
@ApiOperation({ summary: 'Clear entire cart' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
async clearCart(@UserId() userId: string, @RestId() restaurantId: string) {
|
async clearCart(@UserId() userId: string, @RestId() restaurantId: string) {
|
||||||
return this.cartService.clearCart(userId, restaurantId);
|
return this.cartService.clearCart(userId, restaurantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('apply-coupon')
|
@Post('apply-coupon')
|
||||||
@ApiOperation({ summary: 'Apply coupon to cart' })
|
@ApiOperation({ summary: 'Apply coupon to cart' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiBody({ type: ApplyCouponDto })
|
@ApiBody({ type: ApplyCouponDto })
|
||||||
async applyCoupon(@UserId() userId: string, @RestId() restaurantId: string, @Body() applyCouponDto: ApplyCouponDto) {
|
async applyCoupon(@UserId() userId: string, @RestId() restaurantId: string, @Body() applyCouponDto: ApplyCouponDto) {
|
||||||
return this.cartService.applyCoupon(userId, restaurantId, applyCouponDto);
|
return this.cartService.applyCoupon(userId, restaurantId, applyCouponDto);
|
||||||
@@ -94,14 +150,28 @@ export class CartController {
|
|||||||
|
|
||||||
@Delete('coupon')
|
@Delete('coupon')
|
||||||
@ApiOperation({ summary: 'Remove coupon from cart' })
|
@ApiOperation({ summary: 'Remove coupon from cart' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
async removeCoupon(@UserId() userId: string, @RestId() restaurantId: string) {
|
async removeCoupon(@UserId() userId: string, @RestId() restaurantId: string) {
|
||||||
return this.cartService.removeCoupon(userId, restaurantId);
|
return this.cartService.removeCoupon(userId, restaurantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch('address')
|
@Patch('address')
|
||||||
@ApiOperation({ summary: 'Set address for cart' })
|
@ApiOperation({ summary: 'Set address for cart' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiBody({ type: SetAddressDto })
|
@ApiBody({ type: SetAddressDto })
|
||||||
async setAddress(@UserId() userId: string, @RestId() restaurantId: string, @Body() setAddressDto: SetAddressDto) {
|
async setAddress(@UserId() userId: string, @RestId() restaurantId: string, @Body() setAddressDto: SetAddressDto) {
|
||||||
return this.cartService.setAddress(userId, restaurantId, setAddressDto);
|
return this.cartService.setAddress(userId, restaurantId, setAddressDto);
|
||||||
@@ -109,7 +179,14 @@ export class CartController {
|
|||||||
|
|
||||||
@Patch('payment-method')
|
@Patch('payment-method')
|
||||||
@ApiOperation({ summary: 'Set payment method for cart' })
|
@ApiOperation({ summary: 'Set payment method for cart' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiBody({ type: SetPaymentMethodDto })
|
@ApiBody({ type: SetPaymentMethodDto })
|
||||||
async setPaymentMethod(
|
async setPaymentMethod(
|
||||||
@UserId() userId: string,
|
@UserId() userId: string,
|
||||||
@@ -121,7 +198,14 @@ export class CartController {
|
|||||||
|
|
||||||
@Patch('delivery-method')
|
@Patch('delivery-method')
|
||||||
@ApiOperation({ summary: 'Set delivery method for cart' })
|
@ApiOperation({ summary: 'Set delivery method for cart' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiBody({ type: SetDeliveryMethodDto })
|
@ApiBody({ type: SetDeliveryMethodDto })
|
||||||
setDeliveryMethod(
|
setDeliveryMethod(
|
||||||
@UserId() userId: string,
|
@UserId() userId: string,
|
||||||
@@ -133,7 +217,14 @@ export class CartController {
|
|||||||
|
|
||||||
@Patch('description')
|
@Patch('description')
|
||||||
@ApiOperation({ summary: 'Set description for cart' })
|
@ApiOperation({ summary: 'Set description for cart' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiBody({ type: SetDescriptionDto })
|
@ApiBody({ type: SetDescriptionDto })
|
||||||
setDescription(
|
setDescription(
|
||||||
@UserId() userId: string,
|
@UserId() userId: string,
|
||||||
@@ -145,7 +236,14 @@ export class CartController {
|
|||||||
|
|
||||||
@Patch('table-number')
|
@Patch('table-number')
|
||||||
@ApiOperation({ summary: 'Set table number for cart (required when delivery method is DineIn)' })
|
@ApiOperation({ summary: 'Set table number for cart (required when delivery method is DineIn)' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiBody({ type: SetTableNumberDto })
|
@ApiBody({ type: SetTableNumberDto })
|
||||||
setTableNumber(
|
setTableNumber(
|
||||||
@UserId() userId: string,
|
@UserId() userId: string,
|
||||||
|
|||||||
@@ -7,4 +7,3 @@ export class ApplyCouponDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
code!: string;
|
code!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,4 +7,3 @@ export class SetAddressDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
addressId!: string;
|
addressId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,4 +7,3 @@ export class SetDeliveryMethodDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
deliveryMethodId!: string;
|
deliveryMethodId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
import { IsOptional, IsString } from 'class-validator';
|
import { IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
export class SetDescriptionDto {
|
export class SetDescriptionDto {
|
||||||
@ApiProperty({ description: 'Cart description or notes', required: false, example: 'Please deliver to the back door' })
|
@ApiProperty({
|
||||||
|
description: 'Cart description or notes',
|
||||||
|
required: false,
|
||||||
|
example: 'Please deliver to the back door',
|
||||||
|
})
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,4 +7,3 @@ export class SetPaymentMethodDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
paymentMethodId!: string;
|
paymentMethodId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,4 +7,3 @@ export class SetTableNumberDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
tableNumber?: string;
|
tableNumber?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,3 @@ export class UpdateItemQuantityDto {
|
|||||||
@Min(1)
|
@Min(1)
|
||||||
quantity!: number;
|
quantity!: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -613,11 +613,7 @@ export class CartService {
|
|||||||
/**
|
/**
|
||||||
* Set table number for cart
|
* Set table number for cart
|
||||||
*/
|
*/
|
||||||
async setTableNumber(
|
async setTableNumber(userId: string, restaurantId: string, setTableNumberDto: SetTableNumberDto): Promise<Cart> {
|
||||||
userId: string,
|
|
||||||
restaurantId: string,
|
|
||||||
setTableNumberDto: SetTableNumberDto,
|
|
||||||
): Promise<Cart> {
|
|
||||||
const cart = await this.findOneOrFail(userId, restaurantId);
|
const cart = await this.findOneOrFail(userId, restaurantId);
|
||||||
|
|
||||||
// If delivery method is set, validate that table number is required for DineIn
|
// If delivery method is set, validate that table number is required for DineIn
|
||||||
|
|||||||
@@ -25,7 +25,14 @@ export class ContactController {
|
|||||||
|
|
||||||
@Post('public/contact')
|
@Post('public/contact')
|
||||||
@ApiOperation({ summary: 'Create a new contact (public endpoint)' })
|
@ApiOperation({ summary: 'Create a new contact (public endpoint)' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
async create(@Body() createContactDto: CreateContactDto) {
|
async create(@Body() createContactDto: CreateContactDto) {
|
||||||
return this.contactService.create(createContactDto);
|
return this.contactService.create(createContactDto);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,4 +39,3 @@ export class FindContactsDto {
|
|||||||
@ApiPropertyOptional({ enum: ContactStatusEnum, description: 'Filter by status' })
|
@ApiPropertyOptional({ enum: ContactStatusEnum, description: 'Filter by status' })
|
||||||
status?: ContactStatusEnum;
|
status?: ContactStatusEnum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,3 @@ export class UpdateContactStatusDto {
|
|||||||
@ApiProperty({ enum: ContactStatusEnum, description: 'New status for the contact' })
|
@ApiProperty({ enum: ContactStatusEnum, description: 'New status for the contact' })
|
||||||
status!: ContactStatusEnum;
|
status!: ContactStatusEnum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,11 +37,7 @@ export class ContactRepository extends EntityRepository<Contact> {
|
|||||||
|
|
||||||
if (search) {
|
if (search) {
|
||||||
const pattern = `%${search}%`;
|
const pattern = `%${search}%`;
|
||||||
where.$or = [
|
where.$or = [{ subject: { $ilike: pattern } }, { content: { $ilike: pattern } }, { phone: { $ilike: pattern } }];
|
||||||
{ subject: { $ilike: pattern } },
|
|
||||||
{ content: { $ilike: pattern } },
|
|
||||||
{ phone: { $ilike: pattern } },
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const [data, total] = await this.findAndCount(where, {
|
const [data, total] = await this.findAndCount(where, {
|
||||||
@@ -63,4 +59,3 @@ export class ContactRepository extends EntityRepository<Contact> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,14 @@ export class CouponController {
|
|||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Get('public/coupons/me')
|
@Get('public/coupons/me')
|
||||||
@ApiOperation({ summary: 'Get paginated list of restaurant coupons' })
|
@ApiOperation({ summary: 'Get paginated list of restaurant coupons' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiOkResponse({ description: 'Paginated list of restaurant coupons' })
|
@ApiOkResponse({ description: 'Paginated list of restaurant coupons' })
|
||||||
@ApiQuery({ name: 'page', required: false, type: Number })
|
@ApiQuery({ name: 'page', required: false, type: Number })
|
||||||
@ApiQuery({ name: 'limit', required: false, type: Number })
|
@ApiQuery({ name: 'limit', required: false, type: Number })
|
||||||
|
|||||||
@@ -15,4 +15,3 @@ import { JwtModule } from '@nestjs/jwt';
|
|||||||
exports: [CouponRepository, CouponService],
|
exports: [CouponRepository, CouponService],
|
||||||
})
|
})
|
||||||
export class CouponModule {}
|
export class CouponModule {}
|
||||||
|
|
||||||
|
|||||||
@@ -42,4 +42,3 @@ export class FindCouponsDto {
|
|||||||
@ApiPropertyOptional({ enum: ['asc', 'desc'], example: 'desc', description: 'Sort order' })
|
@ApiPropertyOptional({ enum: ['asc', 'desc'], example: 'desc', description: 'Sort order' })
|
||||||
order?: 'asc' | 'desc';
|
order?: 'asc' | 'desc';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,3 @@ import { PartialType } from '@nestjs/swagger';
|
|||||||
import { CreateCouponDto } from './create-coupon.dto';
|
import { CreateCouponDto } from './create-coupon.dto';
|
||||||
|
|
||||||
export class UpdateCouponDto extends PartialType(CreateCouponDto) {}
|
export class UpdateCouponDto extends PartialType(CreateCouponDto) {}
|
||||||
|
|
||||||
|
|||||||
@@ -19,4 +19,3 @@ export class ValidateCouponDto {
|
|||||||
@ApiPropertyOptional({ example: 'user-id', description: 'User ID for per-user usage limits' })
|
@ApiPropertyOptional({ example: 'user-id', description: 'User ID for per-user usage limits' })
|
||||||
userId?: string;
|
userId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,14 @@ export class DeliveryController {
|
|||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Get('public/delivery-methods/restaurant')
|
@Get('public/delivery-methods/restaurant')
|
||||||
@ApiOperation({ summary: 'Get restaurant delivery methods' })
|
@ApiOperation({ summary: 'Get restaurant delivery methods' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiOkResponse({
|
@ApiOkResponse({
|
||||||
description: 'List of restaurant delivery methods',
|
description: 'List of restaurant delivery methods',
|
||||||
type: [Delivery],
|
type: [Delivery],
|
||||||
|
|||||||
@@ -8,4 +8,3 @@ export class DeliveryRepository extends EntityRepository<Delivery> {
|
|||||||
super(em, Delivery);
|
super(em, Delivery);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,14 @@ export class CategoryController {
|
|||||||
|
|
||||||
@Get('public/categories/restaurant/:slug')
|
@Get('public/categories/restaurant/:slug')
|
||||||
@ApiOperation({ summary: 'Get all categories by restaurant slug' })
|
@ApiOperation({ summary: 'Get all categories by restaurant slug' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
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' })
|
||||||
@ApiOkResponse({ description: 'List of all categories for the restaurant' })
|
@ApiOkResponse({ description: 'List of all categories for the restaurant' })
|
||||||
findAllByRestaurant(@Param('slug') slug: string) {
|
findAllByRestaurant(@Param('slug') slug: string) {
|
||||||
|
|||||||
@@ -25,7 +25,14 @@ export class FoodController {
|
|||||||
|
|
||||||
@Get('public/foods/restaurant/:slug')
|
@Get('public/foods/restaurant/:slug')
|
||||||
@ApiOperation({ summary: 'Get all foods by restaurant slug' })
|
@ApiOperation({ summary: 'Get all foods by restaurant slug' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
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' })
|
||||||
@ApiOkResponse({ description: 'List of all foods for the restaurant' })
|
@ApiOkResponse({ description: 'List of all foods for the restaurant' })
|
||||||
findAllByRestaurant(@Param('slug') slug: string) {
|
findAllByRestaurant(@Param('slug') slug: string) {
|
||||||
|
|||||||
@@ -21,7 +21,14 @@ export class NotificationsController {
|
|||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Get('public/notifications')
|
@Get('public/notifications')
|
||||||
@ApiOperation({ summary: 'Get user restaurant notifications' })
|
@ApiOperation({ summary: 'Get user restaurant notifications' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiQuery({ name: 'limit', required: false, type: Number })
|
@ApiQuery({ name: 'limit', required: false, type: Number })
|
||||||
async getUserNotifications(@UserId() userId: string, @RestId() restaurantId: string, @Query('limit') limit?: number) {
|
async getUserNotifications(@UserId() userId: string, @RestId() restaurantId: string, @Query('limit') limit?: number) {
|
||||||
return await this.notificationService.findByUserAndRestaurant(
|
return await this.notificationService.findByUserAndRestaurant(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
import type { ExecutionContext } from '@nestjs/common';
|
||||||
import { AuthenticatedSocket } from '../guards/ws-admin-auth.guard';
|
import { createParamDecorator } from '@nestjs/common';
|
||||||
|
import type { AuthenticatedSocket } from '../guards/ws-admin-auth.guard';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract restaurant ID from authenticated WebSocket client
|
* Extract restaurant ID from authenticated WebSocket client
|
||||||
|
|||||||
@@ -38,4 +38,3 @@ export class CreateNotificationDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
data?: Record<string, any>;
|
data?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,10 @@ export class PushNotificationService {
|
|||||||
|
|
||||||
async sendToToken(payload: PushNotificationPayload): Promise<PushNotificationResponse> {
|
async sendToToken(payload: PushNotificationPayload): Promise<PushNotificationResponse> {
|
||||||
if (!this.firebaseApp || !payload.token) {
|
if (!this.firebaseApp || !payload.token) {
|
||||||
throw new HttpException('Push notification service not configured or token missing', HttpStatus.SERVICE_UNAVAILABLE);
|
throw new HttpException(
|
||||||
|
'Push notification service not configured or token missing',
|
||||||
|
HttpStatus.SERVICE_UNAVAILABLE,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -115,9 +118,7 @@ export class PushNotificationService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const response = await admin.messaging().sendEachForMulticast(message);
|
const response = await admin.messaging().sendEachForMulticast(message);
|
||||||
this.logger.log(
|
this.logger.log(`Push notifications sent: ${response.successCount} successful, ${response.failureCount} failed`);
|
||||||
`Push notifications sent: ${response.successCount} successful, ${response.failureCount} failed`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: response.successCount > 0,
|
success: response.successCount > 0,
|
||||||
@@ -145,4 +146,3 @@ export class PushNotificationService {
|
|||||||
return this.firebaseApp !== null;
|
return this.firebaseApp !== null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,14 @@ export class OrdersController {
|
|||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Post('public/checkout')
|
@Post('public/checkout')
|
||||||
@ApiOperation({ summary: 'Checkout : create order and payment record' })
|
@ApiOperation({ summary: 'Checkout : create order and payment record' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
checkout(@UserId() userId: string, @RestId() restaurantId: string) {
|
checkout(@UserId() userId: string, @RestId() restaurantId: string) {
|
||||||
return this.ordersService.checkout(userId, restaurantId);
|
return this.ordersService.checkout(userId, restaurantId);
|
||||||
}
|
}
|
||||||
@@ -25,7 +32,14 @@ export class OrdersController {
|
|||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Get('public/orders')
|
@Get('public/orders')
|
||||||
@ApiOperation({ summary: 'Get all orders with pagination and filters' })
|
@ApiOperation({ summary: 'Get all orders with pagination and filters' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
findAll(@RestId() restId: string, @Query() dto: FindOrdersDto, @UserId() userId: string) {
|
findAll(@RestId() restId: string, @Query() dto: FindOrdersDto, @UserId() userId: string) {
|
||||||
return this.ordersService.findAllForUser(restId, dto, userId);
|
return this.ordersService.findAllForUser(restId, dto, userId);
|
||||||
}
|
}
|
||||||
@@ -33,7 +47,14 @@ export class OrdersController {
|
|||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Get an order By id for User' })
|
@ApiOperation({ summary: 'Get an order By id for User' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiParam({ name: 'orderId', description: 'Order ID' })
|
@ApiParam({ name: 'orderId', description: 'Order ID' })
|
||||||
@Get('public/orders/:orderId')
|
@Get('public/orders/:orderId')
|
||||||
findOne(@Param('orderId') orderId: string, @RestId() restId: string) {
|
findOne(@Param('orderId') orderId: string, @RestId() restId: string) {
|
||||||
@@ -44,7 +65,14 @@ export class OrdersController {
|
|||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Patch('public/orders/:id/cancel')
|
@Patch('public/orders/:id/cancel')
|
||||||
@ApiOperation({ summary: 'Cancel an order By User' })
|
@ApiOperation({ summary: 'Cancel an order By User' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiParam({ name: 'id', description: 'Order ID' })
|
@ApiParam({ name: 'id', description: 'Order ID' })
|
||||||
cancelOrder(@Param('id') id: string, @RestId() restId: string) {
|
cancelOrder(@Param('id') id: string, @RestId() restId: string) {
|
||||||
return this.ordersService.cancelOrderAsUser(id, restId);
|
return this.ordersService.cancelOrderAsUser(id, restId);
|
||||||
|
|||||||
@@ -124,4 +124,3 @@ export class FindOrdersDto {
|
|||||||
@IsIn(sortOrderOptions)
|
@IsIn(sortOrderOptions)
|
||||||
order?: SortOrder = 'desc';
|
order?: SortOrder = 'desc';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,14 @@ export class PagerController {
|
|||||||
@UseGuards(OptionalAuthGuard)
|
@UseGuards(OptionalAuthGuard)
|
||||||
@Get('public/pager')
|
@Get('public/pager')
|
||||||
@ApiOperation({ summary: 'Get all pager requests for the current session' })
|
@ApiOperation({ summary: 'Get all pager requests for the current session' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiHeader({ name: 'Authorization', required: false, description: 'Bearer token (optional authentication)' })
|
@ApiHeader({ name: 'Authorization', required: false, description: 'Bearer token (optional authentication)' })
|
||||||
async findAll(@Req() request: FastifyRequest) {
|
async findAll(@Req() request: FastifyRequest) {
|
||||||
const cookieId = request.cookies['pagerId'] || undefined;
|
const cookieId = request.cookies['pagerId'] || undefined;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
import type { ExecutionContext } from '@nestjs/common';
|
||||||
import { AuthenticatedSocket } from '../guards/ws-admin-auth.guard';
|
import { createParamDecorator } from '@nestjs/common';
|
||||||
|
import type { AuthenticatedSocket } from '../guards/ws-admin-auth.guard';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract admin ID from authenticated WebSocket client
|
* Extract admin ID from authenticated WebSocket client
|
||||||
@@ -23,4 +24,3 @@ export const WsAdminId = createParamDecorator((data: unknown, ctx: ExecutionCont
|
|||||||
|
|
||||||
return adminId;
|
return adminId;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
import type { ExecutionContext } from '@nestjs/common';
|
||||||
import { AuthenticatedSocket } from '../guards/ws-admin-auth.guard';
|
import { createParamDecorator } from '@nestjs/common';
|
||||||
|
import type { AuthenticatedSocket } from '../guards/ws-admin-auth.guard';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract restaurant ID from authenticated WebSocket client
|
* Extract restaurant ID from authenticated WebSocket client
|
||||||
@@ -23,4 +24,3 @@ export const WsRestId = createParamDecorator((data: unknown, ctx: ExecutionConte
|
|||||||
|
|
||||||
return restId;
|
return restId;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export class PagerService {
|
|||||||
|
|
||||||
// Populate relations before emitting
|
// Populate relations before emitting
|
||||||
await this.em.populate(pager, ['restaurant']);
|
await this.em.populate(pager, ['restaurant']);
|
||||||
// Emit socket event for new pager
|
// Emit socket event for new pager
|
||||||
this.eventEmitter.emit(PagerCreatedEvent.name, new PagerCreatedEvent(pager.restaurant.id));
|
this.eventEmitter.emit(PagerCreatedEvent.name, new PagerCreatedEvent(pager.restaurant.id));
|
||||||
|
|
||||||
return pager;
|
return pager;
|
||||||
|
|||||||
@@ -29,7 +29,14 @@ export class PaymentsController {
|
|||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Get('public/payments/methods/restaurant')
|
@Get('public/payments/methods/restaurant')
|
||||||
@ApiOperation({ summary: 'Get the restaurant payment methods' })
|
@ApiOperation({ summary: 'Get the restaurant payment methods' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiNotFoundResponse({ description: 'Restaurant payment methods not found' })
|
@ApiNotFoundResponse({ description: 'Restaurant payment methods not found' })
|
||||||
getTheRestaurantPaymentMethods(@RestId() restId: string) {
|
getTheRestaurantPaymentMethods(@RestId() restId: string) {
|
||||||
return this.paymentMethodService.findByRestaurant(restId);
|
return this.paymentMethodService.findByRestaurant(restId);
|
||||||
@@ -39,7 +46,14 @@ export class PaymentsController {
|
|||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Get('public/payments')
|
@Get('public/payments')
|
||||||
@ApiOperation({ summary: 'Get all the restaurant payments for user' })
|
@ApiOperation({ summary: 'Get all the restaurant payments for user' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
findAllByRestaurant(@UserId() userId: string, @RestId() restId: string) {
|
findAllByRestaurant(@UserId() userId: string, @RestId() restId: string) {
|
||||||
return this.paymentsService.findAllByRestaurantId(restId, userId);
|
return this.paymentsService.findAllByRestaurantId(restId, userId);
|
||||||
}
|
}
|
||||||
@@ -127,7 +141,14 @@ export class PaymentsController {
|
|||||||
|
|
||||||
@Post('public/payments/verify')
|
@Post('public/payments/verify')
|
||||||
@ApiOperation({ summary: 'Verify a payment' })
|
@ApiOperation({ summary: 'Verify a payment' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiBody({ type: VerifyPaymentDto })
|
@ApiBody({ type: VerifyPaymentDto })
|
||||||
@ApiNotFoundResponse({ description: 'Payment not found' })
|
@ApiNotFoundResponse({ description: 'Payment not found' })
|
||||||
verifyPayment(@Body() verifyPaymentDto: VerifyPaymentDto) {
|
verifyPayment(@Body() verifyPaymentDto: VerifyPaymentDto) {
|
||||||
|
|||||||
@@ -2,4 +2,3 @@ import { PartialType } from '@nestjs/mapped-types';
|
|||||||
import { CreatePaymentMethodDto } from './create-payment-method.dto';
|
import { CreatePaymentMethodDto } from './create-payment-method.dto';
|
||||||
|
|
||||||
export class UpdatePaymentMethodDto extends PartialType(CreatePaymentMethodDto) {}
|
export class UpdatePaymentMethodDto extends PartialType(CreatePaymentMethodDto) {}
|
||||||
|
|
||||||
|
|||||||
@@ -42,10 +42,7 @@ export class PaymentMethodService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async findByRestaurant(restaurantId: string): Promise<PaymentMethod[]> {
|
async findByRestaurant(restaurantId: string): Promise<PaymentMethod[]> {
|
||||||
return this.paymentMethodRepository.find(
|
return this.paymentMethodRepository.find({ restaurant: { id: restaurantId } }, { populate: ['restaurant'] });
|
||||||
{ restaurant: { id: restaurantId } },
|
|
||||||
{ populate: ['restaurant'] },
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, updatePaymentMethodDto: UpdatePaymentMethodDto): Promise<PaymentMethod> {
|
async update(id: string, updatePaymentMethodDto: UpdatePaymentMethodDto): Promise<PaymentMethod> {
|
||||||
|
|||||||
@@ -21,7 +21,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({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
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) {
|
||||||
|
|||||||
@@ -26,7 +26,14 @@ export class ScheduleController {
|
|||||||
|
|
||||||
@Get('public/schedules/restaurant/:slug')
|
@Get('public/schedules/restaurant/:slug')
|
||||||
@ApiOperation({ summary: 'Get schedule of a restaurant by slug' })
|
@ApiOperation({ summary: 'Get schedule of a restaurant by slug' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiParam({ name: 'slug', description: 'Restaurant Slug' })
|
@ApiParam({ name: 'slug', description: 'Restaurant Slug' })
|
||||||
@ApiOkResponse({ description: 'Today schedule of the restaurant', type: Schedule })
|
@ApiOkResponse({ description: 'Today schedule of the restaurant', type: Schedule })
|
||||||
async getTodaySchedules(@Param('slug') slug: string): Promise<Schedule[]> {
|
async getTodaySchedules(@Param('slug') slug: string): Promise<Schedule[]> {
|
||||||
|
|||||||
@@ -29,5 +29,4 @@ export class CreateScheduleDto {
|
|||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
@Type(() => Boolean)
|
@Type(() => Boolean)
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,18 @@ export class RestaurantSpecificationDto {
|
|||||||
longitude?: number;
|
longitude?: number;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
example: { type: 'Polygon', coordinates: [[[51.389, 35.6892], [51.390, 35.6892], [51.390, 35.6902], [51.389, 35.6902], [51.389, 35.6892]]] },
|
example: {
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [
|
||||||
|
[
|
||||||
|
[51.389, 35.6892],
|
||||||
|
[51.39, 35.6892],
|
||||||
|
[51.39, 35.6902],
|
||||||
|
[51.389, 35.6902],
|
||||||
|
[51.389, 35.6892],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
description: 'Service area as GeoJSON Polygon',
|
description: 'Service area as GeoJSON Polygon',
|
||||||
})
|
})
|
||||||
serviceArea?: {
|
serviceArea?: {
|
||||||
|
|||||||
@@ -30,7 +30,14 @@ export class ReviewController {
|
|||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Post('public/reviews')
|
@Post('public/reviews')
|
||||||
@ApiOperation({ summary: 'Create a new review and rating' })
|
@ApiOperation({ summary: 'Create a new review and rating' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiCreatedResponse({ description: 'The review has been successfully created.' })
|
@ApiCreatedResponse({ description: 'The review has been successfully created.' })
|
||||||
@ApiBody({ type: CreateReviewDto })
|
@ApiBody({ type: CreateReviewDto })
|
||||||
create(@Body() createReviewDto: CreateReviewDto, @UserId() userId: string) {
|
create(@Body() createReviewDto: CreateReviewDto, @UserId() userId: string) {
|
||||||
@@ -39,7 +46,14 @@ export class ReviewController {
|
|||||||
|
|
||||||
@Get('public/reviews/:foodId')
|
@Get('public/reviews/:foodId')
|
||||||
@ApiOperation({ summary: 'Get all reviews of food by id(public - only approved)' })
|
@ApiOperation({ summary: 'Get all reviews of food by id(public - only approved)' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiOkResponse({ description: 'List of approved reviews' })
|
@ApiOkResponse({ description: 'List of approved reviews' })
|
||||||
@ApiParam({ name: 'foodId', required: true, type: String })
|
@ApiParam({ name: 'foodId', required: true, type: String })
|
||||||
@ApiQuery({ name: 'page', required: false, type: Number })
|
@ApiQuery({ name: 'page', required: false, type: Number })
|
||||||
@@ -53,7 +67,14 @@ export class ReviewController {
|
|||||||
|
|
||||||
@Get('public/reviews/restuarant/:restuarantSlug')
|
@Get('public/reviews/restuarant/:restuarantSlug')
|
||||||
@ApiOperation({ summary: 'Get all reviews of restuarant by slug(public - only approved)' })
|
@ApiOperation({ summary: 'Get all reviews of restuarant by slug(public - only approved)' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiOkResponse({ description: 'List of approved reviews' })
|
@ApiOkResponse({ description: 'List of approved reviews' })
|
||||||
@ApiParam({ name: 'restuarantSlug', required: true, type: String })
|
@ApiParam({ name: 'restuarantSlug', required: true, type: String })
|
||||||
@ApiQuery({ name: 'page', required: false, type: Number })
|
@ApiQuery({ name: 'page', required: false, type: Number })
|
||||||
@@ -78,7 +99,14 @@ export class ReviewController {
|
|||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Patch('public/reviews/:reviewId')
|
@Patch('public/reviews/:reviewId')
|
||||||
@ApiOperation({ summary: 'Update a review (own reviews only)' })
|
@ApiOperation({ summary: 'Update a review (own reviews only)' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiParam({ name: 'reviewId', required: true })
|
@ApiParam({ name: 'reviewId', required: true })
|
||||||
@ApiBody({ type: UpdateReviewDto })
|
@ApiBody({ type: UpdateReviewDto })
|
||||||
@ApiOkResponse({ description: 'The updated review' })
|
@ApiOkResponse({ description: 'The updated review' })
|
||||||
@@ -90,7 +118,14 @@ export class ReviewController {
|
|||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@Delete('public/reviews/:id')
|
@Delete('public/reviews/:id')
|
||||||
@ApiOperation({ summary: 'Delete a review (own reviews only)' })
|
@ApiOperation({ summary: 'Delete a review (own reviews only)' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiParam({ name: 'id', required: true })
|
@ApiParam({ name: 'id', required: true })
|
||||||
remove(@Param('id') id: string, @UserId() userId: string) {
|
remove(@Param('id') id: string, @UserId() userId: string) {
|
||||||
return this.reviewService.remove(id, userId, false);
|
return this.reviewService.remove(id, userId, false);
|
||||||
|
|||||||
@@ -11,4 +11,3 @@ export class ChangeStatusDto {
|
|||||||
})
|
})
|
||||||
status: ReviewStatus;
|
status: ReviewStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export class UpdateReviewDto {
|
|||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description: 'Positive points about the food',
|
description: 'Positive points about the food',
|
||||||
enum: PositivePoint,
|
enum: PositivePoint,
|
||||||
isArray: true
|
isArray: true,
|
||||||
})
|
})
|
||||||
positivePoints?: PositivePoint[];
|
positivePoints?: PositivePoint[];
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ export class UpdateReviewDto {
|
|||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description: 'Negative points about the food',
|
description: 'Negative points about the food',
|
||||||
enum: NegativePoint,
|
enum: NegativePoint,
|
||||||
isArray: true
|
isArray: true,
|
||||||
})
|
})
|
||||||
negativePoints?: NegativePoint[];
|
negativePoints?: NegativePoint[];
|
||||||
|
|
||||||
@@ -43,4 +43,3 @@ export class UpdateReviewDto {
|
|||||||
@ApiPropertyOptional({ description: 'Review status (admin only)', enum: ReviewStatus })
|
@ApiPropertyOptional({ description: 'Review status (admin only)', enum: ReviewStatus })
|
||||||
status?: ReviewStatus;
|
status?: ReviewStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,4 +3,3 @@ export enum ReviewStatus {
|
|||||||
REJECTED = 'rejected',
|
REJECTED = 'rejected',
|
||||||
APPROVED = 'approved',
|
APPROVED = 'approved',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,12 +69,9 @@ export class FoodRatingCronService {
|
|||||||
// Flush all updates at once for better performance
|
// Flush all updates at once for better performance
|
||||||
await this.em.flush();
|
await this.em.flush();
|
||||||
|
|
||||||
this.logger.log(
|
this.logger.log(`Food rating calculation completed. Updated: ${updatedCount}, Errors: ${errorCount}`);
|
||||||
`Food rating calculation completed. Updated: ${updatedCount}, Errors: ${errorCount}`,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Error in food rating cron job: ${error.message}`, error.stack);
|
this.logger.error(`Error in food rating cron job: ${error.message}`, error.stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export class ReviewRepository extends EntityRepository<Review> {
|
|||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
|
orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
|
||||||
populate: ['food','user'],
|
populate: ['food', 'user'],
|
||||||
});
|
});
|
||||||
|
|
||||||
const totalPages = Math.ceil(total / limit);
|
const totalPages = Math.ceil(total / limit);
|
||||||
|
|||||||
@@ -11,16 +11,9 @@ import { AuthModule } from '../auth/auth.module';
|
|||||||
import { JwtModule } from '@nestjs/jwt';
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [MikroOrmModule.forFeature([Review]), FoodModule, UserModule, AuthModule, JwtModule],
|
||||||
MikroOrmModule.forFeature([Review]),
|
|
||||||
FoodModule,
|
|
||||||
UserModule,
|
|
||||||
AuthModule,
|
|
||||||
JwtModule,
|
|
||||||
],
|
|
||||||
controllers: [ReviewController],
|
controllers: [ReviewController],
|
||||||
providers: [ReviewService, ReviewRepository, FoodRatingCronService],
|
providers: [ReviewService, ReviewRepository, FoodRatingCronService],
|
||||||
exports: [ReviewRepository],
|
exports: [ReviewRepository],
|
||||||
})
|
})
|
||||||
export class ReviewModule {}
|
export class ReviewModule {}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ export class CreateRoleDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
name!: string;
|
name!: string;
|
||||||
|
|
||||||
|
|
||||||
@ApiProperty({ description: 'List of permission IDs', isArray: true, required: false })
|
@ApiProperty({ description: 'List of permission IDs', isArray: true, required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsArray()
|
@IsArray()
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export class Permission extends BaseEntity {
|
|||||||
|
|
||||||
@Property()
|
@Property()
|
||||||
title!: string;
|
title!: string;
|
||||||
// اضافه شد
|
// اضافه شد
|
||||||
@Property({ default: 'general' })
|
@Property({ default: 'general' })
|
||||||
group!: string;
|
group!: string;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { File } from "@nest-lab/fastify-multer";
|
import type { File } from '@nest-lab/fastify-multer';
|
||||||
|
|
||||||
export interface FileUploadResult {
|
export interface FileUploadResult {
|
||||||
file: File;
|
file: File;
|
||||||
@@ -19,7 +19,7 @@ export interface FileMetadata {
|
|||||||
language?: string;
|
language?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FileType = "video" | "audio" | "image" | "document" | "text" | "unknown";
|
export type FileType = 'video' | 'audio' | 'image' | 'document' | 'text' | 'unknown';
|
||||||
|
|
||||||
export interface FileValidationOptions {
|
export interface FileValidationOptions {
|
||||||
maxFileSize: number;
|
maxFileSize: number;
|
||||||
@@ -32,7 +32,7 @@ export interface FileUploadOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// File processing status
|
// File processing status
|
||||||
export type FileProcessingStatus = "pending" | "processing" | "completed" | "failed";
|
export type FileProcessingStatus = 'pending' | 'processing' | 'completed' | 'failed';
|
||||||
|
|
||||||
export interface FileProcessingResult {
|
export interface FileProcessingResult {
|
||||||
status: FileProcessingStatus;
|
status: FileProcessingStatus;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export interface S3UploadResult {
|
|||||||
export interface S3UploadOptions {
|
export interface S3UploadOptions {
|
||||||
contentType: string;
|
contentType: string;
|
||||||
metadata?: Record<string, string>;
|
metadata?: Record<string, string>;
|
||||||
acl?: "private" | "public-read" | "public-read-write";
|
acl?: 'private' | 'public-read' | 'public-read-write';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface S3DownloadOptions {
|
export interface S3DownloadOptions {
|
||||||
|
|||||||
@@ -19,7 +19,14 @@ export class UsersController {
|
|||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Get the current authenticated user profile' })
|
@ApiOperation({ summary: 'Get the current authenticated user profile' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@Get('public/user/me')
|
@Get('public/user/me')
|
||||||
async getUser(@UserId() userId: string) {
|
async getUser(@UserId() userId: string) {
|
||||||
const user = await this.userService.findById(userId);
|
const user = await this.userService.findById(userId);
|
||||||
@@ -28,7 +35,14 @@ export class UsersController {
|
|||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Update the authenticated user profile' })
|
@ApiOperation({ summary: 'Update the authenticated user profile' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiBody({ type: UpdateUserDto })
|
@ApiBody({ type: UpdateUserDto })
|
||||||
@Patch('public/user/update')
|
@Patch('public/user/update')
|
||||||
async updateUser(
|
async updateUser(
|
||||||
@@ -42,7 +56,14 @@ export class UsersController {
|
|||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Get all addresses for the authenticated user' })
|
@ApiOperation({ summary: 'Get all addresses for the authenticated user' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiOkResponse({ description: 'List of user addresses' })
|
@ApiOkResponse({ description: 'List of user addresses' })
|
||||||
@Get('public/user/addresses')
|
@Get('public/user/addresses')
|
||||||
async getUserAddresses(@UserId() userId: string) {
|
async getUserAddresses(@UserId() userId: string) {
|
||||||
@@ -53,7 +74,14 @@ export class UsersController {
|
|||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Create a new address for the authenticated user' })
|
@ApiOperation({ summary: 'Create a new address for the authenticated user' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiBody({ type: CreateUserAddressDto })
|
@ApiBody({ type: CreateUserAddressDto })
|
||||||
@ApiOkResponse({ description: 'Created user address' })
|
@ApiOkResponse({ description: 'Created user address' })
|
||||||
@Post('public/user/addresses')
|
@Post('public/user/addresses')
|
||||||
@@ -68,7 +96,14 @@ export class UsersController {
|
|||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Get a single address by ID for the authenticated user' })
|
@ApiOperation({ summary: 'Get a single address by ID for the authenticated user' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiOkResponse({ description: 'User address details' })
|
@ApiOkResponse({ description: 'User address details' })
|
||||||
@Get('public/user/addresses/:addressId')
|
@Get('public/user/addresses/:addressId')
|
||||||
async getUserAddress(@UserId() userId: string, @Param('addressId') addressId: string) {
|
async getUserAddress(@UserId() userId: string, @Param('addressId') addressId: string) {
|
||||||
@@ -79,7 +114,14 @@ export class UsersController {
|
|||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Update an address for the authenticated user' })
|
@ApiOperation({ summary: 'Update an address for the authenticated user' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiBody({ type: UpdateUserAddressDto })
|
@ApiBody({ type: UpdateUserAddressDto })
|
||||||
@ApiOkResponse({ description: 'Updated user address' })
|
@ApiOkResponse({ description: 'Updated user address' })
|
||||||
@Patch('public/user/addresses/:addressId')
|
@Patch('public/user/addresses/:addressId')
|
||||||
@@ -95,7 +137,14 @@ export class UsersController {
|
|||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Delete an address for the authenticated user' })
|
@ApiOperation({ summary: 'Delete an address for the authenticated user' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiOkResponse({ description: 'Address deleted successfully' })
|
@ApiOkResponse({ description: 'Address deleted successfully' })
|
||||||
@Delete('public/user/addresses/:addressId')
|
@Delete('public/user/addresses/:addressId')
|
||||||
async deleteUserAddress(@UserId() userId: string, @Param('addressId') addressId: string) {
|
async deleteUserAddress(@UserId() userId: string, @Param('addressId') addressId: string) {
|
||||||
@@ -106,7 +155,14 @@ export class UsersController {
|
|||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Set an address as default for the authenticated user' })
|
@ApiOperation({ summary: 'Set an address as default for the authenticated user' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiOkResponse({ description: 'Address set as default' })
|
@ApiOkResponse({ description: 'Address set as default' })
|
||||||
@Patch('public/user/addresses/:addressId/default')
|
@Patch('public/user/addresses/:addressId/default')
|
||||||
async setDefaultAddress(@UserId() userId: string, @Param('addressId') addressId: string) {
|
async setDefaultAddress(@UserId() userId: string, @Param('addressId') addressId: string) {
|
||||||
@@ -130,7 +186,14 @@ export class UsersController {
|
|||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Convert user score (points) to wallet balance' })
|
@ApiOperation({ summary: 'Convert user score (points) to wallet balance' })
|
||||||
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
|
@ApiHeader({
|
||||||
|
name: 'X-Slug',
|
||||||
|
required: true,
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'zhivan',
|
||||||
|
},
|
||||||
|
})
|
||||||
@ApiBody({ type: ConvertScoreToWalletDto })
|
@ApiBody({ type: ConvertScoreToWalletDto })
|
||||||
@Post('public/user/convert-score-to-wallet')
|
@Post('public/user/convert-score-to-wallet')
|
||||||
async convertScoreToWallet(@UserId() userId: string, @Body() dto: ConvertScoreToWalletDto, @RestId() restId: string) {
|
async convertScoreToWallet(@UserId() userId: string, @Body() dto: ConvertScoreToWalletDto, @RestId() restId: string) {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export class CreateUserAddressDto {
|
|||||||
@Max(90)
|
@Max(90)
|
||||||
latitude!: number;
|
latitude!: number;
|
||||||
|
|
||||||
@ApiProperty({ description: 'Longitude coordinate', example: 51.3890, minimum: -180, maximum: 180 })
|
@ApiProperty({ description: 'Longitude coordinate', example: 51.389, minimum: -180, maximum: 180 })
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@Min(-180)
|
@Min(-180)
|
||||||
@Max(180)
|
@Max(180)
|
||||||
@@ -49,4 +49,3 @@ export class CreateUserAddressDto {
|
|||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
isDefault?: boolean;
|
isDefault?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export class UpdateUserAddressDto {
|
|||||||
@Max(90)
|
@Max(90)
|
||||||
latitude?: number;
|
latitude?: number;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'Longitude coordinate', example: 51.3890, minimum: -180, maximum: 180 })
|
@ApiPropertyOptional({ description: 'Longitude coordinate', example: 51.389, minimum: -180, maximum: 180 })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@Min(-180)
|
@Min(-180)
|
||||||
@@ -54,4 +54,3 @@ export class UpdateUserAddressDto {
|
|||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
isDefault?: boolean;
|
isDefault?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,9 +43,6 @@ export class CacheService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// // src/modules/cache/cache.service.ts
|
// // src/modules/cache/cache.service.ts
|
||||||
// import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager';
|
// import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager';
|
||||||
// import { Inject, Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
// import { Inject, Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
||||||
|
|||||||
@@ -28,4 +28,3 @@ export function normalizePhone(phone: string): string {
|
|||||||
|
|
||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { Admin } from '../modules/admin/entities/admin.entity';
|
import { Admin } from '../modules/admin/entities/admin.entity';
|
||||||
import { AdminRole } from '../modules/admin/entities/adminRole.entity';
|
import { AdminRole } from '../modules/admin/entities/adminRole.entity';
|
||||||
import { Role } from '../modules/roles/entities/role.entity';
|
import type { Role } from '../modules/roles/entities/role.entity';
|
||||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||||
import { adminsData } from './data/admins.data';
|
import { adminsData } from './data/admins.data';
|
||||||
import { normalizePhone } from '../modules/utils/phone.util';
|
import { normalizePhone } from '../modules/utils/phone.util';
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { Category } from '../modules/foods/entities/category.entity';
|
import { Category } from '../modules/foods/entities/category.entity';
|
||||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||||
import { categoriesData } from './data/categories.data';
|
import { categoriesData } from './data/categories.data';
|
||||||
|
|
||||||
export class CategoriesSeeder {
|
export class CategoriesSeeder {
|
||||||
async run(
|
async run(em: EntityManager, restaurantsMap: Map<string, Restaurant>): Promise<Map<string, Category>> {
|
||||||
em: EntityManager,
|
|
||||||
restaurantsMap: Map<string, Restaurant>,
|
|
||||||
): Promise<Map<string, Category>> {
|
|
||||||
const categoriesMap = new Map<string, Category>();
|
const categoriesMap = new Map<string, Category>();
|
||||||
|
|
||||||
for (const catData of categoriesData) {
|
for (const catData of categoriesData) {
|
||||||
@@ -36,4 +33,3 @@ export class CategoriesSeeder {
|
|||||||
return categoriesMap;
|
return categoriesMap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { Coupon } from '../modules/coupons/entities/coupon.entity';
|
import { Coupon } from '../modules/coupons/entities/coupon.entity';
|
||||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||||
import { couponsData } from './data/coupons.data';
|
import { couponsData } from './data/coupons.data';
|
||||||
|
|
||||||
export class CouponsSeeder {
|
export class CouponsSeeder {
|
||||||
@@ -40,4 +40,3 @@ export class CouponsSeeder {
|
|||||||
await em.flush();
|
await em.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,4 +29,3 @@ export const adminsData: AdminData[] = [
|
|||||||
restaurantSlug: null,
|
restaurantSlug: null,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -13,4 +13,3 @@ export const categoriesData: CategoryData[] = [
|
|||||||
{ title: 'دسر', restaurantSlug: 'boote' },
|
{ title: 'دسر', restaurantSlug: 'boote' },
|
||||||
{ title: 'نوشیدنی', restaurantSlug: 'boote' },
|
{ title: 'نوشیدنی', restaurantSlug: 'boote' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -92,4 +92,3 @@ export const couponsData: CouponData[] = [
|
|||||||
isActive: true,
|
isActive: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -48,4 +48,3 @@ export const deliveryMethodsData: DeliveryMethodData[] = [
|
|||||||
order: 4,
|
order: 4,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -13,4 +13,3 @@ export const getPermissionsData = () => {
|
|||||||
group: specialPermissions.includes(name) ? 'special' : 'general',
|
group: specialPermissions.includes(name) ? 'special' : 'general',
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,7 @@ export const rolesData: RoleConfig[] = [
|
|||||||
name: 'accountant',
|
name: 'accountant',
|
||||||
isSystem: false,
|
isSystem: false,
|
||||||
permissionFilter: (name: Permission) =>
|
permissionFilter: (name: Permission) =>
|
||||||
name === Permission.VIEW_REPORTS ||
|
name === Permission.VIEW_REPORTS || name === Permission.MANAGE_FINANCES || name === Permission.MANAGE_ORDERS,
|
||||||
name === Permission.MANAGE_FINANCES ||
|
|
||||||
name === Permission.MANAGE_ORDERS,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'superAdmin',
|
name: 'superAdmin',
|
||||||
@@ -29,4 +27,3 @@ export const rolesData: RoleConfig[] = [
|
|||||||
name === Permission.UPDATE_RESTAURANT,
|
name === Permission.UPDATE_RESTAURANT,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,3 @@ export const timeSlots: TimeSlot[] = [
|
|||||||
{ openTime: '12:00', closeTime: '15:00' }, // Lunch
|
{ openTime: '12:00', closeTime: '15:00' }, // Lunch
|
||||||
{ openTime: '18:00', closeTime: '23:00' }, // Dinner
|
{ openTime: '18:00', closeTime: '23:00' }, // Dinner
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export const userAddressesData: UserAddressData[] = [
|
|||||||
province: 'تهران',
|
province: 'تهران',
|
||||||
postalCode: '1234567890',
|
postalCode: '1234567890',
|
||||||
latitude: 35.6892,
|
latitude: 35.6892,
|
||||||
longitude: 51.3890,
|
longitude: 51.389,
|
||||||
phone: '09362532122',
|
phone: '09362532122',
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
},
|
},
|
||||||
@@ -31,8 +31,8 @@ export const userAddressesData: UserAddressData[] = [
|
|||||||
city: 'تهران',
|
city: 'تهران',
|
||||||
province: 'تهران',
|
province: 'تهران',
|
||||||
postalCode: '1234567891',
|
postalCode: '1234567891',
|
||||||
latitude: 35.6900,
|
latitude: 35.69,
|
||||||
longitude: 51.3900,
|
longitude: 51.39,
|
||||||
phone: '09185290775',
|
phone: '09185290775',
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
},
|
},
|
||||||
@@ -43,8 +43,8 @@ export const userAddressesData: UserAddressData[] = [
|
|||||||
city: 'تهران',
|
city: 'تهران',
|
||||||
province: 'تهران',
|
province: 'تهران',
|
||||||
postalCode: '1234567892',
|
postalCode: '1234567892',
|
||||||
latitude: 35.6910,
|
latitude: 35.691,
|
||||||
longitude: 51.3910,
|
longitude: 51.391,
|
||||||
phone: '09129283395',
|
phone: '09129283395',
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
},
|
},
|
||||||
@@ -55,10 +55,9 @@ export const userAddressesData: UserAddressData[] = [
|
|||||||
city: 'تهران',
|
city: 'تهران',
|
||||||
province: 'تهران',
|
province: 'تهران',
|
||||||
postalCode: '1234567893',
|
postalCode: '1234567893',
|
||||||
latitude: 35.6920,
|
latitude: 35.692,
|
||||||
longitude: 51.3920,
|
longitude: 51.392,
|
||||||
phone: '09184317567',
|
phone: '09184317567',
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { Delivery } from '../modules/delivery/entities/delivery.entity';
|
import { Delivery } from '../modules/delivery/entities/delivery.entity';
|
||||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||||
import { deliveryMethodsData } from './data/delivery-methods.data';
|
import { deliveryMethodsData } from './data/delivery-methods.data';
|
||||||
import { DeliveryFeeTypeEnum } from 'src/modules/delivery/interface/delivery';
|
import { DeliveryFeeTypeEnum } from 'src/modules/delivery/interface/delivery';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { Food } from '../modules/foods/entities/food.entity';
|
import { Food } from '../modules/foods/entities/food.entity';
|
||||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||||
import { Category } from '../modules/foods/entities/category.entity';
|
import type { Category } from '../modules/foods/entities/category.entity';
|
||||||
import { foodsData } from './data/foods.data';
|
import { foodsData } from './data/foods.data';
|
||||||
|
|
||||||
export class FoodsSeeder {
|
export class FoodsSeeder {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { PaymentMethod } from '../modules/payments/entities/payment-method.entity';
|
import { PaymentMethod } from '../modules/payments/entities/payment-method.entity';
|
||||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||||
import { paymentMethodsData } from './data/payment-methods.data';
|
import { paymentMethodsData } from './data/payment-methods.data';
|
||||||
|
|
||||||
export class PaymentMethodsSeeder {
|
export class PaymentMethodsSeeder {
|
||||||
@@ -53,4 +53,3 @@ export class PaymentMethodsSeeder {
|
|||||||
await em.flush();
|
await em.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,4 +26,3 @@ export class PermissionsSeeder {
|
|||||||
return createdPermissions;
|
return createdPermissions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,4 +19,3 @@ export class RestaurantsSeeder {
|
|||||||
return restaurantsMap;
|
return restaurantsMap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { Role } from '../modules/roles/entities/role.entity';
|
import { Role } from '../modules/roles/entities/role.entity';
|
||||||
import { Permission } from '../common/enums/permission.enum';
|
import type { Permission } from '../common/enums/permission.enum';
|
||||||
import { Permission as PermissionEntity } from '../modules/roles/entities/permission.entity';
|
import type { Permission as PermissionEntity } from '../modules/roles/entities/permission.entity';
|
||||||
import { rolesData } from './data/roles.data';
|
import { rolesData } from './data/roles.data';
|
||||||
|
|
||||||
export class RolesSeeder {
|
export class RolesSeeder {
|
||||||
@@ -25,9 +25,7 @@ export class RolesSeeder {
|
|||||||
generalPermissions.forEach(p => newRole.permissions.add(p));
|
generalPermissions.forEach(p => newRole.permissions.add(p));
|
||||||
} else {
|
} else {
|
||||||
// For other roles, use the permission filter
|
// For other roles, use the permission filter
|
||||||
const filteredPermissions = permissions.filter(p =>
|
const filteredPermissions = permissions.filter(p => roleConfig.permissionFilter(p.name as Permission));
|
||||||
roleConfig.permissionFilter(p.name as Permission),
|
|
||||||
);
|
|
||||||
filteredPermissions.forEach(p => newRole.permissions.add(p));
|
filteredPermissions.forEach(p => newRole.permissions.add(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,4 +39,3 @@ export class RolesSeeder {
|
|||||||
return rolesMap;
|
return rolesMap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { EntityManager } from '@mikro-orm/core';
|
import type { EntityManager } from '@mikro-orm/core';
|
||||||
import { Schedule } from '../modules/restaurants/entities/schedule.entity';
|
import { Schedule } from '../modules/restaurants/entities/schedule.entity';
|
||||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||||
import { timeSlots } from './data/schedules.data';
|
import { timeSlots } from './data/schedules.data';
|
||||||
|
|
||||||
export class SchedulesSeeder {
|
export class SchedulesSeeder {
|
||||||
@@ -36,4 +36,3 @@ export class SchedulesSeeder {
|
|||||||
await em.flush();
|
await em.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import type { TestingModule } from '@nestjs/testing';
|
||||||
import { INestApplication } from '@nestjs/common';
|
import { Test } from '@nestjs/testing';
|
||||||
|
import type { INestApplication } from '@nestjs/common';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { App } from 'supertest/types';
|
import type { App } from 'supertest/types';
|
||||||
import { AppModule } from './../src/app.module';
|
import { AppModule } from './../src/app.module';
|
||||||
|
|
||||||
describe('AppController (e2e)', () => {
|
describe('AppController (e2e)', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user