notif cursor
This commit is contained in:
@@ -5,7 +5,7 @@ import { map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class ResponseInterceptor implements NestInterceptor {
|
||||
constructor() {}
|
||||
constructor() { }
|
||||
|
||||
intercept(context: ExecutionContext, next: CallHandler<Record<string, unknown>>): Observable<unknown> {
|
||||
// For REST endpoints, wrap the response
|
||||
@@ -35,11 +35,14 @@ export class ResponseInterceptor implements NestInterceptor {
|
||||
};
|
||||
}
|
||||
|
||||
if (data && data.data !== undefined) {
|
||||
if (data && 'data' in data) {
|
||||
return {
|
||||
statusCode,
|
||||
success: true,
|
||||
data: data.data,
|
||||
data: (data as any).data,
|
||||
...('nextCursor' in (data as any) && (data as any).nextCursor != null
|
||||
? { nextCursor: (data as any).nextCursor }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -16,7 +16,7 @@ export class NotificationsController {
|
||||
constructor(
|
||||
private readonly notificationService: NotificationService,
|
||||
private readonly preferenceService: NotificationPreferenceService,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@@ -24,7 +24,12 @@ export class NotificationsController {
|
||||
@ApiOperation({ summary: 'Get user restaurant notifications' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@ApiQuery({ name: 'limit', required: false, type: Number, description: 'Number of items to return (default: 50)' })
|
||||
@ApiQuery({ name: 'cursor', required: false, type: String, description: 'Cursor for pagination (ID of the last item from previous page)' })
|
||||
@ApiQuery({
|
||||
name: 'cursor',
|
||||
required: false,
|
||||
type: String,
|
||||
description: 'Cursor for pagination (ID of the last item from previous page)',
|
||||
})
|
||||
@ApiQuery({ name: 'status', required: false, enum: ['seen', 'unseen'], description: 'Filter by notification status' })
|
||||
async getUserNotifications(
|
||||
@UserId() userId: string,
|
||||
@@ -52,13 +57,29 @@ export class NotificationsController {
|
||||
return { message: 'Notification read successfully' };
|
||||
}
|
||||
|
||||
/* ***************** Admin Endpoints ***************** */
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/notifications')
|
||||
@ApiOperation({ summary: 'Get Admin restaurant notifications' })
|
||||
@ApiOperation({ summary: 'Get Admin notifications' })
|
||||
@ApiQuery({ name: 'limit', required: false, type: Number })
|
||||
async getAdminNotifications(@RestId() restaurantId: string, @Query('limit') limit?: number) {
|
||||
return await this.notificationService.findByRestaurant(restaurantId, limit ? parseInt(limit.toString(), 30) : 50);
|
||||
@ApiQuery({
|
||||
name: 'cursor',
|
||||
required: false,
|
||||
type: String,
|
||||
description: 'Cursor for pagination (ID of the last item from previous page)',
|
||||
})
|
||||
@ApiQuery({ name: 'status', required: false, enum: ['seen', 'unseen'], description: 'Filter by notification status' })
|
||||
async getAdminNotifications(
|
||||
@AdminId() adminId: string,
|
||||
@RestId() restaurantId: string,
|
||||
@Query('limit') limit?: number,
|
||||
@Query('cursor') cursor?: string,
|
||||
@Query('status') status?: 'seen' | 'unseen',
|
||||
) {
|
||||
const parsedLimit = limit ? parseInt(limit.toString(), 10) : 50;
|
||||
return await this.notificationService.findByRestaurant(restaurantId, parsedLimit, cursor, status);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@@ -71,72 +92,6 @@ export class NotificationsController {
|
||||
return { message: 'Notification read successfully' };
|
||||
}
|
||||
|
||||
// @UseGuards(AuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Get('public/notifications/:id')
|
||||
// @ApiOperation({ summary: 'Get a notification by ID' })
|
||||
// @ApiParam({ name: 'id', description: 'Notification ID' })
|
||||
// async getUserNotification(@Param('id') id: string) {
|
||||
// return this.notificationService.findOne(id);
|
||||
// }
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Post('admin/notifications/send')
|
||||
// @ApiOperation({ summary: 'Send a notification (queued)' })
|
||||
// async sendNotification(@Body() dto: SendNotificationDto) {
|
||||
// return this.notificationService.sendNotification(dto);
|
||||
// }
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Get('admin/notifications')
|
||||
// @ApiOperation({ summary: 'Get notifications for a restaurant' })
|
||||
// @ApiQuery({ name: 'limit', required: false, type: Number })
|
||||
// @ApiQuery({ name: 'notificationType', required: false, type: String })
|
||||
// async getRestaurantNotifications(
|
||||
// @RestId() restaurantId: string,
|
||||
// @Query('limit') limit?: number,
|
||||
// @Query('notificationType') notificationType?: string,
|
||||
// ) {
|
||||
// if (notificationType) {
|
||||
// return this.notificationService.findByRestaurantAndType(
|
||||
// restaurantId,
|
||||
// notificationType,
|
||||
// limit ? parseInt(limit.toString(), 10) : 50,
|
||||
// );
|
||||
// }
|
||||
// return this.notificationService.findByRestaurant(restaurantId, limit ? parseInt(limit.toString(), 10) : 50);
|
||||
// }
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Get('admin/notifications/:id')
|
||||
// @ApiOperation({ summary: 'Get a notification by ID' })
|
||||
// @ApiParam({ name: 'id', description: 'Notification ID' })
|
||||
// async getNotification(@Param('id') id: string) {
|
||||
// return this.notificationService.findOne(id);
|
||||
// }
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Post('admin/notifications/:id/retry')
|
||||
// @ApiOperation({ summary: 'Retry a failed notification' })
|
||||
// @ApiParam({ name: 'id', description: 'Notification ID' })
|
||||
// async retryNotification(@Param('id') id: string) {
|
||||
// await this.notificationService.retryFailedNotification(id);
|
||||
// return { message: 'Notification queued for retry' };
|
||||
// }
|
||||
|
||||
// Preference endpoints
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Post('admin/notification-preferences')
|
||||
// @ApiOperation({ summary: 'Create notification preference' })
|
||||
// createPreference(@RestId() restaurantId: string, @Body() dto: CreatePreferenceDto) {
|
||||
// return this.preferenceService.createOrUpdate(restaurantId, dto);
|
||||
// }
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/notification-preferences')
|
||||
@@ -145,28 +100,6 @@ export class NotificationsController {
|
||||
return this.preferenceService.findByRestaurant(restaurantId);
|
||||
}
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Get('admin/notification-preferences/:type')
|
||||
// @ApiOperation({ summary: 'Get a specific notification preference' })
|
||||
// @ApiParam({ name: 'type', description: 'Notification type' })
|
||||
// async getPreference(@RestId() restaurantId: string, @Param('type') notificationType: string) {
|
||||
// return this.preferenceService.findByRestaurantAndType(restaurantId, notificationType);
|
||||
// }
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Patch('admin/notification-preferences/:type/enabled')
|
||||
// @ApiOperation({ summary: 'Enable or disable a notification preference' })
|
||||
// @ApiParam({ name: 'type', description: 'Notification type' })
|
||||
// async updatePreferenceEnabled(
|
||||
// @RestId() restaurantId: string,
|
||||
// @Param('type') notificationType: string,
|
||||
// @Body('enabled') enabled: boolean,
|
||||
// ) {
|
||||
// return this.preferenceService.updateEnabled(restaurantId, notificationType, enabled);
|
||||
// }
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Patch('admin/notification-preferences/:id')
|
||||
@@ -179,14 +112,4 @@ export class NotificationsController {
|
||||
) {
|
||||
return this.preferenceService.updatePreference(preferenceId, restaurantId, dto);
|
||||
}
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Delete('admin/notification-preferences/:id')
|
||||
// @ApiOperation({ summary: 'Delete a notification preference' })
|
||||
// @ApiParam({ name: 'id', description: 'Notification preference ID' })
|
||||
// async deletePreference(@RestId() restaurantId: string, @Param('id') preferenceId: string) {
|
||||
// await this.preferenceService.delete(restaurantId, preferenceId);
|
||||
// return { message: 'Preference deleted successfully' };
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export class NotificationService {
|
||||
private readonly preferenceService: NotificationPreferenceService,
|
||||
private readonly queueService: NotificationQueueService,
|
||||
private readonly notificationGateway: NotificationsGateway,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
async sendNotification(params: NotifRequest): Promise<Notification[]> {
|
||||
const { recipients, message, metadata, restaurantId } = params;
|
||||
@@ -96,16 +96,42 @@ export class NotificationService {
|
||||
return notification;
|
||||
}
|
||||
|
||||
async findByRestaurant(restaurantId: string, limit = 50): Promise<Notification[]> {
|
||||
return this.em.find(
|
||||
Notification,
|
||||
{ restaurant: { id: restaurantId } },
|
||||
{
|
||||
orderBy: { createdAt: 'DESC' },
|
||||
limit,
|
||||
populate: ['user'],
|
||||
},
|
||||
);
|
||||
async findByRestaurant(
|
||||
restaurantId: string,
|
||||
limit = 50,
|
||||
cursor?: string,
|
||||
status?: 'seen' | 'unseen',
|
||||
): Promise<{ data: Notification[]; nextCursor: string | null }> {
|
||||
const where: FilterQuery<Notification> = {
|
||||
restaurant: { id: restaurantId },
|
||||
};
|
||||
|
||||
// Filter by status (seen/unseen)
|
||||
if (status === 'seen') {
|
||||
where.seenAt = { $ne: null };
|
||||
} else if (status === 'unseen') {
|
||||
where.seenAt = null;
|
||||
}
|
||||
|
||||
// Cursor-based pagination: if cursor is provided, get items with id < cursor
|
||||
if (cursor) {
|
||||
where.id = { $lt: cursor };
|
||||
}
|
||||
|
||||
const notifications = await this.em.find(Notification, where, {
|
||||
orderBy: { createdAt: 'DESC', id: 'DESC' },
|
||||
limit: limit + 1, // fetch one extra to determine next page
|
||||
populate: ['user'],
|
||||
});
|
||||
|
||||
const hasNextPage = notifications.length > limit;
|
||||
const data = hasNextPage ? notifications.slice(0, limit) : notifications;
|
||||
const nextCursor = hasNextPage && data.length > 0 ? data[data.length - 1].id : null;
|
||||
|
||||
return {
|
||||
data,
|
||||
nextCursor: nextCursor ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
async findByUserAndRestaurant(
|
||||
@@ -144,7 +170,7 @@ export class NotificationService {
|
||||
|
||||
return {
|
||||
data,
|
||||
nextCursor,
|
||||
nextCursor: nextCursor ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ export class PaymentsController {
|
||||
constructor(
|
||||
private readonly paymentsService: PaymentsService,
|
||||
private readonly paymentMethodService: PaymentMethodService,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@@ -63,40 +63,8 @@ export class PaymentsController {
|
||||
verifyPayment(@Body() verifyPaymentDto: VerifyPaymentDto) {
|
||||
return this.paymentsService.verifyPayment(verifyPaymentDto.authority, verifyPaymentDto.orderId);
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Get('admin/payments/:id')
|
||||
// @ApiOperation({ summary: 'Get a payment by ID' })
|
||||
// @ApiParam({ name: 'id', type: 'number', description: 'Payment ID' })
|
||||
// @ApiOkResponse({ description: 'Payment found' })
|
||||
// @ApiNotFoundResponse({ description: 'Payment not found' })
|
||||
// findOnePayment(@Param('id') id: string) {
|
||||
// return this.paymentsService.findById(+id);
|
||||
// }
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Patch('admin/payments/:id')
|
||||
// @ApiOperation({ summary: 'Update a payment' })
|
||||
// @ApiParam({ name: 'id', type: 'number', description: 'Payment ID' })
|
||||
// @ApiBody({ type: UpdatePaymentDto })
|
||||
// @ApiOkResponse({ description: 'Payment updated successfully' })
|
||||
// @ApiNotFoundResponse({ description: 'Payment not found' })
|
||||
// updatePayment(@Param('id') id: string, @Body() updatePaymentDto: UpdatePaymentDto) {
|
||||
// return this.paymentsService.update(+id, updatePaymentDto);
|
||||
// }
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Delete('admin/payments/:id')
|
||||
// @ApiOperation({ summary: 'Delete a payment' })
|
||||
// @ApiParam({ name: 'id', type: 'number', description: 'Payment ID' })
|
||||
// @ApiOkResponse({ description: 'Payment deleted successfully' })
|
||||
// @ApiNotFoundResponse({ description: 'Payment not found' })
|
||||
// removePayment(@Param('id') id: string) {
|
||||
// return this.paymentsService.remove(+id);
|
||||
// }
|
||||
|
||||
/** payment methods */
|
||||
/** admin routes */
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/payments/methods')
|
||||
|
||||
@@ -7,9 +7,7 @@ import { Logger } from '@nestjs/common';
|
||||
import { GatewayManager } from '../gateways/gateway.manager';
|
||||
import { UserWallet } from 'src/modules/users/entities/user-wallet.entity';
|
||||
import { OrderPaymentContext } from '../interface/payment';
|
||||
import { InventoryService } from 'src/modules/inventory/inventory.service';
|
||||
import { OrderStatus } from 'src/modules/orders/interface/order.interface';
|
||||
import { PaymentMethod } from '../entities/payment-method.entity';
|
||||
|
||||
@Injectable()
|
||||
export class PaymentsService {
|
||||
@@ -18,7 +16,6 @@ export class PaymentsService {
|
||||
constructor(
|
||||
private readonly em: EntityManager,
|
||||
private readonly gatewayManager: GatewayManager,
|
||||
private readonly inventoryService: InventoryService,
|
||||
) {}
|
||||
|
||||
async payOrder(orderId: string): Promise<{ paymentUrl: string | null }> {
|
||||
|
||||
Reference in New Issue
Block a user