admin
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsMobilePhone, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
import { IsArray, IsMobilePhone, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||||
|
import { NotifEvent } from 'src/modules/notification/interfaces/notification.interface';
|
||||||
|
|
||||||
export class CreateAdminDto {
|
export class CreateAdminDto {
|
||||||
@ApiProperty({ description: 'Mobile phone number' })
|
@ApiProperty({ description: 'Mobile phone number' })
|
||||||
@@ -22,4 +23,8 @@ export class CreateAdminDto {
|
|||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@IsString()
|
@IsString()
|
||||||
roleId!: string;
|
roleId!: string;
|
||||||
|
|
||||||
|
@ApiProperty({})
|
||||||
|
@IsArray()
|
||||||
|
notificationPrefrences:NotifEvent[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { Entity, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core';
|
import { Cascade, Entity, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core';
|
||||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||||
import { normalizePhone } from '../../util/phone.util';
|
import { normalizePhone } from '../../util/phone.util';
|
||||||
import { Role } from 'src/modules/roles/entities/role.entity';
|
import { Role } from 'src/modules/roles/entities/role.entity';
|
||||||
import { ulid } from 'ulid';
|
import { ulid } from 'ulid';
|
||||||
|
import { NotificationPreference } from 'src/modules/notification/entities/notification-preference.entity';
|
||||||
|
|
||||||
@Entity({ tableName: 'admins' })
|
@Entity({ tableName: 'admins' })
|
||||||
export class Admin extends BaseEntity {
|
export class Admin extends BaseEntity {
|
||||||
@@ -29,4 +30,9 @@ export class Admin extends BaseEntity {
|
|||||||
|
|
||||||
@ManyToOne(() => Role)
|
@ManyToOne(() => Role)
|
||||||
role: Role
|
role: Role
|
||||||
|
|
||||||
|
// @OneToOne(() => NotificationPreference, pref => pref.admin, {
|
||||||
|
|
||||||
|
// })
|
||||||
|
// notificationPreference!: NotificationPreference;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { BadRequestException, ConflictException, Injectable, NotFoundException } from '@nestjs/common';
|
import { BadRequestException, ConflictException, Injectable, NotFoundException } from '@nestjs/common';
|
||||||
import { Admin } from '../entities/admin.entity';
|
import { Admin } from '../entities/admin.entity';
|
||||||
import { Role } from '../../roles/entities/role.entity';
|
import { Role } from '../../roles/entities/role.entity';
|
||||||
import { EntityManager, RequiredEntityData } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { UpdateAdminDto } from '../dto/update-admin.dto';
|
import { UpdateAdminDto } from '../dto/update-admin.dto';
|
||||||
import { normalizePhone } from '../../util/phone.util';
|
import { normalizePhone } from '../../util/phone.util';
|
||||||
import { CreateAdminDto } from '../dto/create-admin.dto';
|
import { CreateAdminDto } from '../dto/create-admin.dto';
|
||||||
import { AdminRepository } from '../repositories/admin.repository';
|
import { AdminRepository } from '../repositories/admin.repository';
|
||||||
import { RoleRepository } from 'src/modules/roles/respository/role.repository';
|
import { RoleRepository } from 'src/modules/roles/respository/role.repository';
|
||||||
|
import { NotificationPreference } from 'src/modules/notification/entities/notification-preference.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AdminService {
|
export class AdminService {
|
||||||
@@ -17,7 +18,7 @@ export class AdminService {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
async create(dto: CreateAdminDto) {
|
async create(dto: CreateAdminDto) {
|
||||||
const { phone, firstName, lastName, roleId } = dto;
|
const { phone, firstName, lastName, roleId, notificationPrefrences } = dto;
|
||||||
|
|
||||||
const normalizedPhone = normalizePhone(phone);
|
const normalizedPhone = normalizePhone(phone);
|
||||||
|
|
||||||
@@ -25,25 +26,34 @@ export class AdminService {
|
|||||||
phone: normalizedPhone,
|
phone: normalizedPhone,
|
||||||
});
|
});
|
||||||
if (exist) {
|
if (exist) {
|
||||||
throw new NotFoundException('This phone number is already used');
|
throw new BadRequestException('This phone number is already used');
|
||||||
}
|
}
|
||||||
|
|
||||||
const role = await this.roleRepository.findOne({ id: roleId })
|
const role = await this.roleRepository.findOne({ id: roleId })
|
||||||
if (!role) {
|
if (!role) {
|
||||||
throw new NotFoundException('Role not found');
|
throw new BadRequestException('Role not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const createData: RequiredEntityData<Admin> = {
|
|
||||||
phone: normalizedPhone,
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
role
|
|
||||||
}
|
|
||||||
const admin = this.adminRepository.create(createData)
|
|
||||||
|
|
||||||
await this.em.persistAndFlush(admin);
|
return this.em.transactional(async em => {
|
||||||
|
|
||||||
|
const admin = em.create(Admin, {
|
||||||
|
phone: normalizedPhone,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
role
|
||||||
|
})
|
||||||
|
|
||||||
|
const notificationPreference = em.create(NotificationPreference, {
|
||||||
|
admin,
|
||||||
|
events: notificationPrefrences,
|
||||||
|
})
|
||||||
|
|
||||||
|
await this.em.persistAndFlush([admin, notificationPreference]);
|
||||||
|
|
||||||
|
return admin
|
||||||
|
})
|
||||||
|
|
||||||
return admin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async findByPhone(phone: string): Promise<Admin | null> {
|
async findByPhone(phone: string): Promise<Admin | null> {
|
||||||
@@ -82,6 +92,18 @@ export class AdminService {
|
|||||||
if (rest.lastName !== undefined) {
|
if (rest.lastName !== undefined) {
|
||||||
admin.lastName = rest.lastName;
|
admin.lastName = rest.lastName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rest.notificationPrefrences) {
|
||||||
|
//
|
||||||
|
const nf = await this.em.findOne(NotificationPreference, {
|
||||||
|
admin
|
||||||
|
})
|
||||||
|
if (!nf) {
|
||||||
|
throw new BadRequestException('Notification prefrences not found!')
|
||||||
|
}
|
||||||
|
nf.events = rest.notificationPrefrences
|
||||||
|
await this.em.flush()
|
||||||
|
}
|
||||||
if (rest.phone !== undefined && rest.phone !== admin.phone) {
|
if (rest.phone !== undefined && rest.phone !== admin.phone) {
|
||||||
const normalizedPhone = normalizePhone(rest.phone);
|
const normalizedPhone = normalizePhone(rest.phone);
|
||||||
const exists = await this.adminRepository.findOne({ phone: normalizedPhone });
|
const exists = await this.adminRepository.findOne({ phone: normalizedPhone });
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { IsNotEmpty, IsEnum, IsArray } from 'class-validator';
|
import { IsNotEmpty, IsEnum, IsArray } from 'class-validator';
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { NotifTitleEnum } from '../interfaces/notification.interface';
|
import { NotifEvent } from '../interfaces/notification.interface';
|
||||||
import { NotifChannelEnum } from '../interfaces/notification.interface';
|
import { NotifChannelEnum } from '../interfaces/notification.interface';
|
||||||
|
|
||||||
export class CreatePreferenceDto {
|
export class CreatePreferenceDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Notification title/type (e.g., order.created, review.created)',
|
description: 'Notification title/type (e.g., order.created, review.created)',
|
||||||
enum: NotifTitleEnum,
|
enum: NotifEvent,
|
||||||
example: NotifTitleEnum.ORDER_CREATED,
|
example: NotifEvent.NEW_ORDER,
|
||||||
})
|
})
|
||||||
@IsEnum(NotifTitleEnum)
|
@IsEnum(NotifEvent)
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
title!: NotifTitleEnum;
|
title!: NotifEvent;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Notification type (SMS, PUSH, BOTH, or NONE)',
|
description: 'Notification type (SMS, PUSH, BOTH, or NONE)',
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { Entity, Property, ManyToOne, Unique, PrimaryKey } from '@mikro-orm/core';
|
import { Entity, Property, ManyToOne, Unique, PrimaryKey, OneToOne, Cascade } from '@mikro-orm/core';
|
||||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||||
import { NotifTitleEnum } from '../interfaces/notification.interface';
|
import { NotifEvent } from '../interfaces/notification.interface';
|
||||||
import { NotifChannelEnum } from '../interfaces/notification.interface';
|
import { Admin } from 'src/modules/admin/entities/admin.entity';
|
||||||
|
|
||||||
@Entity({ tableName: 'notification_preferences' })
|
@Entity({ tableName: 'notification_preferences' })
|
||||||
export class NotificationPreference extends BaseEntity {
|
export class NotificationPreference extends BaseEntity {
|
||||||
@PrimaryKey({ type: 'bigint', autoincrement: true })
|
@PrimaryKey({ type: 'int', autoincrement: true })
|
||||||
id: bigint
|
id: number
|
||||||
|
|
||||||
@Property()
|
|
||||||
title!: NotifTitleEnum;
|
|
||||||
|
|
||||||
@Property({ type: 'json' })
|
@OneToOne(() => Admin, { owner: true, unique: true, cascade: [Cascade.PERSIST, Cascade.REMOVE] })
|
||||||
channels: NotifChannelEnum[] = [];
|
admin!: Admin;
|
||||||
|
|
||||||
|
@Property({ type: 'json', nullable: true })
|
||||||
|
events: NotifEvent[] = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import { Entity, Property, ManyToOne, Enum, PrimaryKey } from '@mikro-orm/core';
|
import { Entity, Property, ManyToOne, Enum, PrimaryKey } from '@mikro-orm/core';
|
||||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||||
import { User } from '../../user/entities/user.entity';
|
import { User } from '../../user/entities/user.entity';
|
||||||
import { NotifTitleEnum } from '../interfaces/notification.interface';
|
import { NotifEvent } from '../interfaces/notification.interface';
|
||||||
import { Admin } from 'src/modules/admin/entities/admin.entity';
|
import { Admin } from 'src/modules/admin/entities/admin.entity';
|
||||||
|
|
||||||
@Entity({ tableName: 'notifications' })
|
@Entity({ tableName: 'notifications' })
|
||||||
export class Notification extends BaseEntity {
|
export class Notification extends BaseEntity {
|
||||||
@PrimaryKey({ type: 'bigint', autoincrement: true })
|
@PrimaryKey({ type: 'bigint', autoincrement: true })
|
||||||
id: bigint
|
id: bigint
|
||||||
|
|
||||||
@ManyToOne(() => User, { nullable: true })
|
@ManyToOne(() => User, { nullable: true })
|
||||||
user?: User;
|
user?: User;
|
||||||
|
|
||||||
@ManyToOne(() => Admin, { nullable: true })
|
@ManyToOne(() => Admin, { nullable: true })
|
||||||
admin?: Admin;
|
admin?: Admin;
|
||||||
|
|
||||||
@Enum(() => NotifTitleEnum)
|
@Enum(() => NotifEvent)
|
||||||
title!: NotifTitleEnum;
|
title!: NotifEvent;
|
||||||
|
|
||||||
@Property()
|
@Property()
|
||||||
content!: string;
|
content!: string;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { NotifTitleEnum, recipientType } from './notification.interface';
|
import type { NotifEvent, recipientType } from './notification.interface';
|
||||||
|
|
||||||
export interface SmsNotificationQueueJob {
|
export interface SmsNotificationQueueJob {
|
||||||
recipient: recipientType;
|
recipient: recipientType;
|
||||||
@@ -18,7 +18,7 @@ export interface PushNotificationQueueJob {
|
|||||||
}
|
}
|
||||||
export interface InAppNotificationQueueJob {
|
export interface InAppNotificationQueueJob {
|
||||||
recipient: { adminId: string; };
|
recipient: { adminId: string; };
|
||||||
subject: NotifTitleEnum;
|
subject: NotifEvent;
|
||||||
body: string;
|
body: string;
|
||||||
notificationId: string;
|
notificationId: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,17 +8,17 @@ export enum NotifTypeEnum {
|
|||||||
PROMOTIONAL = 'promotional',
|
PROMOTIONAL = 'promotional',
|
||||||
SYSTEM = 'system',
|
SYSTEM = 'system',
|
||||||
}
|
}
|
||||||
export enum NotifTitleEnum {
|
export enum NotifEvent {
|
||||||
PAGER_CREATED = 'pager.created',
|
NEW_REQUEST = 'newRequest',
|
||||||
ORDER_CREATED = 'order.created',
|
INVOICED = 'invoiced',
|
||||||
PAYMENT_SUCCESS = 'payment.success',
|
NEW_ORDER = 'newOrder', // for chapkhane sms bere
|
||||||
REVIEW_CREATED = 'review.created',
|
DESIGNER_ASSIGN = 'designerAssign',
|
||||||
ORDER_STATUS_CHANGED = 'order.status.changed',
|
PAYMENT_SUCCESS = 'paymentSuccess',
|
||||||
}
|
}
|
||||||
export type recipientType = { userId: string } | { adminId: string };
|
export type recipientType = { userId: string } | { adminId: string };
|
||||||
|
|
||||||
export interface NotifRequestMessage {
|
export interface NotifRequestMessage {
|
||||||
title: NotifTitleEnum;
|
title: NotifEvent;
|
||||||
content: string;
|
content: string;
|
||||||
sms: {
|
sms: {
|
||||||
templateId: string;
|
templateId: string;
|
||||||
@@ -53,11 +53,11 @@ interface INotifySmsPayload {
|
|||||||
}
|
}
|
||||||
interface INotifySms {
|
interface INotifySms {
|
||||||
phone: string;
|
phone: string;
|
||||||
subject: NotifTitleEnum;
|
subject: NotifEvent;
|
||||||
}
|
}
|
||||||
export interface IInAppNotificationPayload {
|
export interface IInAppNotificationPayload {
|
||||||
notificationId: string;
|
notificationId: string;
|
||||||
subject: NotifTitleEnum;
|
subject: NotifEvent;
|
||||||
body: string;
|
body: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ interface IPaymentSuccessSmsNotifyPayload extends INotifySmsPayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface IPaymentSuccessSmsNotify extends INotifySms {
|
interface IPaymentSuccessSmsNotify extends INotifySms {
|
||||||
subject: NotifTitleEnum.PAYMENT_SUCCESS;
|
subject: NotifEvent.PAYMENT_SUCCESS;
|
||||||
payload: IPaymentSuccessSmsNotifyPayload;
|
payload: IPaymentSuccessSmsNotifyPayload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { EntityManager } from '@mikro-orm/postgresql';
|
|||||||
import { NotificationPreference } from '../entities/notification-preference.entity';
|
import { NotificationPreference } from '../entities/notification-preference.entity';
|
||||||
import { CreatePreferenceDto } from '../dto/create-preference.dto';
|
import { CreatePreferenceDto } from '../dto/create-preference.dto';
|
||||||
import { UpdatePreferenceDto } from '../dto/update-preference.dto';
|
import { UpdatePreferenceDto } from '../dto/update-preference.dto';
|
||||||
import { NotifTitleEnum } from '../interfaces/notification.interface';
|
import { NotifEvent } from '../interfaces/notification.interface';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class NotificationPreferenceService {
|
export class NotificationPreferenceService {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Notification } from '../entities/notification.entity';
|
|||||||
import { NotificationPreferenceService } from './notification-preference.service';
|
import { NotificationPreferenceService } from './notification-preference.service';
|
||||||
import { NotificationQueueService } from './notification-queue.service';
|
import { NotificationQueueService } from './notification-queue.service';
|
||||||
import { NotificationsGateway } from '../notifications.gateway';
|
import { NotificationsGateway } from '../notifications.gateway';
|
||||||
import { NotifChannelEnum, NotifRequest, NotifTitleEnum } from '../interfaces/notification.interface';
|
import { NotifChannelEnum, NotifRequest, NotifEvent } from '../interfaces/notification.interface';
|
||||||
import { SmsLog } from '../entities/smsLogs.entity';
|
import { SmsLog } from '../entities/smsLogs.entity';
|
||||||
import { SmsLogRepository } from '../repositories/sms-log.repository';
|
import { SmsLogRepository } from '../repositories/sms-log.repository';
|
||||||
import { PaginatedResult } from '../../../common/interfaces/pagination.interface';
|
import { PaginatedResult } from '../../../common/interfaces/pagination.interface';
|
||||||
@@ -21,259 +21,259 @@ export class NotificationService {
|
|||||||
private readonly smsLogRepository: SmsLogRepository,
|
private readonly smsLogRepository: SmsLogRepository,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
// async sendNotification(params: NotifRequest): Promise<Notification[]> {
|
// async sendNotification(params: NotifRequest): Promise<Notification[]> {
|
||||||
// const { recipients, message, metadata, } = params;
|
// const { recipients, message, metadata, } = params;
|
||||||
|
|
||||||
// // create Database notifications
|
// // create Database notifications
|
||||||
// const notifications = await this.createAdminBulkNotifications(
|
// const notifications = await this.createAdminBulkNotifications(
|
||||||
// recipients.map(recipient => ({
|
// recipients.map(recipient => ({
|
||||||
// title: message.title,
|
// title: message.title,
|
||||||
// content: message.content,
|
// content: message.content,
|
||||||
// adminId: 'adminId' in recipient ? recipient.adminId : null,
|
// adminId: 'adminId' in recipient ? recipient.adminId : null,
|
||||||
// userId: 'userId' in recipient ? recipient.userId : null,
|
// userId: 'userId' in recipient ? recipient.userId : null,
|
||||||
// })),
|
// })),
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// // get admin prefrences
|
// // get admin prefrences
|
||||||
// const preference = await this.preferenceService.findByRestaurantAndType(, message.title);
|
// const preference = await this.preferenceService.findByRestaurantAndType(, message.title);
|
||||||
|
|
||||||
// if(preference?.channels?.length === 0) {
|
// if(preference?.channels?.length === 0) {
|
||||||
// this.logger.warn(`Notification type is NONE for restaurant ${}, title ${message.title}`);
|
// this.logger.warn(`Notification type is NONE for restaurant ${}, title ${message.title}`);
|
||||||
// return notifications;
|
// return notifications;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// // send in app notification
|
// // send in app notification
|
||||||
// if (preference?.channels?.includes(NotifChannelEnum.IN_APP)) {
|
// if (preference?.channels?.includes(NotifChannelEnum.IN_APP)) {
|
||||||
// await this.queueService.addBulkInAppNotifications(
|
// await this.queueService.addBulkInAppNotifications(
|
||||||
// notifications.map(notification => ({
|
// notifications.map(notification => ({
|
||||||
// recipient: { adminId: notification.admin?.id || '', },
|
// recipient: { adminId: notification.admin?.id || '', },
|
||||||
// subject: message.title,
|
// subject: message.title,
|
||||||
// body: message.content,
|
// body: message.content,
|
||||||
// notificationId: notification.id,
|
// notificationId: notification.id,
|
||||||
// })),
|
// })),
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// // add sms notifications to queue
|
// // add sms notifications to queue
|
||||||
// if (preference?.channels?.includes(NotifChannelEnum.SMS)) {
|
// if (preference?.channels?.includes(NotifChannelEnum.SMS)) {
|
||||||
// await this.queueService.addBulkSmsNotifications(
|
// await this.queueService.addBulkSmsNotifications(
|
||||||
// recipients.map(recipient => ({
|
// recipients.map(recipient => ({
|
||||||
// recipient,
|
// recipient,
|
||||||
// templateId: message.sms.templateId,
|
// templateId: message.sms.templateId,
|
||||||
// parameters: message.sms.parameters,
|
// 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(
|
// async createAdminBulkNotifications(
|
||||||
// params: {
|
// 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 > {
|
// title: NotifTitleEnum;
|
||||||
// const notification = await this.em.findOne(Notification, { id }, { populate: ['restaurant', 'user'] });
|
// content: string;
|
||||||
// if(!notification) {
|
// adminId: string | null;
|
||||||
// throw new NotFoundException('Notification not found');
|
// userId: string | null;
|
||||||
// }
|
// }[],
|
||||||
// return notification;
|
// ): 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 findByRestaurant(
|
// async findOne(id: string): Promise < Notification > {
|
||||||
// : string,
|
// const notification = await this.em.findOne(Notification, { id }, { populate: ['restaurant', 'user'] });
|
||||||
// adminId: string,
|
// if(!notification) {
|
||||||
// limit = 50,
|
// throw new NotFoundException('Notification not found');
|
||||||
// cursor ?: string,
|
// }
|
||||||
// status ?: 'seen' | 'unseen',
|
// return notification;
|
||||||
// ): Promise < { data: Notification[]; nextCursor: string | null } > {
|
// }
|
||||||
// const where: FilterQuery<Notification> = {
|
|
||||||
// restaurant: { id: },
|
|
||||||
// admin: { id: adminId },
|
|
||||||
// };
|
|
||||||
|
|
||||||
// // Filter by status (seen/unseen)
|
// async findByRestaurant(
|
||||||
// if (status === 'seen') {
|
// : string,
|
||||||
// where.seenAt = { $ne: null };
|
// adminId: string,
|
||||||
// } else if (status === 'unseen') {
|
// limit = 50,
|
||||||
// where.seenAt = null;
|
// cursor ?: string,
|
||||||
// }
|
// status ?: 'seen' | 'unseen',
|
||||||
|
// ): Promise < { data: Notification[]; nextCursor: string | null } > {
|
||||||
|
// const where: FilterQuery<Notification> = {
|
||||||
|
// restaurant: { id: },
|
||||||
|
// admin: { id: adminId },
|
||||||
|
// };
|
||||||
|
|
||||||
// // Cursor-based pagination: if cursor is provided, get items with id < cursor
|
// // Filter by status (seen/unseen)
|
||||||
// if (cursor) {
|
// if (status === 'seen') {
|
||||||
// where.id = { $lt: cursor };
|
// where.seenAt = { $ne: null };
|
||||||
// }
|
// } else if (status === 'unseen') {
|
||||||
|
// where.seenAt = null;
|
||||||
|
// }
|
||||||
|
|
||||||
// const notifications = await this.em.find(Notification, where, {
|
// // Cursor-based pagination: if cursor is provided, get items with id < cursor
|
||||||
// orderBy: { createdAt: 'DESC', id: 'DESC' },
|
// if (cursor) {
|
||||||
// limit: limit + 1, // fetch one extra to determine next page
|
// where.id = { $lt: cursor };
|
||||||
// populate: ['user'],
|
// }
|
||||||
// });
|
|
||||||
|
|
||||||
// const hasNextPage = notifications.length > limit;
|
// const notifications = await this.em.find(Notification, where, {
|
||||||
// const data = hasNextPage ? notifications.slice(0, limit) : notifications;
|
// orderBy: { createdAt: 'DESC', id: 'DESC' },
|
||||||
// const nextCursor = hasNextPage && data.length > 0 ? data[data.length - 1].id : null;
|
// limit: limit + 1, // fetch one extra to determine next page
|
||||||
|
// populate: ['user'],
|
||||||
|
// });
|
||||||
|
|
||||||
// return {
|
// const hasNextPage = notifications.length > limit;
|
||||||
// data,
|
// const data = hasNextPage ? notifications.slice(0, limit) : notifications;
|
||||||
// nextCursor: nextCursor ?? null,
|
// const nextCursor = hasNextPage && data.length > 0 ? data[data.length - 1].id : null;
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async findByUserAndRestaurant(
|
// return {
|
||||||
// userId: string,
|
// data,
|
||||||
// : string,
|
// nextCursor: nextCursor ?? null,
|
||||||
// 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)
|
// async findByUserAndRestaurant(
|
||||||
// if (status === 'seen') {
|
// userId: string,
|
||||||
// where.seenAt = { $ne: null };
|
// : string,
|
||||||
// } else if (status === 'unseen') {
|
// limit = 50,
|
||||||
// where.seenAt = null;
|
// cursor ?: string,
|
||||||
// }
|
// status ?: 'seen' | 'unseen',
|
||||||
|
// ): Promise < { data: Notification[]; nextCursor: string | null } > {
|
||||||
|
// const where: FilterQuery<Notification> = {
|
||||||
|
// user: { id: userId },
|
||||||
|
// restaurant: { id: },
|
||||||
|
// };
|
||||||
|
|
||||||
// // Cursor-based pagination: if cursor is provided, get items with id < cursor (since ULIDs are time-ordered)
|
// // Filter by status (seen/unseen)
|
||||||
// if (cursor) {
|
// if (status === 'seen') {
|
||||||
// where.id = { $lt: cursor };
|
// where.seenAt = { $ne: null };
|
||||||
// }
|
// } else if (status === 'unseen') {
|
||||||
|
// where.seenAt = null;
|
||||||
|
// }
|
||||||
|
|
||||||
// const notifications = await this.em.find(Notification, where, {
|
// // Cursor-based pagination: if cursor is provided, get items with id < cursor (since ULIDs are time-ordered)
|
||||||
// orderBy: { createdAt: 'DESC', id: 'DESC' },
|
// if (cursor) {
|
||||||
// limit: limit + 1, // Fetch one extra to determine if there's a next page
|
// where.id = { $lt: cursor };
|
||||||
// });
|
// }
|
||||||
|
|
||||||
// // Check if there's a next page
|
// const notifications = await this.em.find(Notification, where, {
|
||||||
// const hasNextPage = notifications.length > limit;
|
// orderBy: { createdAt: 'DESC', id: 'DESC' },
|
||||||
// const data = hasNextPage ? notifications.slice(0, limit) : notifications;
|
// limit: limit + 1, // Fetch one extra to determine if there's a next page
|
||||||
// const nextCursor = hasNextPage && data.length > 0 ? data[data.length - 1].id : null;
|
// });
|
||||||
|
|
||||||
// return {
|
// // Check if there's a next page
|
||||||
// data,
|
// const hasNextPage = notifications.length > limit;
|
||||||
// nextCursor: nextCursor ?? null,
|
// const data = hasNextPage ? notifications.slice(0, limit) : notifications;
|
||||||
// };
|
// const nextCursor = hasNextPage && data.length > 0 ? data[data.length - 1].id : null;
|
||||||
// }
|
|
||||||
|
|
||||||
// async readNotificationAdmin(id: string, adminId: string, : string): Promise < void> {
|
// return {
|
||||||
// const notification = await this.em.findOne(Notification, {
|
// data,
|
||||||
// id,
|
// nextCursor: nextCursor ?? null,
|
||||||
// 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[] > {
|
// async readNotificationAdmin(id: string, adminId: string, : string): Promise < void> {
|
||||||
// return this.em.find(
|
// const notification = await this.em.findOne(Notification, {
|
||||||
// Notification,
|
// id,
|
||||||
// {
|
// admin: { id: adminId },
|
||||||
// restaurant: { id: },
|
// restaurant: { id: },
|
||||||
// title,
|
// });
|
||||||
// },
|
// if(!notification) {
|
||||||
// {
|
// throw new NotFoundException('Notification not found');
|
||||||
// orderBy: { createdAt: 'DESC' },
|
// }
|
||||||
// limit,
|
// notification.seenAt = new Date();
|
||||||
// populate: ['user'],
|
// 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 findByAdminAndRestaurant(adminId: string, : string, limit = 50): Promise < Notification[] > {
|
// async findByRestaurantAndType(: string, title: NotifTitleEnum, limit = 50): Promise < Notification[] > {
|
||||||
// return this.em.find(
|
// return this.em.find(
|
||||||
// Notification,
|
// Notification,
|
||||||
// { admin: { id: adminId }, restaurant: { id: } },
|
// {
|
||||||
// {
|
// restaurant: { id: },
|
||||||
// orderBy: { createdAt: 'DESC' },
|
// title,
|
||||||
// limit,
|
// },
|
||||||
// },
|
// {
|
||||||
// );
|
// orderBy: { createdAt: 'DESC' },
|
||||||
// }
|
// limit,
|
||||||
|
// populate: ['user'],
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
// async countUnseenByUserAndRestaurant(userId: string, : string): Promise < number > {
|
// async findByAdminAndRestaurant(adminId: string, : string, limit = 50): Promise < Notification[] > {
|
||||||
// const where: FilterQuery<Notification> = {
|
// return this.em.find(
|
||||||
// user: { id: userId },
|
// Notification,
|
||||||
// restaurant: { id: },
|
// { admin: { id: adminId }, restaurant: { id: } },
|
||||||
// seenAt: null,
|
// {
|
||||||
// };
|
// orderBy: { createdAt: 'DESC' },
|
||||||
// return this.em.count(Notification, where);
|
// limit,
|
||||||
// }
|
// },
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
// async countUnseenByRestaurant(adminId: string, : string): Promise < number > {
|
// async countUnseenByUserAndRestaurant(userId: string, : string): Promise < number > {
|
||||||
// const where: FilterQuery<Notification> = {
|
// const where: FilterQuery<Notification> = {
|
||||||
// admin: { id: adminId },
|
// user: { id: userId },
|
||||||
// restaurant: { id: },
|
// restaurant: { id: },
|
||||||
// seenAt: null,
|
// seenAt: null,
|
||||||
// };
|
// };
|
||||||
// return this.em.count(Notification, where);
|
// return this.em.count(Notification, where);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// readAllNotifsAsUser(userId: string, : string): Promise < number > {
|
// async countUnseenByRestaurant(adminId: string, : string): Promise < number > {
|
||||||
// const where: FilterQuery<Notification> = {
|
// const where: FilterQuery<Notification> = {
|
||||||
// user: { id: userId },
|
// admin: { id: adminId },
|
||||||
// restaurant: { id: },
|
// restaurant: { id: },
|
||||||
// seenAt: null,
|
// seenAt: null,
|
||||||
// };
|
// };
|
||||||
// return this.em.nativeUpdate(Notification, where, { seenAt: new Date() });
|
// return this.em.count(Notification, where);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// readAllNotifsAsAdmin(adminId: string, : string): Promise < number > {
|
// readAllNotifsAsUser(userId: string, : string): Promise < number > {
|
||||||
// const where: FilterQuery<Notification> = {
|
// const where: FilterQuery<Notification> = {
|
||||||
// admin: { id: adminId },
|
// user: { id: userId },
|
||||||
// restaurant: { id: },
|
// restaurant: { id: },
|
||||||
// seenAt: null,
|
// seenAt: null,
|
||||||
// };
|
// };
|
||||||
// return this.em.nativeUpdate(Notification, where, { seenAt: new Date() });
|
// return this.em.nativeUpdate(Notification, where, { seenAt: new Date() });
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// async getSmsCountByRestaurant(
|
// readAllNotifsAsAdmin(adminId: string, : string): Promise < number > {
|
||||||
// page: number = 1,
|
// const where: FilterQuery<Notification> = {
|
||||||
// limit: number = 10,
|
// admin: { id: adminId },
|
||||||
// ): Promise < PaginatedResult < { : string; restaurantName: string; smsCount: number } >> {
|
// restaurant: { id: },
|
||||||
// return this.smsLogRepository.getSmsCountByRestaurant(page, limit);
|
// seenAt: null,
|
||||||
// }
|
// };
|
||||||
|
// return this.em.nativeUpdate(Notification, where, { seenAt: new Date() });
|
||||||
|
// }
|
||||||
|
|
||||||
// async getSmsCountBy(: string): Promise < number > {
|
// async getSmsCountByRestaurant(
|
||||||
// return this.smsLogRepository.getSmsCountBy();
|
// 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();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { OnEvent } from '@nestjs/event-emitter';
|
|||||||
import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
|
import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
|
||||||
import { OrderCreatedEvent, OrderStatusChangedEvent } from '../events/order.events';
|
import { OrderCreatedEvent, OrderStatusChangedEvent } from '../events/order.events';
|
||||||
import { Permission } from 'src/common/enums/permission.enum';
|
import { Permission } from 'src/common/enums/permission.enum';
|
||||||
import { NotifTitleEnum } from 'src/modules/notification/interfaces/notification.interface';
|
import { NotifEvent } from 'src/modules/notification/interfaces/notification.interface';
|
||||||
import { NotificationService } from 'src/modules/notification/services/notification.service';
|
import { NotificationService } from 'src/modules/notification/services/notification.service';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { OrderRepository } from '../repositories/order.repository';
|
import { OrderRepository } from '../repositories/order.repository';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { OnEvent } from '@nestjs/event-emitter';
|
|||||||
import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
|
import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
|
||||||
import { onlinePaymentSucceedEvent, paymentSucceedEvent } from '../events/payment.events';
|
import { onlinePaymentSucceedEvent, paymentSucceedEvent } from '../events/payment.events';
|
||||||
import { Permission } from 'src/common/enums/permission.enum';
|
import { Permission } from 'src/common/enums/permission.enum';
|
||||||
import { NotifTitleEnum } from 'src/modules/notification/interfaces/notification.interface';
|
import { NotifEvent } from 'src/modules/notification/interfaces/notification.interface';
|
||||||
import { NotificationService } from 'src/modules/notification/services/notification.service';
|
import { NotificationService } from 'src/modules/notification/services/notification.service';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { OrderService } from 'src/modules/order/providers/order.service';
|
import { OrderService } from 'src/modules/order/providers/order.service';
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
import { NotifChannelEnum, NotifTitleEnum } from '../../modules/notification/interfaces/notification.interface';
|
import { NotifChannelEnum, NotifEvent } from '../../modules/notification/interfaces/notification.interface';
|
||||||
|
|
||||||
export interface NotificationPreferenceData {
|
export interface NotificationPreferenceData {
|
||||||
title: NotifTitleEnum;
|
title: NotifEvent;
|
||||||
channels: NotifChannelEnum[];
|
channels: NotifChannelEnum[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const notificationPreferencesData: NotificationPreferenceData[] = [
|
export const notificationPreferencesData: NotificationPreferenceData[] = [
|
||||||
{
|
// {
|
||||||
title: NotifTitleEnum.PAGER_CREATED,
|
// title: NotifEvent.PAGER_CREATED,
|
||||||
channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
|
// channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
title: NotifTitleEnum.ORDER_CREATED,
|
// title: NotifEvent.ORDER_CREATED,
|
||||||
channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
|
// channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
title: NotifTitleEnum.PAYMENT_SUCCESS,
|
// title: NotifEvent.PAYMENT_SUCCESS,
|
||||||
channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
|
// channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
title: NotifTitleEnum.REVIEW_CREATED,
|
// title: NotifEvent.REVIEW_CREATED,
|
||||||
channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
|
// channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
title: NotifTitleEnum.ORDER_STATUS_CHANGED,
|
// title: NotifEvent.ORDER_STATUS_CHANGED,
|
||||||
channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
|
// channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
|
||||||
},
|
// },
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user