From 4a449c7895b0eca1fc71bd01201f9693ccf62818 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 4 Jan 2023 22:08:22 +1300 Subject: [PATCH 01/63] Update Buffer method --- server/admin.api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/admin.api.js b/server/admin.api.js index 8570ea1bc..b9b2afbd7 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -16,7 +16,7 @@ const mw = { .status(401) .send('Authorization Required'); } - const [username, password] = new Buffer(req.get('authorization').split(' ').pop(), 'base64') + const [username, password] = Buffer.from(req.get('authorization').split(' ').pop(), 'base64') .toString('ascii') .split(':'); if(process.env.ADMIN_USER === username && process.env.ADMIN_PASS === password){ From d7ee004127bcff65d94556746cbbb8ca96162027 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 4 Jan 2023 22:08:44 +1300 Subject: [PATCH 02/63] Update Admin pages --- client/admin/admin.jsx | 14 ++--------- .../brewCleanup/brewCleanup.jsx | 0 .../brewCleanup/brewCleanup.less | 0 .../brewCompress/brewCompress.jsx | 0 .../brewCompress/brewCompress.less | 0 .../{ => brewUtils}/brewLookup/brewLookup.jsx | 0 .../brewLookup/brewLookup.less | 0 client/admin/brewUtils/brewUtils.jsx | 24 +++++++++++++++++++ client/admin/{ => brewUtils}/stats/stats.jsx | 0 client/admin/{ => brewUtils}/stats/stats.less | 0 10 files changed, 26 insertions(+), 12 deletions(-) rename client/admin/{ => brewUtils}/brewCleanup/brewCleanup.jsx (100%) rename client/admin/{ => brewUtils}/brewCleanup/brewCleanup.less (100%) rename client/admin/{ => brewUtils}/brewCompress/brewCompress.jsx (100%) rename client/admin/{ => brewUtils}/brewCompress/brewCompress.less (100%) rename client/admin/{ => brewUtils}/brewLookup/brewLookup.jsx (100%) rename client/admin/{ => brewUtils}/brewLookup/brewLookup.less (100%) create mode 100644 client/admin/brewUtils/brewUtils.jsx rename client/admin/{ => brewUtils}/stats/stats.jsx (100%) rename client/admin/{ => brewUtils}/stats/stats.less (100%) diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index 92e0b2aee..a84af6f44 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -2,11 +2,7 @@ require('./admin.less'); const React = require('react'); const createClass = require('create-react-class'); - -const BrewCleanup = require('./brewCleanup/brewCleanup.jsx'); -const BrewLookup = require('./brewLookup/brewLookup.jsx'); -const BrewCompress = require ('./brewCompress/brewCompress.jsx'); -const Stats = require('./stats/stats.jsx'); +const BrewUtils = require('./brewUtils/brewUtils.jsx'); const Admin = createClass({ getDefaultProps : function() { @@ -23,13 +19,7 @@ const Admin = createClass({
- -
- -
- -
- +
; } diff --git a/client/admin/brewCleanup/brewCleanup.jsx b/client/admin/brewUtils/brewCleanup/brewCleanup.jsx similarity index 100% rename from client/admin/brewCleanup/brewCleanup.jsx rename to client/admin/brewUtils/brewCleanup/brewCleanup.jsx diff --git a/client/admin/brewCleanup/brewCleanup.less b/client/admin/brewUtils/brewCleanup/brewCleanup.less similarity index 100% rename from client/admin/brewCleanup/brewCleanup.less rename to client/admin/brewUtils/brewCleanup/brewCleanup.less diff --git a/client/admin/brewCompress/brewCompress.jsx b/client/admin/brewUtils/brewCompress/brewCompress.jsx similarity index 100% rename from client/admin/brewCompress/brewCompress.jsx rename to client/admin/brewUtils/brewCompress/brewCompress.jsx diff --git a/client/admin/brewCompress/brewCompress.less b/client/admin/brewUtils/brewCompress/brewCompress.less similarity index 100% rename from client/admin/brewCompress/brewCompress.less rename to client/admin/brewUtils/brewCompress/brewCompress.less diff --git a/client/admin/brewLookup/brewLookup.jsx b/client/admin/brewUtils/brewLookup/brewLookup.jsx similarity index 100% rename from client/admin/brewLookup/brewLookup.jsx rename to client/admin/brewUtils/brewLookup/brewLookup.jsx diff --git a/client/admin/brewLookup/brewLookup.less b/client/admin/brewUtils/brewLookup/brewLookup.less similarity index 100% rename from client/admin/brewLookup/brewLookup.less rename to client/admin/brewUtils/brewLookup/brewLookup.less diff --git a/client/admin/brewUtils/brewUtils.jsx b/client/admin/brewUtils/brewUtils.jsx new file mode 100644 index 000000000..de8c29895 --- /dev/null +++ b/client/admin/brewUtils/brewUtils.jsx @@ -0,0 +1,24 @@ +const React = require('react'); +const createClass = require('create-react-class'); + + +const BrewCleanup = require('./brewCleanup/brewCleanup.jsx'); +const BrewLookup = require('./brewLookup/brewLookup.jsx'); +const BrewCompress = require ('./brewCompress/brewCompress.jsx'); +const Stats = require('./stats/stats.jsx'); + +const BrewUtils = createClass({ + render : function(){ + return <> + +
+ +
+ +
+ + ; + } +}); + +module.exports = BrewUtils; diff --git a/client/admin/stats/stats.jsx b/client/admin/brewUtils/stats/stats.jsx similarity index 100% rename from client/admin/stats/stats.jsx rename to client/admin/brewUtils/stats/stats.jsx diff --git a/client/admin/stats/stats.less b/client/admin/brewUtils/stats/stats.less similarity index 100% rename from client/admin/stats/stats.less rename to client/admin/brewUtils/stats/stats.less From f66664a3e2b6d065dd5107c73e1282d9ae99cf5b Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 4 Jan 2023 22:49:38 +1300 Subject: [PATCH 03/63] Tabify Admin page, add Notification tab --- client/admin/admin.jsx | 22 ++++++++++++++++++- .../notificationUtils/notificationUtils.jsx | 12 ++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 client/admin/notificationUtils/notificationUtils.jsx diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index a84af6f44..f2ee241f2 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -3,12 +3,28 @@ const React = require('react'); const createClass = require('create-react-class'); const BrewUtils = require('./brewUtils/brewUtils.jsx'); +const NotificationUtils = require('./notificationUtils/notificationUtils.jsx'); + +const tabGroups = ['brew', 'notifications']; const Admin = createClass({ getDefaultProps : function() { return {}; }, + getInitialState : function(){ + return ({ + currentTab : 'brew' + }); + }, + + handleClick : function(newTab){ + if(this.state.currentTab === newTab) return; + this.setState({ + currentTab : newTab + }); + }, + render : function(){ return
@@ -19,7 +35,11 @@ const Admin = createClass({
- +
+ {tabGroups.map((tab)=>{ return ; })} +
+ {this.state.currentTab==='brew' && } + {this.state.currentTab==='notifications' && }
; } diff --git a/client/admin/notificationUtils/notificationUtils.jsx b/client/admin/notificationUtils/notificationUtils.jsx new file mode 100644 index 000000000..9c7b4cbe3 --- /dev/null +++ b/client/admin/notificationUtils/notificationUtils.jsx @@ -0,0 +1,12 @@ +const React = require('react'); +const createClass = require('create-react-class'); + +const NotificationUtils = createClass({ + render : function(){ + return <> +
Notifications
+ ; + } +}); + +module.exports = NotificationUtils; From 620cb95ae89f6e3f961abc0e69ccdc092dff6a72 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 4 Jan 2023 22:50:24 +1300 Subject: [PATCH 04/63] Initial pass at Notification Mongoose model --- server/notifications.model.js | 72 +++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 server/notifications.model.js diff --git a/server/notifications.model.js b/server/notifications.model.js new file mode 100644 index 000000000..12fecb3fa --- /dev/null +++ b/server/notifications.model.js @@ -0,0 +1,72 @@ +const mongoose = require('mongoose'); +const { nanoid } = require('nanoid'); +const _ = require('lodash'); + +const NotificationSchema = mongoose.Schema({ + dissmissKey : { type: String, default: ()=>{return nanoid(12);}, index: { unique: 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]); + }); + }); +}; + +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); + }); + }); +}; + +NotificationSchema.statics.addNotification = async function(title, text, startAt=new Date, stopAt=new Date){ + const data = { + title : title, + text : text, + startAt : startAt, + stopAt : stopAt + }; + const newNotification = new Notification(data); + await newNotification.save(); + + return newNotification; +}; + +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 data = { + title : title, + text : text, + startAt : startAt, + stopAt : stopAt + }; + for (const [key, value] of Object.entries(data)){ + if(value === null) delete data[key]; + } + + await Notification.updateOne(data) + .exec((err, notifications)=>{ + if(err) return reject('Can not find notification'); + return resolve(notifications); + }); +}; + +const Notification = mongoose.model('Notification', NotificationSchema); + +module.exports = { + schema : NotificationSchema, + model : Notification, +}; From b8fd8a7a868a6674f971eb187578bc19fe7cd8b9 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 4 Jan 2023 22:52:37 +1300 Subject: [PATCH 05/63] Add Notification lookup to Admin API --- server/admin.api.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/server/admin.api.js b/server/admin.api.js index b9b2afbd7..7190f7076 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -1,4 +1,5 @@ const HomebrewModel = require('./homebrew.model.js').model; +const NotificationModel = require('./notifications.model.js').model; const router = require('express').Router(); const Moment = require('moment'); //const render = require('vitreum/steps/render'); @@ -99,6 +100,15 @@ router.get('/admin/stats', mw.adminOnly, (req, res)=>{ }); }); +/* Searches for matching edit or share id, also attempts to partial match */ +router.get('/admin/notification/lookup/:id', mw.adminOnly, (req, res, next)=>{ + NotificationModel.findOne({ $or : [ + { dismissKey: { '$regex': req.params.id } }, + ] }).exec((err, notification)=>{ + return res.json(notification); + }); +}); + router.get('/admin', mw.adminOnly, (req, res)=>{ templateFn('admin', { url : req.originalUrl From 35b4c354f22143f66ea15cc27f0a9f738ca3d008 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 4 Jan 2023 23:20:11 +1300 Subject: [PATCH 06/63] Add key prop to Admin tabs --- client/admin/admin.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index f2ee241f2..6ebfe6ca8 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -36,7 +36,7 @@ const Admin = createClass({
- {tabGroups.map((tab)=>{ return ; })} + {tabGroups.map((tab, idx)=>{ return ; })}
{this.state.currentTab==='brew' && } {this.state.currentTab==='notifications' && } From a499bb3a54e719f075beab4e950b0af3e489da0b Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Wed, 4 Jan 2023 23:32:35 +1300 Subject: [PATCH 07/63] Add basic Notification Lookup functionality --- .../notificationLookup/notificationLookup.jsx | 82 +++++++++++++++++++ .../notificationLookup.less | 30 +++++++ .../notificationUtils/notificationUtils.jsx | 4 +- 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 client/admin/notificationUtils/notificationLookup/notificationLookup.jsx create mode 100644 client/admin/notificationUtils/notificationLookup/notificationLookup.less diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx new file mode 100644 index 000000000..3ed0410ee --- /dev/null +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx @@ -0,0 +1,82 @@ +require('./notificationLookup.less'); +const React = require('react'); +const createClass = require('create-react-class'); +const cx = require('classnames'); + +const request = require('superagent'); +const Moment = require('moment'); + + +const NotificationLookup = createClass({ + getDefaultProps() { + return {}; + }, + getInitialState() { + return { + query : '', + foundNotification : null, + searching : false, + error : null + }; + }, + handleChange(e){ + this.setState({ query: e.target.value }); + }, + lookup(){ + this.setState({ searching: true, error: null }); + + request.get(`/admin/notification/lookup/${this.state.query}`) + .then((res)=>this.setState({ foundNotification: res.body })) + .catch((err)=>this.setState({ error: err })) + .finally(()=>this.setState({ searching: false })); + }, + + renderFoundNotification(){ + const notification = this.state.foundnotification; + return
+
+
Key
+
{notification.dismissKey}
+ +
Title
+
{notification.title || 'No Title'}
+ +
Text
+
{notification.text || 'No Text'}
+ +
Created
+
{Moment(notification.createdAt).fromNow()}
+ +
Start
+
{Moment(notification.startAt).fromNow() || 'No Start Time'}
+ +
Created
+
{Moment(notification.stopAt).fromNow() || 'No End Time'}
+
+
; + }, + + render(){ + return
+

Notification Lookup

+ + + + {this.state.error + &&
{this.state.error.toString()}
+ } + + {this.state.foundNotification + ? this.renderFoundNotification() + :
No notification found.
+ } +
; + } +}); + +module.exports = NotificationLookup; diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.less b/client/admin/notificationUtils/notificationLookup/notificationLookup.less new file mode 100644 index 000000000..720b4e044 --- /dev/null +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.less @@ -0,0 +1,30 @@ + +.notificationLookup{ + input{ + height : 33px; + margin-bottom : 20px; + padding : 0px 10px; + font-family : monospace; + } + button{ + vertical-align : middle; + height : 37px; + } + dl{ + @maxItemWidth : 132px; + dt{ + float : left; + clear : left; + width : @maxItemWidth; + text-align : right; + &::after { + content: " : "; + } + } + dd{ + height : 1em; + margin-left : @maxItemWidth + 6px; + padding : 0 0 0.5em 0; + } + } +} \ No newline at end of file diff --git a/client/admin/notificationUtils/notificationUtils.jsx b/client/admin/notificationUtils/notificationUtils.jsx index 9c7b4cbe3..f68462377 100644 --- a/client/admin/notificationUtils/notificationUtils.jsx +++ b/client/admin/notificationUtils/notificationUtils.jsx @@ -1,10 +1,12 @@ const React = require('react'); const createClass = require('create-react-class'); +const NotificationLookup = require('./notificationLookup/notificationLookup.jsx'); + const NotificationUtils = createClass({ render : function(){ return <> -
Notifications
+ ; } }); From 42125f404101d6bb080d5919081911beadbda9eb Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 5 Jan 2023 09:03:51 +1300 Subject: [PATCH 08/63] Update Notification schema --- server/notifications.model.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/notifications.model.js b/server/notifications.model.js index 12fecb3fa..d09b80ce6 100644 --- a/server/notifications.model.js +++ b/server/notifications.model.js @@ -1,9 +1,8 @@ const mongoose = require('mongoose'); -const { nanoid } = require('nanoid'); const _ = require('lodash'); const NotificationSchema = mongoose.Schema({ - dissmissKey : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } }, + dissmissKey : { type: String, index: { unique: true } }, title : { type: String, default: '' }, text : { type: String, default: '' }, From ec040cc2bb0bd059ad34fb22ac7acaf5d07322e9 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 5 Jan 2023 10:04:45 +1300 Subject: [PATCH 09/63] Add DisplayNames --- .../notificationLookup/notificationLookup.jsx | 1 + client/admin/notificationUtils/notificationUtils.jsx | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx index 3ed0410ee..317f2679c 100644 --- a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx @@ -8,6 +8,7 @@ const Moment = require('moment'); const NotificationLookup = createClass({ + displayName : 'NotificationLookup', getDefaultProps() { return {}; }, diff --git a/client/admin/notificationUtils/notificationUtils.jsx b/client/admin/notificationUtils/notificationUtils.jsx index f68462377..3e595265b 100644 --- a/client/admin/notificationUtils/notificationUtils.jsx +++ b/client/admin/notificationUtils/notificationUtils.jsx @@ -2,10 +2,15 @@ const React = require('react'); const createClass = require('create-react-class'); const NotificationLookup = require('./notificationLookup/notificationLookup.jsx'); +const NotificationAdd = require('./notificationAdd/notificationAdd.jsx'); const NotificationUtils = createClass({ + displayName : 'NotificationUtils', + render : function(){ return <> + +
; } From f5c7761c61b1fe981c3316920383ddc9b844abcf Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 5 Jan 2023 10:52:24 +1300 Subject: [PATCH 10/63] Add styling to active tab --- client/admin/admin.jsx | 3 +-- client/admin/admin.less | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index 6ebfe6ca8..f675ff37a 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -27,7 +27,6 @@ const Admin = createClass({ render : function(){ return
-
@@ -36,7 +35,7 @@ const Admin = createClass({
- {tabGroups.map((tab, idx)=>{ return ; })} + {tabGroups.map((tab, idx)=>{ return ; })}
{this.state.currentTab==='brew' && } {this.state.currentTab==='notifications' && } diff --git a/client/admin/admin.less b/client/admin/admin.less index a61335835..8d3d70763 100644 --- a/client/admin/admin.less +++ b/client/admin/admin.less @@ -40,5 +40,25 @@ body{ margin : 30px 0px; } - + .container{ + .tabs button{ + background-color: #eee; + margin-left: 3px; + margin-right: 3px; + border: 1px solid #444; + border-radius: 5px; + color: black; + &:hover{ + background-color: #444; + color: #eee; + } + &.active{ + border: 2px solid #444; + text-decoration: underline; + margin-left: 2px; + margin-right: 2px; + background-color: #ccc; + } + } + } } From 9e169aba919ab297e93c3549828debf433a698ee Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 5 Jan 2023 10:56:50 +1300 Subject: [PATCH 11/63] Tweak LookUp title --- .../notificationUtils/notificationLookup/notificationLookup.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx index 317f2679c..973212e85 100644 --- a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx @@ -59,7 +59,7 @@ const NotificationLookup = createClass({ render(){ return
-

Notification Lookup

+

Lookup

+ + {this.state.error + &&
{this.state.error.toString()}
+ } +
; + } +}); + +module.exports = NotificationAdd; diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.less b/client/admin/notificationUtils/notificationAdd/notificationAdd.less new file mode 100644 index 000000000..32cd61d25 --- /dev/null +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.less @@ -0,0 +1,20 @@ + +.notificationAdd{ + input{ + height : 33px; + margin-bottom : 20px; + padding : 0px 10px; + font-family : monospace; + } + button{ + vertical-align : middle; + height : 37px; + } + .fieldLabel{ + display: inline-block; + width: 10%; + } + .fieldInput{ + margin-bottom: 5px; + } +} \ No newline at end of file diff --git a/server/admin.api.js b/server/admin.api.js index 7190f7076..906bfdcae 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -100,13 +100,18 @@ router.get('/admin/stats', mw.adminOnly, (req, res)=>{ }); }); -/* Searches for matching edit or share id, also attempts to partial match */ +/* Searches for notification with matching key */ router.get('/admin/notification/lookup/:id', mw.adminOnly, (req, res, next)=>{ - NotificationModel.findOne({ $or : [ - { dismissKey: { '$regex': req.params.id } }, - ] }).exec((err, notification)=>{ - return res.json(notification); - }); + NotificationModel.findOne({ dismissKey: req.params.id }) + .exec((err, notification)=>{ + return res.json(notification); + }); +}); + +/* Add new notification */ +router.post('/admin/notification/add', mw.adminOnly, async (req, res, next)=>{ + const notification = await NotificationModel.addNotification(req.body); + return res.json(notification); }); router.get('/admin', mw.adminOnly, (req, res)=>{ diff --git a/server/notifications.model.js b/server/notifications.model.js index 0465822d5..721c4a89d 100644 --- a/server/notifications.model.js +++ b/server/notifications.model.js @@ -2,9 +2,9 @@ const mongoose = require('mongoose'); const _ = require('lodash'); const NotificationSchema = mongoose.Schema({ - dissmissKey : { type: String, index: { unique: true } }, - title : { type: String, default: '' }, - text : { type: String, default: '' }, + 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 }, @@ -30,19 +30,23 @@ NotificationSchema.statics.getByKey = function(key, fields=null){ }); }; -NotificationSchema.statics.addNotification = async function(dismissKey, text, title=null, startAt=new Date, stopAt=new Date){ - const data = { - dismissKey : dismissKey, - title : title, - text : text, - startAt : startAt, - stopAt : stopAt +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 }; + _.mergeWith(data, defaults, (item)=>{ if(!item) return undefined; }); const newNotification = new Notification(data); - await newNotification.save() - .catch((err)=>{return err;}); + const savedNotification = await newNotification.save() + .catch((err)=>{ + return { err: err }; + }); - return newNotification; + return savedNotification; }; NotificationSchema.statics.updateNotification = async function(dismissKey, title=null, text=null, startAt=null, stopAt=null){ From b00a962e77ec1456e5450559ec03b4136ccb6da9 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sun, 15 Jan 2023 20:44:41 +1300 Subject: [PATCH 14/63] Fix typo --- .../notificationUtils/notificationLookup/notificationLookup.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx index 973212e85..0ed9398c5 100644 --- a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx @@ -33,7 +33,7 @@ const NotificationLookup = createClass({ }, renderFoundNotification(){ - const notification = this.state.foundnotification; + const notification = this.state.foundNotification; return
Key
From 5e2171ceb12c71cc7f3d43075f649771f9451c17 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sun, 15 Jan 2023 22:21:06 +1300 Subject: [PATCH 15/63] Remove debugging console.log calls --- .../notificationUtils/notificationAdd/notificationAdd.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx index 78438644a..51fbecfb4 100644 --- a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx @@ -47,7 +47,6 @@ const NotificationAdd = createClass({ .then((response)=>{ return response.body; }); - console.log(notification); const update = { notificationResult : `Created notification: ${JSON.stringify(notification, null, 2)}` @@ -63,8 +62,6 @@ const NotificationAdd = createClass({ update.stopAt = ''; } - console.log(update); - this.setState(update); }, From ccd5cacb0c1d91a33f0cf218b4747c08742cdf4e Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sun, 15 Jan 2023 23:25:17 +1300 Subject: [PATCH 16/63] Stub notification deletion function --- .../notificationLookup/notificationLookup.jsx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx index 0ed9398c5..4f7c0b3d7 100644 --- a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx @@ -32,6 +32,16 @@ const NotificationLookup = createClass({ .finally(()=>this.setState({ searching: false })); }, + deleteNotification : function(){ + console.log('DELETE'); + if(!confirm(`Really delete notification ${this.state.foundNotification.dismissKey} : ${this.state.foundNotification.title}?`)) { + console.log('CANCELLED'); + return; + } + console.log('CONFIRMED'); + return; + }, + renderFoundNotification(){ const notification = this.state.foundNotification; return
@@ -46,14 +56,15 @@ const NotificationLookup = createClass({
{notification.text || 'No Text'}
Created
-
{Moment(notification.createdAt).fromNow()}
+
{Moment(notification.createdAt).toLocaleString()}
Start
-
{Moment(notification.startAt).fromNow() || 'No Start Time'}
+
{Moment(notification.startAt).toLocaleString() || 'No Start Time'}
-
Created
-
{Moment(notification.stopAt).fromNow() || 'No End Time'}
+
Stop
+
{Moment(notification.stopAt).toLocaleString() || 'No End Time'}
+
; }, From 24adbdc429f8127d125f779effaf183baec35771 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sun, 15 Jan 2023 23:28:54 +1300 Subject: [PATCH 17/63] Change model to use defaults rather than mergeWith --- server/notifications.model.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/notifications.model.js b/server/notifications.model.js index 721c4a89d..bd74ad5b7 100644 --- a/server/notifications.model.js +++ b/server/notifications.model.js @@ -39,7 +39,7 @@ NotificationSchema.statics.addNotification = async function(data){ startAt : new Date, stopAt : new Date }; - _.mergeWith(data, defaults, (item)=>{ if(!item) return undefined; }); + _.defaults(data, defaults); const newNotification = new Notification(data); const savedNotification = await newNotification.save() .catch((err)=>{ From da0372e44cb4094c594449858f0af980296ae563 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Mon, 23 Jan 2023 15:18:50 +1300 Subject: [PATCH 18/63] WIP commit --- .../notificationAdd/notificationAdd.jsx | 13 +++++++++---- server/admin.api.js | 1 + server/notifications.model.js | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx index 51fbecfb4..a1480c0a9 100644 --- a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx @@ -38,8 +38,8 @@ const NotificationAdd = createClass({ dismissKey : this.state.dismissKey, title : this.state.title, text : this.state.text, - startAt : this.state.startAt, - stopAt : this.state.stopAt + startAt : Date.parse(this.state.startAt), + stopAt : Date.parse(this.state.stopAt) }; const notification = await request.post('/admin/notification/add') @@ -52,7 +52,10 @@ const NotificationAdd = createClass({ notificationResult : `Created notification: ${JSON.stringify(notification, null, 2)}` }; if(notification.err) { - update.notificationResult = err; + update.notificationResult = JSON.stringify(notification.err); + if(notification.err.code == 11000) { + update.notificationResult = `Duplicate dismissKey error! ${this.state.dismissKey} already exists.`; + } }; if(!notification.err) { update.dismissKey = ''; @@ -62,6 +65,8 @@ const NotificationAdd = createClass({ update.stopAt = ''; } + console.log(update); + this.setState(update); }, @@ -74,7 +79,7 @@ const NotificationAdd = createClass({ this.handleChange(e, field)} placeholder={field} />
; })} - {this.state.notificationResult} +
{this.state.notificationResult}
{/* diff --git a/server/admin.api.js b/server/admin.api.js index 906bfdcae..53b08faaf 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -110,6 +110,7 @@ router.get('/admin/notification/lookup/:id', mw.adminOnly, (req, res, next)=>{ /* 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); }); diff --git a/server/notifications.model.js b/server/notifications.model.js index bd74ad5b7..6cf4b9c8d 100644 --- a/server/notifications.model.js +++ b/server/notifications.model.js @@ -43,7 +43,7 @@ NotificationSchema.statics.addNotification = async function(data){ const newNotification = new Notification(data); const savedNotification = await newNotification.save() .catch((err)=>{ - return { err: err }; + return { err }; }); return savedNotification; From 46882c4fb4bc4a71a07f91b9c0a1eff73d066b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Thu, 29 Aug 2024 00:00:55 +0200 Subject: [PATCH 19/63] add error logging on admin route --- server/admin.api.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/admin.api.js b/server/admin.api.js index 15bb95a77..21dcc2b58 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -159,7 +159,10 @@ router.get('/admin', mw.adminOnly, (req, res)=>{ url : req.originalUrl }) .then((page)=>res.send(page)) - .catch((err)=>res.sendStatus(500)); + .catch((err)=> { + console.log(err) + res.sendStatus(500) + }) }); module.exports = router; From 51d3d11bff00f5c9505fad19d18d0d6603d142ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Thu, 29 Aug 2024 00:01:02 +0200 Subject: [PATCH 20/63] "Refactor notification utils components to use React Hooks instead of createClass" --- .../notificationAdd/notificationAdd.jsx | 184 ++++++++++-------- .../notificationLookup/notificationLookup.jsx | 151 +++++++------- .../notificationUtils/notificationUtils.jsx | 21 +- 3 files changed, 184 insertions(+), 172 deletions(-) diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx index a1480c0a9..f41401377 100644 --- a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx @@ -1,101 +1,113 @@ require('./notificationAdd.less'); const React = require('react'); -const createClass = require('create-react-class'); -const cx = require('classnames'); - +const { useState } = require('react'); +const cx = require('classnames'); const request = require('superagent'); const fields = ['dismissKey', 'title', 'text', 'startAt', 'stopAt']; +const NotificationAdd = () => { + const [state, setState] = useState({ + query: '', + notificationResult: null, + searching: false, + error: null, + dismissKey: '', + title: '', + text: '', + startAt: '', + stopAt: '' + }); -const NotificationAdd = createClass({ - displayName : 'NotificationAdd', - getDefaultProps() { - return {}; - }, - getInitialState() { - return { - query : '', - notificationResult : null, - searching : false, - error : null, + const handleChange = (e, field) => { + const value = e.target.value; + setState(prevState => ({ + ...prevState, + [field]: value + })); + }; - dismissKey : '', - title : '', - text : '', - startAt : '', - stopAt : '' - }; - }, - handleChange(e, field){ - const data = {}; - data[field] = e.target.value; - this.setState(data); - }, - saveNotification : async function(){ - if(!this.state.dismissKey) return 'No notification key!'; - const data = { - dismissKey : this.state.dismissKey, - title : this.state.title, - text : this.state.text, - startAt : Date.parse(this.state.startAt), - stopAt : Date.parse(this.state.stopAt) - }; + const saveNotification = async () => { + if (!state.dismissKey) { + setState(prevState => ({ + ...prevState, + error: 'No notification key!' + })); + return; + } - const notification = await request.post('/admin/notification/add') - .send(data) - .then((response)=>{ - return response.body; - }); + const data = { + dismissKey: state.dismissKey, + title: state.title, + text: state.text, + startAt: Date.parse(state.startAt), + stopAt: Date.parse(state.stopAt) + }; - const update = { - notificationResult : `Created notification: ${JSON.stringify(notification, null, 2)}` - }; - if(notification.err) { - update.notificationResult = JSON.stringify(notification.err); - if(notification.err.code == 11000) { - update.notificationResult = `Duplicate dismissKey error! ${this.state.dismissKey} already exists.`; - } - }; - if(!notification.err) { - update.dismissKey = ''; - update.title = ''; - update.text = ''; - update.startAt = ''; - update.stopAt = ''; - } + try { + const response = await request.post('/admin/notification/add').send(data); + const notification = response.body; + let update = { + notificationResult: `Created notification: ${JSON.stringify(notification, null, 2)}` + }; - console.log(update); + if (notification.err) { + update.notificationResult = JSON.stringify(notification.err); + if (notification.err.code == 11000) { + update.notificationResult = `Duplicate dismissKey error! ${state.dismissKey} already exists.`; + } + } else { + update = { + ...update, + dismissKey: '', + title: '', + text: '', + startAt: '', + stopAt: '' + }; + } - this.setState(update); - }, + setState(prevState => ({ + ...prevState, + ...update, + searching: false + })); + } catch (err) { + setState(prevState => ({ + ...prevState, + error: err.message, + searching: false + })); + } + }; - render(){ - return
-

Add

- {fields.map((field, idx)=>{ - return
- - this.handleChange(e, field)} placeholder={field} /> -
; - })} -
{this.state.notificationResult}
- {/* - - - */} - - - {this.state.error - &&
{this.state.error.toString()}
- } -
; - } -}); + return ( +
+

Add

+ {fields.map((field, idx) => ( +
+ + handleChange(e, field)} + placeholder={field} + /> +
+ ))} +
{state.notificationResult}
+ + {state.error &&
{state.error.toString()}
} +
+ ); +}; module.exports = NotificationAdd; diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx index 4f7c0b3d7..7fa3e980e 100644 --- a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx @@ -1,94 +1,97 @@ require('./notificationLookup.less'); + const React = require('react'); -const createClass = require('create-react-class'); -const cx = require('classnames'); +const { useState } = require('react'); +const cx = require('classnames'); const request = require('superagent'); const Moment = require('moment'); +const NotificationLookup = () => { + const [query, setQuery] = useState(''); + const [foundNotification, setFoundNotification] = useState(null); + const [searching, setSearching] = useState(false); + const [error, setError] = useState(null); -const NotificationLookup = createClass({ - displayName : 'NotificationLookup', - getDefaultProps() { - return {}; - }, - getInitialState() { - return { - query : '', - foundNotification : null, - searching : false, - error : null - }; - }, - handleChange(e){ - this.setState({ query: e.target.value }); - }, - lookup(){ - this.setState({ searching: true, error: null }); + const handleChange = (e) => { + setQuery(e.target.value); + }; - request.get(`/admin/notification/lookup/${this.state.query}`) - .then((res)=>this.setState({ foundNotification: res.body })) - .catch((err)=>this.setState({ error: err })) - .finally(()=>this.setState({ searching: false })); - }, + const lookup = () => { + setSearching(true); + setError(null); - deleteNotification : function(){ - console.log('DELETE'); - if(!confirm(`Really delete notification ${this.state.foundNotification.dismissKey} : ${this.state.foundNotification.title}?`)) { - console.log('CANCELLED'); - return; - } - console.log('CONFIRMED'); - return; - }, + request.get(`/admin/notification/lookup/${query}`) + .then((res) => setFoundNotification(res.body)) + .catch((err) => setError(err)) + .finally(() => setSearching(false)); + }; - renderFoundNotification(){ - const notification = this.state.foundNotification; - return
-
-
Key
-
{notification.dismissKey}
+ const deleteNotification = () => { + if (!foundNotification) return; -
Title
-
{notification.title || 'No Title'}
+ const confirmed = window.confirm(`Really delete notification ${foundNotification.dismissKey} : ${foundNotification.title}?`); + if (!confirmed) { + console.log('CANCELLED'); + return; + } + console.log('CONFIRMED'); + // Perform delete operation here + }; -
Text
-
{notification.text || 'No Text'}
+ const renderFoundNotification = () => { + if (!foundNotification) return null; -
Created
-
{Moment(notification.createdAt).toLocaleString()}
+ return ( +
+
+
Key
+
{foundNotification.dismissKey}
-
Start
-
{Moment(notification.startAt).toLocaleString() || 'No Start Time'}
+
Title
+
{foundNotification.title || 'No Title'}
-
Stop
-
{Moment(notification.stopAt).toLocaleString() || 'No End Time'}
-
- -
; - }, +
Text
+
{foundNotification.text || 'No Text'}
- render(){ - return
-

Lookup

- - +
Created
+
{Moment(foundNotification.createdAt).toLocaleString()}
- {this.state.error - &&
{this.state.error.toString()}
- } +
Start
+
{Moment(foundNotification.startAt).toLocaleString() || 'No Start Time'}
- {this.state.foundNotification - ? this.renderFoundNotification() - :
No notification found.
- } -
; - } -}); +
Stop
+
{Moment(foundNotification.stopAt).toLocaleString() || 'No End Time'}
+
+ +
+ ); + }; + + return ( +
+

Lookup

+ + + + {error &&
{error.toString()}
} + + {foundNotification + ? renderFoundNotification() + :
No notification found.
+ } +
+ ); +}; module.exports = NotificationLookup; diff --git a/client/admin/notificationUtils/notificationUtils.jsx b/client/admin/notificationUtils/notificationUtils.jsx index 3e595265b..e3cf87d37 100644 --- a/client/admin/notificationUtils/notificationUtils.jsx +++ b/client/admin/notificationUtils/notificationUtils.jsx @@ -1,19 +1,16 @@ const React = require('react'); -const createClass = require('create-react-class'); const NotificationLookup = require('./notificationLookup/notificationLookup.jsx'); const NotificationAdd = require('./notificationAdd/notificationAdd.jsx'); -const NotificationUtils = createClass({ - displayName : 'NotificationUtils', - - render : function(){ - return <> - -
- - ; - } -}); +const NotificationUtils = () => { + return ( + <> + +
+ + + ); +}; module.exports = NotificationUtils; From 0c6c0c9fd63521345e8da1ccdf383565db6ffdbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Thu, 29 Aug 2024 00:23:22 +0200 Subject: [PATCH 21/63] use actual inputs and textarea with good attributes --- .../notificationAdd/notificationAdd.jsx | 142 +++++++++++------- 1 file changed, 91 insertions(+), 51 deletions(-) diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx index f41401377..f3c01fd9c 100644 --- a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx @@ -1,111 +1,151 @@ -require('./notificationAdd.less'); const React = require('react'); -const { useState } = require('react'); +const { useState, useRef } = require('react'); const cx = require('classnames'); const request = require('superagent'); -const fields = ['dismissKey', 'title', 'text', 'startAt', 'stopAt']; - const NotificationAdd = () => { const [state, setState] = useState({ - query: '', notificationResult: null, searching: false, error: null, - dismissKey: '', - title: '', - text: '', - startAt: '', - stopAt: '' }); - const handleChange = (e, field) => { - const value = e.target.value; - setState(prevState => ({ - ...prevState, - [field]: value - })); - }; + const dismissKeyRef = useRef(null); + const titleRef = useRef(null); + const textRef = useRef(null); + const [startAt, setStartAt] = useState(null); + const [stopAt, setStopAt] = useState(null); const saveNotification = async () => { - if (!state.dismissKey) { + const dismissKey = dismissKeyRef.current.value; + const title = titleRef.current.value; + const text = textRef.current.value; + + // Basic validation + if (!dismissKey || !title || !text || !startAt || !stopAt) { setState(prevState => ({ ...prevState, - error: 'No notification key!' + error: 'All fields are required!', })); return; } const data = { - dismissKey: state.dismissKey, - title: state.title, - text: state.text, - startAt: Date.parse(state.startAt), - stopAt: Date.parse(state.stopAt) + dismissKey, + title, + text, + startAt: startAt ? startAt.toISOString() : '', + stopAt: stopAt ? stopAt.toISOString() : '', }; try { + setState(prevState => ({ ...prevState, searching: true, error: null })); const response = await request.post('/admin/notification/add').send(data); const notification = response.body; + let update = { - notificationResult: `Created notification: ${JSON.stringify(notification, null, 2)}` + notificationResult: `Created notification: ${JSON.stringify(notification, null, 2)}`, }; if (notification.err) { update.notificationResult = JSON.stringify(notification.err); - if (notification.err.code == 11000) { - update.notificationResult = `Duplicate dismissKey error! ${state.dismissKey} already exists.`; + if (notification.err.code === 11000) { + update.notificationResult = `Duplicate dismissKey error! ${dismissKey} already exists.`; } } else { update = { ...update, - dismissKey: '', - title: '', - text: '', - startAt: '', - stopAt: '' + notificationResult: `Notification successfully created.`, }; + // Reset form fields + dismissKeyRef.current.value = ''; + titleRef.current.value = ''; + textRef.current.value = ''; + setStartAt(null); + setStopAt(null); } setState(prevState => ({ ...prevState, ...update, - searching: false + searching: false, })); } catch (err) { setState(prevState => ({ ...prevState, - error: err.message, - searching: false + error: `Error saving notification: ${err.message}`, + searching: false, })); } }; return (
-

Add

- {fields.map((field, idx) => ( -
- - handleChange(e, field)} - placeholder={field} - /> -
- ))} +

Add Notification

+ + + + + + + + + + +
{state.notificationResult}
- - {state.error &&
{state.error.toString()}
} + + {state.error &&
{state.error}
}
); }; From 4edbfa10b5e8af72989178fa0b56a6db57cecddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Thu, 29 Aug 2024 10:45:36 +0200 Subject: [PATCH 22/63] log config vars --- server/app.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/app.js b/server/app.js index 90d14aa5b..34139e561 100644 --- a/server/app.js +++ b/server/app.js @@ -420,6 +420,12 @@ app.get('/account', asyncHandler(async (req, res, next)=>{ })); const nodeEnv = config.get('node_env'); + +//console group +console.group(); +console.log('Config vars') +console.table(config); +console.groupEnd(); const isLocalEnvironment = config.get('local_environments').includes(nodeEnv); // Local only if(isLocalEnvironment){ From 78642e514dc86607ecb867111218bcd33587792d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Thu, 29 Aug 2024 10:47:38 +0200 Subject: [PATCH 23/63] revert console log --- server/app.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/server/app.js b/server/app.js index 34139e561..90d14aa5b 100644 --- a/server/app.js +++ b/server/app.js @@ -420,12 +420,6 @@ app.get('/account', asyncHandler(async (req, res, next)=>{ })); const nodeEnv = config.get('node_env'); - -//console group -console.group(); -console.log('Config vars') -console.table(config); -console.groupEnd(); const isLocalEnvironment = config.get('local_environments').includes(nodeEnv); // Local only if(isLocalEnvironment){ From 65759e18bd665f97982c3d74b5dd76bc626484b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Thu, 29 Aug 2024 23:39:54 +0200 Subject: [PATCH 24/63] clean inputs --- .../notificationAdd/notificationAdd.jsx | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx index f3c01fd9c..ca249fbe5 100644 --- a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx @@ -83,8 +83,8 @@ const NotificationAdd = () => {

Add Notification

- @@ -133,8 +139,7 @@ const NotificationAdd = () => { setStopAt(date)} + ref={stopAtRef} required /> From 4488fe36db6bf5088b645b1a3ab6cb3dd71fbcc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sat, 31 Aug 2024 12:17:12 +0200 Subject: [PATCH 28/63] "Refactored notification lookup and management functionality in admin API and model, added new endpoints for getting all notifications and deleting a notification by dismiss key." --- .../notificationLookup/notificationLookup.jsx | 194 +++++++++++++----- server/admin.api.js | 52 ++++- server/notifications.model.js | 148 +++++++------ 3 files changed, 269 insertions(+), 125 deletions(-) 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, }; From 10ce69633327e1ec2e85cc3c1a383203038d4edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sat, 31 Aug 2024 12:50:53 +0200 Subject: [PATCH 29/63] basic styles --- client/admin/admin.less | 99 +++++++++++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 29 deletions(-) diff --git a/client/admin/admin.less b/client/admin/admin.less index 7ee28981e..4cea390b5 100644 --- a/client/admin/admin.less +++ b/client/admin/admin.less @@ -54,44 +54,85 @@ body { border : 2px solid #444444; } } + section.notificationUtils { + display : flex; + gap : 50px; + justify-content : space-between; - .notificationAdd { - position : relative; - display : flex; - flex-direction : column; - width : 500px; + .notificationAdd { + position : relative; + display : flex; + flex-direction : column; + width : 500px; - .field { - display : grid; - grid-template-columns : 120px 150px; - align-items : center; - justify-items : stretch; - width : 100%; - margin-bottom : 20px; + .field { + display : grid; + grid-template-columns : 120px 150px; + align-items : center; + justify-items : stretch; + width : 100%; + margin-bottom : 20px; - input { - height : 33px; - padding : 0px 10px; - font-family : monospace; - } + input { + height : 33px; + padding : 0px 10px; + font-family : monospace; + } - textarea { - min-width : 50ch; - max-width : 100ch; - min-height : 7em; - max-height : 30em; + textarea { + resize:vertical; + width : 50ch; + min-height : 7em; + max-height : 25em; + } + } + + button { + + position : absolute; + right : 0; + bottom : 0; + + i { margin-right : 10px; } } } - - button { + + .notificationLookup { + width : max-content; + min-width : 450px; + + .notificationList { + display : flex; + flex-direction : column; + gap : px; + margin-block : 20px; + border : 1px solid; + border-radius : 5px; + + details { + padding : 10px; + background : #CCCCCC; + + &:nth-child(even) { background : #DDDDDD; } + &:first-child { + border-top-left-radius : 5px; + border-top-right-radius : 5px; + } + &:last-child { + border-bottom-right-radius : 5px; + border-bottom-left-radius : 5px; + } + + summary { + font-size : 20px; + font-weight : 900; + } + } + } - position : absolute; - right : 0; - bottom : 0; - - i { margin-right : 10px; } } } + } } From e88272c68421c04f17f350dbe9bab6aa4ee02b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sat, 31 Aug 2024 12:51:06 +0200 Subject: [PATCH 30/63] "Refactor admin UI components: update class names, element types, and nesting in admin.jsx, notificationLookup.jsx, and notificationUtils.jsx" --- client/admin/admin.jsx | 8 ++++---- .../notificationLookup/notificationLookup.jsx | 2 +- client/admin/notificationUtils/notificationUtils.jsx | 5 ++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index f675ff37a..f2f2667a4 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -33,13 +33,13 @@ const Admin = createClass({ homebrewery admin
-
-
+
+
+ {this.state.currentTab==='brew' && } {this.state.currentTab==='notifications' && } -
+
; } }); diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx index cddc0c9d3..ff33e7554 100644 --- a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx @@ -129,7 +129,7 @@ const NotificationLookup = () => { } return ( -
+
{notifications.map((notification) => (
{notification.title || 'No Title'} diff --git a/client/admin/notificationUtils/notificationUtils.jsx b/client/admin/notificationUtils/notificationUtils.jsx index e3cf87d37..2e2c4b634 100644 --- a/client/admin/notificationUtils/notificationUtils.jsx +++ b/client/admin/notificationUtils/notificationUtils.jsx @@ -5,11 +5,10 @@ const NotificationAdd = require('./notificationAdd/notificationAdd.jsx'); const NotificationUtils = () => { return ( - <> +
-
- +
); }; From d352b76efecc8953b90385abe4405aba5e4dce21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sat, 31 Aug 2024 13:12:53 +0200 Subject: [PATCH 31/63] join styles and lint --- client/admin/admin.less | 110 +++++------------- .../brewUtils/brewCleanup/brewCleanup.less | 13 +-- .../brewUtils/brewCompress/brewCompress.less | 13 +-- .../admin/brewUtils/brewLookup/brewLookup.jsx | 1 - .../brewUtils/brewLookup/brewLookup.less | 30 ----- client/admin/brewUtils/stats/stats.less | 23 +--- .../notificationAdd/notificationAdd.jsx | 1 + .../notificationAdd/notificationAdd.less | 51 +++++--- .../notificationLookup/notificationLookup.jsx | 6 +- .../notificationLookup.less | 55 +++++---- themes/themes.json | 4 +- 11 files changed, 119 insertions(+), 188 deletions(-) delete mode 100644 client/admin/brewUtils/brewLookup/brewLookup.less diff --git a/client/admin/admin.less b/client/admin/admin.less index 4cea390b5..4c30cff93 100644 --- a/client/admin/admin.less +++ b/client/admin/admin.less @@ -21,7 +21,7 @@ body { text-rendering : optimizeLegibility; } -.admin { +:where(.admin) { header { padding : 20px 0px; @@ -34,7 +34,35 @@ body { hr { margin : 30px 0px; } - .container { + :where(.container) { + input { + height : 33px; + padding : 0px 10px; + margin-bottom : 20px; + font-family : monospace; + } + + button { + height : 37px; + vertical-align : middle; + } + + dl { + @maxItemWidth : 132px; + dt { + float : left; + width : @maxItemWidth; + clear : left; + text-align : right; + &::after { content : ' : '; } + } + dd { + height : 1em; + padding : 0 0 0.5em 0; + margin-left : @maxItemWidth + 6px; + } + } + .tabs button { margin-right : 3px; margin-left : 3px; @@ -54,85 +82,11 @@ body { border : 2px solid #444444; } } - section.notificationUtils { + + .notificationUtils { display : flex; gap : 50px; justify-content : space-between; - - .notificationAdd { - position : relative; - display : flex; - flex-direction : column; - width : 500px; - - .field { - display : grid; - grid-template-columns : 120px 150px; - align-items : center; - justify-items : stretch; - width : 100%; - margin-bottom : 20px; - - - input { - height : 33px; - padding : 0px 10px; - font-family : monospace; - } - - textarea { - resize:vertical; - width : 50ch; - min-height : 7em; - max-height : 25em; - } - } - - button { - - position : absolute; - right : 0; - bottom : 0; - - i { margin-right : 10px; } - } - } - - .notificationLookup { - width : max-content; - min-width : 450px; - - .notificationList { - display : flex; - flex-direction : column; - gap : px; - margin-block : 20px; - border : 1px solid; - border-radius : 5px; - - details { - padding : 10px; - background : #CCCCCC; - - &:nth-child(even) { background : #DDDDDD; } - &:first-child { - border-top-left-radius : 5px; - border-top-right-radius : 5px; - } - &:last-child { - border-bottom-right-radius : 5px; - border-bottom-left-radius : 5px; - } - - summary { - font-size : 20px; - font-weight : 900; - } - } - } - - } } - } } diff --git a/client/admin/brewUtils/brewCleanup/brewCleanup.less b/client/admin/brewUtils/brewCleanup/brewCleanup.less index ec7582855..16fc98957 100644 --- a/client/admin/brewUtils/brewCleanup/brewCleanup.less +++ b/client/admin/brewUtils/brewCleanup/brewCleanup.less @@ -1,10 +1,9 @@ -.BrewCleanup{ - .removeBox{ - margin-top: 20px; - button{ - background-color: @red; - margin-right: 10px; +.BrewCleanup { + .removeBox { + margin-top : 20px; + button { + margin-right : 10px; + background-color : @red; } } - } \ No newline at end of file diff --git a/client/admin/brewUtils/brewCompress/brewCompress.less b/client/admin/brewUtils/brewCompress/brewCompress.less index 2a2bf42ea..8668e9280 100644 --- a/client/admin/brewUtils/brewCompress/brewCompress.less +++ b/client/admin/brewUtils/brewCompress/brewCompress.less @@ -1,10 +1,9 @@ -.BrewCompress{ - .removeBox{ - margin-top: 20px; - button{ - background-color: @red; - margin-right: 10px; +.BrewCompress { + .removeBox { + margin-top : 20px; + button { + margin-right : 10px; + background-color : @red; } } - } \ No newline at end of file diff --git a/client/admin/brewUtils/brewLookup/brewLookup.jsx b/client/admin/brewUtils/brewLookup/brewLookup.jsx index c9212d990..50a2f2015 100644 --- a/client/admin/brewUtils/brewLookup/brewLookup.jsx +++ b/client/admin/brewUtils/brewLookup/brewLookup.jsx @@ -1,4 +1,3 @@ -require('./brewLookup.less'); const React = require('react'); const createClass = require('create-react-class'); const cx = require('classnames'); diff --git a/client/admin/brewUtils/brewLookup/brewLookup.less b/client/admin/brewUtils/brewLookup/brewLookup.less deleted file mode 100644 index 61eeec424..000000000 --- a/client/admin/brewUtils/brewLookup/brewLookup.less +++ /dev/null @@ -1,30 +0,0 @@ - -.brewLookup{ - input{ - height : 33px; - margin-bottom : 20px; - padding : 0px 10px; - font-family : monospace; - } - button{ - vertical-align : middle; - height : 37px; - } - dl{ - @maxItemWidth : 132px; - dt{ - float : left; - clear : left; - width : @maxItemWidth; - text-align : right; - &::after { - content: " : "; - } - } - dd{ - height : 1em; - margin-left : @maxItemWidth + 6px; - padding : 0 0 0.5em 0; - } - } -} \ No newline at end of file diff --git a/client/admin/brewUtils/stats/stats.less b/client/admin/brewUtils/stats/stats.less index 5337bf671..b5a4612e1 100644 --- a/client/admin/brewUtils/stats/stats.less +++ b/client/admin/brewUtils/stats/stats.less @@ -1,28 +1,13 @@ -.Stats{ +.Stats { position : relative; - .pending{ + + .pending { position : absolute; top : 0px; left : 0px; - height : 100%; width : 100%; + height : 100%; background-color : rgba(238,238,238, 0.5); } - dl{ - @maxItemWidth : 132px; - dt{ - float : left; - clear : left; - width : @maxItemWidth; - text-align : right; - &::after { - content: " : "; - } - } - dd{ - margin : 0 0 0 @maxItemWidth + 10px; - padding : 0 0 0.5em 0; - } - } } \ No newline at end of file diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx index ffcce8f10..46f22b0d8 100644 --- a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx @@ -1,3 +1,4 @@ +require('./notificationAdd.less'); const React = require('react'); const { useState, useRef } = require('react'); const cx = require('classnames'); diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.less b/client/admin/notificationUtils/notificationAdd/notificationAdd.less index 32cd61d25..74a977272 100644 --- a/client/admin/notificationUtils/notificationAdd/notificationAdd.less +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.less @@ -1,20 +1,39 @@ +.notificationAdd { + position : relative; + display : flex; + flex-direction : column; + width : 500px; -.notificationAdd{ - input{ - height : 33px; - margin-bottom : 20px; - padding : 0px 10px; - font-family : monospace; + .field { + display : grid; + grid-template-columns : 120px 150px; + align-items : center; + justify-items : stretch; + width : 100%; + margin-bottom : 20px; + + + input { + height : 33px; + padding : 0px 10px; + margin-bottom : unset; + font-family : monospace; + } + + textarea { + width : 50ch; + min-height : 7em; + max-height : 25em; + resize : vertical; + } } - button{ - vertical-align : middle; - height : 37px; - } - .fieldLabel{ - display: inline-block; - width: 10%; - } - .fieldInput{ - margin-bottom: 5px; + + button { + + position : absolute; + right : 0; + bottom : 0; + + i { margin-right : 10px; } } } \ No newline at end of file diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx index ff33e7554..1d4d45321 100644 --- a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx @@ -125,7 +125,7 @@ const NotificationLookup = () => { } if (notifications.length === 0) { - return
No notifications available.
; + return
No notifications available.
; } return ( @@ -143,7 +143,7 @@ const NotificationLookup = () => { return (
-

Lookup

+

Lookup by dissmiss key

{ {renderFoundNotification()}
-

All Notifications

+

Check all Notifications

- - {renderFoundNotification()} -
-
-

Check all Notifications

- + {renderNotificationsList()} -
); }; From d947ff45e2ad1ccf63db53075acc2c53c348b6b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Fri, 13 Sep 2024 20:33:21 +0200 Subject: [PATCH 35/63] correct style to be coherent and nice --- .../notificationAdd/notificationAdd.less | 7 ++----- .../notificationLookup/notificationLookup.less | 9 +++++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.less b/client/admin/notificationUtils/notificationAdd/notificationAdd.less index 74a977272..f274e694f 100644 --- a/client/admin/notificationUtils/notificationAdd/notificationAdd.less +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.less @@ -23,16 +23,13 @@ textarea { width : 50ch; min-height : 7em; - max-height : 25em; + max-height : 20em; resize : vertical; } } button { - - position : absolute; - right : 0; - bottom : 0; + width: 200px; i { margin-right : 10px; } } diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.less b/client/admin/notificationUtils/notificationLookup/notificationLookup.less index 8b731a2da..9402dc9f7 100644 --- a/client/admin/notificationUtils/notificationLookup/notificationLookup.less +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.less @@ -6,12 +6,13 @@ .notificationList { display : flex; flex-direction : column; - gap : px; + max-height : 500px; margin-block : 20px; + overflow : auto; border : 1px solid; border-radius : 5px; - details { + li { padding : 10px; background : #CCCCCC; @@ -29,6 +30,10 @@ font-size : 20px; font-weight : 900; } + + dl dt{ + font-weight: 900; + } } } .noNotification { margin-block : 20px; } From 629b51a26c93ef56621d5a8e6a1a1c3563fcc530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Fri, 13 Sep 2024 20:33:58 +0200 Subject: [PATCH 36/63] remove logs and unecessary state --- .../notificationUtils/notificationAdd/notificationAdd.jsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx index 46f22b0d8..05bf69e01 100644 --- a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx @@ -32,11 +32,6 @@ const NotificationAdd = () => { })); return; } - console.log('dismissKey: ', dismissKey); - console.log('title: ', title); - console.log('text: ', text); - console.log('startAt: ', startAt); - console.log('stopAt: ', stopAt); const data = { dismissKey, @@ -69,8 +64,6 @@ const NotificationAdd = () => { dismissKeyRef.current.value = ''; titleRef.current.value = ''; textRef.current.value = ''; - setStartAt(null); - setStopAt(null); } setState(prevState => ({ From 4f2c2916d632777d7f719ed75ea8575e7d1ac0c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Fri, 13 Sep 2024 23:03:40 +0200 Subject: [PATCH 37/63] remove cx --- .../notificationUtils/notificationAdd/notificationAdd.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx index 05bf69e01..3d8571cf8 100644 --- a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx @@ -1,7 +1,6 @@ require('./notificationAdd.less'); const React = require('react'); const { useState, useRef } = require('react'); -const cx = require('classnames'); const request = require('superagent'); const NotificationAdd = () => { @@ -142,10 +141,10 @@ const NotificationAdd = () => { From dbbfb0b62826a61df238c691414131853efbab30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Fri, 13 Sep 2024 23:29:36 +0200 Subject: [PATCH 38/63] suggestions added, linted --- .../notificationAdd/notificationAdd.jsx | 212 +++++++----------- 1 file changed, 87 insertions(+), 125 deletions(-) diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx index 3d8571cf8..78862eb8e 100644 --- a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx @@ -3,155 +3,117 @@ const React = require('react'); const { useState, useRef } = require('react'); const request = require('superagent'); -const NotificationAdd = () => { - const [state, setState] = useState({ - notificationResult: null, - searching: false, - error: null, - }); +const NotificationAdd = ()=>{ + const [state, setState] = useState({ + notificationResult : null, + searching : false, + error : null, + }); - const dismissKeyRef = useRef(null); - const titleRef = useRef(null); - const textRef = useRef(null); - const startAtRef = useRef(null); - const stopAtRef = useRef(null); + const dismissKeyRef = useRef(null); + const titleRef = useRef(null); + const textRef = useRef(null); + const startAtRef = useRef(null); + const stopAtRef = useRef(null); - const saveNotification = async () => { - const dismissKey = dismissKeyRef.current.value; - const title = titleRef.current.value; - const text = textRef.current.value; - const startAt = new Date(startAtRef.current.value); - const stopAt = new Date(stopAtRef.current.value); + const saveNotification = async ()=>{ + const dismissKey = dismissKeyRef.current.value; + const title = titleRef.current.value; + const text = textRef.current.value; + const startAt = new Date(startAtRef.current.value); + const stopAt = new Date(stopAtRef.current.value); - // Basic validation - if (!dismissKey || !title || !text || !startAt || !stopAt) { - setState(prevState => ({ - ...prevState, - error: 'All fields are required!', - })); - return; - } + // Basic validation + if(!dismissKey || !title || !text || !startAt || !stopAt) { + setState((prevState)=>({ + ...prevState, + error : 'All fields are required!', + })); + return; + } - const data = { - dismissKey, - title, - text, - startAt: startAt ? startAt.toISOString() : '', - stopAt: stopAt ? stopAt.toISOString() : '', - }; + const data = { + dismissKey, + title, + text, + startAt : startAt?.toISOString() ?? '', + stopAt : stopAt?.stopAt.toISOString() ?? '', + }; - try { - setState(prevState => ({ ...prevState, searching: true, error: null })); - const response = await request.post('/admin/notification/add').send(data); - const notification = response.body; + try { + setState((prevState)=>({ ...prevState, searching: true, error: null })); + const response = await request.post('/admin/notification/add').send(data); + console.log(response.body); + const update = { notificationResult: `Notification successfully created.` }; - let update = { - notificationResult: `Created notification: ${JSON.stringify(notification, null, 2)}`, - }; + // Reset form fields + dismissKeyRef.current.value = ''; + titleRef.current.value = ''; + textRef.current.value = ''; - if (notification.err) { - update.notificationResult = JSON.stringify(notification.err); - if (notification.err.code === 11000) { - update.notificationResult = `Duplicate dismissKey error! ${dismissKey} already exists.`; - } - } else { - update = { - ...update, - notificationResult: `Notification successfully created.`, - }; - // Reset form fields - dismissKeyRef.current.value = ''; - titleRef.current.value = ''; - textRef.current.value = ''; - } + setState((prevState)=>({ + ...prevState, + ...update, + searching : false, + })); + } catch (err) { + setState((prevState)=>({ + ...prevState, + error : `Error saving notification: ${err.message}`, + searching : false, + })); + } + }; - setState(prevState => ({ - ...prevState, - ...update, - searching: false, - })); - } catch (err) { - setState(prevState => ({ - ...prevState, - error: `Error saving notification: ${err.message}`, - searching: false, - })); - } - }; + return ( +
+

Add Notification

- return ( -
-

Add Notification

- - - - - - -
{state.notificationResult}
+
{state.notificationResult}
- + - {state.error &&
{state.error}
} -
- ); + {state.error &&
{state.error}
} +
+ ); }; module.exports = NotificationAdd; From ebc3b4ee66bf2ee2a729cdae8c72f63ced63265e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sat, 14 Sep 2024 23:58:47 +0200 Subject: [PATCH 39/63] "Updated admin notification management: added error handling and styling, modified notification add and lookup functionality, and refactored server-side API routes and error handling." --- client/admin/admin.less | 8 + .../notificationAdd/notificationAdd.jsx | 23 ++- .../notificationAdd/notificationAdd.less | 1 + .../notificationLookup/notificationLookup.jsx | 179 +++++++++--------- .../notificationLookup.less | 4 +- server/admin.api.js | 16 +- server/app.js | 2 +- 7 files changed, 121 insertions(+), 112 deletions(-) diff --git a/client/admin/admin.less b/client/admin/admin.less index 4c30cff93..c6c9b4662 100644 --- a/client/admin/admin.less +++ b/client/admin/admin.less @@ -89,4 +89,12 @@ body { justify-content : space-between; } } + + .error { + background: rgb(178, 54, 54); + color:white; + font-weight: 900; + margin-block:10px; + padding:10px; + } } diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx index 78862eb8e..951a25e6b 100644 --- a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx @@ -24,20 +24,27 @@ const NotificationAdd = ()=>{ const stopAt = new Date(stopAtRef.current.value); // Basic validation - if(!dismissKey || !title || !text || !startAt || !stopAt) { - setState((prevState)=>({ + if (!dismissKey || !title || !text || isNaN(startAt.getTime()) || isNaN(stopAt.getTime())) { + setState((prevState) => ({ ...prevState, - error : 'All fields are required!', + error: 'All fields are required', })); return; } - + if (startAt >= stopAt) { + setState((prevState) => ({ + ...prevState, + error: 'End date must be after the start date!', + })); + return; + } + const data = { dismissKey, title, text, startAt : startAt?.toISOString() ?? '', - stopAt : stopAt?.stopAt.toISOString() ?? '', + stopAt : stopAt?.toISOString() ?? '', }; try { @@ -56,10 +63,11 @@ const NotificationAdd = ()=>{ ...update, searching : false, })); - } catch (err) { + } catch (error) { + console.log(error.response.body.message); setState((prevState)=>({ ...prevState, - error : `Error saving notification: ${err.message}`, + error : `Error saving notification: ${error.response.body.message}`, searching : false, })); } @@ -110,7 +118,6 @@ const NotificationAdd = ()=>{ Save Notification - {state.error &&
{state.error}
}
); diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.less b/client/admin/notificationUtils/notificationAdd/notificationAdd.less index f274e694f..878da24c2 100644 --- a/client/admin/notificationUtils/notificationAdd/notificationAdd.less +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.less @@ -25,6 +25,7 @@ min-height : 7em; max-height : 20em; resize : vertical; + padding : 10px; } } diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx index b9214921a..b9762149e 100644 --- a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx @@ -1,118 +1,111 @@ require('./notificationLookup.less'); const React = require('react'); -const { useState, useRef } = require('react'); -const cx = require('classnames'); +const { useState } = require('react'); const request = require('superagent'); const Moment = require('moment'); -const NotificationDetail = ({ notification, onDelete }) => ( -
-
-
Key
-
{notification.dismissKey}
+const NotificationDetail = ({ notification, onDelete })=>( + <> +
+
Key
+
{notification.dismissKey}
-
Title
-
{notification.title || 'No Title'}
+
Title
+
{notification.title || 'No Title'}
-
Text
-
{notification.text || 'No Text'}
+
Text
+
{notification.text || 'No Text'}
-
Created
-
{Moment(notification.createdAt).format('LLLL')}
+
Created
+
{Moment(notification.createdAt).format('LLLL')}
-
Start
-
{Moment(notification.startAt).format('LLLL') || 'No Start Time'}
+
Start
+
{Moment(notification.startAt).format('LLLL') || 'No Start Time'}
-
Stop
-
{Moment(notification.stopAt).format('LLLL') || 'No End Time'}
-
- -
+
Stop
+
{Moment(notification.stopAt).format('LLLL') || 'No End Time'}
+ + + ); -const NotificationLookup = () => { - const [foundNotification, setFoundNotification] = useState(null); - const [searching, setSearching] = useState(false); - const [error, setError] = useState(null); - const [notifications, setNotifications] = useState([]); +const NotificationLookup = ()=>{ + const [foundNotification, setFoundNotification] = useState(null); + const [searching, setSearching] = useState(false); + const [error, setError] = useState(null); + const [notifications, setNotifications] = useState([]); - const lookupAll = async () => { - setSearching(true); - setError(null); + const lookupAll = async ()=>{ + setSearching(true); + setError(null); - try { - const res = await request.get('/admin/notification/all'); - setNotifications(res.body || []); - } catch { - setError('Error fetching all notifications.'); - } finally { - setSearching(false); - } - }; + try { + const res = await request.get('/admin/notification/all'); + setNotifications(res.body || []); + } catch (error) { + console.log(error); + setError(`Error looking up notifications: ${error.response.body.message}`) + } finally { + setSearching(false); + } + }; - const deleteNotification = async (dismissKey) => { - if (!dismissKey) return; + const deleteNotification = async (dismissKey)=>{ + if(!dismissKey) return; - const confirmed = window.confirm( - `Really delete notification ${dismissKey}?` - ); - if (!confirmed) { - console.log('CANCELLED'); - return; - } - console.log('CONFIRMED'); - 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 confirmed = window.confirm( + `Really delete notification ${dismissKey}?` + ); + if(!confirmed) { + console.log('Delete notification cancelled'); + return; + } + console.log('Delete notification confirm'); + 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); + } + lookupAll(); + } catch (error) { + console.log(error); + setError(`Error deleting notification: ${error.response.body.message}`) + }; + } - const renderNotificationsList = () => { - if (error) { - return
{error}
; - } + const renderNotificationsList = ()=>{ + if(error) { + return
{error}
; + } - if (notifications.length === 0) { - return
No notifications available.
; - } + if(notifications.length === 0) { + return
No notifications available.
; + } - return ( -
    - {notifications.map((notification) => ( -
  • -
    - {notification.title || 'No Title'} - -
  • - - ))} -
- ); - }; + return ( +
    + {notifications.map((notification)=>( +
  • +
    + {notification.title || 'No Title'} + +
  • - return ( -
    -

    Check all Notifications

    - + ))} +
+ ); + }; - {renderNotificationsList()} -
- ); + return ( +
+

Check all Notifications

+ {renderNotificationsList()} +
+ ); }; module.exports = NotificationLookup; diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.less b/client/admin/notificationUtils/notificationLookup/notificationLookup.less index 9402dc9f7..3f9b78310 100644 --- a/client/admin/notificationUtils/notificationLookup/notificationLookup.less +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.less @@ -1,7 +1,7 @@ .notificationLookup { - width : max-content; - min-width : 450px; + width : 450px; + height : fit-content; .notificationList { display : flex; diff --git a/server/admin.api.js b/server/admin.api.js index 55bea2892..4c9fa5e39 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -145,20 +145,20 @@ router.get('/admin/notification/all', async (req, res, next) => { try { const notifications = await NotificationModel.getAll(); return res.json(notifications); - } catch (err) { - return next(err); + } catch (error) { + console.log('Error getting all notifications: ', error.message); + return res.status(500).json({message: error.message}); } }); 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); + return res.status(201).json(notification); } catch (error) { - console.error('Error adding notification:', error); - return res.status(500).json({ error: 'An error occurred while adding the notification' }); + console.log('Error adding notification: ', error.message); + return res.status(500).json({message: error.message}); } }); @@ -167,8 +167,8 @@ router.delete('/admin/notification/delete/:id', mw.adminOnly, async (req, res, n 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' }); + console.error('Error deleting notification: { key: ', req.params.id , ' error: ', error.message ,' }'); + return res.status(500).json({message: error.message}); } }); diff --git a/server/app.js b/server/app.js index c03fb2dc2..16c145573 100644 --- a/server/app.js +++ b/server/app.js @@ -517,7 +517,7 @@ const getPureError = (error)=>{ app.use(async (err, req, res, next)=>{ err.originalUrl = req.originalUrl; - console.error(err); + console.error('console.log in app.js: ', err); if(err.originalUrl?.startsWith('/api/')) { // console.log('API error'); From 0cdc1947c130bd2dbbf991b4dd7451725acaec5c Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 18 Sep 2024 14:45:17 -0400 Subject: [PATCH 40/63] Linting --- .../notificationAdd/notificationAdd.jsx | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx index 951a25e6b..1aaf11945 100644 --- a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx @@ -24,21 +24,21 @@ const NotificationAdd = ()=>{ const stopAt = new Date(stopAtRef.current.value); // Basic validation - if (!dismissKey || !title || !text || isNaN(startAt.getTime()) || isNaN(stopAt.getTime())) { - setState((prevState) => ({ + if(!dismissKey || !title || !text || isNaN(startAt.getTime()) || isNaN(stopAt.getTime())) { + setState((prevState)=>({ ...prevState, - error: 'All fields are required', + error : 'All fields are required', })); return; } - if (startAt >= stopAt) { - setState((prevState) => ({ + if(startAt >= stopAt) { + setState((prevState)=>({ ...prevState, - error: 'End date must be after the start date!', + error : 'End date must be after the start date!', })); return; } - + const data = { dismissKey, title, @@ -50,8 +50,7 @@ const NotificationAdd = ()=>{ try { setState((prevState)=>({ ...prevState, searching: true, error: null })); const response = await request.post('/admin/notification/add').send(data); - console.log(response.body); - const update = { notificationResult: `Notification successfully created.` }; + console.log(response.body); // Reset form fields dismissKeyRef.current.value = ''; @@ -60,11 +59,11 @@ const NotificationAdd = ()=>{ setState((prevState)=>({ ...prevState, - ...update, - searching : false, + notificationResult : `Notification successfully created.`, + searching : false, })); } catch (error) { - console.log(error.response.body.message); + console.log(error.response.body.message); setState((prevState)=>({ ...prevState, error : `Error saving notification: ${error.response.body.message}`, @@ -78,22 +77,21 @@ const NotificationAdd = ()=>{

Add Notification