diff --git a/README.md b/README.md index fef74c84b..6c52abaf6 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,6 @@ If you have a disability that prevents you from walking around campus, getting a The **Rider App** branch can be found [here](https://github.com/cornell-dti/carriage-rider). The **Driver App** branch can be found [here](https://github.com/cornell-dti/carriage-driver). -## Contributors: - ## Contributors ### Current Contributors diff --git a/frontend/src/components/RideDetails/RideOverview.tsx b/frontend/src/components/RideDetails/RideOverview.tsx index 7dbfb55bd..ef4a0db23 100644 --- a/frontend/src/components/RideDetails/RideOverview.tsx +++ b/frontend/src/components/RideDetails/RideOverview.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Box, Typography, @@ -18,6 +18,8 @@ import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import dayjs, { Dayjs } from 'dayjs'; import CalendarTodayIcon from '@mui/icons-material/CalendarToday'; +import FavoriteIcon from '@mui/icons-material/Favorite'; +import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder'; import AccessTimeIcon from '@mui/icons-material/AccessTime'; import InfoIcon from '@mui/icons-material/Info'; import DirectionsCarIcon from '@mui/icons-material/DirectionsCar'; @@ -39,6 +41,7 @@ import RiderList from './RiderList'; import { isNewRide } from '../../util/modelFixtures'; import { validateRideTimes } from './TimeValidation'; import styles from './RideOverview.module.css'; +import axios from '../../util/axios'; interface RideOverviewProps { userRole: 'rider' | 'driver' | 'admin'; @@ -205,6 +208,7 @@ const RideOverview: React.FC = ({ userRole }) => { const ride = editedRide!; const temporalType = getTemporalType(ride); const showRecurrence = userRole !== 'driver'; // Hide recurrence for drivers + const [isClicked, setIsClicked] = useState(false); const formatDateTime = (dateTimeString: string) => { const date = new Date(dateTimeString); @@ -313,6 +317,51 @@ const RideOverview: React.FC = ({ userRole }) => { updateRideField('type', event.target.value); }; + /** + * handleFavorite + * + * Triggered when a user clicks the favorite or unfavorite button for a past ride + * Intended to send a POST or DELETE request to database then be added or unadded + * as a favorites card + * + * Current status: + * - Function is implemented and makes both requests. + * - Issue: Favoriting and deleting favorites are currently failing with a 500 + * Internal Server Error + * + * TODO: + * - Fix backend schema mismatch or ensure correct key format + * - Add responsivity for button icon + */ + + const handleFavorite = async () => { + if (!isClicked) { + //user favorited icon + setIsClicked(true); + try { + await axios.post('/api/favorites', { + rideId: ride.id, + }); + } catch (error: any) { + console.error( + 'Error favoriting ride:', + error.response?.data || error.message + ); + } + } else { + //user unfavorited icon + setIsClicked(false); + try { + await axios.delete('/api/favorites/ride.id'); + } catch (error: any) { + console.error( + 'Error unfavoriting ride:', + error.response?.data || error.message + ); + } + } + }; + const renderPersonSection = () => { if (userRole === 'admin') return null; // Admin overview shows only ride info, people are in separate tab @@ -377,6 +426,24 @@ const RideOverview: React.FC = ({ userRole }) => {
Ride Overview + + {userRole === 'rider' && ( +
+ + {isClicked ? ( + + ) : ( + + )} + +
+ )}
{/* Schedule Section */} diff --git a/server/src/app.ts b/server/src/app.ts index 1c96ae228..b66607277 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -17,6 +17,7 @@ import upload from './router/upload'; import auth from './router/auth'; import stats from './router/stats'; import initSchedule from './util/repeatingRide'; +import favorites from './router/favorites'; import notification from './router/notification'; import initDynamoose from './util/dynamoose'; @@ -55,6 +56,7 @@ app.use('/api/auth', auth); app.use('/api/upload', upload); app.use('/api/notification', notification); app.use('/api/stats', stats); +app.use('/api/favorites', favorites); app.get('/api/health-check', (_, response) => response.status(200).send('OK')); // Serve static files from frontend diff --git a/server/src/models/favorite.ts b/server/src/models/favorite.ts index 0e84e6a9e..f255b4ec5 100644 --- a/server/src/models/favorite.ts +++ b/server/src/models/favorite.ts @@ -5,7 +5,7 @@ const schema = new dynamoose.Schema({ // we store references because storing the ride duplicates the data, comp key for efficiency and better normalization userId: { type: String, required: true, hashKey: true }, rideId: { type: String, required: true, rangeKey: true }, - favoritedAt: { type: Date, default: () => new Date() }, + favoritedAt: { type: String, default: () => new Date().toISOString() }, }); export const Favorite = dynamoose.model( diff --git a/server/src/models/index.ts b/server/src/models/index.ts index 9ec9db703..64cdcca1c 100644 --- a/server/src/models/index.ts +++ b/server/src/models/index.ts @@ -7,3 +7,4 @@ export { Admin as Admin } from './admin'; export { Notification as Notification } from './notification'; export { Stats as Stats } from './stats'; export { Rider as Rider } from './rider'; +export { Favorite as Favorite } from './favorite'; diff --git a/server/src/router/favorites.ts b/server/src/router/favorites.ts index e21f3a6c6..8dab23456 100644 --- a/server/src/router/favorites.ts +++ b/server/src/router/favorites.ts @@ -14,11 +14,17 @@ router.post('/', validateUser('User'), async (req, res) => { const { rideId } = req.body; const userId = res.locals.user.id; + console.log(rideId); + if (!rideId) { return res.status(400).send({ err: 'rideId is required' }); } + console.log(rideId); + const ride = await new Promise((resolve) => { + console.log('reach1 getting ride id') + db.getById(res, Ride, rideId, 'Rides', (rideData) => { resolve(rideData); }); @@ -30,23 +36,55 @@ router.post('/', validateUser('User'), async (req, res) => { }); } - const existingFavorite = await new Promise((resolve) => { - db.getById(res, Favorite, { userId, rideId }, tableName, (fav) => { - resolve(fav); - }); - }); + console.log('reach2 before existing fav'); - if (existingFavorite) { - return res.status(222).send({ msg: 'Ride already favorited' }); - } + console.log("Checking Favorite.get key:", { userId, rideId }); + + +const existingFavorite = await Favorite.query('userId').eq(userId) + .filter('rideId').eq(rideId) + .exec(); + + + // await new Promise((resolve) => { + // console.log('reach3 in existing favs'); + + // db.getById(res, Favorite, { userId, rideId }, tableName, (fav) => { + // resolve(fav); + // }); + // console.log('reach4 after existing favs call'); + // }); + + console.log('reach4.125 before checking existing'); + + +if (existingFavorite.count > 0) { + // already favorited +} + + console.log('reach4.25 before creating of new FavRid'); + + + + const favoriteRide = new Favorite({ userId, rideId, favoritedAt: new Date(), }); - db.create(res, favoriteRide, (doc) => res.send(doc)); + console.log('reach4.5 before try'); + + + try { + console.log('reach5 before creating new fav ride') + db.create(res, favoriteRide, (doc) => res.send(doc)); + } catch (err) { + console.log('reach5 after creating new fav ride'); +console.log(err); + } + }); // Get all favorite rides for the current user