diff --git a/jetset/src/components/Admin/ChangePasswordForm.js b/jetset/src/components/Admin/ChangePasswordForm.js new file mode 100644 index 0000000..6c504b5 --- /dev/null +++ b/jetset/src/components/Admin/ChangePasswordForm.js @@ -0,0 +1,67 @@ +import React, { useState } from "react"; +import axios from "axios"; + +const ChangePasswordForm = ({ touristId }) => { + const [oldPassword, setOldPassword] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [message, setMessage] = useState(""); + + const handleChangePassword = async (e) => { + e.preventDefault(); + + if (newPassword !== confirmPassword) { + setMessage("New passwords do not match"); + return; + } + + try { + const response = await axios.put(`http://localhost:3000/cpAdmin/66fabf44cd7601258892778b`, { + oldPassword, + newPassword, + }); + setMessage(response.data.message); + } catch (error) { + setMessage(error.response?.data.message || "Error updating password"); + } + }; + + return ( +
+

Change Password

+
+ + + + +
+ {message &&

{message}

} +
+ ); +}; + +export default ChangePasswordForm; diff --git a/jetset/src/components/AdminFrontend.js b/jetset/src/components/AdminFrontend.js index c4e61d9..fda18fc 100644 --- a/jetset/src/components/AdminFrontend.js +++ b/jetset/src/components/AdminFrontend.js @@ -25,6 +25,7 @@ import SortProducts from "./Products/SortProducts"; import DeleteAccount from "./Admin/DeleteAccount"; import CreateAdmin from "./Admin/CreateAdmin"; import CreateTourismGovernor from "./Admin/CreateTourismGovernor"; +import ChangePasswordForm from "./Admin/ChangePasswordForm"; function AdminFrontend() { // State for managing tags @@ -41,6 +42,8 @@ function AdminFrontend() { const [products, setProducts] = useState([]); const [productLoading, setProductLoading] = useState(true); const [productError, setProductError] = useState(null); + // State to manage ChangePasswordForm visibility + const [showChangePasswordForm, setShowChangePasswordForm] = useState(false); // Fetch tags from the backend const fetchTags = async () => { @@ -113,6 +116,17 @@ function AdminFrontend() { ) : (

No tags available to update, delete, or display.

)} + + + {/* Button to toggle ChangePasswordForm */} + + + {/* Conditionally render ChangePasswordForm */} + {showChangePasswordForm && } + + {/* Category Management Section */}

Category Management

{categoryLoading &&

Loading categories...

} diff --git a/jetset/src/components/Advertiser/ChangePasswordForm.js b/jetset/src/components/Advertiser/ChangePasswordForm.js new file mode 100644 index 0000000..36cf69a --- /dev/null +++ b/jetset/src/components/Advertiser/ChangePasswordForm.js @@ -0,0 +1,67 @@ +import React, { useState } from "react"; +import axios from "axios"; + +const ChangePasswordForm = ({ touristId }) => { + const [oldPassword, setOldPassword] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [message, setMessage] = useState(""); + + const handleChangePassword = async (e) => { + e.preventDefault(); + + if (newPassword !== confirmPassword) { + setMessage("New passwords do not match"); + return; + } + + try { + const response = await axios.put(`http://localhost:3000/cpAdvertiser/6701a2e8077eb6e57b56801f`, { + oldPassword, + newPassword, + }); + setMessage(response.data.message); + } catch (error) { + setMessage(error.response?.data.message || "Error updating password"); + } + }; + + return ( +
+

Change Password

+
+ + + + +
+ {message &&

{message}

} +
+ ); +}; + +export default ChangePasswordForm; diff --git a/jetset/src/components/Advertiser/DeleteAccount.js b/jetset/src/components/Advertiser/DeleteAccount.js new file mode 100644 index 0000000..b43ab89 --- /dev/null +++ b/jetset/src/components/Advertiser/DeleteAccount.js @@ -0,0 +1,23 @@ +// src/DeleteAccount.js +import React from 'react'; +import axios from 'axios'; + +const DeleteAccount = ({ advertiserId }) => { + + const handleDeleteAccount = async () => { + try { + const response = await axios.delete(`http://localhost:3000/deleteAccAdvertiser/6707bb596d08e5f1f78e31f1`); + alert(response.data.message); // Show success message + } catch (err) { + alert(err.response?.data?.message || "An error occurred"); // Show error message + } + }; + + return ( +
+ +
+ ); +}; + +export default DeleteAccount; \ No newline at end of file diff --git a/jetset/src/components/Advertiser/ProfileAdvertiser.js b/jetset/src/components/Advertiser/ProfileAdvertiser.js index 0b690a6..54eeddc 100644 --- a/jetset/src/components/Advertiser/ProfileAdvertiser.js +++ b/jetset/src/components/Advertiser/ProfileAdvertiser.js @@ -1,6 +1,8 @@ import React, { useState, useEffect } from "react"; import axios from "axios"; import ActivityForm from "../Activity/ActivityProfileAdv"; // Import ActivityForm +import ChangePasswordForm from "./ChangePasswordForm"; +import DeleteAccount from "./DeleteAccount"; const ProfileForm = ({ onProfileCreated }) => { const BASE_URL = process.env.REACT_APP_BASE_URL || "http://localhost:3000"; @@ -25,6 +27,7 @@ const ProfileForm = ({ onProfileCreated }) => { const [profiles, setProfiles] = useState([]); // State to store profiles const [isFetchingProfiles, setIsFetchingProfiles] = useState(false); // State to indicate fetching const [selectedAdvertiserId, setSelectedAdvertiserId] = useState(null); // For selected advertiser ID + const [showChangePassword, setShowChangePassword] = useState(false); // State to toggle ChangePasswordForm // Fetch profiles when the component mounts useEffect(() => { @@ -234,6 +237,17 @@ const ProfileForm = ({ onProfileCreated }) => { console.log("Activity created:", activity); }} /> + + {/* Button to toggle ChangePasswordForm */} + + + {/* Conditionally render the ChangePasswordForm */} + {showChangePassword && } + + + ); }; diff --git a/jetset/src/components/Place.js b/jetset/src/components/Place.js index 3f414d4..f2305fe 100644 --- a/jetset/src/components/Place.js +++ b/jetset/src/components/Place.js @@ -4,6 +4,7 @@ import PlaceForm from "./Place/PlaceForm"; import TagForm from "./Tag/TagForm"; import ActivitiesList from "./Activity/ActivityListAdv"; import ItineraryList from "./Itinerary/ItinerariesList"; +import ChangePasswordForm from "./Place/ChangePasswordForm"; const PlaceManagement = () => { return ( @@ -15,6 +16,8 @@ const PlaceManagement = () => { +

Change Password

+ ); }; diff --git a/jetset/src/components/Place/ChangePasswordForm.js b/jetset/src/components/Place/ChangePasswordForm.js new file mode 100644 index 0000000..c19402d --- /dev/null +++ b/jetset/src/components/Place/ChangePasswordForm.js @@ -0,0 +1,67 @@ +import React, { useState } from "react"; +import axios from "axios"; + +const ChangePasswordForm = ({ touristId }) => { + const [oldPassword, setOldPassword] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [message, setMessage] = useState(""); + + const handleChangePassword = async (e) => { + e.preventDefault(); + + if (newPassword !== confirmPassword) { + setMessage("New passwords do not match"); + return; + } + + try { + const response = await axios.put(`http://localhost:3000/cpTourismgoverner/67236bbdb4febd354fcca5e2`, { + oldPassword, + newPassword, + }); + setMessage(response.data.message); + } catch (error) { + setMessage(error.response?.data.message || "Error updating password"); + } + }; + + return ( +
+

Change Password

+
+ + + + +
+ {message &&

{message}

} +
+ ); +}; + +export default ChangePasswordForm; diff --git a/jetset/src/components/Register.js b/jetset/src/components/Register.js index b1ccba8..e158c21 100644 --- a/jetset/src/components/Register.js +++ b/jetset/src/components/Register.js @@ -15,7 +15,7 @@ const Register = () => { // Navigate to the appropriate page based on role selection if (selectedRole === "tourist") { - navigate("/touristregister"); // Navigate to Tourist register page + navigate("/tourist"); // Navigate to Tourist register page } else if (selectedRole === "admin") { navigate("/admin"); // Navigate to Admin page } else if (selectedRole === "seller") { diff --git a/jetset/src/components/RegisterAST.js b/jetset/src/components/RegisterAST.js index 1831118..ce24e80 100644 --- a/jetset/src/components/RegisterAST.js +++ b/jetset/src/components/RegisterAST.js @@ -1,3 +1,106 @@ +// import React, { useState } from "react"; +// import { useNavigate } from "react-router-dom"; // Import useNavigate +// import axios from "axios"; +// import "./styles.css"; // Import your CSS file + +// const Register = () => { +// const [username, setUsername] = useState(""); +// const [password, setPassword] = useState(""); +// const [email, setEmail] = useState(""); +// const [role, setRole] = useState(""); +// const [message, setMessage] = useState(null); +// const [showModal, setShowModal] = useState(false); +// const navigate = useNavigate(); // Use useNavigate + +// const handleRegister = async (e) => { +// console.log({ username, password, email, role }); + +// e.preventDefault(); +// try { +// const response = await axios.post("http://localhost:3000/register", { +// username, +// password, +// email, +// role, +// }); + +// setMessage(response.data.msg); + +// if (role === "seller" || role === "advertiser" || role === "tourguide") { +// setShowModal(true); // Show the popup for both seller and advertiser +// } +// } catch (err) { +// setMessage("Error creating account."); +// } +// }; + +// const handleCloseModal = () => { +// setShowModal(false); +// if (role === "seller") { +// navigate("/createseller", { state: { username, password, email } }); // Navigate to seller page +// } else if (role === "advertiser") { +// navigate("/createadvertiser", { state: { username, password, email } }); // Navigate to advertiser page +// } else if (role === "tourguide") { +// navigate("/tourguide", { state: { username, password, email } }); +// } +// }; + +// return ( +//
+//
+//

Register

+// setUsername(e.target.value)} +// required +// /> +// setPassword(e.target.value)} +// required +// /> +// setEmail(e.target.value)} +// required +// /> +// + +// +// {message && ( +//

+// {message} +//

+// )} +//
+ +// {showModal && ( +//
+//

You are accepted as a {role}!

+// +//
+// )} +//
+// ); +// }; + +// export default Register; + import React, { useState } from "react"; import { useNavigate } from "react-router-dom"; // Import useNavigate import axios from "axios"; @@ -10,6 +113,7 @@ const Register = () => { const [role, setRole] = useState(""); const [message, setMessage] = useState(null); const [showModal, setShowModal] = useState(false); + const [termsAccepted, setTermsAccepted] = useState(false); // Track terms acceptance const navigate = useNavigate(); // Use useNavigate const handleRegister = async (e) => { @@ -35,13 +139,15 @@ const Register = () => { }; const handleCloseModal = () => { - setShowModal(false); - if (role === "seller") { - navigate("/createseller", { state: { username, password, email } }); // Navigate to seller page - } else if (role === "advertiser") { - navigate("/createadvertiser", { state: { username, password, email } }); // Navigate to advertiser page - } else if (role === "tourguide") { - navigate("/tourguide", { state: { username, password, email } }); + if (termsAccepted) { + setShowModal(false); + if (role === "seller") { + navigate("/createseller", { state: { username, password, email } }); // Navigate to seller page + } else if (role === "advertiser") { + navigate("/createadvertiser", { state: { username, password, email } }); // Navigate to advertiser page + } else if (role === "tourguide") { + navigate("/tourguide", { state: { username, password, email } }); + } } }; @@ -90,9 +196,63 @@ const Register = () => { {showModal && ( -
-

You are accepted as a {role}!

- +
+
+

Terms and Conditions

+

Please read and accept the terms and conditions to proceed.

+
+ {/* Include your full Terms and Conditions here */} +

+ 1. Introduction
+ Welcome to JetSet! By using our website and services, you agree to these Terms and Conditions... + 1. Use of the Website +Eligibility: You must be at least 18 years old to use our services. By accessing our site, you confirm that you are of legal age. +Account Registration: Some features may require you to register an account. You are responsible for maintaining the confidentiality of your account credentials and for all activities that occur under your account. +Accuracy of Information: You agree to provide accurate and complete information when registering or using any part of our site. +
2. Services Offered
+Our website provides tools to plan virtual and real-world trips, including but not limited to itinerary building, virtual tours, trip bookings, and destination guides. Our services are for informational purposes and should be used at your own discretion. + +
3. User Responsibilities
+Compliance with Laws: You agree to comply with all applicable laws when using our website and services. +Content Sharing: Any content shared on our site, such as reviews, photos, and travel stories, must be your original work and must not infringe on any third-party rights. +Prohibited Activities: You agree not to use our services for any fraudulent, unlawful, or harmful activities. +
4. Booking and Payment Terms
+Third-Party Providers: Our site may feature third-party service providers (e.g., hotels, airlines). We are not responsible for any issues or disputes arising from these third-party services. +Payment Processing: Payments for any trip-related services may be processed by third-party payment systems. By using these services, you agree to their terms. +Cancellations and Refunds: Please review the specific cancellation and refund policies for each booking. These policies vary depending on the provider and may be subject to fees. +
5. Limitation of Liability
+No Guarantees: While we strive to offer accurate and reliable information, we make no guarantees regarding the completeness, accuracy, or availability of our services. +Assumption of Risk: You agree that you are using our site at your own risk and that we are not liable for any losses, damages, or injuries resulting from the use of our services or the reliance on any information provided. +
6. Privacy Policy
+Your privacy is important to us. Please review our Privacy Policy, which outlines how we collect, use, and protect your information. + +
7. Intellectual Property
+All content on this website, including text, images, graphics, and logos, is the property of JetSet and is protected by copyright and intellectual property laws. Unauthorized use of our content is prohibited. + +
8. Changes to the Terms
+We reserve the right to modify or update these Terms and Conditions at any time. Changes will be effective immediately upon posting. Your continued use of the site constitutes acceptance of the new terms. + +
9. Governing Law
+These Terms and Conditions are governed by the laws of Egypt, and any disputes will be resolved in the courts of Egypt. + +
10. Contact Us
+If you have any questions or concerns regarding these Terms and Conditions, please contact us at 12339. + +
By using our website and services, you acknowledge that you have read and agreed to these Terms and Conditions. +

+
+ + +
)}
diff --git a/jetset/src/components/Seller/ChangePasswordForm.js b/jetset/src/components/Seller/ChangePasswordForm.js new file mode 100644 index 0000000..1221f87 --- /dev/null +++ b/jetset/src/components/Seller/ChangePasswordForm.js @@ -0,0 +1,67 @@ +import React, { useState } from "react"; +import axios from "axios"; + +const ChangePasswordForm = ({ touristId }) => { + const [oldPassword, setOldPassword] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [message, setMessage] = useState(""); + + const handleChangePassword = async (e) => { + e.preventDefault(); + + if (newPassword !== confirmPassword) { + setMessage("New passwords do not match"); + return; + } + + try { + const response = await axios.put(`http://localhost:3000/cpSeller/66fc4923b6a7073d648f1bc0`, { + oldPassword, + newPassword, + }); + setMessage(response.data.message); + } catch (error) { + setMessage(error.response?.data.message || "Error updating password"); + } + }; + + return ( +
+

Change Password

+
+ + + + +
+ {message &&

{message}

} +
+ ); +}; + +export default ChangePasswordForm; diff --git a/jetset/src/components/Seller/DeleteAccount.js b/jetset/src/components/Seller/DeleteAccount.js new file mode 100644 index 0000000..b809e41 --- /dev/null +++ b/jetset/src/components/Seller/DeleteAccount.js @@ -0,0 +1,23 @@ +// src/DeleteAccount.js +import React from 'react'; +import axios from 'axios'; + +const DeleteAccount = ({ sellerId }) => { + + const handleDeleteAccount = async () => { + try { + const response = await axios.delete(`http://localhost:3000/deleteAccSeller/670307388ee1a9de350b6b1e`); + alert(response.data.message); // Show success message + } catch (err) { + alert(err.response?.data?.message || "An error occurred"); // Show error message + } + }; + + return ( +
+ +
+ ); +}; + +export default DeleteAccount; \ No newline at end of file diff --git a/jetset/src/components/SellerFrontend.js b/jetset/src/components/SellerFrontend.js index 0e0d1a5..8511ffe 100644 --- a/jetset/src/components/SellerFrontend.js +++ b/jetset/src/components/SellerFrontend.js @@ -9,6 +9,8 @@ import FilterProducts from "./Products/FilterProducts"; // Import FilterProducts import ProductList from "./Products/ProductList"; // Import ProductList import SearchProduct from "./Products/SearchProduct"; // Import SearchProduct import SortProducts from "./Products/SortProducts"; // Import SortProducts +import ChangePasswordForm from "./Seller/ChangePasswordForm"; +import DeleteAccount from "./Seller/DeleteAccount"; const SellerFrontend = () => { const sellerId = "66fc4923b6a7073d648f1bc0"; // Hardcoded seller ID @@ -19,6 +21,7 @@ const SellerFrontend = () => { const [showProductList, setShowProductList] = useState(false); const [showSearchProduct, setShowSearchProduct] = useState(false); const [showSortProducts, setShowSortProducts] = useState(false); + const [showChangePassword, setShowChangePassword] = useState(false); return (
@@ -65,6 +68,14 @@ const SellerFrontend = () => { {showSortProducts ? "Cancel Sort" : "Sort Products"} {showSortProducts && } + + {/* Button to toggle ChangePassword */} + + {showChangePassword && } + +
); }; diff --git a/jetset/src/components/TourGuide.js b/jetset/src/components/TourGuide.js index d0b0a10..7184c9c 100644 --- a/jetset/src/components/TourGuide.js +++ b/jetset/src/components/TourGuide.js @@ -10,6 +10,8 @@ import UpdateTouristItineraryForm from "./TouristItinerary/UpdateTouristItinerar import DeleteTouristItineraryForm from "./TouristItinerary/DeleteTouristItineraryForm.js"; import ViewCreatedItineraries from "./Itinerary/ViewCreatedItineraries.js"; import ReadItineraryList from "./Itinerary/ReadItineraryList.js"; +import ChangePasswordForm from "./Tourguide/ChangePasswordForm.js"; +import DeleteAccount from "./Tourguide/DeleteAccount.js"; function TourGuide() { const [view, setView] = useState("itineraries"); // State to toggle between views @@ -56,6 +58,12 @@ function TourGuide() { + + {/* Conditionally render components based on the view */} @@ -84,6 +92,12 @@ function TourGuide() { {view === "ViewCreatedItineraries" && ( )} + {view === "ChangePassword" && ( + + )} + {view === "deleteAcc" && ( + + )} ); diff --git a/jetset/src/components/Tourguide/ChangePasswordForm.js b/jetset/src/components/Tourguide/ChangePasswordForm.js new file mode 100644 index 0000000..93e1331 --- /dev/null +++ b/jetset/src/components/Tourguide/ChangePasswordForm.js @@ -0,0 +1,67 @@ +import React, { useState } from "react"; +import axios from "axios"; + +const ChangePasswordForm = ({ touristId }) => { + const [oldPassword, setOldPassword] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [message, setMessage] = useState(""); + + const handleChangePassword = async (e) => { + e.preventDefault(); + + if (newPassword !== confirmPassword) { + setMessage("New passwords do not match"); + return; + } + + try { + const response = await axios.put(`http://localhost:3000/cpTourguide/66fff1c213c1a607c2caa0c6`, { + oldPassword, + newPassword, + }); + setMessage(response.data.message); + } catch (error) { + setMessage(error.response?.data.message || "Error updating password"); + } + }; + + return ( +
+

Change Password

+
+ + + + +
+ {message &&

{message}

} +
+ ); +}; + +export default ChangePasswordForm; diff --git a/jetset/src/components/Tourguide/DeleteAccount.js b/jetset/src/components/Tourguide/DeleteAccount.js new file mode 100644 index 0000000..981e567 --- /dev/null +++ b/jetset/src/components/Tourguide/DeleteAccount.js @@ -0,0 +1,23 @@ +// src/DeleteAccount.js +import React from 'react'; +import axios from 'axios'; + +const DeleteAccount = ({ tourguideId }) => { + + const handleDeleteAccount = async () => { + try { + const response = await axios.delete(`http://localhost:3000/deleteAccTourguide/6728fc73fe820b19a48d6fbd`); + alert(response.data.message); // Show success message + } catch (err) { + alert(err.response?.data?.message || "An error occurred"); // Show error message + } + }; + + return ( +
+ +
+ ); +}; + +export default DeleteAccount; \ No newline at end of file diff --git a/jetset/src/components/TouristApp.js b/jetset/src/components/TouristApp.js index 521e0ab..d9e2783 100644 --- a/jetset/src/components/TouristApp.js +++ b/jetset/src/components/TouristApp.js @@ -9,6 +9,10 @@ import SearchProduct from "./Products/SearchProduct"; // Ensure the casing match import ProductList from "./Products/ProductList"; // Ensure the casing matches the file import FilterProducts from "./Products/FilterProducts"; // Ensure the casing matches the file import SortProducts from "./Products/SortProducts"; // Ensure the casing matches the file +import ChangePassword from "./tourist/ChangePasswordForm"; +import Book from "./tourist/ActivitiesAndItineraries"; +import Transportations from "./tourist/Transportations"; +import DeleteAccount from "./tourist/DeleteAccount"; import "./styles.css"; const Tourist = () => { @@ -34,6 +38,14 @@ const Tourist = () => { return ; case "SortProduct": return ; + case "changepassword": + return ; + case "booking": + return ; + case "transportation": + return ; + case "deleteAcc": + return ; default: return (
@@ -63,6 +75,18 @@ const Tourist = () => { + + + +
); } diff --git a/jetset/src/components/tourist/ActivitiesAndItineraries.js b/jetset/src/components/tourist/ActivitiesAndItineraries.js new file mode 100644 index 0000000..bf28cf3 --- /dev/null +++ b/jetset/src/components/tourist/ActivitiesAndItineraries.js @@ -0,0 +1,216 @@ + +// import React, { useEffect, useState } from "react"; +// import axios from "axios"; + +// const ActivitiesAndItineraries = ({ touristId }) => { +// const [activities, setActivities] = useState([]); +// const [itineraries, setItineraries] = useState([]); +// const [selectedActivityId, setSelectedActivityId] = useState(null); // Track selected activity +// const [selectedItineraryId, setSelectedItineraryId] = useState(null); // Track selected itinerary + +// useEffect(() => { +// const fetchData = async () => { +// try { +// const activitiesResponse = await axios.get("/getactivity"); +// const itinerariesResponse = await axios.get("/Itineraries"); +// setActivities(activitiesResponse.data); +// setItineraries(itinerariesResponse.data); +// } catch (error) { +// console.error("Error fetching data", error); +// } +// }; + +// fetchData(); +// }, []); + +// const handleBookActivity = async (activityId) => { +// setSelectedActivityId(activityId); // Set the selected activity ID +// try { +// const response = await axios.post(`/book/670d4935dcbc415cf0e18713/activity/${activityId}`); +// alert(response.data.message); +// } catch (error) { +// console.error("Error booking activity", error); +// alert(error.response ? error.response.data.message : "Booking failed!"); +// } finally { +// setSelectedActivityId(null); // Reset selection after booking +// } +// }; + +// const handleBookItinerary = async (itineraryId) => { +// setSelectedItineraryId(itineraryId); // Set the selected itinerary ID +// try { +// const response = await axios.post(`/book/670d4935dcbc415cf0e18713/itinerary/${itineraryId}`); +// alert(response.data.message); +// } catch (error) { +// console.error("Error booking itinerary", error); +// alert(error.response ? error.response.data.message : "Booking failed!"); +// } finally { +// setSelectedItineraryId(null); // Reset selection after booking +// } +// }; + +// return ( +//
+//

Activities

+//
    +// {activities.map((activity) => ( +//
  • +// {activity.title} - ${activity.budget} +// +//
  • +// ))} +//
+ +//

Itineraries

+//
    +// {itineraries.map((itinerary) => ( +//
  • +// {itinerary.name} - Budget: ${itinerary.budget} +// +//
  • +// ))} +//
+//
+// ); +// }; + +// export default ActivitiesAndItineraries; + +import React, { useEffect, useState } from "react"; +import axios from "axios"; + +const ActivitiesAndItineraries = ({ touristId }) => { + const [activities, setActivities] = useState([]); + const [itineraries, setItineraries] = useState([]); + const [bookedActivities, setBookedActivities] = useState([]); + const [bookedItineraries, setBookedItineraries] = useState([]); + const [selectedActivityId, setSelectedActivityId] = useState(null); + const [selectedItineraryId, setSelectedItineraryId] = useState(null); + + useEffect(() => { + const fetchData = async () => { + try { + const activitiesResponse = await axios.get("/getactivity"); + const itinerariesResponse = await axios.get("/Itineraries"); + setActivities(activitiesResponse.data); + setItineraries(itinerariesResponse.data); + + // Fetch tourist data to get booked activities and itineraries + const touristResponse = await axios.get(`/getTourist/672398fc87d64e4709f43cde`); + setBookedActivities(touristResponse.data.bookedActivities); + setBookedItineraries(touristResponse.data.bookedItineraries); + } catch (error) { + console.error("Error fetching data", error); + } + }; + + fetchData(); + }, [touristId]); + + const handleBookActivity = async (activityId) => { + setSelectedActivityId(activityId); + try { + const response = await axios.post(`/book/672398fc87d64e4709f43cde/activity/${activityId}`); + alert(response.data.message); + // Update booked activities after booking + setBookedActivities([...bookedActivities, activityId]); + } catch (error) { + console.error("Error booking activity", error); + alert(error.response ? error.response.data.message : "Booking failed!"); + } finally { + setSelectedActivityId(null); + } + }; + + const handleCancelActivity = async (activityId) => { + setSelectedActivityId(activityId); + try { + const response = await axios.delete(`/cancelActivity/672398fc87d64e4709f43cde/${activityId}`); + alert(response.data.message); + // Update booked activities after cancellation + setBookedActivities(bookedActivities.filter(id => id !== activityId)); + } catch (error) { + console.error("Error cancelling activity", error); + alert(error.response ? error.response.data.message : "Cancellation failed!"); + } finally { + setSelectedActivityId(null); + } + }; + + const handleBookItinerary = async (itineraryId) => { + setSelectedItineraryId(itineraryId); + try { + const response = await axios.post(`/book/672398fc87d64e4709f43cde/itinerary/${itineraryId}`); + alert(response.data.message); + // Update booked itineraries after booking + setBookedItineraries([...bookedItineraries, itineraryId]); + } catch (error) { + console.error("Error booking itinerary", error); + alert(error.response ? error.response.data.message : "Booking failed!"); + } finally { + setSelectedItineraryId(null); + } + }; + + const handleCancelItinerary = async (itineraryId) => { + setSelectedItineraryId(itineraryId); + try { + const response = await axios.delete(`/cancelItinerary/672398fc87d64e4709f43cde/${itineraryId}`); + alert(response.data.message); + // Update booked itineraries after cancellation + setBookedItineraries(bookedItineraries.filter(id => id !== itineraryId)); + } catch (error) { + console.error("Error cancelling itinerary", error); + alert(error.response ? error.response.data.message : "Cancellation failed!"); + } finally { + setSelectedItineraryId(null); + } + }; + + return ( +
+

Activities

+
    + {activities.map((activity) => { + const isBooked = bookedActivities.includes(activity._id); + return ( +
  • + {activity.title} - ${activity.budget} + {isBooked ? ( + + ) : ( + + )} +
  • + ); + })} +
+ +

Itineraries

+
    + {itineraries.map((itinerary) => { + const isBooked = bookedItineraries.includes(itinerary._id); + return ( +
  • + {itinerary.name} - Budget: ${itinerary.budget} + {isBooked ? ( + + ) : ( + + )} +
  • + ); + })} +
+
+ ); +}; + +export default ActivitiesAndItineraries; + diff --git a/jetset/src/components/tourist/ChangePasswordForm.js b/jetset/src/components/tourist/ChangePasswordForm.js new file mode 100644 index 0000000..14b6eb1 --- /dev/null +++ b/jetset/src/components/tourist/ChangePasswordForm.js @@ -0,0 +1,67 @@ +import React, { useState } from "react"; +import axios from "axios"; + +const ChangePasswordForm = ({ touristId }) => { + const [oldPassword, setOldPassword] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [message, setMessage] = useState(""); + + const handleChangePassword = async (e) => { + e.preventDefault(); + + if (newPassword !== confirmPassword) { + setMessage("New passwords do not match"); + return; + } + + try { + const response = await axios.put(`http://localhost:3000/cpTourist/672398fc87d64e4709f43cde`, { + oldPassword, + newPassword, + }); + setMessage(response.data.message); + } catch (error) { + setMessage(error.response?.data.message || "Error updating password"); + } + }; + + return ( +
+

Change Password

+
+ + + + +
+ {message &&

{message}

} +
+ ); +}; + +export default ChangePasswordForm; diff --git a/jetset/src/components/tourist/DeleteAccount.js b/jetset/src/components/tourist/DeleteAccount.js new file mode 100644 index 0000000..c06c31f --- /dev/null +++ b/jetset/src/components/tourist/DeleteAccount.js @@ -0,0 +1,23 @@ +// src/DeleteAccount.js +import React from 'react'; +import axios from 'axios'; + +const DeleteAccount = ({ touristId }) => { + + const handleDeleteAccount = async () => { + try { + const response = await axios.delete(`http://localhost:3000/deleteAccTourist/672398fc87d64e4709f43cde`); + alert(response.data.message); // Show success message + } catch (err) { + alert(err.response?.data?.message || "An error occurred"); // Show error message + } + }; + + return ( +
+ +
+ ); +}; + +export default DeleteAccount; \ No newline at end of file diff --git a/jetset/src/components/tourist/Transportations.js b/jetset/src/components/tourist/Transportations.js new file mode 100644 index 0000000..2036b3a --- /dev/null +++ b/jetset/src/components/tourist/Transportations.js @@ -0,0 +1,67 @@ +import React, { useEffect, useState } from "react"; +import axios from "axios"; + +const Transportations = ({ touristId }) => { + const [transportations, setTransportations] = useState([]); + const [bookedTransportations, setBookedTransportations] = useState([]); + const [selectedTransportationId, setSelectedTransportationId] = useState(null); + + useEffect(() => { + const fetchData = async () => { + try { + // Fetch activities, itineraries, and transportations + const transportationsResponse = await axios.get("/gettrans"); + const touristResponse = await axios.get(`/getTourist/67276362cb7de3270b15bc0c`); + setTransportations(transportationsResponse.data.transportation); + + // Set booked transportations from tourist data + setBookedTransportations(touristResponse.data.bookedTransportations); + } catch (error) { + console.error("Error fetching data", error); + } + }; + + fetchData(); + }, [touristId]); + + const handleBookTransportation = async (transportationId) => { + setSelectedTransportationId(transportationId); + try { + const response = await axios.post(`/bookTransportation/67276362cb7de3270b15bc0c/${transportationId}`); + alert(response.data.message); + + // Update booked transportations after booking + setBookedTransportations([...bookedTransportations, transportationId]); + } catch (error) { + console.error("Error booking transportation", error); + alert(error.response ? error.response.data.message : "Booking failed!"); + } finally { + setSelectedTransportationId(null); + } + }; + + return ( +
+

Transportations

+
    + {transportations.map((transportation) => { + const isBooked = bookedTransportations.includes(transportation._id); + return ( +
  • + {transportation.type} by {transportation.company} - ${transportation.price} +
    + From {transportation.pickup_location} to {transportation.dropoff_location} on {new Date(transportation.availability).toLocaleDateString()} + {isBooked ? ( + Already Booked + ) : ( + + )} +
  • + ); + })} +
+
+ ); +}; + +export default Transportations; diff --git a/jetset/src/styles.css b/jetset/src/styles.css index 9d543e1..393e327 100644 --- a/jetset/src/styles.css +++ b/jetset/src/styles.css @@ -176,3 +176,61 @@ footer { border-top-right-radius: 30px; box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1); } + +/* Overlay background */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(160, 224, 224, 0.9), rgba(240, 248, 255, 0.9)); /* Soft gradient with transparency */ + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; /* Center modal content vertically */ + z-index: 1000; /* Ensure it appears above other content */ + } + + /* Modal content */ + .modal { + background: white; + padding: 20px; + width: 400px; + max-width: 90%; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + text-align: center; + z-index: 1001; + } + + .terms-content { + height: 150px; + overflow-y: auto; + margin: 10px 0; + border: 1px solid #ddd; + padding: 10px; + border-radius: 4px; + } + + .modal button { + margin-top: 10px; + padding: 10px 20px; + background-color: #007bff; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + } + + .modal button:disabled { + background-color: #ccc; + cursor: not-allowed; + } + + .modal label { + display: block; + margin-top: 10px; + font-size: 14px; + } + \ No newline at end of file diff --git a/src/App.js b/src/App.js index ff2a002..3528ef7 100644 --- a/src/App.js +++ b/src/App.js @@ -18,6 +18,10 @@ const { getAdsById, changePasswordAdvertiser, getadvertiser, + createTransportation, + gettransportation, + updateActivityCreator, + deleteAdvertiserAccount, } = require("./Routes/advertiserController"); // Adjust path as necessary const itinerary = require("./Models/Itinerary"); @@ -46,6 +50,7 @@ const { filterProductSeller, getSellerById, changePasswordSeller, + deleteSellerAccount, } = require("./Routes/sellerController"); const { createPrefTag, @@ -88,6 +93,12 @@ const { searchActivity, searchItinerary, changePasswordTourist, + bookActivity, + bookItinerary, + cancelActivityBooking, + cancelItineraryBooking, + bookTransportation, + deleteTouristAccount, } = require("../src/Routes/touristController"); const { @@ -102,6 +113,7 @@ const { getItinerariesByDateRange, gettourguide, changePasswordTourGuide, + deleteTourGuideAccount, } = require("../src/Routes/tourguideController"); //tourguide tourist itinerary @@ -258,3 +270,16 @@ app.get("/getadvertiser", getadvertiser); app.put("/cpSeller/:id", changePasswordSeller); app.put("/cpTourguide/:id", changePasswordTourGuide); app.put("/cpTourismgoverner/:id", changePasswordTourismGoverner); + +app.post("/book/:touristId/activity/:activityId", bookActivity); +app.post("/book/:touristId/itinerary/:itineraryId", bookItinerary); +app.delete('/cancelActivity/:touristId/:activityId', cancelActivityBooking); +app.delete('/cancelItinerary/:touristId/:itineraryId', cancelItineraryBooking); +app.post("/bookTransportation/:touristId/:transportationId", bookTransportation); +app.post("/transportation", createTransportation); +app.get("/gettrans", gettransportation); +app.delete("/deleteAccTourist/:id", deleteTouristAccount); +app.delete("/deleteAccTourguide/:id", deleteTourGuideAccount); +app.put("/cr/:id", updateActivityCreator); +app.delete("/deleteAccAdvertiser/:id", deleteAdvertiserAccount); +app.delete("/deleteAccSeller/:id", deleteSellerAccount); \ No newline at end of file diff --git a/src/Models/Product.js b/src/Models/Product.js index 5965cee..344bff6 100644 --- a/src/Models/Product.js +++ b/src/Models/Product.js @@ -12,8 +12,9 @@ const productSchema = new Schema( required: true, }, description: String, - seller_username: { - type: String, + seller_id: { + type: Schema.Types.ObjectId, + ref: "Seller" }, // added ratings: { type: Number, diff --git a/src/Models/Tourist.js b/src/Models/Tourist.js index 6864300..f383115 100644 --- a/src/Models/Tourist.js +++ b/src/Models/Tourist.js @@ -22,6 +22,24 @@ const touristSchema = new Schema( DOB: Date, job: String, wallet: Number, + bookedActivities: [ + { + type: mongoose.Schema.Types.ObjectId, + ref: "Activity", + }, + ], + bookedItineraries: [ + { + type: mongoose.Schema.Types.ObjectId, + ref: "Itinerary", + }, + ], + bookedTransportations: [ + { + type: mongoose.Schema.Types.ObjectId, + ref: "Transportation", + }, + ], }, { timestamps: true } ); diff --git a/src/Models/Transportation.js b/src/Models/Transportation.js new file mode 100644 index 0000000..38e2b89 --- /dev/null +++ b/src/Models/Transportation.js @@ -0,0 +1,38 @@ +// Models/Transportation.js +const mongoose = require("mongoose"); +const Schema = mongoose.Schema; + +const transportationSchema = new Schema( + { + type: { + type: String, // e.g., "car", "bus", "train" + required: true, + }, + company: { + type: String, // Transportation company name + required: true, + }, + price: { + type: Number, // Price per trip + required: true, + }, + availability: { + type: Date, // Availability date + }, + pickup_location: { + type: String, // Starting location + required: true, + }, + dropoff_location: { + type: String, // Destination + required: true, + }, + creator: { + type: mongoose.Schema.Types.ObjectId, + ref: "Advertiser", + }, + }, + { timestamps: true } +); + +module.exports = mongoose.model("Transportation", transportationSchema); diff --git a/src/Routes/advertiserController.js b/src/Routes/advertiserController.js index 386a720..49a861e 100644 --- a/src/Routes/advertiserController.js +++ b/src/Routes/advertiserController.js @@ -1,5 +1,7 @@ const advertiserModel = require("../Models/Advertiser.js"); const Activity = require("../Models/Activity.js"); +const Transportation = require("../Models/Transportation"); +const Tourist = require("../Models/Tourist.js"); const createProfile = async (req, res) => { const { @@ -109,7 +111,7 @@ const createActivity = async (req, res) => { location, category, special_discount, - bookingOpen, + booking_open, tags, } = req.body; @@ -122,7 +124,7 @@ const createActivity = async (req, res) => { location, category, special_discount, - bookingOpen, + booking_open, tags, }); res.status(201).json(activity); @@ -234,6 +236,106 @@ const getadvertiser = async (req, res) => { res.status(400).json({ message: "Error retrieving users", error }); } }; +const createTransportation = async (req, res) => { + const { type, company, price, availability, pickup_location, dropoff_location, creator } = req.body; + + try { + const transportation = await Transportation.create({ + type, + company, + price, + availability, + pickup_location, + dropoff_location, + creator, + }); + + res.status(200).json({ message: "Transportation created successfully", transportation }); + } catch (error) { + res.status(400).json({ error: error.message }); + } +}; + +const gettransportation = async (req, res) => { + try { + const transportation = await Transportation.find(); + res.status(200).json({ transportation }); + } catch (error) { + res.status(400).json({ message: "Error retrieving transpotations", error }); + } +}; + +const updateActivityCreator = async (req, res) => { + const { id } = req.params; + const { + creator + } = req.body; + const sanitizedId = id.replace(/:/g, ""); + try { + const activity = await Activity.findByIdAndUpdate( + sanitizedId, + { + creator + }, + { new: true } + ); + + if (!activity) { + return res.status(404).json({ error: "Activity not found" }); + } + + res.status(200).json(activity); + } catch (err) { + res.status(400).json({ error: err.message }); + } +}; +const deleteAdvertiserAccount = async (req, res) => { + try { + const { id } = req.params; // Get advertiser ID from the URL + console.log(`Request to delete advertiser account with ID: ${id}`); + + // Find the advertiser by ID + const advertiser = await advertiserModel.findById(id); + if (!advertiser) { + console.log(`Advertiser account not found for ID: ${id}`); + return res.status(404).json({ success: false, message: "Advertiser account not found" }); + } + console.log(`Found advertiser: ${advertiser.username}`); + + // Find activities created by the advertiser + const activities = await Activity.find({ creator: advertiser._id }); + console.log(`Found activities created by advertiser: ${activities.length}`); + + // Collect all activity IDs created by the advertiser + const activityIds = activities.map(activity => activity._id); + console.log(`Activity IDs: ${activityIds}`); + + // Check if any tourists have booked these activities + const touristsWithBookings = await Tourist.find({ + bookedActivities: { $in: activityIds }, + }); + console.log(`Tourists with bookings for these activities: ${touristsWithBookings.length}`); + + if (touristsWithBookings.length > 0) { + // If any tourist has booked the activities, deny deletion + console.log("Cannot delete account: there are tourists who have booked activities."); + return res.status(403).json({ success: false, message: "Cannot delete account: you have booked activities" }); + } + + // If no tourists have booked the activities, delete the advertiser account and their activities + await Activity.deleteMany({ creator: advertiser._id }); // Delete activities + console.log(`Deleted activities created by advertiser: ${advertiser.username}`); + + await advertiserModel.findByIdAndDelete(id); // Delete advertiser account + console.log(`Deleted advertiser account: ${advertiser.username}`); + + return res.status(200).json({ success: true, message: "Advertiser account deleted successfully" }); + } catch (error) { + console.error("Error occurred while deleting advertiser account:", error); + return res.status(500).json({ success: false, message: "An error occurred while trying to delete the account" }); + } +}; + module.exports = { createProfile, getProfile, @@ -247,4 +349,8 @@ module.exports = { getAdsById, changePasswordAdvertiser, getadvertiser, + createTransportation, + gettransportation, + updateActivityCreator, + deleteAdvertiserAccount, }; diff --git a/src/Routes/sellerController.js b/src/Routes/sellerController.js index 84c1c12..a55e348 100644 --- a/src/Routes/sellerController.js +++ b/src/Routes/sellerController.js @@ -247,6 +247,40 @@ const changePasswordSeller = async (req, res) => { } }; +const deleteSellerAccount = async (req, res) => { + try { + const { id } = req.params; // Get seller ID from the URL + console.log(`Request to delete seller account with ID: ${id}`); + + // Find the seller by ID + const seller = await Seller.findById(id); + if (!seller) { + console.log(`Seller account not found for ID: ${id}`); + return res.status(404).json({ success: false, message: "Seller account not found" }); + } + console.log(`Found seller: ${seller.username}`); + + // Find products created by the seller + const products = await Product.find({ seller_id: seller._id }); + console.log(`Found products created by seller: ${products.length}`); + + // If products exist, delete them + if (products.length > 0) { + await Product.deleteMany({ seller_id: seller._id }); // Delete all products associated with the seller + console.log(`Deleted products created by seller: ${seller.username}`); + } + + // Delete the seller account + await Seller.findByIdAndDelete(id); + console.log(`Deleted seller account: ${seller.username}`); + + return res.status(200).json({ success: true, message: "Seller account deleted successfully" }); + } catch (error) { + console.error("Error occurred while deleting seller account:", error); + return res.status(500).json({ success: false, message: "An error occurred while trying to delete the account" }); + } +}; + module.exports = { createSeller, getSeller, @@ -259,4 +293,5 @@ module.exports = { updateProductSeller, getSellerById, changePasswordSeller, + deleteSellerAccount, }; diff --git a/src/Routes/tourguideController.js b/src/Routes/tourguideController.js index 2cb99bf..2768463 100644 --- a/src/Routes/tourguideController.js +++ b/src/Routes/tourguideController.js @@ -4,6 +4,7 @@ const mongoose = require("mongoose"); const bookingModel = require("../Models/Activity.js"); //TOURIST ITINERARY const TouristItinerary = require("../Models/TouristsItinerary.js"); +const Tourist = require("../Models/Tourist.js"); const Tag = require("../Models/Tag.js"); // Assuming tags are stored here const createTourGuideProfile = async (req, res) => { @@ -389,6 +390,43 @@ const changePasswordTourGuide = async (req, res) => { } }; +const deleteTourGuideAccount = async (req, res) => { + try { + const { id } = req.params; // Get tour guide ID from the URL + + // Find the tour guide by ID + const tourGuide = await TourGuide.findById(id); + if (!tourGuide) { + return res.status(404).json({ success: false, message: "Tour guide account not found" }); + } + + // Find itineraries created by the tour guide + const itineraries = await itineraryModel.find({ created_by: tourGuide._id }); + + // Collect all itinerary IDs created by the tour guide + const itineraryIds = itineraries.map(itinerary => itinerary._id); + + // Check if any tourists have booked these itineraries + const touristsWithBookings = await Tourist.find({ + bookedItineraries: { $in: itineraryIds }, + }); + + if (touristsWithBookings.length > 0) { + // If any tourist has booked the itineraries, deny deletion + return res.status(403).json({ success: false, message: "Cannot delete account: you have booked itineraries" }); + } + + // If no tourists have booked the itineraries, delete the tour guide account and their itineraries + await itineraryModel.deleteMany({ created_by: tourGuide._id }); // Delete itineraries + + await TourGuide.findByIdAndDelete(id); // Delete tour guide account + + return res.status(200).json({ success: true, message: "Tour guide account deleted successfully" }); + } catch (error) { + return res.status(500).json({ success: false, message: "An error occurred while trying to delete the account" }); + } +}; + module.exports = { createTourGuideProfile, getTourGuides, @@ -405,4 +443,5 @@ module.exports = { getItinerariesByDateRange, gettourguide, changePasswordTourGuide, + deleteTourGuideAccount, }; diff --git a/src/Routes/touristController.js b/src/Routes/touristController.js index a9013e6..5e91d71 100644 --- a/src/Routes/touristController.js +++ b/src/Routes/touristController.js @@ -4,6 +4,7 @@ const Itinerary = require("../Models/Itinerary"); const Historical = require("../Models/Historical"); const Tourist = require("../Models/Tourist.js"); const Category = require("../Models/Category.js"); +const Transportation = require("../Models/Transportation"); const { default: mongoose } = require("mongoose"); const createTourist = async (req, res) => { @@ -455,6 +456,193 @@ const changePasswordTourist = async (req, res) => { } }; +const bookActivity = async (req, res) => { + const { touristId, activityId } = req.params; + + try { + // Find the tourist + const tourist = await Tourist.findById(touristId); + if (!tourist) { + return res.status(404).json({ message: "Tourist not found" }); + } + + // Find the activity + const activity = await Activity.findById(activityId); + if (!activity || !activity.booking_open) { + return res.status(404).json({ message: "Activity not found or not available for booking" }); + } + + // Check if the tourist has already booked this activity + if (tourist.bookedActivities.includes(activityId)) { + return res.status(400).json({ message: "Activity already booked" }); + } + + // Update the tourist's bookedActivities and increment bookings count + tourist.bookedActivities.push(activityId); + // activity.bookings += 1; // Increment bookings count + await tourist.save(); + // await activity.save(); + + res.status(200).json({ message: "Activity booked successfully" }); + } catch (error) { + res.status(500).json({ message: "Error booking activity", error }); + } +}; + +const bookItinerary = async (req, res) => { + const { touristId, itineraryId } = req.params; + + try { + // Find the tourist + const tourist = await Tourist.findById(touristId); + if (!tourist) { + return res.status(404).json({ message: "Tourist not found" }); + } + + // Find the itinerary + const itinerary = await Itinerary.findById(itineraryId); + if (!itinerary) { + return res.status(404).json({ message: "Itinerary not found" }); + } + + // Check if the tourist has already booked this itinerary + if (tourist.bookedItineraries.includes(itineraryId)) { + return res.status(400).json({ message: "Itinerary already booked" }); + } + + // Update the tourist's bookedItineraries and increment bookings count + tourist.bookedItineraries.push(itineraryId); + // itinerary.bookings += 1; // Increment bookings count + await tourist.save(); + // await itinerary.save(); + + res.status(200).json({ message: "Itinerary booked successfully" }); + } catch (error) { + res.status(500).json({ message: "Error booking itinerary", error }); + } +}; + +const cancelActivityBooking = async (req, res) => { + const { touristId, activityId } = req.params; + + try { + // Find the tourist + const tourist = await Tourist.findById(touristId); + if (!tourist) { + return res.status(404).json({ message: "Tourist not found" }); + } + + // Find the activity + const activity = await Activity.findById(activityId); + if (!activity) { + return res.status(404).json({ message: "Activity not found" }); + } + + // Check if the activity is within the 48-hour cancellation period + const hoursUntilActivity = (new Date(activity.date) - new Date()) / (1000 * 60 * 60); + if (hoursUntilActivity < 48) { + return res.status(400).json({ message: "Cannot cancel less than 48 hours before the activity" }); + } + + // Remove the activity ID from tourist's bookedActivities + tourist.bookedActivities = tourist.bookedActivities.filter( + (id) => id.toString() !== activityId + ); + await tourist.save(); + + res.status(200).json({ message: "Activity booking cancelled successfully" }); + } catch (error) { + res.status(500).json({ message: "Error cancelling activity booking", error }); + } +}; + +const cancelItineraryBooking = async (req, res) => { + const { touristId, itineraryId } = req.params; + + try { + // Find the tourist + const tourist = await Tourist.findById(touristId); + if (!tourist) { + return res.status(404).json({ message: "Tourist not found" }); + } + + // Find the itinerary + const itinerary = await Itinerary.findById(itineraryId); + if (!itinerary) { + return res.status(404).json({ message: "Itinerary not found" }); + } + + // Check if the itinerary is within the 48-hour cancellation period + const hoursUntilItinerary = (new Date(itinerary.start_date) - new Date()) / (1000 * 60 * 60); + if (hoursUntilItinerary < 48) { + return res.status(400).json({ message: "Cannot cancel less than 48 hours before the itinerary start date" }); + } + + // Remove the itinerary ID from tourist's bookedItineraries + tourist.bookedItineraries = tourist.bookedItineraries.filter( + (id) => id.toString() !== itineraryId + ); + await tourist.save(); + + res.status(200).json({ message: "Itinerary booking cancelled successfully" }); + } catch (error) { + res.status(500).json({ message: "Error cancelling itinerary booking", error }); + } +}; + +const bookTransportation = async (req, res) => { + const { touristId, transportationId } = req.params; + + try { + const tourist = await Tourist.findById(touristId); + if (!tourist) { + return res.status(404).json({ message: "Tourist not found" }); + } + + const transportation = await Transportation.findById(transportationId); + if (!transportation) { + return res.status(404).json({ message: "Transportation option not found" }); + } + + if (tourist.bookedTransportations.includes(transportationId)) { + return res.status(400).json({ message: "Transportation already booked" }); + } + + tourist.bookedTransportations.push(transportationId); + await tourist.save(); + + res.status(200).json({ message: "Transportation booked successfully" }); + } catch (error) { + res.status(500).json({ message: "Error booking transportation", error }); + } +}; + +const deleteTouristAccount = async (req, res) => { + try { + const { id } = req.params; // Get tourist ID from the URL + + // Find the tourist by ID + const tourist = await Tourist.findById(id); + + if (!tourist) { + return res.status(404).json({ success: false, message: "Tourist account not found" }); + } + + // Check if the tourist has any booked activities or itineraries + if (tourist.bookedActivities.length === 0 && tourist.bookedItineraries.length === 0) { + // If no activities or itineraries are booked, delete the account + await Tourist.findByIdAndDelete(id); + return res.status(200).json({ success: true, message: "Tourist account deleted successfully" }); + } else { + // If there are booked activities or itineraries, deny deletion + return res.status(403).json({ success: false, message: "Cannot delete account: you have booked activities or itineraries" }); + } + } catch (error) { + console.error(error); + return res.status(500).json({ success: false, message: "An error occurred while trying to delete the account" }); + } +}; + module.exports = { getProducts, @@ -477,4 +665,10 @@ module.exports = { searchActivity, searchItinerary, changePasswordTourist, + bookActivity, + bookItinerary, + cancelActivityBooking, + cancelItineraryBooking, + bookTransportation, + deleteTouristAccount, };