diff --git a/README.md b/README.md index 390c4fe..0047fe2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,43 @@ +
+ +# 🎨 STATION GUIDE πŸ–ŒοΈ + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
🌟 Stars🍴 ForksπŸ› IssuesπŸ”” Open PRsπŸ”• Close PRsπŸ› οΈ LanguagesπŸ‘₯ Contributors
StarsForksIssuesOpen Pull RequestsClose Pull RequestsGitHub language countGitHub Contributors count
+
+
+ + + + # STATION GUIDE : YOUR PLATFORM GUIDE diff --git a/backend/.env.sample b/backend/.env.sample new file mode 100644 index 0000000..3d377ae --- /dev/null +++ b/backend/.env.sample @@ -0,0 +1,9 @@ +EMAIL_USER=your_gmail +#your email +EMAIL_USER=your_gmail + +# To create a passkey on the phone or computer you’re on: + +# 1. Go to https://myaccount.google.com/signinoptions/passkeys. +# 2. Tap Create a passkey and then Continue.(You'll be required to unlock your device.) +# 3. A 16 character passkey is generated which you can use in below. \ No newline at end of file diff --git a/backend/controllers/contactusController.js b/backend/controllers/contactusController.js new file mode 100644 index 0000000..4b0c192 --- /dev/null +++ b/backend/controllers/contactusController.js @@ -0,0 +1,49 @@ +import nodemailer from "nodemailer"; +import "dotenv/config"; + +export const createContactUs = async (req, res) => { + const { mail, subject, message } = req.body; + + try { + const transporter = nodemailer.createTransport({ + service: "gmail", + host: "smtp.gmail.com", + port: 587, + secure: false, + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASS, + }, + tls: { + rejectUnauthorized: false, // Disable strict SSL verification + }, + }); + + const mailOptions = { + from: mail, + to: process.env.EMAIL_USER, + subject: subject, + text: message, + }; + + // Send mail with defined transport object + transporter.sendMail(mailOptions, (error, mailOptions) => { + if (error) { + console.error("Error occurred: " + error.message); + return; + } + }); + + res.status(200).json({ + status: "success", + message: "Your contact request has been successfully received.", + }); + } catch (err) { + console.error(`Error at transport : ${err}`); + res.status(500).json({ + status: "error", + message: + "There was an error sending your message. Please try again later.", + }); + } +}; diff --git a/backend/controllers/submitFeedback.js b/backend/controllers/submitFeedback.js index 5062e6d..43654f4 100644 --- a/backend/controllers/submitFeedback.js +++ b/backend/controllers/submitFeedback.js @@ -1,4 +1,9 @@ +import nodemailer from 'nodemailer'; +import User from '../models/User.js'; +import { generateOTP , verifyOTP} from '../utils/otputils.js'; // Import the OTP generation function +import { sendOTPEmail } from '../utils/emailUtils.js'; // Import the email sender utility if separated into a different file +import { hashPassword } from '../utils/authFunctions.js'; // Controller to handle user feedback submission export const submitFeedback = async (req, res) => { @@ -21,3 +26,104 @@ export const submitFeedback = async (req, res) => { return res.status(error.statusCode || 500).json({ message: error.message || 'An error occurred while submitting feedback' }); } }; + +export const sendOTPToEmail = async (req, res) => { + try { + const { email } = req.body; + + // Check if email is provided + if (!email) { + return res.status(400).json({ error: 'Email is required' }); + } + + // Find user by email + const user = await User.findOne({ email }); + + if (!user) { + return res.status(404).json({ error: 'User does not exist' }); + } + + // Generate OTP and set expiry (10 minutes from now) + const otp = generateOTP(); + const otpExpiry = new Date(Date.now() + 10 * 60 * 1000); // OTP expires in 10 minutes + + // Store the OTP and expiry in the user's document + user.otp = otp; + user.otpExpiry = otpExpiry; + await user.save(); + + // Send OTP to user's email using the utility + await sendOTPEmail(email, otp); + + res.status(200).json({ message: 'OTP sent to email' }); + } catch (error) { + console.error('Error sending OTP:', error); + return res.status(500).json({ error: error.message || 'Internal Server Error' }); + } +}; + +export const verifyOTPController = async (req, res) => { + try { + const { email, otp } = req.body; + + // Check if email and OTP are provided + if (!email || !otp) { + return res.status(400).json({ error: 'Email and OTP are required' }); + } + + // Find user by email + const user = await User.findOne({ email }); + + if (!user) { + return res.status(404).json({ error: 'User not found' }); + } + + // Verify the OTP and check expiration + const isValid = verifyOTP(user, otp); + + if (!isValid) { + return res.status(400).json({ error: 'Invalid or expired OTP' }); + } + + // Clear OTP after successful verification (optional, but recommended) + user.otp = null; + user.otpExpiry = null; + await user.save(); + + // OTP is valid + res.status(200).json({ message: 'OTP verified successfully' }); + } catch (error) { + console.error('Error verifying OTP:', error); + res.status(500).json({ error: 'Internal Server Error' }); + } +}; + +export const resetPassword = async (req, res) => { + try { + const { email, newPassword } = req.body; + + // Check if both email and new password are provided + if (!email || !newPassword) { + return res.status(400).json({ error: 'Email and new password are required' }); + } + + // Find the user by email + const user = await User.findOne({ email }); + + if (!user) { + return res.status(404).json({ error: 'User not found' }); + } + + // Hash the new password + const hashedPassword = await hashPassword(newPassword); + + // Update the user's password in the database + user.password = hashedPassword; + await user.save(); + + res.status(200).json({ message: 'Password reset successfully' }); + } catch (error) { + console.error('Error resetting password:', error); + res.status(500).json({ error: 'Internal Server Error' }); + } +}; diff --git a/backend/controllers/trainController.js b/backend/controllers/trainController.js new file mode 100644 index 0000000..caee9d9 --- /dev/null +++ b/backend/controllers/trainController.js @@ -0,0 +1,62 @@ +import Train from "../models/Trains.js"; + + +// Create a new train +export const createTrain = async (req, res) => { + try { + const { trainNumber, trainName, nextStation, services, platformDetails, coachDetails } = req.body; + + + const existingTrain = await Train.findOne({ trainNumber }); + if (existingTrain) { + return res.status(400).json({ message: "Train with this number already exists" }); + } + + + const newTrain = new Train({ + trainNumber, + trainName, + nextStation, + services, + platformDetails, + coachDetails, + }); + + const savedTrain = await newTrain.save(); + + return res.status(201).json({ + message: "Train created successfully", + train: savedTrain, + }); + } catch (error) { + return res.status(500).json({ message: "Error creating train", error: error.message }); + } +}; + +// Fetch all trains +export const getAllTrains = async (req, res) => { + try { + const trains = await Train.find(); + return res.status(200).json(trains); + } catch (error) { + return res.status(500).json({ message: "Error fetching trains", error: error.message }); + } +}; + +// Fetch a single train by train number +export const getTrainByNumber = async (req, res) => { + try { + const { trainNumber } = req.params; + + const train = await Train.findOne({ trainNumber }); + + if (!train) { + return res.status(404).json({ message: "Train not found" }); + } + + return res.status(200).json(train); + } catch (error) { + return res.status(500).json({ message: "Error fetching train", error: error.message }); + } +}; + diff --git a/backend/index.js b/backend/index.js index 3ecc689..7eb5448 100644 --- a/backend/index.js +++ b/backend/index.js @@ -29,10 +29,15 @@ connectDB(); import authRoutes from "./routes/authRoutes.js"; import stationRoutes from "./routes/stationRoutes.js"; +import trainRoutes from "./routes/trainRoutes.js"; +import contactUs from "./routes/contactUsRouter.js"; app.use("/auth", authRoutes); app.use("/api", authRoutes); app.use("/station", stationRoutes); +app.use("/train", trainRoutes); +app.use("/contact", contactUs); + app.get("/", (req, res) => { res.send("Working..."); diff --git a/backend/models/Trains.js b/backend/models/Trains.js new file mode 100644 index 0000000..9d29e6e --- /dev/null +++ b/backend/models/Trains.js @@ -0,0 +1,82 @@ +// train.js +import mongoose from "mongoose"; + +const Schema = mongoose.Schema; + +const trainSchema = new Schema( + { + trainNumber: { + type: Number, + required: true, + unique: true, + trim: true, + }, + trainName: { + type: String, + required: true, + trim: true, + }, + nextStation: { + type: { + name: { + type: String, + required: true, + trim: true, + }, + stationCode: { + type: String, + required: true, + trim: true, + }, + arrivalTime: { + type: Date, + required: true, + }, + }, + required: true, + }, + services: { + type: [String], // An array of available services like ["WiFi", "Food", "Lounge"] + required: true, + }, + platformDetails: { + platformNumber: { + type: Number, + required: true, + }, + boardingTime: { + type: Date, + required: true, + }, + }, + coachDetails: [ + { + coachNumber: { + type: String, + required: true, + trim: true, + }, + coachType: { + type: String, + enum: ['Sleeper', 'AC', 'General', 'ChairCar', 'FirstClass'], + required: true, + }, + capacity: { + type: Number, + required: true, + }, + reservedSeats: { + type: Number, + required: true, + default: 0, + }, + }, + ], + }, + { + timestamps: true, + } +); + +const Train = mongoose.model("Train", trainSchema); +export default Train; diff --git a/backend/models/User.js b/backend/models/User.js index 05ac800..ca95f98 100644 --- a/backend/models/User.js +++ b/backend/models/User.js @@ -32,6 +32,14 @@ const userSchema = new Schema({ type: String, trim: true, default: '', + }, + otp: { + type: String, // The OTP code + required: false, + }, + otpExpiry: { + type: Date, // The OTP expiry timestamp + required: false, } }, { timestamps: true diff --git a/backend/package-lock.json b/backend/package-lock.json index 67ee812..54dbbc5 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -19,6 +19,7 @@ "jsonwebtoken": "^9.0.2", "mongoose": "^8.7.0", "node": "^22.8.0", + "nodemailer": "^6.9.15", "nodemon": "^3.1.7", "socket.io": "^4.8.0" } @@ -2305,6 +2306,15 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/nodemailer": { + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.15.tgz", + "integrity": "sha512-AHf04ySLC6CIfuRtRiEYtGEXgRfa6INgWGluDhnxTZhHSKvrBu7lc1VVchQ0d8nPc4cFaZoPq8vkyNoZr0TpGQ==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nodemon": { "version": "3.1.7", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", diff --git a/backend/package.json b/backend/package.json index ff8eb97..7004140 100644 --- a/backend/package.json +++ b/backend/package.json @@ -14,6 +14,7 @@ "jsonwebtoken": "^9.0.2", "mongoose": "^8.7.0", "node": "^22.8.0", + "nodemailer": "^6.9.15", "nodemon": "^3.1.7", "socket.io": "^4.8.0" }, diff --git a/backend/routes/contactUsRouter.js b/backend/routes/contactUsRouter.js new file mode 100644 index 0000000..6cbc0fa --- /dev/null +++ b/backend/routes/contactUsRouter.js @@ -0,0 +1,7 @@ +import express from 'express'; +const router = express.Router(); +import { createContactUs } from "../controllers/contactusController.js"; + +router.post("/contactus", createContactUs); + +export default router; \ No newline at end of file diff --git a/backend/routes/stationRoutes.js b/backend/routes/stationRoutes.js index 1629b20..3ea5ecc 100644 --- a/backend/routes/stationRoutes.js +++ b/backend/routes/stationRoutes.js @@ -2,7 +2,7 @@ import express from 'express'; import { getCloakroomBookingsByStation, getCoolieBookingsByStation, getStationBookings, getWheelchairBookingsByStation } from '../controllers/stationBookingsController.js'; import { createStation, getAllStations } from '../controllers/StationController.js'; - +import { sendOTPToEmail ,verifyOTPController, resetPassword} from '../controllers/submitFeedback.js'; const router = express.Router(); // Route to fetch all bookings for a station @@ -15,5 +15,7 @@ router.post('/', createStation); router.get('/:id/coolies', getCoolieBookingsByStation); router.get('/:id/wheelchairs', getWheelchairBookingsByStation); router.get('/:id/cloakrooms', getCloakroomBookingsByStation); - +router.post('/send-otp', sendOTPToEmail); +router.post('/verify-otp', verifyOTPController); +router.post('/reset-password', resetPassword); export default router; diff --git a/backend/routes/trainRoutes.js b/backend/routes/trainRoutes.js new file mode 100644 index 0000000..ec16855 --- /dev/null +++ b/backend/routes/trainRoutes.js @@ -0,0 +1,13 @@ +// routes.js +import express from 'express'; +import { createTrain, getAllTrains, getTrainByNumber } from '../controllers/trainController.js'; + + +const router = express.Router(); + +router.get('/:trainNumber', getTrainByNumber); +router.post('/', createTrain); +router.get('/', getAllTrains); + + +export default router; diff --git a/backend/utils/emailUtils.js b/backend/utils/emailUtils.js new file mode 100644 index 0000000..906e3b8 --- /dev/null +++ b/backend/utils/emailUtils.js @@ -0,0 +1,28 @@ +import nodemailer from 'nodemailer'; + +// Function to send OTP via email +export const sendOTPEmail = async (email, otp) => { + try { + // Create a transporter using SMTP settings (e.g., Gmail) + const transporter = nodemailer.createTransport({ + service: 'gmail', + auth: { + user: process.env.EMAIL_USER, // Your email address + pass: process.env.EMAIL_PASS // Your email password or app-specific password + } + }); + + const mailOptions = { + from: process.env.EMAIL_USER, + to: email, + subject: 'Your Password Reset OTP', + text: `Your OTP for password reset is ${otp}. It will expire in 10 minutes.` + }; + + // Send the email + await transporter.sendMail(mailOptions); + } catch (error) { + console.error('Error sending email:', error); + throw new Error('Failed to send OTP email'); + } +}; diff --git a/backend/utils/otputils.js b/backend/utils/otputils.js new file mode 100644 index 0000000..f9e6348 --- /dev/null +++ b/backend/utils/otputils.js @@ -0,0 +1,16 @@ +// Generate a 6-digit OTP +export const generateOTP = () => { + return Math.floor(100000 + Math.random() * 900000).toString(); + }; + + // Check if OTP is valid (both matching and not expired) + export const verifyOTP = (user, submittedOtp) => { + return user.otp === submittedOtp && user.otpExpiry > Date.now(); + }; + + // Clear OTP after it's used + export const clearOTP = async (user) => { + user.otp = null; + user.otpExpiry = null; + await user.save(); + }; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8c6d4ff..16254e0 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -16,18 +16,22 @@ "@react-oauth/google": "^0.12.1", "axios": "^1.7.7", "dotenv": "^16.4.5", + "firebase": "^11.0.1", "framer-motion": "^11.11.8", "google-translate-api-x": "^10.7.1", "jwt-decode": "^4.0.0", "leaflet": "^1.9.4", + "lucide-react": "^0.453.0", "react": "^18.3.1", "react-datepicker": "^7.4.0", "react-dom": "^18.3.1", "react-icons": "^5.3.0", "react-leaflet": "^4.2.1", + "react-responsive": "^10.0.0", "react-router-dom": "^6.26.2", "shadcn-ui": "^0.2.3", "socket.io-client": "^4.8.0", + "station-saarthi": "file:", "styled-components": "^6.1.13", "yup": "^1.4.0" }, @@ -889,6 +893,568 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@firebase/analytics": { + "version": "0.10.9", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.9.tgz", + "integrity": "sha512-FrvW6u6xDBKXUGYUy1WIUh0J9tvbppMsk90mig0JhHST8iLveKu/dIBVeVE/ZYZhmXy4fkI7SPSWvD1V0O4tXw==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/installations": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.15.tgz", + "integrity": "sha512-C5to422Sr8FkL0MPwXcIecbMnF4o2Ll7MtoWvIm4Q/LPJvvM+tWa1DiU+LzsCdsd1/CYE9EIW9Ma3ko9XnAAYw==", + "dependencies": { + "@firebase/analytics": "0.10.9", + "@firebase/analytics-types": "0.8.2", + "@firebase/component": "0.6.10", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.2.tgz", + "integrity": "sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw==" + }, + "node_modules/@firebase/app": { + "version": "0.10.15", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.15.tgz", + "integrity": "sha512-he6qlG3pmwL+LHdG/BrSMBQeJzzutciq4fpXN3lGa1uSwYSijJ24VtakS/bP2X9SiDf8jGywJ4u+OgXAenJsNg==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.9.tgz", + "integrity": "sha512-YzVn1mMLzD2JboMPVVO0Pe20YOgWzrF+aXoAmmd0v3xec051n83YpxSUZbacL69uYvk0dHrEsbea44QtQ5WPDA==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.16", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.16.tgz", + "integrity": "sha512-AxIGzLRXrTFNL+H6V+4BO0w/gERloROfRbWI/FoJUnQd0qPZIzyfdHZBbThFzFGLfDt/mVs2kdjYFx/l9I8NhQ==", + "dependencies": { + "@firebase/app-check": "0.8.9", + "@firebase/app-check-types": "0.5.2", + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz", + "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.2.tgz", + "integrity": "sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.2.45", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.45.tgz", + "integrity": "sha512-5rYbXq1ndtMTg+07oH4WrkYuP+NZq61uzVwW1hlmybp/gr4cXq2SfaP9fc6/9IzTKmu3dh3H0fjj++HG7Z7o/w==", + "dependencies": { + "@firebase/app": "0.10.15", + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz", + "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==" + }, + "node_modules/@firebase/auth": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.8.0.tgz", + "integrity": "sha512-/O7UDWE5S5ux456fzNHSLx/0YN/Kykw/WyAzgDQ6wvkddZhSEmPX19EzxgsFldzhuFjsl5uOZTz8kzlosCiJjg==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.15.tgz", + "integrity": "sha512-jz6k1ridPiecKI8CBRiqCM6IMOhwYp2MD+YvoxnMiK8nQLSTm57GvHETlPNX3WlbyQnCjMCOvrAhe27whyxAEg==", + "dependencies": { + "@firebase/auth": "1.8.0", + "@firebase/auth-types": "0.12.2", + "@firebase/component": "0.6.10", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz", + "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==" + }, + "node_modules/@firebase/auth-types": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.2.tgz", + "integrity": "sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.10.tgz", + "integrity": "sha512-OsNbEKyz9iLZSmMUhsl6+kCADzte00iisJIRUspnUqvDCX+RSGZOBIqekukv/jN177ovjApBQNFaxSYIDc/SyQ==", + "dependencies": { + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/data-connect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.1.1.tgz", + "integrity": "sha512-RBJ7XE/a3oXFv31Jlw8cbMRdsxQoI8F3L7xm4n93ab+bIr1NQUiYGgW9L7TTw7obdNev91ZnW0xfqJtXcPA5yA==", + "dependencies": { + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.9.tgz", + "integrity": "sha512-EkiPSKSu2TJJGtOjyISASf3UFpFJDil1lMbfqnxilfbmIsilvC8DzgjuLoYD+eOitcug4wtU9Fh1tt2vgBhskA==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.0.tgz", + "integrity": "sha512-2xlODKWwf/vNAxCmou0GFhymx2pqZKkhXMN9B5aiTjZ6+81sOxGim53ELY2lj+qKG2IvgiCYFc4X+ZJA2Ad5vg==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/database": "1.0.9", + "@firebase/database-types": "1.0.6", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.6.tgz", + "integrity": "sha512-sMI7IynSZBsyGbUugc8PKE1jwKbnvaieAz/RxuM57PZQNCi6Rteiviwcw/jqZOX6igqYJwXWZ3UzKOZo2nUDRA==", + "dependencies": { + "@firebase/app-types": "0.9.2", + "@firebase/util": "1.10.1" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.4.tgz", + "integrity": "sha512-K2nq4w+NF8J1waGawY5OHLawP/Aw5CYxyDstVv1NZemGPcM3U+LZ9EPaXr1PatYIrPA7fS4DxZoWcbB0aGJ8Zg==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "@firebase/webchannel-wrapper": "1.0.2", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.39", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.39.tgz", + "integrity": "sha512-CsK8g34jNeHx95LISDRTcArJLonW+zJCqHI1Ez9WNiLAK2X8FeQ4UiD+RwOwxAIR+t2a6xED/5Fe6ZIqx7MuoQ==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/firestore": "4.7.4", + "@firebase/firestore-types": "3.0.2", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.2.tgz", + "integrity": "sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.11.9", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.9.tgz", + "integrity": "sha512-dhO5IUfQRCsrc20YD20nSOX+QCT+cH6N86HlZOLz2XgyEFgzOdBQnUot4EabBJQRkMBI7fZWUrbYfRcnov53ug==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.10", + "@firebase/messaging-interop-types": "0.2.2", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.15.tgz", + "integrity": "sha512-eiHpc6Sd9Y/SNhBsGi944SapiFbfTPKsiSUQ74QxNSs0yoxvABeIRolVMFk4TokP57NGmstGYpYte02XGNPcYw==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/functions": "0.11.9", + "@firebase/functions-types": "0.6.2", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.2.tgz", + "integrity": "sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w==" + }, + "node_modules/@firebase/installations": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.10.tgz", + "integrity": "sha512-TuGSOMqkFrllxa0X/8VZIqBCRH4POndU/iWKWkRmkh12+/xKSpdp+y/kWaVbsySrelltan6LeYlcYPmLibWbwg==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/util": "1.10.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.10.tgz", + "integrity": "sha512-YTonkcVz3AK7RF8xFhvs5CwDuJ0xbzzCJIwXoV14gnzdYbMgy6vWlUUbzkvbtEDXzPRHB0n7aGZl56oy9dLOFw==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/installations": "0.6.10", + "@firebase/installations-types": "0.5.2", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.2.tgz", + "integrity": "sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.3.tgz", + "integrity": "sha512-Th42bWJg18EF5bJwhRosn2M/eYxmbWCwXZr4hHX7ltO0SE3QLrpgiMKeRBR/NW7vJke7i0n3i8esbCW2s93qBw==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.13", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.13.tgz", + "integrity": "sha512-YLa8PWl+BgiOVR5WOyzl21fVJFJeBRfniNuN25d9DBrQzppSAahuN6yS+vt1OIjvZNPN4pZ/lcRLYupbGu4W0w==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/installations": "0.6.10", + "@firebase/messaging-interop-types": "0.2.2", + "@firebase/util": "1.10.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.13.tgz", + "integrity": "sha512-9ootPClS6m2c2KIzo7AqSHaWzAw28zWcjQPjVv7WeQDu6wjufpbOg+7tuVzb+gqpF9Issa3lDoYOwlO0ZudO3g==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/messaging": "0.12.13", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz", + "integrity": "sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA==" + }, + "node_modules/@firebase/performance": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.10.tgz", + "integrity": "sha512-x/mNYKGxq7A+QV0EiEZeD2S+E+kw+UcZ8FXuE7qDJyGGt/0Wd+bIIL7RakG/VrFt7/UYc//nKygDc7/Ig7sOmQ==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/installations": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.10.tgz", + "integrity": "sha512-0h1qYkF6I79DSSpHfTQFvb91fo8shmmwiPzWFYAPdPK02bSWpKwVssNYlZX2iUnumxerDMbl7dWN+Im/W3bnXA==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/performance": "0.6.10", + "@firebase/performance-types": "0.2.2", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.2.tgz", + "integrity": "sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.10.tgz", + "integrity": "sha512-jTRjy3TdqzVna19m5a1HEHE5BG4Z3BQTxBgvQRTmMKlHacx4QS0CToAas7R9M9UkxpgFcVuAE7FpWIOWQGCEWw==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/installations": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.10.tgz", + "integrity": "sha512-fIi5OB2zk0zpChMV/tTd0oEZcZI8TlwQDlLlcrDpMOV5l5dqd0JNlWKh6Fwmh4izmytk+rZIAIpnak/NjGVesQ==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/remote-config": "0.4.10", + "@firebase/remote-config-types": "0.3.2", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz", + "integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA==" + }, + "node_modules/@firebase/storage": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.3.tgz", + "integrity": "sha512-B5HiJ7isYKaT4dOEV43f2ySdhQxzq+SQEm7lqXebJ8AYCsebdHrgGzrPR0LR962xGjPzJHFKx63gA8Be/P2MCw==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.13.tgz", + "integrity": "sha512-15kje7JALswRCBKsCSvKg5FbqUYykaIMqMbZRD7I6uVRWwdyTvez5MBQfMhBia2JcEmPiDpXhJTXH4PAWFiA8g==", + "dependencies": { + "@firebase/component": "0.6.10", + "@firebase/storage": "0.13.3", + "@firebase/storage-types": "0.8.2", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz", + "integrity": "sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.1.tgz", + "integrity": "sha512-AIhFnCCjM8FmCqSNlNPTuOk3+gpHC1RkeNUBLtPbcqGYpN5MxI5q7Yby+rxycweOZOCboDzfIj8WyaY4tpQG/g==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/vertexai": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@firebase/vertexai/-/vertexai-1.0.0.tgz", + "integrity": "sha512-48N3Lp/9GgiCCRfrSdHS+Y1IiMdYXvnHFO/f+HL1PgUtBq7WQ/fWmYOX3mzAN36zvytq13nb68ImF+GALopp+Q==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/component": "0.6.10", + "@firebase/logger": "0.4.3", + "@firebase/util": "1.10.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.2.tgz", + "integrity": "sha512-3F4iA2E+NtdMbOU0XC1cHE8q6MqpGIKRj62oGOF38S6AAx5VHR9cXmoDUSj7ejvTAT7m6jxuEeQkHeq0F+mU2w==" + }, "node_modules/@floating-ui/core": { "version": "1.6.8", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", @@ -937,73 +1503,86 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==" }, - "node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz", - "integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz", - "integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==", - "license": "MIT", - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-regular-svg-icons": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.6.0.tgz", - "integrity": "sha512-Yv9hDzL4aI73BEwSEh20clrY8q/uLxawaQ98lekBx6t9dQKDHcDzzV1p2YtBGTtolYtNqcWdniOnhzB+JPnQEQ==", - "license": "(CC-BY-4.0 AND MIT)", - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.6.0.tgz", - "integrity": "sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==", - "license": "(CC-BY-4.0 AND MIT)", - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/react-fontawesome": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz", - "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==", - "license": "MIT", - "dependencies": { - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "@fortawesome/fontawesome-svg-core": "~1 || ~6", - "react": ">=16.3" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", - "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", - "dev": true, - "engines": { - "node": ">=18.18.0" - } - }, +"node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz", + "integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==", + "license": "MIT", + "engines": { "node": ">=6" } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz", + "integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==", + "license": "MIT", + "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" }, + "engines": { "node": ">=6" } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.6.0.tgz", + "integrity": "sha512-Yv9hDzL4aI73BEwSEh20clrY8q/uLxawaQ98lekBx6t9dQKDHcDzzV1p2YtBGTtolYtNqcWdniOnhzB+JPnQEQ==", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" }, + "engines": { "node": ">=6" } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.6.0.tgz", + "integrity": "sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" }, + "engines": { "node": ">=6" } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", + "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { "node": "^8.13.0 || >=10.10.0" } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { "node": ">=6" } + } + }, +"node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz", + "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } +}, +"node_modules/@humanfs/core": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", + "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } +} +, "node_modules/@humanfs/node": { "version": "0.16.5", "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", @@ -1286,6 +1865,60 @@ "node": ">=14" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, "node_modules/@react-leaflet/core": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz", @@ -1756,7 +2389,6 @@ "version": "22.7.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", - "dev": true, "dependencies": { "undici-types": "~6.19.2" } @@ -2417,6 +3049,102 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cliui/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==" + }, + "node_modules/cliui/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==" + }, + "node_modules/cliui/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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==", + "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/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -2534,6 +3262,11 @@ "node": ">=4" } }, + "node_modules/css-mediaquery": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", + "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==" + }, "node_modules/css-to-react-native": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", @@ -2748,6 +3481,7 @@ "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -3001,7 +3735,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "engines": { "node": ">=6" } @@ -3376,6 +4109,17 @@ "reusify": "^1.0.4" } }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/fdir": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.0.tgz", @@ -3451,6 +4195,41 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/firebase": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.0.1.tgz", + "integrity": "sha512-qsFb8dMcQINEDhJteG7RP+GqwgSRvfyiexQqHd5JToDdm87i9I2rGC4XQsGawKGxzKwZ/ISdgwNWxXAFYdCC6A==", + "dependencies": { + "@firebase/analytics": "0.10.9", + "@firebase/analytics-compat": "0.2.15", + "@firebase/app": "0.10.15", + "@firebase/app-check": "0.8.9", + "@firebase/app-check-compat": "0.3.16", + "@firebase/app-compat": "0.2.45", + "@firebase/app-types": "0.9.2", + "@firebase/auth": "1.8.0", + "@firebase/auth-compat": "0.5.15", + "@firebase/data-connect": "0.1.1", + "@firebase/database": "1.0.9", + "@firebase/database-compat": "2.0.0", + "@firebase/firestore": "4.7.4", + "@firebase/firestore-compat": "0.3.39", + "@firebase/functions": "0.11.9", + "@firebase/functions-compat": "0.3.15", + "@firebase/installations": "0.6.10", + "@firebase/installations-compat": "0.2.10", + "@firebase/messaging": "0.12.13", + "@firebase/messaging-compat": "0.2.13", + "@firebase/performance": "0.6.10", + "@firebase/performance-compat": "0.2.10", + "@firebase/remote-config": "0.4.10", + "@firebase/remote-config-compat": "0.2.10", + "@firebase/storage": "0.13.3", + "@firebase/storage-compat": "0.3.13", + "@firebase/util": "1.10.1", + "@firebase/vertexai": "1.0.0" + } + }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", @@ -3667,6 +4446,14 @@ "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==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -3913,6 +4700,11 @@ "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, "node_modules/https-proxy-agent": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-6.2.1.tgz", @@ -3933,6 +4725,16 @@ "node": ">=14.18.0" } }, + "node_modules/hyphenate-style-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", + "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==" + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -4161,7 +4963,6 @@ "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, "engines": { "node": ">=8" } @@ -4627,6 +5428,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4659,6 +5465,11 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4679,6 +5490,14 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.453.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.453.0.tgz", + "integrity": "sha512-kL+RGZCcJi9BvJtzg2kshO192Ddy9hv3ij+cPrVPWSRzgCWCVazoQJxOjAwgK53NomL07HB7GPHW120FimjNhQ==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, "node_modules/magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", @@ -4688,6 +5507,14 @@ "sourcemap-codec": "^1.4.8" } }, + "node_modules/matchmediaquery": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.4.2.tgz", + "integrity": "sha512-wrZpoT50ehYOudhDjt/YvUJc6eUzcdFPdmbizfgvswCKNHD1/OBOHYJpHie+HXpu6bSkEGieFMYk6VuutaiRfA==", + "dependencies": { + "css-mediaquery": "^0.1.2" + } + }, "node_modules/material-ripple-effects": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/material-ripple-effects/-/material-ripple-effects-2.0.1.tgz", @@ -5466,6 +6293,29 @@ "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -5599,6 +6449,23 @@ "node": ">=0.10.0" } }, + "node_modules/react-responsive": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-10.0.0.tgz", + "integrity": "sha512-N6/UiRLGQyGUqrarhBZmrSmHi2FXSD++N5VbSKsBBvWfG0ZV7asvUBluSv5lSzdMyEVjzZ6Y8DL4OHABiztDOg==", + "dependencies": { + "hyphenate-style-name": "^1.0.0", + "matchmediaquery": "^0.4.2", + "prop-types": "^15.6.1", + "shallow-equal": "^3.1.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/react-router": { "version": "6.27.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz", @@ -5714,6 +6581,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "2.0.0-next.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", @@ -6057,6 +6932,11 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/shallow-equal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-3.1.0.tgz", + "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==" + }, "node_modules/shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", @@ -6150,6 +7030,10 @@ "deprecated": "Please use @jridgewell/sourcemap-codec instead", "dev": true }, + "node_modules/station-saarthi": { + "resolved": "", + "link": true + }, "node_modules/stdin-discarder": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", @@ -6778,8 +7662,7 @@ "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/universalify": { "version": "2.0.1", @@ -6908,6 +7791,27 @@ "node": ">= 8" } }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7159,6 +8063,14 @@ "node": ">=0.4.0" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -7177,6 +8089,68 @@ "node": ">= 14" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/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==" + }, + "node_modules/yargs/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 40c0cc2..6f5b953 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,18 +19,22 @@ "@react-oauth/google": "^0.12.1", "axios": "^1.7.7", "dotenv": "^16.4.5", + "firebase": "^11.0.1", "framer-motion": "^11.11.8", "google-translate-api-x": "^10.7.1", "jwt-decode": "^4.0.0", "leaflet": "^1.9.4", + "lucide-react": "^0.453.0", "react": "^18.3.1", "react-datepicker": "^7.4.0", "react-dom": "^18.3.1", "react-icons": "^5.3.0", "react-leaflet": "^4.2.1", + "react-responsive": "^10.0.0", "react-router-dom": "^6.26.2", "shadcn-ui": "^0.2.3", "socket.io-client": "^4.8.0", + "station-saarthi": "file:", "styled-components": "^6.1.13", "yup": "^1.4.0" }, diff --git a/frontend/src/App.css b/frontend/src/App.css index 8358b80..92558d7 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -10,7 +10,7 @@ body{ ::-webkit-scrollbar { - width: 12px; + width: 5px; height: 12px; } @@ -20,9 +20,9 @@ body{ } ::-webkit-scrollbar-thumb:hover { - background: #0056b3; + background: #3591f3; } ::-webkit-scrollbar-thumb { - background: #007BFF; - border-radius: 10px; + background: #3591f3; + border-radius: 12px; } diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 5c7babd..edccaa1 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -17,7 +17,6 @@ import Chatbot from "./components/chatbot"; import ContactUs from "./Pages/ContactUs"; import Settings from "./components/Settings"; import Help from "./components/help"; -import About from "./components/about"; import Contributor from "./Pages/contributor"; import Payment from "./Pages/Payment"; import HelpAndSupport from "./Pages/HelpandSupport"; @@ -47,7 +46,7 @@ function App() { } /> } /> } /> - } /> + } /> } /> } /> } /> diff --git a/frontend/src/Pages/AboutUs.jsx b/frontend/src/Pages/AboutUs.jsx index 15e2ae8..521f558 100644 --- a/frontend/src/Pages/AboutUs.jsx +++ b/frontend/src/Pages/AboutUs.jsx @@ -1,9 +1,9 @@ import React from 'react'; // Importing React import { useNavigate } from 'react-router-dom'; // Importing navigation function import backicon from '../assets/svg/backicon.svg'; // Importing back icon asset - +import TeamSection from '../components/TeamSection'; // About component -const About = () => { +const AboutUs = () => { // UseNavigate hook for navigation const navigate = useNavigate(); @@ -12,15 +12,8 @@ const About = () => { // Navigates to the home page when back button is clicked navigate('/'); }; - - - // Return JSX structure return ( -
- - {/* Back button to navigate to home */} +
- - {/* Main Heading */} -

- ABOUT US -

- - {/* Introduction Section */} -

- Welcome to - - StationSaarthi - , - your one-stop platform designed to elevate your Indian Railway Station experience. - With cutting-edge technology and user-friendly design, we aim to provide smooth and efficient - travel assistance for all passengers. -

- - {/* Our Mission Section */} -
-

- Our Mission -

-

- At - - StationSaarthi - , - our mission is to simplify the railway station experience by integrating advanced technology - and providing real-time, actionable information to every traveler. - We are committed to enhancing travel convenience for all. + {/* Hero Section */} +

+

Welcome to Station Saarthi

+

+ Elevate Your Railway Station Experience +

+

+ StationSaarthi is your one-stop platform designed to elevate your Indian Railway Station + experience. With cutting-edge technology and user-friendly design, we aim to provide + smooth and efficient travel assistance for all passengers.

-
+ - {/* Our Vision Section */} -
-

- Our Vision -

-

- Our vision is a future where every journey is hassle-free. - We strive to create a travel ecosystem that connects passengers, railways, and services - through seamless digital integration. -

-
+ {/* Our Mission & Vision Section */} +
+
+

Our Mission

+

+ At StationSaarthi, our mission is to simplify the railway station experience by + integrating advanced technology and providing real-time, actionable information to every + traveler. We are committed to enhancing travel convenience for all. +

+
+
+

Our Vision

+

+ Our vision is a future where every journey is hassle-free. We strive to create a travel + ecosystem that connects passengers, railways, and services through seamless digital + integration. +

+
+
{/* What We Offer Section */} -
-

- What We Offer -

-

- With - - StationSaarthi - , - you get access to: -

    -
  • Real-time train updates and notifications
  • -
  • Interactive station maps for easy navigation
  • -
  • Personalized travel recommendations
  • -
  • Multi-language support to cater to diverse passengers
  • -
  • Accessible services for differently-abled travelers
  • -
-

+
+

What We Offer

+
+
+
+ +
+

Real-Time Train Updates

+

+ Stay informed with live train schedules and notifications for timely travel assistance. +

+
+
+
+ +
+

Interactive Station Maps

+

+ Navigate the station effortlessly with detailed maps, making it easier to find your way. +

+
+
+
+ +
+

Accessible Services

+

+ Our platform ensures accessible services, providing support for differently-abled travelers. +

+
+
{/* Why Choose Us Section */} -
-

- Why Choose Us? -

-

- - StationSaarthi - - is more than a service; it's a commitment to revolutionizing your railway station experience. - We believe in technology's power to enhance every aspect of your journey, ensuring safety, - comfort, and convenience at every step. -

-
+
+

Why Choose StationSaarthi?

+

+ With StationSaarthi, you’re not just navigating a station β€” you're embracing a revolutionary approach to travel. Our platform is designed to streamline your experience, providing you with essential resources, convenience, and tailored support. +

+
+
+

Smart Journey Planning

+

Plan your trips efficiently with our smart journey planning tools and suggestions.

+
+
+

User-Friendly Interface

+

Enjoy a seamless experience with our intuitive and easy-to-use platform.

+
+
+

Community Support

+

Join a community of travelers to share experiences, tips, and support for a better journey.

+
+
+
- {/* Our Team Section */} -
-

- Meet the Team -

-

- Our diverse team of professionals is driven by a passion for innovation. - With expertise spanning technology, transportation, and user experience design, - we are dedicated to making railway stations smarter, more efficient, and more - enjoyable for every passenger. -

-
- {/* Footer */} -
+ {/* Meet the Team Section */} +
+ + + +
+ + {/* Footer Section */} +

Β© 2024 StationSaarthi | All rights reserved

-
+
); -}; +} -// Exporting the About component for use in other parts of the app -export default About; +export default AboutUs; diff --git a/frontend/src/Pages/ContactUs.jsx b/frontend/src/Pages/ContactUs.jsx index a7511e8..6477ea1 100644 --- a/frontend/src/Pages/ContactUs.jsx +++ b/frontend/src/Pages/ContactUs.jsx @@ -1,353 +1,513 @@ // src/components/ContactUs.jsx -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from "react"; import { - FaPhone, - FaEnvelope, - FaMapMarkerAlt, - FaTwitter, - FaFacebook, -} from 'react-icons/fa'; -import { AiOutlineArrowUp,AiOutlineArrowDown } from 'react-icons/ai'; + FaPhone, + FaEnvelope, + FaMapMarkerAlt, + FaTwitter, + FaFacebook, +} from "react-icons/fa"; +import { AiOutlineArrowUp, AiOutlineArrowDown } from "react-icons/ai"; const ContactUs = () => { - // Function to scroll to the top of the page - const scrollToTop = () => { - window.scrollTo({ - top: 0, - behavior: 'smooth', - }); - }; - const scrollToBottom = () => { - window.scrollTo({ - top: 10000, - behavior: 'smooth', - }); - }; - useEffect(() => { - document.title = 'Station Saarthi |ContactUs'; - }, []); + // Function to scroll to the top of the page + const scrollToTop = () => { + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + }; + const scrollToBottom = () => { + window.scrollTo({ + top: 10000, + behavior: "smooth", + }); + }; + useEffect(() => { + document.title = "Station Saarthi |ContactUs"; + }, []); - return ( -
- {/* Header Section */} -
-

Contact Us

-

Indian Railways

-
+ const [formData, setFormData] = useState({ + senderEmail: "", + subject: "", + message: "", + }); - {/* General Information */} -
-
- -

General Information

-
-
-
    -
  • - - - Phone Number: 139 (Railway Enquiry) - -
  • -
  • - - - Email: customercare@indianrailways.gov.in - -
  • -
-
-
+ const [isLoading, setIsLoading] = useState(false); + const [alert, setAlert] = useState(false); + const API_URL = "http://localhost:3000"; - {/* Emergency Services */} -
-
- -

Emergency Services

-
-
-
    -
  • - - - Phone Number: 182 (Security & Emergencies) - -
  • -
  • - - - Phone Number: 138 (Passenger Helpline) - -
  • -
  • - - - Email: emergencyservices@indianrailways.gov.in - -
  • -
-
-
+ const handleChange = (e) => { + const { name, value } = e.target; + setFormData((prevData) => ({ + ...prevData, + [name]: value, + })); + }; - {/* Reservation and Ticketing */} -
-
- -

Reservation and Ticketing

-
-
-
    -
  • - - - Phone Number: 139 (IRCTC Helpline) - -
  • -
  • - - - Email: care@irctc.co.in - -
  • -
  • - Website:{' '} - - IRCTC Website - -
  • -
-
-
+ const handleSubmit = async (e) => { + e.preventDefault(); + setIsLoading(true); + + try { + const response = await fetch(`${API_URL}/contact/contactus`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + mail: formData.senderEmail, + subject: formData.subject, + message: formData.message, + }), + }); + + if (response.ok) { + setTimeout(() => { + setIsLoading(false); + setAlert(true); + setFormData({ + senderEmail: "", + subject: "", + message: "", + }); + }, 2000); + + setTimeout(() => { + setAlert(false); + }, 4000); + } else { + alert("Something went wrong, please try later!!"); + setIsLoading(false); + } + } catch (err) { + console.error("Mail sending failed: ", err); + alert("Something went wrong, please try later!!"); + setIsLoading(false); + } + }; - {/* Lost & Found */} -
-
- -

Lost & Found

-
-
-
    -
  • - - Phone Number: 139 -
  • -
  • - - - Email: lostandfound@indianrailways.gov.in - -
  • -
-
-
+ return ( +
+ {/* Header Section */} +
+

Contact Us

+

Indian Railways

+
- {/* Grievances and Complaints */} -
-
- -

Grievances and Complaints

-
-
-
    -
  • - - - Phone Number: 139 (Complaint Registration) - -
  • -
  • - - - Email: complaints@indianrailways.gov.in - -
  • -
  • - Online Portal:{' '} - - RailMadad - -
  • -
-
-
+ {/* E-Mail Section */} +
+

+ Send Email +

+
+
+ + +
+
+ + +
+
+ + +
+ - {/* Divisional Railway Manager (DRM) Offices */} -
-
- -

Divisional Railway Manager (DRM) Offices

-
-
-
    -
  • - - - Northern Railway DRM Office: drm.nr@indianrailways.gov.in - -
  • -
  • - - - Western Railway DRM Office: drm.wr@indianrailways.gov.in - -
  • -
  • - - - Southern Railway DRM Office: drm.sr@indianrailways.gov.in - -
  • -
  • - - - Eastern Railway DRM Office: drm.er@indianrailways.gov.in - -
  • -
-
-
+
+ {alert &&

Thank You, We will contact you soon..

} +
+
+
- {/* Railway Police Force (RPF) */} -
-
- -

Railway Police Force (RPF)

-
-
-
    -
  • - - Phone Number: 182 -
  • -
  • - - - Email: rpf@indianrailways.gov.in - -
  • -
-
-
+ {/* General Information */} +
+
+ +

+ General Information +

+
+
+
    +
  • + + + Phone Number: 139 (Railway Enquiry) + +
  • +
  • + + + Email: customercare@indianrailways.gov.in + +
  • +
+
+
- {/* Corporate Office */} -
-
- -

Corporate Office

-
-
-
    -
  • - - - Address: Indian Railways Corporate Office, New Delhi, India - -
  • -
  • - - - Phone Number: +91-11-23389184 - -
  • -
  • - - - Email: corporateaffairs@indianrailways.gov.in - -
  • -
-
-
+ {/* Emergency Services */} +
+
+ +

+ Emergency Services +

+
+
+
    +
  • + + + Phone Number: 182 (Security & Emergencies) + +
  • +
  • + + + Phone Number: 138 (Passenger Helpline) + +
  • +
  • + + + Email: emergencyservices@indianrailways.gov.in + +
  • +
+
+
- {/* Stay Connected */} -
-
- -

Stay Connected

-
-
- -
-
+ {/* Reservation and Ticketing */} +
+
+ +

+ Reservation and Ticketing +

+
+
+
    +
  • + + + Phone Number: 139 (IRCTC Helpline) + +
  • +
  • + + + Email: care@irctc.co.in + +
  • +
  • + Website:{" "} + + IRCTC Website + +
  • +
+
+
- {/* Feedback */} -
-
- -

Feedback

-
-
- -
-
+ {/* Lost & Found */} +
+
+ +

Lost & Found

+
+
+
    +
  • + + + Phone Number: 139 + +
  • +
  • + + + Email: lostandfound@indianrailways.gov.in + +
  • +
+
+
- {/* Back to Top Button */} - - + {/* Grievances and Complaints */} +
+
+ +

+ Grievances and Complaints +

+
+
+
    +
  • + + + Phone Number: 139 (Complaint Registration) + +
  • +
  • + + + Email: complaints@indianrailways.gov.in + +
  • +
  • + Online Portal:{" "} + + RailMadad + +
  • +
+
+
+ + {/* Divisional Railway Manager (DRM) Offices */} +
+
+ +

+ Divisional Railway Manager (DRM) Offices +

+
+
+
    +
  • + + + Northern Railway DRM Office:{" "} + drm.nr@indianrailways.gov.in + +
  • +
  • + + + Western Railway DRM Office:{" "} + drm.wr@indianrailways.gov.in + +
  • +
  • + + + Southern Railway DRM Office:{" "} + drm.sr@indianrailways.gov.in + +
  • +
  • + + + Eastern Railway DRM Office:{" "} + drm.er@indianrailways.gov.in + +
  • +
+
+
+ + {/* Railway Police Force (RPF) */} +
+
+ +

+ Railway Police Force (RPF) +

+
+
+
    +
  • + + + Phone Number: 182 + +
  • +
  • + + + Email: rpf@indianrailways.gov.in + +
  • +
+
+
+ + {/* Corporate Office */} +
+
+ +

+ Corporate Office +

+
+
+
    +
  • + + + Address: Indian Railways Corporate Office, New Delhi, India + +
  • +
  • + + + Phone Number: +91-11-23389184 + +
  • +
  • + + + Email: corporateaffairs@indianrailways.gov.in + +
  • +
- ); +
+ + {/* Stay Connected */} +
+
+ +

+ Stay Connected +

+
+
+ +
+
+ + {/* Feedback */} +
+
+ +

Feedback

+
+
+ +
+
+ + {/* Back to Top Button */} + + +
+ ); }; export default ContactUs; diff --git a/frontend/src/Pages/Firebase.jsx b/frontend/src/Pages/Firebase.jsx new file mode 100644 index 0000000..fd25afc --- /dev/null +++ b/frontend/src/Pages/Firebase.jsx @@ -0,0 +1,24 @@ +import { initializeApp } from "firebase/app"; +import { getAuth, GoogleAuthProvider, signInWithPopup } from "firebase/auth"; // Import GoogleAuthProvider and signInWithPopup + +// Firebase configuration +const firebaseConfig = { + apiKey: "AIzaSyCZjGQb8lWxKdPf94ipmk-xleQbuSAe-xU", + authDomain: "scrollme-40ba6.firebaseapp.com", + projectId: "scrollme-40ba6", + storageBucket: "scrollme-40ba6.appspot.com", + messagingSenderId: "748771881750", + appId: "1:748771881750:web:2cb97bb2c7b7468a7c1637" +}; + +// Initialize Firebase +const app = initializeApp(firebaseConfig); + +// Initialize Firebase Authentication and get a reference to the auth service +const auth = getAuth(app); + +// Set up Google Auth Provider +const provider = new GoogleAuthProvider(); + +// Export app, auth, provider, and signInWithPopup +export { app, auth, provider, signInWithPopup }; diff --git a/frontend/src/Pages/Herosection.jsx b/frontend/src/Pages/Herosection.jsx index 1f6cbe8..0ce92df 100644 --- a/frontend/src/Pages/Herosection.jsx +++ b/frontend/src/Pages/Herosection.jsx @@ -8,130 +8,108 @@ import noticationsvg from '../assets/svg/notification.svg'; import mapsvg from '../assets/svg/3dmap.svg'; import schedulesvg from '../assets/svg/schedule.svg'; import contributorsvg from '../assets/svg/contributor.svg'; -import { useNavigate } from 'react-router-dom'; import chatbotsvg from '../assets/svg/chatbot.svg'; +import { useNavigate } from 'react-router-dom'; import Chatbot from '../components/chatbot'; import Navbar from '../components/navbar'; import HamburgerMenu from './HamburgerMenu'; -import Language from '../components/language'; // From main branch -import { FaUserAlt } from "react-icons/fa"; // From main branch -import Popup from '../components/popup'; // From main branch -import Hamburger from './hamburger'; +import Language from '../components/language'; +import { FaUserAlt } from "react-icons/fa"; +import Popup from '../components/popup'; const Herosection = () => { - const navigate = useNavigate(); + const navigate = useNavigate(); + + const LoginClick = () => navigate('/Login'); + const RegisterClick = () => navigate('/Register'); + const StationCLick = () => navigate('/Stations'); + const UserCLick = () => navigate('/user'); + const NavigationCLick = () => navigate('/Navigation'); + const BookingCLick = () => navigate('/Booking'); + const MapCLick = () => navigate('/3DMap'); + const ScheduleCLick = () => navigate('/Schedule'); + const NotificationCLick = () => navigate('/Notification'); + const ContributorCLick = () => navigate('/contributor'); + + return ( + <> +
+ +
+ + +
+
+ + + +

Namaste !! Yatree

+
+
+ Station Saarthi Logo +

Station Saarthi : Your Platform Guide

+
+
+
+ +
+
+ Contributors Icon +

Contributors

+
+ +
+ Navigation Icon + +
- const LoginClick = () => { - navigate('/Login'); // Navigates to the login page - }; - const RegisterClick = () => { - navigate('/Register'); // Navigates to the login page - }; - const StationCLick = () => { - navigate('/Stations'); // Navigates to the Stations page - }; - const UserCLick = () => { - navigate('/user'); // Navigates to the user page - }; - const NavigationCLick = () => { - navigate('/Navigation'); // Navigates to the Navigation page - }; - const BookingCLick = () => { - navigate('/Booking'); // Navigates to the Booking page - }; - const MapCLick = () => { - navigate('/3DMap'); // Navigates to the 3D Map page - }; - const ScheduleCLick = () => { - navigate('/Schedule'); // Navigates to the Schedule page - }; - const NotificationCLick = () => { - navigate('/Notification'); // Navigates to the Notification page - }; - const ContributorCLick = () => { - navigate('/contributor'); // Navigates to the Contributor page - }; +
+ Booking Icon + +
- return ( - <> -
-
{/* From HamburgerMenu branch */} -
- -
-
-
+
+ Station Icon + +
- {/* From main branch */} - -

Namaste !! Yatree

-
-
- -

Station Saarthi : Your Platform Guide

-
-
-
+
+ 3D Map Icon + +
-
-
- -

Contributors

-
+
+ Schedule Icon + +
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
+
+ Notification Icon + +
+
-
- - -

Saarthi

-
- - ); +
+ + +

Saarthi

+
+ + ); }; export default Herosection; diff --git a/frontend/src/Pages/LoginPage.jsx b/frontend/src/Pages/LoginPage.jsx index 06790ca..51c3662 100644 --- a/frontend/src/Pages/LoginPage.jsx +++ b/frontend/src/Pages/LoginPage.jsx @@ -1,32 +1,35 @@ -import React, { useState, useEffect } from "react"; +import { useState, useEffect } from "react"; import logo from "../assets/stationsaarthi.svg"; // Import your logo import { useNavigate } from "react-router-dom"; import backicon from "../assets/svg/backicon.svg"; // Assuming you have a back icon import { loginValidation } from "../validations/validation"; import { MdOutlinePassword } from "react-icons/md"; import { FaUser } from "react-icons/fa"; +import { auth, provider, signInWithPopup } from "./Firebase"; // Import Firebase authentication methods +import { FaGoogle } from "react-icons/fa"; // Import Google icon const Login = () => { useEffect(() => { document.title = "Station Saarthi | LoginPage"; }, []); + const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); - const [loginSuccess, setLoginSuccess] = useState(false); // State for login success message + const [loginSuccess, setLoginSuccess] = useState(false); const [errors, setErrors] = useState({}); - const [passwordVisible, setPasswordVisible] = useState(false); // State for password visibility + const [passwordVisible, setPasswordVisible] = useState(false); const navigate = useNavigate(); const togglePasswordVisibility = () => { - setPasswordVisible(!passwordVisible); // Toggle password visibility + setPasswordVisible(!passwordVisible); }; const RegisterClick = () => { - navigate("/Register"); // Navigates to the login page + navigate("/Register"); }; const HomeClick = () => { - navigate("/"); // Navigates to the home page + navigate("/"); }; const handleLogin = async (e) => { @@ -42,16 +45,14 @@ const Login = () => { const newErrors = {}; error.inner.forEach((err) => { newErrors[err.path] = err.message; - // newErrors[err.path] = err.errors[0]; }); - setErrors(newErrors); return; } // Handle login logic here - setLoginSuccess(true); // Set login success to true upon successful login - setUsername(""); // Clear input fields after login + setLoginSuccess(true); + setUsername(""); setPassword(""); }; @@ -59,22 +60,32 @@ const Login = () => { navigate("/password-recovery"); }; + const handleGoogleLogin = async () => { + try { + const result = await signInWithPopup(auth, provider); + const user = result.user; + console.log("Google User Info:", user); + // Optionally navigate or handle user state here + } catch (error) { + console.error("Google login error:", error); + } + }; + return ( -
- {/* Logo and Title */} - -
+
Station Saarthi Logo -

+

Station Saarthi

-

+

Your Trusted Platform Guide

@@ -83,100 +94,108 @@ const Login = () => {
- {/* Login Heading */} -

+

Login

- {/* Success Message */} {loginSuccess && ( -

+

Login successful! Welcome back.

)} {/* Username Input */} -
+
setUsername(e.target.value)} - placeholder='Enter your username' - className='w-full px-4 py-2 transition duration-300 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500' + placeholder="Enter your username" + className="w-full px-4 py-2 transition duration-300 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" required /> {errors.username && ( -
{errors.username}
+
{errors.username}
)}
{/* Password Input */} -
+
setPassword(e.target.value)} - placeholder='Enter your password' - className='w-full px-4 py-2 transition duration-300 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500' + placeholder="Enter your password" + className="w-full px-4 py-2 transition duration-300 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" required - />{" "} + /> {errors.password && ( -
{errors.password}
+
{errors.password}
)}
{/* Login Button */} + + {/* Google Login Button */} + {/* Forgot Password Link */} -

+

{/* Don't have an account link */} -

+

Don't have an account?{" "} diff --git a/frontend/src/Pages/PasswordRecovery.jsx b/frontend/src/Pages/PasswordRecovery.jsx index 8a7e1fc..beb1dae 100644 --- a/frontend/src/Pages/PasswordRecovery.jsx +++ b/frontend/src/Pages/PasswordRecovery.jsx @@ -3,15 +3,114 @@ import { useNavigate } from "react-router-dom"; const PasswordRecovery = () => { const [email, setEmail] = useState(""); + const [otp, setOtp] = useState(""); // For OTP input + const [newPassword, setNewPassword] = useState(""); // For new password input + const [confirmPassword, setConfirmPassword] = useState(""); // For confirm password input + const [step, setStep] = useState(1); // For handling the steps (1: Email, 2: OTP, 3: Password Reset) + const [otpDisabled, setOtpDisabled] = useState(false); // For disabling OTP input after submission const [message, setMessage] = useState(""); + const [error, setError] = useState(""); + const [loading, setLoading] = useState(false); const navigate = useNavigate(); + // Step 1: Handle sending OTP to email const handlePasswordRecovery = async (e) => { e.preventDefault(); - // Implement password recovery logic here - setMessage( - "If an account with that email exists, a password recovery link has been sent." - ); + setMessage(""); + setError(""); + setLoading(true); + + try { + const response = await fetch("http://localhost:5000/station/send-otp", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email }), + }); + + const data = await response.json(); + + if (response.ok) { + setMessage("OTP has been sent to your email."); + setStep(2); // Move to OTP input step + } else { + setError(data.error || "Failed to send OTP. Please try again."); + } + } catch (error) { + setError("An error occurred. Please try again later."); + } finally { + setLoading(false); + } + }; + + // Step 2: Handle OTP verification + const handleVerifyOTP = async (e) => { + e.preventDefault(); + setMessage(""); + setError(""); + setLoading(true); + + try { + const response = await fetch("http://localhost:5000/station/verify-otp", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email, otp }), + }); + + const data = await response.json(); + + if (response.ok) { + setMessage("OTP verified successfully. You can now reset your password."); + setStep(3); // Move to password reset step + setOtpDisabled(true); // Disable OTP input after successful verification + } else { + setError(data.error || "Invalid OTP. Please try again."); + } + } catch (error) { + setError("An error occurred. Please try again later."); + } finally { + setLoading(false); + } + }; + + // Step 3: Handle new password submission + const handlePasswordReset = async (e) => { + e.preventDefault(); + setMessage(""); + setError(""); + setLoading(true); + + if (newPassword !== confirmPassword) { + setError("Passwords do not match."); + setLoading(false); + return; + } + + try { + const response = await fetch("http://localhost:5000/station/reset-password", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email, newPassword }), + }); + + const data = await response.json(); + + if (response.ok) { + setMessage("Password reset successfully. You can now log in."); + navigate("/login"); + } else { + setError(data.error || "Failed to reset password. Please try again."); + } + } catch (error) { + setError("An error occurred. Please try again later."); + } finally { + setLoading(false); + } }; const handleBackToLogin = () => { @@ -19,44 +118,123 @@ const PasswordRecovery = () => { }; return ( -

-
-

+
+
+

Password Recovery

+ + {/* Success Message */} {message && ( -

+

{message}

)} -
-
-