diff --git a/src/controllers/notificationsController.ts b/src/controllers/notificationsController.ts index 515adfbc..5146c930 100644 --- a/src/controllers/notificationsController.ts +++ b/src/controllers/notificationsController.ts @@ -2,6 +2,8 @@ import { Request, Response } from 'express'; import Notification from '../database/models/notification'; import { sendInternalErrorResponse } from '../validations'; import logger from '../logs/config'; +import sequelize from '../database/models'; +import { Transaction } from 'sequelize'; export const getNotifications = async (req: Request, res: Response): Promise => { try { @@ -37,9 +39,90 @@ export const markNotificationAsRead = async (req: Request, res: Response): Promi } notification.isRead = isRead; await notification.save(); - res.status(200).json({ ok: true, message: 'Notification were updated successfully' }); + res.status(200).json({ ok: true, message: 'Notification were updated successfully', data: notification }); } catch (error) { logger.error(error); sendInternalErrorResponse(res, error); } }; +export const markAllNotificationsAsRead = async (req: Request, res: Response) => { + const { userId } = req.params; + const { isRead } = req.body; + try { + const allNotifications = await Notification.findAll({ where: { userId } }); + await Promise.all( + allNotifications.map(async notification => { + notification.isRead = isRead; + return notification.save(); + }) + ); + res.status(200).json({ ok: true, message: 'all notifications marked successfully' }); + } catch (error) { + logger.error(error); + sendInternalErrorResponse(res, error); + } +}; +export const getSingleNotification = async (req: Request, res: Response): Promise => { + const { id } = req.params; + try { + const oneNotification = await Notification.findByPk(id); + if (!oneNotification) { + res.status(404).json({ + ok: false, + message: "Notification can't be found", + }); + return; + } + res.status(200).json({ + ok: true, + message: 'Notifications was found successfully', + data: oneNotification, + }); + } catch (error) { + logger.error(error); + sendInternalErrorResponse(res, error); + } +}; + +export const deleteSingleNotification = async (req: Request, res: Response): Promise => { + const { id } = req.params; + try { + await Notification.destroy({ + where: { + id, + }, + }); + } catch (error) { + logger.error(error); + sendInternalErrorResponse(res, error); + } +}; +export const deleteAllNotifications = async (req: Request, res: Response): Promise => { + const { userId } = req.params; + + try { + const notifications = await Notification.findAll({ + where: { userId: userId }, + attributes: ['id'], + }); + + if (notifications.length === 0) { + res.status(404).json({ + ok: false, + message: 'No notifications found for this user', + }); + return; + } + const deletedCount = await Notification.destroy({ + where: { id: notifications.map(n => n.id) }, + }); + + res.status(200).json({ + ok: true, + message: `Successfully deleted ${deletedCount} notification(s)`, + }); + } catch (error) { + logger.error('Error deleting notifications:', error); + sendInternalErrorResponse(res, error); + } +}; diff --git a/src/controllers/productsController.ts b/src/controllers/productsController.ts index 4a387553..68f8e148 100644 --- a/src/controllers/productsController.ts +++ b/src/controllers/productsController.ts @@ -361,7 +361,33 @@ Product.afterCreate(async product => { } sendEmail('added_product_notification', { email: user.email, name: user.firstName }); }); - +Product.afterUpdate(async product => { + const notification = await Notification.create({ + message: `Product called: ${product.name} was updated successfully`, + isRead: false, + userId: product.sellerId, + }); + const user = await User.findOne({ + where: { id: product.sellerId }, + attributes: ['email', 'firstName', 'lastName'], + }); + if (!user) { + return Promise.reject(new Error("User cannot be found! So the email won't be send successfully")); + } + sendEmail('updated_product_notification', { email: user.email, name: user.firstName }); +}); +Product.afterDestroy(async product => { + const notification = await Notification.create({ + message: 'Product was deleted successfully', + isRead: false, + userId: product.sellerId, + }); + const user = await User.findOne({ where: { id: product.sellerId }, attributes: ['email', 'firstName', 'lastName'] }); + if (!user) { + return Promise.reject(new Error("User can't be found, so the email won't be sent successfully")); + } + sendEmail('deleted_product_notification', { email: user.email, name: user.firstName }); +}); // Review a product (feedback + rating) export const provideReviewToProduct = async (req: Request, res: Response) => { try { diff --git a/src/docs/notifications.yaml b/src/docs/notifications.yaml new file mode 100644 index 00000000..9b9d4689 --- /dev/null +++ b/src/docs/notifications.yaml @@ -0,0 +1,127 @@ +tags: + - name: Notifications + description: Managing notifications for product's lifecycle changes + +paths: + /api/notifications/{userId}: + get: + description: Get all notifications based on the the user's ID + summmary: Get all notifications + security: + - bearerAuth: [] + tags: + - Notifications + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The id of the user + responses: + '200': + description: Notifications fetched successfully + content: + application/json: + schema: + type: object + properties: + id: + type: string + isRead: + type: boolean + message: + type: string + userId: + type: string + '404': + description: No notifications were found + content: + application/json: + schema: + type: object + properties: + ok: + type: boolean + message: + type: string + delete: + description: User should be able to delete all notifications at once + summary: Delete all notifications at once + tags: + - Notifications + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The user ID + responses: + '200': + description: All notifications were successfully deleted + content: + application/json: + schema: + type: object + properties: + ok: + type: boolean + message: + type: string + '404': + description: Notifications for this user wcan't be found + content: + application/json: + schema: + type: object + properties: + ok: + type: boolean + message: + type: string + patch: + description: User should be able to updates all notifications at once + summary: Updates all notifications at once + tags: + - Notifications + parameters: + - in: path + name: userId + required: true + description: Updates all notifications + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + isRead: + type: boolean + example: true + /api/notifications/{id}: + patch: + description: User should be able to update notification, provided the notification ID + summary: Update the notification status + tags: + - Notifications + parameters: + - in: path + name: id + required: true + schema: + type: string + description: The notification id + requestBody: + description: Update the notification + required: true + content: + application/json: + schema: + type: object + properties: + isRead: + type: boolean diff --git a/src/helpers/send-email.ts b/src/helpers/send-email.ts index eed9e95f..c1358943 100644 --- a/src/helpers/send-email.ts +++ b/src/helpers/send-email.ts @@ -232,7 +232,7 @@ export const sendEmail = async (type: string, data: IData) => { button: { color: '#22BC66', text: 'View on the platform', - link: 'https://e-commerce-mavericks.com/login', + link: process.env.URL_HOST as string, }, }, outro: 'Thank you for working with us. If you need any help, please free to contact us!', @@ -241,6 +241,44 @@ export const sendEmail = async (type: string, data: IData) => { mailOptions.subject = 'Product added successfully'; mailOptions.html = mailGenerator.generate(email); break; + case 'updated_product_notification': + email = { + body: { + name: data.name, + intro: `Your product has been updated successfully!`, + action: { + instructions: 'To view your product on platform, Click here:', + button: { + color: '#22BC66', + text: 'View on the platform', + link: process.env.URL_HOST as string, + }, + }, + outro: 'Thank you for working with us. If you need any help, please free to contact us!', + }, + }; + mailOptions.subject = 'Product updated successfully'; + mailOptions.html = mailGenerator.generate(email); + break; + case 'deleted_product_notification': + email = { + body: { + name: data.name, + intro: `Your product has been deleted successfully!`, + action: { + instructions: 'To view your other products on platform, Click here:', + button: { + color: '#22BC66', + text: 'View on the platform', + link: process.env.URL_HOST as string, + }, + }, + outro: 'Thank you for working with us. If you need any help, please free to contact us!', + }, + }; + mailOptions.subject = 'Product deleted successfully'; + mailOptions.html = mailGenerator.generate(email); + break; case 'password_prompt': email = { diff --git a/src/routes/notificationRoutes.ts b/src/routes/notificationRoutes.ts index 48bf9ef7..55da2fdf 100644 --- a/src/routes/notificationRoutes.ts +++ b/src/routes/notificationRoutes.ts @@ -1,9 +1,20 @@ import express from 'express'; -import { getNotifications, markNotificationAsRead } from '../controllers/notificationsController'; +import { + deleteAllNotifications, + deleteSingleNotification, + getNotifications, + getSingleNotification, + markAllNotificationsAsRead, + markNotificationAsRead, +} from '../controllers/notificationsController'; const route = express.Router(); route.get('/:userId', getNotifications); route.patch('/:id', markNotificationAsRead); +route.patch('/update/:userId', markAllNotificationsAsRead); +route.get('/:id', getSingleNotification); +route.delete('/:id', deleteSingleNotification); +route.delete('/delete/:userId', deleteAllNotifications); export default route;