Skip to content

Commit

Permalink
add user profile and events
Browse files Browse the repository at this point in the history
  • Loading branch information
haseebzaki-07 committed Nov 7, 2024
1 parent f34b7ec commit 18fb485
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 46 deletions.
25 changes: 24 additions & 1 deletion backend/controller/customer.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,28 @@ async function resetPassword(req, res) {
}
}

async function getCustomerDetail (req, res) {
const { id } = req.user;

try {
// Find user by ID and populate related fields if needed
const user = await Customer.findById(id)
.populate('bookedEvents') // Populate booked events if needed
.populate('orders') // Populate orders if needed
.populate('reservations') // Populate reservations if needed
.select('-password -verificationCode -otp'); // Exclude sensitive fields

if (!user) {
return res.status(404).json({ message: 'User not found' });
}

res.status(200).json(user);
} catch (error) {
console.error("Error fetching user profile:", error);
res.status(500).json({ message: 'Server error while fetching user profile' });
}
}

async function logout(req, res){
req.session.destroy((err) => {
if (err) {
Expand All @@ -202,5 +224,6 @@ module.exports = {
loginCustomer,
resetPassword,
logout,
verifyOtp
verifyOtp,
getCustomerDetail,
};
67 changes: 66 additions & 1 deletion backend/controller/event.controller.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const logger = require("../config/logger");
const Customer = require("../models/customer.model");
const Event = require("../models/events.model");

// Create a new event
Expand Down Expand Up @@ -67,4 +68,68 @@ const getEvents = async (req, res) => {
}
};

module.exports = { createEvent, getEvents, deleteEvent };
const bookEvent = async (req, res) => {
const { eventId } = req.body;
const userId = req.user;

try {
// Check if eventId is provided
if (!eventId) {
return res.status(400).json({ message: "Event ID is required" });
}

// Check if the event exists
const event = await Event.findById(eventId);
if (!event) {
return res.status(404).json({ message: "Event not found" });
}

// Find the user and check if they have already booked this event
const customer = await Customer.findById(userId);
if (!customer) {
return res.status(404).json({ message: "User not found" });
}

// Check if the event is already booked
const isAlreadyBooked = customer.bookedEvents.some(
(bookedEvent) => bookedEvent.toString() === eventId
);

if (isAlreadyBooked) {
return res.status(400).json({ message: "Event already booked" });
}

// Add the event to the user's bookedEvents array
customer.bookedEvents.push(eventId);
await customer.save();

res.status(200).json({
message: "Event successfully booked",
bookedEvent: event,
});
} catch (error) {
console.error("Error booking event:", error);
res.status(500).json({ message: "Internal server error while booking event" });
}
};

const getBookedEvents = async (req, res) => {
const { id } = req.user;

try {
// Find the customer by ID and populate the bookedEvents field with event details
const customer = await Customer.findById(id).populate('bookedEvents');

if (!customer) {
return res.status(404).json({ message: 'Customer not found' });
}

res.status(200).json({
bookedEvents: customer.bookedEvents,
});
} catch (error) {
console.error("Error fetching booked events:", error);
res.status(500).json({ message: 'Server error while fetching booked events' });
}
}
module.exports = { createEvent, getEvents, deleteEvent , getBookedEvents , bookEvent};
2 changes: 2 additions & 0 deletions backend/routes/customerRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const {
resetPassword,
logout,
verifyOtp,
getCustomerDetail,
} = require("../controller/customer.controller");
const authenticateCustomer = require("../middlewares/authCustomer");
const passport = require("../config/passport.config");
Expand Down Expand Up @@ -38,5 +39,6 @@ router.get(

router.post("/login", loginCustomer);
router.post("/reset-password", resetPassword);
router.get('/profile',authenticateCustomer, getCustomerDetail );

module.exports = router;
5 changes: 5 additions & 0 deletions backend/routes/eventRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const {
createEvent,
getEvents,
deleteEvent,
getBookedEvents,

bookEvent,
} = require("../controller/event.controller");
const authenticateCustomer = require("../middlewares/authCustomer");

Expand All @@ -30,6 +33,8 @@ router.get("/", async (req, res) => {
router.post("/create",authenticateCustomer, createEvent);
router.get("/all",authenticateCustomer, getEvents);
router.get("/delete",authenticateCustomer, deleteEvent);
router.get('/booked-events',authenticateCustomer, getBookedEvents);
router.post('/book',authenticateCustomer, bookEvent);


module.exports = router;
173 changes: 129 additions & 44 deletions frontend/src/components/Pages/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import Cookies from 'js-cookie';
import { jwtDecode } from 'jwt-decode';
import {jwtDecode} from 'jwt-decode'; // Import correction

function Profile() {
const Dashboard = () => {
const [profile, setProfile] = useState(null);
const [reservations, setReservations] = useState([]);
const [events, setEvents] = useState([]);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
const navigate = useNavigate();
const API_URL = import.meta.env.VITE_BACKEND_URL || 'http://localhost:3000';
const API_URL = 'http://localhost:3000';

// Fetch reservation data from API
useEffect(() => {
const fetchReservations = async () => {
const authToken = Cookies.get('authToken'); // Retrieve the authToken from cookies
const fetchUserData = async () => {
setLoading(true);
const authToken = Cookies.get('authToken');

if (!authToken) {
alert("Please sign in to view your reservations.");
alert("Please sign in to view your profile, reservations, and events.");
navigate('/login');
return;
}

// Decode the token to get the user ID
let userId;
try {
const decodedToken = jwtDecode(authToken);
userId = decodedToken.sub; // Use `sub` based on the backend token payload
console.log("Decoded userId:", userId); // Debugging line
userId = decodedToken.sub;
} catch (decodeError) {
console.error("Error decoding token:", decodeError);
alert("Invalid token. Please log in again.");
Expand All @@ -35,40 +35,82 @@ function Profile() {
}

try {
const response = await fetch(
`${API_URL}/api/reservation/get/${userId}`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${authToken}`, // Pass the token in headers
},
credentials: 'include',
}
);

if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || 'Failed to fetch reservations');
// Fetch Profile Data
const profileResponse = await fetch(`${API_URL}/api/user/profile`, {
headers: { 'Authorization': `Bearer ${authToken}` },
credentials: 'include',
});

if (!profileResponse.ok) {
const profileError = await profileResponse.json();
console.error("Profile fetch error:", profileError);
throw new Error(profileError.message || 'Failed to fetch profile data');
}
const profileData = await profileResponse.json();
setProfile(profileData);

// Fetch Reservations Data
const reservationsResponse = await fetch(`${API_URL}/api/reservation/get/${userId}`, {
headers: { 'Authorization': `Bearer ${authToken}` },
credentials: 'include',
});

const data = await response.json();
setReservations(data.data);
if (!reservationsResponse.ok) {
const reservationsError = await reservationsResponse.json();
console.error("Reservations fetch error:", reservationsError);
throw new Error(reservationsError.message || 'Failed to fetch reservations');
}
const reservationData = await reservationsResponse.json();
setReservations(reservationData.data || []);

} catch (error) {
console.error("Error fetching user data:", error);
setError(error.message);
console.error('Error fetching reservations:', error);

} finally {
setLoading(false); // Set loading to false after the fetch operation
setLoading(false);
}
};

fetchReservations();
fetchUserData();
}, [navigate]);

useEffect(() => {
const fetchEvents = async () => {
const authToken = Cookies.get('authToken');
console.log("Testing events fetch with token:", authToken);

if (!authToken) {
console.error("No auth token found");
return;
}

try {
const eventsResponse = await fetch(`${API_URL}/api/event/booked-events`, {
headers: {
'Authorization': `Bearer ${authToken}`,
},
credentials: 'include',
});

if (!eventsResponse.ok) throw new Error('Failed to fetch events');

const eventData = await eventsResponse.json();
console.log("Fetched events successfully:", eventData);
setEvents(eventData.bookedEvents || []);
} catch (error) {
console.error("Error fetching events:", error.message);
}
};

fetchEvents();
}, [API_URL]);


return (
<div className="min-h-screen bg-gray-100 dark:bg-black p-4 mt-10 ">
<div className="min-h-screen bg-gray-100 dark:bg-black p-4 mt-10">
<div className="container mx-auto">
<h1 className="text-4xl font-bold mb-6 text-center">Your Reservations</h1>
<h1 className="text-4xl font-bold mb-6 text-center">Your Profile</h1>

{error && (
<p className="text-red-500 text-center" aria-live="assertive">
Expand All @@ -77,24 +119,51 @@ function Profile() {
)}

{loading ? (
<p className="text-gray-500 text-center">Loading your reservations...</p>
<p className="text-gray-500 text-center">Loading your profile, reservations, and events...</p>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{reservations.length > 0 ? (
reservations.map((reservation, index) => (
<ReservationCard key={index} reservation={reservation} />
))
) : (
<p className="text-gray-500 text-center">No reservations found.</p>
)}
</div>
<>
{profile && <ProfileCard profile={profile} />}

<h2 className="text-4xl font-bold mt-10 mb-6 text-center">Your Reservations</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{reservations && reservations.length > 0 ? (
reservations.map((reservation, index) => (
<ReservationCard key={index} reservation={reservation} />
))
) : (
<p className="text-gray-500 text-center">No reservations found.</p>
)}
</div>

<h2 className="text-4xl font-bold mt-10 mb-6 text-center">Your Events</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{events.length > 0 ? (
events.map((event, index) => (
<EventCard key={index} event={event} />
))
) : (
<p className="text-gray-500 text-center">No events found.</p>
)}
</div>
</>
)}
</div>
</div>
);
}

// Separate ReservationCard component for better readability
// ProfileCard component for displaying user profile information
const ProfileCard = ({ profile }) => (
<div className="bg-white dark:bg-amber-800 rounded-lg shadow-md p-6 mb-10">
<h2 className="text-2xl font-semibold mb-4 text-center">Profile Information</h2>
<p className="text-md mb-2"><strong>Name:</strong> {profile.name}</p>
<p className="text-md mb-2"><strong>Email:</strong> {profile.email}</p>
<p className="text-md mb-2"><strong>Role:</strong> {profile.role}</p>
{profile.bio && <p className="text-md mb-2"><strong>Bio:</strong> {profile.bio}</p>}
</div>
);

// ReservationCard component for displaying each reservation
const ReservationCard = ({ reservation }) => (
<div className="bg-white dark:bg-amber-800 rounded-lg shadow-md p-4">
<h2 className="text-xl font-semibold mb-2">Reservation Details</h2>
Expand All @@ -109,4 +178,20 @@ const ReservationCard = ({ reservation }) => (
</div>
);

export default Profile;
// EventCard component for displaying each event
const EventCard = ({ event }) => (
<div className="bg-white dark:bg-amber-800 rounded-lg shadow-md p-4">
<h2 className="text-xl font-semibold mb-2">Event Details</h2>
<p className="text-md mb-1"><strong>Title:</strong> {event.title}</p>
<p className="text-md mb-1"><strong>Date:</strong> {new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(new Date(event.date))}</p>
<p className="text-md mb-1"><strong>Location:</strong> {event.location}</p>
<p className="text-md mb-1"><strong>Description:</strong> {event.description}</p>
</div>
);


export default Dashboard;

0 comments on commit 18fb485

Please sign in to comment.