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 76%
rename from src/components/home/FeaturedCard.jsx
rename to client/src/components/home/FeaturedCard.jsx
index 52a26cb..4e5422f
--- a/src/components/home/FeaturedCard.jsx
+++ b/client/src/components/home/FeaturedCard.jsx
@@ -1,86 +1,89 @@
-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 } from "react";
+import AuthContext from "../../context/AuthContext";
+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 } = 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 (
-
-
-
-
-
-
-
-
Find the best restaurants in Addis Ababa
-
Local favorites, menus, and honest reviews — all in one place.
-
-
-
-
-
-
-
-
-
-
-
- {slides.map((_, i) => (
-
-
- );
-}
-
-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 (
+
+
+
+
+
+
+
+
Find the best restaurants in Addis Ababa
+
Local favorites, menus, and honest reviews — all in one place.
+
+
+
+
+
+
+
+
+
+
+
+ {slides.map((_, i) => (
+
+
+ );
+}
+
+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 (
-
- );
-}
-
-export default Footer;
+import React from "react";
+
+function Footer() {
+ return (
+
+ );
+}
+
+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 (
+
+ );
+}
+
+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.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.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.text}
-
{review.date}
- {canEdit() && (
-
-
-
-
- )}
-
-
setOpen(false)}>
-
-
- setForm((f) => ({ ...f, rating: Number(e.target.value) }))} />
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-export default ReviewCard;
+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.text}
+
{review.date}
+ {canEdit() && (
+
+
+
+
+ )}
+
+
setOpen(false)}>
+
+
+ setForm((f) => ({ ...f, rating: Number(e.target.value) }))} />
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default ReviewCard;
diff --git a/src/components/user/LoginForm.jsx b/client/src/components/user/LoginForm.jsx
old mode 100644
new mode 100755
similarity index 66%
rename from src/components/user/LoginForm.jsx
rename to client/src/components/user/LoginForm.jsx
index 8648fdf..427cbb8
--- a/src/components/user/LoginForm.jsx
+++ b/client/src/components/user/LoginForm.jsx
@@ -1,29 +1,33 @@
-import React from "react";
-import InputField from "../common/InputField";
-import Button from "../common/Button";
-
-function LoginForm({ onLogin }) {
- const [form, setForm] = React.useState({ email: "", password: "", role: "user" });
-
- function handleChange(e) {
- setForm({ ...form, [e.target.name]: e.target.value });
- }
-
- function handleSubmit(e) {
- e.preventDefault();
- if (onLogin) onLogin(form);
- }
-
- return (
-
- );
-}
-
-export default LoginForm;
+import React from "react";
+import InputField from "../common/InputField";
+import Button from "../common/Button";
+
+function LoginForm({ onLogin }) {
+ const [form, setForm] = React.useState({ email: "", password: "", role: "user" });
+
+ function handleChange(e) {
+ setForm({ ...form, [e.target.name]: e.target.value });
+ }
+
+ function handleSubmit(e) {
+ e.preventDefault();
+ if (onLogin) onLogin(form);
+ }
+
+ return (
+
+ );
+}
+
+export default LoginForm;
diff --git a/client/src/components/user/SignupForm.jsx b/client/src/components/user/SignupForm.jsx
new file mode 100755
index 0000000..02e90bd
--- /dev/null
+++ b/client/src/components/user/SignupForm.jsx
@@ -0,0 +1,53 @@
+import React from "react";
+import { useState } from "react";
+import InputField from "../common/InputField";
+import Button from "../common/Button";
+
+function SignupForm({ 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/users", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ name: form.name,
+ email: form.email,
+ password: form.password
+ })
+ });
+ 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 SignupForm;
diff --git a/src/components/user/UserProfile.jsx b/client/src/components/user/UserProfile.jsx
old mode 100644
new mode 100755
similarity index 96%
rename from src/components/user/UserProfile.jsx
rename to client/src/components/user/UserProfile.jsx
index bdf8fad..f5ac6f5
--- a/src/components/user/UserProfile.jsx
+++ b/client/src/components/user/UserProfile.jsx
@@ -1,16 +1,16 @@
-import React from "react";
-
-function UserProfile({ user }) {
- if (!user) return No user
;
- return (
-
-
{user.name?.[0] ?? "U"}
-
-
{user.name}
-
{user.email}
-
-
- );
-}
-
-export default UserProfile;
+import React from "react";
+
+function UserProfile({ user }) {
+ if (!user) return No user
;
+ return (
+
+
{user.name?.[0] ?? "U"}
+
+
{user.name}
+
{user.email}
+
+
+ );
+}
+
+export default UserProfile;
diff --git a/src/context/AuthContext.jsx b/client/src/context/AuthContext.jsx
old mode 100644
new mode 100755
similarity index 62%
rename from src/context/AuthContext.jsx
rename to client/src/context/AuthContext.jsx
index cf7e094..8591905
--- a/src/context/AuthContext.jsx
+++ b/client/src/context/AuthContext.jsx
@@ -1,82 +1,109 @@
-import React from "react";
-import axios from "axios";
-
-const AuthContext = React.createContext();
-
-export function AuthProvider({ children }) {
-
- const [user, setUser] = React.useState(() => {
- try {
- const raw = localStorage.getItem("fa_user");
- return raw ? JSON.parse(raw) : null;
- } catch (e) {
- return null;
- }
- });
-
- const [token, setToken] = React.useState(() => {
- try {
- return localStorage.getItem("fa_token");
- } catch (e) {
- return null;
- }
- });
-
- React.useEffect(() => {
- try {
- if (user) {
- localStorage.setItem("fa_user", JSON.stringify(user));
- } else {
- localStorage.removeItem("fa_user");
- }
-
- if (token) {
- localStorage.setItem("fa_token", token);
- axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
- } else {
- localStorage.removeItem("fa_token");
- delete axios.defaults.headers.common['Authorization'];
- }
- } catch (e) { }
- }, [user, token]);
-
- async function login(credentials) {
- try {
- const res = await axios.post("http://localhost:5000/api/users/login", credentials);
- setUser(res.data);
- setToken(res.data.token);
- return res.data;
- } catch (err) {
- console.error("Login failed", err);
- throw err.response?.data?.message || "Login failed";
- }
- }
-
- function logout() {
- setUser(null);
- setToken(null);
- }
-
- async function signup(details) {
- try {
- const res = await axios.post("http://localhost:5000/api/users/register", details);
- setUser(res.data);
- setToken(res.data.token);
- return res.data;
- } catch (err) {
- console.error("Signup failed", err);
- throw err.response?.data?.message || "Signup failed";
- }
- }
-
- const isAdmin = () => user?.role === "admin";
- const isOwner = () => user?.role === "restaurant_owner" || !!user?.managedRestaurantId;
-
- return (
-
- {children}
-
- );
-}
-
-export default AuthContext;
+import React from "react";
+import axios from "axios";
+import { jwtDecode } from "jwt-decode";
+
+const AuthContext = React.createContext();
+
+export function AuthProvider({ children }) {
+
+ const [user, setUser] = React.useState(() => {
+ try {
+ const raw = localStorage.getItem("fa_user");
+ return raw ? JSON.parse(raw) : null;
+ } catch (e) {
+ console.log(e)
+ return null;
+ }
+ });
+
+ const [token, setToken] = React.useState(() => {
+ try {
+ return localStorage.getItem("fa_token");
+ } catch (e) {
+ console.log(e)
+ return null;
+ }
+ });
+
+ React.useEffect(() => {
+ try {
+ if (user) {
+ localStorage.setItem("fa_user", JSON.stringify(user));
+ } else {
+ localStorage.removeItem("fa_user");
+ }
+
+ if (token) {
+ localStorage.setItem("fa_token", token);
+ axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
+ } else {
+ localStorage.removeItem("fa_token");
+ delete axios.defaults.headers.common['Authorization'];
+ }
+ } catch (e) {
+ console.log(e)
+ }
+ }, [user, token]);
+
+ async function login(credentials) {
+ try {
+ const res = await axios.post("http://localhost:3000/api/users/login", credentials);
+ const decoded = jwtDecode(res.data.token);
+
+ setUser(decoded);
+ setToken(res.data.token);
+ return res.data;
+ } catch (err) {
+ console.error("Login failed", err);
+ throw err.response?.data?.message || "Login failed";
+ }
+ }
+
+ async function ownerLogin(credentials) {
+ try {
+ const res = await axios.post(
+ "http://localhost:3000/api/RestaurantOwners/login",
+ credentials
+ );
+ const decoded = jwtDecode(res.data.token);
+ setUser(decoded);
+ setToken(res.data.token);
+
+ // optional: mark role
+ localStorage.setItem("role", "owner");
+
+ return res.data;
+ } catch (err) {
+ console.error("Owner login failed", err);
+ throw err.response?.data?.message || "Owner login failed";
+ }
+ }
+
+ function logout() {
+ setUser(null);
+ setToken(null);
+ }
+
+ async function signup(details) {
+ try {
+ const res = await axios.post("http://localhost:3000/api/users/register", details);//removed /register here
+ setUser(res.data.user);
+ setToken(res.data.token);
+ return res.data;
+ } catch (err) {
+ console.error("Signup failed", err);
+ throw err.response?.data?.message || "Signup failed";
+ }
+ }
+
+ const isAdmin = () => user?.role === "admin";
+ const isOwner = () => user?.role === "restaurant_owner" || !!user?.managedRestaurantId;
+
+ return (
+
+ {children}
+
+ );
+}
+
+export default AuthContext;
diff --git a/client/src/context/RestaurantsContext.jsx b/client/src/context/RestaurantsContext.jsx
new file mode 100755
index 0000000..f4734ef
--- /dev/null
+++ b/client/src/context/RestaurantsContext.jsx
@@ -0,0 +1,122 @@
+import React from "react";
+import axios from "axios";
+import SAMPLE_RESTAURANTS from "../data/restaurants";
+
+const RestaurantsContext = React.createContext();
+
+export function RestaurantsProvider({ children }) {
+ const [restaurants, setRestaurants] = React.useState([]);
+
+ React.useEffect(() => {
+ axios.get("http://localhost:3000/api/restaurants")
+ .then((res) => {
+ if (res.data.length === 0) {
+ // console.log("no resturant data ... ")
+ // Fallback or handle initial seed
+ setRestaurants([]);
+ } else {
+ // console.log("yes resturant data ... ", res.data)
+ setRestaurants(res.data);
+ }
+ })
+ .catch((err) => {
+ console.error("Failed to fetch restaurants", err);
+ // Fallback to sample data if backend fails, for smoother demo
+ setRestaurants(SAMPLE_RESTAURANTS);
+ });
+ }, []);
+
+ const [favorites, setFavorites] = React.useState(() => {
+ try {
+ const raw = localStorage.getItem("fa_favorites");
+ return raw ? JSON.parse(raw) : [];
+ } catch (e) {
+ console.log(e)
+ return [];
+ }
+ });
+
+ React.useEffect(() => {
+ try {
+ localStorage.setItem("fa_favorites", JSON.stringify(favorites));
+ } catch (e) {
+ console.log(e)
+ }
+ }, [favorites]);
+
+ function addReview(restaurantId, review) {
+ return axios.post(`http://localhost:3000/api/restaurants/${restaurantId}/reviews`, review)
+ .then((res) => {
+ // Optimistically update or re-fetch. Let's update state manually to save a fetch.
+ // res.data contains the new review with ID
+ 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:3000/api/restaurants/${restaurantId}/reviews/${reviewId}`, updated)
+ .then((res) => {
+ console.log(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:3000/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) {
+ setRestaurants((prev) => [newR, ...prev]);
+ }
+
+ function updateRestaurantImage(restaurantId, dataUrl) {
+ setRestaurants((prev) =>
+ prev.map((r) => (r.id === restaurantId ? { ...r, images: [dataUrl, ...(r.images || []).slice(1)] } : r))
+ );
+ }
+
+ function toggleFavorite(restaurantId) {
+ setFavorites((prev) => (prev.includes(restaurantId) ? prev.filter((id) => id !== restaurantId) : [...prev, restaurantId]));
+ }
+
+ function isFavorite(restaurantId) {
+ return favorites.includes(restaurantId);
+ }
+
+ return (
+
+ {children}
+
+ );
+}
+
+export default RestaurantsContext;
diff --git a/src/data/restaurants.js b/client/src/data/restaurants.js
old mode 100644
new mode 100755
similarity index 96%
rename from src/data/restaurants.js
rename to client/src/data/restaurants.js
index 9afdece..2f5be2f
--- a/src/data/restaurants.js
+++ b/client/src/data/restaurants.js
@@ -1,79 +1,79 @@
-// Sample restaurants focused on popular Addis spots (local images where available)
-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";
-
-const SAMPLE_RESTAURANTS = [
- {
- id: "lucy",
- name: "Lucy Ethiopian Restaurant",
- category: "Ethiopian",
- rating: 4.6,
- price: "$$",
- address: "Bole, Addis Ababa",
- images: [LucyImg],
- hours: "08:00 - 23:00",
- description: "Traditional Ethiopian dishes served with authentic injera and a warm atmosphere.",
- menu: ["Doro Wat", "Tibs", "Shiro"],
- reviews: [
- { id: "v1", user: "Marta", rating: 5, text: "Amazing flavours!", date: "2025-10-10" },
- ],
- },
- {
- id: "tomoca",
- name: "Tomoca Coffee",
- category: "Cafe",
- rating: 4.5,
- price: "$",
- address: "Multiple locations, Addis Ababa",
- images: [tomocaImg],
- hours: "07:00 - 20:00",
- description: "Historic Ethiopian coffee roaster, known for strong, aromatic brews.",
- menu: ["Ethiopian coffee", "Pastries"],
- reviews: [],
- },
- {
- id: "kategna",
- name: "Kategna",
- category: "Ethiopian",
- rating: 4.4,
- price: "$$",
- address: "Old Airport, Addis Ababa",
- images: [kategnaImg],
- hours: "10:00 - 22:00",
- description: "Local favorite serving homestyle Ethiopian favorites in a relaxed setting.",
- menu: ["Kitfo", "Tibs"],
- reviews: [],
- },
- {
- id: "addiscafe",
- name: "Addis Cafe",
- category: "Cafe",
- rating: 4.0,
- price: "$",
- address: "Piassa, Addis Ababa",
- images: [AddisImg],
- hours: "07:00 - 20:00",
- description: "Cozy spot for coffee and light bites.",
- menu: ["Coffee", "Pastries", "Sandwiches"],
- reviews: [{ id: "v2", user: "Sam", rating: 4, text: "Nice coffee and calm place.", date: "2025-09-21" }],
- },
- {
- id: "italiano",
- name: "Italiano Corner",
- category: "Italian",
- rating: 4.2,
- price: "$$$",
- address: "Kazanchis, Addis Ababa",
- images: [ItalianoImg],
- hours: "11:00 - 22:00",
- description: "Wood-fired pizzas and homemade pasta (popular with locals and expats).",
- menu: ["Margherita", "Carbonara"],
- reviews: [],
- },
-];
-
-export default SAMPLE_RESTAURANTS;
+// Sample restaurants focused on popular Addis spots (local images where available)
+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";
+
+const SAMPLE_RESTAURANTS = [
+ {
+ id: "lucy",
+ name: "Lucy Ethiopian Restaurant",
+ category: "Ethiopian",
+ rating: 4.6,
+ price: "$$",
+ address: "Bole, Addis Ababa",
+ images: [LucyImg],
+ hours: "08:00 - 23:00",
+ description: "Traditional Ethiopian dishes served with authentic injera and a warm atmosphere.",
+ menu: ["Doro Wat", "Tibs", "Shiro"],
+ reviews: [
+ { id: "v1", user: "Marta", rating: 5, text: "Amazing flavours!", date: "2025-10-10" },
+ ],
+ },
+ {
+ id: "tomoca",
+ name: "Tomoca Coffee",
+ category: "Cafe",
+ rating: 4.5,
+ price: "$",
+ address: "Multiple locations, Addis Ababa",
+ images: [tomocaImg],
+ hours: "07:00 - 20:00",
+ description: "Historic Ethiopian coffee roaster, known for strong, aromatic brews.",
+ menu: ["Ethiopian coffee", "Pastries"],
+ reviews: [],
+ },
+ {
+ id: "kategna",
+ name: "Kategna",
+ category: "Ethiopian",
+ rating: 4.4,
+ price: "$$",
+ address: "Old Airport, Addis Ababa",
+ images: [kategnaImg],
+ hours: "10:00 - 22:00",
+ description: "Local favorite serving homestyle Ethiopian favorites in a relaxed setting.",
+ menu: ["Kitfo", "Tibs"],
+ reviews: [],
+ },
+ {
+ id: "addiscafe",
+ name: "Addis Cafe",
+ category: "Cafe",
+ rating: 4.0,
+ price: "$",
+ address: "Piassa, Addis Ababa",
+ images: [AddisImg],
+ hours: "07:00 - 20:00",
+ description: "Cozy spot for coffee and light bites.",
+ menu: ["Coffee", "Pastries", "Sandwiches"],
+ reviews: [{ id: "v2", user: "Sam", rating: 4, text: "Nice coffee and calm place.", date: "2025-09-21" }],
+ },
+ {
+ id: "italiano",
+ name: "Italiano Corner",
+ category: "Italian",
+ rating: 4.2,
+ price: "$$$",
+ address: "Kazanchis, Addis Ababa",
+ images: [ItalianoImg],
+ hours: "11:00 - 22:00",
+ description: "Wood-fired pizzas and homemade pasta (popular with locals and expats).",
+ menu: ["Margherita", "Carbonara"],
+ reviews: [],
+ },
+];
+
+export default SAMPLE_RESTAURANTS;
diff --git a/src/main.jsx b/client/src/main.jsx
old mode 100644
new mode 100755
similarity index 81%
rename from src/main.jsx
rename to client/src/main.jsx
index 109c8c7..094deea
--- a/src/main.jsx
+++ b/client/src/main.jsx
@@ -1,10 +1,9 @@
-import React from "react";
-import ReactDOM from "react-dom/client";
-import "bootstrap/dist/css/bootstrap.min.css";
-import App from "./App";
-
-ReactDOM.createRoot(document.getElementById("root")).render(
-
-
-
-);
+import React from "react";
+import ReactDOM from "react-dom/client";
+import App from "./App";
+
+ReactDOM.createRoot(document.getElementById("root")).render(
+
+
+
+);
diff --git a/src/pages/About.jsx b/client/src/pages/About.jsx
old mode 100644
new mode 100755
similarity index 96%
rename from src/pages/About.jsx
rename to client/src/pages/About.jsx
index dee61f5..8028727
--- a/src/pages/About.jsx
+++ b/client/src/pages/About.jsx
@@ -1,12 +1,12 @@
-import React from "react";
-
-function About() {
- return (
-
-
About Find Addis
-
Find Addis is a community-driven restaurant directory focused on Addis Ababa. Discover local restaurants, read reviews, and share your experience.
-
- );
-}
-
-export default About;
+import React from "react";
+
+function About() {
+ return (
+
+
About Find Addis
+
Find Addis is a community-driven restaurant directory focused on Addis Ababa. Discover local restaurants, read reviews, and share your experience.
+
+ );
+}
+
+export default About;
diff --git a/src/pages/Contact.jsx b/client/src/pages/Contact.jsx
old mode 100644
new mode 100755
similarity index 97%
rename from src/pages/Contact.jsx
rename to client/src/pages/Contact.jsx
index 64c111f..05ae50c
--- a/src/pages/Contact.jsx
+++ b/client/src/pages/Contact.jsx
@@ -1,22 +1,22 @@
-import React from "react";
-
-function Contact() {
- return (
-
-
Contact us
-
For partnership or support, email help@findaddis.example or call +251 911 000 000.
-
Or use this quick form:
-
-
- );
-}
-
-export default Contact;
+import React from "react";
+
+function Contact() {
+ return (
+
+
Contact us
+
For partnership or support, email help@findaddis.example or call +251 911 000 000.
+
Or use this quick form:
+
+
+ );
+}
+
+export default Contact;
diff --git a/client/src/pages/Favorites.jsx b/client/src/pages/Favorites.jsx
new file mode 100755
index 0000000..eb7f732
--- /dev/null
+++ b/client/src/pages/Favorites.jsx
@@ -0,0 +1,45 @@
+import React, { useContext, useState, useEffect } from "react";
+import AuthContext from "../context/AuthContext";
+import RestaurantsContext from "../context/RestaurantsContext";
+import RestaurantList from "../components/restaurant/RestaurantList";
+
+function Favorites() {
+ const { user } = useContext(AuthContext);
+ const [favorites, setFavorites] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ if (!user) return;
+ const id = user._id
+ const fetchFavorites = async () => {
+ try {
+ const res = await fetch(`http://localhost:3000/api/users/favorites/${id}`);
+ if (!res.ok) throw new Error("Failed to fetch favorites");
+ const data = await res.json();
+ setFavorites(data.favorites);
+ } catch (err) {
+ console.error(err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchFavorites();
+ }, [user]);
+
+ if (loading) return Loading favorites...
;
+
+ return (
+
+
Favorites
+ {favorites.length === 0 ? (
+
You haven't saved any restaurants yet.
+ ) : (
+
+ )}
+
+ );
+}
+
+
+export default Favorites;
diff --git a/src/pages/Home.jsx b/client/src/pages/Home.jsx
old mode 100644
new mode 100755
similarity index 52%
rename from src/pages/Home.jsx
rename to client/src/pages/Home.jsx
index 1c812aa..b1e5bb4
--- a/src/pages/Home.jsx
+++ b/client/src/pages/Home.jsx
@@ -1,57 +1,39 @@
-import React from "react";
-import HeroSection from "../components/home/HeroSection";
-import CategoryCards from "../components/home/CategoryCards";
-import RestaurantList from "../components/restaurant/RestaurantList";
-import FeaturedCard from "../components/home/FeaturedCard";
-import { useContext } from "react";
-import RestaurantsContext from "../context/RestaurantsContext";
-import { useNavigate } from "react-router-dom";
-
-function Home() {
- const { restaurants } = useContext(RestaurantsContext);
- const navigate = useNavigate();
-
- function handleQuickSearch(q) {
- navigate(`/search?q=${encodeURIComponent(q)}`);
- }
-
- return (
-
-
-
-
-
-
- Popular near you
-
- {restaurants && restaurants.filter(r => r.category === 'Ethiopian').slice(0, 4).map((r) => (
-
- ))}
-
-
-
-
- Top rated in Addis
-
- {[...restaurants].sort((a, b) => b.rating - a.rating).slice(0, 4).map((r) => (
-
- ))}
-
-
-
-
- Trending this week
-
- {[...restaurants].sort(() => 0.5 - Math.random()).slice(0, 4).map((r) => (
-
- ))}
-
-
-
- );
-}
-
-export default Home;
+import React from "react";
+import HeroSection from "../components/home/HeroSection";
+import CategoryCards from "../components/home/CategoryCards";
+import RestaurantList from "../components/restaurant/RestaurantList";
+import FeaturedCard from "../components/home/FeaturedCard";
+import { useContext } from "react";
+import RestaurantsContext from "../context/RestaurantsContext";
+import { useNavigate } from "react-router-dom";
+
+function Home() {
+ const { restaurants } = useContext(RestaurantsContext);
+ const navigate = useNavigate();
+
+ function handleQuickSearch(q) {
+ navigate(`/search?q=${encodeURIComponent(q)}`);
+ }
+
+ return (
+
+
+
+
+
+
+ Featured restaurants
+
+ {restaurants.slice(0, 6).map((r) => (
+
+ ))}
+
+
+
+ );
+}
+
+export default Home;
diff --git a/src/pages/Login.jsx b/client/src/pages/Login.jsx
old mode 100644
new mode 100755
similarity index 75%
rename from src/pages/Login.jsx
rename to client/src/pages/Login.jsx
index de8bf13..d89b9e3
--- a/src/pages/Login.jsx
+++ b/client/src/pages/Login.jsx
@@ -1,36 +1,31 @@
-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 Login() {
- const { login } = useContext(AuthContext);
- const navigate = useNavigate();
- const [error, setError] = React.useState(null);
-
- async function handleLogin(credentials) {
- setError(null);
- try {
- const userData = await login(credentials);
- // Redirect Admins and Vendors directly to their dashboard
- if (userData.role === 'admin' || userData.role === 'restaurant_owner') {
- navigate("/profile");
- } else {
- navigate("/");
- }
- } catch (e) {
- setError(e);
- }
- }
-
- return (
-
-
Log in
- {error &&
{error}
}
-
-
- );
-}
-
-export default Login;
+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 Login() {
+ const { login } = useContext(AuthContext);
+ const navigate = useNavigate();
+ const [error, setError] = React.useState(null);
+
+ async function handleLogin(credentials) {
+ setError(null);
+ try {
+ await login(credentials);
+ navigate("/");
+ } catch (e) {
+ setError(e);
+ }
+ }
+
+ return (
+
+
Log in
+ {error &&
{error}
}
+
+
+ );
+}
+
+export default Login;
diff --git a/src/pages/NotFound.jsx b/client/src/pages/NotFound.jsx
old mode 100644
new mode 100755
similarity index 96%
rename from src/pages/NotFound.jsx
rename to client/src/pages/NotFound.jsx
index b9eb14a..8a329cc
--- a/src/pages/NotFound.jsx
+++ b/client/src/pages/NotFound.jsx
@@ -1,14 +1,14 @@
-import React from "react";
-import { Link } from "react-router-dom";
-
-function NotFound() {
- return (
-
-
Page not found
-
Sorry — we couldn't find that page.
-
Go home
-
- );
-}
-
-export default NotFound;
+import React from "react";
+import { Link } from "react-router-dom";
+
+function NotFound() {
+ return (
+
+
Page not found
+
Sorry — we couldn't find that page.
+
Go home
+
+ );
+}
+
+export default NotFound;
diff --git a/client/src/pages/OwnerLoginPage.jsx b/client/src/pages/OwnerLoginPage.jsx
new file mode 100755
index 0000000..b9dc724
--- /dev/null
+++ b/client/src/pages/OwnerLoginPage.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 OwnerLoginPage() {
+ const { ownerLogin } = useContext(AuthContext);
+ const navigate = useNavigate();
+
+ async function handleLogin(credentials) {
+ try {
+ await ownerLogin(credentials);
+ navigate("/");
+ } catch (e) {
+ alert(e);
+ }
+ }
+
+ return (
+
+
Log in
+
+
+ );
+}
+
+export default OwnerLoginPage;
diff --git a/client/src/pages/OwnerSignup.jsx b/client/src/pages/OwnerSignup.jsx
new file mode 100755
index 0000000..1195f05
--- /dev/null
+++ b/client/src/pages/OwnerSignup.jsx
@@ -0,0 +1,28 @@
+import React from "react";
+import OwnerSignupForm from "../components/owner/OwnerSignupForm";
+import { useContext } from "react";
+import AuthContext from "../context/AuthContext";
+import { useNavigate } from "react-router-dom";
+
+function OwnerSignUp() {
+ const { signup } = useContext(AuthContext);
+ const navigate = useNavigate();
+
+ async function handleSignup(details) {
+ try {
+ await signup(details);
+ navigate("/");
+ } catch (e) {
+ alert(e);
+ }
+ }
+
+ return (
+
+
Create an account
+
+
+ );
+}
+
+export default OwnerSignUp;
diff --git a/src/pages/RestaurantDetailsPage.jsx b/client/src/pages/RestaurantDetailsPage.jsx
old mode 100644
new mode 100755
similarity index 95%
rename from src/pages/RestaurantDetailsPage.jsx
rename to client/src/pages/RestaurantDetailsPage.jsx
index 29bc3e4..aad88a7
--- a/src/pages/RestaurantDetailsPage.jsx
+++ b/client/src/pages/RestaurantDetailsPage.jsx
@@ -1,69 +1,69 @@
-import React from "react";
-import { useParams, useNavigate } from "react-router-dom";
-import RestaurantsContext from "../context/RestaurantsContext";
-import { useContext } from "react";
-import MenuSection from "../components/restaurant/MenuSection";
-import ReviewCard from "../components/restaurant/Reviewcard";
-import StarRating from "../components/common/StarRating";
-import MapSection from "../components/common/MapSection";
-
-
-
-function RestaurantDetailsPage() {
- const { id } = useParams();
- const navigate = useNavigate();
- const { restaurants } = useContext(RestaurantsContext);
- const r = restaurants.find((x) => x.id === id);
-
- if (!r) {
- return Restaurant not found
;
- }
-
- return (
-
-
-
-
-
{r.name}
-
-
{r.category} • {r.price}
-
-
-
-
{r.description}
-
-
-
-
-
-
-
- Reviews
- {r.reviews && r.reviews.length === 0 && No reviews yet—be the first.
}
-
- {r.reviews.map((rev) => )}
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-export default RestaurantDetailsPage;
+import React from "react";
+import { useParams, useNavigate } from "react-router-dom";
+import RestaurantsContext from "../context/RestaurantsContext";
+import { useContext } from "react";
+import MenuSection from "../components/restaurant/MenuSection";
+import ReviewCard from "../components/restaurant/Reviewcard";
+import StarRating from "../components/common/StarRating";
+import MapSection from "../components/common/MapSection";
+
+
+
+function RestaurantDetailsPage() {
+ const { id } = useParams();
+ const navigate = useNavigate();
+ const { restaurants } = useContext(RestaurantsContext);
+ const r = restaurants.find((res) => res._id === id);
+
+ if (!r) {
+ return Restaurant not found
;
+ }
+
+ return (
+
+
+
+
+
{r.name}
+
+
{r.category} • {r.price}
+
+
+
+
{r.description}
+
+
+
+
+
+
+
+ Reviews
+ {r.reviews && r.reviews.length === 0 && No reviews yet—be the first.
}
+
+ {r.reviews.map((rev) => )}
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default RestaurantDetailsPage;
diff --git a/src/pages/RestaurantListPage.jsx b/client/src/pages/RestaurantListPage.jsx
old mode 100644
new mode 100755
similarity index 85%
rename from src/pages/RestaurantListPage.jsx
rename to client/src/pages/RestaurantListPage.jsx
index 9f51671..b58e9fd
--- a/src/pages/RestaurantListPage.jsx
+++ b/client/src/pages/RestaurantListPage.jsx
@@ -1,30 +1,30 @@
-import React from "react";
-import Sidebar from "../components/layout/Sidebar";
-import RestaurantList from "../components/restaurant/RestaurantList";
-import { useContext, useState, useMemo } from "react";
-import RestaurantsContext from "../context/RestaurantsContext";
-
-
-
-function RestaurantListPage() {
- const { restaurants } = useContext(RestaurantsContext);
- const [filters, setFilters] = useState({ category: "", minRating: 0 });
-
- const filtered = useMemo(() => {
- return restaurants.filter((r) => (!filters.category || r.category === filters.category) && r.rating >= filters.minRating);
- }, [restaurants, filters]);
-
- return (
-
-
-
-
-
All restaurants
-
-
-
-
- );
-}
-
-export default RestaurantListPage;
+import React from "react";
+import Sidebar from "../components/layout/Sidebar";
+import RestaurantList from "../components/restaurant/RestaurantList";
+import { useContext, useState, useMemo } from "react";
+import RestaurantsContext from "../context/RestaurantsContext";
+
+
+
+function RestaurantListPage() {
+ const { restaurants } = useContext(RestaurantsContext);
+ const [filters, setFilters] = useState({ category: "", minRating: 0 });
+console.log("RestaurantListPage",restaurants)
+ const filtered = useMemo(() => {
+ return restaurants.filter((r) => (!filters.category || r.category === filters.category) && r.rating >= filters.minRating);
+ }, [restaurants, filters]);
+
+ return (
+
+
+
+
+
All restaurants
+
+
+
+
+ );
+}
+
+export default RestaurantListPage;
diff --git a/src/pages/SearchResultsPage.jsx b/client/src/pages/SearchResultsPage.jsx
old mode 100644
new mode 100755
similarity index 97%
rename from src/pages/SearchResultsPage.jsx
rename to client/src/pages/SearchResultsPage.jsx
index 5a53642..cc397f1
--- a/src/pages/SearchResultsPage.jsx
+++ b/client/src/pages/SearchResultsPage.jsx
@@ -1,36 +1,36 @@
-import React from "react";
-import { useLocation } from "react-router-dom";
-import RestaurantsContext from "../context/RestaurantsContext";
-import { useContext, useMemo } from "react";
-import RestaurantList from "../components/restaurant/RestaurantList";
-
-function useQuery() {
- return new URLSearchParams(useLocation().search);
-}
-
-function SearchResultsPage() {
- const { restaurants } = useContext(RestaurantsContext);
- const q = useQuery().get("q") || "";
-
- const results = useMemo(() => {
- if (!restaurants) return [];
- const lower = q.toLowerCase();
- return restaurants.filter((r) => {
- // Defensive checks for properties
- const nameMatch = r.name && r.name.toLowerCase().includes(lower);
- const descMatch = r.description && r.description.toLowerCase().includes(lower);
- const menuMatch = r.menu && Array.isArray(r.menu) && r.menu.some((m) => m && typeof m === 'string' && m.toLowerCase().includes(lower));
-
- return nameMatch || descMatch || menuMatch;
- });
- }, [restaurants, q]);
-
- return (
-
-
Search results for “{q}”
-
-
- );
-}
-
-export default SearchResultsPage;
+import React from "react";
+import { useLocation } from "react-router-dom";
+import RestaurantsContext from "../context/RestaurantsContext";
+import { useContext, useMemo } from "react";
+import RestaurantList from "../components/restaurant/RestaurantList";
+
+function useQuery() {
+ return new URLSearchParams(useLocation().search);
+}
+
+function SearchResultsPage() {
+ const { restaurants } = useContext(RestaurantsContext);
+ const q = useQuery().get("q") || "";
+
+ const results = useMemo(() => {
+ if (!restaurants) return [];
+ const lower = q.toLowerCase();
+ return restaurants.filter((r) => {
+ // Defensive checks for properties
+ const nameMatch = r.name && r.name.toLowerCase().includes(lower);
+ const descMatch = r.description && r.description.toLowerCase().includes(lower);
+ const menuMatch = r.menu && Array.isArray(r.menu) && r.menu.some((m) => m && typeof m === 'string' && m.toLowerCase().includes(lower));
+
+ return nameMatch || descMatch || menuMatch;
+ });
+ }, [restaurants, q]);
+
+ return (
+
+
Search results for “{q}”
+
+
+ );
+}
+
+export default SearchResultsPage;
diff --git a/src/pages/Signup.jsx b/client/src/pages/Signup.jsx
old mode 100644
new mode 100755
similarity index 76%
rename from src/pages/Signup.jsx
rename to client/src/pages/Signup.jsx
index 5bd29a2..8e9a61b
--- a/src/pages/Signup.jsx
+++ b/client/src/pages/Signup.jsx
@@ -1,36 +1,31 @@
-import React from "react";
-import SignupForm from "../components/user/SignupForm";
-import { useContext } from "react";
-import AuthContext from "../context/AuthContext";
-import { useNavigate } from "react-router-dom";
-
-function Signup() {
- const { signup } = useContext(AuthContext);
- const navigate = useNavigate();
- const [error, setError] = React.useState(null);
-
- async function handleSignup(details) {
- setError(null);
- try {
- const userData = await signup(details);
- // Redirect Admins and Vendors directly to their dashboard
- if (userData.role === 'admin' || userData.role === 'restaurant_owner') {
- navigate("/profile");
- } else {
- navigate("/");
- }
- } catch (e) {
- setError(e);
- }
- }
-
- return (
-
-
Create an account
- {error &&
{error}
}
-
-
- );
-}
-
-export default Signup;
+import React from "react";
+import SignupForm from "../components/user/SignupForm";
+import { useContext } from "react";
+import AuthContext from "../context/AuthContext";
+import { useNavigate } from "react-router-dom";
+
+function Signup() {
+ const { signup } = useContext(AuthContext);
+ const navigate = useNavigate();
+ const [error, setError] = React.useState(null);
+
+ async function handleSignup(details) {
+ setError(null);
+ try {
+ await signup(details);
+ navigate("/");
+ } catch (e) {
+ setError(e);
+ }
+ }
+
+ return (
+
+
Create an account
+ {error &&
{error}
}
+
+
+ );
+}
+
+export default Signup;
diff --git a/client/src/pages/UserProfilePage.jsx b/client/src/pages/UserProfilePage.jsx
new file mode 100755
index 0000000..cba8219
--- /dev/null
+++ b/client/src/pages/UserProfilePage.jsx
@@ -0,0 +1,157 @@
+import React from "react";
+import { useContext } from "react";
+import UserProfile from "../components/user/UserProfile";
+import AuthContext from "../context/AuthContext";
+import axios from "axios";
+
+function UserProfilePage() {
+ const { user, isAdmin, isOwner, logout } = useContext(AuthContext);
+ const [requests, setRequests] = React.useState([]);
+
+ // Owner State
+ const [isEditing, setIsEditing] = React.useState(false);
+ const [formData, setFormData] = React.useState({ name: '', description: '', menu: '' });
+ const [msg, setMsg] = React.useState(null);
+
+ function fetchRequests() {
+ axios.get('http://localhost:3000/api/requests')
+ .then(res => setRequests(res.data))
+ .catch(err => console.error("Failed to fetch requests", err));
+ }
+ // Admin: Fetch pending requests
+ React.useEffect(() => {
+ if (isAdmin()) {
+ fetchRequests();
+ }
+ }, [user]);
+
+
+
+ function handleApprove(id) {
+ axios.post(`http://localhost:3000/api/requests/${id}/approve`)
+ .then(() => {
+ alert("Request approved!");
+ fetchRequests();
+ })
+ .catch(err => alert(err.response?.data?.message || "Error approving"));
+ }
+
+ function handleReject(id) {
+ axios.post(`http://localhost:3000/api/requests/${id}/reject`)
+ .then(() => {
+ alert("Request rejected.");
+ fetchRequests();
+ })
+ .catch(err => alert(err.response?.data?.message || "Error rejecting"));
+ }
+
+ function submitUpdate(e) {
+ e.preventDefault();
+ const payload = {
+ type: 'update', // or create, if new
+ targetRestaurantId: user.managedRestaurantId,
+ data: {
+ ...formData,
+ menu: formData.menu.split(',').map(s => s.trim()) // simple CSV parsing
+ }
+ };
+
+ axios.post('http://localhost:000/api/requests', payload)
+ .then(() => {
+ setMsg("Update request submitted for approval.");
+ setIsEditing(false);
+ })
+ .catch(err => setMsg("Error submitting request: " + err.message));
+ }
+
+ if (!user) return Please log in to view your profile.
;
+
+ return (
+
+
+
Dashboard
+
+
+
+
+
+ {msg &&
{msg}
}
+
+ {isAdmin() && (
+
+ Admin Dashboard
+
+ Pending Requests ({requests.length})
+ {requests.length === 0 ? (
+ No pending requests.
+ ) : (
+
+ {requests.map(req => (
+
+
+ {req.type.toUpperCase()} Request
+ {new Date(req.createdAt).toLocaleDateString()}
+
+
User: {req.userId?.name} ({req.userId?.email})
+
Target: {req.targetRestaurantId}
+
+ {JSON.stringify(req.data, null, 2)}
+
+
+
+
+
+
+ ))}
+
+ )}
+
+ )}
+
+ {isOwner() && (
+
+ My Restaurant
+
+ {!isEditing ? (
+
+
Managed Restaurant ID: {user.managedRestaurantId || "None Assigned"}
+ {user.managedRestaurantId && (
+
+ )}
+
* Updates require admin approval before going live.
+
+ ) : (
+
+ )}
+
+ )}
+
+ {!isAdmin() && !isOwner() && (
+
+ My Favorites
+ {/* Reuse Favorite Logic or Component later */}
+ Visit the favorites page to manage your list.
+
+ )}
+
+ );
+}
+
+export default UserProfilePage;
diff --git a/src/pages/WriteReviewPage.jsx b/client/src/pages/WriteReviewPage.jsx
old mode 100644
new mode 100755
similarity index 97%
rename from src/pages/WriteReviewPage.jsx
rename to client/src/pages/WriteReviewPage.jsx
index 723af91..266ae91
--- a/src/pages/WriteReviewPage.jsx
+++ b/client/src/pages/WriteReviewPage.jsx
@@ -1,45 +1,45 @@
-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
-
-
- );
-}
-
-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
+
+
+ );
+}
+
+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..f705364 100644
--- a/package.json
+++ b/package.json
@@ -1,32 +1,17 @@
{
- "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\"",
+ "lint": "npm run lint --workspace client ",
+ "build": "npm run build --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 (
-
- );
-}
-
-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 (
-
- );
-}
-
-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.
-
- ) : (
-
- )}
-
- )}
-
- {!isAdmin() && !isOwner() && (
-
- My Favorites
- {/* Reuse Favorite Logic or Component later */}
- Visit the favorites page to manage your list.
-
- )}
-
- );
-}
-
-export default UserProfilePage;