diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx index 7fa3e980e..cddc0c9d3 100644 --- a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx @@ -1,95 +1,183 @@ require('./notificationLookup.less'); const React = require('react'); -const { useState } = require('react'); +const { useState, useRef } = require('react'); const cx = require('classnames'); - const request = require('superagent'); const Moment = require('moment'); +const NotificationDetail = ({ notification, onDelete }) => ( +
+
+
Key
+
{notification.dismissKey}
+ +
Title
+
{notification.title || 'No Title'}
+ +
Text
+
{notification.text || 'No Text'}
+ +
Created
+
{Moment(notification.createdAt).format('LLLL')}
+ +
Start
+
{Moment(notification.startAt).format('LLLL') || 'No Start Time'}
+ +
Stop
+
{Moment(notification.stopAt).format('LLLL') || 'No End Time'}
+
+ +
+); + const NotificationLookup = () => { - const [query, setQuery] = useState(''); const [foundNotification, setFoundNotification] = useState(null); const [searching, setSearching] = useState(false); const [error, setError] = useState(null); + const [notifications, setNotifications] = useState([]); + + const lookupRef = useRef(null); - const handleChange = (e) => { - setQuery(e.target.value); - }; + const lookup = async () => { + const query = lookupRef.current.value; + + if (!query.trim()) { + setError('Please enter a valid dismiss key.'); + return; + } - const lookup = () => { setSearching(true); setError(null); - request.get(`/admin/notification/lookup/${query}`) - .then((res) => setFoundNotification(res.body)) - .catch((err) => setError(err)) - .finally(() => setSearching(false)); + try { + const res = await request.get(`/admin/notification/lookup/${query}`); + if (res.body) { + setFoundNotification(res.body); + } else { + setFoundNotification(null); + setError('No notification found.'); + } + } catch { + setError('Error fetching notification.'); + } finally { + setSearching(false); + } }; - const deleteNotification = () => { - if (!foundNotification) return; + const lookupAll = async () => { + setSearching(true); + setError(null); - const confirmed = window.confirm(`Really delete notification ${foundNotification.dismissKey} : ${foundNotification.title}?`); + try { + const res = await request.get('/admin/notification/all'); + setNotifications(res.body || []); + } catch { + setError('Error fetching all notifications.'); + } finally { + setSearching(false); + } + }; + + const deleteNotification = async (dismissKey) => { + if (!dismissKey) return; + + const confirmed = window.confirm( + `Really delete notification ${dismissKey}?` + ); if (!confirmed) { console.log('CANCELLED'); return; } console.log('CONFIRMED'); - // Perform delete operation here + try { + await request.delete(`/admin/notification/delete/${dismissKey}`); + // Only reset the foundNotification if it matches the one being deleted + if (foundNotification && foundNotification.dismissKey === dismissKey) { + setFoundNotification(null); + } + // Optionally refresh the list of all notifications + lookupAll(); + } catch { + setError('Error deleting notification.'); + } }; const renderFoundNotification = () => { - if (!foundNotification) return null; + if (error) { + return
{error}
; + } + + if (!foundNotification) { + return
No notification found.
; + } return ( -
-
-
Key
-
{foundNotification.dismissKey}
+
+ +
+ ); + }; -
Title
-
{foundNotification.title || 'No Title'}
+ const renderNotificationsList = () => { + if (error) { + return
{error}
; + } -
Text
-
{foundNotification.text || 'No Text'}
+ if (notifications.length === 0) { + return
No notifications available.
; + } -
Created
-
{Moment(foundNotification.createdAt).toLocaleString()}
- -
Start
-
{Moment(foundNotification.startAt).toLocaleString() || 'No Start Time'}
- -
Stop
-
{Moment(foundNotification.stopAt).toLocaleString() || 'No End Time'}
-
- + return ( +
+ {notifications.map((notification) => ( +
+ {notification.title || 'No Title'} + +
+ ))}
); }; return ( -
-

Lookup

- - +
+
+

Lookup

+ { + if (e.key === 'Enter') { + lookup(); + } + }} + placeholder="dismiss key" + /> + - {error &&
{error.toString()}
} + {renderFoundNotification()} +
+
+

All Notifications

+ - {foundNotification - ? renderFoundNotification() - :
No notification found.
- } + {renderNotificationsList()} +
); }; diff --git a/server/admin.api.js b/server/admin.api.js index 21dcc2b58..58958da3d 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -139,19 +139,51 @@ router.get('/admin/stats', mw.adminOnly, async (req, res)=>{ } }); +// ####################### NOTIFICATIONS + /* Searches for notification with matching key */ -router.get('/admin/notification/lookup/:id', mw.adminOnly, (req, res, next)=>{ - NotificationModel.findOne({ dismissKey: req.params.id }) - .exec((err, notification)=>{ - return res.json(notification); - }); +router.get('/admin/notification/lookup/:id', mw.adminOnly, async (req, res, next) => { + try { + const notification = await NotificationModel.findOne({ dismissKey: req.params.id }).exec(); + if (!notification) { + return res.status(404).json({ message: 'Notification not found' }); + } + return res.json(notification); + } catch (err) { + return next(err); + } }); -/* Add new notification */ -router.post('/admin/notification/add', mw.adminOnly, async (req, res, next)=>{ - console.log(req.body); - const notification = await NotificationModel.addNotification(req.body); - return res.json(notification); +// get all notifications +router.get('/admin/notification/all', mw.adminOnly, async (req, res, next) => { + try { + const notifications = await NotificationModel.getAll(); + return res.json(notifications); + } catch (err) { + return next(err); + } +}); + +router.post('/admin/notification/add', mw.adminOnly, async (req, res, next) => { + console.table(req.body); + try { + // Assuming you have some validation logic here + const notification = await NotificationModel.addNotification(req.body); + return res.json(notification); + } catch (error) { + console.error('Error adding notification:', error); + return res.status(500).json({ error: 'An error occurred while adding the notification' }); + } +}); + +router.delete('/admin/notification/delete/:id', mw.adminOnly, async (req, res, next) => { + try { + const notification = await NotificationModel.deleteNotification(req.params.id); + return res.json(notification); + } catch (error) { + console.error('Error deleting notification: { key: ', req.params.id , ' error: ', error ,' }'); + return res.status(500).json({ error: 'An error occurred while deleting the notification' }); + } }); router.get('/admin', mw.adminOnly, (req, res)=>{ diff --git a/server/notifications.model.js b/server/notifications.model.js index 6cf4b9c8d..a834b1a65 100644 --- a/server/notifications.model.js +++ b/server/notifications.model.js @@ -1,80 +1,104 @@ const mongoose = require('mongoose'); const _ = require('lodash'); -const NotificationSchema = mongoose.Schema({ - dismissKey : { type: String, unique: true, required: true }, - title : { type: String, default: '' }, - text : { type: String, default: '' }, - - createdAt : { type: Date, default: Date.now }, - startAt : { type: Date, default: Date.now }, - stopAt : { type: Date, default: Date.now }, +// Define the schema for the notification +const NotificationSchema = new mongoose.Schema({ + dismissKey: { type: String, unique: true, required: true }, + title: { type: String, default: '' }, + text: { type: String, default: '' }, + createdAt: { type: Date, default: Date.now }, + startAt: { type: Date, default: Date.now }, + stopAt: { type: Date, default: Date.now }, }, { versionKey: false }); -NotificationSchema.statics.get = function(query, fields=null){ - return new Promise((resolve, reject)=>{ - Notification.find(query, fields, null, (err, notifications)=>{ - if(err || !notifications.length) return reject('Can not find notification'); - return resolve(notifications[0]); - }); - }); +// Static method to get a notification based on a query +NotificationSchema.statics.get = async function(query, fields = null) { + try { + const notifications = await this.find(query, fields).exec(); + if (!notifications.length) throw new Error('Cannot find notification'); + return notifications[0]; + } catch (err) { + throw new Error(err.message || 'Error finding notification'); + } }; -NotificationSchema.statics.getByKey = function(key, fields=null){ - return new Promise((resolve, reject)=>{ - const query = { dissmissKey: key }; - Notification.findOne(query, fields).lean().exec((err, notifications)=>{ //lean() converts results to JSObjects - if(err) return reject('Can not find notification'); - return resolve(notifications); - }); - }); +// Static method to get a notification by its dismiss key +NotificationSchema.statics.getByKey = async function(key, fields = null) { + try { + const notification = await this.findOne({ dismissKey: key }, fields).lean().exec(); + if (!notification) throw new Error('Cannot find notification'); + return notification; + } catch (err) { + throw new Error(err.message || 'Error finding notification'); + } }; -NotificationSchema.statics.addNotification = async function(data){ - // console.log('add notification'); - if(!data.dismissKey) return 'Dismiss key is required!'; - const defaults = { - title : '', - text : '', - startAt : new Date, - stopAt : new Date - }; - _.defaults(data, defaults); - const newNotification = new Notification(data); - const savedNotification = await newNotification.save() - .catch((err)=>{ - return { err }; - }); - - return savedNotification; +// Static method to add a new notification +NotificationSchema.statics.addNotification = async function(data) { + if (!data.dismissKey) return 'Dismiss key is required!'; + const defaults = { + title: '', + text: '', + startAt: new Date(), + stopAt: new Date() + }; + _.defaults(data, defaults); + const newNotification = new this(data); + try { + const savedNotification = await newNotification.save(); + return savedNotification; + } catch (err) { + throw new Error(err.message || 'Error saving notification'); + } }; -NotificationSchema.statics.updateNotification = async function(dismissKey, title=null, text=null, startAt=null, stopAt=null){ - if(!dismissKey) return 'No key!'; - if(!title && !text && !startAt && !stopAt) return 'No data!'; - const filter = { - dismissKey : dismissKey - }; - const data = { - title : title, - text : text, - startAt : startAt, - stopAt : stopAt - }; - for (const [key, value] of Object.entries(data)){ - if(value === null) delete data[key]; - } +// Static method to update a notification +NotificationSchema.statics.updateNotification = async function(dismissKey, title = null, text = null, startAt = null, stopAt = null) { + if (!dismissKey) return 'No key!'; + if (!title && !text && !startAt && !stopAt) return 'No data!'; + const filter = { dismissKey: dismissKey }; + const data = { title, text, startAt, stopAt }; - await Notification.findOneAndUpdate(filter, data, { new: true }) - .exec((err, notifications)=>{ - if(err) return reject('Can not find notification'); - return resolve(notifications); - }); + // Remove null values from the data object + for (const [key, value] of Object.entries(data)) { + if (value === null) delete data[key]; + } + + try { + const updatedNotification = await this.findOneAndUpdate(filter, data, { new: true }).exec(); + if (!updatedNotification) throw new Error('Cannot find notification'); + return updatedNotification; + } catch (err) { + throw new Error(err.message || 'Error updating notification'); + } }; +// Static method to delete a notification +NotificationSchema.statics.deleteNotification = async function(dismissKey) { + if (!dismissKey) return 'No key provided!'; + try { + const deletedNotification = await this.findOneAndDelete({ dismissKey }).exec(); + if (!deletedNotification) throw new Error('Notification not found'); + return deletedNotification; + } catch (err) { + throw new Error(err.message || 'Error deleting notification'); + } +}; + +// Static method to get all notifications +NotificationSchema.statics.getAll = async function() { + try { + const notifications = await this.find().exec(); + return notifications; + } catch (err) { + throw new Error(err.message || 'Error retrieving notifications'); + } +}; + +// Create and export the model const Notification = mongoose.model('Notification', NotificationSchema); module.exports = { - schema : NotificationSchema, - model : Notification, + schema: NotificationSchema, + model: Notification, };