From 89463d90ca79d506f5440112002c845bc5715e65 Mon Sep 17 00:00:00 2001 From: camila-carrillo Date: Mon, 10 Nov 2025 14:46:57 -0500 Subject: [PATCH 1/6] integrate notif frontend w/ backend findByUser and delete endpoints --- frontend/src/external/bcanSatchel/actions.ts | 5 +- frontend/src/external/bcanSatchel/store.ts | 3 +- frontend/src/main-page/header/Bell.tsx | 64 ++++++++++++------- .../notifications/GrantNotification.tsx | 15 ++++- .../notifications/NotificationPopup.tsx | 48 +++++++++++++- 5 files changed, 106 insertions(+), 29 deletions(-) diff --git a/frontend/src/external/bcanSatchel/actions.ts b/frontend/src/external/bcanSatchel/actions.ts index 6b03e1a1..096e11ba 100644 --- a/frontend/src/external/bcanSatchel/actions.ts +++ b/frontend/src/external/bcanSatchel/actions.ts @@ -2,6 +2,7 @@ import { action } from 'satcheljs'; import { Grant } from '../../../../middle-layer/types/Grant' import { User } from '../../../../middle-layer/types/User' import { Status } from '../../../../middle-layer/types/Status' +import { Notification } from '../../../../middle-layer/types/Notification'; /** * Set whether the user is authenticated, update the user object, @@ -69,7 +70,5 @@ export const updateSearchQuery = action( export const setNotifications = action( 'setNotifications', - (notifications: {id: number; title: string; message: string }[]) => ({ - notifications, - }) + (notifications: Notification[]) => ({notifications}) ) diff --git a/frontend/src/external/bcanSatchel/store.ts b/frontend/src/external/bcanSatchel/store.ts index 9b0ce34e..4bd416f3 100644 --- a/frontend/src/external/bcanSatchel/store.ts +++ b/frontend/src/external/bcanSatchel/store.ts @@ -2,6 +2,7 @@ import { createStore } from 'satcheljs'; import { User } from '../../../../middle-layer/types/User' import { Grant } from '../../../../middle-layer/types/Grant' import { Status } from '../../../../middle-layer/types/Status' +import { Notification } from '../../../../middle-layer/types/Notification' export interface AppState { isAuthenticated: boolean; @@ -14,7 +15,7 @@ export interface AppState { endDateFilter: Date | null; searchQuery: string; yearFilter:number[] | null; - notifications: { id: number; title: string; message: string; }[]; + notifications: Notification[]; } // Define initial state diff --git a/frontend/src/main-page/header/Bell.tsx b/frontend/src/main-page/header/Bell.tsx index 1aeb811d..374b7ce9 100644 --- a/frontend/src/main-page/header/Bell.tsx +++ b/frontend/src/main-page/header/Bell.tsx @@ -1,17 +1,20 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faBell } from "@fortawesome/free-solid-svg-icons"; import { useEffect, useState } from "react"; -//import { api } from "../../api"; //todo: swap out dummy data with real api fetch when backend is ready +import { api } from "../../api"; import NotificationPopup from "../notifications/NotificationPopup"; import { setNotifications as setNotificationsAction } from "../../external/bcanSatchel/actions"; import { getAppStore } from "../../external/bcanSatchel/store"; - +import { useAuthContext } from "../../context/auth/authContext"; // get current user id // const currUserID = sessionStorage.getItem('userId'); // const currUserID = "bcanuser33"; const BellButton = () => { + // gets current user from auth context + const { user } = useAuthContext(); + // stores notifications for the current user const store = getAppStore(); const notifications = store.notifications ?? []; @@ -26,26 +29,43 @@ const BellButton = () => { // function that handles when button is clicked and fetches notifications const handleClick = async () => { - //temporary dummy data for now - const dummyNotifications = [ - {id: 1, title: "Grant Deadline", message: "Grant A deadline approaching in 3 days"}, - {id: 2, title: "Grant Deadline", message: "Grant B deadline tomorrow!"}, - {id: 3, title: "Grant Deadline", message: "Grant C deadline passed yesterday!"}, - {id: 4, title: "Grant Deadline", message: "Grant D deadline tomorrow!"} - ]; - //previous api logic (for later) - //const response = await api( - //`/notifications/user/${currUserID}`, - //{ - //method: "GET", - //} - //); - //console.log(response); - //const currNotifications = await response.json(); - setNotificationsAction(dummyNotifications); - setClicked(!isClicked); - return notifications; - }; + // don't fetch if user isn't logged in (safe fallback) + if (!user?.userId) { + console.warn("No user logged in, cannot fetch notifications"); + setClicked(!isClicked); + return; + } + + try { + // call backend route + const response = await api( + `/notifications/user/${user.userId}`, + { + method: "GET", + } + ); + + if (!response.ok) { + console.error("Failed to fetch notifications:", response.statusText); + // still open popup even if fetch fails (show empty state) + setClicked(!isClicked); + return; + } + + // parse the notifications from response + const fetchedNotifications = await response.json(); + + // update store with fetched notifications + setNotificationsAction(fetchedNotifications); + + // toggle popup open + setClicked(!isClicked); + } catch (error) { + console.error("Error fetching notifications:", error); + //still open popup on error + setClicked(!isClicked); + } + }; const handleClose = () => setClicked(false); diff --git a/frontend/src/main-page/notifications/GrantNotification.tsx b/frontend/src/main-page/notifications/GrantNotification.tsx index ef59f2da..ee09a9ca 100644 --- a/frontend/src/main-page/notifications/GrantNotification.tsx +++ b/frontend/src/main-page/notifications/GrantNotification.tsx @@ -3,11 +3,22 @@ import { faBell } from "@fortawesome/free-solid-svg-icons"; import { FaTrash } from "react-icons/fa"; interface GrantNotificationProps { + notificationId: string; title: string; message: string; + onDelete: (notificationId: string) => void; } -const GrantNotification: React.FC = ({ title, message }) => { +const GrantNotification: React.FC = ({ + notificationId, + title, + message, + onDelete +}) => { + const handleDelete = () => { + onDelete(notificationId); + }; + return (
@@ -20,6 +31,8 @@ const GrantNotification: React.FC = ({ title, message })
); diff --git a/frontend/src/main-page/notifications/NotificationPopup.tsx b/frontend/src/main-page/notifications/NotificationPopup.tsx index 26f2be59..53bd95f8 100644 --- a/frontend/src/main-page/notifications/NotificationPopup.tsx +++ b/frontend/src/main-page/notifications/NotificationPopup.tsx @@ -1,9 +1,13 @@ import { createPortal } from 'react-dom'; import GrantNotification from "./GrantNotification"; import '../../styles/notification.css'; +import { api } from "../../api"; +import { setNotifications as setNotificationsAction } from "../../external/bcanSatchel/actions"; +import { useAuthContext } from "../../context/auth/authContext"; +import { Notification } from "../../../../middle-layer/types/Notification"; interface NotificationPopupProps { - notifications: { id: number; title: string; message: string }[]; + notifications: Notification[]; onClose: () => void; } @@ -11,6 +15,40 @@ const NotificationPopup: React.FC = ({ notifications, onClose }) => { + const { user } = useAuthContext(); + + const handleDelete = async (notificationId: string) => { + try { + const response = await api( + `/notifications/${notificationId}`, + { + method: "DELETE", + } + ); + + if (!response.ok) { + console.error("Failed to delete notification:", response.statusText); + return; + } + + if (user?.userId) { + const fetchResponse = await api( + `/notifications/user/${user.userId}`, + { + method: "GET", + } + ); + + if (fetchResponse.ok) { + const updatedNotifications = await fetchResponse.json(); + setNotificationsAction(updatedNotifications); + } + } + } catch (error) { + console.error("Error deleting notification:", error); + } + }; + return createPortal(
@@ -23,7 +61,13 @@ const NotificationPopup: React.FC = ({
{notifications && notifications.length > 0 ? ( notifications.map((n) => ( - + )) ) : (

No new notifications

From 9422ce0e199d0f33d85bd4ce9eded37613ab62f4 Mon Sep 17 00:00:00 2001 From: camila-carrillo Date: Tue, 11 Nov 2025 00:38:37 -0500 Subject: [PATCH 2/6] tested- working integration and display --- backend/src/grant/grant.module.ts | 2 ++ frontend/src/main-page/header/Bell.tsx | 16 ++++++---- .../notifications/NotificationPopup.tsx | 32 ++++++++++++------- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/backend/src/grant/grant.module.ts b/backend/src/grant/grant.module.ts index 420037aa..1da0a8f7 100644 --- a/backend/src/grant/grant.module.ts +++ b/backend/src/grant/grant.module.ts @@ -1,8 +1,10 @@ import { Module } from '@nestjs/common'; import { GrantService } from './grant.service'; import { GrantController } from './grant.controller'; +import { NotificationsModule } from '../notifications/notification.module'; @Module({ + imports: [NotificationsModule], controllers: [GrantController], providers: [GrantService], }) diff --git a/frontend/src/main-page/header/Bell.tsx b/frontend/src/main-page/header/Bell.tsx index 374b7ce9..bd44edae 100644 --- a/frontend/src/main-page/header/Bell.tsx +++ b/frontend/src/main-page/header/Bell.tsx @@ -29,17 +29,21 @@ const BellButton = () => { // function that handles when button is clicked and fetches notifications const handleClick = async () => { + // TODO: Remove hardcoded userId after /auth/session endpoint is fixed + + const testUserId = "bcanuser33"; //hardcoded for testing + // don't fetch if user isn't logged in (safe fallback) - if (!user?.userId) { - console.warn("No user logged in, cannot fetch notifications"); - setClicked(!isClicked); - return; - } + //if (!user?.userId) { + //console.warn("No user logged in, cannot fetch notifications"); + //setClicked(!isClicked); + //return; + //} try { // call backend route const response = await api( - `/notifications/user/${user.userId}`, + `/notifications/user/${testUserId}`, { method: "GET", } diff --git a/frontend/src/main-page/notifications/NotificationPopup.tsx b/frontend/src/main-page/notifications/NotificationPopup.tsx index 53bd95f8..8ce1df79 100644 --- a/frontend/src/main-page/notifications/NotificationPopup.tsx +++ b/frontend/src/main-page/notifications/NotificationPopup.tsx @@ -5,17 +5,21 @@ import { api } from "../../api"; import { setNotifications as setNotificationsAction } from "../../external/bcanSatchel/actions"; import { useAuthContext } from "../../context/auth/authContext"; import { Notification } from "../../../../middle-layer/types/Notification"; +import { getAppStore } from "../../external/bcanSatchel/store"; +import { observer } from 'mobx-react-lite'; interface NotificationPopupProps { notifications: Notification[]; onClose: () => void; } -const NotificationPopup: React.FC = ({ +const NotificationPopup: React.FC = observer(({ notifications, onClose }) => { const { user } = useAuthContext(); + const store = getAppStore(); + const liveNotifications = store.notifications ?? []; const handleDelete = async (notificationId: string) => { try { @@ -31,24 +35,28 @@ const NotificationPopup: React.FC = ({ return; } - if (user?.userId) { - const fetchResponse = await api( - `/notifications/user/${user.userId}`, - { - method: "GET", - } - ); + // TODO: Remove hardcoded userId after /auth/session endpoint is fixed + const testUserId = "bcanuser33"; //hardcode userid for refetch (test) + + const fetchResponse = await api( + `/notifications/user/${testUserId}`, + { + method: "GET", + } + ); if (fetchResponse.ok) { const updatedNotifications = await fetchResponse.json(); setNotificationsAction(updatedNotifications); } } - } catch (error) { + catch (error) { console.error("Error deleting notification:", error); } }; + console.log("Live notifications:", liveNotifications); + return createPortal(
@@ -59,8 +67,8 @@ const NotificationPopup: React.FC = ({
- {notifications && notifications.length > 0 ? ( - notifications.map((n) => ( + {liveNotifications && liveNotifications.length > 0 ? ( + liveNotifications.map((n) => ( = ({
, document.body ); -}; +}); export default NotificationPopup; \ No newline at end of file From f7af48ac3c98f522a3716749c183415c0b163e54 Mon Sep 17 00:00:00 2001 From: prooflesben Date: Sat, 29 Nov 2025 19:49:00 -0500 Subject: [PATCH 3/6] Trying to make things work --- backend/src/grant/grant.module.ts | 1 - .../src/notifications/notifcation.service.ts | 19 ++- frontend/package-lock.json | 142 ++++-------------- frontend/src/main-page/header/Bell.tsx | 3 +- 4 files changed, 49 insertions(+), 116 deletions(-) diff --git a/backend/src/grant/grant.module.ts b/backend/src/grant/grant.module.ts index 419819a0..1da0a8f7 100644 --- a/backend/src/grant/grant.module.ts +++ b/backend/src/grant/grant.module.ts @@ -7,6 +7,5 @@ import { NotificationsModule } from '../notifications/notification.module'; imports: [NotificationsModule], controllers: [GrantController], providers: [GrantService], - imports: [NotificationsModule], }) export class GrantModule { } \ No newline at end of file diff --git a/backend/src/notifications/notifcation.service.ts b/backend/src/notifications/notifcation.service.ts index c3330e31..a9cf61fc 100644 --- a/backend/src/notifications/notifcation.service.ts +++ b/backend/src/notifications/notifcation.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import AWS from 'aws-sdk'; import { Notification } from '../../../middle-layer/types/Notification'; @@ -7,6 +7,8 @@ export class NotificationService { private dynamoDb = new AWS.DynamoDB.DocumentClient(); private ses = new AWS.SES({ region: process.env.AWS_REGION }); + private readonly logger = new Logger(NotificationService.name); + // function to create a notification // Should this have a check to prevent duplicate notifications? @@ -33,8 +35,15 @@ export class NotificationService { // ExpressionAttributeValues specifies the actual value of the key // IndexName specifies our Global Secondary Index, which was created in the BCANNotifs table to // allow for querying by userId, as it is not a primary/partition key + const notificationTableName = process.env.DYNAMODB_NOTIFICATION_TABLE_NAME; + this.logger.log(`Fetching notifications for userId: ${userId} from table: ${notificationTableName}`); + + if (!notificationTableName) { + this.logger.error('DYNAMODB_NOTIFICATION_TABLE_NAME is not defined in environment variables'); + throw new Error("Internal Server Error") + } const params = { - TableName: process.env.DYNAMODB_NOTIFICATION_TABLE_NAME || 'TABLE_FAILURE', + TableName: notificationTableName, IndexName: 'userId-alertTime-index', KeyConditionExpression: 'userId = :userId', ExpressionAttributeValues: { @@ -47,14 +56,16 @@ export class NotificationService { const data = await this.dynamoDb.query(params).promise(); + // This is never hit, because no present userId throws an error if (!data || !data.Items || data.Items.length == 0) { - throw new Error('No notifications with user id ' + userId + ' found.'); + this.logger.warn(`No notifications found for userId: ${userId}`); + return [] as Notification[]; } return data.Items as Notification[]; } catch (error) { - console.log(error) + this.logger.error(`Error retrieving notifications for userId: ${userId}`, error as string); throw new Error('Failed to retrieve notifications.'); } } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 86e281e9..84cdaaad 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -31,7 +31,8 @@ "react-router-dom": "^6.26.2", "react-transition-group": "^4.4.5", "recharts": "^3.2.1", - "satcheljs": "^4.3.1" + "satcheljs": "^4.3.1", + "tailwindcss": "^3.4.17" }, "devDependencies": { "@eslint/js": "^9.9.0", @@ -50,7 +51,6 @@ "globals": "^15.9.0", "jsdom": "^25.0.1", "postcss": "^8.5.3", - "tailwindcss": "^3.4.17", "typescript": "^5.7.3", "typescript-eslint": "^8.0.1", "vite": "^5.4.8", @@ -83,7 +83,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -286,6 +285,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -704,6 +704,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -727,6 +728,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -737,7 +739,6 @@ "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -800,6 +801,7 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -1513,7 +1515,6 @@ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.0.1.tgz", "integrity": "sha512-0VpNtO5cNe1/HQWMkl4OdncYK/mv9hnBte0Ew0n6DMzmo3Q3WzDFABHm6LeNTipt5zAyhQ6Ugjiu8aLaEjh1gg==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -2576,7 +2577,6 @@ "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">=18" } @@ -2593,7 +2593,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2616,7 +2615,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2639,7 +2637,6 @@ "os": [ "darwin" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2656,7 +2653,6 @@ "os": [ "darwin" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2673,7 +2669,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2690,7 +2685,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2707,7 +2701,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2724,7 +2717,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2741,7 +2733,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2758,7 +2749,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2775,7 +2765,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2792,7 +2781,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2815,7 +2803,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2838,7 +2825,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2861,7 +2847,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2884,7 +2869,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2907,7 +2891,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2930,7 +2913,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2950,7 +2932,6 @@ ], "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, - "peer": true, "dependencies": { "@emnapi/runtime": "^1.5.0" }, @@ -2973,7 +2954,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2993,7 +2973,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -3013,7 +2992,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -3047,6 +3025,7 @@ "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.9.0.tgz", "integrity": "sha512-yaN3brAnHRD+4KyyOsJyk49XUvj2wtbNACSqg0bz3u8t2VuzhC8Q5dfRnrSxjnnbDb+ienBnkn1TzQfE154vyg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@swc/helpers": "^0.5.0" } @@ -3064,7 +3043,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -3082,7 +3060,6 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -3095,7 +3072,6 @@ "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -3108,14 +3084,12 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -3133,7 +3107,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -3149,7 +3122,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -3212,8 +3184,7 @@ "version": "15.5.3", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.3.tgz", "integrity": "sha512-RSEDTRqyihYXygx/OJXwvVupfr9m04+0vH8vyy0HfZ7keRto6VX9BbEk0J2PUk0VGy6YhklJUSrgForov5F9pw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { "version": "15.5.3", @@ -3227,7 +3198,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3244,7 +3214,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3261,7 +3230,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3278,7 +3246,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3295,7 +3262,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3312,7 +3278,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3329,7 +3294,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3346,7 +3310,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3397,6 +3360,7 @@ "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", @@ -3690,7 +3654,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "license": "MIT", "optional": true, "engines": { @@ -3702,6 +3665,7 @@ "resolved": "https://registry.npmjs.org/@pothos/core/-/core-3.41.2.tgz", "integrity": "sha512-iR1gqd93IyD/snTW47HwKSsRCrvnJaYwjVNcUG8BztZPqMxyJKPAnjPHAgu1XB82KEdysrNqIUnXqnzZIs08QA==", "license": "ISC", + "peer": true, "peerDependencies": { "graphql": ">=15.1.0" } @@ -4151,6 +4115,7 @@ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -4408,6 +4373,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.17.tgz", "integrity": "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -4429,6 +4395,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz", "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==", "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4440,6 +4407,7 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -4536,6 +4504,7 @@ "integrity": "sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.44.1", "@typescript-eslint/types": "8.44.1", @@ -5878,6 +5847,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5977,14 +5947,12 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, "license": "MIT" }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -5998,7 +5966,6 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true, "license": "MIT" }, "node_modules/argparse": { @@ -6257,7 +6224,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6394,6 +6360,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001741", @@ -6517,7 +6484,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -6654,7 +6620,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -6679,7 +6644,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -6765,8 +6729,7 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/cliui": { "version": "8.0.1", @@ -6981,7 +6944,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -7023,7 +6985,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, "license": "MIT", "bin": { "cssesc": "bin/cssesc" @@ -7207,7 +7168,8 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz", "integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/date-fns": { "version": "4.1.0", @@ -7379,7 +7341,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true, "license": "Apache-2.0" }, "node_modules/dir-glob": { @@ -7398,7 +7359,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true, "license": "MIT" }, "node_modules/dom-accessibility-api": { @@ -7466,7 +7426,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, "license": "MIT" }, "node_modules/ee-first": { @@ -7651,6 +7610,7 @@ "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -8228,7 +8188,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -8245,7 +8204,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -8469,7 +8427,6 @@ "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -8490,7 +8447,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" @@ -8509,7 +8465,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -8519,7 +8474,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -8601,6 +8555,7 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==", "license": "MIT", + "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -8773,6 +8728,7 @@ "resolved": "https://registry.npmjs.org/graphql-yoga/-/graphql-yoga-5.16.0.tgz", "integrity": "sha512-/R2dJea7WgvNlXRU4F8iFwWd95Qn1mN+R+yC8XBs1wKjUzr0Pvv8cGYtt6UUcVHw5CiDEtu7iQY5oOe3sDAWCQ==", "license": "MIT", + "peer": true, "dependencies": { "@envelop/core": "^5.3.0", "@envelop/instrumentation": "^1.0.0", @@ -9177,7 +9133,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -9416,7 +9371,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/isomorphic-ws": { @@ -9432,7 +9386,6 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -9495,6 +9448,7 @@ "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cssstyle": "^4.1.0", "data-urls": "^5.0.0", @@ -9622,7 +9576,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, "license": "MIT", "engines": { "node": ">=14" @@ -9997,7 +9950,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -10008,6 +9960,7 @@ "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.13.7.tgz", "integrity": "sha512-aChaVU/DO5aRPmk1GX8L+whocagUUpBQqoPtJk+cm7UOXUk87J4PeWCh6nNmTTIfEhiR9DI/+FnA8dln/hTK7g==", "license": "MIT", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/mobx" @@ -10104,7 +10057,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, "license": "MIT", "dependencies": { "any-promise": "^1.0.0", @@ -10151,7 +10103,6 @@ "resolved": "https://registry.npmjs.org/next/-/next-15.5.3.tgz", "integrity": "sha512-r/liNAx16SQj4D+XH/oI1dlpv9tdKJ6cONYPwwcCC46f2NjpaRWY+EKCzULfgQYV6YKXjHBchff2IZBSlZmJNw==", "license": "MIT", - "peer": true, "dependencies": { "@next/env": "15.5.3", "@swc/helpers": "0.5.15", @@ -10204,7 +10155,6 @@ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.8.0" } @@ -10228,7 +10178,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -10332,7 +10281,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10374,7 +10322,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -10510,7 +10457,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/param-case": { @@ -10633,7 +10579,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -10670,7 +10615,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", @@ -10687,7 +10631,6 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, "license": "ISC" }, "node_modules/path-to-regexp": { @@ -10771,7 +10714,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10781,7 +10723,6 @@ "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -10815,6 +10756,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -10828,7 +10770,6 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, "license": "MIT", "dependencies": { "postcss-value-parser": "^4.0.0", @@ -10846,7 +10787,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", - "dev": true, "funding": [ { "type": "opencollective", @@ -10872,7 +10812,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -10908,7 +10847,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -10921,7 +10859,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -10947,7 +10884,6 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -11262,6 +11198,7 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", + "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -11342,7 +11279,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, "license": "MIT", "dependencies": { "pify": "^2.3.0" @@ -11366,7 +11302,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -11426,7 +11361,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -11875,7 +11811,6 @@ "hasInstallScript": true, "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.0", @@ -11918,7 +11853,6 @@ "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", "license": "Apache-2.0", "optional": true, - "peer": true, "engines": { "node": ">=8" } @@ -11929,7 +11863,6 @@ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "optional": true, - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -11941,7 +11874,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -11954,7 +11886,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -12221,7 +12152,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -12249,7 +12179,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -12346,7 +12275,6 @@ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", "license": "MIT", - "peer": true, "dependencies": { "client-only": "0.0.1" }, @@ -12384,7 +12312,6 @@ "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", @@ -12407,7 +12334,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -12495,7 +12421,6 @@ "version": "3.4.17", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", - "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -12533,7 +12458,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, "license": "MIT", "dependencies": { "any-promise": "^1.0.0" @@ -12543,7 +12467,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, "license": "MIT", "dependencies": { "thenify": ">= 3.1.0 < 4" @@ -12710,7 +12633,6 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true, "license": "Apache-2.0" }, "node_modules/ts-log": { @@ -12790,6 +12712,7 @@ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12987,6 +12910,7 @@ "resolved": "https://registry.npmjs.org/urql/-/urql-4.2.2.tgz", "integrity": "sha512-3GgqNa6iF7bC4hY/ImJKN4REQILcSU9VKcKL8gfELZM8mM5BnLH1BsCc8kBdnVGD1LIFOs4W3O2idNHhON1r0w==", "license": "MIT", + "peer": true, "dependencies": { "@urql/core": "^5.1.1", "wonka": "^6.3.2" @@ -13088,6 +13012,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.20.tgz", "integrity": "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -13774,7 +13699,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -13859,7 +13783,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -13878,6 +13801,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, diff --git a/frontend/src/main-page/header/Bell.tsx b/frontend/src/main-page/header/Bell.tsx index bd44edae..1240fbab 100644 --- a/frontend/src/main-page/header/Bell.tsx +++ b/frontend/src/main-page/header/Bell.tsx @@ -31,7 +31,6 @@ const BellButton = () => { const handleClick = async () => { // TODO: Remove hardcoded userId after /auth/session endpoint is fixed - const testUserId = "bcanuser33"; //hardcoded for testing // don't fetch if user isn't logged in (safe fallback) //if (!user?.userId) { @@ -43,7 +42,7 @@ const BellButton = () => { try { // call backend route const response = await api( - `/notifications/user/${testUserId}`, + `/notifications/user/${user?.userId}`, { method: "GET", } From eb8f6a068e4c21b0b9d7cae11ddaba85371da92b Mon Sep 17 00:00:00 2001 From: camila-carrillo Date: Wed, 3 Dec 2025 17:15:31 -0500 Subject: [PATCH 4/6] minor css change, fix infinite console.log statement --- frontend/src/external/bcanSatchel/store.ts | 1 - frontend/src/styles/notification.css | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/external/bcanSatchel/store.ts b/frontend/src/external/bcanSatchel/store.ts index 4083bcca..e4bf40de 100644 --- a/frontend/src/external/bcanSatchel/store.ts +++ b/frontend/src/external/bcanSatchel/store.ts @@ -92,6 +92,5 @@ export function persistToSessionStorage() { */ export function getAppStore() { const state = store(); - console.log('Current store.user:', state.user); // Debug: log current user when accessed return state; } diff --git a/frontend/src/styles/notification.css b/frontend/src/styles/notification.css index dd6e0f8d..23c2ec71 100644 --- a/frontend/src/styles/notification.css +++ b/frontend/src/styles/notification.css @@ -24,7 +24,8 @@ .popup-header h3 { font-size: 1.1rem; font-weight: 600; - color: #333; + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + color: black; margin: 0; } From f3943e9f584e1e3d313a5ca31ec06e885f991afc Mon Sep 17 00:00:00 2001 From: camila-carrillo Date: Wed, 3 Dec 2025 17:19:12 -0500 Subject: [PATCH 5/6] fix typescript unused import --- frontend/src/main-page/notifications/NotificationPopup.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/main-page/notifications/NotificationPopup.tsx b/frontend/src/main-page/notifications/NotificationPopup.tsx index 481527c6..fd3cfcae 100644 --- a/frontend/src/main-page/notifications/NotificationPopup.tsx +++ b/frontend/src/main-page/notifications/NotificationPopup.tsx @@ -15,7 +15,7 @@ const NotificationPopup: React.FC = observer(({ setOpenModal }) => { const store = getAppStore(); - const liveNotifications = store.notifications ?? []; + const liveNotifications: Notification[] = store.notifications ?? []; const handleDelete = async (notificationId: string) => { try { From 30ca400eb8b321af8c78dcdf7f6e8c168c0309d6 Mon Sep 17 00:00:00 2001 From: prooflesben Date: Wed, 3 Dec 2025 19:12:27 -0500 Subject: [PATCH 6/6] Changed the notifications so it only pops up relevant notis(notis that have their alert date as before the current date) --- .../notifications/__test__/notification.service.test.ts | 8 ++++---- backend/src/notifications/notification.controller.ts | 5 +++++ backend/src/notifications/notification.service.ts | 8 ++++++++ frontend/src/main-page/header/Bell.tsx | 2 +- .../src/main-page/notifications/NotificationPopup.tsx | 2 +- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/backend/src/notifications/__test__/notification.service.test.ts b/backend/src/notifications/__test__/notification.service.test.ts index 791af140..3837560e 100644 --- a/backend/src/notifications/__test__/notification.service.test.ts +++ b/backend/src/notifications/__test__/notification.service.test.ts @@ -273,17 +273,17 @@ describe('NotificationController', () => { - it('should throw error when notifications is null', async () => { + it('should throw error when notifications is null', async () => { // Arrange - Setup query mock to return no items const mockQueryResponse = { - Items: null // or undefined or [] + Items: [] // Empty array instead of null }; mockQuery.mockReturnValue({ promise: vi.fn().mockResolvedValue(mockQueryResponse) }); // Act & Assert - await expect(notificationService.getNotificationByUserId('nonexistent-user')) - .rejects.toThrow('Failed to retrieve notifications.'); + const result = await notificationService.getNotificationByUserId('nonexistent-user'); + expect(result).toEqual([]); }); it('should create notification with valid data in the set table', async () => { diff --git a/backend/src/notifications/notification.controller.ts b/backend/src/notifications/notification.controller.ts index fd1d45cd..f0aadfe4 100644 --- a/backend/src/notifications/notification.controller.ts +++ b/backend/src/notifications/notification.controller.ts @@ -21,6 +21,11 @@ export class NotificationController { return await this.notificationService.getNotificationByNotificationId(notificationId); } + @Get('/user/:userId/current') + async findCurrentByUser(@Param('userId') userId: string) { + return await this.notificationService.getCurrentNotificationsByUserId(userId); + } + // gets notifications by user id (sorted by most recent notifications first) @Get('/user/:userId') async findByUser(@Param('userId') userId: string) { diff --git a/backend/src/notifications/notification.service.ts b/backend/src/notifications/notification.service.ts index 5949315e..2a369be0 100644 --- a/backend/src/notifications/notification.service.ts +++ b/backend/src/notifications/notification.service.ts @@ -27,6 +27,14 @@ export class NotificationService { return notification; } + async getCurrentNotificationsByUserId(userId: string): Promise { + const notifactions = await this.getNotificationByUserId(userId); + + const currentTime = new Date(); + + return notifactions.filter(notification => new Date(notification.alertTime) <= currentTime); + } + // function that returns array of notifications by user id (sorted by most recent notifications first) async getNotificationByUserId(userId: string): Promise { diff --git a/frontend/src/main-page/header/Bell.tsx b/frontend/src/main-page/header/Bell.tsx index 01b1ce9d..5e06da94 100644 --- a/frontend/src/main-page/header/Bell.tsx +++ b/frontend/src/main-page/header/Bell.tsx @@ -30,7 +30,7 @@ const BellButton: React.FC = observer(({ setOpenModal, openModa // function that handles when button is clicked and fetches notifications const handleClick = async () => { const response = await api( - `/notifications/user/${store.user?.userId}`, + `/notifications/user/${store.user?.userId}/current`, { method: "GET", } diff --git a/frontend/src/main-page/notifications/NotificationPopup.tsx b/frontend/src/main-page/notifications/NotificationPopup.tsx index fd3cfcae..91a89f7b 100644 --- a/frontend/src/main-page/notifications/NotificationPopup.tsx +++ b/frontend/src/main-page/notifications/NotificationPopup.tsx @@ -33,7 +33,7 @@ const NotificationPopup: React.FC = observer(({ const fetchResponse = await api( - `/notifications/user/${store.user?.userId}`, + `/notifications/user/${store.user?.userId}/current`, { method: "GET", }