From 8f7cb87424575d8e9a3b79348b4db4079d671912 Mon Sep 17 00:00:00 2001 From: rosy Date: Thu, 29 Jan 2026 21:41:22 +0300 Subject: [PATCH 1/3] add server code --- .env | 6 + .gitignore | 19 +- backend/middleware/authMiddleware.js | 38 - backend/models/Restaurant.js | 34 - backend/models/RestaurantRequest.js | 15 - backend/models/User.js | 17 - backend/routes/requestRoutes.js | 78 - backend/routes/restaurantRoutes.js | 243 -- backend/routes/userRoutes.js | 114 - backend/server.js | 119 - README.md => client/README.md | 0 eslint.config.js => client/eslint.config.js | 0 index.html => client/index.html | 0 client/package-lock.json | 3243 +++++++++++++++++ client/package.json | 33 + .../public}/images/addis-cafe.jpg | Bin .../public}/images/italiano.webp | Bin {public => client/public}/images/kategna.png | Bin {public => client/public}/images/lucy.jpg | Bin {public => client/public}/images/tomoca.png | Bin {public => client/public}/vite.svg | 0 {src => client/src}/App.jsx | 137 +- {src => client/src}/assets/addis-cafe.jpg | Bin {src => client/src}/assets/heroSection.png | Bin {src => client/src}/assets/heroSection2.jpg | Bin {src => client/src}/assets/heroSection3.jpg | Bin {src => client/src}/assets/heroSection4.jpg | Bin {src => client/src}/assets/herosection5.jpg | Bin {src => client/src}/assets/italiano.webp | Bin {src => client/src}/assets/kategna.png | Bin {src => client/src}/assets/lucy.jpg | Bin {src => client/src}/assets/react.svg | 0 {src => client/src}/assets/tomoca.png | Bin {src => client/src}/assets/wa.jpg | Bin {src => client/src}/assets/wal.jpg | Bin .../src}/components/common/Button.jsx | 34 +- .../src}/components/common/Dropdown.jsx | 33 +- .../src}/components/common/InputField.jsx | 40 +- .../src}/components/common/MapSection.jsx | 82 +- .../src}/components/common/Modal.jsx | 36 +- client/src/components/common/Popups.jsx | 35 + .../src}/components/common/StarRating.jsx | 48 +- .../src}/components/home/CategoryCards.jsx | 34 +- .../src}/components/home/FeaturedCard.jsx | 176 +- .../src}/components/home/HeroSection.jsx | 124 +- {src => client/src}/components/index.js | 46 +- .../src}/components/layout/Footer.jsx | 34 +- client/src/components/layout/Navbar.jsx | 144 + .../src}/components/layout/Sidebar.jsx | 83 +- .../src/components/owner/OwnerLoginForm.jsx | 28 + client/src/components/owner/OwnerProfile.jsx | 0 .../src/components/owner/OwnerSignupForm.jsx | 55 + .../components/restaurant/MenuSection.jsx | 32 +- .../components/restaurant/RestaurantCard.jsx | 162 +- .../components/restaurant/RestaurantList.jsx | 40 +- .../src}/components/restaurant/Reviewcard.jsx | 126 +- .../src}/components/user/LoginForm.jsx | 62 +- client/src/components/user/SignupForm.jsx | 53 + .../src}/components/user/UserProfile.jsx | 32 +- {src => client/src}/context/AuthContext.jsx | 189 +- client/src/context/RestaurantsContext.jsx | 120 + {src => client/src}/data/restaurants.js | 158 +- {src => client/src}/main.jsx | 19 +- {src => client/src}/pages/About.jsx | 24 +- {src => client/src}/pages/Contact.jsx | 44 +- client/src/pages/Favorites.jsx | 45 + {src => client/src}/pages/Home.jsx | 96 +- {src => client/src}/pages/Login.jsx | 67 +- {src => client/src}/pages/NotFound.jsx | 28 +- client/src/pages/OwnerLoginPage.jsx | 28 + client/src/pages/OwnerSignup.jsx | 28 + .../src}/pages/RestaurantDetailsPage.jsx | 138 +- .../src}/pages/RestaurantListPage.jsx | 60 +- .../src}/pages/SearchResultsPage.jsx | 72 +- {src => client/src}/pages/Signup.jsx | 67 +- client/src/pages/UserProfilePage.jsx | 156 + {src => client/src}/pages/WriteReviewPage.jsx | 90 +- {src => client/src}/styles/base.css | 70 +- {src => client/src}/styles/components.css | 291 +- {src => client/src}/styles/home.css | 410 +-- {src => client/src}/styles/layout.css | 547 ++- {src => client/src}/styles/main.css | 34 +- {src => client/src}/styles/restaurant.css | 760 ++-- {src => client/src}/styles/user.css | 98 +- {src => client/src}/styles/utils.css | 32 +- {src => client/src}/styles/variables.css | 14 +- vite.config.js => client/vite.config.js | 0 package-lock.json | 2524 ++++++++++--- package.json | 33 +- server/db/connection.js | 16 + server/middleware/UserAuthMiddleware.js | 25 + server/middleware/ownerAuthMiddleware.js | 26 + server/models/Restaurant.js | 32 + server/models/RestaurantOwner.js | 18 + server/models/User.js | 22 + {backend => server}/package-lock.json | 381 +- {backend => server}/package.json | 16 +- server/routes/RestaurantApp.js | 137 + server/routes/RestaurantOwnersApp.js | 104 + server/routes/UserApp.js | 162 + server/server.js | 34 + server/utils/Mailer.js | 20 + src/components/layout/Navbar.jsx | 71 - src/components/user/SignupForm.jsx | 56 - src/context/RestaurantsContext.jsx | 160 - src/pages/Favorites.jsx | 21 - src/pages/UserProfilePage.jsx | 304 -- 107 files changed, 9355 insertions(+), 4127 deletions(-) create mode 100644 .env mode change 100644 => 100755 .gitignore delete mode 100644 backend/middleware/authMiddleware.js delete mode 100644 backend/models/Restaurant.js delete mode 100644 backend/models/RestaurantRequest.js delete mode 100644 backend/models/User.js delete mode 100644 backend/routes/requestRoutes.js delete mode 100644 backend/routes/restaurantRoutes.js delete mode 100644 backend/routes/userRoutes.js delete mode 100644 backend/server.js rename README.md => client/README.md (100%) mode change 100644 => 100755 rename eslint.config.js => client/eslint.config.js (100%) mode change 100644 => 100755 rename index.html => client/index.html (100%) mode change 100644 => 100755 create mode 100755 client/package-lock.json create mode 100755 client/package.json rename {public => client/public}/images/addis-cafe.jpg (100%) mode change 100644 => 100755 rename {public => client/public}/images/italiano.webp (100%) mode change 100644 => 100755 rename {public => client/public}/images/kategna.png (100%) mode change 100644 => 100755 rename {public => client/public}/images/lucy.jpg (100%) mode change 100644 => 100755 rename {public => client/public}/images/tomoca.png (100%) mode change 100644 => 100755 rename {public => client/public}/vite.svg (100%) mode change 100644 => 100755 rename {src => client/src}/App.jsx (59%) mode change 100644 => 100755 rename {src => client/src}/assets/addis-cafe.jpg (100%) mode change 100644 => 100755 rename {src => client/src}/assets/heroSection.png (100%) mode change 100644 => 100755 rename {src => client/src}/assets/heroSection2.jpg (100%) mode change 100644 => 100755 rename {src => client/src}/assets/heroSection3.jpg (100%) mode change 100644 => 100755 rename {src => client/src}/assets/heroSection4.jpg (100%) mode change 100644 => 100755 rename {src => client/src}/assets/herosection5.jpg (100%) mode change 100644 => 100755 rename {src => client/src}/assets/italiano.webp (100%) mode change 100644 => 100755 rename {src => client/src}/assets/kategna.png (100%) mode change 100644 => 100755 rename {src => client/src}/assets/lucy.jpg (100%) mode change 100644 => 100755 rename {src => client/src}/assets/react.svg (100%) mode change 100644 => 100755 rename {src => client/src}/assets/tomoca.png (100%) mode change 100644 => 100755 rename {src => client/src}/assets/wa.jpg (100%) mode change 100644 => 100755 rename {src => client/src}/assets/wal.jpg (100%) mode change 100644 => 100755 rename {src => client/src}/components/common/Button.jsx (54%) mode change 100644 => 100755 rename {src => client/src}/components/common/Dropdown.jsx (55%) mode change 100644 => 100755 rename {src => client/src}/components/common/InputField.jsx (96%) mode change 100644 => 100755 rename {src => client/src}/components/common/MapSection.jsx (97%) mode change 100644 => 100755 rename {src => client/src}/components/common/Modal.jsx (96%) mode change 100644 => 100755 create mode 100644 client/src/components/common/Popups.jsx rename {src => client/src}/components/common/StarRating.jsx (96%) mode change 100644 => 100755 rename {src => client/src}/components/home/CategoryCards.jsx (72%) mode change 100644 => 100755 rename {src => client/src}/components/home/FeaturedCard.jsx (82%) mode change 100644 => 100755 rename {src => client/src}/components/home/HeroSection.jsx (97%) mode change 100644 => 100755 rename {src => client/src}/components/index.js (98%) mode change 100644 => 100755 rename {src => client/src}/components/layout/Footer.jsx (96%) mode change 100644 => 100755 create mode 100755 client/src/components/layout/Navbar.jsx rename {src => client/src}/components/layout/Sidebar.jsx (63%) mode change 100644 => 100755 create mode 100644 client/src/components/owner/OwnerLoginForm.jsx create mode 100644 client/src/components/owner/OwnerProfile.jsx create mode 100644 client/src/components/owner/OwnerSignupForm.jsx rename {src => client/src}/components/restaurant/MenuSection.jsx (95%) mode change 100644 => 100755 rename {src => client/src}/components/restaurant/RestaurantCard.jsx (97%) mode change 100644 => 100755 rename {src => client/src}/components/restaurant/RestaurantList.jsx (60%) mode change 100644 => 100755 rename {src => client/src}/components/restaurant/Reviewcard.jsx (97%) mode change 100644 => 100755 rename {src => client/src}/components/user/LoginForm.jsx (66%) mode change 100644 => 100755 create mode 100755 client/src/components/user/SignupForm.jsx rename {src => client/src}/components/user/UserProfile.jsx (96%) mode change 100644 => 100755 rename {src => client/src}/context/AuthContext.jsx (63%) mode change 100644 => 100755 create mode 100755 client/src/context/RestaurantsContext.jsx rename {src => client/src}/data/restaurants.js (96%) mode change 100644 => 100755 rename {src => client/src}/main.jsx (81%) mode change 100644 => 100755 rename {src => client/src}/pages/About.jsx (96%) mode change 100644 => 100755 rename {src => client/src}/pages/Contact.jsx (97%) mode change 100644 => 100755 create mode 100755 client/src/pages/Favorites.jsx rename {src => client/src}/pages/Home.jsx (52%) mode change 100644 => 100755 rename {src => client/src}/pages/Login.jsx (75%) mode change 100644 => 100755 rename {src => client/src}/pages/NotFound.jsx (96%) mode change 100644 => 100755 create mode 100755 client/src/pages/OwnerLoginPage.jsx create mode 100755 client/src/pages/OwnerSignup.jsx rename {src => client/src}/pages/RestaurantDetailsPage.jsx (95%) mode change 100644 => 100755 rename {src => client/src}/pages/RestaurantListPage.jsx (85%) mode change 100644 => 100755 rename {src => client/src}/pages/SearchResultsPage.jsx (97%) mode change 100644 => 100755 rename {src => client/src}/pages/Signup.jsx (76%) mode change 100644 => 100755 create mode 100755 client/src/pages/UserProfilePage.jsx rename {src => client/src}/pages/WriteReviewPage.jsx (97%) mode change 100644 => 100755 rename {src => client/src}/styles/base.css (95%) mode change 100644 => 100755 rename {src => client/src}/styles/components.css (84%) mode change 100644 => 100755 rename {src => client/src}/styles/home.css (94%) mode change 100644 => 100755 rename {src => client/src}/styles/layout.css (90%) mode change 100644 => 100755 rename {src => client/src}/styles/main.css (95%) mode change 100644 => 100755 rename {src => client/src}/styles/restaurant.css (94%) mode change 100644 => 100755 rename {src => client/src}/styles/user.css (93%) mode change 100644 => 100755 rename {src => client/src}/styles/utils.css (93%) mode change 100644 => 100755 rename {src => client/src}/styles/variables.css (96%) mode change 100644 => 100755 rename vite.config.js => client/vite.config.js (100%) mode change 100644 => 100755 create mode 100644 server/db/connection.js create mode 100644 server/middleware/UserAuthMiddleware.js create mode 100644 server/middleware/ownerAuthMiddleware.js create mode 100644 server/models/Restaurant.js create mode 100644 server/models/RestaurantOwner.js create mode 100644 server/models/User.js rename {backend => server}/package-lock.json (77%) rename {backend => server}/package.json (57%) create mode 100644 server/routes/RestaurantApp.js create mode 100644 server/routes/RestaurantOwnersApp.js create mode 100644 server/routes/UserApp.js create mode 100644 server/server.js create mode 100644 server/utils/Mailer.js delete mode 100644 src/components/layout/Navbar.jsx delete mode 100644 src/components/user/SignupForm.jsx delete mode 100644 src/context/RestaurantsContext.jsx delete mode 100644 src/pages/Favorites.jsx delete mode 100644 src/pages/UserProfilePage.jsx diff --git a/.env b/.env new file mode 100644 index 0000000..01fc750 --- /dev/null +++ b/.env @@ -0,0 +1,6 @@ +URI="mongodb://localhost:27017/findaddis" +PORT=3000 +JWT_SECRET=6a782dd97223bfad984188306146c93cec93a0bba08dcad00bd4e4c4475db962a0ab763feade99f9e10d0087713ba6385d910df953ff656cdb6eb4b810c15d36 +EXPIRESIN="7d" +EMAILPASS="bxhl jqed hbor hfmr" +EMAIL="rosabekele020@gmail.com" diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index d508a6c..d107285 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,20 @@ # Logs logs -*.log +**/*.log npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* lerna-debug.log* -node_modules -dist -dist-ssr -*.local +# Dependencies +**/node_modules + +# Build outputs +**/dist +**/dist-ssr +**/build +**/*.local # Editor directories and files .vscode/* @@ -22,5 +26,6 @@ dist-ssr *.njsproj *.sln *.sw? -# Environment variables -.env + +#configs +/server/.env \ No newline at end of file diff --git a/backend/middleware/authMiddleware.js b/backend/middleware/authMiddleware.js deleted file mode 100644 index 9713829..0000000 --- a/backend/middleware/authMiddleware.js +++ /dev/null @@ -1,38 +0,0 @@ -const jwt = require('jsonwebtoken'); -const User = require('../models/User'); - -const protect = async (req, res, next) => { - let token; - - if ( - req.headers.authorization && - req.headers.authorization.startsWith('Bearer') - ) { - try { - token = req.headers.authorization.split(' ')[1]; - // Default secret if not in env - const secret = process.env.JWT_SECRET || 'secret'; - const decoded = jwt.verify(token, secret); - - req.user = await User.findById(decoded.id).select('-password'); - next(); - } catch (error) { - console.error(error); - res.status(401).json({ message: 'Not authorized, token failed' }); - } - } - - if (!token) { - res.status(401).json({ message: 'Not authorized, no token' }); - } -}; - -const admin = (req, res, next) => { - if (req.user && req.user.role === 'admin') { - next(); - } else { - res.status(401).json({ message: 'Not authorized as an admin' }); - } -}; - -module.exports = { protect, admin }; diff --git a/backend/models/Restaurant.js b/backend/models/Restaurant.js deleted file mode 100644 index 1dfd6a4..0000000 --- a/backend/models/Restaurant.js +++ /dev/null @@ -1,34 +0,0 @@ -const mongoose = require('mongoose'); - -const restaurantSchema = new mongoose.Schema({ - name: { type: String, required: true }, - id: { type: String }, - category: { type: String, required: true }, - rating: { type: Number, required: true, default: 0 }, - price: { type: String, required: true }, - address: { type: String, required: true }, - images: [String], // URL strings or paths - hours: String, - description: String, - menu: [String], - reviews: [ - { - user: String, - rating: Number, - text: String, - date: String, - id: String - } - ], - // Adding coordinates for map - location: { - lat: { type: Number }, - lng: { type: Number } - }, - isApproved: { type: Boolean, default: false }, - owner: { type: mongoose.Schema.Types.ObjectId, ref: 'User' } -}, { - timestamps: true -}); - -module.exports = mongoose.model('Restaurant', restaurantSchema); diff --git a/backend/models/RestaurantRequest.js b/backend/models/RestaurantRequest.js deleted file mode 100644 index 8cb5f93..0000000 --- a/backend/models/RestaurantRequest.js +++ /dev/null @@ -1,15 +0,0 @@ -const mongoose = require('mongoose'); - -const restaurantRequestSchema = new mongoose.Schema({ - userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true }, - type: { type: String, enum: ['CREATE', 'UPDATE', 'DELETE'], required: true }, - target: { - type: String, - required: function () { return this.type !== 'CREATE'; } - }, - data: { type: Object, required: true }, - status: { type: String, enum: ['PENDING', 'APPROVED', 'REJECTED'], default: 'PENDING' }, - adminComments: { type: String } -}, { timestamps: true }); - -module.exports = mongoose.model('RestaurantRequest', restaurantRequestSchema); diff --git a/backend/models/User.js b/backend/models/User.js deleted file mode 100644 index 69653d8..0000000 --- a/backend/models/User.js +++ /dev/null @@ -1,17 +0,0 @@ -const mongoose = require('mongoose'); - -const userSchema = mongoose.Schema({ - name: { type: String, required: true }, - email: { - type: String, - required: true, - unique: true, - match: [/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please fill a valid email address'] - }, - password: { type: String, required: true }, - role: { type: String, enum: ['user', 'admin', 'restaurant_owner'], default: 'user' }, - managedRestaurantId: { type: String }, - favorites: [String], // Array of restaurant IDs -}, { timestamps: true }); - -module.exports = mongoose.model('User', userSchema); diff --git a/backend/routes/requestRoutes.js b/backend/routes/requestRoutes.js deleted file mode 100644 index 24ab652..0000000 --- a/backend/routes/requestRoutes.js +++ /dev/null @@ -1,78 +0,0 @@ -const express = require('express'); -const router = express.Router(); -const RestaurantRequest = require('../models/RestaurantRequest'); -const Restaurant = require('../models/Restaurant'); -const { protect, admin } = require('../middleware/authMiddleware'); -const mongoose = require('mongoose'); - -// Create a request (Owner/User) -router.post('/', protect, async (req, res) => { - const { type, target, data } = req.body; - - try { - const request = await RestaurantRequest.create({ - userId: req.user._id, - type: type.toUpperCase(), - target, - data, - }); - res.status(201).json(request); - } catch (e) { - res.status(400).json({ message: e.message }); - } -}); - -// List requests (Admin) -router.get('/', protect, admin, async (req, res) => { - const requests = await RestaurantRequest.find({ status: 'PENDING' }).populate('userId', 'name email'); - res.json(requests); -}); - -// Approve Request (Admin) -router.post('/:id/approve', protect, admin, async (req, res) => { - const request = await RestaurantRequest.findById(req.params.id); - if (!request) return res.status(404).json({ message: 'Request not found' }); - - if (request.status !== 'PENDING') return res.status(400).json({ message: 'Request already processed' }); - - try { - if (request.type === 'UPDATE') { - const restaurant = await Restaurant.findById(request.target); - if (restaurant) { - Object.assign(restaurant, request.data); - await restaurant.save(); - } else { - return res.status(404).json({ message: "Target restaurant not found" }); - } - } else if (request.type === 'CREATE') { - const restaurantData = { - ...request.data, - owner: request.userId, - isApproved: true - }; - const newRest = await Restaurant.create(restaurantData); - - // Link to user - const User = require('../models/User'); - await User.findByIdAndUpdate(request.userId, { managedRestaurantId: newRest._id }); - } - - request.status = 'APPROVED'; - await request.save(); - res.json({ message: 'Request approved' }); - } catch (e) { - res.status(500).json({ message: e.message }); - } -}); - -// Reject Request (Admin) -router.post('/:id/reject', protect, admin, async (req, res) => { - const request = await RestaurantRequest.findById(req.params.id); - if (!request) return res.status(404).json({ message: 'Request not found' }); - - request.status = 'REJECTED'; - await request.save(); - res.json({ message: 'Request rejected' }); -}); - -module.exports = router; diff --git a/backend/routes/restaurantRoutes.js b/backend/routes/restaurantRoutes.js deleted file mode 100644 index 3d91fae..0000000 --- a/backend/routes/restaurantRoutes.js +++ /dev/null @@ -1,243 +0,0 @@ -const express = require('express'); -const router = express.Router(); -const Restaurant = require('../models/Restaurant'); -const User = require('../models/User'); -const { protect, admin } = require('../middleware/authMiddleware'); - -// Get all approved restaurants -router.get('/', async (req, res) => { - try { - const { sort, search } = req.query; - let query = { isApproved: true }; - - if (search) { - query.name = { $regex: search, $options: 'i' }; - } - - let restaurants = Restaurant.find(query); - - if (sort === 'rating') { - restaurants = restaurants.sort({ rating: -1 }); - } else if (sort === 'trending') { - // Simple trending logic: random or review count. Let's sort by newly created for now - restaurants = restaurants.sort({ createdAt: -1 }); - } - - const result = await restaurants; - res.json(result); - } catch (error) { - res.status(500).json({ message: error.message }); - } -}); - -// Admin: Get pending restaurants -router.get('/pending', protect, admin, async (req, res) => { - try { - const restaurants = await Restaurant.find({ isApproved: false }); - res.json(restaurants); - } catch (error) { - res.status(500).json({ message: error.message }); - } -}); - -// Create a new restaurant (Vendor/Admin) -router.post('/', protect, async (req, res) => { - try { - const { name, category, price, address, description, location } = req.body; - - // Check if vendor already has a restaurant (optional check, can be relaxed if needed) - - // Generate a custom ID for legacy compatibility if needed - const baseName = name ? name.toLowerCase().replace(/[^a-z0-9]+/g, '-') : 'restaurant'; - const generatedId = `${baseName}-${Date.now()}`; - - const restaurantData = { - name, - category, - price, - address, - description, - location, - menu: req.body.menu, - owner: req.user._id, - isApproved: req.user.role === 'admin' ? true : false, - id: generatedId // Keep for backward compatibility - }; - - const newRestaurant = new Restaurant(restaurantData); - const savedRestaurant = await newRestaurant.save(); - - // Assign REAL MongoDB _id to user for reliable updates - if (req.user.role === 'restaurant_owner') { - req.user.managedRestaurantId = savedRestaurant._id; - await req.user.save(); - } - - res.status(201).json(savedRestaurant); - } catch (error) { - res.status(400).json({ message: error.message }); - } -}); - -// Approve restaurant (Admin) -router.put('/:id/approve', protect, admin, async (req, res) => { - try { - // Try finding by _id first (modern), then fallback to custom id (legacy) - let restaurant = await Restaurant.findById(req.params.id); - if (!restaurant) { - restaurant = await Restaurant.findOne({ id: req.params.id }); - } - - if (!restaurant) return res.status(404).json({ message: 'Restaurant not found' }); - - restaurant.isApproved = true; - await restaurant.save(); - res.json(restaurant); - } catch (error) { - res.status(400).json({ message: error.message }); - } -}); - - -// Get single restaurant by _id or custom ID -router.get('/:id', async (req, res) => { - try { - let restaurant = await Restaurant.findById(req.params.id).catch(() => null); - if (!restaurant) { - restaurant = await Restaurant.findOne({ id: req.params.id }); - } - - if (restaurant) { - res.json(restaurant); - } else { - res.status(404).json({ message: 'Restaurant not found' }); - } - } catch (error) { - res.status(500).json({ message: error.message }); - } -}); - - -// Update restaurant by _id or custom ID (Owner or Admin) -router.put('/:id', protect, async (req, res) => { - try { - let restaurant = await Restaurant.findById(req.params.id).catch(() => null); - if (!restaurant) { - restaurant = await Restaurant.findOne({ id: req.params.id }); - } - - if (restaurant) { - // Check ownership - if (req.user.role !== 'admin' && restaurant.owner && restaurant.owner.toString() !== req.user._id.toString()) { - return res.status(401).json({ message: 'Not authorized to edit this restaurant' }); - } - - Object.assign(restaurant, req.body); - const updatedRestaurant = await restaurant.save(); - res.json(updatedRestaurant); - } else { - res.status(404).json({ message: 'Restaurant not found' }); - } - } catch (error) { - res.status(400).json({ message: error.message }); - } -}); - -// Delete restaurant (Admin only for now, or Owner) -router.delete('/:id', protect, admin, async (req, res) => { - try { - let restaurant = await Restaurant.findByIdAndDelete(req.params.id).catch(() => null); - if (!restaurant) { - restaurant = await Restaurant.findOneAndDelete({ id: req.params.id }); - } - res.json({ message: 'Restaurant removed' }); - } catch (error) { - res.status(500).json({ message: error.message }); - } -}); - - -// Add a review (User only) -router.post('/:id/reviews', protect, async (req, res) => { - try { - const restaurant = await Restaurant.findOne({ id: req.params.id }); - if (!restaurant) { - return res.status(404).json({ message: 'Restaurant not found' }); - } - - const review = { - user: req.user.name, // Use authenticated user name - rating: Number(req.body.rating), - text: req.body.text, - date: new Date().toISOString().split('T')[0], - id: req.body.id || Math.random().toString(36).substr(2, 9), - userId: req.user._id - }; - - restaurant.reviews.push(review); - await restaurant.save(); - res.status(201).json(review); - } catch (error) { - res.status(400).json({ message: error.message }); - } -}); - -// Update a review -router.put('/:id/reviews/:reviewId', protect, async (req, res) => { - try { - const restaurant = await Restaurant.findOne({ id: req.params.id }); - if (!restaurant) { - return res.status(404).json({ message: 'Restaurant not found' }); - } - - const reviewIndex = restaurant.reviews.findIndex(r => r.id === req.params.reviewId || r._id.toString() === req.params.reviewId); - - if (reviewIndex === -1) { - return res.status(404).json({ message: 'Review not found' }); - } - - const review = restaurant.reviews[reviewIndex]; - // Check permissions: Admin or Review Author - const isAuthor = (review.userId && review.userId.toString() === req.user._id.toString()) || review.user === req.user.name; - if (req.user.role !== 'admin' && !isAuthor) { - return res.status(401).json({ message: 'Not authorized to edit this review' }); - } - - // Update fields - if (req.body.text) restaurant.reviews[reviewIndex].text = req.body.text; - if (req.body.rating) restaurant.reviews[reviewIndex].rating = req.body.rating; - - await restaurant.save(); - res.json(restaurant.reviews[reviewIndex]); - } catch (error) { - res.status(400).json({ message: error.message }); - } -}); - -// Delete a review (Admin or Author) -router.delete('/:id/reviews/:reviewId', protect, async (req, res) => { - try { - const restaurant = await Restaurant.findOne({ id: req.params.id }); - if (!restaurant) { - return res.status(404).json({ message: 'Restaurant not found' }); - } - - // Find review to check permissions before deleting - const review = restaurant.reviews.find(r => r.id === req.params.reviewId || r._id.toString() === req.params.reviewId); - - if (review) { - const isAuthor = (review.userId && review.userId.toString() === req.user._id.toString()) || review.user === req.user.name; - if (req.user.role !== 'admin' && !isAuthor) { - return res.status(401).json({ message: 'Not authorized to delete this review' }); - } - } - - restaurant.reviews = restaurant.reviews.filter(r => r.id !== req.params.reviewId && r._id.toString() !== req.params.reviewId); - await restaurant.save(); - res.json({ message: 'Review deleted' }); - } catch (error) { - res.status(400).json({ message: error.message }); - } -}); - -module.exports = router; diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js deleted file mode 100644 index 9c1488b..0000000 --- a/backend/routes/userRoutes.js +++ /dev/null @@ -1,114 +0,0 @@ -const express = require('express'); -const router = express.Router(); -const bcrypt = require('bcryptjs'); -const jwt = require('jsonwebtoken'); -const User = require('../models/User'); -const { protect } = require('../middleware/authMiddleware'); - -const generateToken = (id) => { - return jwt.sign({ id }, process.env.JWT_SECRET || 'secret', { - expiresIn: '30d', - }); -}; - -router.post('/register', async (req, res) => { - const { name, email, password, role, adminSecret } = req.body; - - // Secure Admin Registration - if (role === 'admin' && adminSecret !== 'admin123') { - return res.status(400).json({ message: 'Invalid Admin Secret' }); - } - - // Secure Vendor Registration - if (role === 'restaurant_owner' && adminSecret !== 'vendor123') { // re-using adminSecret field for simplicity or add a new field - // Wait, the form might send it as adminSecret or we should check a new field. - // Simple solution: check adminSecret for both, distinguishing by role. - return res.status(400).json({ message: 'Invalid Vendor Secret' }); - } - - const userExists = await User.findOne({ email }); - - if (userExists) { - return res.status(400).json({ message: 'User already exists' }); - } - - const salt = await bcrypt.genSalt(10); - const hashedPassword = await bcrypt.hash(password, salt); - - try { - const user = await User.create({ - name, - email, - password: hashedPassword, - role: role || 'user', - }); - - if (user) { - res.status(201).json({ - _id: user._id, - name: user.name, - email: user.email, - role: user.role, - token: generateToken(user._id), - }); - } else { - res.status(400).json({ message: 'Invalid user data' }); - } - } catch (error) { - res.status(400).json({ message: error.message }); - } -}); - -router.post('/login', async (req, res) => { - const { email, password } = req.body; - - const user = await User.findOne({ email }); - - if (user && (await bcrypt.compare(password, user.password))) { - res.json({ - _id: user._id, - name: user.name, - email: user.email, - role: user.role, - managedRestaurantId: user.managedRestaurantId, - token: generateToken(user._id), - }); - } else { - res.status(401).json({ message: 'Invalid email or password' }); - } -}); - -router.get('/profile', protect, async (req, res) => { - res.json(req.user); -}); - -// Get User Favorites -router.get('/favorites', protect, async (req, res) => { - // req.user is already populated by protect middleware - res.json(req.user.favorites || []); -}); - -// Add Favorite -router.post('/favorites/:restaurantId', protect, async (req, res) => { - const { restaurantId } = req.params; - const user = req.user; - - if (!user.favorites.includes(restaurantId)) { - user.favorites.push(restaurantId); - await user.save(); - } - res.json(user.favorites); -}); - -// Remove Favorite -router.delete('/favorites/:restaurantId', protect, async (req, res) => { - const { restaurantId } = req.params; - const user = req.user; - - user.favorites = user.favorites.filter(id => id !== restaurantId); - await user.save(); - res.json(user.favorites); -}); - - -module.exports = router; diff --git a/backend/server.js b/backend/server.js deleted file mode 100644 index 86191c2..0000000 --- a/backend/server.js +++ /dev/null @@ -1,119 +0,0 @@ -const express = require('express'); -const dotenv = require('dotenv'); -const cors = require('cors'); -const mongoose = require('mongoose'); -const Restaurant = require('./models/Restaurant'); - -dotenv.config(); - -const app = express(); - -app.use(cors()); -app.use(express.json()); - -// Connect to MongoDB -mongoose.connect(process.env.MONGO_URI || 'mongodb://localhost:27017/findaddis') - .then(() => console.log('MongoDB connected')) - .catch((err) => console.log(err)); - -const userRoutes = require('./routes/userRoutes'); -const requestRoutes = require('./routes/requestRoutes'); -const restaurantRoutes = require('./routes/restaurantRoutes'); - -app.use('/api/restaurants', restaurantRoutes); -app.use('/api/users', userRoutes); -app.use('/api/requests', requestRoutes); - -// Seed Route -app.get('/api/seed', async (req, res) => { - await Restaurant.deleteMany({}); - - const sampleRestaurants = [ - { - id: "lucy", - name: "Lucy Ethiopian Restaurant", - category: "Ethiopian", - rating: 4.6, - price: "$$", - address: "Bole, Addis Ababa", - images: ["/images/lucy.jpg"], - hours: "08:00 - 23:00", - description: "Traditional Ethiopian dishes served with authentic injera and a warm atmosphere.", - menu: ["Doro Wat", "Tibs", "Shiro"], - reviews: [ - { user: "Marta", rating: 5, text: "Amazing flavours!", date: "2025-10-10" }, - ], - location: { lat: 9.006, lng: 38.785 }, - isApproved: true - }, - { - id: "tomoca", - name: "Tomoca Coffee", - category: "Cafe", - rating: 4.5, - price: "$", - address: "Multiple locations, Addis Ababa", - images: ["/images/tomoca.png"], - hours: "07:00 - 20:00", - description: "Historic Ethiopian coffee roaster, known for strong, aromatic brews.", - menu: ["Ethiopian coffee", "Pastries"], - reviews: [], - location: { lat: 9.021, lng: 38.752 }, - isApproved: true - }, - { - id: "kategna", - name: "Kategna", - category: "Ethiopian", - rating: 4.4, - price: "$$", - address: "Old Airport, Addis Ababa", - images: ["/images/kategna.png"], - hours: "10:00 - 22:00", - description: "Local favorite serving homestyle Ethiopian favorites in a relaxed setting.", - menu: ["Kitfo", "Tibs"], - reviews: [], - location: { lat: 8.995, lng: 38.730 }, - isApproved: true - }, - { - id: "addiscafe", - name: "Addis Cafe", - category: "Cafe", - rating: 4.0, - price: "$", - address: "Piassa, Addis Ababa", - images: ["/images/addis-cafe.jpg"], - hours: "07:00 - 20:00", - description: "Cozy spot for coffee and light bites.", - menu: ["Coffee", "Pastries", "Sandwiches"], - reviews: [{ user: "Sam", rating: 4, text: "Nice coffee and calm place.", date: "2025-09-21" }], - location: { lat: 9.030, lng: 38.753 }, - isApproved: true - }, - { - id: "italiano", - name: "Italiano Corner", - category: "Italian", - rating: 4.2, - price: "$$$", - address: "Kazanchis, Addis Ababa", - images: ["/images/italiano.webp"], - hours: "11:00 - 22:00", - description: "Wood-fired pizzas and homemade pasta (popular with locals and expats).", - menu: ["Margherita", "Carbonara"], - reviews: [], - location: { lat: 9.015, lng: 38.766 }, - isApproved: true - }, - ]; - try { - const createdRestaurants = await Restaurant.insertMany(sampleRestaurants); - res.json(createdRestaurants); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}); - -const PORT = process.env.PORT || 5000; -app.listen(PORT, () => console.log(`Server running on port ${PORT}`)); diff --git a/README.md b/client/README.md old mode 100644 new mode 100755 similarity index 100% rename from README.md rename to client/README.md diff --git a/eslint.config.js b/client/eslint.config.js old mode 100644 new mode 100755 similarity index 100% rename from eslint.config.js rename to client/eslint.config.js diff --git a/index.html b/client/index.html old mode 100644 new mode 100755 similarity index 100% rename from index.html rename to client/index.html diff --git a/client/package-lock.json b/client/package-lock.json new file mode 100755 index 0000000..740b22f --- /dev/null +++ b/client/package-lock.json @@ -0,0 +1,3243 @@ +{ + "name": "package.json", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "package.json", + "version": "0.0.0", + "dependencies": { + "axios": "^1.13.2", + "leaflet": "^1.9.4", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-leaflet": "^5.0.0", + "react-router-dom": "^7.10.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "vite": "^7.2.4" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@react-leaflet/core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz", + "integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==", + "license": "Hippocratic-2.1", + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz", + "integrity": "sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", + "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.1.tgz", + "integrity": "sha512-WQfkSw0QbQ5aJ2CHYw23ZGkqnRwqKHD/KYsMeTkZzPT4Jcf0DcBxBtwMJxnu6E7oxw5+JC6ZAiePgh28uJ1HBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.5", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.47", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", + "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", + "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.25", + "caniuse-lite": "^1.0.30001754", + "electron-to-chromium": "^1.5.249", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001759", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001759.tgz", + "integrity": "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.263", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.263.tgz", + "integrity": "sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==", + "dev": true, + "license": "ISC" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.1", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz", + "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "license": "BSD-2-Clause" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", + "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", + "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.0" + } + }, + "node_modules/react-leaflet": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz", + "integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==", + "license": "Hippocratic-2.1", + "dependencies": { + "@react-leaflet/core": "^3.0.0" + }, + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.10.0.tgz", + "integrity": "sha512-FVyCOH4IZ0eDDRycODfUqoN8ZSR2LbTvtx6RPsBgzvJ8xAXlMZNCrOFpu+jb8QbtZnpAd/cEki2pwE848pNGxw==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.10.0.tgz", + "integrity": "sha512-Q4haR150pN/5N75O30iIsRJcr3ef7p7opFaKpcaREy0GQit6uCRu1NEiIFIwnHJQy0bsziRFBweR/5EkmHgVUQ==", + "license": "MIT", + "dependencies": { + "react-router": "7.10.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.6.tgz", + "integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", + "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/client/package.json b/client/package.json new file mode 100755 index 0000000..19a36e0 --- /dev/null +++ b/client/package.json @@ -0,0 +1,33 @@ +{ + "name": "client", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "start": "vite preview", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "axios": "^1.13.2", + "jwt-decode": "^4.0.0", + "leaflet": "^1.9.4", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-leaflet": "^5.0.0", + "react-router-dom": "^7.10.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "vite": "^7.2.4" + } +} diff --git a/public/images/addis-cafe.jpg b/client/public/images/addis-cafe.jpg old mode 100644 new mode 100755 similarity index 100% rename from public/images/addis-cafe.jpg rename to client/public/images/addis-cafe.jpg diff --git a/public/images/italiano.webp b/client/public/images/italiano.webp old mode 100644 new mode 100755 similarity index 100% rename from public/images/italiano.webp rename to client/public/images/italiano.webp diff --git a/public/images/kategna.png b/client/public/images/kategna.png old mode 100644 new mode 100755 similarity index 100% rename from public/images/kategna.png rename to client/public/images/kategna.png diff --git a/public/images/lucy.jpg b/client/public/images/lucy.jpg old mode 100644 new mode 100755 similarity index 100% rename from public/images/lucy.jpg rename to client/public/images/lucy.jpg diff --git a/public/images/tomoca.png b/client/public/images/tomoca.png old mode 100644 new mode 100755 similarity index 100% rename from public/images/tomoca.png rename to client/public/images/tomoca.png diff --git a/public/vite.svg b/client/public/vite.svg old mode 100644 new mode 100755 similarity index 100% rename from public/vite.svg rename to client/public/vite.svg diff --git a/src/App.jsx b/client/src/App.jsx old mode 100644 new mode 100755 similarity index 59% rename from src/App.jsx rename to client/src/App.jsx index de16253..4bcd35b --- a/src/App.jsx +++ b/client/src/App.jsx @@ -1,71 +1,66 @@ -import React from "react"; -import { BrowserRouter, Routes, Route } from "react-router-dom"; - -import Navbar from "./components/layout/Navbar"; -import Footer from "./components/layout/Footer"; - -import Home from "./pages/Home"; -import RestaurantListPage from "./pages/RestaurantListPage"; -import RestaurantDetailsPage from "./pages/RestaurantDetailsPage"; -import SearchResultsPage from "./pages/SearchResultsPage"; -import WriteReviewPage from "./pages/WriteReviewPage"; -import Login from "./pages/Login"; -import Signup from "./pages/Signup"; -import UserProfilePage from "./pages/UserProfilePage"; -import Favorites from "./pages/Favorites"; -import About from "./pages/About"; -import Contact from "./pages/Contact"; -import NotFound from "./pages/NotFound"; - -import { RestaurantsProvider } from "./context/RestaurantsContext"; -import { AuthProvider } from "./context/AuthContext"; -import AuthContext from "./context/AuthContext"; -import { useContext } from "react"; -import { Navigate } from "react-router-dom"; -import "./styles/main.css"; - -// Helper component to restrict Admins/Vendors to their dashboard -function RoleBasedRoute({ children }) { - const { user, isAdmin, isOwner } = useContext(AuthContext); - - // If logged in as Admin or Vendor, the standard pages are restricted - if (user && (isAdmin() || isOwner())) { - return ; - } - - return children; -} - -function App() { - return ( - - - -
- -
- - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - -
- -
-
-
-
-
- ); -} - -export default App; +import React from "react"; +import { BrowserRouter, Routes, Route } from "react-router-dom"; + +import Navbar from "./components/layout/Navbar"; +import Footer from "./components/layout/Footer"; + +import Home from "./pages/Home"; +import RestaurantListPage from "./pages/RestaurantListPage"; +import RestaurantDetailsPage from "./pages/RestaurantDetailsPage"; +import SearchResultsPage from "./pages/SearchResultsPage"; +import WriteReviewPage from "./pages/WriteReviewPage"; +import Login from "./pages/Login"; +import OwnerLoginPage from "./pages/OwnerLoginPage" +import Signup from "./pages/Signup"; +import OwnerSignUp from "./pages/OwnerSignup"; +import UserProfilePage from "./pages/UserProfilePage"; +import Favorites from "./pages/Favorites"; +import About from "./pages/About"; +import Contact from "./pages/Contact"; +import NotFound from "./pages/NotFound"; + +import { RestaurantsProvider } from "./context/RestaurantsContext"; +import { AuthProvider } from "./context/AuthContext"; +import "./styles/main.css"; + + + + + + + +function App() { + return ( + + + +
+ +
+ + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + +
+ +
+
+
+
+
+ ); +} + +export default App; diff --git a/src/assets/addis-cafe.jpg b/client/src/assets/addis-cafe.jpg old mode 100644 new mode 100755 similarity index 100% rename from src/assets/addis-cafe.jpg rename to client/src/assets/addis-cafe.jpg diff --git a/src/assets/heroSection.png b/client/src/assets/heroSection.png old mode 100644 new mode 100755 similarity index 100% rename from src/assets/heroSection.png rename to client/src/assets/heroSection.png diff --git a/src/assets/heroSection2.jpg b/client/src/assets/heroSection2.jpg old mode 100644 new mode 100755 similarity index 100% rename from src/assets/heroSection2.jpg rename to client/src/assets/heroSection2.jpg diff --git a/src/assets/heroSection3.jpg b/client/src/assets/heroSection3.jpg old mode 100644 new mode 100755 similarity index 100% rename from src/assets/heroSection3.jpg rename to client/src/assets/heroSection3.jpg diff --git a/src/assets/heroSection4.jpg b/client/src/assets/heroSection4.jpg old mode 100644 new mode 100755 similarity index 100% rename from src/assets/heroSection4.jpg rename to client/src/assets/heroSection4.jpg diff --git a/src/assets/herosection5.jpg b/client/src/assets/herosection5.jpg old mode 100644 new mode 100755 similarity index 100% rename from src/assets/herosection5.jpg rename to client/src/assets/herosection5.jpg diff --git a/src/assets/italiano.webp b/client/src/assets/italiano.webp old mode 100644 new mode 100755 similarity index 100% rename from src/assets/italiano.webp rename to client/src/assets/italiano.webp diff --git a/src/assets/kategna.png b/client/src/assets/kategna.png old mode 100644 new mode 100755 similarity index 100% rename from src/assets/kategna.png rename to client/src/assets/kategna.png diff --git a/src/assets/lucy.jpg b/client/src/assets/lucy.jpg old mode 100644 new mode 100755 similarity index 100% rename from src/assets/lucy.jpg rename to client/src/assets/lucy.jpg diff --git a/src/assets/react.svg b/client/src/assets/react.svg old mode 100644 new mode 100755 similarity index 100% rename from src/assets/react.svg rename to client/src/assets/react.svg diff --git a/src/assets/tomoca.png b/client/src/assets/tomoca.png old mode 100644 new mode 100755 similarity index 100% rename from src/assets/tomoca.png rename to client/src/assets/tomoca.png diff --git a/src/assets/wa.jpg b/client/src/assets/wa.jpg old mode 100644 new mode 100755 similarity index 100% rename from src/assets/wa.jpg rename to client/src/assets/wa.jpg diff --git a/src/assets/wal.jpg b/client/src/assets/wal.jpg old mode 100644 new mode 100755 similarity index 100% rename from src/assets/wal.jpg rename to client/src/assets/wal.jpg diff --git a/src/components/common/Button.jsx b/client/src/components/common/Button.jsx old mode 100644 new mode 100755 similarity index 54% rename from src/components/common/Button.jsx rename to client/src/components/common/Button.jsx index 27d1845..7e4c902 --- a/src/components/common/Button.jsx +++ b/client/src/components/common/Button.jsx @@ -1,18 +1,16 @@ -import React from "react"; - -function Button({ children, onClick, type = "button", className = "", variant = "primary", disabled = false }) { - const base = `button button-${variant}`; - const disabledClass = disabled ? "button-disabled" : ""; - return ( - - ); -} - -export default Button; +import React from "react"; + +function Button({ children, onClick, type = "button", className = "", disabled = false }) { + return ( + + ); +} + +export default Button; diff --git a/src/components/common/Dropdown.jsx b/client/src/components/common/Dropdown.jsx old mode 100644 new mode 100755 similarity index 55% rename from src/components/common/Dropdown.jsx rename to client/src/components/common/Dropdown.jsx index 9a2baa9..3e1ea94 --- a/src/components/common/Dropdown.jsx +++ b/client/src/components/common/Dropdown.jsx @@ -1,18 +1,15 @@ -import React from "react"; - -function Dropdown({ label, options = [], value, onChange, className = "" }) { - return ( -
- {label && } - -
- ); -} - -export default Dropdown; +import React from "react"; + +function Dropdown({ label, value, onChange, className = "", children }) { + return ( +
+ {label && } + +
+ ); +} + + +export default Dropdown; diff --git a/src/components/common/InputField.jsx b/client/src/components/common/InputField.jsx old mode 100644 new mode 100755 similarity index 96% rename from src/components/common/InputField.jsx rename to client/src/components/common/InputField.jsx index e826de1..637ae8b --- a/src/components/common/InputField.jsx +++ b/client/src/components/common/InputField.jsx @@ -1,20 +1,20 @@ -import React from "react"; - -function InputField({ label, type = "text", value, onChange, placeholder = "", className = "", name }) { - return ( -
- {label && } - -
- ); -} - -export default InputField; +import React from "react"; + +function InputField({ label, type = "text", value, onChange, placeholder = "", className = "", name }) { + return ( +
+ {label && } + +
+ ); +} + +export default InputField; diff --git a/src/components/common/MapSection.jsx b/client/src/components/common/MapSection.jsx old mode 100644 new mode 100755 similarity index 97% rename from src/components/common/MapSection.jsx rename to client/src/components/common/MapSection.jsx index 2d713b3..fb1b924 --- a/src/components/common/MapSection.jsx +++ b/client/src/components/common/MapSection.jsx @@ -1,41 +1,41 @@ -import React from 'react'; -import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'; -import 'leaflet/dist/leaflet.css'; -import L from 'leaflet'; - -// Fix for default marker icon missing in React Leaflet -// We need to import the images explicitly to work with Vite/Webpack -import markerIcon2x from 'leaflet/dist/images/marker-icon-2x.png'; -import markerIcon from 'leaflet/dist/images/marker-icon.png'; -import markerShadow from 'leaflet/dist/images/marker-shadow.png'; - -delete L.Icon.Default.prototype._getIconUrl; - -L.Icon.Default.mergeOptions({ - iconRetinaUrl: markerIcon2x, - iconUrl: markerIcon, - shadowUrl: markerShadow, -}); - -const MapSection = ({ location, name }) => { - // Default to Addis Ababa if no location - const position = location && location.lat && location.lng ? [location.lat, location.lng] : [9.03, 38.74]; - - return ( -
- - - - - {name || "Location"} - - - -
- ); -}; - -export default MapSection; +import React from 'react'; +import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'; +import 'leaflet/dist/leaflet.css'; +import L from 'leaflet'; + +// Fix for default marker icon missing in React Leaflet +// We need to import the images explicitly to work with Vite/Webpack +import markerIcon2x from 'leaflet/dist/images/marker-icon-2x.png'; +import markerIcon from 'leaflet/dist/images/marker-icon.png'; +import markerShadow from 'leaflet/dist/images/marker-shadow.png'; + +delete L.Icon.Default.prototype._getIconUrl; + +L.Icon.Default.mergeOptions({ + iconRetinaUrl: markerIcon2x, + iconUrl: markerIcon, + shadowUrl: markerShadow, +}); + +const MapSection = ({ location, name }) => { + // Default to Addis Ababa if no location + const position = location && location.lat && location.lng ? [location.lat, location.lng] : [9.03, 38.74]; + + return ( +
+ + + + + {name || "Location"} + + + +
+ ); +}; + +export default MapSection; diff --git a/src/components/common/Modal.jsx b/client/src/components/common/Modal.jsx old mode 100644 new mode 100755 similarity index 96% rename from src/components/common/Modal.jsx rename to client/src/components/common/Modal.jsx index a5c4ad9..2e1a1fc --- a/src/components/common/Modal.jsx +++ b/client/src/components/common/Modal.jsx @@ -1,18 +1,18 @@ -import React from "react"; - -function Modal({ title, children, open = false, onClose }) { - if (!open) return null; - return ( -
-
-
-

{title}

- -
-
{children}
-
-
- ); -} - -export default Modal; +import React from "react"; + +function Modal({ title, children, open = false, onClose }) { + if (!open) return null; + return ( +
+
+
+

{title}

+ +
+
{children}
+
+
+ ); +} + +export default Modal; diff --git a/client/src/components/common/Popups.jsx b/client/src/components/common/Popups.jsx new file mode 100644 index 0000000..772b4ce --- /dev/null +++ b/client/src/components/common/Popups.jsx @@ -0,0 +1,35 @@ +import React, { useState, useRef, useEffect } from "react"; +import { Link } from "react-router-dom"; + +function PopupButton() { + const [open, setOpen] = useState(false); + const containerRef = useRef(null); + + // Close the popup if clicked outside + useEffect(() => { + const handleClickOutside = (event) => { + if (containerRef.current && !containerRef.current.contains(event.target)) { + setOpen(false); + } + }; + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, []); + + return ( +
+ setOpen(!open)}>Sign up + + {open && ( +
+ User + Owner +
+ )} +
+ ); +} + +export default PopupButton; \ No newline at end of file diff --git a/src/components/common/StarRating.jsx b/client/src/components/common/StarRating.jsx old mode 100644 new mode 100755 similarity index 96% rename from src/components/common/StarRating.jsx rename to client/src/components/common/StarRating.jsx index 631cecf..9f47b1b --- a/src/components/common/StarRating.jsx +++ b/client/src/components/common/StarRating.jsx @@ -1,24 +1,24 @@ -import React from "react"; - -function StarRating({ value = 0, size = 16 }) { - const full = Math.round(value); - return ( -
- {Array.from({ length: 5 }).map((_, i) => ( - - ))} - {value.toFixed(1)} -
- ); -} - -export default StarRating; +import React from "react"; + +function StarRating({ value = 0, size = 16 }) { + const full = Math.round(value); + return ( +
+ {Array.from({ length: 5 }).map((_, i) => ( + + ))} + {value.toFixed(1)} +
+ ); +} + +export default StarRating; diff --git a/src/components/home/CategoryCards.jsx b/client/src/components/home/CategoryCards.jsx old mode 100644 new mode 100755 similarity index 72% rename from src/components/home/CategoryCards.jsx rename to client/src/components/home/CategoryCards.jsx index ae95a89..90b7933 --- a/src/components/home/CategoryCards.jsx +++ b/client/src/components/home/CategoryCards.jsx @@ -1,17 +1,17 @@ -import React from "react"; -import { Link } from "react-router-dom"; - -function CategoryCards() { - const cats = ["Ethiopian", "Italian", "Cafe", "Fast food"]; - return ( -
- {cats.map((c) => ( - - {c} - - ))} -
- ); -} - -export default CategoryCards; +import React from "react"; +import { Link } from "react-router-dom"; + +function CategoryCards({ restaurants }) { +const cats = [...new Set(restaurants.map(r => r.category))]; + return ( +
+ {cats.map((c) => ( + + {c} + + ))} +
+ ); +} + +export default CategoryCards; diff --git a/src/components/home/FeaturedCard.jsx b/client/src/components/home/FeaturedCard.jsx old mode 100644 new mode 100755 similarity index 82% rename from src/components/home/FeaturedCard.jsx rename to client/src/components/home/FeaturedCard.jsx index 52a26cb..533f575 --- a/src/components/home/FeaturedCard.jsx +++ b/client/src/components/home/FeaturedCard.jsx @@ -1,86 +1,90 @@ -import React, { useContext, useRef, useState } from "react"; -import { Link } from "react-router-dom"; -import RestaurantsContext from "../../context/RestaurantsContext"; -import StarRating from "../common/StarRating"; -import LucyImg from '../../assets/lucy.jpg'; -import ItalianoImg from '../../assets/italiano.webp'; -import AddisImg from '../../assets/addis-cafe.jpg'; -import KategnaImg from '../../assets/kategna.png'; -import TomocaImg from '../../assets/tomoca.png'; -import Placeholder from "../../assets/addis-cafe.jpg"; - - -function FeaturedCard({ restaurant }) { - const { toggleFavorite, isFavorite, updateRestaurantImage } = useContext(RestaurantsContext); - const fav = isFavorite ? isFavorite(restaurant.id) : false; - const inputRef = useRef(null); - const [uploading, setUploading] = useState(false); - - // Default images for known restaurants - const defaults = { - lucy: LucyImg, - italiano: ItalianoImg, - addiscafe: AddisImg, - kategna: KategnaImg, - tomoca: TomocaImg, - }; - - // FINAL IMAGE LOGIC: first image > uploaded image > defaults > placeholder - const finalImg = - (restaurant.images && restaurant.images.length > 0 && restaurant.images[0]) || - restaurant.image || - defaults[restaurant.id] || - Placeholder; - - return ( -
- -
-
- - -
-
-

- {restaurant.name} -

- -
- -
- {restaurant.category && ( -
- {restaurant.category} {restaurant.price ? `• ${restaurant.price}` : ''} -
- )} - {typeof restaurant.rating !== 'undefined' && ( -
- {restaurant.rating} -
- )} -
- -

{restaurant.description}

- -
- {restaurant.address &&
{restaurant.address}
} - View -
-
-
- ); -} - -export default FeaturedCard; +import React, { useContext, useRef, useState } from "react"; +import AuthContext from "../../context/AuthContext"; +import { Link } from "react-router-dom"; +import axios from "axios"; +import RestaurantsContext from "../../context/RestaurantsContext"; +import StarRating from "../common/StarRating"; +import LucyImg from '../../assets/lucy.jpg'; +import ItalianoImg from '../../assets/italiano.webp'; +import AddisImg from '../../assets/addis-cafe.jpg'; +import KategnaImg from '../../assets/kategna.png'; +import TomocaImg from '../../assets/tomoca.png'; +import Placeholder from "../../assets/addis-cafe.jpg"; + + +function FeaturedCard({ restaurant }) { + + const { toggleFavorite, isFavorite, updateRestaurantImage } = useContext(RestaurantsContext); + const fav = isFavorite ? isFavorite(restaurant._id) : false; + + const inputRef = useRef(null); + const [uploading, setUploading] = useState(false); + + // Default images for known restaurants + const defaults = { + lucy: LucyImg, + italiano: ItalianoImg, + addiscafe: AddisImg, + kategna: KategnaImg, + tomoca: TomocaImg, + }; + + // FINAL IMAGE LOGIC: first image > uploaded image > defaults > placeholder + const finalImg = + (restaurant.images && restaurant.images.length > 0 && restaurant.images[0]) || + restaurant.image || + defaults[restaurant._id] || + Placeholder; + + return ( +
+ +
+
+ + +
+
+

+ {restaurant.name} +

+ +
+ +
+ {restaurant.category && ( +
+ {restaurant.category} {restaurant.price ? `• ${restaurant.price}` : ''} +
+ )} + {typeof restaurant.rating !== 'undefined' && ( +
+ {restaurant.rating} +
+ )} +
+ +

{restaurant.description}

+ +
+ {restaurant.address &&
{restaurant.address}
} + View +
+
+
+ ); +} + +export default FeaturedCard; diff --git a/src/components/home/HeroSection.jsx b/client/src/components/home/HeroSection.jsx old mode 100644 new mode 100755 similarity index 97% rename from src/components/home/HeroSection.jsx rename to client/src/components/home/HeroSection.jsx index 51a190a..3e9e8d4 --- a/src/components/home/HeroSection.jsx +++ b/client/src/components/home/HeroSection.jsx @@ -1,62 +1,62 @@ -import React, { useEffect, useState } from "react"; -import Button from "../common/Button"; -import hero1 from '../../assets/heroSection.png'; -import hero2 from '../../assets/heroSection2.jpg'; -import hero3 from '../../assets/heroSection3.jpg'; -import hero4 from '../../assets/heroSection4.jpg'; -import hero5 from '../../assets/herosection5.jpg'; - -function HeroSection({ onQuickSearch }) { - const slides = [hero1, hero2, hero3, hero4, hero5].filter(Boolean); - const [index, setIndex] = useState(0); - - useEffect(() => { - const id = setInterval(() => { - setIndex((i) => (i + 1) % slides.length); - }, 5000); - return () => clearInterval(id); - }, [slides.length]); - - function prev() { - setIndex((i) => (i - 1 + slides.length) % slides.length); - } - - function next() { - setIndex((i) => (i + 1) % slides.length); - } - - return ( -
-
- ); -} - -export default HeroSection; +import React, { useEffect, useState } from "react"; +import Button from "../common/Button"; +import hero1 from '../../assets/heroSection.png'; +import hero2 from '../../assets/heroSection2.jpg'; +import hero3 from '../../assets/heroSection3.jpg'; +import hero4 from '../../assets/heroSection4.jpg'; +import hero5 from '../../assets/herosection5.jpg'; + +function HeroSection({ onQuickSearch }) { + const slides = [hero1, hero2, hero3, hero4, hero5].filter(Boolean); + const [index, setIndex] = useState(0); + + useEffect(() => { + const id = setInterval(() => { + setIndex((i) => (i + 1) % slides.length); + }, 5000); + return () => clearInterval(id); + }, [slides.length]); + + function prev() { + setIndex((i) => (i - 1 + slides.length) % slides.length); + } + + function next() { + setIndex((i) => (i + 1) % slides.length); + } + + return ( +
+
+ ); +} + +export default HeroSection; diff --git a/src/components/index.js b/client/src/components/index.js old mode 100644 new mode 100755 similarity index 98% rename from src/components/index.js rename to client/src/components/index.js index 95dde95..79a58b0 --- a/src/components/index.js +++ b/client/src/components/index.js @@ -1,23 +1,23 @@ -// Barrel exports for common components to simplify imports -export { default as Button } from './common/Button' -export { default as Dropdown } from './common/Dropdown' -export { default as InputField } from './common/InputField' -export { default as Modal } from './common/Modal' -export { default as StarRating } from './common/StarRating' - -export { default as HeroSection } from './home/HeroSection' -export { default as CategoryCards } from './home/CategoryCards' -export { default as FeaturedCard } from './home/FeaturedCard' - -export { default as Navbar } from './layout/Navbar' -export { default as Footer } from './layout/Footer' -export { default as Sidebar } from './layout/Sidebar' - -export { default as RestaurantCard } from './restaurant/RestaurantCard' -export { default as RestaurantList } from './restaurant/RestaurantList' -export { default as MenuSection } from './restaurant/MenuSection' -export { default as Reviewcard } from './restaurant/Reviewcard' - -export { default as LoginForm } from './user/LoginForm' -export { default as SignupForm } from './user/SignupForm' -export { default as UserProfile } from './user/UserProfile' +// Barrel exports for common components to simplify imports +export { default as Button } from './common/Button' +export { default as Dropdown } from './common/Dropdown' +export { default as InputField } from './common/InputField' +export { default as Modal } from './common/Modal' +export { default as StarRating } from './common/StarRating' + +export { default as HeroSection } from './home/HeroSection' +export { default as CategoryCards } from './home/CategoryCards' +export { default as FeaturedCard } from './home/FeaturedCard' + +export { default as Navbar } from './layout/Navbar' +export { default as Footer } from './layout/Footer' +export { default as Sidebar } from './layout/Sidebar' + +export { default as RestaurantCard } from './restaurant/RestaurantCard' +export { default as RestaurantList } from './restaurant/RestaurantList' +export { default as MenuSection } from './restaurant/MenuSection' +export { default as Reviewcard } from './restaurant/Reviewcard' + +export { default as LoginForm } from './user/LoginForm' +export { default as SignupForm } from './user/SignupForm' +export { default as UserProfile } from './user/UserProfile' diff --git a/src/components/layout/Footer.jsx b/client/src/components/layout/Footer.jsx old mode 100644 new mode 100755 similarity index 96% rename from src/components/layout/Footer.jsx rename to client/src/components/layout/Footer.jsx index f5e53bf..808a062 --- a/src/components/layout/Footer.jsx +++ b/client/src/components/layout/Footer.jsx @@ -1,17 +1,17 @@ -import React from "react"; - -function Footer() { - return ( -
-
-
© {new Date().getFullYear()} Find Addis
-
- About - Contact -
-
-
- ); -} - -export default Footer; +import React from "react"; + +function Footer() { + return ( +
+
+
© {new Date().getFullYear()} Find Addis
+
+ About + Contact +
+
+
+ ); +} + +export default Footer; diff --git a/client/src/components/layout/Navbar.jsx b/client/src/components/layout/Navbar.jsx new file mode 100755 index 0000000..726fb79 --- /dev/null +++ b/client/src/components/layout/Navbar.jsx @@ -0,0 +1,144 @@ +import React, { useContext, useState, useRef, useEffect } from "react"; +import { Link, useNavigate } from "react-router-dom"; +import AuthContext from "../../context/AuthContext"; + +function Navbar({ onSearch }) { + const { user } = useContext(AuthContext); + const navigate = useNavigate(); + const [q, setQ] = useState(""); + const [signupOption, setSignupOption] = useState(false); + const [loginOption, setLoginOption] = useState(false); + + const loginRef = useRef(null); + const signupRef = useRef(null); + + // Close popups if clicked outside + useEffect(() => { + const handleClickOutside = (event) => { + if (loginRef.current && !loginRef.current.contains(event.target)) { + setLoginOption(false); + } + if (signupRef.current && !signupRef.current.contains(event.target)) { + setSignupOption(false); + } + }; + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, []); + + const toggleLogin = () => setLoginOption(!loginOption); + const toggleSignup = () => setSignupOption(!signupOption); + + const submitSearch = (e) => { + e.preventDefault(); + const qs = q.trim(); + if (!qs) return; + navigate(`/search?q=${encodeURIComponent(qs)}`); + if (onSearch) onSearch(qs); + }; + + return ( +
+
+ Find Addis +
+ +
+
+ + setQ(e.target.value)} + /> +
+ +
+ +
+ + +
+ {user ? ( +
+ + Hi, {user.name} + +
+ ) : ( + <> + {/* LOGIN */} +
+ { e.preventDefault(); toggleLogin(); }} + className="nav-link auth-login" + > + Log In + + + {loginOption && ( +
+ User + Owner +
+ )} +
+ + {/* SIGNUP */} +
+ { e.preventDefault(); toggleSignup(); }} + className="button button-primary auth-signup" + > + Sign Up + + + {signupOption && ( +
+ User + Owner +
+ )} +
+ + )} +
+
+
+ ); +} + +export default Navbar; diff --git a/src/components/layout/Sidebar.jsx b/client/src/components/layout/Sidebar.jsx old mode 100644 new mode 100755 similarity index 63% rename from src/components/layout/Sidebar.jsx rename to client/src/components/layout/Sidebar.jsx index b0aebf6..f398e35 --- a/src/components/layout/Sidebar.jsx +++ b/client/src/components/layout/Sidebar.jsx @@ -1,38 +1,45 @@ -import React from "react"; -import Dropdown from "../common/Dropdown"; - -function Sidebar({ filters, setFilters }) { - return ( - - ); -} - -export default Sidebar; +import React from "react"; +import Dropdown from "../common/Dropdown"; + +function Sidebar({ filters, setFilters, restaurants }) { + console.log("Sidebar", restaurants) + const uniqueCategories = [...new Set(restaurants.map(r => r.category))] + console.log('uniqueCategories', uniqueCategories) + return ( + + ); +} + +export default Sidebar; diff --git a/client/src/components/owner/OwnerLoginForm.jsx b/client/src/components/owner/OwnerLoginForm.jsx new file mode 100644 index 0000000..4258e9a --- /dev/null +++ b/client/src/components/owner/OwnerLoginForm.jsx @@ -0,0 +1,28 @@ +import React from "react"; +import LoginForm from "../components/user/LoginForm"; +import { useContext } from "react"; +import AuthContext from "../context/AuthContext"; +import { useNavigate } from "react-router-dom"; + +function OwnerLogin() { + const { login } = useContext(AuthContext); + const navigate = useNavigate(); + + async function handleLogin(credentials) { + try { + await login(credentials); + navigate("/"); + } catch (e) { + alert(e); + } + } + + return ( +
+

Log in

+ +
+ ); +} + +export default OwnerLogin; diff --git a/client/src/components/owner/OwnerProfile.jsx b/client/src/components/owner/OwnerProfile.jsx new file mode 100644 index 0000000..e69de29 diff --git a/client/src/components/owner/OwnerSignupForm.jsx b/client/src/components/owner/OwnerSignupForm.jsx new file mode 100644 index 0000000..fd83cf6 --- /dev/null +++ b/client/src/components/owner/OwnerSignupForm.jsx @@ -0,0 +1,55 @@ +import React from "react"; +import { useState } from "react"; +import InputField from "../common/InputField"; +import Button from "../common/Button"; + +function OwnerSignupForm({ onSignup }) { + const [form, setForm] = useState({ name: "", email: "", password: "", confirmPassword: "" }); + const [showPassword, setShowPassword] = useState(false); + function handleChange(e) { + setForm({ ...form, [e.target.name]: e.target.value }); + } + + async function handleSubmit(e) { + e.preventDefault(); + try { + const res = await fetch("http://localhost:3000/api/RestaurantOwners", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + name: form.name, + email: form.email, + password: form.password, + phoneNumber: form.phoneNumber + }) + }); + const data = await res.json(); + if (!res.ok) { + alert(data.message); + return; + } + alert("Account created successfully!"); + } catch (err) { + + console.error(err); + alert("Server error"); + } + if (onSignup) onSignup(form); + } + + return ( +
+ + + + + + + + + ); +} + +export default OwnerSignupForm; diff --git a/src/components/restaurant/MenuSection.jsx b/client/src/components/restaurant/MenuSection.jsx old mode 100644 new mode 100755 similarity index 95% rename from src/components/restaurant/MenuSection.jsx rename to client/src/components/restaurant/MenuSection.jsx index 10bb573..dc64067 --- a/src/components/restaurant/MenuSection.jsx +++ b/client/src/components/restaurant/MenuSection.jsx @@ -1,16 +1,16 @@ -import React from "react"; - -function MenuSection({ menu = [] }) { - return ( -
-

Menu

-
    - {menu.map((m, idx) => ( -
  • {m}
  • - ))} -
-
- ); -} - -export default MenuSection; +import React from "react"; + +function MenuSection({ menu = [] }) { + return ( +
+

Menu

+
    + {menu.map((m, idx) => ( +
  • {m}
  • + ))} +
+
+ ); +} + +export default MenuSection; diff --git a/src/components/restaurant/RestaurantCard.jsx b/client/src/components/restaurant/RestaurantCard.jsx old mode 100644 new mode 100755 similarity index 97% rename from src/components/restaurant/RestaurantCard.jsx rename to client/src/components/restaurant/RestaurantCard.jsx index 0cdd9cf..1f7af3b --- a/src/components/restaurant/RestaurantCard.jsx +++ b/client/src/components/restaurant/RestaurantCard.jsx @@ -1,81 +1,81 @@ -import React, { useContext } from "react"; -import { Link } from "react-router-dom"; -import StarRating from "../common/StarRating"; -import RestaurantsContext from "../../context/RestaurantsContext"; -import Placeholder from "../../assets/addis-cafe.jpg"; - - -function RestaurantCard({ restaurant }) { - const { toggleFavorite, isFavorite } = useContext(RestaurantsContext); - const fav = isFavorite ? isFavorite(restaurant.id) : false; - - const imgSrc = (restaurant.images && restaurant.images.length > 0 && restaurant.images[0]) || Placeholder; - - return ( -
-
- - {`${restaurant.name} - - -
-
- - {restaurant.name} - -
-
- {restaurant.category} • {restaurant.price} -
- -
-
{restaurant.address}
-
-
- ); -} - -export default RestaurantCard; +import React, { useContext } from "react"; +import { Link } from "react-router-dom"; +import StarRating from "../common/StarRating"; +import RestaurantsContext from "../../context/RestaurantsContext"; +import Placeholder from "../../assets/addis-cafe.jpg"; + + +function RestaurantCard({ restaurant }) { + const { toggleFavorite, isFavorite } = useContext(RestaurantsContext); + const fav = isFavorite ? isFavorite(restaurant.id) : false; + + const imgSrc = (restaurant.images && restaurant.images.length > 0 && restaurant.images[0]) || Placeholder; + + return ( +
+
+ + {`${restaurant.name} + + +
+
+ + {restaurant.name} + +
+
+ {restaurant.category} • {restaurant.price} +
+ +
+
{restaurant.address}
+
+
+ ); +} + +export default RestaurantCard; diff --git a/src/components/restaurant/RestaurantList.jsx b/client/src/components/restaurant/RestaurantList.jsx old mode 100644 new mode 100755 similarity index 60% rename from src/components/restaurant/RestaurantList.jsx rename to client/src/components/restaurant/RestaurantList.jsx index e177dbe..19a5f40 --- a/src/components/restaurant/RestaurantList.jsx +++ b/client/src/components/restaurant/RestaurantList.jsx @@ -1,22 +1,18 @@ -import React from "react"; -import RestaurantCard from "./RestaurantCard"; - -function RestaurantList({ restaurants }) { - if (!restaurants || restaurants.length === 0) { - return
No restaurants found.
; - } - - return ( -
-
- {restaurants.map((r) => ( -
- -
- ))} -
-
- ); -} - -export default RestaurantList; +import React from "react"; +import RestaurantCard from "./RestaurantCard"; + +function RestaurantList({ restaurants }) { + if (!restaurants || restaurants.length === 0) { + return
No restaurants found.
; + } + + return ( +
+ {restaurants.map((r) => ( + + ))} +
+ ); +} + +export default RestaurantList; diff --git a/src/components/restaurant/Reviewcard.jsx b/client/src/components/restaurant/Reviewcard.jsx old mode 100644 new mode 100755 similarity index 97% rename from src/components/restaurant/Reviewcard.jsx rename to client/src/components/restaurant/Reviewcard.jsx index fd9595a..4aee63a --- a/src/components/restaurant/Reviewcard.jsx +++ b/client/src/components/restaurant/Reviewcard.jsx @@ -1,63 +1,63 @@ -import React from "react"; -import StarRating from "../common/StarRating"; -import Modal from "../common/Modal"; -import { useState, useContext } from "react"; -import RestaurantsContext from "../../context/RestaurantsContext"; -import AuthContext from "../../context/AuthContext"; - -function ReviewCard({ review, restaurantId }) { - const { editReview, deleteReview } = useContext(RestaurantsContext); - const { user, isAdmin } = useContext(AuthContext); - const [open, setOpen] = useState(false); - const [form, setForm] = useState({ rating: review.rating, text: review.text }); - - function canEdit() { - if (!user) return false; - return isAdmin() || user.name === review.user || user.email === review.user; - } - - function handleSave() { - editReview(restaurantId, review.id, { rating: form.rating, text: form.text }); - setOpen(false); - } - - function handleDelete() { - if (confirm("Delete this review?")) { - deleteReview(restaurantId, review.id); - } - } - - return ( -
-
-
{review.user}
- -
-
{review.text}
-
{review.date}
- {canEdit() && ( -
- - -
- )} - - setOpen(false)}> -
- - setForm((f) => ({ ...f, rating: Number(e.target.value) }))} /> -
-
- - -
- -
- -
- ); -} - -export default WriteReviewPage; +import React from "react"; +import { useLocation, useNavigate } from "react-router-dom"; +import RestaurantsContext from "../context/RestaurantsContext"; +import { useContext } from "react"; + +function useQuery() { + return new URLSearchParams(useLocation().search); +} + +function WriteReviewPage() { + const { addReview } = useContext(RestaurantsContext); + const q = useQuery(); + const restaurantId = q.get("restaurant") || ""; + const [form, setForm] = React.useState({ user: "", rating: 5, text: "" }); + const navigate = useNavigate(); + + async function submit(e) { + e.preventDefault(); + if (!restaurantId) return; + const review = { id: `rv${Date.now()}`, user: form.user || "Anonymous", rating: Number(form.rating), text: form.text, date: new Date().toISOString().slice(0, 10) }; + await addReview(restaurantId, review); + navigate(`/restaurants/${restaurantId}`); + } + + return ( +
+

Write a review

+
+ + setForm({ ...form, user: e.target.value })} placeholder="Your name" /> + + + + +
+ +
+
+
+ ); +} + +export default WriteReviewPage; diff --git a/src/styles/base.css b/client/src/styles/base.css old mode 100644 new mode 100755 similarity index 95% rename from src/styles/base.css rename to client/src/styles/base.css index 31652ff..bb0491e --- a/src/styles/base.css +++ b/client/src/styles/base.css @@ -1,36 +1,36 @@ -/* ======================= - Reset & Base Styles -======================= */ -* { - box-sizing: border-box; - margin: 0; - padding: 0; - font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; -} - -body { - background-color: #fafafa; - color: #222; - line-height: 1.5; -} - -/* ensure the background covers full viewport behind content */ -.app-root { - min-height: 100vh; - position: relative; - overflow: hidden; -} - -/* blurred background using pseudo-element (keeps content sharp) */ -.app-root::before { - display: none; -} - -.app-root::after { - display: none; -} - -.app-root>* { - position: relative; - z-index: 0; +/* ======================= + Reset & Base Styles +======================= */ +* { + box-sizing: border-box; + margin: 0; + padding: 0; + font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; +} + +body { + background-color: #fafafa; + color: #222; + line-height: 1.5; +} + +/* ensure the background covers full viewport behind content */ +.app-root { + min-height: 100vh; + position: relative; + overflow: hidden; +} + +/* blurred background using pseudo-element (keeps content sharp) */ +.app-root::before { + display: none; +} + +.app-root::after { + display: none; +} + +.app-root>* { + position: relative; + z-index: 0; } \ No newline at end of file diff --git a/src/styles/components.css b/client/src/styles/components.css old mode 100644 new mode 100755 similarity index 84% rename from src/styles/components.css rename to client/src/styles/components.css index b949b48..5e9bee5 --- a/src/styles/components.css +++ b/client/src/styles/components.css @@ -1,155 +1,138 @@ -/* ======================= - Buttons -======================= */ -.button { - padding: 10px 16px; - border-radius: 8px; - cursor: pointer; - font-weight: 600; - border: none; - transition: all 0.2s ease; -} - -.button-primary { - background-color: var(--accent); - color: #fff; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.button-primary:hover { - background-color: var(--accent-dark); - box-shadow: 0 4px 12px rgba(59, 107, 116, 0.2); - transform: translateY(-1px); -} - -.button-outline { - background: transparent; - border: 1.5px solid var(--accent); - color: var(--accent); -} - -.button-outline:hover { - background: rgba(59, 107, 116, 0.05); -} - -.button-ghost { - background: transparent; - border: 1px solid #ddd; - color: #555; -} - -.button-ghost:hover { - background: #f9f9f9; -} - -.button-disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.button-small { - padding: 6px 12px; - font-size: 13px; -} - -/* ======================= - Inputs & Dropdowns -======================= */ -.input-field-container { - display: flex; - flex-direction: column; - gap: 6px; -} - -.input-label { - font-size: 13px; - color: #333; - font-weight: 500; -} - -.input-field { - padding: 10px 12px; - border-radius: 8px; - border: 1px solid #ddd; - font-size: 14px; - transition: border-color 0.2s; -} - -.input-field:focus { - border-color: var(--accent); - outline: none; -} - -.dropdown-container { - display: flex; - flex-direction: column; - gap: 6px; -} - -.dropdown-select { - padding: 10px 12px; - border-radius: 8px; - border: 1px solid #ddd; - font-size: 14px; -} - -/* ======================= - Star Ratings -======================= */ -.star-rating { - display: flex; - align-items: center; - gap: 4px; -} - -.star { - fill: #ddd; -} - -.star-filled { - fill: #f5b50a; -} - -.star-value { - font-size: 13px; - color: #555; -} - -/* ======================= - Modal -======================= */ -.modal-backdrop { - position: fixed; - inset: 0; - background: rgba(0, 0, 0, 0.4); - display: flex; - justify-content: center; - align-items: center; - z-index: 200; -} - -.modal-container { - background: #fff; - padding: 20px; - border-radius: 12px; - width: 90%; - max-width: 500px; -} - -.modal-header { - display: flex; - justify-content: space-between; - align-items: center; -} - -.modal-title { - font-size: 18px; - font-weight: 600; -} - -.modal-close { - font-size: 20px; - background: none; - border: none; - cursor: pointer; +/* ======================= + Buttons +======================= */ +.button { + padding: 10px 16px; + border-radius: 8px; + cursor: pointer; + font-weight: 600; + border: none; + transition: all 0.2s ease; +} + +.button-primary { + background-color: var(--accent); + color: #fff; +} + +.button-primary:hover { + background-color: var(--accent-dark); +} + +.button-disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.button-ghost { + background: transparent; + border: 1px solid #ddd; + color: white; +} + +.button-small { + padding: 6px 12px; + font-size: 13px; +} + +/* ======================= + Inputs & Dropdowns +======================= */ +.input-field-container { + display: flex; + flex-direction: column; + gap: 6px; +} + +.input-label { + font-size: 13px; + color: #333; + font-weight: 500; +} + +.input-field { + padding: 10px 12px; + border-radius: 8px; + border: 1px solid #ddd; + font-size: 14px; + transition: border-color 0.2s; +} + +.input-field:focus { + border-color: var(--accent); + outline: none; +} + +.dropdown-container { + display: flex; + flex-direction: column; + gap: 6px; +} + +.dropdown-select { + padding: 10px 12px; + border-radius: 8px; + border: 1px solid #ddd; + font-size: 14px; +} + +/* ======================= + Star Ratings +======================= */ +.star-rating { + display: flex; + align-items: center; + gap: 4px; +} + +.star { + fill: #ddd; +} + +.star-filled { + fill: #f5b50a; +} + +.star-value { + font-size: 13px; + color: #555; +} + +/* ======================= + Modal +======================= */ +.modal-backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.4); + display: flex; + justify-content: center; + align-items: center; + z-index: 200; +} + +.modal-container { + background: #fff; + padding: 20px; + border-radius: 12px; + width: 90%; + max-width: 500px; +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal-title { + font-size: 18px; + font-weight: 600; +} + +.modal-close { + font-size: 20px; + background: none; + border: none; + cursor: pointer; } \ No newline at end of file diff --git a/src/styles/home.css b/client/src/styles/home.css old mode 100644 new mode 100755 similarity index 94% rename from src/styles/home.css rename to client/src/styles/home.css index b951171..d7b3014 --- a/src/styles/home.css +++ b/client/src/styles/home.css @@ -1,206 +1,206 @@ -/* ======================= - Hero Section -======================= */ -.hero-section { - display: flex; - align-items: center; - gap: 40px; - padding: 50px 20px; -} - -.hero-left { - flex: 1; -} - -.hero-title { - font-size: 32px; - font-weight: 700; - margin-bottom: 12px; -} - -.hero-sub { - font-size: 16px; - color: #555; - margin-bottom: 20px; -} - -.hero-actions { - display: flex; - gap: 12px; -} - -.hero-right { - flex: 1; - display: flex; - justify-content: center; -} - -.hero-image-placeholder { - width: 100%; - height: 250px; - background-color: #ddd; - display: flex; - align-items: center; - justify-content: center; - color: #666; - font-weight: 600; - font-size: 18px; -} - -.hero-image { - width: 100%; - height: 380px; - background-size: cover; - background-position: center; - transition: background 300ms ease; -} - -.hero-indicators { - display: flex; - gap: 6px; - margin-top: 8px; - justify-content: center -} - -.hero-dot { - width: 10px; - height: 10px; - border-radius: 50%; - background: rgba(255, 255, 255, 0.6); - border: none; - cursor: pointer -} - -.hero-dot.active { - background: var(--accent) -} - -@media (max-width: 800px) { - .hero-image { - height: 220px - } -} - -/* Full-bleed hero background and overlay */ -.hero-section { - position: relative; - min-height: 420px; - display: block; - overflow: hidden; -} - -.hero-bg { - position: absolute; - inset: 0; - background-size: cover; - background-position: center; - z-index: 0; -} - -.hero-overlay { - position: absolute; - inset: 0; - background: linear-gradient(180deg, rgba(0, 0, 0, 0.35), rgba(0, 0, 0, 0.35)); - z-index: 1; -} - -.hero-content { - position: relative; - z-index: 2; - padding: 48px; - max-width: 1100px; - margin: 0 auto; -} - -.hero-left { - color: #fff; - max-width: 640px; -} - -.hero-title { - color: #fff; - font-size: 36px; - line-height: 1.05; -} - -.hero-sub { - color: rgba(255, 255, 255, 0.9); - margin-top: 12px; - margin-bottom: 18px; -} - -/* nav buttons */ -.hero-nav { - position: absolute; - top: 50%; - transform: translateY(-50%); - z-index: 3; - background: rgba(0, 0, 0, 0.45); - color: #fff; - border: none; - width: 44px; - height: 44px; - border-radius: 22px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 24px; -} - -.hero-prev { - left: 12px -} - -.hero-next { - right: 12px -} - -.hero-indicators-bottom { - position: absolute; - left: 0; - right: 0; - bottom: 14px; - display: flex; - gap: 8px; - justify-content: center; - z-index: 4 -} - -@media (max-width: 800px) { - .hero-title { - font-size: 24px - } - - .hero-content { - padding: 20px - } - - .hero-section { - flex-direction: column; - } -} - -/* ======================= - Category Cards -======================= */ -.category-cards { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); - gap: 16px; - margin-top: 12px; -} - -.category-card { - padding: 20px; - background-color: #fff; - border-radius: 12px; - text-align: center; - text-decoration: none; -} - -.category-card:hover { - background-color: var(--accent); - color: #fff; - transform: translateY(-2px); +/* ======================= + Hero Section +======================= */ +.hero-section { + display: flex; + align-items: center; + gap: 40px; + padding: 50px 20px; +} + +.hero-left { + flex: 1; +} + +.hero-title { + font-size: 32px; + font-weight: 700; + margin-bottom: 12px; +} + +.hero-sub { + font-size: 16px; + color: #555; + margin-bottom: 20px; +} + +.hero-actions { + display: flex; + gap: 12px; +} + +.hero-right { + flex: 1; + display: flex; + justify-content: center; +} + +.hero-image-placeholder { + width: 100%; + height: 250px; + background-color: #ddd; + display: flex; + align-items: center; + justify-content: center; + color: #666; + font-weight: 600; + font-size: 18px; +} + +.hero-image { + width: 100%; + height: 380px; + background-size: cover; + background-position: center; + transition: background 300ms ease; +} + +.hero-indicators { + display: flex; + gap: 6px; + margin-top: 8px; + justify-content: center +} + +.hero-dot { + width: 10px; + height: 10px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.6); + border: none; + cursor: pointer +} + +.hero-dot.active { + background: var(--accent) +} + +@media (max-width: 800px) { + .hero-image { + height: 220px + } +} + +/* Full-bleed hero background and overlay */ +.hero-section { + position: relative; + min-height: 420px; + display: block; + overflow: hidden; +} + +.hero-bg { + position: absolute; + inset: 0; + background-size: cover; + background-position: center; + z-index: 0; +} + +.hero-overlay { + position: absolute; + inset: 0; + background: linear-gradient(180deg, rgba(0, 0, 0, 0.35), rgba(0, 0, 0, 0.35)); + z-index: 1; +} + +.hero-content { + position: relative; + z-index: 2; + padding: 48px; + max-width: 1100px; + margin: 0 auto; +} + +.hero-left { + color: #fff; + max-width: 640px; +} + +.hero-title { + color: #fff; + font-size: 36px; + line-height: 1.05; +} + +.hero-sub { + color: rgba(255, 255, 255, 0.9); + margin-top: 12px; + margin-bottom: 18px; +} + +/* nav buttons */ +.hero-nav { + position: absolute; + top: 50%; + transform: translateY(-50%); + z-index: 3; + background: rgba(0, 0, 0, 0.45); + color: #fff; + border: none; + width: 44px; + height: 44px; + border-radius: 22px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 24px; +} + +.hero-prev { + left: 12px +} + +.hero-next { + right: 12px +} + +.hero-indicators-bottom { + position: absolute; + left: 0; + right: 0; + bottom: 14px; + display: flex; + gap: 8px; + justify-content: center; + z-index: 4 +} + +@media (max-width: 800px) { + .hero-title { + font-size: 24px + } + + .hero-content { + padding: 20px + } + + .hero-section { + flex-direction: column; + } +} + +/* ======================= + Category Cards +======================= */ +.category-cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); + gap: 16px; + margin-top: 12px; +} + +.category-card { + padding: 20px; + background-color: #fff; + border-radius: 12px; + text-align: center; + text-decoration: none; +} + +.category-card:hover { + background-color: var(--accent); + color: #fff; + transform: translateY(-2px); } \ No newline at end of file diff --git a/src/styles/layout.css b/client/src/styles/layout.css old mode 100644 new mode 100755 similarity index 90% rename from src/styles/layout.css rename to client/src/styles/layout.css index cb0e737..3ec1f5e --- a/src/styles/layout.css +++ b/client/src/styles/layout.css @@ -1,278 +1,271 @@ -/* ======================= - Layout -======================= */ -.container { - max-width: 1100px; - margin: 0 auto; - padding: 20px; -} - -/* ======================= - Navbar -======================= */ -/* ======================= - Navbar -======================= */ -.nav-container { - display: flex; - align-items: center; - justify-content: space-between; - gap: 20px; - padding: 12px 20px; - border-bottom: 1px solid #eee; - background: #fff; - position: sticky; - top: 0; - z-index: 100; -} - -.nav-left { - display: flex; - align-items: center; - min-width: 140px; -} - -.nav-logo { - font-weight: 800; - color: var(--accent); - text-decoration: none; - font-size: 24px; - letter-spacing: -0.5px; -} - -/* Yelp-style Search Bar */ -.nav-search-bar { - flex: 1; - max-width: 700px; - display: flex; - align-items: center; - background: #fff; - border-radius: 8px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - margin: 0 20px; - border: 1px solid #ddd; - height: 46px; -} - -.search-group { - flex: 1; - display: flex; - align-items: center; - padding: 0 12px; - position: relative; - height: 100%; -} - -.search-label { - font-weight: 700; - font-size: 14px; - color: #333; - margin-right: 8px; - white-space: nowrap; - display: none; - /* Hide label on small screens or keep distinct? Yelp usually puts placeholder or label inside. */ -} - -/* Show labels on larger screens if desired equivalent to Yelp's left-side text in input group, - but typically it's placeholder. - Wait, the user image shows standard search. - Looking at Yelp desktop: It's "Find [input] | Near [input]". - The inputs have "tacos" etc placeholder. - I will make the 'Find' and 'Near' text essentially labels that sit next to input. -*/ -.search-label { - display: block; - color: #666; - font-size: 14px; - font-weight: 700; -} - -.search-input { - border: none; - outline: none; - width: 100%; - font-size: 15px; - color: #333; - padding: 4px; - background: transparent; -} - -.search-input::placeholder { - color: #999; - font-weight: 400; -} - -.search-divider { - width: 1px; - height: 24px; - background: #ddd; -} - -.search-btn { - width: 48px; - height: 46px; - background: var(--accent); - border: none; - border-top-right-radius: 8px; - border-bottom-right-radius: 8px; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - color: white; - transition: background 0.2s; -} - -.search-btn:hover { - background: var(--accent-dark); -} - -/* Right Side Actions */ -.nav-right { - display: flex; - align-items: center; - gap: 24px; -} - -.nav-actions { - display: flex; - gap: 20px; -} - -.nav-actions .nav-link { - font-weight: 600; - color: #555; - font-size: 14px; - text-decoration: none; - transition: color 0.1s; -} - -.nav-actions .nav-link:hover { - color: var(--accent); - text-decoration: none; -} - -.nav-auth { - display: flex; - align-items: center; - gap: 10px; -} - -.auth-login { - font-weight: 700; - color: #333; - padding: 8px 16px; - border-radius: 8px; - text-decoration: none; - font-size: 14px; - transition: all 0.2s; - border: 1px solid transparent; -} - -.auth-login:hover { - color: var(--accent); - background: rgba(59, 107, 116, 0.05); /* very light teal */ -} - -.auth-signup { - padding: 8px 18px; - border-radius: 8px; /* matching theme radius */ - font-size: 14px; - font-weight: 700; - text-decoration: none; - display: inline-block; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.auth-signup:hover { - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); - transform: translateY(-1px); -} - -/* ======================= - Footer -======================= */ -/* ======================= - Footer -======================= */ -.footer-container { - border-top: 1px solid #eee; - margin-top: 40px; - background: #fff; - padding: 0; -} - -.footer-content { - display: flex; - justify-content: space-between; - align-items: center; - padding: 40px 20px; -} - -.footer-copyright { - color: #888; - font-size: 14px; -} - -.footer-links { - display: flex; - gap: 24px; -} - -.footer-link { - text-decoration: none; - color: #555; - font-size: 14px; - font-weight: 500; - transition: color 0.2s; -} - -.footer-link:hover { - color: var(--accent); - text-decoration: underline; -} - -/* ======================= - Sidebar & Filters -======================= */ -.sidebar-container { - width: 260px; - background: #fff; - padding: 12px; - border-radius: 12px; - box-shadow: 0 1px 6px rgba(0, 0, 0, 0.05); -} - -.filter-block { - margin-bottom: 20px; -} - -.filter-title { - font-weight: 600; - margin-bottom: 6px; -} - -.filter-range { - width: 100%; -} - -.filter-range-value { - text-align: right; - font-size: 12px; - color: #555; -} - -/* Responsive Layout */ -@media (max-width: 900px) { - .nav-search-bar { - display: none; - } - - .nav-actions { - display: none; - } - - .sidebar-container { - display: none; - } +/* ======================= + Layout +======================= */ +.container { + max-width: 1100px; + margin: 0 auto; + padding: 20px; +} + +/* ======================= + Navbar +======================= */ +/* ======================= + Navbar +======================= */ +.nav-container { + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; + padding: 12px 20px; + border-bottom: 1px solid #eee; + background: #fff; + position: sticky; + top: 0; + z-index: 100; +} + +.nav-left { + display: flex; + align-items: center; + min-width: 140px; +} + +.nav-logo { + font-weight: 800; + color: var(--accent); + text-decoration: none; + font-size: 24px; + letter-spacing: -0.5px; +} + +/* Yelp-style Search Bar */ +.nav-search-bar { + flex: 1; + max-width: 700px; + display: flex; + align-items: center; + background: #fff; + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + margin: 0 20px; + border: 1px solid #ddd; + height: 46px; +} + +.search-group { + flex: 1; + display: flex; + align-items: center; + padding: 0 12px; + position: relative; + height: 100%; +} + +.search-label { + font-weight: 700; + font-size: 14px; + color: #333; + margin-right: 8px; + white-space: nowrap; + display: none; + /* Hide label on small screens or keep distinct? Yelp usually puts placeholder or label inside. */ +} + +/* Show labels on larger screens if desired equivalent to Yelp's left-side text in input group, + but typically it's placeholder. + Wait, the user image shows standard search. + Looking at Yelp desktop: It's "Find [input] | Near [input]". + The inputs have "tacos" etc placeholder. + I will make the 'Find' and 'Near' text essentially labels that sit next to input. +*/ +.search-label { + display: block; + color: #666; + font-size: 14px; + font-weight: 700; +} + +.search-input { + border: none; + outline: none; + width: 100%; + font-size: 15px; + color: #333; + padding: 4px; + background: transparent; +} + +.search-input::placeholder { + color: #999; + font-weight: 400; +} + +.search-divider { + width: 1px; + height: 24px; + background: #ddd; +} + +.search-btn { + width: 48px; + height: 46px; + background: var(--accent); + border: none; + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + color: white; + transition: background 0.2s; +} + +.search-btn:hover { + background: var(--accent-dark); +} + +/* Right Side Actions */ +.nav-right { + display: flex; + align-items: center; + gap: 24px; +} + +.nav-actions { + display: flex; + gap: 20px; +} + +.nav-actions .nav-link { + font-weight: 600; + color: #555; + font-size: 14px; + text-decoration: none; + transition: color 0.1s; +} + +.nav-actions .nav-link:hover { + color: var(--accent); + text-decoration: none; +} + +.nav-auth { + display: flex; + align-items: center; + gap: 10px; +} + +.auth-login { + font-weight: 700; + color: #555; + padding: 8px 16px; + border-radius: 4px; + text-decoration: none; + font-size: 14px; + transition: background 0.1s; +} + +.auth-login:hover { + background: rgba(0, 0, 0, 0.05); +} + +.auth-signup { + /* Primary button style overlay */ + padding: 8px 16px; + border-radius: 4px; + font-size: 14px; + font-weight: 700; + text-decoration: none; + display: inline-block; +} + +/* ======================= + Footer +======================= */ +/* ======================= + Footer +======================= */ +.footer-container { + border-top: 1px solid #eee; + margin-top: 40px; + background: #fff; + padding: 0; +} + +.footer-content { + display: flex; + justify-content: space-between; + align-items: center; + padding: 40px 20px; +} + +.footer-copyright { + color: #888; + font-size: 14px; +} + +.footer-links { + display: flex; + gap: 24px; +} + +.footer-link { + text-decoration: none; + color: #555; + font-size: 14px; + font-weight: 500; + transition: color 0.2s; +} + +.footer-link:hover { + color: var(--accent); + text-decoration: underline; +} + +/* ======================= + Sidebar & Filters +======================= */ +.sidebar-container { + width: 260px; + background: #fff; + padding: 12px; + border-radius: 12px; + box-shadow: 0 1px 6px rgba(0, 0, 0, 0.05); +} + +.filter-block { + margin-bottom: 20px; +} + +.filter-title { + font-weight: 600; + margin-bottom: 6px; +} + +.filter-range { + width: 100%; +} + +.filter-range-value { + text-align: right; + font-size: 12px; + color: #555; +} + +/* Responsive Layout */ +@media (max-width: 900px) { + .nav-search-bar { + display: none; + } + + .nav-actions { + display: none; + } + + .sidebar-container { + display: none; + } } \ No newline at end of file diff --git a/src/styles/main.css b/client/src/styles/main.css old mode 100644 new mode 100755 similarity index 95% rename from src/styles/main.css rename to client/src/styles/main.css index 7454959..8076058 --- a/src/styles/main.css +++ b/client/src/styles/main.css @@ -1,18 +1,18 @@ -/* ======================= - FindAddis Main Stylesheet - Imports all split CSS modules -======================= */ - -/* Base & Reset */ -@import './variables.css'; -@import './base.css'; - -/* Components & Common */ -@import './components.css'; -@import './utils.css'; -@import './layout.css'; - -/* Domain Specific */ -@import './user.css'; -@import './home.css'; +/* ======================= + FindAddis Main Stylesheet + Imports all split CSS modules +======================= */ + +/* Base & Reset */ +@import './variables.css'; +@import './base.css'; + +/* Components & Common */ +@import './components.css'; +@import './utils.css'; +@import './layout.css'; + +/* Domain Specific */ +@import './user.css'; +@import './home.css'; @import './restaurant.css'; \ No newline at end of file diff --git a/src/styles/restaurant.css b/client/src/styles/restaurant.css old mode 100644 new mode 100755 similarity index 94% rename from src/styles/restaurant.css rename to client/src/styles/restaurant.css index 198c5aa..8b1f453 --- a/src/styles/restaurant.css +++ b/client/src/styles/restaurant.css @@ -1,381 +1,381 @@ -/* Restaurant Cards & List */ -.restaurant-list { - display: grid; - gap: 20px; - grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); -} - -.featured-row { - display: flex; - gap: 16px; - overflow-x: auto; - padding: 8px 4px; - -webkit-overflow-scrolling: touch; - flex-wrap: nowrap; - align-items: stretch; -} - -.featured-row::-webkit-scrollbar { - height: 8px; -} - -.featured-row::-webkit-scrollbar-thumb { - background: rgba(0, 0, 0, 0.12); - border-radius: 8px; -} - -/* Featured Card */ -.featured-card { - min-width: 300px; - flex: 0 0 300px; - max-width: 340px; - display: flex; - flex-direction: column; - gap: 0; - align-items: stretch; - padding: 0; - background: var(--card-bg); - border-radius: 12px; - box-shadow: 0 8px 24px rgba(38, 50, 56, 0.06); -} - -/* Featured card visual styles */ -.featured-card-inner { - display: flex; - gap: 12px; - background: var(--card-bg); - border-radius: 12px; - padding: 12px; - box-shadow: 0 8px 24px rgba(38, 50, 56, 0.06); - align-items: stretch; - height: 100%; -} - -.featured-image-link { - width: 100%; - display: block; - height: 200px; - border-radius: 0; - overflow: hidden; -} - -.featured-image { - width: 100%; - height: 100%; - display: block; - background-size: cover; - background-position: center; - border-radius: 0; -} - -.featured-image.placeholder { - background: linear-gradient(90deg, #f3f4f6, #e9eef0); - width: 100%; - height: 100%; - border-radius: 0; -} - -.featured-image { - position: relative; -} - -.hidden-file-input { - display: none; -} - -.image-upload-btn { - position: absolute; - right: 8px; - top: 8px; - background: rgba(255, 255, 255, 0.9); - border: 1px solid rgba(0, 0, 0, 0.06); - border-radius: 18px; - padding: 6px 8px; - cursor: pointer; - font-size: 14px; -} - -.image-upload-btn:hover { - transform: scale(1.04); -} - -.featured-content { - flex: 1; - display: flex; - flex-direction: column; - gap: 8px; - padding: 16px; -} - -.featured-header { - display: flex; - justify-content: space-between; - align-items: start; - gap: 8px; -} - -.featured-title { - font-size: 18px; - margin: 0; - line-height: 1.3; -} - -.featured-title a { - color: var(--accent); - text-decoration: none; -} - -.featured-title a:hover { - text-decoration: underline; -} - -.featured-meta { - display: flex; - gap: 12px; - align-items: center; - font-size: 13px; - color: var(--muted); -} - -.featured-desc { - font-size: 13px; - color: #374151; - margin: 0 0 6px 0; -} - -.featured-footer { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: auto; -} - -.featured-address { - font-size: 13px; - color: var(--muted); -} - -/* Standard Restaurant Card */ -.restaurant-card { - display: flex; - flex-direction: column; - border-radius: 10px; - overflow: hidden; - background-color: var(--card-bg); - box-shadow: 0 6px 18px rgba(38, 50, 56, 0.06); - transition: transform 0.18s ease, box-shadow 0.18s ease; - background-color: #fff; - /* fallback */ -} - -.restaurant-card:hover { - transform: translateY(-4px); - box-shadow: 0 12px 30px rgba(38, 50, 56, 0.08); -} - -.restaurant-image { - height: 160px; - background-size: cover; - background-position: center; -} - -.restaurant-image-wrap { - position: relative; -} - -.restaurant-image-img { - width: 100%; - height: 160px; - object-fit: cover; - display: block; -} - -.restaurant-image-img.placeholder { - filter: grayscale(60%); - opacity: 0.95; -} - -/* Favorite Button shared */ -.favorite-btn { - position: absolute; - right: 10px; - top: 8px; - background: rgba(255, 255, 255, 0.95); - border: 1px solid #eee; - border-radius: 18px; - padding: 6px 8px; - cursor: pointer; - font-size: 14px; - line-height: 1; -} - -.featured-card .favorite-btn { - background: transparent; - border: 1px solid rgba(0, 0, 0, 0.06); - padding: 6px 8px; - border-radius: 8px; - cursor: pointer; - position: static; -} - -/* override for featured */ - -.favorite-btn.fav-active { - background: rgba(59, 107, 116, 0.08); - color: var(--accent); - border-color: rgba(59, 107, 116, 0.14); -} - -.favorite-btn svg { - display: block; - width: 18px; - height: 18px; - color: var(--accent); -} - -.favorite-btn { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 6px; - border-radius: 50%; -} - -.favorite-btn:hover { - transform: scale(1.04); -} - -.favorite-btn.fav-active svg { - color: var(--accent); -} - -.favorite-btn svg[fill="none"] { - color: var(--muted); -} - -.restaurant-card-body { - padding: 12px; - display: flex; - flex-direction: column; - gap: 6px; -} - -.restaurant-name { - font-weight: 700; - color: #222; - text-decoration: none; -} - -.restaurant-meta { - display: flex; - justify-content: space-between; - align-items: center; - font-size: 13px; - color: #555; -} - -.restaurant-address { - font-size: 13px; - color: #777; -} - -/* Reviews */ -.review-card { - border-top: 1px solid #eee; - padding: 10px 0; -} - -.review-header { - display: flex; - justify-content: space-between; - align-items: center; -} - -.review-text { - margin-top: 6px; - font-size: 14px; - color: #444; -} - -.review-date { - margin-top: 4px; - font-size: 12px; - color: #999; -} - -.review-actions { - margin-top: 8px; - display: flex; - gap: 8px; -} - -/* Restaurant Details Page */ -.details-grid { - display: grid; - grid-template-columns: 1fr 300px; - gap: 24px; -} - -.details-image { - height: 300px; - background-size: cover; - background-position: center; - border-radius: 8px; -} - -.restaurant-title { - font-size: 28px; - font-weight: 700; - margin-top: 12px; -} - -.restaurant-sub { - display: flex; - justify-content: space-between; - align-items: center; - margin: 8px 0; -} - -.restaurant-description { - margin: 12px 0; - font-size: 14px; - color: #555; -} - -.menu-section { - margin-top: 20px; -} - -.menu-title { - font-weight: 600; - margin-bottom: 8px; -} - -.menu-list { - list-style: none; -} - -.menu-item { - padding: 6px 0; - border-bottom: 1px solid #eee; -} - -/* Modal Actions */ -.modal-actions { - display: flex; - gap: 8px; - margin-top: 12px; -} - -.form-row { - display: flex; - flex-direction: column; - gap: 6px; - margin-bottom: 8px; -} - -@media (max-width: 880px) { - .details-grid { - grid-template-columns: 1fr; - } +/* Restaurant Cards & List */ +.restaurant-list { + display: grid; + gap: 20px; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); +} + +.featured-row { + display: flex; + gap: 16px; + overflow-x: auto; + padding: 8px 4px; + -webkit-overflow-scrolling: touch; + flex-wrap: nowrap; + align-items: stretch; +} + +.featured-row::-webkit-scrollbar { + height: 8px; +} + +.featured-row::-webkit-scrollbar-thumb { + background: rgba(0, 0, 0, 0.12); + border-radius: 8px; +} + +/* Featured Card */ +.featured-card { + min-width: 300px; + flex: 0 0 300px; + max-width: 340px; + display: flex; + flex-direction: column; + gap: 0; + align-items: stretch; + padding: 0; + background: var(--card-bg); + border-radius: 12px; + box-shadow: 0 8px 24px rgba(38, 50, 56, 0.06); +} + +/* Featured card visual styles */ +.featured-card-inner { + display: flex; + gap: 12px; + background: var(--card-bg); + border-radius: 12px; + padding: 12px; + box-shadow: 0 8px 24px rgba(38, 50, 56, 0.06); + align-items: stretch; + height: 100%; +} + +.featured-image-link { + width: 100%; + display: block; + height: 200px; + border-radius: 0; + overflow: hidden; +} + +.featured-image { + width: 100%; + height: 100%; + display: block; + background-size: cover; + background-position: center; + border-radius: 0; +} + +.featured-image.placeholder { + background: linear-gradient(90deg, #f3f4f6, #e9eef0); + width: 100%; + height: 100%; + border-radius: 0; +} + +.featured-image { + position: relative; +} + +.hidden-file-input { + display: none; +} + +.image-upload-btn { + position: absolute; + right: 8px; + top: 8px; + background: rgba(255, 255, 255, 0.9); + border: 1px solid rgba(0, 0, 0, 0.06); + border-radius: 18px; + padding: 6px 8px; + cursor: pointer; + font-size: 14px; +} + +.image-upload-btn:hover { + transform: scale(1.04); +} + +.featured-content { + flex: 1; + display: flex; + flex-direction: column; + gap: 8px; + padding: 16px; +} + +.featured-header { + display: flex; + justify-content: space-between; + align-items: start; + gap: 8px; +} + +.featured-title { + font-size: 18px; + margin: 0; + line-height: 1.3; +} + +.featured-title a { + color: var(--accent); + text-decoration: none; +} + +.featured-title a:hover { + text-decoration: underline; +} + +.featured-meta { + display: flex; + gap: 12px; + align-items: center; + font-size: 13px; + color: var(--muted); +} + +.featured-desc { + font-size: 13px; + color: #374151; + margin: 0 0 6px 0; +} + +.featured-footer { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: auto; +} + +.featured-address { + font-size: 13px; + color: var(--muted); +} + +/* Standard Restaurant Card */ +.restaurant-card { + display: flex; + flex-direction: column; + border-radius: 10px; + overflow: hidden; + background-color: var(--card-bg); + box-shadow: 0 6px 18px rgba(38, 50, 56, 0.06); + transition: transform 0.18s ease, box-shadow 0.18s ease; + background-color: #fff; + /* fallback */ +} + +.restaurant-card:hover { + transform: translateY(-4px); + box-shadow: 0 12px 30px rgba(38, 50, 56, 0.08); +} + +.restaurant-image { + height: 160px; + background-size: cover; + background-position: center; +} + +.restaurant-image-wrap { + position: relative; +} + +.restaurant-image-img { + width: 100%; + height: 160px; + object-fit: cover; + display: block; +} + +.restaurant-image-img.placeholder { + filter: grayscale(60%); + opacity: 0.95; +} + +/* Favorite Button shared */ +.favorite-btn { + position: absolute; + right: 10px; + top: 8px; + background: rgba(255, 255, 255, 0.95); + border: 1px solid #eee; + border-radius: 18px; + padding: 6px 8px; + cursor: pointer; + font-size: 14px; + line-height: 1; +} + +.featured-card .favorite-btn { + background: transparent; + border: 1px solid rgba(0, 0, 0, 0.06); + padding: 6px 8px; + border-radius: 8px; + cursor: pointer; + position: static; +} + +/* override for featured */ + +.favorite-btn.fav-active { + background: rgba(59, 107, 116, 0.08); + color: var(--accent); + border-color: rgba(59, 107, 116, 0.14); +} + +.favorite-btn svg { + display: block; + width: 18px; + height: 18px; + color: var(--accent); +} + +.favorite-btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 6px; + border-radius: 50%; +} + +.favorite-btn:hover { + transform: scale(1.04); +} + +.favorite-btn.fav-active svg { + color: var(--accent); +} + +.favorite-btn svg[fill="none"] { + color: var(--muted); +} + +.restaurant-card-body { + padding: 12px; + display: flex; + flex-direction: column; + gap: 6px; +} + +.restaurant-name { + font-weight: 700; + color: #222; + text-decoration: none; +} + +.restaurant-meta { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 13px; + color: #555; +} + +.restaurant-address { + font-size: 13px; + color: #777; +} + +/* Reviews */ +.review-card { + border-top: 1px solid #eee; + padding: 10px 0; +} + +.review-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.review-text { + margin-top: 6px; + font-size: 14px; + color: #444; +} + +.review-date { + margin-top: 4px; + font-size: 12px; + color: #999; +} + +.review-actions { + margin-top: 8px; + display: flex; + gap: 8px; +} + +/* Restaurant Details Page */ +.details-grid { + display: grid; + grid-template-columns: 1fr 300px; + gap: 24px; +} + +.details-image { + height: 300px; + background-size: cover; + background-position: center; + border-radius: 8px; +} + +.restaurant-title { + font-size: 28px; + font-weight: 700; + margin-top: 12px; +} + +.restaurant-sub { + display: flex; + justify-content: space-between; + align-items: center; + margin: 8px 0; +} + +.restaurant-description { + margin: 12px 0; + font-size: 14px; + color: #555; +} + +.menu-section { + margin-top: 20px; +} + +.menu-title { + font-weight: 600; + margin-bottom: 8px; +} + +.menu-list { + list-style: none; +} + +.menu-item { + padding: 6px 0; + border-bottom: 1px solid #eee; +} + +/* Modal Actions */ +.modal-actions { + display: flex; + gap: 8px; + margin-top: 12px; +} + +.form-row { + display: flex; + flex-direction: column; + gap: 6px; + margin-bottom: 8px; +} + +@media (max-width: 880px) { + .details-grid { + grid-template-columns: 1fr; + } } \ No newline at end of file diff --git a/src/styles/user.css b/client/src/styles/user.css old mode 100644 new mode 100755 similarity index 93% rename from src/styles/user.css rename to client/src/styles/user.css index 0abed01..d43127e --- a/src/styles/user.css +++ b/client/src/styles/user.css @@ -1,50 +1,50 @@ -/* ======================= - Forms -======================= */ -.login-form, -.signup-form, -.review-form { - display: flex; - flex-direction: column; - gap: 12px; -} - -.form-actions { - margin-top: 12px; -} - -/* ======================= - User Profile -======================= */ -.user-profile { - display: flex; - align-items: center; - gap: 12px; -} - -.user-avatar-placeholder { - width: 60px; - height: 60px; - background-color: #ddd; - border-radius: 50%; - display: flex; - justify-content: center; - align-items: center; - font-weight: 700; - font-size: 20px; -} - -.user-info { - display: flex; - flex-direction: column; -} - -.user-name { - font-weight: 600; - font-size: 16px; -} - -.user-email { - font-size: 13px; - color: #555; +/* ======================= + Forms +======================= */ +.login-form, +.signup-form, +.review-form { + display: flex; + flex-direction: column; + gap: 12px; +} + +.form-actions { + margin-top: 12px; +} + +/* ======================= + User Profile +======================= */ +.user-profile { + display: flex; + align-items: center; + gap: 12px; +} + +.user-avatar-placeholder { + width: 60px; + height: 60px; + background-color: #ddd; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + font-weight: 700; + font-size: 20px; +} + +.user-info { + display: flex; + flex-direction: column; +} + +.user-name { + font-weight: 600; + font-size: 16px; +} + +.user-email { + font-size: 13px; + color: #555; } \ No newline at end of file diff --git a/src/styles/utils.css b/client/src/styles/utils.css old mode 100644 new mode 100755 similarity index 93% rename from src/styles/utils.css rename to client/src/styles/utils.css index 1fea57c..d226d05 --- a/src/styles/utils.css +++ b/client/src/styles/utils.css @@ -1,17 +1,17 @@ -/* ======================= - Pages Generic -======================= */ -.page-title { - font-size: 20px; - margin-bottom: 16px; -} - -.muted { - color: #777; -} - -.empty-list { - padding: 40px; - text-align: center; - color: #999; +/* ======================= + Pages Generic +======================= */ +.page-title { + font-size: 20px; + margin-bottom: 16px; +} + +.muted { + color: #777; +} + +.empty-list { + padding: 40px; + text-align: center; + color: #999; } \ No newline at end of file diff --git a/src/styles/variables.css b/client/src/styles/variables.css old mode 100644 new mode 100755 similarity index 96% rename from src/styles/variables.css rename to client/src/styles/variables.css index 9219c3c..93f73c4 --- a/src/styles/variables.css +++ b/client/src/styles/variables.css @@ -1,8 +1,8 @@ -:root { - --accent: #3b6b74; - /* muted teal/gray */ - --accent-dark: #2f5b61; - --muted: #6b7280; - --card-bg: #ffffff; - --overlay: rgba(255, 255, 255, 0.72); +:root { + --accent: #3b6b74; + /* muted teal/gray */ + --accent-dark: #2f5b61; + --muted: #6b7280; + --card-bg: #ffffff; + --overlay: rgba(255, 255, 255, 0.72); } \ No newline at end of file diff --git a/vite.config.js b/client/vite.config.js old mode 100644 new mode 100755 similarity index 100% rename from vite.config.js rename to client/vite.config.js diff --git a/package-lock.json b/package-lock.json index 303929e..78b3c08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,23 @@ { - "name": "package.json", - "version": "0.0.0", + "name": "find-addis", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "package.json", + "name": "find-addis", + "workspaces": [ + "client", + "server" + ], + "devDependencies": { + "concurrently": "^9.2.1" + } + }, + "client": { "version": "0.0.0", "dependencies": { "axios": "^1.13.2", - "bootstrap": "^5.3.8", + "jwt-decode": "^4.0.0", "leaflet": "^1.9.4", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -29,13 +37,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -44,9 +52,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", "dev": true, "license": "MIT", "engines": { @@ -54,22 +62,21 @@ } }, "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -86,14 +93,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -103,13 +110,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", + "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -130,29 +137,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -162,9 +169,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true, "license": "MIT", "engines": { @@ -202,27 +209,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.28.6" }, "bin": { "parser": "bin/babel-parser.js" @@ -264,33 +271,33 @@ } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", "debug": "^4.3.1" }, "engines": { @@ -298,9 +305,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", "dev": true, "license": "MIT", "dependencies": { @@ -312,9 +319,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", "cpu": [ "ppc64" ], @@ -329,9 +336,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", "cpu": [ "arm" ], @@ -346,9 +353,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", "cpu": [ "arm64" ], @@ -363,9 +370,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", "cpu": [ "x64" ], @@ -380,9 +387,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", "cpu": [ "arm64" ], @@ -397,9 +404,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", "cpu": [ "x64" ], @@ -414,9 +421,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", "cpu": [ "arm64" ], @@ -431,9 +438,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", "cpu": [ "x64" ], @@ -448,9 +455,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", "cpu": [ "arm" ], @@ -465,9 +472,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", "cpu": [ "arm64" ], @@ -482,9 +489,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", "cpu": [ "ia32" ], @@ -499,9 +506,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", "cpu": [ "loong64" ], @@ -516,9 +523,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", "cpu": [ "mips64el" ], @@ -533,9 +540,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", "cpu": [ "ppc64" ], @@ -550,9 +557,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", "cpu": [ "riscv64" ], @@ -567,9 +574,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", "cpu": [ "s390x" ], @@ -584,9 +591,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", "cpu": [ "x64" ], @@ -601,9 +608,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", "cpu": [ "arm64" ], @@ -618,9 +625,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", "cpu": [ "x64" ], @@ -635,9 +642,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", "cpu": [ "arm64" ], @@ -652,9 +659,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", "cpu": [ "x64" ], @@ -669,9 +676,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", "cpu": [ "arm64" ], @@ -686,9 +693,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", "cpu": [ "x64" ], @@ -703,9 +710,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", "cpu": [ "arm64" ], @@ -720,9 +727,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", "cpu": [ "ia32" ], @@ -737,9 +744,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", "cpu": [ "x64" ], @@ -754,9 +761,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -874,9 +881,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", - "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, "license": "MIT", "engines": { @@ -1012,15 +1019,13 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "node_modules/@mongodb-js/saslprep": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.5.tgz", + "integrity": "sha512-k64Lbyb7ycCSXHSLzxVdb2xsKGPMvYZfCICXvDsI8Z65CeWQzTEKS4YmGbnqw+U9RBvLPTsB6UCmwkgsDTGWIw==", "license": "MIT", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" + "dependencies": { + "sparse-bitfield": "^3.0.3" } }, "node_modules/@react-leaflet/core": { @@ -1035,16 +1040,16 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.47", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz", - "integrity": "sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==", + "version": "1.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", + "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", "dev": true, "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", - "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.0.tgz", + "integrity": "sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA==", "cpu": [ "arm" ], @@ -1056,9 +1061,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", - "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.0.tgz", + "integrity": "sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg==", "cpu": [ "arm64" ], @@ -1070,9 +1075,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", - "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.0.tgz", + "integrity": "sha512-/NNIj9A7yLjKdmkx5dC2XQ9DmjIECpGpwHoGmA5E1AhU0fuICSqSWScPhN1yLCkEdkCwJIDu2xIeLPs60MNIVg==", "cpu": [ "arm64" ], @@ -1084,9 +1089,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", - "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.0.tgz", + "integrity": "sha512-xoh8abqgPrPYPr7pTYipqnUi1V3em56JzE/HgDgitTqZBZ3yKCWI+7KUkceM6tNweyUKYru1UMi7FC060RyKwA==", "cpu": [ "x64" ], @@ -1098,9 +1103,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", - "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.0.tgz", + "integrity": "sha512-PCkMh7fNahWSbA0OTUQ2OpYHpjZZr0hPr8lId8twD7a7SeWrvT3xJVyza+dQwXSSq4yEQTMoXgNOfMCsn8584g==", "cpu": [ "arm64" ], @@ -1112,9 +1117,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", - "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.0.tgz", + "integrity": "sha512-1j3stGx+qbhXql4OCDZhnK7b01s6rBKNybfsX+TNrEe9JNq4DLi1yGiR1xW+nL+FNVvI4D02PUnl6gJ/2y6WJA==", "cpu": [ "x64" ], @@ -1126,9 +1131,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", - "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.0.tgz", + "integrity": "sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q==", "cpu": [ "arm" ], @@ -1140,9 +1145,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", - "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.0.tgz", + "integrity": "sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA==", "cpu": [ "arm" ], @@ -1154,9 +1159,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", - "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.0.tgz", + "integrity": "sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==", "cpu": [ "arm64" ], @@ -1168,9 +1173,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", - "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.0.tgz", + "integrity": "sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw==", "cpu": [ "arm64" ], @@ -1182,9 +1187,23 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", - "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.0.tgz", + "integrity": "sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.0.tgz", + "integrity": "sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ==", "cpu": [ "loong64" ], @@ -1196,9 +1215,23 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", - "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.0.tgz", + "integrity": "sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.0.tgz", + "integrity": "sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA==", "cpu": [ "ppc64" ], @@ -1210,9 +1243,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", - "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.0.tgz", + "integrity": "sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ==", "cpu": [ "riscv64" ], @@ -1224,9 +1257,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", - "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.0.tgz", + "integrity": "sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ==", "cpu": [ "riscv64" ], @@ -1238,9 +1271,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", - "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.0.tgz", + "integrity": "sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg==", "cpu": [ "s390x" ], @@ -1252,9 +1285,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", - "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.0.tgz", + "integrity": "sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A==", "cpu": [ "x64" ], @@ -1266,9 +1299,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", - "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.0.tgz", + "integrity": "sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw==", "cpu": [ "x64" ], @@ -1279,10 +1312,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.0.tgz", + "integrity": "sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", - "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.0.tgz", + "integrity": "sha512-v5xwKDWcu7qhAEcsUubiav7r+48Uk/ENWdr82MBZZRIm7zThSxCIVDfb3ZeRRq9yqk+oIzMdDo6fCcA5DHfMyA==", "cpu": [ "arm64" ], @@ -1294,9 +1341,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", - "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.0.tgz", + "integrity": "sha512-XnaaaSMGSI6Wk8F4KK3QP7GfuuhjGchElsVerCplUuxRIzdvZ7hRBpLR0omCmw+kI2RFJB80nenhOoGXlJ5TfQ==", "cpu": [ "arm64" ], @@ -1308,9 +1355,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", - "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.0.tgz", + "integrity": "sha512-3K1lP+3BXY4t4VihLw5MEg6IZD3ojSYzqzBG571W3kNQe4G4CcFpSUQVgurYgib5d+YaCjeFow8QivWp8vuSvA==", "cpu": [ "ia32" ], @@ -1322,9 +1369,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", - "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.0.tgz", + "integrity": "sha512-MDk610P/vJGc5L5ImE4k5s+GZT3en0KoK1MKPXCRgzmksAMk79j4h3k1IerxTNqwDLxsGxStEZVBqG0gIqZqoA==", "cpu": [ "x64" ], @@ -1336,9 +1383,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", - "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.0.tgz", + "integrity": "sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ==", "cpu": [ "x64" ], @@ -1409,12 +1456,11 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", - "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", + "version": "19.2.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", + "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1429,17 +1475,32 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/@vitejs/plugin-react": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.1.tgz", - "integrity": "sha512-WQfkSw0QbQ5aJ2CHYw23ZGkqnRwqKHD/KYsMeTkZzPT4Jcf0DcBxBtwMJxnu6E7oxw5+JC6ZAiePgh28uJ1HBA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz", + "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.47", + "@rolldown/pluginutils": "1.0.0-beta.53", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, @@ -1450,13 +1511,25 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1491,6 +1564,16 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -1507,6 +1590,20 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1521,9 +1618,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz", + "integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -1539,32 +1636,59 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.32", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", - "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==", + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" } }, - "node_modules/bootstrap": { - "version": "5.3.8", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz", - "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/twbs" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/bootstrap" - } - ], + "node_modules/bcryptjs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", + "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, "license": "MIT", - "peerDependencies": { - "@popperjs/core": "^2.11.8" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/brace-expansion": { @@ -1578,10 +1702,23 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", - "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, "funding": [ { @@ -1598,13 +1735,12 @@ } ], "license": "MIT", - "peer": true, "dependencies": { - "baseline-browser-mapping": "^2.8.25", - "caniuse-lite": "^1.0.30001754", - "electron-to-chromium": "^1.5.249", + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", - "update-browserslist-db": "^1.1.4" + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -1613,6 +1749,30 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bson": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-7.1.1.tgz", + "integrity": "sha512-TtJgBB+QyOlWjrbM+8bRgH84VM/xrDjyBFgSgGrfZF4xvt6gbEDtcswm27Tn9F9TWsjQybxT8b8VpCP/oJK4Dw==", + "license": "Apache-2.0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -1626,6 +1786,22 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1637,9 +1813,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001759", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001759.tgz", - "integrity": "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==", + "version": "1.0.30001766", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", + "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==", "dev": true, "funding": [ { @@ -1674,6 +1850,76 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/client": { + "resolved": "client", + "link": true + }, + "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==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1713,6 +1959,53 @@ "dev": true, "license": "MIT" }, + "node_modules/concurrently": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1721,12 +2014,34 @@ "license": "MIT" }, "node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { - "node": ">=18" + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" }, "funding": { "type": "opencollective", @@ -1759,7 +2074,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -1789,6 +2103,27 @@ "node": ">=0.4.0" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1803,13 +2138,44 @@ "node": ">= 0.4" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/electron-to-chromium": { - "version": "1.5.263", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.263.tgz", - "integrity": "sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==", + "version": "1.5.279", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.279.tgz", + "integrity": "sha512-0bblUU5UNdOt5G7XqGiJtpZMONma6WAfq9vsFmtn9x1+joAObr6x1chfqyxFSDCAFwFhCQDrqeAr6MYdpwJ9Hg==", "dev": true, "license": "ISC" }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -1856,9 +2222,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1869,32 +2235,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" } }, "node_modules/escalade": { @@ -1907,6 +2273,12 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1921,12 +2293,11 @@ } }, "node_modules/eslint": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", - "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -1934,7 +2305,7 @@ "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.1", + "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -2002,9 +2373,9 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz", - "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==", + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2060,9 +2431,9 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -2105,9 +2476,61 @@ "node": ">=0.10.0" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "license": "MIT" @@ -2126,24 +2549,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -2157,6 +2562,40 @@ "node": ">=16.0.0" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2231,6 +2670,45 @@ "node": ">= 6" } }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2265,6 +2743,16 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2406,6 +2894,42 @@ "hermes-estree": "0.25.1" } }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2416,6 +2940,13 @@ "node": ">= 4" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -2443,6 +2974,34 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2453,6 +3012,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2466,6 +3035,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2540,6 +3125,79 @@ "node": ">=6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/kareem": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-3.0.0.tgz", + "integrity": "sha512-RKhaOBSPN8L7y4yAgNhDT2602G5FD6QbOIISbjN9D6mjHPeqeg7K+EB5IGSU5o81/X2Gzm3ICnAvQW3x3OP8HA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2554,8 +3212,7 @@ "version": "1.9.4", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", - "license": "BSD-2-Clause", - "peer": true + "license": "BSD-2-Clause" }, "node_modules/levn": { "version": "0.4.1", @@ -2587,6 +3244,42 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2594,6 +3287,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -2613,25 +3312,56 @@ "node": ">= 0.4" } }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/minimatch": { @@ -2647,38 +3377,144 @@ "node": "*" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" + "node_modules/mongodb": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.0.0.tgz", + "integrity": "sha512-vG/A5cQrvGGvZm2mTnCSz1LUcbOPl83hfB6bxULKQ8oFZauyox/2xbZOoGNl+64m8VBrETkdGCDBdOsCr3F3jg==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^7.0.0", + "mongodb-connection-string-url": "^7.0.0" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" + "node": ">=20.19.0" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.806.0", + "@mongodb-js/zstd": "^7.0.0", + "gcp-metadata": "^7.0.1", + "kerberos": "^7.0.0", + "mongodb-client-encryption": ">=7.0.0 <7.1.0", + "snappy": "^7.3.2", + "socks": "^2.8.6" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.1.tgz", + "integrity": "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^13.0.0", + "whatwg-url": "^14.1.0" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/mongoose": { + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.1.5.tgz", + "integrity": "sha512-N6gypEO+wLmZp8kCYNQmrEWxVMT0KhyHvVttBZoKA/1ngY7aUsBjqHzCPtDgz+i8JAnqMOiEKmuJIDEQu1b9Dw==", + "license": "MIT", + "dependencies": { + "kareem": "3.0.0", + "mongodb": "~7.0", + "mpath": "0.9.0", + "mquery": "6.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-6.0.0.tgz", + "integrity": "sha512-b2KQNsmgtkscfeDgkYMcWGn9vZI9YoXh802VDEwE6qc50zxBFQ0Oo8ROkawbPAsXCY1/Z1yp0MagqsZStPWJjw==", + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, "node_modules/node-releases": { "version": "2.0.27", @@ -2687,6 +3523,132 @@ "dev": true, "license": "MIT" }, + "node_modules/nodemailer": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.13.tgz", + "integrity": "sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/nodemon": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", + "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -2750,6 +3712,15 @@ "node": ">=6" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -2770,6 +3741,16 @@ "node": ">=8" } }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2778,14 +3759,13 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", - "peer": true, "engines": { - "node": ">=12" + "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -2830,43 +3810,99 @@ "node": ">= 0.8.0" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/react": { - "version": "19.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", - "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", - "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.0" + "react": "^19.2.4" } }, "node_modules/react-leaflet": { @@ -2894,9 +3930,9 @@ } }, "node_modules/react-router": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.10.0.tgz", - "integrity": "sha512-FVyCOH4IZ0eDDRycODfUqoN8ZSR2LbTvtx6RPsBgzvJ8xAXlMZNCrOFpu+jb8QbtZnpAd/cEki2pwE848pNGxw==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.0.tgz", + "integrity": "sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -2916,12 +3952,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.10.0.tgz", - "integrity": "sha512-Q4haR150pN/5N75O30iIsRJcr3ef7p7opFaKpcaREy0GQit6uCRu1NEiIFIwnHJQy0bsziRFBweR/5EkmHgVUQ==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.0.tgz", + "integrity": "sha512-5CO/l5Yahi2SKC6rGZ+HDEjpjkGaG/ncEP7eWFTvFxbHP8yeeI0PxTDjimtpXYlR3b3i9/WIL4VJttPrESIf2g==", "license": "MIT", "dependencies": { - "react-router": "7.10.0" + "react-router": "7.13.0" }, "engines": { "node": ">=20.0.0" @@ -2931,6 +3967,42 @@ "react-dom": ">=18" } }, + "node_modules/react-router/node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2942,9 +4014,9 @@ } }, "node_modules/rollup": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", - "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.0.tgz", + "integrity": "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==", "dev": true, "license": "MIT", "dependencies": { @@ -2958,31 +4030,86 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.53.3", - "@rollup/rollup-android-arm64": "4.53.3", - "@rollup/rollup-darwin-arm64": "4.53.3", - "@rollup/rollup-darwin-x64": "4.53.3", - "@rollup/rollup-freebsd-arm64": "4.53.3", - "@rollup/rollup-freebsd-x64": "4.53.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", - "@rollup/rollup-linux-arm-musleabihf": "4.53.3", - "@rollup/rollup-linux-arm64-gnu": "4.53.3", - "@rollup/rollup-linux-arm64-musl": "4.53.3", - "@rollup/rollup-linux-loong64-gnu": "4.53.3", - "@rollup/rollup-linux-ppc64-gnu": "4.53.3", - "@rollup/rollup-linux-riscv64-gnu": "4.53.3", - "@rollup/rollup-linux-riscv64-musl": "4.53.3", - "@rollup/rollup-linux-s390x-gnu": "4.53.3", - "@rollup/rollup-linux-x64-gnu": "4.53.3", - "@rollup/rollup-linux-x64-musl": "4.53.3", - "@rollup/rollup-openharmony-arm64": "4.53.3", - "@rollup/rollup-win32-arm64-msvc": "4.53.3", - "@rollup/rollup-win32-ia32-msvc": "4.53.3", - "@rollup/rollup-win32-x64-gnu": "4.53.3", - "@rollup/rollup-win32-x64-msvc": "4.53.3", + "@rollup/rollup-android-arm-eabi": "4.57.0", + "@rollup/rollup-android-arm64": "4.57.0", + "@rollup/rollup-darwin-arm64": "4.57.0", + "@rollup/rollup-darwin-x64": "4.57.0", + "@rollup/rollup-freebsd-arm64": "4.57.0", + "@rollup/rollup-freebsd-x64": "4.57.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.0", + "@rollup/rollup-linux-arm-musleabihf": "4.57.0", + "@rollup/rollup-linux-arm64-gnu": "4.57.0", + "@rollup/rollup-linux-arm64-musl": "4.57.0", + "@rollup/rollup-linux-loong64-gnu": "4.57.0", + "@rollup/rollup-linux-loong64-musl": "4.57.0", + "@rollup/rollup-linux-ppc64-gnu": "4.57.0", + "@rollup/rollup-linux-ppc64-musl": "4.57.0", + "@rollup/rollup-linux-riscv64-gnu": "4.57.0", + "@rollup/rollup-linux-riscv64-musl": "4.57.0", + "@rollup/rollup-linux-s390x-gnu": "4.57.0", + "@rollup/rollup-linux-x64-gnu": "4.57.0", + "@rollup/rollup-linux-x64-musl": "4.57.0", + "@rollup/rollup-openbsd-x64": "4.57.0", + "@rollup/rollup-openharmony-arm64": "4.57.0", + "@rollup/rollup-win32-arm64-msvc": "4.57.0", + "@rollup/rollup-win32-ia32-msvc": "4.57.0", + "@rollup/rollup-win32-x64-gnu": "4.57.0", + "@rollup/rollup-win32-x64-msvc": "4.57.0", "fsevents": "~2.3.2" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -2999,12 +4126,67 @@ "semver": "bin/semver.js" } }, - "node_modules/set-cookie-parser": { - "version": "2.7.2", + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/server-fad": { + "resolved": "server", + "link": true + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", "license": "MIT" }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3028,6 +4210,123 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -3038,6 +4337,52 @@ "node": ">=0.10.0" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3052,16 +4397,19 @@ } }, "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/tinyglobby": { @@ -3081,6 +4429,98 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3094,10 +4534,40 @@ "node": ">= 0.8.0" } }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/update-browserslist-db": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", - "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -3135,15 +4605,36 @@ "punycode": "^2.1.0" } }, + "node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vite": { - "version": "7.2.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.6.tgz", - "integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "esbuild": "^0.25.0", + "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", @@ -3211,6 +4702,59 @@ } } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3237,6 +4781,40 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -3244,6 +4822,35 @@ "dev": true, "license": "ISC" }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "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/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==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -3258,12 +4865,11 @@ } }, "node_modules/zod": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", - "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -3280,6 +4886,24 @@ "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } + }, + "server": { + "name": "server-fad", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "bcryptjs": "^3.0.3", + "cors": "^2.8.6", + "dotenv": "^17.2.3", + "express": "^5.2.1", + "jsonwebtoken": "^9.0.3", + "mongoose": "^9.1.5", + "nodemailer": "^7.0.13", + "uuid": "^13.0.0" + }, + "devDependencies": { + "nodemon": "^3.1.11" + } } } } diff --git a/package.json b/package.json index 57564f8..fd0c79f 100644 --- a/package.json +++ b/package.json @@ -1,32 +1,15 @@ { - "name": "package.json", + "name": "find-addis", "private": true, - "version": "0.0.0", - "type": "module", + "workspaces": [ + "client", + "server" + ], "scripts": { - "dev": "vite", - "build": "vite build", - "lint": "eslint .", - "preview": "vite preview" - }, - "dependencies": { - "axios": "^1.13.2", - "bootstrap": "^5.3.8", - "leaflet": "^1.9.4", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "react-leaflet": "^5.0.0", - "react-router-dom": "^7.10.0" + "dev": "concurrently \"npm run dev --workspace server\" \"npm run dev --workspace client\"", + "start": "npm run build --workspace client && concurrently \"npm run start --workspace server\" \"npm run start --workspace client\"" }, "devDependencies": { - "@eslint/js": "^9.39.1", - "@types/react": "^19.2.5", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^5.1.1", - "eslint": "^9.39.1", - "eslint-plugin-react-hooks": "^7.0.1", - "eslint-plugin-react-refresh": "^0.4.24", - "globals": "^16.5.0", - "vite": "^7.2.4" + "concurrently": "^9.2.1" } } diff --git a/server/db/connection.js b/server/db/connection.js new file mode 100644 index 0000000..4e91899 --- /dev/null +++ b/server/db/connection.js @@ -0,0 +1,16 @@ +import mongoose from 'mongoose' + + + +const connectdb = async () => { + try { + await mongoose.connect(process.env.URI); + console.log("MongoDB connected successfully"); + } catch (err) { + console.error("MongoDB connection error:", err); + process.exit(1); + } +}; + + +export default connectdb; \ No newline at end of file diff --git a/server/middleware/UserAuthMiddleware.js b/server/middleware/UserAuthMiddleware.js new file mode 100644 index 0000000..3195ee2 --- /dev/null +++ b/server/middleware/UserAuthMiddleware.js @@ -0,0 +1,25 @@ +import jwt from 'jsonwebtoken' + +const userAuthMiddleware = (req, res, next) => { + const authHeader = req.headers.authorization; + + if (!authHeader) + return res.status(401).json({ error: "No token provided" }); + + const token = authHeader.split(" ")[1]; // "Bearer TOKEN" + + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + req.user = { + id: decoded.id, + name: decoded.name, + email: decoded.email, + role: decoded.role + }; + next(); + } catch (err) { + res.status(401).json({ error: "Invalid token" }); + } +}; + +export default userAuthMiddleware; diff --git a/server/middleware/ownerAuthMiddleware.js b/server/middleware/ownerAuthMiddleware.js new file mode 100644 index 0000000..d7ae17a --- /dev/null +++ b/server/middleware/ownerAuthMiddleware.js @@ -0,0 +1,26 @@ +import jwt from 'jsonwebtoken' + +const ownerAuthMiddleware = (req, res, next) => { + const authHeader = req.headers.authorization; + + if (!authHeader) + return res.status(401).json({ error: "No token provided" }); + + const token = authHeader.split(" ")[1]; // "Bearer TOKEN" + + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + req.owner = { + id: decoded.id, + name: decoded.name, + email: decoded.email, + role: decoded.role, + restaurantsOwned: decoded.restaurantsOwned + }; + next(); + } catch (err) { + res.status(401).json({ error: "Invalid token" }); + } +}; + +export default ownerAuthMiddleware; diff --git a/server/models/Restaurant.js b/server/models/Restaurant.js new file mode 100644 index 0000000..5b161bd --- /dev/null +++ b/server/models/Restaurant.js @@ -0,0 +1,32 @@ +import mongoose from 'mongoose'; + +export const reviewSchema = new mongoose.Schema({ + userId: String, + rating: Number, + text: String, + date: Date +}); + + +const restaurantSchema = mongoose.Schema({ + name: { type: String, required: true }, + category: { type: String, required: true }, + rating: { type: Number, required: true, default: 0 }, + price: { type: String, required: true }, + address: { type: String, required: true }, + images: [String], // will store base64 strings + hours: String, + description: String, + menu: [String], + reviews: [reviewSchema], + // Adding coordinates for map + location: { + lat: { type: Number }, + lng: { type: Number } + } +}, { + timestamps: true +}); + +export const Restaurant = mongoose.model('restaurants', restaurantSchema); + diff --git a/server/models/RestaurantOwner.js b/server/models/RestaurantOwner.js new file mode 100644 index 0000000..9a2aff4 --- /dev/null +++ b/server/models/RestaurantOwner.js @@ -0,0 +1,18 @@ +import mongoose from 'mongoose'; +import { v4 as uuidv4 } from 'uuid'; + + +const restaurantOwnerSchema = mongoose.Schema({ + name: { type: String, required: true }, + email: { type: String, required: true, unique: true }, + password: { type: String, required: true }, + phoneNumber: [{ type: String, required: true }], + isVerified: { type: Boolean, default: false }, + emailVerificationExpiry: { type: Date }, + token: { type: String }, + restaurantsOwned: [{ type: mongoose.Schema.Types.ObjectId, ref: "Restaurant" }] +}, { timestamps: true }); + + +const RestaurantOwners = mongoose.model('owners', restaurantOwnerSchema); +export default RestaurantOwners; \ No newline at end of file diff --git a/server/models/User.js b/server/models/User.js new file mode 100644 index 0000000..89000ff --- /dev/null +++ b/server/models/User.js @@ -0,0 +1,22 @@ +import mongoose from 'mongoose'; +import { v4 as uuidv4 } from 'uuid'; + +const favoriteSchema = new mongoose.Schema({ + user: { type: mongoose.Schema.Types.ObjectId, ref: "User" }, + restaurant: { type: mongoose.Schema.Types.ObjectId, ref: "Restaurant" }, + createdAt: { type: Date, default: Date.now } +}); + + +const userSchema = mongoose.Schema({ + name: { type: String, required: true }, + email: { type: String, required: true, unique: true }, + password: { type: String, required: true }, + isVerified: { type: Boolean, default: false }, + token: { type: String }, + emailVerificationExpiry: { type: Date }, + favorites: [{ type: mongoose.Schema.Types.ObjectId, ref: "Restaurant" }] +}, { timestamps: true }); + +const User = mongoose.model('users', userSchema); +export default User; \ No newline at end of file diff --git a/backend/package-lock.json b/server/package-lock.json similarity index 77% rename from backend/package-lock.json rename to server/package-lock.json index 3c2cede..65286a6 100644 --- a/backend/package-lock.json +++ b/server/package-lock.json @@ -1,20 +1,24 @@ { - "name": "backend", + "name": "server-fad", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "backend", + "name": "server-fad", "version": "1.0.0", "license": "ISC", "dependencies": { "bcryptjs": "^3.0.3", - "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^5.2.1", "jsonwebtoken": "^9.0.3", - "mongoose": "^9.1.5" + "mongoose": "^9.1.5", + "nodemailer": "^7.0.13", + "uuid": "^13.0.0" + }, + "devDependencies": { + "nodemon": "^3.1.11" } }, "node_modules/@mongodb-js/saslprep": { @@ -54,6 +58,27 @@ "node": ">= 0.6" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/bcryptjs": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", @@ -63,6 +88,19 @@ "bcrypt": "bin/bcrypt" } }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/body-parser": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", @@ -87,6 +125,30 @@ "url": "https://opencollective.com/express" } }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/bson": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/bson/-/bson-7.1.1.tgz", @@ -140,6 +202,38 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, "node_modules/content-disposition": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", @@ -180,19 +274,6 @@ "node": ">=6.6.0" } }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -357,6 +438,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", @@ -396,6 +490,21 @@ "node": ">= 0.8" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -442,6 +551,19 @@ "node": ">= 0.4" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -454,6 +576,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -514,6 +646,13 @@ "url": "https://opencollective.com/express" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -529,6 +668,52 @@ "node": ">= 0.10" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -690,6 +875,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/mongodb": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.0.0.tgz", @@ -803,10 +1001,49 @@ "node": ">= 0.6" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "node_modules/nodemailer": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.13.tgz", + "integrity": "sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/nodemon": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", + "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -864,6 +1101,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -877,6 +1127,13 @@ "node": ">= 0.10" } }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -925,6 +1182,19 @@ "node": ">= 0.10" } }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -1108,6 +1378,19 @@ "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", "license": "MIT" }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -1126,6 +1409,32 @@ "node": ">= 0.8" } }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -1135,6 +1444,16 @@ "node": ">=0.6" } }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, "node_modules/tr46": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", @@ -1161,6 +1480,13 @@ "node": ">= 0.6" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -1170,6 +1496,19 @@ "node": ">= 0.8" } }, + "node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/backend/package.json b/server/package.json similarity index 57% rename from backend/package.json rename to server/package.json index dea5cb2..7db0e97 100644 --- a/backend/package.json +++ b/server/package.json @@ -1,23 +1,27 @@ { - "name": "backend", + "name": "server-fad", "version": "1.0.0", "description": "", "main": "index.js", + "type": "module", "scripts": { "start": "node server.js", - "dev": "npx nodemon server.js", - "test": "echo \"Error: no test specified\" && exit 1" + "dev": "nodemon server.js" }, "keywords": [], "author": "", "license": "ISC", - "type": "commonjs", "dependencies": { "bcryptjs": "^3.0.3", - "cors": "^2.8.5", + "cors": "^2.8.6", "dotenv": "^17.2.3", "express": "^5.2.1", "jsonwebtoken": "^9.0.3", - "mongoose": "^9.1.5" + "mongoose": "^9.1.5", + "nodemailer": "^7.0.13", + "uuid": "^13.0.0" + }, + "devDependencies": { + "nodemon": "^3.1.11" } } diff --git a/server/routes/RestaurantApp.js b/server/routes/RestaurantApp.js new file mode 100644 index 0000000..0bf6d17 --- /dev/null +++ b/server/routes/RestaurantApp.js @@ -0,0 +1,137 @@ +import { Restaurant, reviewSchema } from '../models/Restaurant.js'; +import { Router } from 'express' +import mongoose from 'mongoose'; +import RestaurantOwners from '../models/RestaurantOwner.js'; +import userAuthMiddleware from '../middleware/UserAuthMiddleware.js' +import ownerAuthMiddleware from '../middleware/ownerAuthMiddleware.js'; +const route = Router(); + +route.post('/', ownerAuthMiddleware, async (req, res) => { + if (req.owner.role != 'owner') { + + return res.status(400).send({ msg: 'Operation not allowed' }) + } + try { + const newRestaurant = new Restaurant(req.body); + await newRestaurant.save(); + //register restaurant id for the owner + const ownerId = req.owner.id; + console.log(ownerId, newRestaurant._id) + await RestaurantOwners.findByIdAndUpdate(ownerId, { $push: { restaurantsOwned: newRestaurant._id } }) + res.status(201).json({ id: newRestaurant._id }); + + } catch (err) { + res.status(400).json({ error: err.message }); + } +}) + +route.post('/:id/reviews', userAuthMiddleware, async (req, res) => { + if (req.user.role != 'user') { + return res.status(400).send({ msg: 'Operation not allowed' }) + } + const restaurantId = req.params.id; + try { + const newReview = { + userId: req.user.id, + rating: req.body.rating, + text: req.body.text, + date: new Date() + } + // + const review = new mongoose.Types.Subdocument(newReview, reviewSchema); + + const existingReview = await Restaurant.findOne({ + _id: restaurantId, + 'reviews.userId': newReview.userId + }) + if (existingReview) { + res.status(409).json({ msg: 'review already exist with this user account' }) + return; + } + + await Restaurant.updateOne( + { _id: restaurantId }, { $push: { reviews: review } } + ) + res.send({ id: review._id }) + } catch (err) { + res.status(400).json({ error: err.message }); + } + +}) + +//get all restaurants or filtered +route.get('/', async (req, res) => { + try { + const ratingReq = req.query.rating; + const category = req.query.category; + //filter by both category and rating + if (category && ratingReq) { + const rating = parseFloat(ratingReq); + const restaurants = await Restaurant.find({ rating, category }); + return res.status(200).json(restaurants); + } + //filter by rating + if (ratingReq) { + const rating = parseFloat(ratingReq); + const restaurants = await Restaurant.find({ rating }); + return res.status(200).json(restaurants); + } + //filter by category + if (category) { + const restaurants = await Restaurant.find({ category }); + return res.status(200).json(restaurants); + } + + // return all without filter + const restaurants = await Restaurant.find(); + res.status(200).json(restaurants); + } catch (err) { + res.status(500).json({ error: err.message }); + } + +}) + +//delete restaurant using id +route.delete('/:id', ownerAuthMiddleware, async (req, res) => { + if (req.owner.role != 'owner') { + return res.status(400).send({ msg: 'Operation not allowed' }) + } + const restaurantId = req.params.id + const ownerId = req.owner.id; + const isOwnerofRestaurant = RestaurantOwners.findOne({ _id: ownerId, restaurantsOwned: restaurantId }) + if (!isOwnerofRestaurant) { + return res.status(400).send({ msg: 'Operation Restricted' }) + } + try { + const deleted = await Restaurant.findByIdAndDelete(restaurantId) + if (!deleted) { + return res.status(404).json({ error: 'Restaurant not found' }); + } + //also remove from owners list + await RestaurantOwners.findByIdAndUpdate(ownerId, { $pull: { restaurantsOwned: restaurantId } }) + res.status(200).send({ msg: 'deleted successfully' }) + } catch (err) { + res.status(500).json({ error: err.message }); + } +}) + +//update restaurant + +route.patch('/:id', ownerAuthMiddleware, async (req, res) => { + const id = req.params.id + try { + const updatedRestaurant = await Restaurant.findByIdAndUpdate( + id, { $set: req.body }, { new: true } + ); + + if (!updatedRestaurant) { + return res.status(404).json({ error: 'Restaurant not found' }); + } + + res.status(200).json(updatedRestaurant); + } catch (err) { + + } +}); + +export default route; \ No newline at end of file diff --git a/server/routes/RestaurantOwnersApp.js b/server/routes/RestaurantOwnersApp.js new file mode 100644 index 0000000..310e0cd --- /dev/null +++ b/server/routes/RestaurantOwnersApp.js @@ -0,0 +1,104 @@ +import { Router } from 'express' +import bcrypt from "bcryptjs" +import crypto from "crypto" +import SendEmail from '../utils/Mailer.js'; +import RestaurantOwners from '../models/RestaurantOwner.js' +import jwt from 'jsonwebtoken' +const router = Router(); + +//owners signup +router.post('/', async (req, res) => { + const { name, email, password, phoneNumber } = req.body; + try { + const ownerExists = await RestaurantOwners.findOne({ email }); + if (ownerExists) { + res.status(409).json({ msg: 'Email already in use' }); + return; + } + const token = crypto.randomBytes(32).toString("hex"); + const hashedToken = crypto.createHash("sha256").update(token).digest("hex"); + const verificationExpiry = Date.now() + 1000 * 60 * 60; + + const salt = await bcrypt.genSalt(10); + const hashedPassword = await bcrypt.hash(password, salt); + const newOwner = new RestaurantOwners({ name, email, password: hashedPassword, phoneNumber, token: hashedToken, emailVerificationExpiry: verificationExpiry }); + await newOwner.save(); + + const verificationLink = "http://localhost:3000/api/RestaurantOwners/verify?token=" + token + "&email=" + email; + try { + SendEmail(process.env.EMAIL, newOwner.email, 'verify findAddis account', verificationLink) + res.status(201).json({ message: "Restaurant Owner registered successfully. Please verify your email using the link sent to your account" }); + + } catch (err) { + console.log("error", err) + res.status(500).send({ msg: 'Unable to send verification email to Owner' }) + } + } catch (err) { + res.status(500).json({ error: err.message }); + + } +}) +//owners verifying +router.get('/verify', async (req, res) => { + const email = req.query.email + const token = req.query.token; + if (!token || !email) { + res.status(400).json({ msg: 'please provide both email and token' }) + return; + } + const unverifiedOwner = await RestaurantOwners.findOne({ email }) + if (!unverifiedOwner) { + res.status(404).send({ msg: 'owner not found' }) + } + if (unverifiedOwner.isVerified) { + res.status(400).json({ msg: 'owner account already verified' }) + return; + } + if (unverifiedOwner.emailVerificationExpiry < Date.now()) { + res.status(400).json({ msg: 'verification link expired' }) + return; + } + const tokenToverify = unverifiedOwner.token; + const hashedToken = crypto.createHash("sha256").update(token).digest("hex"); + console.log("tokento verify", tokenToverify) + console.log("hashed token", hashedToken) + if (tokenToverify != hashedToken) { + res.status(400).json({ msg: 'invalid token requested' }) + return; + } + + // verify user + const verifyOwner = { isVerified: true, token: null } + await RestaurantOwners.findByIdAndUpdate(unverifiedOwner.id, { $set: verifyOwner }, { new: true }) + res.status(200).json({ msg: 'Owner successfully verified' }) +}) + +//owners login +router.post('/login', async (req, res) => { + const { email, password } = req.body; + const owner = await RestaurantOwners.findOne({ email }) + if (!owner) { + res.status(404).json({ msg: 'account not found,please signup first' }) + return; + } + const storedPassword = owner.password + const ismatch = await bcrypt.compare(password, storedPassword) + + if (!ismatch) { + res.status(400).json({ msg: 'incorrect email or password' }) + return; + } + if (!owner.isVerified) { + res.status(400).json({ msg: 'please verify your email to login' }) + return; + } + const token = jwt.sign( + { id: owner._id, name: owner.name, email: owner.email, restaurantsOwned: owner.restaurantsOwned, role: 'owner' }, + process.env.JWT_SECRET, + { expiresIn: process.env.EXPIRESIN }) + res.status(200).json({ token, expiresIn: process.env.EXPIRESIN }) + + +}) + +export default router; \ No newline at end of file diff --git a/server/routes/UserApp.js b/server/routes/UserApp.js new file mode 100644 index 0000000..5458666 --- /dev/null +++ b/server/routes/UserApp.js @@ -0,0 +1,162 @@ +import User from '../models/User.js' +import { Router } from 'express' +import bcrypt from "bcryptjs" +import crypto from "crypto" +import jwt from "jsonwebtoken" +import SendEmail from '../utils/Mailer.js' +import userAuthmiddleware from "../middleware/UserAuthMiddleware.js" +const router = Router(); + + +router.get('/', async (req, res) => { + try { + const users = await User.find(); + res.status(200).json({ users }); + } catch (err) { + res.status(500).json({ error: err.message }); + + } +}) + +//register user +router.post('/', async (req, res) => { + const { name, email, password } = req.body; + try { + const existingUser = await User.findOne({ email: email }) + if (existingUser) { + res.status(409).json({ msg: 'Email already in use' }); + return; + } + const token = crypto.randomBytes(32).toString("hex"); + const hashedToken = crypto.createHash("sha256").update(token).digest("hex"); + const verificationExpiry = Date.now() + 1000 * 60 * 60; //1hour + + const salt = await bcrypt.genSalt(10); + const hashedPassword = await bcrypt.hash(password, salt); + const newUser = new User({ name, email, password: hashedPassword, token: hashedToken, emailVerificationExpiry: verificationExpiry }); + await newUser.save(); + + //send token to users Email + const verificationLink = "http://localhost:3000/api/users/verify?token=" + token + "&email=" + email; + try { + SendEmail(process.env.EMAIL, newUser.email, 'verify findAddis account', verificationLink) + res.status(201).json({ message: "User registered successfully. Please verify your email using the link sent to your account" }); + } catch (err) { + res.status(500).send({ msg: 'Unable to send verification email to user' }) + } + } catch (err) { + res.status(500).json({ error: err.message }); + } + +}) + +router.get('/verify', async (req, res) => { + const email = req.query.email + const token = req.query.token; + if (!token || !email) { + res.status(400).json({ msg: 'please provide both email and token' }) + return; + } + const unverifiedUser = await User.findOne({ email }) + if (!unverifiedUser) { + res.status(404).json({ msg: 'user not found' }) + return; + } + if (unverifiedUser.isVerified) { + res.status(400).json({ msg: 'user already verified' }) + return; + } + if (unverifiedUser.emailVerificationExpiry < Date.now()) { + res.status(400).json({ msg: 'verification link expired' }) + return; + } + const tokenToverify = unverifiedUser.token; + const hashedToken = crypto.createHash("sha256").update(token).digest("hex"); + if (tokenToverify != hashedToken) { + res.status(400).json({ msg: 'invalid token requested' }) + return; + } + + // verify user + const verifyUser = { isVerified: true, token: null } + await User.findByIdAndUpdate(unverifiedUser.id, { $set: verifyUser }, { new: true }) + res.status(200).json({ msg: 'user successfully verified' }) +}) + +router.post('/login', async (req, res) => { + const { email, password } = req.body; + const user = await User.findOne({ email }) + if (!user) { + res.status(404).json({ msg: 'user not found,please signup first' }) + return; + } + const storedPassword = user.password + const ismatch = await bcrypt.compare(password, storedPassword) + + if (!ismatch) { + res.status(400).json({ msg: 'incorrect email or password' }) + return; + } + if (!user.isVerified) { + res.status(400).json({ msg: 'please verify your email to login' }) + return; + } + const token = jwt.sign( + { id: user._id, name: user.name, email: user.email, role: "user" }, + process.env.JWT_SECRET, + { expiresIn: process.env.EXPIRESIN }) + res.status(200).json({ token, expiresIn: process.env.EXPIRESIN }) + + +}) + +// router.post('/login', async (req, res) => { +// const { email, password } = req.body; +// const user = await User.findOne({ email }) +// if (!user) return res.status(404).json({ msg: 'user not found' }); + +// const isMatch = await bcrypt.compare(password, user.password) +// if (!isMatch) return res.status(400).json({ msg: 'incorrect email or password' }); +// if (!user.isVerified) return res.status(400).json({ msg: 'please verify your email' }); + +// const token = jwt.sign( +// { id: user._id, name: user.name, email: user.email, role: "user" }, +// process.env.JWT_SECRET, +// { expiresIn: process.env.EXPIRESIN } +// ); + +// res.status(200).json({ +// user: { +// id: user._id, +// name: user.name, +// email: user.email, +// role: "user" +// }, +// token, +// expiresIn: process.env.EXPIRESIN +// }); +// }); + + +router.post("/favorites/:restaurantId", userAuthmiddleware, async (req, res) => { + try { + const userId = req.user.id; + const restaurantId = req.params.restaurantId; + + const user = await User.findByIdAndUpdate( + userId, + { $addToSet: { favorites: restaurantId } }, + { new: true } + ).populate("favorites"); + + res.status(200).json(user.favorites); + } catch (err) { + console.error(err); + res.status(500).json({ msg: "Server error" }); + } +}); + + + + +export default router; \ No newline at end of file diff --git a/server/server.js b/server/server.js new file mode 100644 index 0000000..845a0d8 --- /dev/null +++ b/server/server.js @@ -0,0 +1,34 @@ +import express from 'express' +import dotenv from "dotenv"; +import connectdb from "./db/connection.js" +import restaurantRoute from "./routes/RestaurantApp.js" +import UserRoute from "./routes/UserApp.js" +import RestaurantOwnersRoute from "./routes/RestaurantOwnersApp.js" +import cors from 'cors' + +//import authMiddleware from './middleware/AuthMiddleware.js' + +// load .env file +dotenv.config(); + +const app = express(); + +// allow frontend to call the server +app.use(cors()); + +// allow bigger payload size as image uploading is needed for this project. +app.use(express.json({ limit: "5mb" })); + +// create DB connection. +connectdb(); + +app.use('/api/restaurants', restaurantRoute); +app.use('/api/users', UserRoute); +app.use('/api/RestaurantOwners', RestaurantOwnersRoute) + +app.get('/', (req, res) => { + res.send('server is running') +}) + +const PORT = process.env.PORT || 3000; +app.listen(PORT, () => console.log(`server running on :http://localhost:${PORT}`)) \ No newline at end of file diff --git a/server/utils/Mailer.js b/server/utils/Mailer.js new file mode 100644 index 0000000..c44898d --- /dev/null +++ b/server/utils/Mailer.js @@ -0,0 +1,20 @@ +import nodemailer from 'nodemailer' + + +function SendEmail(from, to, subject, text) { + const transporter = nodemailer.createTransport({ + service: "gmail", + auth: { + user: process.env.EMAIL, + pass: process.env.EMAILPASS + } + }); + transporter.sendMail({ + from, + to, + subject, + text + }) +} + +export default SendEmail \ No newline at end of file diff --git a/src/components/layout/Navbar.jsx b/src/components/layout/Navbar.jsx deleted file mode 100644 index 85dbbcc..0000000 --- a/src/components/layout/Navbar.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from "react"; -import { Link, useNavigate, useLocation } from "react-router-dom"; -import InputField from "../common/InputField"; -import AuthContext from "../../context/AuthContext"; -import { useContext } from "react"; - - - -function Navbar({ onSearch }) { - const { user, isAdmin, isOwner } = useContext(AuthContext); - const navigate = useNavigate(); - const [q, setQ] = React.useState(""); - const location = useLocation(); - - function submitSearch(e) { - e.preventDefault(); - const qs = q.trim(); - if (!qs) return; - navigate(`/search?q=${encodeURIComponent(qs)}`); - if (onSearch) onSearch(qs); - } - - return ( -
-
- Find Addis -
- - {(!user || (!isAdmin() && !isOwner())) && ( -
-
- - setQ(e.target.value)} - /> -
- -
- )} - -
- - -
- {user ? ( -
- - Hi, {user.name} - -
- ) : ( - <> - Log In - Sign Up - - )} -
-
-
- ); -} - -export default Navbar; diff --git a/src/components/user/SignupForm.jsx b/src/components/user/SignupForm.jsx deleted file mode 100644 index 9ee4c90..0000000 --- a/src/components/user/SignupForm.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from "react"; -import InputField from "../common/InputField"; -import Button from "../common/Button"; - -function SignupForm({ onSignup }) { - const [form, setForm] = React.useState({ name: "", email: "", password: "", role: "user", adminSecret: "" }); - - function handleChange(e) { - setForm({ ...form, [e.target.name]: e.target.value }); - } - - function handleSubmit(e) { - e.preventDefault(); - - // Frontend Email Validation - const emailRegex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/; - if (!emailRegex.test(form.email)) { - alert("Please enter a valid email address"); - return; - } - - if (onSignup) onSignup(form); - } - - return ( -
- - - - -
- - -
- - {(form.role === "admin" || form.role === "restaurant_owner") && ( - - )} - - - - ); -} - -export default SignupForm; diff --git a/src/context/RestaurantsContext.jsx b/src/context/RestaurantsContext.jsx deleted file mode 100644 index a05cfbb..0000000 --- a/src/context/RestaurantsContext.jsx +++ /dev/null @@ -1,160 +0,0 @@ -import React, { useContext, useEffect, useState } from "react"; -import axios from "axios"; -import SAMPLE_RESTAURANTS from "../data/restaurants"; -import AuthContext from "./AuthContext"; - -const RestaurantsContext = React.createContext(); - -export function RestaurantsProvider({ children }) { - const [restaurants, setRestaurants] = useState([]); - const { user } = useContext(AuthContext); - const [favorites, setFavorites] = useState([]); - - // Fetch Restaurants - useEffect(() => { - axios.get("http://localhost:5000/api/restaurants") - .then((res) => { - if (res.data.length === 0) { - // Fallback or handle initial seed - setRestaurants([]); - } else { - setRestaurants(res.data); - } - }) - .catch((err) => { - console.error("Failed to fetch restaurants", err); - setRestaurants(SAMPLE_RESTAURANTS); - }); - }, []); - - // Fetch Favorites (Sync with Backend if logged in, else LocalStorage) - useEffect(() => { - if (user) { - axios.get("http://localhost:5000/api/users/favorites") - .then((res) => setFavorites(res.data)) - .catch((err) => console.error("Failed to fetch favorites", err)); - } else { - // Load from local storage if guest - try { - const raw = localStorage.getItem("fa_favorites"); - if (raw) setFavorites(JSON.parse(raw)); - } catch (e) { } - } - }, [user]); - - // Sync Favorites to LocalStorage (as backup/guest mode) - useEffect(() => { - if (!user) { - try { - localStorage.setItem("fa_favorites", JSON.stringify(favorites)); - } catch (e) { } - } - }, [favorites, user]); - - function addReview(restaurantId, review) { - return axios.post(`http://localhost:5000/api/restaurants/${restaurantId}/reviews`, review) - .then((res) => { - const newReview = res.data; - setRestaurants((prev) => - prev.map((r) => (r.id === restaurantId ? { ...r, reviews: [...(r.reviews || []), newReview] } : r)) - ); - }) - .catch((err) => console.error("Error adding review", err)); - } - - function editReview(restaurantId, reviewId, updated) { - axios.put(`http://localhost:5000/api/restaurants/${restaurantId}/reviews/${reviewId}`, updated) - .then((res) => { - setRestaurants((prev) => - prev.map((r) => - r.id === restaurantId - ? { ...r, reviews: (r.reviews || []).map((rv) => (rv.id === reviewId || rv._id === reviewId ? { ...rv, ...updated } : rv)) } - : r - ) - ); - }) - .catch((err) => console.error("Error editing review", err)); - } - - function deleteReview(restaurantId, reviewId) { - axios.delete(`http://localhost:5000/api/restaurants/${restaurantId}/reviews/${reviewId}`) - .then(() => { - setRestaurants((prev) => - prev.map((r) => (r.id === restaurantId ? { ...r, reviews: (r.reviews || []).filter((rv) => rv.id !== reviewId && rv._id !== reviewId) } : r)) - ); - }) - .catch((err) => console.error("Error deleting review", err)); - } - - function addRestaurant(newR) { - // Optimistic update - setRestaurants((prev) => [newR, ...prev]); - - axios.post("http://localhost:5000/api/restaurants", newR) - .then((res) => { - // Replace optimistic with real server data (which might have extra fields like _id) - setRestaurants((prev) => [res.data, ...prev.filter(r => r.id !== newR.id)]); - }) - .catch((err) => { - console.error("Error creating restaurant", err); - // Revert optimistic update? For now just log. - }); - } - - function updateRestaurantImage(restaurantId, dataUrl) { - const target = restaurants.find(r => r.id === restaurantId); - if (!target) return; - - // Optimistic update - const newImages = [dataUrl, ...(target.images || []).slice(0, 4)]; // Keep max 5 for safety - setRestaurants((prev) => - prev.map((r) => (r.id === restaurantId ? { ...r, images: newImages } : r)) - ); - - // Persist - axios.put(`http://localhost:5000/api/restaurants/${restaurantId}`, { images: newImages }) - .catch((err) => console.error("Error updating images", err)); - } - - function toggleFavorite(restaurantId) { - const isAdding = !favorites.includes(restaurantId); - - // Optimistic update - setFavorites((prev) => (isAdding ? [...prev, restaurantId] : prev.filter((id) => id !== restaurantId))); - - if (user) { - const url = `http://localhost:5000/api/users/favorites/${restaurantId}`; - const request = isAdding ? axios.post(url) : axios.delete(url); - - request.catch((err) => { - console.error("Error syncing favorite", err); - // Revert - setFavorites((prev) => (isAdding ? prev.filter(id => id !== restaurantId) : [...prev, restaurantId])); - }); - } - } - - function isFavorite(restaurantId) { - return favorites.includes(restaurantId); - } - - return ( - - {children} - - ); -} - -export default RestaurantsContext; diff --git a/src/pages/Favorites.jsx b/src/pages/Favorites.jsx deleted file mode 100644 index 6c0d32f..0000000 --- a/src/pages/Favorites.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import React, { useContext } from "react"; -import RestaurantsContext from "../context/RestaurantsContext"; -import RestaurantList from "../components/restaurant/RestaurantList"; - -function Favorites() { - const { restaurants, favorites } = useContext(RestaurantsContext); - const favRestaurants = restaurants.filter((r) => favorites.includes(r.id)); - - return ( -
-

Favorites

- {favRestaurants.length === 0 ? ( -

You haven't saved any restaurants yet.

- ) : ( - - )} -
- ); -} - -export default Favorites; diff --git a/src/pages/UserProfilePage.jsx b/src/pages/UserProfilePage.jsx deleted file mode 100644 index dfc77f1..0000000 --- a/src/pages/UserProfilePage.jsx +++ /dev/null @@ -1,304 +0,0 @@ -import { useContext, useEffect, useCallback, useState } from "react"; -import UserProfile from "../components/user/UserProfile"; -import { useNavigate } from "react-router-dom"; -import AuthContext from "../context/AuthContext"; -import axios from "axios"; - -function UserProfilePage() { - const { user, token, isAdmin, isOwner, logout } = useContext(AuthContext); - const navigate = useNavigate(); - - // Owner State - const [isEditing, setIsEditing] = useState(false); - const [formData, setFormData] = useState({ name: '', description: '', menu: '', category: '', price: '', address: '' }); - const [msg, setMsg] = useState(null); - const [error, setError] = useState(null); - const [myRestaurant, setMyRestaurant] = useState(null); - - const [pendingRestaurants, setPendingRestaurants] = useState([]); - const [requests, setRequests] = useState([]); - - const [isNotFound, setIsNotFound] = useState(false); - - const config = { - headers: { Authorization: `Bearer ${token}` } - }; - - const fetchAdminData = useCallback(() => { - if (isAdmin() && token) { - axios.get('http://localhost:5000/api/requests', config) - .then(res => setRequests(res.data)) - .catch(err => setError("Error fetching requests: " + err.message)); - } - }, [isAdmin, token]); - - const fetchOwnerData = useCallback(() => { - if (isOwner() && user?.managedRestaurantId && token) { - setIsNotFound(false); - axios.get(`http://localhost:5000/api/restaurants/${user.managedRestaurantId}`, config) - .then(res => { - setMyRestaurant(res.data); - setFormData({ - name: res.data.name, - description: res.data.description || '', - menu: Array.isArray(res.data.menu) ? res.data.menu.join(', ') : '', - category: res.data.category, - price: res.data.price, - address: res.data.address - }); - }) - .catch(err => { - if (err.response?.status === 404) { - setIsNotFound(true); - setError(null); - } else { - setError("Error fetching restaurant details: " + err.message); - } - }); - } - }, [isOwner, user?.managedRestaurantId, token]); - - useEffect(() => { - if (user) { - if (isAdmin()) fetchAdminData(); - if (isOwner()) fetchOwnerData(); - } - }, [user, isAdmin, isOwner, fetchAdminData, fetchOwnerData]); - - const handleLogout = () => { - logout(); - navigate("/login"); - }; - - function clearFeedback() { - setMsg(null); - setError(null); - } - - const handleApproveRequest = (id) => { - clearFeedback(); - axios.post(`http://localhost:5000/api/requests/${id}/approve`, {}, config) - .then(() => { - setMsg("Request approved!"); - fetchAdminData(); - }) - .catch(err => setError(err.response?.data?.message || "Error approving")); - }; - - const handleRejectRequest = (id) => { - clearFeedback(); - axios.post(`http://localhost:5000/api/requests/${id}/reject`, {}, config) - .then(() => { - setMsg("Request rejected."); - fetchAdminData(); - }) - .catch(err => setError(err.response?.data?.message || "Error rejecting")); - }; - - const handleApproveRestaurant = (id) => { - clearFeedback(); - axios.put(`http://localhost:5000/api/restaurants/${id}/approve`, {}, config) - .then(() => { - setMsg("Restaurant approved!"); - fetchAdminData(); - }) - .catch(err => setError(err.response?.data?.message || "Error approving")); - }; - - const handleRejectRestaurant = (id) => { - clearFeedback(); - axios.delete(`http://localhost:5000/api/restaurants/${id}`, config) - .then(() => { - setMsg("Restaurant rejected/removed."); - fetchAdminData(); - }) - .catch(err => setError(err.response?.data?.message || "Error rejecting")); - }; - - const submitUpdate = (e) => { - e.preventDefault(); - clearFeedback(); - const payload = { - type: 'UPDATE', - target: user.managedRestaurantId, - data: { - ...formData, - menu: typeof formData.menu === 'string' ? formData.menu.split(',').map(s => s.trim()) : formData.menu - } - }; - - axios.post('http://localhost:5000/api/requests', payload, config) - .then(() => { - setMsg("Update request submitted for approval."); - setIsEditing(false); - }) - .catch(err => setError("Error submitting request: " + err.message)); - }; - - const submitCreation = (e) => { - e.preventDefault(); - clearFeedback(); - const payload = { - type: 'CREATE', - data: { - ...formData, - menu: typeof formData.menu === 'string' ? formData.menu.split(',').map(s => s.trim()) : [] - } - }; - - axios.post('http://localhost:5000/api/requests', payload, config) - .then(() => { - setMsg("Registration request submitted! Please wait for admin approval."); - setIsEditing(false); - // We don't reload anymore, just show the message and let the UI state handle what it can, - // OR we can reload to refresh managedRestaurantId if the backend had a different logic, - // but here it only gets linked AFTER approval. - }) - .catch(err => setError(err.response?.data?.message || "Error submitting registration")); - }; - - if (!user) return

Please log in to view your profile.

; - - return ( -
-
-

Dashboard

- -
- - - - {error &&
{error}
} - {msg &&
{msg}
} - - {isAdmin() && ( -
-

Admin Dashboard

- -

Pending Requests ({requests.length})

- {requests.length === 0 ? ( -

No pending requests.

- ) : ( -
- {requests.map(req => ( -
-
- {req.type} Request - {new Date(req.createdAt).toLocaleDateString()} -
-

User: {req.userId?.name} ({req.userId?.email})

- {req.type !== 'CREATE' &&

Target ID: {req.target}

} -
-                    {JSON.stringify(req.data, null, 2)}
-                  
-
- - -
-
- ))} -
- )} -
- )} - - {isOwner() && ( -
-

My Restaurant

- - {!user.managedRestaurantId && !isEditing ? ( -
-

You haven't listed a restaurant yet.

- -
- ) : !isEditing ? ( -
-

Managed Restaurant ID: {user.managedRestaurantId}

- -
- {myRestaurant ? ( - myRestaurant.isApproved ? ( -

✅ Request Approved
Your restaurant is live and visible to users.

- ) : ( -

⏳ Registration Waiting for Approval
Your restaurant is currently pending. Once an admin approves it, it will be visible.

- ) - ) : isNotFound ? ( -
-

⚠️ Restaurant Not Found

-

It seems your restaurant registration was removed or the database was reset. You may need to register again.

- -
- ) : ( -

Loading status...

- )} -
- - {!isNotFound && ( - - )} -

* Updates require admin approval before going live.

-
- ) : ( -
-

{(user.managedRestaurantId && !isNotFound) ? 'Edit Restaurant' : 'Register New Restaurant'}

- -
- - setFormData({ ...formData, name: e.target.value })} required /> -
- -
- - -
- -
- - -
- -
- - setFormData({ ...formData, address: e.target.value })} required /> -
- -
- -