From 9fa56368c0abec742ff231560dc967a094f3290a Mon Sep 17 00:00:00 2001 From: evan Date: Wed, 30 Jul 2025 19:26:08 -0700 Subject: [PATCH 01/17] nothing really From ec9e48e94d6fb0066b3b87ebe2cab6838d3585ae Mon Sep 17 00:00:00 2001 From: leishaaf <152096600+leishaaf@users.noreply.github.com> Date: Wed, 2 Jul 2025 14:49:11 -1000 Subject: [PATCH 02/17] deleted delete expiredAd method --- src/Pages/Advertisement/AdvertisementAdmin.js | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/Pages/Advertisement/AdvertisementAdmin.js b/src/Pages/Advertisement/AdvertisementAdmin.js index 049f65087..ab3c46600 100644 --- a/src/Pages/Advertisement/AdvertisementAdmin.js +++ b/src/Pages/Advertisement/AdvertisementAdmin.js @@ -42,30 +42,17 @@ export default function AdvertisementAdmin() { }, user.token); await getAdsFromDB(); - } - - async function deleteExpiredAds() { - const adsFromDB = await getAds(user.token); - if (!adsFromDB.error) { - const currentDate = new Date(); - const expiredAds = adsFromDB.responseData.filter(ad => { - if (ad.expireDate === undefined) return false; - return new Date(ad.expireDate) < currentDate; - }); - - for (const ad of expiredAds) { - await deleteAd(ad, user.token); - } - } + // createAd returns a value - what is it? + // how do we know if createAd() works or not? + // is there a field we can check?^ + // + // if createAd worked, how can we update the array without calling the backend again? + // } useEffect(() => { getAdsFromDB(); - const intervalId = setInterval(async () => { - await deleteExpiredAds(); - await getAdsFromDB(); - }, 20); }, []); From 1211bfd5e59f7bf5a4ee79d49e4ec182e53bce3c Mon Sep 17 00:00:00 2001 From: leishaaf <152096600+leishaaf@users.noreply.github.com> Date: Tue, 1 Jul 2025 18:40:42 -1000 Subject: [PATCH 03/17] adding tZZZZZ# --- api/main_endpoints/routes/Advertisement.js | 8 +-- src/Pages/Advertisement/AdvertisementAdmin.js | 69 +++++++------------ 2 files changed, 27 insertions(+), 50 deletions(-) diff --git a/api/main_endpoints/routes/Advertisement.js b/api/main_endpoints/routes/Advertisement.js index a726a992c..e99f22c7f 100644 --- a/api/main_endpoints/routes/Advertisement.js +++ b/api/main_endpoints/routes/Advertisement.js @@ -23,6 +23,7 @@ router.get('/', async (req, res) => { }); }); + router.get('/getAllAdvertisements', async (req, res) => { if (!checkIfTokenSent(req)) { return res.sendStatus(FORBIDDEN); @@ -50,6 +51,7 @@ router.post('/createAdvertisement', async (req, res) => { message: req.body.message, expireDate: req.body.expireDate }); + console.log("expire date " + expireDate); try { const createdAd = await Advertisement.create(newAd); @@ -83,12 +85,6 @@ router.post('/deleteAdvertisement', async (req, res) => { } try { - const adToDelete = await Advertisement.findById(req.body._id); - - if (!adToDelete) { - return res.sendStatus(NOT_FOUND); - } - const deleteResult = await Advertisement.deleteOne({_id: req.body._id}); if(deleteResult.deletedCount < 1) { diff --git a/src/Pages/Advertisement/AdvertisementAdmin.js b/src/Pages/Advertisement/AdvertisementAdmin.js index ab3c46600..640d4e8e4 100644 --- a/src/Pages/Advertisement/AdvertisementAdmin.js +++ b/src/Pages/Advertisement/AdvertisementAdmin.js @@ -8,9 +8,7 @@ export default function AdvertisementAdmin() { const [ads, setAds] = useState([]); const [message, setMessage] = useState(''); - const [year, setYear] = useState(); - const [month, setMonth] = useState(); - const [day, setDay] = useState(); + const [expireDate, setExpireDate] = useState(); async function getAdsFromDB() { const adsFromDB = await getAds(user.token); @@ -20,20 +18,8 @@ export default function AdvertisementAdmin() { } async function createAdHandler() { - // make sure empty inputs are properly set as undefined if empty - if (year === '') { - setYear(undefined); - } - if (month === '') { - setMonth(undefined); - } - if (day === '') { - setDay(undefined); - } - - let expireDate = new Date(year, month - 1, day); - if (isNaN(expireDate.getTime())) { - expireDate = undefined; + if(expireDate === null){ + setExpireDate(undefined); } await createAd({ @@ -42,18 +28,29 @@ export default function AdvertisementAdmin() { }, user.token); await getAdsFromDB(); - // createAd returns a value - what is it? - // how do we know if createAd() works or not? - // is there a field we can check?^ - // - // if createAd worked, how can we update the array without calling the backend again? - // + } + + async function deleteExpiredAds() { + const adsFromDB = await getAds(user.token); + if (!adsFromDB.error) { + const currentDate = new Date(); + + const expiredAds = adsFromDB.responseData.filter(ad => { + if (ad.expireDate === undefined) return false; + return new Date(ad.expireDate) < currentDate; + }); + + for (const ad of expiredAds) { + await deleteAd(ad, user.token); + } + } } useEffect(() => { getAdsFromDB(); + }, []); return ( @@ -89,28 +86,12 @@ export default function AdvertisementAdmin() {
{ - setYear(event.target.value); - }} - /> - { - setMonth(event.target.value); - }} - /> - { - setDay(event.target.value); + className='flex-1 text-sm input input-bordered sm:text-base' + type='datetime-local' + onChange={ event => { + setExpireDate(event.target.value); }} + // value={expireDate} />
- { - setExpireDate(event.target.value); - }} - /> + {expireButtonClicked ? ( + <> + {/* ^^ fragment wrapping */} + { + setExpireDate(event.target.value); + }} + /> + + + ) : ( + + )} +
{expireButtonClicked ? ( - <> - {/* ^^ fragment wrapping */} - { - setExpireDate(event.target.value); - }} - /> - + ) : ( + ); + }else{ + return( + <> + { + setExpireDate(event.target.value); + }} + /> + + + ); + } +} useEffect(() => { getAdsFromDB(); @@ -80,36 +112,8 @@ export default function AdvertisementAdmin() {
-
- {expireButtonClicked ? ( - <> - {/* ^^ fragment wrapping */} - { - setExpireDate(event.target.value); - }} - /> - - - ) : ( - - )} - +
+ {maybeRenderExpirationInput()} + ); }else{ return( <> - { - setExpireDate(event.target.value); - }} - /> - - + + ); + } } -} useEffect(() => { getAdsFromDB(); From 9ad26088fda077c5d2fddb88c9b90d2462b26edb Mon Sep 17 00:00:00 2001 From: leishaaf <152096600+leishaaf@users.noreply.github.com> Date: Tue, 15 Jul 2025 23:09:54 -1000 Subject: [PATCH 15/17] changed cancel button --- src/Pages/Advertisement/AdvertisementAdmin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pages/Advertisement/AdvertisementAdmin.js b/src/Pages/Advertisement/AdvertisementAdmin.js index e50e6ad47..80259701a 100644 --- a/src/Pages/Advertisement/AdvertisementAdmin.js +++ b/src/Pages/Advertisement/AdvertisementAdmin.js @@ -69,7 +69,7 @@ export default function AdvertisementAdmin() { setExpireDate(undefined); // so that if the user decides to cancel after inputting a date, it will be N/A not prev input }} > - Cancel + Remove Expiration ); From 6d876c2ddd230ab2962e52b50a4d8848b3f23b79 Mon Sep 17 00:00:00 2001 From: evan Date: Sat, 2 Aug 2025 11:06:19 -0700 Subject: [PATCH 16/17] my name is leisha and i live on an island --- src/APIFunctions/Advertisement.js | 1 - src/Enums.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/APIFunctions/Advertisement.js b/src/APIFunctions/Advertisement.js index afcd65193..e539be9e5 100644 --- a/src/APIFunctions/Advertisement.js +++ b/src/APIFunctions/Advertisement.js @@ -66,7 +66,6 @@ export async function createAd(newAd, token) { status.responseData = err; status.error = true; } - // console.log('STATUS ' + JSON.stringify(status.responseData)); return status; } diff --git a/src/Enums.js b/src/Enums.js index f9961fba3..71ba63922 100644 --- a/src/Enums.js +++ b/src/Enums.js @@ -48,7 +48,7 @@ function membershipStateToString(accessLevel) { return membershipStatusArray[accessLevel + 2]; } -const BASE_API_URL = process.env.REACT_APP_BASE_API_URL || 'http://localhost:8080'; +const BASE_API_URL = process.env.REACT_APP_BASE_API_URL || 'http://localhost:8080/'; module.exports = { memberApplicationState, From 39556497ee3e17ec52c88d149c71f4b7a3f2d03d Mon Sep 17 00:00:00 2001 From: evan Date: Sat, 2 Aug 2025 12:51:04 -0700 Subject: [PATCH 17/17] ui looks good + remove redundant expireDate field, tests --- api/main_endpoints/models/Advertisement.js | 5 +- api/main_endpoints/routes/Advertisement.js | 14 +- src/Pages/Advertisement/AdvertisementAdmin.js | 167 +++++++++++------- 3 files changed, 114 insertions(+), 72 deletions(-) diff --git a/api/main_endpoints/models/Advertisement.js b/api/main_endpoints/models/Advertisement.js index 80263fcb4..62e0d54ee 100644 --- a/api/main_endpoints/models/Advertisement.js +++ b/api/main_endpoints/models/Advertisement.js @@ -8,16 +8,13 @@ const AdvertisementSchema = new Schema( required: true, maxlength: [255, 'message must be at most 255 characters long'] }, - expireDate: { - type: Date, - }, expireAt: { type: Date, default: undefined, index: {expireAfterSeconds: 0}, // TTL only kicks in when expireAt is set } }, - { collection: 'Advertisements' } + { collection: 'Advertisements', timestamps: { createdAt: true } } ); module.exports = mongoose.model('Advertisement', AdvertisementSchema); diff --git a/api/main_endpoints/routes/Advertisement.js b/api/main_endpoints/routes/Advertisement.js index e7f2d063d..09bb7bb75 100644 --- a/api/main_endpoints/routes/Advertisement.js +++ b/api/main_endpoints/routes/Advertisement.js @@ -19,6 +19,7 @@ router.get('/', async (req, res) => { res.status(OK).send(items || {}); }) .catch(error => { + logger.error('/api/Advertisement/ had an error', error); res.sendStatus(BAD_REQUEST); }); }); @@ -31,8 +32,10 @@ router.get('/getAllAdvertisements', async (req, res) => { return res.sendStatus(UNAUTHORIZED); } Advertisement.find() + .sort({ createdAt: -1 }) .then(items => res.status(OK).send(items)) .catch(error => { + logger.error('/api/Advertisement/getAllAdvertisements/ had an error', error); res.sendStatus(BAD_REQUEST); }); }); @@ -49,7 +52,6 @@ router.post('/createAdvertisement', async (req, res) => { const newAd = new Advertisement({ message: req.body.message, - expireDate: req.body.expireDate, expireAt: req.body.expireDate }); @@ -60,7 +62,7 @@ router.post('/createAdvertisement', async (req, res) => { action: AuditLogActions.CREATE_AD, details: { message: createdAd.message, - expireDate: createdAd.expireDate, + expireAt: createdAd.expireAt, advertisementId: createdAd._id } }).catch(logger.error); @@ -85,9 +87,9 @@ router.post('/deleteAdvertisement', async (req, res) => { } try { - const deleteResult = await Advertisement.deleteOne({_id: req.body._id}); + const deleteResult = await Advertisement.findOneAndDelete({ _id: req.body._id }); - if(deleteResult.deletedCount < 1) { + if(!deleteResult) { return res.sendStatus(NOT_FOUND); } @@ -96,8 +98,8 @@ router.post('/deleteAdvertisement', async (req, res) => { action: AuditLogActions.DELETE_AD, details: { deletedAd: { - id: adToDelete._id, - message: adToDelete.message, + id: deleteResult._id, + message: deleteResult.message, } } }).catch(logger.error); diff --git a/src/Pages/Advertisement/AdvertisementAdmin.js b/src/Pages/Advertisement/AdvertisementAdmin.js index 80259701a..12ef6c447 100644 --- a/src/Pages/Advertisement/AdvertisementAdmin.js +++ b/src/Pages/Advertisement/AdvertisementAdmin.js @@ -6,7 +6,6 @@ import { useSCE } from '../../Components/context/SceContext.js'; export default function AdvertisementAdmin() { const { user } = useSCE(); - const [errorMesssage, setErrorMessage] = useState(''); const [ads, setAds] = useState([]); const [message, setMessage] = useState(''); const [expireDate, setExpireDate] = useState(); @@ -19,61 +18,125 @@ export default function AdvertisementAdmin() { } } - function isExpired(expireDate){ + function isExpired() { + if (!expireDate) { + return false; + } const currDate = new Date(); const expireDateObject = new Date(expireDate); return expireDateObject < currDate; } async function createAdHandler() { - if(expireDate === null){ - setExpireDate(undefined); + const newAd = { message }; + if (expireDate) { + const asDateObject = new Date(expireDate); + newAd.expireDate = asDateObject.toISOString(); } // expireDate is a string so we need to turn into date object - if(isExpired(expireDate)){ - setErrorMessage('Setting an expiration date in the past will not properly save an ad to database!!'); - }else{ - setErrorMessage(''); - } - await createAd({ - message, - expireDate, - }, user.token); + await createAd(newAd, user.token); await getAdsFromDB(); - } - function maybeRenderExpirationInput(){ - if(!expireButtonClicked){ - return( - ); - }else{ - return( + + function maybeRenderExpirationInput() { + if (!message) { + return null; + } + if (!expireButtonClicked) { + return ( <> - { - setExpireDate(event.target.value); - }} - /> ); } + return ( + <> + { + setExpireDate(event.target.value); + }} + /> + + + ); + } + + function getFormattedTime(maybeISOString = null) { + let date = new Date(); + if (maybeISOString) { + date = new Date(maybeISOString); + } + + return date.toLocaleString('en-US', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: 'numeric', + minute: '2-digit', + hour12: true, + timeZoneName: 'short', + }); + } + + function maybeRenderCreateAdInputs() { + if (!message) { + return; + } + return ( + <> + {message &&
+ +
} +
+ {maybeRenderExpirationInput()} + +
+ { + isExpired() &&
+ + +
+ } + + ); } useEffect(() => { @@ -85,7 +148,6 @@ export default function AdvertisementAdmin() {

Welcome to the Advertisement Admin Page!!

-

{errorMesssage}

-
- -
-
- {maybeRenderExpirationInput()} - -
+ {maybeRenderCreateAdInputs()} @@ -145,12 +188,12 @@ export default function AdvertisementAdmin() { {ad.message}
- {ad.expireDate === undefined ? 'N/A' : ad.expireDate} + {ad.expireAt ? getFormattedTime(ad.expireAt) : '-'}