payment
This commit is contained in:
@@ -5,13 +5,10 @@ import { NotificationPreferenceService } from '../services/notification-preferen
|
||||
import { AuthGuard } from '../../auth/guards/auth.guard';
|
||||
import { UserId } from '../../../common/decorators/user-id.decorator';
|
||||
import { AdminAuthGuard } from '../../auth/guards/adminAuth.guard';
|
||||
import { } from '../../../common/decorators/rest-id.decorator';
|
||||
import { UpdatePreferenceDto } from '../dto/update-preference.dto';
|
||||
import { CreatePreferenceDto } from '../dto/create-preference.dto';
|
||||
import { AdminId } from 'src/common/decorators/admin-id.decorator';
|
||||
import { API_HEADER_SLUG } from 'src/common/constants/index';
|
||||
import { NotificationMessage } from 'src/common/enums/message.enum';
|
||||
import { SuperAdminAuthGuard } from 'src/modules/auth/guards/superAdminAuth.guard';
|
||||
import { Permissions } from 'src/common/decorators/permissions.decorator';
|
||||
import { Permission } from 'src/common/enums/permission.enum';
|
||||
|
||||
@@ -23,174 +20,156 @@ export class NotificationsController {
|
||||
private readonly preferenceService: NotificationPreferenceService,
|
||||
) { }
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('public/notifications')
|
||||
@ApiOperation({ summary: 'Get user restaurant notifications' })
|
||||
// @UseGuards(AuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Get('public/notifications')
|
||||
// @ApiOperation({ summary: 'Get user restaurant notifications' })
|
||||
|
||||
@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: 'status', required: false, enum: ['seen', 'unseen'], description: 'Filter by notification status' })
|
||||
async getUserNotifications(
|
||||
@UserId() userId: string,
|
||||
,
|
||||
@Query('limit') limit?: number,
|
||||
@Query('cursor') cursor?: string,
|
||||
@Query('status') status?: 'seen' | 'unseen',
|
||||
) {
|
||||
return await this.notificationService.findByUserAndRestaurant(
|
||||
userId,
|
||||
,
|
||||
limit ? parseInt(limit.toString(), 10) : 50,
|
||||
cursor,
|
||||
status,
|
||||
);
|
||||
}
|
||||
// @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: 'status', required: false, enum: ['seen', 'unseen'], description: 'Filter by notification status' })
|
||||
// async getUserNotifications(
|
||||
// @UserId() userId: string,
|
||||
// @Query('limit') limit?: number,
|
||||
// @Query('cursor') cursor?: string,
|
||||
// @Query('status') status?: 'seen' | 'unseen',
|
||||
// ) {
|
||||
// return await this.notificationService.findByUserAndRestaurant(
|
||||
// userId,
|
||||
// limit ? parseInt(limit.toString(), 10) : 50,
|
||||
// cursor,
|
||||
// status,
|
||||
// );
|
||||
// }
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('public/notifications/unseen-count')
|
||||
@ApiOperation({ summary: 'Get unseen notifications count for user' })
|
||||
// @UseGuards(AuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Get('public/notifications/unseen-count')
|
||||
// @ApiOperation({ summary: 'Get unseen notifications count for user' })
|
||||
|
||||
async getUserUnseenCount(@UserId() userId: string,) {
|
||||
const count = await this.notificationService.countUnseenByUserAndRestaurant(userId,);
|
||||
return { count };
|
||||
}
|
||||
// async getUserUnseenCount(@UserId() userId: string,) {
|
||||
// const count = await this.notificationService.countUnseenByUserAndRestaurant(userId,);
|
||||
// return { count };
|
||||
// }
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Put('public/notifications/:id')
|
||||
@ApiOperation({ summary: 'Read a notification ' })
|
||||
@ApiParam({ name: 'id', description: 'Notification ID' })
|
||||
async readNotificationUser(, @Param('id') id: string, @UserId() userId: string) {
|
||||
await this.notificationService.readNotificationAsUser(id, userId,);
|
||||
return { message: NotificationMessage.READ_SUCCESS };
|
||||
}
|
||||
// @UseGuards(AuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Put('public/notifications/:id')
|
||||
// @ApiOperation({ summary: 'Read a notification ' })
|
||||
// @ApiParam({ name: 'id', description: 'Notification ID' })
|
||||
// async readNotificationUser(, @Param('id') id: string, @UserId() userId: string) {
|
||||
// await this.notificationService.readNotificationAsUser(id, userId,);
|
||||
// return { message: NotificationMessage.READ_SUCCESS };
|
||||
// }
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Put('public/notifications/read/all')
|
||||
@ApiOperation({ summary: 'Read all notification ' })
|
||||
async readAllNotificationUser(, @UserId() userId: string) {
|
||||
await this.notificationService.readAllNotifsAsUser(userId,);
|
||||
return { message: NotificationMessage.READ_SUCCESS };
|
||||
}
|
||||
/* ***************** Admin Endpoints ***************** */
|
||||
// @UseGuards(AuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Put('public/notifications/read/all')
|
||||
// @ApiOperation({ summary: 'Read all notification ' })
|
||||
// async readAllNotificationUser(, @UserId() userId: string) {
|
||||
// await this.notificationService.readAllNotifsAsUser(userId,);
|
||||
// return { message: NotificationMessage.READ_SUCCESS };
|
||||
// }
|
||||
// /* ***************** Admin Endpoints ***************** */
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/notifications')
|
||||
@ApiOperation({ summary: 'Get Admin notifications' })
|
||||
@ApiQuery({ name: 'limit', required: false, type: Number })
|
||||
@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,
|
||||
,
|
||||
@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(, adminId, parsedLimit, cursor, status);
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Get('admin/notifications')
|
||||
// @ApiOperation({ summary: 'Get Admin notifications' })
|
||||
// @ApiQuery({ name: 'limit', required: false, type: Number })
|
||||
// @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,
|
||||
// ,
|
||||
// @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( adminId, parsedLimit, cursor, status);
|
||||
// }
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/notifications/unseen-count')
|
||||
@ApiOperation({ summary: 'Get unseen notifications count for admin' })
|
||||
async getAdminUnseenCount(@AdminId() adminId: string,) {
|
||||
const count = await this.notificationService.countUnseenByRestaurant(adminId,);
|
||||
return { count };
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Get('admin/notifications/unseen-count')
|
||||
// @ApiOperation({ summary: 'Get unseen notifications count for admin' })
|
||||
// async getAdminUnseenCount(@AdminId() adminId: string,) {
|
||||
// const count = await this.notificationService.countUnseenByRestaurant(adminId,);
|
||||
// return { count };
|
||||
// }
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Put('admin/notifications/:id')
|
||||
@ApiOperation({ summary: 'Read a notification ' })
|
||||
@ApiParam({ name: 'id', description: 'Notification ID' })
|
||||
async readNotificationAdmin(, @AdminId() adminId: string, @Param('id') id: string) {
|
||||
await this.notificationService.readNotificationAdmin(id, adminId,);
|
||||
return { message: NotificationMessage.READ_SUCCESS };
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Put('admin/notifications/:id')
|
||||
// @ApiOperation({ summary: 'Read a notification ' })
|
||||
// @ApiParam({ name: 'id', description: 'Notification ID' })
|
||||
// async readNotificationAdmin(, @AdminId() adminId: string, @Param('id') id: string) {
|
||||
// await this.notificationService.readNotificationAdmin(id, adminId,);
|
||||
// return { message: NotificationMessage.READ_SUCCESS };
|
||||
// }
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Put('admin/notifications/read/all')
|
||||
@ApiOperation({ summary: 'Read all notification ' })
|
||||
async readAllNotificationAdmin(, @AdminId() adminId: string, @Param('id') id: string) {
|
||||
await this.notificationService.readAllNotifsAsAdmin(adminId,);
|
||||
return { message: NotificationMessage.READ_SUCCESS };
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Put('admin/notifications/read/all')
|
||||
// @ApiOperation({ summary: 'Read all notification ' })
|
||||
// async readAllNotificationAdmin(, @AdminId() adminId: string, @Param('id') id: string) {
|
||||
// await this.notificationService.readAllNotifsAsAdmin(adminId,);
|
||||
// return { message: NotificationMessage.READ_SUCCESS };
|
||||
// }
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Permissions(Permission.MANAGE_SETTINGS)
|
||||
@Get('admin/notification-preferences')
|
||||
@ApiOperation({ summary: 'Get all notification preferences for a restaurant' })
|
||||
async getPreferences() {
|
||||
return this.preferenceService.findByRestaurant();
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Permissions(Permission.MANAGE_SETTINGS)
|
||||
// @Get('admin/notification-preferences')
|
||||
// @ApiOperation({ summary: 'Get all notification preferences for a restaurant' })
|
||||
// async getPreferences() {
|
||||
// return this.preferenceService.findByRestaurant();
|
||||
// }
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Permissions(Permission.MANAGE_SETTINGS)
|
||||
@Patch('admin/notification-preferences/:id')
|
||||
@ApiOperation({ summary: 'Update notification channels' })
|
||||
@ApiParam({ name: 'id', description: 'Notification preference ID' })
|
||||
async updatePreference(
|
||||
,
|
||||
@Param('id') preferenceId: string,
|
||||
@Body() dto: UpdatePreferenceDto,
|
||||
) {
|
||||
return this.preferenceService.updatePreference(preferenceId, , dto);
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Permissions(Permission.MANAGE_SETTINGS)
|
||||
// @Patch('admin/notification-preferences/:id')
|
||||
// @ApiOperation({ summary: 'Update notification channels' })
|
||||
// @ApiParam({ name: 'id', description: 'Notification preference ID' })
|
||||
// async updatePreference(
|
||||
// ,
|
||||
// @Param('id') preferenceId: string,
|
||||
// @Body() dto: UpdatePreferenceDto,
|
||||
// ) {
|
||||
// return this.preferenceService.updatePreference(preferenceId, dto);
|
||||
// }
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Permissions(Permission.MANAGE_SETTINGS)
|
||||
@Post('admin/notification-preferences')
|
||||
@ApiOperation({ summary: 'Create notification preference' })
|
||||
async createPreference(
|
||||
,
|
||||
@Body() dto: CreatePreferenceDto,
|
||||
) {
|
||||
return this.preferenceService.create(, dto);
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Permissions(Permission.MANAGE_SETTINGS)
|
||||
// @Post('admin/notification-preferences')
|
||||
// @ApiOperation({ summary: 'Create notification preference' })
|
||||
// async createPreference(
|
||||
// ,
|
||||
// @Body() dto: CreatePreferenceDto,
|
||||
// ) {
|
||||
// return this.preferenceService.create(dto);
|
||||
// }
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/notifications/sms-usage')
|
||||
@ApiOperation({ summary: 'Get SMS usage for my restaurant' })
|
||||
async getRestaurantSmsUsage() {
|
||||
const smsCount = await this.notificationService.getSmsCountBy();
|
||||
return { smsCount };
|
||||
}
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Get('admin/notifications/sms-usage')
|
||||
// @ApiOperation({ summary: 'Get SMS usage for my restaurant' })
|
||||
// async getRestaurantSmsUsage() {
|
||||
// const smsCount = await this.notificationService.getSmsCountBy();
|
||||
// return { smsCount };
|
||||
// }
|
||||
|
||||
// super admin endpoints
|
||||
|
||||
@UseGuards(SuperAdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('super-admin/notifications/sms-count')
|
||||
@ApiOperation({ summary: 'Get SMS count for each restaurant with pagination' })
|
||||
@ApiQuery({ name: 'page', required: false, type: Number, description: 'Page number (default: 1)', minimum: 1 })
|
||||
@ApiQuery({ name: 'limit', required: false, type: Number, description: 'Items per page (default: 10)', minimum: 1 })
|
||||
async getSmsCount(
|
||||
@Query('page') page?: number,
|
||||
@Query('limit') limit?: number,
|
||||
) {
|
||||
const parsedPage = page ? parseInt(page.toString(), 10) : 1;
|
||||
const parsedLimit = limit ? parseInt(limit.toString(), 10) : 10;
|
||||
return await this.notificationService.getSmsCountByRestaurant(parsedPage, parsedLimit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import type { ExecutionContext } from '@nestjs/common';
|
||||
import { createParamDecorator } from '@nestjs/common';
|
||||
import type { AuthenticatedSocket } from '../guards/ws-admin-auth.guard';
|
||||
|
||||
/**
|
||||
* Extract restaurant ID from authenticated WebSocket client
|
||||
* Use this decorator in WebSocket handlers to get the from the authenticated admin
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* @SubscribeMessage('get:notifications')
|
||||
* handleGetNotifications(@Ws() : string) {
|
||||
* // is automatically extracted from authenticated admin
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export const Ws = createParamDecorator((data: unknown, ctx: ExecutionContext): string => {
|
||||
const client = ctx.switchToWs().getClient<AuthenticatedSocket>();
|
||||
const = client.;
|
||||
|
||||
if (!) {
|
||||
throw new Error('Restaurant ID not found. Ensure WsAdminAuthGuard is applied.');
|
||||
}
|
||||
|
||||
return;
|
||||
});
|
||||
@@ -1,14 +1,10 @@
|
||||
import { Entity, Property, ManyToOne, Unique } from '@mikro-orm/core';
|
||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
|
||||
import { NotifTitleEnum } from '../interfaces/notification.interface';
|
||||
import { NotifChannelEnum } from '../interfaces/notification.interface';
|
||||
|
||||
@Entity({ tableName: 'notification_preferences' })
|
||||
@Unique({ properties: ['restaurant', 'title'] })
|
||||
export class NotificationPreference extends BaseEntity {
|
||||
@ManyToOne(() => Restaurant)
|
||||
restaurant!: Restaurant;
|
||||
|
||||
@Property()
|
||||
title!: NotifTitleEnum;
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { Entity, Property, ManyToOne, PrimaryKey } from '@mikro-orm/core';
|
||||
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
|
||||
import { Entity, Property, PrimaryKey } from '@mikro-orm/core';
|
||||
|
||||
@Entity({ tableName: 'sms_logs' })
|
||||
export class SmsLog {
|
||||
@PrimaryKey()
|
||||
id!: number;
|
||||
|
||||
@ManyToOne(() => Restaurant)
|
||||
restaurant!: Restaurant;
|
||||
|
||||
@Property()
|
||||
phone!: string;
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import { IAdminTokenPayload } from '../../auth/interfaces/IToken-payload';
|
||||
|
||||
export interface AuthenticatedSocket extends Socket {
|
||||
adminId?: string;
|
||||
?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@@ -41,16 +40,15 @@ export class WsAdminAuthGuard implements CanActivate {
|
||||
secret,
|
||||
});
|
||||
|
||||
if (!payload.adminId || !payload.) {
|
||||
if (!payload.adminId) {
|
||||
this.logger.error('Invalid token payload structure', payload);
|
||||
throw new WsException('Invalid token payload');
|
||||
}
|
||||
|
||||
// Attach admin info to socket
|
||||
(client as AuthenticatedSocket).adminId = payload.adminId;
|
||||
(client as AuthenticatedSocket). = payload.;
|
||||
|
||||
this.logger.log(`Admin authenticated via WebSocket: ${payload.adminId}, : ${payload.}`);
|
||||
this.logger.log(`Admin authenticated via WebSocket: ${payload.adminId}`);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
|
||||
@@ -3,7 +3,6 @@ import type { NotifTitleEnum, recipientType } from './notification.interface';
|
||||
export interface SmsNotificationQueueJob {
|
||||
recipient: recipientType;
|
||||
templateId: string;
|
||||
: string;
|
||||
parameters?: Record<string, string>;
|
||||
}
|
||||
export interface PushNotificationQueueJob {
|
||||
@@ -18,7 +17,7 @@ export interface PushNotificationQueueJob {
|
||||
pushTokens?: string[]; // Multiple FCM tokens for bulk push notifications
|
||||
}
|
||||
export interface InAppNotificationQueueJob {
|
||||
recipient: { adminId: string; : string };
|
||||
recipient: { adminId: string; };
|
||||
subject: NotifTitleEnum;
|
||||
body: string;
|
||||
notificationId: string;
|
||||
|
||||
@@ -40,7 +40,6 @@ export interface NotifRequest {
|
||||
// timestamp: Date;
|
||||
// notifType: NotifTypeEnum;
|
||||
// channels: NotifChannelEnum[];
|
||||
: string;
|
||||
recipients: recipientType[];
|
||||
message: NotifRequestMessage;
|
||||
metadata: {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { SmsSentEvent } from '../events/sms.events';
|
||||
import { RestRepository } from '../../restaurants/repositories/rest.repository';
|
||||
import { EntityManager } from '@mikro-orm/postgresql';
|
||||
import { SmsLog } from '../entities/smsLogs.entity';
|
||||
|
||||
@@ -10,7 +9,6 @@ export class SmsListeners {
|
||||
private readonly logger = new Logger(SmsListeners.name);
|
||||
|
||||
constructor(
|
||||
private readonly restRepository: RestRepository,
|
||||
private readonly em: EntityManager,
|
||||
) {
|
||||
}
|
||||
@@ -19,22 +17,11 @@ export class SmsListeners {
|
||||
async handleSmsSent(event: SmsSentEvent) {
|
||||
try {
|
||||
this.logger.log(
|
||||
`SMS sent event received: phone ${event.phoneNumber} for restaurant: ${event.}`,
|
||||
`SMS sent event received: phone ${event.phoneNumber} `,
|
||||
);
|
||||
|
||||
// Get the restaurant entity
|
||||
const restaurant = await this.restRepository.findOne({ id: event. });
|
||||
|
||||
if (!restaurant) {
|
||||
this.logger.warn(
|
||||
`Restaurant not found for SMS log: ${event.}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create SMS log record
|
||||
const smsLog = this.em.create(SmsLog, {
|
||||
restaurant: restaurant,
|
||||
phone: event.phoneNumber,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
@@ -46,7 +33,7 @@ export class SmsListeners {
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to create SMS log for event: ${event.}`,
|
||||
`Failed to create SMS `,
|
||||
error instanceof Error ? error.stack : String(error),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import {
|
||||
// SubscribeMessage,
|
||||
OnGatewayConnection,
|
||||
OnGatewayDisconnect,
|
||||
// MessageBody,
|
||||
// ConnectedSocket,
|
||||
} from '@nestjs/websockets';
|
||||
import { Server, Socket } from 'socket.io';
|
||||
import { Logger, Inject, forwardRef } from '@nestjs/common';
|
||||
@@ -70,8 +68,8 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
|
||||
// }
|
||||
}
|
||||
|
||||
sendInAppNotification(repipient: { adminId: string; : string }, payload: IInAppNotificationPayload) {
|
||||
const room = this.getRoom(repipient.adminId, repipient.);
|
||||
sendInAppNotification(repipient: { adminId: string; }, payload: IInAppNotificationPayload) {
|
||||
const room = this.getRoom(repipient.adminId,);
|
||||
|
||||
this.logger.log(`Sending in app notification to admin: ${repipient.adminId}`);
|
||||
this.server.to(room).emit('notifications', payload, async () => {
|
||||
@@ -81,19 +79,19 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
|
||||
this.logger.log(`In app notification sent to admin: ${repipient.adminId}`);
|
||||
}
|
||||
|
||||
private getRoom(adminId: string, : string) {
|
||||
return `restaurant:${}-admin:${adminId}`;
|
||||
private getRoom(adminId: string,) {
|
||||
return `admin:${adminId}`;
|
||||
}
|
||||
|
||||
handleLeaveRoom(client: AuthenticatedSocket) {
|
||||
const room = this.getRoom(client.adminId!, client.!);
|
||||
const room = this.getRoom(client.adminId!,);
|
||||
void client.leave(room);
|
||||
this.logger.log(`Admin ${client.adminId} (Client ${client.id}) left room: ${room}`);
|
||||
void client.emit('left', { room, message: 'Successfully Admin left room' });
|
||||
}
|
||||
|
||||
handleJoinRoom(client: AuthenticatedSocket) {
|
||||
const room = this.getRoom(client.adminId!, client.!);
|
||||
const room = this.getRoom(client.adminId!,);
|
||||
void client.join(room);
|
||||
this.logger.log(`Admin ${client.adminId} (Client ${client.id}) joined room: ${room}`);
|
||||
void client.emit('joined', { room, message: 'Successfully Admin joined room' });
|
||||
|
||||
@@ -9,69 +9,5 @@ export class SmsLogRepository extends EntityRepository<SmsLog> {
|
||||
super(em, SmsLog);
|
||||
}
|
||||
|
||||
async getSmsCountByRestaurant(
|
||||
page: number = 1,
|
||||
limit: number = 10,
|
||||
): Promise<PaginatedResult<{ : string; restaurantName: string; smsCount: number
|
||||
}>> {
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// Get total count of unique restaurants with SMS logs
|
||||
const totalResult = await this.em.execute(
|
||||
`
|
||||
SELECT COUNT(DISTINCT sl.restaurant_id) as total
|
||||
FROM sms_logs sl
|
||||
WHERE sl.restaurant_id IS NOT NULL
|
||||
`,
|
||||
);
|
||||
const total = parseInt(totalResult[0]?.total || '0', 10);
|
||||
|
||||
// Get paginated results with SMS counts grouped by restaurant
|
||||
const results = await this.em.execute(
|
||||
`
|
||||
SELECT
|
||||
r.id as "",
|
||||
r.name as "restaurantName",
|
||||
COUNT(sl.id)::int as "smsCount"
|
||||
FROM sms_logs sl
|
||||
INNER JOIN restaurants r ON sl.restaurant_id = r.id
|
||||
GROUP BY r.id, r.name
|
||||
ORDER BY "smsCount" DESC, r.name ASC
|
||||
LIMIT ? OFFSET ?
|
||||
`,
|
||||
[limit, offset],
|
||||
);
|
||||
|
||||
const data = results.map((row: any) => ({
|
||||
: row.,
|
||||
restaurantName: row.restaurantName || 'Unknown',
|
||||
smsCount: parseInt(row.smsCount || '0', 10),
|
||||
}));
|
||||
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
return {
|
||||
data,
|
||||
meta: {
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
totalPages,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async getSmsCountBy(: string): Promise < number > {
|
||||
const result = await this.em.execute(
|
||||
`
|
||||
SELECT COUNT(sl.id)::int as "smsCount"
|
||||
FROM sms_logs sl
|
||||
WHERE sl.restaurant_id = ?
|
||||
`,
|
||||
[],
|
||||
);
|
||||
|
||||
return parseInt(result[0]?.smsCount || '0', 10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { EntityManager } from '@mikro-orm/postgresql';
|
||||
import { NotificationPreference } from '../entities/notification-preference.entity';
|
||||
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
|
||||
import { CreatePreferenceDto } from '../dto/create-preference.dto';
|
||||
import { UpdatePreferenceDto } from '../dto/update-preference.dto';
|
||||
import { NotifTitleEnum } from '../interfaces/notification.interface';
|
||||
@@ -10,34 +9,34 @@ import { NotifTitleEnum } from '../interfaces/notification.interface';
|
||||
export class NotificationPreferenceService {
|
||||
constructor(private readonly em: EntityManager) { }
|
||||
|
||||
async create(: string, dto: CreatePreferenceDto): Promise<NotificationPreference> {
|
||||
const restaurant = await this.em.findOne(Restaurant, { id: });
|
||||
if (!restaurant) {
|
||||
throw new NotFoundException('Restaurant not found');
|
||||
}
|
||||
// async create( dto: CreatePreferenceDto): Promise<NotificationPreference> {
|
||||
// const restaurant = await this.em.findOne(Restaurant, { id: });
|
||||
// if (!restaurant) {
|
||||
// throw new NotFoundException('Restaurant not found');
|
||||
// }
|
||||
|
||||
const preference = this.em.create(NotificationPreference, {
|
||||
restaurant,
|
||||
channels: dto.channels,
|
||||
title: dto.title,
|
||||
});
|
||||
// const preference = this.em.create(NotificationPreference, {
|
||||
// restaurant,
|
||||
// channels: dto.channels,
|
||||
// title: dto.title,
|
||||
// });
|
||||
|
||||
await this.em.persistAndFlush(preference);
|
||||
return preference;
|
||||
}
|
||||
// await this.em.persistAndFlush(preference);
|
||||
// return preference;
|
||||
// }
|
||||
|
||||
async findByRestaurant(: string): Promise<NotificationPreference[]> {
|
||||
return this.em.find(NotificationPreference, {
|
||||
restaurant: { id: },
|
||||
});
|
||||
}
|
||||
// async findByRestaurant(: string): Promise<NotificationPreference[]> {
|
||||
// return this.em.find(NotificationPreference, {
|
||||
// restaurant: { id: },
|
||||
// });
|
||||
// }
|
||||
|
||||
async findByRestaurantAndType(: string, title: NotifTitleEnum): Promise<NotificationPreference | null> {
|
||||
return this.em.findOne(NotificationPreference, {
|
||||
restaurant: { id: },
|
||||
title,
|
||||
});
|
||||
}
|
||||
// async findByRestaurantAndType(: string, title: NotifTitleEnum): Promise<NotificationPreference | null> {
|
||||
// return this.em.findOne(NotificationPreference, {
|
||||
// restaurant: { id: },
|
||||
// title,
|
||||
// });
|
||||
// }
|
||||
|
||||
// async updateEnabled(
|
||||
// : string,
|
||||
@@ -54,32 +53,32 @@ export class NotificationPreferenceService {
|
||||
// return preference;
|
||||
// }
|
||||
|
||||
async updatePreference(
|
||||
preferenceId: string,
|
||||
: string,
|
||||
dto: UpdatePreferenceDto,
|
||||
): Promise<NotificationPreference> {
|
||||
const preference = await this.em.findOne(NotificationPreference, {
|
||||
id: preferenceId,
|
||||
restaurant: { id: },
|
||||
});
|
||||
if (!preference) {
|
||||
throw new NotFoundException('Notification preference not found');
|
||||
}
|
||||
// async updatePreference(
|
||||
// preferenceId: string,
|
||||
// : string,
|
||||
// dto: UpdatePreferenceDto,
|
||||
// ): Promise<NotificationPreference> {
|
||||
// const preference = await this.em.findOne(NotificationPreference, {
|
||||
// id: preferenceId,
|
||||
// restaurant: { id: },
|
||||
// });
|
||||
// if (!preference) {
|
||||
// throw new NotFoundException('Notification preference not found');
|
||||
// }
|
||||
|
||||
this.em.assign(preference, dto);
|
||||
await this.em.persistAndFlush(preference);
|
||||
return preference;
|
||||
}
|
||||
// this.em.assign(preference, dto);
|
||||
// await this.em.persistAndFlush(preference);
|
||||
// return preference;
|
||||
// }
|
||||
|
||||
async delete(: string, preferenceId: string): Promise<void> {
|
||||
const preference = await this.em.findOne(NotificationPreference, {
|
||||
restaurant: { id: },
|
||||
id: preferenceId,
|
||||
});
|
||||
if (!preference) {
|
||||
throw new NotFoundException('Notification preference not found');
|
||||
}
|
||||
await this.em.removeAndFlush(preference);
|
||||
}
|
||||
// async delete(: string, preferenceId: string): Promise<void> {
|
||||
// const preference = await this.em.findOne(NotificationPreference, {
|
||||
// restaurant: { id: },
|
||||
// id: preferenceId,
|
||||
// });
|
||||
// if (!preference) {
|
||||
// throw new NotFoundException('Notification preference not found');
|
||||
// }
|
||||
// await this.em.removeAndFlush(preference);
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -21,259 +21,259 @@ export class NotificationService {
|
||||
private readonly smsLogRepository: SmsLogRepository,
|
||||
) { }
|
||||
|
||||
async sendNotification(params: NotifRequest): Promise<Notification[]> {
|
||||
const { recipients, message, metadata, } = params;
|
||||
// async sendNotification(params: NotifRequest): Promise<Notification[]> {
|
||||
// const { recipients, message, metadata, } = params;
|
||||
|
||||
// create Database notifications
|
||||
const notifications = await this.createAdminBulkNotifications(
|
||||
recipients.map(recipient => ({
|
||||
title: message.title,
|
||||
content: message.content,
|
||||
adminId: 'adminId' in recipient ? recipient.adminId : null,
|
||||
userId: 'userId' in recipient ? recipient.userId : null,
|
||||
})),
|
||||
);
|
||||
// // create Database notifications
|
||||
// const notifications = await this.createAdminBulkNotifications(
|
||||
// recipients.map(recipient => ({
|
||||
// title: message.title,
|
||||
// content: message.content,
|
||||
// adminId: 'adminId' in recipient ? recipient.adminId : null,
|
||||
// userId: 'userId' in recipient ? recipient.userId : null,
|
||||
// })),
|
||||
// );
|
||||
|
||||
// get admin prefrences
|
||||
const preference = await this.preferenceService.findByRestaurantAndType(, message.title);
|
||||
// // get admin prefrences
|
||||
// const preference = await this.preferenceService.findByRestaurantAndType(, message.title);
|
||||
|
||||
if(preference?.channels?.length === 0) {
|
||||
this.logger.warn(`Notification type is NONE for restaurant ${}, title ${message.title}`);
|
||||
return notifications;
|
||||
}
|
||||
// if(preference?.channels?.length === 0) {
|
||||
// this.logger.warn(`Notification type is NONE for restaurant ${}, title ${message.title}`);
|
||||
// return notifications;
|
||||
// }
|
||||
|
||||
// send in app notification
|
||||
if (preference?.channels?.includes(NotifChannelEnum.IN_APP)) {
|
||||
await this.queueService.addBulkInAppNotifications(
|
||||
notifications.map(notification => ({
|
||||
recipient: { adminId: notification.admin?.id || '', },
|
||||
subject: message.title,
|
||||
body: message.content,
|
||||
notificationId: notification.id,
|
||||
})),
|
||||
);
|
||||
}
|
||||
// // send in app notification
|
||||
// if (preference?.channels?.includes(NotifChannelEnum.IN_APP)) {
|
||||
// await this.queueService.addBulkInAppNotifications(
|
||||
// notifications.map(notification => ({
|
||||
// recipient: { adminId: notification.admin?.id || '', },
|
||||
// subject: message.title,
|
||||
// body: message.content,
|
||||
// notificationId: notification.id,
|
||||
// })),
|
||||
// );
|
||||
// }
|
||||
|
||||
// add sms notifications to queue
|
||||
if (preference?.channels?.includes(NotifChannelEnum.SMS)) {
|
||||
await this.queueService.addBulkSmsNotifications(
|
||||
recipients.map(recipient => ({
|
||||
recipient,
|
||||
templateId: message.sms.templateId,
|
||||
parameters: message.sms.parameters,
|
||||
})),
|
||||
);
|
||||
}
|
||||
// // add sms notifications to queue
|
||||
// if (preference?.channels?.includes(NotifChannelEnum.SMS)) {
|
||||
// await this.queueService.addBulkSmsNotifications(
|
||||
// recipients.map(recipient => ({
|
||||
// recipient,
|
||||
// templateId: message.sms.templateId,
|
||||
// parameters: message.sms.parameters,
|
||||
// })),
|
||||
// );
|
||||
// }
|
||||
|
||||
this.logger.log(`Queued notification for restaurant ${}, title ${message.title}`);
|
||||
// this.logger.log(`Queued notification for restaurant ${}, title ${message.title}`);
|
||||
|
||||
return notifications;
|
||||
}
|
||||
// return notifications;
|
||||
// }
|
||||
|
||||
async createAdminBulkNotifications(
|
||||
params: {
|
||||
// async createAdminBulkNotifications(
|
||||
// params: {
|
||||
|
||||
title: NotifTitleEnum;
|
||||
content: string;
|
||||
adminId: string | null;
|
||||
userId: string | null;
|
||||
}[],
|
||||
): Promise < Notification[] > {
|
||||
const notifications = params.map(param => {
|
||||
return this.em.create(Notification, {
|
||||
admin: param.adminId,
|
||||
user: param.userId && param.userId.trim() !== '' ? param.userId : null,
|
||||
title: param.title,
|
||||
content: param.content,
|
||||
});
|
||||
});
|
||||
await this.em.persistAndFlush(notifications);
|
||||
return notifications;
|
||||
}
|
||||
|
||||
async findOne(id: string): Promise < Notification > {
|
||||
const notification = await this.em.findOne(Notification, { id }, { populate: ['restaurant', 'user'] });
|
||||
if(!notification) {
|
||||
throw new NotFoundException('Notification not found');
|
||||
}
|
||||
return notification;
|
||||
}
|
||||
|
||||
async findByRestaurant(
|
||||
: string,
|
||||
adminId: string,
|
||||
limit = 50,
|
||||
cursor ?: string,
|
||||
status ?: 'seen' | 'unseen',
|
||||
): Promise < { data: Notification[]; nextCursor: string | null } > {
|
||||
const where: FilterQuery<Notification> = {
|
||||
restaurant: { id: },
|
||||
admin: { id: adminId },
|
||||
};
|
||||
|
||||
// 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(
|
||||
userId: string,
|
||||
: string,
|
||||
limit = 50,
|
||||
cursor ?: string,
|
||||
status ?: 'seen' | 'unseen',
|
||||
): Promise < { data: Notification[]; nextCursor: string | null } > {
|
||||
const where: FilterQuery<Notification> = {
|
||||
user: { id: userId },
|
||||
restaurant: { id: },
|
||||
};
|
||||
|
||||
// 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 (since ULIDs are time-ordered)
|
||||
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 if there's a next page
|
||||
});
|
||||
|
||||
// Check if there's a next page
|
||||
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 readNotificationAdmin(id: string, adminId: string, : string): Promise < void> {
|
||||
const notification = await this.em.findOne(Notification, {
|
||||
id,
|
||||
admin: { id: adminId },
|
||||
restaurant: { id: },
|
||||
});
|
||||
if(!notification) {
|
||||
throw new NotFoundException('Notification not found');
|
||||
}
|
||||
notification.seenAt = new Date();
|
||||
await this.em.persistAndFlush(notification);
|
||||
}
|
||||
async readNotificationAsUser(id: string, userId: string, : string): Promise < void> {
|
||||
const notification = await this.em.findOne(Notification, {
|
||||
id,
|
||||
user: { id: userId },
|
||||
restaurant: { id: },
|
||||
});
|
||||
if(!notification) {
|
||||
throw new NotFoundException('Notification not found');
|
||||
}
|
||||
notification.seenAt = new Date();
|
||||
await this.em.persistAndFlush(notification);
|
||||
}
|
||||
|
||||
async findByRestaurantAndType(: string, title: NotifTitleEnum, limit = 50): Promise < Notification[] > {
|
||||
return this.em.find(
|
||||
Notification,
|
||||
{
|
||||
restaurant: { id: },
|
||||
title,
|
||||
},
|
||||
{
|
||||
orderBy: { createdAt: 'DESC' },
|
||||
limit,
|
||||
populate: ['user'],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async findByAdminAndRestaurant(adminId: string, : string, limit = 50): Promise < Notification[] > {
|
||||
return this.em.find(
|
||||
Notification,
|
||||
{ admin: { id: adminId }, restaurant: { id: } },
|
||||
{
|
||||
orderBy: { createdAt: 'DESC' },
|
||||
limit,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async countUnseenByUserAndRestaurant(userId: string, : string): Promise < number > {
|
||||
const where: FilterQuery<Notification> = {
|
||||
user: { id: userId },
|
||||
restaurant: { id: },
|
||||
seenAt: null,
|
||||
};
|
||||
return this.em.count(Notification, where);
|
||||
}
|
||||
|
||||
async countUnseenByRestaurant(adminId: string, : string): Promise < number > {
|
||||
const where: FilterQuery<Notification> = {
|
||||
admin: { id: adminId },
|
||||
restaurant: { id: },
|
||||
seenAt: null,
|
||||
};
|
||||
return this.em.count(Notification, where);
|
||||
}
|
||||
|
||||
readAllNotifsAsUser(userId: string, : string): Promise < number > {
|
||||
const where: FilterQuery<Notification> = {
|
||||
user: { id: userId },
|
||||
restaurant: { id: },
|
||||
seenAt: null,
|
||||
};
|
||||
return this.em.nativeUpdate(Notification, where, { seenAt: new Date() });
|
||||
}
|
||||
|
||||
readAllNotifsAsAdmin(adminId: string, : string): Promise < number > {
|
||||
const where: FilterQuery<Notification> = {
|
||||
admin: { id: adminId },
|
||||
restaurant: { id: },
|
||||
seenAt: null,
|
||||
};
|
||||
return this.em.nativeUpdate(Notification, where, { seenAt: new Date() });
|
||||
}
|
||||
|
||||
async getSmsCountByRestaurant(
|
||||
page: number = 1,
|
||||
limit: number = 10,
|
||||
): Promise < PaginatedResult < { : string; restaurantName: string; smsCount: number } >> {
|
||||
return this.smsLogRepository.getSmsCountByRestaurant(page, limit);
|
||||
}
|
||||
|
||||
async getSmsCountBy(: string): Promise < number > {
|
||||
return this.smsLogRepository.getSmsCountBy();
|
||||
}
|
||||
// title: NotifTitleEnum;
|
||||
// content: string;
|
||||
// adminId: string | null;
|
||||
// userId: string | null;
|
||||
// }[],
|
||||
// ): Promise < Notification[] > {
|
||||
// const notifications = params.map(param => {
|
||||
// return this.em.create(Notification, {
|
||||
// admin: param.adminId,
|
||||
// user: param.userId && param.userId.trim() !== '' ? param.userId : null,
|
||||
// title: param.title,
|
||||
// content: param.content,
|
||||
// });
|
||||
// });
|
||||
// await this.em.persistAndFlush(notifications);
|
||||
// return notifications;
|
||||
// }
|
||||
|
||||
// async findOne(id: string): Promise < Notification > {
|
||||
// const notification = await this.em.findOne(Notification, { id }, { populate: ['restaurant', 'user'] });
|
||||
// if(!notification) {
|
||||
// throw new NotFoundException('Notification not found');
|
||||
// }
|
||||
// return notification;
|
||||
// }
|
||||
|
||||
// async findByRestaurant(
|
||||
// : string,
|
||||
// adminId: string,
|
||||
// limit = 50,
|
||||
// cursor ?: string,
|
||||
// status ?: 'seen' | 'unseen',
|
||||
// ): Promise < { data: Notification[]; nextCursor: string | null } > {
|
||||
// const where: FilterQuery<Notification> = {
|
||||
// restaurant: { id: },
|
||||
// admin: { id: adminId },
|
||||
// };
|
||||
|
||||
// // 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(
|
||||
// userId: string,
|
||||
// : string,
|
||||
// limit = 50,
|
||||
// cursor ?: string,
|
||||
// status ?: 'seen' | 'unseen',
|
||||
// ): Promise < { data: Notification[]; nextCursor: string | null } > {
|
||||
// const where: FilterQuery<Notification> = {
|
||||
// user: { id: userId },
|
||||
// restaurant: { id: },
|
||||
// };
|
||||
|
||||
// // 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 (since ULIDs are time-ordered)
|
||||
// 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 if there's a next page
|
||||
// });
|
||||
|
||||
// // Check if there's a next page
|
||||
// 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 readNotificationAdmin(id: string, adminId: string, : string): Promise < void> {
|
||||
// const notification = await this.em.findOne(Notification, {
|
||||
// id,
|
||||
// admin: { id: adminId },
|
||||
// restaurant: { id: },
|
||||
// });
|
||||
// if(!notification) {
|
||||
// throw new NotFoundException('Notification not found');
|
||||
// }
|
||||
// notification.seenAt = new Date();
|
||||
// await this.em.persistAndFlush(notification);
|
||||
// }
|
||||
// async readNotificationAsUser(id: string, userId: string, : string): Promise < void> {
|
||||
// const notification = await this.em.findOne(Notification, {
|
||||
// id,
|
||||
// user: { id: userId },
|
||||
// restaurant: { id: },
|
||||
// });
|
||||
// if(!notification) {
|
||||
// throw new NotFoundException('Notification not found');
|
||||
// }
|
||||
// notification.seenAt = new Date();
|
||||
// await this.em.persistAndFlush(notification);
|
||||
// }
|
||||
|
||||
// async findByRestaurantAndType(: string, title: NotifTitleEnum, limit = 50): Promise < Notification[] > {
|
||||
// return this.em.find(
|
||||
// Notification,
|
||||
// {
|
||||
// restaurant: { id: },
|
||||
// title,
|
||||
// },
|
||||
// {
|
||||
// orderBy: { createdAt: 'DESC' },
|
||||
// limit,
|
||||
// populate: ['user'],
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
|
||||
// async findByAdminAndRestaurant(adminId: string, : string, limit = 50): Promise < Notification[] > {
|
||||
// return this.em.find(
|
||||
// Notification,
|
||||
// { admin: { id: adminId }, restaurant: { id: } },
|
||||
// {
|
||||
// orderBy: { createdAt: 'DESC' },
|
||||
// limit,
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
|
||||
// async countUnseenByUserAndRestaurant(userId: string, : string): Promise < number > {
|
||||
// const where: FilterQuery<Notification> = {
|
||||
// user: { id: userId },
|
||||
// restaurant: { id: },
|
||||
// seenAt: null,
|
||||
// };
|
||||
// return this.em.count(Notification, where);
|
||||
// }
|
||||
|
||||
// async countUnseenByRestaurant(adminId: string, : string): Promise < number > {
|
||||
// const where: FilterQuery<Notification> = {
|
||||
// admin: { id: adminId },
|
||||
// restaurant: { id: },
|
||||
// seenAt: null,
|
||||
// };
|
||||
// return this.em.count(Notification, where);
|
||||
// }
|
||||
|
||||
// readAllNotifsAsUser(userId: string, : string): Promise < number > {
|
||||
// const where: FilterQuery<Notification> = {
|
||||
// user: { id: userId },
|
||||
// restaurant: { id: },
|
||||
// seenAt: null,
|
||||
// };
|
||||
// return this.em.nativeUpdate(Notification, where, { seenAt: new Date() });
|
||||
// }
|
||||
|
||||
// readAllNotifsAsAdmin(adminId: string, : string): Promise < number > {
|
||||
// const where: FilterQuery<Notification> = {
|
||||
// admin: { id: adminId },
|
||||
// restaurant: { id: },
|
||||
// seenAt: null,
|
||||
// };
|
||||
// return this.em.nativeUpdate(Notification, where, { seenAt: new Date() });
|
||||
// }
|
||||
|
||||
// async getSmsCountByRestaurant(
|
||||
// page: number = 1,
|
||||
// limit: number = 10,
|
||||
// ): Promise < PaginatedResult < { : string; restaurantName: string; smsCount: number } >> {
|
||||
// return this.smsLogRepository.getSmsCountByRestaurant(page, limit);
|
||||
// }
|
||||
|
||||
// async getSmsCountBy(: string): Promise < number > {
|
||||
// return this.smsLogRepository.getSmsCountBy();
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user