diff --git a/backend/controllers/friendsController.js b/backend/controllers/friendsController.js index b546501..d7f2fc9 100644 --- a/backend/controllers/friendsController.js +++ b/backend/controllers/friendsController.js @@ -27,7 +27,7 @@ const friendsLookup = async (req, res) => { } try { - const user = await User.findById(id).populate("friends"); + const user = await User.findById(id) if (!user) { return res.status(404).json({ msg: "User not found" }); } @@ -37,42 +37,14 @@ const friendsLookup = async (req, res) => { } }; -// Add a user to the friends list -const addFriends = async (req, res) => { - const { id } = req.params; - const { friendId } = req.body; - if (!checkIdIsValid(id, res) || !checkIdIsValid(friendId, res)) return; - - if (!checkAuthorization(req, id)) { - return res.status(403).json({ msg: "User not authorized" }); - } - - try { - const user = await User.findById(id); - const friend = await User.findById(friendId); - if (!user || !friend) { - return res.status(404).json({ err: "User or friend not found" }); - } - if (user.friends.includes(friendId)) { - return res.status(400).json({ err: "User already in friends list" }); - } - user.friends.push(friendId); - friend.friends.push(id); - await user.save(); - await friend.save(); - res.status(200).json({ msg: "Friend added to friends list" }); - } catch (err) { - res.status(500).json({ err: "Server error" }); - } -}; - // Accept a friend request const acceptFriendRequest = async (req, res) => { const { id } = req.params; // The user accepting the request const { friendIdentifier } = req.body; // The friend who sent the request const friendId = await getUserIdByUsernameOrEmail(friendIdentifier); - if (!checkIdIsValid(id, res) || !checkIdIsValid(friendId, res)) return; + if (!checkIdIsValid(id, res) || !checkIdIsValid(friendId.friendId, res)) + return; if (!checkAuthorization(req, id)) { return res.status(403).json({ msg: "User not authorized" }); @@ -80,34 +52,42 @@ const acceptFriendRequest = async (req, res) => { try { const user = await User.findById(id); - const friend = await User.findById(friendId); - + + const friend = await User.findById(friendId.friendId); + if (!user || !friend) { return res.status(404).json({ err: "User or friend not found" }); } // Check if a friend request from `friendId` exists in `user` - if (!user.friendsRequests.includes(friendId.toString())) { + if (user.friendsRequests.includes(friendId.username)) { return res.status(400).json({ err: "Friend request not sent" }); } - + + // Check if user is already friends with sender + if (user.friends.includes(friendIdentifier)) { + return res.status(400).json({err: `User already accepted request from ${friendId.username}` }) + } + // Add each other to the friends list - user.friends.push(friendId); - friend.friends.push(id); + user.friends.push(friendIdentifier); + friend.friends.push(user.username); // Remove the friend request from both users' `friendsRequests` and `sentFriendRequests` arrays user.friendsRequests = user.friendsRequests.filter( - (requestId) => requestId.toString() !== friendId.toString() + (requestId) => requestId !== friendIdentifier ); friend.sentFriendRequests = friend.sentFriendRequests.filter( - (requestId) => requestId.toString() !== id.toString() + (requestId) => requestId !== user.username ); // Save both the user and the friend after modifications await user.save(); await friend.save(); - res.status(200).json({ msg: "Friend request accepted and added to friends list" }); + res + .status(200) + .json({ msg: "Friend request accepted and added to friends list" }); } catch (err) { res.status(500).json({ err: "Server error", msg: err.message }); } @@ -115,11 +95,11 @@ const acceptFriendRequest = async (req, res) => { // Decline a friend request const declineFriendRequest = async (req, res) => { - const { id } = req.params; // The user declining the request - const { friendIdentifier } = req.body; // The friend who sent the request - const friendId = await getUserIdByUsernameOrEmail(friendIdentifier); + const { id, requestId } = req.params; // The user declining the request + // const { friendIdentifier } = req.body; // The friend who sent the request + const friendId = await getUserIdByUsernameOrEmail(requestId); - if (!checkIdIsValid(id, res) || !checkIdIsValid(friendId, res)) return; + if (!checkIdIsValid(id, res) || !checkIdIsValid(friendId.friendId, res)) return; if (!checkAuthorization(req, id)) { return res.status(403).json({ msg: "User not authorized" }); @@ -127,30 +107,32 @@ const declineFriendRequest = async (req, res) => { try { const user = await User.findById(id); - const friend = await User.findById(friendId); + const friend = await User.findById(friendId.friendId); if (!user || !friend) { return res.status(404).json({ err: "User or friend not found" }); } // Check if a friend request exists in `user` - if (!user.friendsRequests.includes(friendId.toString())) { + if (user.friendsRequests.includes(friendId.toString())) { return res.status(400).json({ err: "Friend request not sent" }); } // Remove the friend request from both users' `friendsRequests` and `sentFriendRequests` arrays user.friendsRequests = user.friendsRequests.filter( - (requestId) => requestId.toString() !== friendId.toString() + (requestId) => requestId !== requestId ); friend.sentFriendRequests = friend.sentFriendRequests.filter( - (requestId) => requestId.toString() !== id.toString() + (requestId) => requestId.toString() !== user.username ); // Save the changes await user.save(); await friend.save(); - res.status(200).json({ msg: "Friend request declined and removed from both users" }); + res + .status(200) + .json({ msg: "Friend request declined and removed from both users" }); } catch (err) { res.status(500).json({ err: "Server error", msg: err.message }); } @@ -165,50 +147,8 @@ const getUserIdByUsernameOrEmail = async (identifier) => { if (!user) { throw new Error("User not found"); } - - return user._id; -}; - -// Delete a friend request (for the recipient) -const deleteFriendRequest = async (req, res) => { - const { id } = req.params; - const { friendIdentifier } = req.body; - - try { - if (!checkIdIsValid(id, res)) return; - - const friendId = await getUserIdByUsernameOrEmail(friendIdentifier); - - if (!checkIdIsValid(friendId, res)) return; - - if (!checkAuthorization(req, id)) { - return res.status(403).json({ msg: "User not authorized" }); - } - - const user = await User.findById(id); - const friend = await User.findById(friendId); - - if (!user || !friend) { - return res.status(404).json({ err: "User or friend not found" }); - } - if (!user.friendsRequests.includes(friendId)) { - return res.status(400).json({ err: "Friend request not sent" }); - } - - // Remove the friend request from both users - user.friendsRequests = user.friendsRequests.filter( - (requestId) => requestId.toString() !== friendId.toString() - ); - friend.sentFriendRequests = friend.sentFriendRequests.filter( - (requestId) => requestId.toString() !== id.toString() - ); - await user.save(); - await friend.save(); - - res.status(200).json({ msg: "Friend request deleted" }); - } catch (err) { - return res.status(500).json({ err: "Server error" }); - } + return { friendId: user._id.toString(), friendname: user.username }; + // return user._id; }; // Send a friend request @@ -224,7 +164,7 @@ const sendFriendRequest = async (req, res) => { } const user = await User.findById(id); - const friend = await User.findById(friendId); + const friend = await User.findById(friendId.friendId); if (!user || !friend) { return res.status(404).json({ err: "User or friend not found" }); @@ -238,9 +178,9 @@ const sendFriendRequest = async (req, res) => { // Check if a friend request or friendship already exists if ( - user.sentFriendRequests.includes(friendId) || + user.sentFriendRequests.includes(friendId.friendId) || friend.friendsRequests.includes(id) || - user.friends.includes(friendId) || + user.friends.includes(friendId.friendId) || friend.friends.includes(id) ) { return res.status(400).json({ @@ -249,11 +189,12 @@ const sendFriendRequest = async (req, res) => { } // Add the `friendId` to the user's `sentFriendRequests` list - user.sentFriendRequests.push(friendId); + user.sentFriendRequests.push(friendId.friendname); // Add the `userId` to the friend's `friendsRequests` list - friend.friendsRequests.push(id); - + // needs conversion from friendID to friend name when displaying in frontend list + friend.friendsRequests.push(user.username ? user.username : "could not push name"); + console.log(user.username) // Save both the user and the friend await user.save(); await friend.save(); @@ -266,31 +207,35 @@ const sendFriendRequest = async (req, res) => { // Remove a friend from the list const removeFriend = async (req, res) => { - const { id } = req.params; - const { friendIdentifier } = req.body; - const friendId = await getUserIdByUsernameOrEmail(friendIdentifier); + const { id, friendName } = req.params; + const friendId = await getUserIdByUsernameOrEmail(friendName); if (!checkIdIsValid(id, res)) return; - if (!checkIdIsValid(friendId, res)) return; + if (!checkIdIsValid(friendId.friendId, res)) return; if (!checkAuthorization(req, id)) { return res.status(403).json({ msg: "User not authorized" }); } try { const user = await User.findById(id); - const friend = await User.findById(friendId); + const friend = await User.findById(friendId.friendId); + if (!user || !friend) { return res.status(404).json({ err: "User or friend not found" }); } - if (!user.friends.includes(friendId.toString())) { + if (user.friends.includes(friendId.username)) { return res.status(400).json({ err: "User not in friends list" }); } // Remove each other from the friends list - user.friends = user.friends.filter((fid) => fid.toString() !== friendId.toString()); - friend.friends = friend.friends.filter((uid) => uid.toString() !== id.toString()); + user.friends = user.friends.filter( + (fid) => fid !== friendName + ); + friend.friends = friend.friends.filter( + (uid) => uid !== user.username + ); // Save both users after the updates await user.save(); @@ -304,9 +249,13 @@ const removeFriend = async (req, res) => { // Remove a friend request (for the sender) const removeFriendRequest = async (req, res) => { - const { id } = req.params; - const { friendId } = req.body; - if (!checkIdIsValid(id, res) || !checkIdIsValid(friendId, res)) return; + const { id, friendname } = req.params; + // const { friendId } = req.body; + + const friendId = await getUserIdByUsernameOrEmail(friendname); + + if (!checkIdIsValid(id, res) || !checkIdIsValid(friendId.friendId, res)) + return; if (!checkAuthorization(req, id)) { return res.status(403).json({ msg: "User not authorized" }); @@ -314,53 +263,75 @@ const removeFriendRequest = async (req, res) => { try { const user = await User.findById(id); - const friend = await User.findById(friendId); + const friend = await User.findById(friendId.friendId); if (!user || !friend) { return res.status(404).json({ err: "User or friend not found" }); } - if (!user.sentFriendRequests.includes(friendId)) { + if (!user.sentFriendRequests.includes(friend.username)) { return res.status(400).json({ err: "Friend request not sent" }); } user.sentFriendRequests = user.sentFriendRequests.filter( - (requestId) => requestId.toString() !== friendId + (requestId) => requestId !== friendname ); friend.friendsRequests = friend.friendsRequests.filter( - (requestId) => requestId.toString() !== id + (requestId) => requestId !== user.username ); await user.save(); + await friend.save(); res.status(200).json({ msg: "Friend request removed" }); } catch (err) { res.status(500).json({ err: "Server error" }); } }; + +// List sent friend request (for the receiver) const getFriendRequest = async (req, res) => { const { id } = req.params; - if (!checkIdIsValid(id, res)) return; + if (!checkIdIsValid(id, res)) return; - if (!checkAuthorization(req, id)) { - return res.status(403).json({ msg: "User not authorized" }); + if (!checkAuthorization(req, id)) { + return res.status(403).json({ msg: "User not authorized" }); + } + + try { + const user = await User.findById(id); + if (!user) { + return res.status(404).json({ err: "User not found" }); } + res.status(200).json(user.friendsRequests); + } catch (err) { + res.status(500).json({ err: "Server error" }); + } +}; + +// List sent friend requests (for the sender) +const getSentFriendRequest = async (req, res) => { + const { id } = req.params; + if (!checkIdIsValid(id, res)) return; - try { - const user = await User.findById(id); - if (!user) { - return res.status(404).json({ err: "User not found" }); - } - res.status(200).json(user.friendsRequests); - } catch (err) { - res.status(500).json({ err: "Server error" }); + if (!checkAuthorization(req, id)) { + return res.status(403).json({ msg: "User not authorized" }); + } + + try { + const user = await User.findById(id); + if (!user) { + return res.status(404).json({ err: "User not found" }); } -} + res.status(200).json(user.sentFriendRequests); + } catch (err) { + res.status(500).json({ err: "Server error" }); + } +}; module.exports = { friendsLookup, sendFriendRequest, acceptFriendRequest, - addFriends, getFriendRequest, removeFriend, removeFriendRequest, - deleteFriendRequest, declineFriendRequest, + getSentFriendRequest, }; diff --git a/backend/models/User.js b/backend/models/User.js index ebbe089..c7c01c5 100644 --- a/backend/models/User.js +++ b/backend/models/User.js @@ -9,9 +9,9 @@ const userSchema = new mongoose.Schema({ tasks: [{ type: mongoose.Schema.Types.ObjectId, ref: "Task" }], groups: [{ type: mongoose.Schema.Types.ObjectId, ref: "Group" }], evidenceImages: [{ type: mongoose.Schema.Types.ObjectId, ref: "Image" }], - friends: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }], - friendsRequests: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }], - sentFriendRequests: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }], + friends: [{ type: mongoose.Schema.Types.String, ref: "User" }], + friendsRequests: [{ type: mongoose.Schema.Types.String, ref: "User" }], + sentFriendRequests: [{ type: mongoose.Schema.Types.String, ref: "User" }], lastReset: { type: Date }, resetToken : {type: String}, resetTokenExpiry: {type: Date}, diff --git a/backend/package-lock.json b/backend/package-lock.json index e3cb953..dc0b8aa 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -208,9 +208,9 @@ } }, "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==" }, "node_modules/@types/range-parser": { "version": "1.2.7", @@ -503,20 +503,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -702,22 +688,6 @@ "dev": true, "license": "MIT" }, - "node_modules/chai-http/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/chai-http/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -1275,7 +1245,7 @@ "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", @@ -1284,7 +1254,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", @@ -1325,12 +1295,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -1341,14 +1311,6 @@ "node": ">= 0.8" } }, - "node_modules/finalhandler/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2607,9 +2569,9 @@ } }, "node_modules/nodemon": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.4.tgz", - "integrity": "sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.5.tgz", + "integrity": "sha512-V5UtfYc7hjFD4SI3EzD5TR8ChAHEZ+Ns7Z5fBk8fAbTVAj+q3G+w7sHJrHxXBkVn6ApLVTljau8wfHwqmGUjMw==", "dependencies": { "chokidar": "^3.5.2", "debug": "^4", @@ -2872,11 +2834,11 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -3051,7 +3013,7 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.0.tgz", "integrity": "sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.18.0" diff --git a/backend/routes/friends.js b/backend/routes/friends.js index 98728ff..753cc39 100644 --- a/backend/routes/friends.js +++ b/backend/routes/friends.js @@ -6,35 +6,40 @@ const { sendFriendRequest, getFriendRequest, acceptFriendRequest, - addFriends, removeFriend, - deleteFriendRequest, + // friendNamesLookup, + // deleteFriendRequest, + removeFriendRequest, declineFriendRequest, + getSentFriendRequest, } = require("../controllers/friendsController"); // look up users current friends -router.get("/friends/:id", friendsLookup); - -// adding friends -router.post("/add-friends/:id", addFriends); +router.get("/:id", friendsLookup); // sending friend request router.post("/send-req/:id", sendFriendRequest); -// available friend requests +// available friend requests for reciever router.get("/friend-requests/:id", getFriendRequest); // accepting friend request router.post("/accept-req/:id", acceptFriendRequest); //declining friend request -router.delete("/decline-req/:id", declineFriendRequest); +router.delete("/decline-req/:id/:requestId", declineFriendRequest); //remove a friend -router.delete("/friend/remove/:id", removeFriend); +router.delete("/remove/:id/:friendName", removeFriend); //remove a friend from a friend request -router.delete("/request/remove/:id", deleteFriendRequest); +router.delete("/request/remove/:id/:friendname", removeFriendRequest); + +// available friend requests for sender +router.get("/sentfriend-requests/:id", getSentFriendRequest); + +// get friend names for sent requests +// router.get("/sentfriend-request-names/:id", friendNamesLookup); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ab96e5f..e8f8301 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -13,8 +13,9 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", - "axios": "^1.6.8", + "axios": "^1.7.4", "bootstrap": "^5.3.3", + "firebase": "^10.12.4", "react": "^18.3.1", "react-bootstrap": "^2.10.2", "react-dom": "^18.3.1", @@ -107,6 +108,11 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "node_modules/@babel/eslint-parser": { "version": "7.25.1", "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.1.tgz", @@ -2274,11 +2280,6 @@ "stylis": "4.2.0" } }, - "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, "node_modules/@emotion/cache": { "version": "11.13.1", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz", @@ -2457,6 +2458,577 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@firebase/analytics": { + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.7.tgz", + "integrity": "sha512-GE29uTT6y/Jv2EP0OjpTezeTQZ5FTCTaZXKrrdVGjb/t35AU4u/jiU+hUwUPpuK8fqhhiHkS/AawE3a3ZK/a9Q==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/installations": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.13.tgz", + "integrity": "sha512-aZ4wGfNDMsCxhKzDbK2g1aV0JKsdQ9FbeIsjpNJPzhahV0XYj+z36Y4RNLPpG/6hHU4gxnezxs+yn3HhHkNL8w==", + "dependencies": { + "@firebase/analytics": "0.10.7", + "@firebase/analytics-types": "0.8.2", + "@firebase/component": "0.6.8", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.2.tgz", + "integrity": "sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw==" + }, + "node_modules/@firebase/app": { + "version": "0.10.10", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.10.tgz", + "integrity": "sha512-sDqkdeFdVn5uygQm5EuIKOQ6/wxTcX/qKfm0MR46AiwLRHGLCDUMrXBkc8GhkK3ca2d6mPUSfPmndggo43D6PQ==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "idb": "7.1.1", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.7.tgz", + "integrity": "sha512-EkOeJcMKVR0zZ6z/jqcFTqHb/xq+TVIRIuBNGHdpcIuFU1czhSlegvqv2+nC+nFrkD8M6Xvd3tAlUOkdbMeS6A==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.14.tgz", + "integrity": "sha512-kK3bPfojAfXE53W+20rxMqIxrloFswXG9vh4kEdYL6Wa2IB3sD5++2dPiK3yGxl8oQiqS8qL2wcKB5/xLpEVEg==", + "dependencies": { + "@firebase/app-check": "0.8.7", + "@firebase/app-check-types": "0.5.2", + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz", + "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.2.tgz", + "integrity": "sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.2.40", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.40.tgz", + "integrity": "sha512-2L5MW4MH2ya7Wvw0hzWy3ZWeB4SqC5gYXDAV5AS1lBTL4zL3k8dsqJmry/cFV00GgkCI01WJbcXvFMCXJvgyow==", + "dependencies": { + "@firebase/app": "0.10.10", + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz", + "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==" + }, + "node_modules/@firebase/auth": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.7.8.tgz", + "integrity": "sha512-1KJlDrTrEEFTIBj9MxjAWjQ4skecBD4bmoayQ0l14QDbNc1a8qGbi+MFSJkH7O6VnGE6bTMcWSw6RrQNecqKaw==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0", + "undici": "6.19.7" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.13.tgz", + "integrity": "sha512-rV6TMxUU6wBBZ2ouDMtjJsJXeewtvYvVzslzt3/P7O/kxiWlreHT/2M/1guMiXKo3zk52XK3GqP0uM2bN7fEow==", + "dependencies": { + "@firebase/auth": "1.7.8", + "@firebase/auth-types": "0.12.2", + "@firebase/component": "0.6.8", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0", + "undici": "6.19.7" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz", + "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==" + }, + "node_modules/@firebase/auth-types": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.2.tgz", + "integrity": "sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.8.tgz", + "integrity": "sha512-LcNvxGLLGjBwB0dJUsBGCej2fqAepWyBubs4jt1Tiuns7QLbXHuyObZ4aMeBjZjWx4m8g1LoVI9QFpSaq/k4/g==", + "dependencies": { + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.7.tgz", + "integrity": "sha512-wjXr5AO8RPxVVg7rRCYffT7FMtBjHRfJ9KMwi19MbOf0vBf0H9YqW3WCgcnLpXI6ehiUcU3z3qgPnnU0nK6SnA==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.7.tgz", + "integrity": "sha512-R/3B+VVzEFN5YcHmfWns3eitA8fHLTL03io+FIoMcTYkajFnrBdS3A+g/KceN9omP7FYYYGTQWF9lvbEx6eMEg==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/database": "1.0.7", + "@firebase/database-types": "1.0.4", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.4.tgz", + "integrity": "sha512-mz9ZzbH6euFXbcBo+enuJ36I5dR5w+enJHHjy9Y5ThCdKUseqfDjW3vCp1YxE9zygFCSjJJ/z1cQ+zodvUcwPQ==", + "dependencies": { + "@firebase/app-types": "0.9.2", + "@firebase/util": "1.9.7" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.1.tgz", + "integrity": "sha512-WliQNa8GVcH6EWkH0NAf+uAnxNiBuH+G8Buzr2ZS1NznOhJDK/q6Hsjv5TzNrijLTAdEfj/wk9VEv994KDSjxg==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "@firebase/webchannel-wrapper": "1.0.1", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0", + "undici": "6.19.7" + }, + "engines": { + "node": ">=10.10.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.36.tgz", + "integrity": "sha512-NtoIm7CT9f+SFB7cPMCtyCSxZReh/+SII5X4TQH394S3dwhru9HIfvEOKAMuAnXsSsLH72jXPUgdsEAUqg6Oug==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/firestore": "4.7.1", + "@firebase/firestore-types": "3.0.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.2.tgz", + "integrity": "sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.7.tgz", + "integrity": "sha512-xaUsGI2kYrI8zJXgrNB7SrJKB8v1vJqR16YYi6g6dFTgBz4+VzWJFqqVU60BbdAWm6fXnUrg9gjlJQeqomT2Vg==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.8", + "@firebase/messaging-interop-types": "0.2.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0", + "undici": "6.19.7" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.13.tgz", + "integrity": "sha512-qcZvJO2ed6PAD+18DanVztw7WyQVQK43HoRhxusHAwDFvK/xY+mcGpj+IpfdxTNMBGCOIxKFp4Xqk/c2nubBlQ==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/functions": "0.11.7", + "@firebase/functions-types": "0.6.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.2.tgz", + "integrity": "sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w==" + }, + "node_modules/@firebase/installations": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.8.tgz", + "integrity": "sha512-57V374qdb2+wT5v7+ntpLXBjZkO6WRgmAUbVkRfFTM/4t980p0FesbqTAcOIiM8U866UeuuuF8lYH70D3jM/jQ==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/util": "1.9.7", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.8.tgz", + "integrity": "sha512-pI2q8JFHB7yIq/szmhzGSWXtOvtzl6tCUmyykv5C8vvfOVJUH6mP4M4iwjbK8S1JotKd/K70+JWyYlxgQ0Kpyw==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/installations": "0.6.8", + "@firebase/installations-types": "0.5.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.2.tgz", + "integrity": "sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.2.tgz", + "integrity": "sha512-Q1VuA5M1Gjqrwom6I6NUU4lQXdo9IAQieXlujeHZWvRt1b7qQ0KwBaNAjgxG27jgF9/mUwsNmO8ptBCGVYhB0A==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.10", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.10.tgz", + "integrity": "sha512-fGbxJPKpl2DIKNJGhbk4mYPcM+qE2gl91r6xPoiol/mN88F5Ym6UeRdMVZah+pijh9WxM55alTYwXuW40r1Y2Q==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/installations": "0.6.8", + "@firebase/messaging-interop-types": "0.2.2", + "@firebase/util": "1.9.7", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.10.tgz", + "integrity": "sha512-FXQm7rcowkDm8kFLduHV35IRYCRo+Ng0PIp/t1+EBuEbyplaKkGjZ932pE+owf/XR+G/60ku2QRBptRGLXZydg==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/messaging": "0.12.10", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz", + "integrity": "sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA==" + }, + "node_modules/@firebase/performance": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.8.tgz", + "integrity": "sha512-F+alziiIZ6Yn8FG47mxwljq+4XkgkT2uJIFRlkyViUQRLzrogaUJW6u/+6ZrePXnouKlKIwzqos3PVJraPEcCA==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/installations": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.8.tgz", + "integrity": "sha512-o7TFClRVJd3VIBoY7KZQqtCeW0PC6v9uBzM6Lfw3Nc9D7hM6OonqecYvh7NwJ6R14k+xM27frLS4BcCvFHKw2A==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/performance": "0.6.8", + "@firebase/performance-types": "0.2.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.2.tgz", + "integrity": "sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.8.tgz", + "integrity": "sha512-AMLqe6wfIRnjc6FkCWOSUjhc1fSTEf8o+cv1NolFvbiJ/tU+TqN4pI7pT+MIKQzNiq5fxLehkOx+xtAQBxPJKQ==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/installations": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.8.tgz", + "integrity": "sha512-UxSFOp6dzFj2AHB8Bq/BYtbq5iFyizKx4Rd6WxAdaKYM8cnPMeK+l2v+Oogtjae+AeyHRI+MfL2acsfVe5cd2A==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/remote-config": "0.4.8", + "@firebase/remote-config-types": "0.3.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz", + "integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA==" + }, + "node_modules/@firebase/storage": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.1.tgz", + "integrity": "sha512-L6AJ5tWgHSi2g/gbc/2Pbm3qxmoEg9THmPIOpRsLwuz9LPeWbhyMQeGlqxWqtZGQO/z/LMjGYadNlupQj0HNfw==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0", + "undici": "6.19.7" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.11.tgz", + "integrity": "sha512-EEa9jgm/aRVIGSD0ByYAsZ0tvEKfVwSp9uFoa/97BISGWGjSNPIWjenaDvpDZ7aL8OxaGIpwuk700aHy7/T0Ug==", + "dependencies": { + "@firebase/component": "0.6.8", + "@firebase/storage": "0.13.1", + "@firebase/storage-types": "0.8.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz", + "integrity": "sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.7.tgz", + "integrity": "sha512-fBVNH/8bRbYjqlbIhZ+lBtdAAS4WqZumx03K06/u7fJSpz1TGjEMm1ImvKD47w+xaFKIP2ori6z8BrbakRfjJA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/vertexai-preview": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@firebase/vertexai-preview/-/vertexai-preview-0.0.3.tgz", + "integrity": "sha512-KVtUWLp+ScgiwkDKAvNkVucAyhLVQp6C6lhnVEuIg4mWhWcS3oerjAeVhZT4uNofKwWxRsOaB2Yec7DMTXlQPQ==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/component": "0.6.8", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.7", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.1.tgz", + "integrity": "sha512-jmEnr/pk0yVkA7mIlHNnxCi+wWzOFUg0WyIotgkKAb2u1J7fAeDBcVNSTjTihbAYNusCLQdW5s9IJ5qwnEufcQ==" + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", + "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@grpc/proto-loader/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@grpc/proto-loader/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -3739,11 +4311,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/@jest/transform/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, "node_modules/@jest/transform/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -4057,6 +4624,60 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, "node_modules/@react-aria/ssr": { "version": "3.9.5", "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.5.tgz", @@ -4723,6 +5344,37 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/@testing-library/react/node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/@testing-library/react/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -4933,9 +5585,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.12", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", - "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "version": "29.5.13", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.13.tgz", + "integrity": "sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==", "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" @@ -5012,9 +5664,9 @@ "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==" }, "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, "node_modules/@types/q": { "version": "1.5.8", @@ -5731,7 +6383,6 @@ "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -6499,20 +7150,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/bonjour-service": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", @@ -7028,9 +7665,9 @@ } }, "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "node_modules/cookie": { "version": "0.6.0", @@ -7323,6 +7960,15 @@ "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" }, + "node_modules/css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/cssdb": { "version": "7.11.2", "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.2.tgz", @@ -7576,37 +8222,6 @@ "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" }, - "node_modules/deep-equal": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", - "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -8496,6 +9111,37 @@ "deep-equal": "^2.0.5" } }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/eslint-plugin-react": { "version": "7.35.2", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.2.tgz", @@ -8980,7 +9626,7 @@ "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", @@ -8989,7 +9635,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", @@ -9184,12 +9830,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -9208,14 +9854,6 @@ "ms": "2.0.0" } }, - "node_modules/finalhandler/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -9254,6 +9892,40 @@ "node": ">=8" } }, + "node_modules/firebase": { + "version": "10.13.1", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.13.1.tgz", + "integrity": "sha512-L5BSkmvB2dzCUMpr8i/O8WMJC3Nqj5Ld8Wj/qnak+tz2Ga+JH6/FO93xArg9IGhktCrPXVODoWp6t9ybdgmXCA==", + "dependencies": { + "@firebase/analytics": "0.10.7", + "@firebase/analytics-compat": "0.2.13", + "@firebase/app": "0.10.10", + "@firebase/app-check": "0.8.7", + "@firebase/app-check-compat": "0.3.14", + "@firebase/app-compat": "0.2.40", + "@firebase/app-types": "0.9.2", + "@firebase/auth": "1.7.8", + "@firebase/auth-compat": "0.5.13", + "@firebase/database": "1.0.7", + "@firebase/database-compat": "1.0.7", + "@firebase/firestore": "4.7.1", + "@firebase/firestore-compat": "0.3.36", + "@firebase/functions": "0.11.7", + "@firebase/functions-compat": "0.3.13", + "@firebase/installations": "0.6.8", + "@firebase/installations-compat": "0.2.8", + "@firebase/messaging": "0.12.10", + "@firebase/messaging-compat": "0.2.10", + "@firebase/performance": "0.6.8", + "@firebase/performance-compat": "0.2.8", + "@firebase/remote-config": "0.4.8", + "@firebase/remote-config-compat": "0.2.8", + "@firebase/storage": "0.13.1", + "@firebase/storage-compat": "0.3.11", + "@firebase/util": "1.9.7", + "@firebase/vertexai-preview": "0.0.3" + } + }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -10239,6 +10911,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-from/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "extraneous": true, + "engines": { + "node": ">=4" + } + }, "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", @@ -14587,6 +15268,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -14612,6 +15298,11 @@ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -16827,6 +17518,29 @@ "react": ">=0.14.0" } }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -16876,11 +17590,11 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -17501,9 +18215,9 @@ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "dependencies": { "regenerate": "^1.4.2" }, @@ -17692,11 +18406,6 @@ } } }, - "node_modules/resolve-url-loader/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, "node_modules/resolve-url-loader/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", @@ -18181,54 +18890,10 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.0.tgz", "integrity": "sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-static/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-static/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/serve-static/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serve-static/node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" @@ -19571,15 +20236,23 @@ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" }, + "node_modules/undici": { + "version": "6.19.7", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.7.tgz", + "integrity": "sha512-HR3W/bMGPSr90i8AAp2C4DM3wChFdJPLrWYpIS++LxS8K+W535qftjt+4MyjNYHeWabMj1nvtmLIi7l++iq91A==", + "engines": { + "node": ">=18.17" + } + }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "engines": { "node": ">=4" } @@ -19597,9 +20270,9 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", "engines": { "node": ">=4" } @@ -19752,11 +20425,6 @@ "node": ">=10.12.0" } }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, "node_modules/v8-to-istanbul/node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index 2f7bde7..d806449 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,8 +9,9 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", - "axios": "^1.6.8", + "axios": "^1.7.4", "bootstrap": "^5.3.3", + "firebase": "^10.12.4", "react": "^18.3.1", "react-bootstrap": "^2.10.2", "react-dom": "^18.3.1", @@ -22,7 +23,7 @@ "web-vitals": "^2.1.4" }, "scripts": { - "start": "GENERATE_SOURCEMAP=false && react-scripts start", + "start": "set GENERATE_SOURCEMAP=false && react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" diff --git a/frontend/src/App.js b/frontend/src/App.js index ad54e8d..40cd15d 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,36 +1,57 @@ -import React from 'react'; -import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; -import { SharedStateProvider } from './Context/SharedStateContext'; -import { useAuthContext } from './hooks/useAuthContext'; -import Home from './pages/Home'; -import TaskDetails from './pages/TaskDetails'; -import Groups from './pages/Groups'; -import UserSignUp from './pages/SignUp'; -import Login from './pages/Login'; -import ForgotPassword from './components/AuthComponents/ForgotPassword'; -import ResetPassword from './components/AuthComponents/ResetPassword'; +import React from "react"; +import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; +import { SharedStateProvider } from "./Context/SharedStateContext"; +import { useAuthContext } from "./hooks/useAuthContext"; +import Home from "./pages/Home"; +import Friends from "./pages/Friends"; +import TaskDetails from "./pages/TaskDetails"; +import Groups from "./pages/Groups"; +import UserSignUp from "./pages/SignUp"; +import Login from "./pages/Login"; +import ForgotPassword from "./components/AuthComponents/ForgotPassword"; +import ResetPassword from "./components/AuthComponents/ResetPassword"; +import { FriendRequestsProvider } from "./components/FriendComponents/FriendRequestContext"; function App() { const { user } = useAuthContext(); return (
- - -
- - : } /> - : } /> - : } /> - : } /> - : } /> - } /> - } /> - } /> {/* Catch-all route */} - -
-
-
+ + + +
+ + : } /> + : } + /> + : } /> + : } + /> + : } + /> + } /> + } + /> + : } + /> + } />{" "} + {/* Catch-all route */} + +
+
+
+
); } diff --git a/frontend/src/Context/SharedStateContext.js b/frontend/src/Context/SharedStateContext.js index a86b5c7..1f1fa05 100644 --- a/frontend/src/Context/SharedStateContext.js +++ b/frontend/src/Context/SharedStateContext.js @@ -161,4 +161,4 @@ const SharedStateProvider = ({ children }) => { ); }; -export { SharedStateContext, SharedStateProvider }; +export { SharedStateContext, SharedStateProvider }; \ No newline at end of file diff --git a/frontend/src/Images/Friends_Page_Images/add-friend-svgrepo-com.svg b/frontend/src/Images/Friends_Page_Images/add-friend-svgrepo-com.svg new file mode 100644 index 0000000..363ffb2 --- /dev/null +++ b/frontend/src/Images/Friends_Page_Images/add-friend-svgrepo-com.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/Images/Logov3.PNG b/frontend/src/Images/Logov3.PNG new file mode 100644 index 0000000..090ccf9 Binary files /dev/null and b/frontend/src/Images/Logov3.PNG differ diff --git a/frontend/src/components/FriendComponents/FriendList.css b/frontend/src/components/FriendComponents/FriendList.css new file mode 100644 index 0000000..b919fb2 --- /dev/null +++ b/frontend/src/components/FriendComponents/FriendList.css @@ -0,0 +1,81 @@ +/* FriendList.css */ + +.friends-list-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 20px; + border: 2px solid #83afe8; + border-radius: 25px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + width: 1500px; + height: 600px; + margin: 20px auto; + background-color: #022d66; + +} + +.friends-list-title { + position: relative; + bottom: 50px; + right: 50px; + margin-bottom: 20px; + color: #fffefe; + font-size: 28px; + text-align: center; +} + +.friends-list-alert { + color: #999; /* Light gray for no friends found message */ + font-size: 18px; + text-align: center; +} + +.friends-list-ul { + list-style-type: none; /* Remove bullet points */ + padding: 0; /* Remove default padding */ + width: 100%; + color: whitesmoke + +} + +.friends-list-listcontainer{ + background-color: #12253d; + border-radius: 25px; + width: 600px; + height: 500px; + z-index: +1; + +} + +.friends-list-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; + border-bottom: 1px solid #eee; /* Separator between friends */ + font-size: 18px; +} + +.friends-list-item:last-child { + border-bottom: none; /* Remove border for the last item */ +} + +.friends-list-delete-button { + background-color: #ff4d4d; /* Red background for delete button */ + color: white; + border: none; + border-radius: 4px; + padding: 5px 10px; + cursor: pointer; + transition: background-color 0.3s; +} + +.friends-list-delete-button:hover { + background-color: #ff1a1a; /* Darker red on hover */ +} + +.friends-list-delete-button:focus { + outline: none; /* Remove default outline */ +} \ No newline at end of file diff --git a/frontend/src/components/FriendComponents/FriendList.js b/frontend/src/components/FriendComponents/FriendList.js new file mode 100644 index 0000000..bad7c4d --- /dev/null +++ b/frontend/src/components/FriendComponents/FriendList.js @@ -0,0 +1,73 @@ +import React, { useState, useEffect } from 'react'; +import createAxiosInstance from '../../axiosInstance'; +import { useAuthContext } from "../../hooks/useAuthContext" +import './FriendList.css'; +import { useFriendRequests } from './FriendRequestContext'; + +const FriendsList = () => { + const {friends, setFriends} = useFriendRequests(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const { user: currentUser } = useAuthContext(); + const axiosInstance = createAxiosInstance(currentUser?.token); + const userId = currentUser?.id; + + // Directly extract user ID from the context + // const userId = "66ea2d2920dbbcc14dbb74ce"; + + useEffect(() => { + const fetchFriends = async () => { + setLoading(true); + setError(''); + try { + const response = await axiosInstance.get(`/api/friends/${userId}`) + setFriends(response.data); + } catch (err) { + setError('Error fetching friends list'); + } + setLoading(false); + }; + + fetchFriends(); + }, [userId]); + + const deleteFriend = async (friendName) => { + if (window.confirm('Are you sure you want to delete this friend?')) { + setLoading(true); + setError(''); + try { + console.log("requesting deletion:", friendName) + await axiosInstance.delete(`/api/friends/remove/${userId}/${friendName}`); + // Remove the friend from the local state + setFriends(friends?.filter(friend => friend !== friendName)); + } catch (err) { + setError('Error deleting friend'); + } + setLoading(false); + } + }; + + + + return ( +
+

List of Friends

+ {friends?.length === 0 ? ( +

No friends found.

+ ) : ( +
+ +
+ )} +
+ ); +}; + +export default FriendsList; \ No newline at end of file diff --git a/frontend/src/components/FriendComponents/FriendRequestContext.js b/frontend/src/components/FriendComponents/FriendRequestContext.js new file mode 100644 index 0000000..32d9fbf --- /dev/null +++ b/frontend/src/components/FriendComponents/FriendRequestContext.js @@ -0,0 +1,16 @@ +import { createContext, useState, useContext } from "react"; + +const FriendRequestsContext = createContext(); + +export const FriendRequestsProvider = ({ children }) => { + const [sentFriendRequests, setSentFriendRequests] = useState([]); + const [friends, setFriends] = useState([]); + + return ( + + {children} + + ); +}; + +export const useFriendRequests = () => useContext(FriendRequestsContext); diff --git a/frontend/src/components/FriendComponents/FriendRequestList.css b/frontend/src/components/FriendComponents/FriendRequestList.css new file mode 100644 index 0000000..532b875 --- /dev/null +++ b/frontend/src/components/FriendComponents/FriendRequestList.css @@ -0,0 +1,358 @@ +/* FriendRequestList.css */ + +/* .friend-requests-container { + width: 600px; + margin: 20px auto; + padding: 20px; + border: 2px solid #83afe8; + border-radius: 25px; + background-color: #12253d; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + +} + +.friend-request-title, +.sent-request-title { + font-size: 24px; + margin-bottom: 10px; + color: #ffffff; +} + +.friend-request-loading, +.sent-request-loading { + font-size: 16px; + color: #666; +} + +.friend-request-list, +.sent-request-list { + list-style-type: none; + padding: 0; +} + +.friend-request-list-item, +.sent-request-list-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; + border: 1px solid #ddd; + border-radius: 5px; + margin-bottom: 10px; + background-color: #fff; + transition: background-color 0.2s; +} + +.friend-request-list-item:hover, +.sent-request-list-item:hover { + background-color: #f1f1f1; +} + +.friend-request-accept-button, +.friend-request-decline-button, +.friend-request-delete-button, +.sent-request-remove-button { + padding: 6px 12px; + border: none; + border-radius: 5px; + cursor: pointer; + font-weight: bold; + transition: background-color 0.2s; +} + +.friend-request-accept-button { + background-color: #4caf50; + color: white; +} + +.friend-request-decline-button { + background-color: #f44336; + color: white; +} + +.friend-request-delete-button { + background-color: #ff9800; + color: white; +} + +.sent-request-remove-button { + background-color: #2196f3; + color: white; +} + +.friend-request-accept-button:hover, +.friend-request-decline-button:hover, +.friend-request-delete-button:hover, +.sent-request-remove-button:hover { + opacity: 0.8; +} + +.friend-request-alert, +.sent-request-alert { + color: #888; + text-align: center; + font-size: 16px; + margin: 10px 0; +} + +.sent-request-error { + text-align: center; + font-size: 16px; + margin-top: 20px; + color: red; +} + + + +.friend-request-tabs.active{ + + background-color: #4caf50; +} + +.sent-request-tabs.active{ + + background-color: #4caf50; +} + +.friend-request-tabs{ + display: none; +} + +.sent-request-tabs{ + + display: none; +} */ + + + +/* General Container */ +.friend-requests-container { + width: 600px; + margin: 20px auto; + padding: 20px; + border: 2px solid #83afe8; + border-radius: 25px; + background-color: #12253d; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +/* Tabs Navigation */ +.friend-requests-tabs { + display: flex; + justify-content: space-between; + margin-bottom: 20px; +} + +.friend-requests-tab, +.sent-requests-tab { + + background-color: #022d66; + color: white; + height: 60px; + border: 2px solid #83afe8; + border-radius: 20px; + cursor: pointer; + transition: background-color 0.3s ease; + font-weight: normal; + font-size: 25px; + text-align: center; + margin-top: 5%; + margin-bottom: 5%; + width: 250px; + + position: relative; + +} + +.friend-requests-tab-active, +.sent-requests-tab-active { + background-color: #12253d; + color: white; + border-radius: 20px; + border: 2px solid #83afe8; + width: 250px; + font-size: 25px; + position: relative; + margin-top: 5%; + margin-bottom: 5%; + + +} + +.friend-requests-tab:hover, +.sent-requests-tab:hover { + background-color: #0056b3; +} + +.friend-requests-tab-active:hover, +.sent-requests-tab-active:hover { + background-color: #0056b3; +} + +/* Active Tab Content */ +.friend-requests-active, +.sent-requests-active { + display: block; +} + +.friend-requests, +.sent-requests { + display: none; +} + +/* Friend Requests Section */ +.friend-request-title, +.sent-request-title { + font-size: 24px; + font-weight: bold; + margin-bottom: 15px; + color: #ffffff; +} + +.friend-request-list, +.sent-request-list { + list-style-type: none; + padding: 0; +} + +.friend-request-list-item, +.sent-request-list-item { + background-color: #83afe8; + margin: 10px 0; + padding: 15px; + border-radius: 50px; + border: 1px solid #4f729d; + display: flex; + width: 90%; + align-items: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.friend-request-list-item:hover, +.sent-request-list-item:hover { + background-color: #f1f1f1; +} + +.friend-request-alert, +.sent-request-alert { + font-style: italic; + color: #777; +} + +.friend-request-accept-button, +.friend-request-decline-button, +.sent-request-remove-button { + padding: 8px 16px; + margin-left: 10px; + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.3s ease; +} + +.friend-request-accept-button { + background-color: #28a745; + color: white; + border-radius: 100%; + height: 30px; +} + +.friend-request-decline-button { + background-color: #dc3545; + border-radius: 100%; + height: 30px; +} + +.sent-request-remove-button { + background-color: #007bff; + border-radius: 100%; + height: 30px; +} + +.friend-request-accept-button:hover, +.friend-request-decline-button:hover, +.sent-request-remove-button:hover { + opacity: 0.8; +} + +.friend-request-loading, +.sent-request-loading { + font-size: 16px; + color: #007bff; +} + +.friend-request-error, +.sent-request-error { + font-size: 16px; + color: #dc3545; +} + +/* Loading Spinner (Optional) */ +.friend-request-loading::after, +.sent-request-loading::after { + content: "..."; + animation: dot-blink 1s infinite step-end; + +} + +.friend-request-list-container{ +background-color: #4f729d; +border-color: #022d66; +height: 150px; +border-radius: 20px; +display: flex; +justify-content: space-between; +align-items: center; +flex-direction: column; + +} + +.sent-request-list-container{ + background-color: #4f729d; + border-color: #022d66; + height: 150px; + border-radius: 20px; + display: flex; + justify-content: space-between; + align-items: center; + flex-direction: column; + + + } + +@keyframes dot-blink { + 0% { content: '.'; } + 33% { content: '..'; } + 66% { content: '...'; } +} + +/* Mobile Responsiveness */ +@media (max-width: 600px) { + .friend-requests-container { + padding: 10px; + } + + .friend-requests-tabs { + flex-direction: column; + } + + .friend-requests-tab, + .sent-requests-tab { + width: 100%; + margin-bottom: 10px; + } + + .friend-request-list-item, + .sent-request-list-item { + flex-direction: column; + text-align: center; + color: #0056b3; + } + + .friend-request-accept-button, + .friend-request-decline-button, + .sent-request-remove-button { + margin-top: 10px; + } +} diff --git a/frontend/src/components/FriendComponents/FriendRequestList.js b/frontend/src/components/FriendComponents/FriendRequestList.js new file mode 100644 index 0000000..148ee4b --- /dev/null +++ b/frontend/src/components/FriendComponents/FriendRequestList.js @@ -0,0 +1,210 @@ +import React, { useState, useEffect } from "react"; +import { useAuthContext } from "../../hooks/useAuthContext"; +import createAxiosInstance from "../../axiosInstance"; +import "./FriendRequestList.css"; // Make sure to style the new layout here +import { useFriendRequests } from "./FriendRequestContext"; + +//User recieved friend request components +const FriendRequestComponent = () => { + const [friendRequests, setFriendRequests] = useState([]); + const {friends, setFriends} = useFriendRequests(); + const {sentFriendRequests, setSentFriendRequests} = useFriendRequests(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(""); + const [tab, setTab] = useState(1); + const { user: currentUser } = useAuthContext(); + const userId = currentUser?.id; + const axiosInstance = createAxiosInstance(currentUser?.token); + + const switchTab = (index) => { + setTab(index); + }; + + useEffect(() => { + const loadFriendRequests = async () => { + setLoading(true); + setError(""); + try { + const response = await axiosInstance.get( + `/api/friends/friend-requests/${userId}` + ); + const friendRequestsList = response.data; + setFriendRequests(friendRequestsList); + } catch (err) { + setError("Error fetching friend requests"); + } + setLoading(false); + }; + + loadFriendRequests(); + }, [userId]); + + console.log(friendRequests ? friendRequests : "undefined"); + + const handleAcceptRequest = async (requestId) => { + setLoading(true); + setError(""); + // console.log(requestId); + try { + await axiosInstance.post(`/api/friends/accept-req/${userId}`, { + friendIdentifier: requestId, + }); + // await axiosInstance.post(`/api/friends/add-friends/${userId}`,{ + // friendId: requestId + // }); + setFriendRequests(friendRequests.filter((req) => req !== requestId)); + setFriends([...friends, requestId]) + } catch (err) { + setError("Error accepting friend request"); + } + setLoading(false); + }; + + + const handleDeclineRequest = async (requestId) => { + console.log(userId, requestId); + setLoading(true); + setError(""); + try { + await axiosInstance.delete( + `/api/friends/decline-req/${userId}/${requestId}` + ); + setFriendRequests(friendRequests.filter((req) => req !== requestId)); + } catch (err) { + setError("Error declining friend request"); + } + setLoading(false); + }; + + //User sent friend requests components + + useEffect(() => { + const loadSentFriendRequests = async () => { + setLoading(true); + setError(""); + try { + const response = await axiosInstance.get( + `/api/friends/sentfriend-requests/${userId}` + ); + // const sentFriendRequestsList = response.data; // assigning a new value + // console.log(sentFriendRequestsList); + console.log(response.data); + // const FriendName = await axiosInstance.get(`/api/friends/sentfriend-requests-names/${userId}`); + + // console.log(FriendName.data); + setSentFriendRequests(response.data); + } catch (err) { + setError("Error fetching friend requests"); + } + setLoading(false); + }; + + loadSentFriendRequests(); + }, [userId]); + + const handleRemoveRequest = async (friendname) => { + setLoading(true); + setError(""); + try { + await axiosInstance.delete( + `/api/friends/request/remove/${userId}/${friendname}` + ); + setSentFriendRequests( + sentFriendRequests.filter((req) => req !== friendname) + ); + } catch (err) { + setError("Error deleting friend request"); + } + setLoading(false); + }; + + return ( +
+
+ + + +
+ +
+

Friend Requests

+ {loading ? ( +

Loading...

+ ) : ( + + )} +
+ +
+

Sent Friend Requests

+ {loading ? ( +

Loading...

+ ) : ( + + )} + + {error && ( +

+ {error} +

+ )} +
+
+ ); +}; + +export default FriendRequestComponent; diff --git a/frontend/src/components/FriendComponents/FriendSearch.css b/frontend/src/components/FriendComponents/FriendSearch.css new file mode 100644 index 0000000..d120c18 --- /dev/null +++ b/frontend/src/components/FriendComponents/FriendSearch.css @@ -0,0 +1,117 @@ +.send-request-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 20px; + border: 2px solid #83afe8; + border-radius: 20px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + width: 600px; + height: 300px; + margin: 20px auto; + background-color: #12253d; + } + + .send-request-title { + position: relative; + right: 140px; + bottom: 140px; + margin-bottom: 45px; + color: #ffffff; + font-size: 20px; + text-align: center; + font-weight: normal; + + + } + + .send-request-input { + padding: 10px; + border: 1px solid #ccc; + border-radius: 20px; + width: 100%; + height: 100%; + box-sizing: border-box; + margin-bottom: 10px; + font-size: 20px; + transition: border-color 0.3s; + } + + .send-request-input:focus { + border-color: #007bff; /* Blue border on focus */ + outline: none; /* Remove default outline */ + } + + .send-request-button { + position: relative; + right: 30px; + padding: 10px 15px; + border: none; + border-radius: 20px; + background-color: #83afe8; + color: white; + font-size: 20px; + cursor: pointer; + transition: background-color 0.3s; + width: 30%; /* Full width button */ + + } + + .send-request-button:disabled { + background-color: #ccc; /* Grey background for disabled state */ + cursor: not-allowed; /* Change cursor to indicate disabled state */ + } + + .send-request-button:hover:not(:disabled) { + background-color: #0056b3; /* Darker blue on hover */ + } + + .send-request-successmsg, + .send-request-errormsg { + margin: 5px 0; + font-size: 14px; + text-align: center; /* Center the messages */ + } + + .send-request-successmsg { + color: green; /* Green for success messages */ + } + + .send-request-errormsg { + color: red; /* Red for error messages */ + } + + .send-request-searchbar{ + + position: relative; + bottom: 50px; + display: flex; + flex-direction: row; + width:100%; + + + } + + .send-request-titlecontainer{ + position: relative; + right: 140px; + bottom: 70px; + border-radius: 20px; + background-color: #ffffff; + width: 50%; + height: 30%; + z-index: -1; + background-color: #12253d; + border: 2px solid #83afe8; + } + + .send-request-intro{ + position: relative; + bottom: 100px; + color: white; + text-align: left; + font-size: medium; + } + + \ No newline at end of file diff --git a/frontend/src/components/FriendComponents/FriendSearch.js b/frontend/src/components/FriendComponents/FriendSearch.js new file mode 100644 index 0000000..646f2a7 --- /dev/null +++ b/frontend/src/components/FriendComponents/FriendSearch.js @@ -0,0 +1,74 @@ +import React, { useState } from "react"; +import { useAuthContext } from "../../hooks/useAuthContext"; +import createAxiosInstance from "../../axiosInstance"; +import "./FriendSearch.css"; // Make sure to style the new layout here +import { useFriendRequests } from "./FriendRequestContext"; + +const FriendSearchComponent = () => { + const [newRequestReceiver, setNewRequestReceiver] = useState(""); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(""); + const [statusMessage, setStatusMessage] = useState(""); // For status feedback + const { user: currentUser } = useAuthContext(); + const userId = currentUser?.id; + const axiosInstance = createAxiosInstance(currentUser?.token); + const {sentFriendRequests, setSentFriendRequests} = useFriendRequests(); + + + const handleSendRequest = async () => { + if (newRequestReceiver) { + setLoading(true); + setError(""); + try { + await axiosInstance.post(`/api/friends/send-req/${userId}`, { + friendIdentifier: newRequestReceiver, + }); + setNewRequestReceiver(""); + setStatusMessage("Friend request sent successfully!"); + setSentFriendRequests([...sentFriendRequests, newRequestReceiver]) + } catch (err) { + setError("Error sending friend request"); + } + setLoading(false); + } + }; + + return ( +
+
+

Send Friend Request

+

+ Enter the username of another user to send a friend request to the + recipient +

+
+ setNewRequestReceiver(e.target.value)} + placeholder="Enter username" + /> + +
+ {statusMessage && ( +

+ {statusMessage} +

+ )} + {error && ( +

+ {error} +

+ )} +
+ ); +}; + +export default FriendSearchComponent; diff --git a/frontend/src/components/Navbar.js b/frontend/src/components/Navbar.js deleted file mode 100644 index 3352329..0000000 --- a/frontend/src/components/Navbar.js +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react' - -export const Navbar = () => { - return ( -
Navbar
- ) -} diff --git a/frontend/src/components/PageHeader.js b/frontend/src/components/PageHeader.js index 4b9a087..eca62c7 100644 --- a/frontend/src/components/PageHeader.js +++ b/frontend/src/components/PageHeader.js @@ -1,10 +1,14 @@ import React from 'react' +import User from "../Images/user.png" import "./PageHeader.css" function PageHeader() { return (

Home

+ {/*
+ {/* User Profile +
Username
*/}
) diff --git a/frontend/src/components/SideBar.css b/frontend/src/components/SideBar.css new file mode 100644 index 0000000..5ed6341 --- /dev/null +++ b/frontend/src/components/SideBar.css @@ -0,0 +1,279 @@ +:root{ + + --darkGreen: #5c8d89; + --green: #74b49b; + --lightGreen: #a7d7c5; + --veryLightGreen: #f4f9f4; + --darkGrey: #828282; + --white: #ffffff; + --lightGrey: #f2f2f2; + font-family :"Andada Pro", serif; + + } + + + + + + + body { + overflow: hidden; + height: 100vh; + width: 100vw; + font-family: -"Andada Pro", serif; + background-color: var(--veryLightGreen); + text-transform: capitalize; + } + + h1 { + width: 100vw; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + } + + .VerticalNavigation { + background-color:var(--white); + position: absolute; + top: 0; + left: 0; + padding:2rem 3rem; + width: 18rem; + height: 100vh; + transition: 0.5s all; + float: left; + + .logoContainer { + display: flex; + align-items: center; + justify-content: flex-start; + position: absolute; + padding-bottom: 3rem; + + h2.title { + font-size: 31.42px; + color: var(--green); + padding: 0 0 0 1rem; + } + } + + .burgerContainer { + content: ""; + background-color: var(--green); + position: absolute; + right: calc(-35px / 2); + top: calc(7rem / 1.15); + width: 33px; + height: 33px; + border-radius: 2rem; + z-index: 1; + + + .burgerTrigger { + content: ""; + width: 35px; + height: 35px; + background-color: red; + cursor: pointer; + z-index: 2; + opacity: 0; + position: relative; + } + + .burgerMenu { + content: ""; + background-color: var(--green); + position: relative; + z-index: 1; + width: 20px; + height: 3px; + border-radius: 2rem; + bottom: calc(39px / 2); + left: 0.45rem; + transition: 0.5s all; + + } + + .burgerMenu::after { + content: ""; + background-color:var(--white); + position: absolute; + width: 20px; + height: 3px; + border-radius: 2rem; + top: 0.4rem; + transform: translateY(-0.4rem) rotate(45deg); + transition: 0.5s all; + } + + .burgerMenu::before { + content: ""; + background-color:var(--white); + position: absolute; + width: 20px; + height: 3px; + border-radius: 2rem; + top: -0.4rem; + transform: translateY(0.4rem) rotate(-45deg); + transition: 0.5s all; + + + } + } + + /* .burgerContainer::after { + content: ""; + position: absolute; + right: 35px; + top: (35px / 2); + z-index: 0; + width: calc(6rem + 15rem); + background-color: var(--lightGreen); + height: 2px; + transition: 0.5s all; + padding-top: 2rem; + transform: translateX(8rem); + } */ + + /* .profileContainer { + display: flex; + align-items: center; + justify-content: flex-start; + padding: 3rem 0 0 0; + + img { + width: auto; + height: 3rem; + } + + .profileContents { + padding: 0 0 0 1rem; + + p.name { + padding: 0; + margin: 0; + font-size: 16.7px; + color: var(--darkGreen); + text-transform: capitalize; + font-weight: bold; + } + + p { + padding: 0; + margin: 0; + font-size: 13.9px; + color: var(--darkGreen); + text-transform: lowercase; + } + } + } + */ + .contentsContainer { + margin: 2rem 0 0 0; + padding-top: 5rem ; + padding-right: 3rem; + + ul { + display: flex; + align-items: flex-start; + justify-content: space-between; + flex-direction: column; + list-style: none; + padding: 0; + margin: 0; + + + li { + display: flex; + align-items: left; + justify-content: flex-start; + flex-direction: row; + margin: 0.75rem 0; + padding: 0.75rem 0; + width: 100%; + border-radius: 1rem; + cursor: pointer; + + a { + text-decoration: none; + color: var(--darkGreen); + font-size: 20px; + display: flex; + align-items: center; + justify-content: space-between; + margin: 0 0 0 0.5rem; + } + } + + li.active { + background-color: var(--veryLightGreen); + + a { + color: var(--green); + } + } + + li:hover { + background-color: var(--lightGreen); + transition: 0.25s; + } + } + } + } + .ulimage{ + + height: 2rem; + margin: auto 5px; + float: left; + } + .ulimagehome{ + + height: 3rem; + } + .logo{ + height: 3rem; + } + .contentsContainer:last-child { + margin-top: auto; + } + .VerticalNavigation.active { + width: 8rem; + transition: 0.5s all; + + .logoContainer.active { + h2.title { + opacity: 0; + } + } + + .burgerContainer.active { + transition: 0.5s all; + .burgerMenu { + background-color:var(--white); + transition: 0.5s all; + } + + .burgerMenu::before { + transform: translateY(0rem) rotate(0deg); + transition: 0.5s all; + } + + .burgerMenu::after { + transform: translateY(0rem) rotate(0deg); + transition: 0.5s all; + } + } + + .profileContainer { + .profileContents { + opacity: 0; + } + } + + .contentsContainer { + a { + opacity: 0; + } + } + } \ No newline at end of file diff --git a/frontend/src/components/SideBar.jsx b/frontend/src/components/SideBar.jsx new file mode 100644 index 0000000..f2e8df6 --- /dev/null +++ b/frontend/src/components/SideBar.jsx @@ -0,0 +1,144 @@ +import React, { useState } from 'react' +import './SideBar.css' +import Logo from "../Images/Logo.png" +import Group from "../Images/Group.png" +import Home from "../Images/home-1.svg" +import Friends from "../Images/networking.png" +import List from "../Images/list-1-svgrepo-com.svg" +import Settings from "../Images/settings.png" +/*import LogOut from "../Images/exit.png"*/ +import { useLocation } from 'react-router-dom'; + +/* const SideBar = () => { +return ( + + +) +} + +export default SideBar */ + +const SideBar = () => { + const location =useLocation(); + + const [closeMenu, setCloseMenu] = useState(false); + + const handleCloseMenu = () => { + setCloseMenu(!closeMenu); + }; + + return ( +
+
+ Logo +

GOALSEEK

+
+
+
{ + handleCloseMenu(); + }} + >
+
+
+ {/*
+ {/* profile +
+

Hello, John👋

+

johnsmith@gmail.com

+
+
*/} + + + +
+ +
+
+ ); +}; + +export default SideBar; diff --git a/frontend/src/components/VerticalNavigation.css b/frontend/src/components/VerticalNavigation.css index 7acde4c..f4f9a16 100644 --- a/frontend/src/components/VerticalNavigation.css +++ b/frontend/src/components/VerticalNavigation.css @@ -1,71 +1,75 @@ .vertical-navbar { background-color: #3B3A3A; - width: auto; - /* Adjust width as needed */ - height: 100%; - /* Adjust height as needed */ + max-width: auto; /* Adjust width as needed */ + height: 100%; /* Adjust height as needed */ position: relative; left: 0; top: 0; - overflow-y: auto; - /* Enable vertical scrolling if needed */ - z-index: 1000; - /* Ensure navbar is above other content */ + overflow-y: auto; /* Enable vertical scrolling if needed */ + z-index: 1000; /* Ensure navbar is above other content */ transition: 0.5s all; - -} - -.logout-button { - background: none; - border: none; - color: inherit; - text-decoration: underline; - cursor: pointer; - padding: 0; - font: inherit; } .vertical-navbar.active { - - width: 80px; + width: 80px; /* Adjusted width when active */ transition: 0.5s all; - } -.Logocontainer.active { +.Logocontainer { + display: flex; + align-items: center; + justify-content: center; + background-color: #4B4B4B; + font-family: 'Lucida Sans', Geneva, Verdana, sans-serif; + padding: 10px 0; + h1.title { - opacity: 0; - transition: 0.5s all; + font-size: max( 31px); + color: #B3E6A9; + padding-top: 10px; + letter-spacing: 2.5px; + transition: opacity 0.5s; } - img { - margin-left: 175px; - transition: 0.5s all; + + img.Logo { + width: auto; + height: 4.5rem; + padding: 10px; + cursor: pointer; + } } -.contentsContainer.active { +.vertical-navbar ul { + list-style-type: none; + padding: 0; + margin: 0; +} - li { +.vertical-navbar li { + display: flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; + width: 80%; + border-radius: 10px; + margin-left: 20px; + margin-top: 30px; - padding-right: 5px; + img { + padding: 0 10px; + width: auto; + height: 1.5rem; } - li:hover { + &:hover { background-image: linear-gradient(to left, #767D75, #A9AB98); - height: 60%; - width: 60%; + height: 80%; /* Adjust hover height */ + width: 80%; /* Adjust hover width */ opacity: 0.7; } } - -.vertical-navbar ul { - list-style-type: none; - padding: 0; - margin: 0; -} - - .vertical-navbar li a { display: block; color: #BCC5C8; @@ -73,71 +77,87 @@ text-decoration: none; } +.LogoutContainer { + height: 80%; + color: #BCC5C8; +} +.logout-button { + background: none; + border: none; + color: inherit; + text-decoration: underline; + cursor: pointer; + padding: 0; + font: inherit; +} -.vertical-navbar { - - .Logocontainer { - display: flex; - align-items: center; - justify-content: center; - background-color: #4B4B4B; - font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; - padding: 10px 0 10px 0; - - h1.title { - font-size: 31px; - color: #B3E6A9; - padding: 10px 0 0 0; - letter-spacing: 2.5px; - } - img.Logo { - width: auto; - height: 4.5rem; - padding: 10px 10px 10px 10px; - cursor: pointer; - } - +/* Active state styles for logo container */ +.Logocontainer.active { + h1.title { + opacity: 0; } + img { + margin-left: 175px; /* Adjust for the active state */ + transition: margin-left 0.5s; /* Smooth transition for margin change */ + } } +/* Active state styles for contents */ +.contentsContainer.active { + li { + padding-right: 5px; /* Adjust padding */ + } + li:hover { + background-image: linear-gradient(to left, #767D75, #A9AB98); + height: 60%; /* Adjust hover height */ + width: 60%; /* Adjust hover width */ + opacity: 0.7; + } +} -.vertical-navbar { +/* Media Queries for Responsiveness */ +@media (max-width: 768px) { + .vertical-navbar { + width: 100%; /* Full width on smaller screens */ + } - li { - display: flex; - align-items: center; - justify-content: flex-start; - flex-direction: row; - width: 80%; - border-radius: 10px; - margin-left: 20px; - margin-top: 30px; - - img { - padding: 0 10px 0 10px; - width: auto; - height: 1.5rem; - } + .Logocontainer { + justify-content: space-between; /* Space out logo and title */ + } + .Logocontainer h1.title { + font-size: 24px; /* Smaller title on mobile */ } -} + .vertical-navbar li { + margin-left: 10px; /* Less margin on mobile */ + } -li:hover { - background-image: linear-gradient(to left, #767D75, #A9AB98); - height: 80%; - width: 80%; - opacity: 0.7; + .vertical-navbar li a { + padding: 10px 15px; /* Smaller padding on mobile */ + } + .LogoutContainer { + height: auto; /* Adjust height for mobile */ + } } -.LogoutContainer { - - height: 80%; - color: #BCC5C8; +@media (max-width: 480px) { + .vertical-navbar { + .Logocontainer h1.title { + display: none; /* Hide title on very small screens */ + } + img.Logo { + height: 3rem; /* Smaller logo */ + } + } + .vertical-navbar li { + flex-direction: column; /* Stack items on top of each other */ + align-items: center; /* Center align items */ + } } \ No newline at end of file diff --git a/frontend/src/components/VerticalNavigation.js b/frontend/src/components/VerticalNavigation.js index 39ff6e1..e0ef056 100644 --- a/frontend/src/components/VerticalNavigation.js +++ b/frontend/src/components/VerticalNavigation.js @@ -1,54 +1,67 @@ -import React, { useState } from 'react'; -import './VerticalNavigation.css'; -import Logo from "../Images/Logo-v2.PNG"; -import Group from "../Images/users-group-rounded-svgrepo-com.svg"; -import Home from "../Images/home-alt-3-svgrepo-com.svg"; -import Logout from "../Images/log-out-04-svgrepo-com.svg"; -import { useLogOut } from "../hooks/useLogOut"; +import React, {useState} from 'react' +import { useLocation } from 'react-router-dom' +import './VerticalNavigation.css' +import Logo from "../Images/Logo-v2.PNG" +import Group from "../Images/users-group-rounded-svgrepo-com.svg" +import Home from "../Images/home-alt-3-svgrepo-com.svg" +import Friends from "../Images/network-1-svgrepo-com.svg" +import Logout from "../Images/log-out-04-svgrepo-com.svg" +import {useLogOut} from "../hooks/useLogOut" const VerticalNavigation = () => { - const { logout } = useLogOut(); + + const { logout } = useLogOut() + const location =useLocation(); const [closeMenu, setCloseMenu] = useState(false); const handleCloseMenu = () => { - setCloseMenu(!closeMenu); + setCloseMenu(!closeMenu); }; - const handleLogoutClick = (e) => { - e.preventDefault(); - logout(); + const handleClick = () => { + logout () + + } - return ( -
-
- Logo -

GOALSEEK

-
-
- -
+return ( +
+ + + +
+ Logo { + handleCloseMenu(); + }} /> +

GOALSEEK

- ); -}; + + +
+ + + + +
+
+) +} export default VerticalNavigation; diff --git a/frontend/src/pages/Friends.css b/frontend/src/pages/Friends.css new file mode 100644 index 0000000..eabc5b1 --- /dev/null +++ b/frontend/src/pages/Friends.css @@ -0,0 +1,9 @@ +.FriendComponents{ + display: flex; + flex-direction: row; + justify-content: flex-start; + flex-wrap: wrap; + align-items: stretch; + margin: 0px; + padding-top: 40px; +} \ No newline at end of file diff --git a/frontend/src/pages/Friends.js b/frontend/src/pages/Friends.js new file mode 100644 index 0000000..37d21bb --- /dev/null +++ b/frontend/src/pages/Friends.js @@ -0,0 +1,20 @@ + +import React from 'react'; +import UserSearch from '../components/FriendComponents/FriendSearch.js'; +import FriendsList from '../components/FriendComponents/FriendList'; +import FriendRequestList from '../components/FriendComponents/FriendRequestList.js' +import './Friends.css' + +const FriendPage = () => { + + return ( +
+ + + + +
+ ); +} + +export default FriendPage; \ No newline at end of file diff --git a/frontend/src/pages/Home.css b/frontend/src/pages/Home.css index 18af891..70fe81f 100644 --- a/frontend/src/pages/Home.css +++ b/frontend/src/pages/Home.css @@ -1,4 +1,5 @@ .Home{ display: flex; flex-direction: row; + border-bottom: 0; } \ No newline at end of file diff --git a/frontend/src/pages/Home.js b/frontend/src/pages/Home.js index f3aaa6e..57834d2 100644 --- a/frontend/src/pages/Home.js +++ b/frontend/src/pages/Home.js @@ -1,4 +1,5 @@ import React from 'react' +import SideBar from '../components/VerticalNavigation' import PageHeader from '../components/PageHeader' import VerticalNavigation from '../components/VerticalNavigation'; @@ -6,10 +7,10 @@ import VerticalNavigation from '../components/VerticalNavigation'; const Home = () => { return (
- - -
-
+ + +
+
) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..fae2697 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "GoalTrackApp", + "lockfileVersion": 2, + "requires": true, + "packages": {} +}