Skip to content

Commit 5a41276

Browse files
abhikaboyDavidYu75benjaspet
authored
Menu item reviews hookup (#109)
Co-authored-by: David Yu <yu.dav@northeastern.edu> Co-authored-by: Ben Petrillo <benpetrillo.bp@gmail.com>
1 parent 8ec5ecd commit 5a41276

File tree

15 files changed

+388
-64
lines changed

15 files changed

+388
-64
lines changed

backend/internal/handlers/restaurant/routes.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,6 @@ func Routes(app *fiber.App, collections map[string]*mongo.Collection) {
2828
rest.Post("/:id/menu-items", handler.AddMenuItem) // POST /api/v1/restaurant/:id/menu-items
2929
rest.Get("/:rid/super-stars", handler.GetSuperStars)
3030
rest.Get("/:uid/:rid", handler.GetRestaurantFriendsFav)
31+
32+
3133
}

backend/internal/handlers/restaurant/service.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,4 @@ func (s *Service) GetSuperStars(rid primitive.ObjectID) (int, error) {
267267
}
268268

269269
return superStars, nil
270-
}
270+
}

backend/internal/handlers/review/review.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package review
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"math"
78
"strconv"
89
"time"
@@ -372,5 +373,48 @@ func (h *Handler) GetTopReviews(c *fiber.Ctx) error {
372373
}
373374

374375
return c.JSON(reviews)
376+
}
377+
378+
func (h *Handler) GetUserReviewsByRestaurant(c *fiber.Ctx) error {
379+
userID := c.Params("uid")
380+
userOID, err := primitive.ObjectIDFromHex(userID)
381+
if err != nil {
382+
return err
383+
}
384+
rid := c.Params("rid")
385+
ridOID, err := primitive.ObjectIDFromHex(rid)
386+
if err != nil {
387+
return err
388+
}
389+
390+
// print both ids
391+
fmt.Println(userOID)
392+
fmt.Println(ridOID)
393+
cursor := h.service.GetUserReviewsByRestaurant(userOID, ridOID)
394+
defer cursor.Close(context.Background())
395+
396+
var reviews []ReviewDocument = make([]ReviewDocument, 0)
397+
if err := cursor.All(context.Background(), &reviews); err != nil {
398+
return err
399+
}
375400

401+
return c.JSON(&reviews)
376402
}
403+
404+
func (h *Handler) GetAllReviewsByRestaurant(c *fiber.Ctx) error {
405+
rid := c.Params("rid")
406+
ridOID, err := primitive.ObjectIDFromHex(rid)
407+
if err != nil {
408+
return err
409+
}
410+
411+
cursor := h.service.GetAllReviewsByRestaurant(ridOID)
412+
defer cursor.Close(context.Background())
413+
414+
var reviews []ReviewDocument = make([]ReviewDocument, 0)
415+
if err := cursor.All(context.Background(), &reviews); err != nil {
416+
return err
417+
}
418+
419+
return c.JSON(&reviews)
420+
}

backend/internal/handlers/review/routes.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ func Routes(app *fiber.App, collections map[string]*mongo.Collection) {
1818
// Add review group under API Version 1
1919
review := apiV1.Group("/review")
2020

21+
22+
// get a user's reviews for a restaurant
23+
review.Get("/:rid/user/:uid/reviews", handler.GetUserReviewsByRestaurant)
24+
25+
// get all reviews for a restaurant
26+
review.Get("/:rid/reviews", handler.GetAllReviewsByRestaurant)
27+
28+
2129
review.Post("/", handler.CreateReview)
2230
review.Get("/", handler.GetReviews)
2331
review.Get("/:id", handler.GetReview)

backend/internal/handlers/review/service.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,3 +610,27 @@ func (s *Service) Vote(reviewID primitive.ObjectID, userID primitive.ObjectID, l
610610
_, err = s.reviews.UpdateOne(ctx, filter, update)
611611
return finalVote, err
612612
}
613+
614+
func (s *Service) GetUserReviewsByRestaurant(uid primitive.ObjectID, rid primitive.ObjectID) *mongo.Cursor {
615+
ctx := context.Background()
616+
filter := bson.M{"reviewer._id": uid, "restaurantId": rid}
617+
618+
cursor, err := s.reviews.Find(ctx, filter)
619+
if err != nil {
620+
return nil
621+
}
622+
return cursor
623+
}
624+
625+
func (s *Service) GetAllReviewsByRestaurant(rid primitive.ObjectID) *mongo.Cursor {
626+
ctx := context.Background()
627+
filter := bson.M{"restaurantId": rid}
628+
629+
cursor, err := s.reviews.Find(ctx, filter)
630+
if err != nil {
631+
return nil
632+
}
633+
return cursor
634+
}
635+
636+

frontend/api/menu-items.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ import { TMenuItem } from "@/types/menu-item";
22
import { makeRequest } from "@/api/base";
33
import { TRestaurantMenuItemsMetrics } from "@/types/restaurant";
44

5+
interface ReviewQueryParams {
6+
userID?: string;
7+
sortBy?: "portion" | "taste" | "value" | "overall" | "timestamp";
8+
}
9+
510
export const getMenuItems = async ({
611
name,
712
tags,
@@ -42,6 +47,21 @@ export const getRandomMenuItems = async (limit: number): Promise<TMenuItem[]> =>
4247
return await makeRequest(`/api/v1/menu-items/random?limit=${limit}`, "GET");
4348
};
4449

50+
export const getMenuItemReviews = async (menuItemId: string, params?: ReviewQueryParams) => {
51+
const queryParams = new URLSearchParams();
52+
if (params?.userID) {
53+
queryParams.append("userID", params.userID);
54+
}
55+
if (params?.sortBy) {
56+
queryParams.append("sortBy", params.sortBy);
57+
}
58+
59+
const queryString = queryParams.toString();
60+
const path = `/api/v1/menu-items/${menuItemId}/reviews${queryString ? `?${queryString}` : ""}`;
61+
62+
return await makeRequest(path, "GET");
63+
};
64+
4565
export const getRecommendations = async (userId: string): Promise<TMenuItem[]> => {
4666
return await makeRequest(`:4000/reccomendation?user_id=${userId}`, "GET");
4767
};

frontend/api/reviews.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,17 @@ export const getReviewById = async (id: string): Promise<TReview> => {
1212
export const getFriendsReviews = async (id: string): Promise<TReview[]> => {
1313
return await makeRequest(`/api/v1/reviews/${id}/friendReviews`, "GET");
1414
};
15+
16+
export const getUserReviews = async (userId: string): Promise<TReview[]> => {
17+
return await makeRequest(`/api/v1/review/user/${userId}`, "GET");
18+
};
19+
20+
export const getRestaurantReviews = async (restaurantId: string): Promise<TReview[]> => {
21+
console.log("bang 3");
22+
return await makeRequest(`/api/v1/review/${restaurantId}/reviews`, "GET");
23+
};
24+
25+
export const getRestaurantReviewsByUser = async (userId: string, restaurantId: string): Promise<TReview[]> => {
26+
console.log("bang 4");
27+
return await makeRequest(`/api/v1/review/${restaurantId}/user/${userId}/reviews`, "GET");
28+
};

frontend/app/(menuItem)/[id].tsx

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { ReviewButton } from "@/components/review/ReviewButton";
1010
import HighlightCard from "@/components/restaurant/HighlightCard";
1111
import { PersonWavingIcon, RestaurantIcon, ThumbsUpIcon } from "@/components/icons/Icons";
1212
import { useLocalSearchParams, useNavigation, useRouter } from "expo-router";
13-
import { getMenuItemById } from "@/api/menu-items";
13+
import { getMenuItemById, getMenuItemReviews } from "@/api/menu-items";
1414
import { TMenuItem } from "@/types/menu-item";
1515
import ReviewFlow from "@/components/review/ReviewFlow";
1616
import AddReviewButton from "@/components/AddReviewButton";
@@ -21,6 +21,8 @@ export default function Route() {
2121

2222
const { id } = useLocalSearchParams<{ id: string }>();
2323

24+
const [reviews, setReviews] = useState<TReview[]>([]);
25+
2426
const navigation = useNavigation();
2527
const [menuItem, setMenuItem] = useState<TMenuItem | null>(null);
2628

@@ -34,7 +36,11 @@ export default function Route() {
3436
getMenuItemById(id).then((data) => {
3537
setMenuItem(data);
3638
});
37-
new Promise((resolve) => setTimeout(resolve, 1500)).then(() => setLoading(false));
39+
40+
getMenuItemReviews(id).then((data) => {
41+
setReviews(data);
42+
});
43+
new Promise((resolve) => setTimeout(resolve, 500)).then(() => setLoading(false));
3844
}, [navigation]);
3945

4046
return (
@@ -135,7 +141,7 @@ export default function Route() {
135141
<View style={styles.reviewStats}>
136142
<View style={styles.ratingContainer}>
137143
<ThemedText style={styles.ratingText}>4/5</ThemedText>
138-
<StarRating avgRating={4.2} numRatings={428} showAvgRating={false} />
144+
<StarRating avgRating={4.2} numRatings={4} showAvgRating={false} />
139145
</View>
140146
</View>
141147

@@ -159,18 +165,21 @@ export default function Route() {
159165
))}
160166
</View>
161167

162-
<ReviewPreview
163-
plateName="Pad Thai"
164-
restaurantName="Pad Thai Kitchen"
165-
tags={["Vegan", "Healthy", "Green", "Low-Cal"]}
166-
rating={4}
167-
content="The Buddha Bowl at Green Garden exceeded my expectations! Fresh ingredients, perfectly balanced flavors, and generous portions make this a must-try for health-conscious diners. The avocado was perfectly ripe, and the quinoa was cooked to perfection. I especially loved the homemade tahini dressing."
168-
authorId={""}
169-
reviewId="64f5a95cc7330b78d33265f1"
170-
authorUsername={"username"}
171-
authorName={"First Name"}
172-
authorAvatar={"https://placehold.co/600x400/png?text=P"}
173-
/>
168+
{reviews.map((review) => (
169+
<ReviewPreview
170+
key={review._id}
171+
reviewId={review._id}
172+
plateName={review.menuItemName}
173+
authorId={review.reviewer._id}
174+
authorUsername={review.reviewer.username}
175+
restaurantName={review.restaurantName}
176+
tags={[]}
177+
rating={review.rating.overall}
178+
content={review.content}
179+
authorName={review.reviewer.username}
180+
authorAvatar={review.reviewer.pfp || "https://placehold.co/600x400/png?text=P"}
181+
/>
182+
))}
174183
</ThemedView>
175184
</Skeleton>
176185
</ThemedView>

frontend/app/(restaurant)/[id].tsx

Lines changed: 74 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,52 @@ import { useLocalSearchParams, useNavigation, useRouter } from "expo-router";
1717
import { getRestaurant } from "@/api/restaurant";
1818
import { TRestaurant } from "@/types/restaurant";
1919
import { Skeleton } from "moti/skeleton";
20-
import { getReviews } from "@/api/reviews";
20+
import { getRestaurantReviews, getRestaurantReviewsByUser, getReviews } from "@/api/reviews";
21+
import { TReview } from "@/types/review";
22+
import { useUser } from "@/context/user-context";
2123

2224
export default function Route() {
2325
const restaurantTags = ["Fast Food", "Fried Chicken", "Chicken Sandwiches", "Order Online"];
2426
const [activeTab, setActiveTab] = React.useState(0);
2527
const [filterTab, setFilterTab] = React.useState(0);
2628

29+
const [reviews, setReviews] = React.useState<TReview[]>([]);
30+
const [myReviews, setMyReviews] = React.useState<TReview[]>([]);
31+
2732
const router = useRouter();
2833

2934
const { id } = useLocalSearchParams<{
3035
id: string;
3136
}>();
3237

38+
// get the user id
39+
const { user } = useUser();
40+
3341
const [restaurant, setRestaurant] = React.useState<TRestaurant | null>(null);
3442
const [loading, setLoading] = React.useState(true);
3543
const navigation = useNavigation();
3644

3745
useEffect(() => {
46+
console.log("bang");
47+
if (!user) {
48+
return;
49+
}
50+
console.log("bang 2");
3851
getRestaurant(id).then(async (res) => {
3952
setRestaurant(res);
4053
});
41-
new Promise((resolve) => setTimeout(resolve, 1500)).then(() => setLoading(false));
54+
55+
getRestaurantReviews(id).then((res) => {
56+
console.log(res);
57+
setReviews(res);
58+
});
59+
60+
getRestaurantReviewsByUser(id, user.id).then((res) => {
61+
console.log(res);
62+
setMyReviews(res);
63+
});
64+
65+
new Promise((resolve) => setTimeout(resolve, 500)).then(() => setLoading(false));
4266
navigation.setOptions({ headerShown: false });
4367
}, [navigation]);
4468

@@ -127,33 +151,54 @@ export default function Route() {
127151

128152
<Skeleton colorMode={"light"}>
129153
<ThemedView>
130-
{filterTab == 0 && (
131-
<>
132-
<ThemedView style={{ paddingVertical: 12 }}>
133-
<FeedTabs
134-
tabs={["Friends", "Top Reviews", "My Reviews"]}
135-
activeTab={activeTab}
136-
setActiveTab={setActiveTab}
137-
/>
138-
</ThemedView>
139-
<TouchableOpacity onPress={() => router.push("/(review)/827b36v4b234")}>
140-
<ReviewPreview
141-
plateName={"Big Whopper"}
142-
reviewId={"827b36v4b234"}
143-
restaurantName={"Burger King"}
144-
tags={["juicy", "artificial", "fake meat"]}
145-
rating={4}
146-
content={
147-
"This is fake meat and is not good for you. Not sure why we are even serving it."
148-
}
149-
authorName={"First Last"}
150-
authorAvatar={"https://placehold.co/600x400/png?text=P"}
151-
authorUsername={"username"}
152-
authorId={""}
153-
/>
154-
</TouchableOpacity>
155-
</>
156-
)}
154+
{filterTab == 2 ||
155+
(filterTab == 0 && (
156+
<>
157+
<ThemedView style={{ paddingVertical: 12 }}>
158+
<FeedTabs
159+
tabs={["Friends", "Top Reviews", "My Reviews"]}
160+
activeTab={activeTab}
161+
setActiveTab={setActiveTab}
162+
/>
163+
</ThemedView>
164+
{activeTab == 0 &&
165+
myReviews.map((review) => (
166+
<ReviewPreview
167+
key={review._id}
168+
reviewId={review._id}
169+
plateName={review.menuItemName}
170+
authorId={review.reviewer._id}
171+
authorUsername={review.reviewer.username}
172+
restaurantName={review.restaurantName}
173+
tags={[]}
174+
rating={review.rating.overall}
175+
content={review.content}
176+
authorName={review.reviewer.username}
177+
authorAvatar={
178+
review.reviewer.pfp || "https://placehold.co/600x400/png?text=P"
179+
}
180+
/>
181+
))}
182+
{activeTab == 1 &&
183+
reviews.map((review) => (
184+
<ReviewPreview
185+
key={review._id}
186+
reviewId={review._id}
187+
plateName={review.menuItemName}
188+
authorId={review.reviewer._id}
189+
authorUsername={review.reviewer.username}
190+
restaurantName={review.restaurantName}
191+
tags={[]}
192+
rating={review.rating.overall}
193+
content={review.content}
194+
authorName={review.reviewer.username}
195+
authorAvatar={
196+
review.reviewer.pfp || "https://placehold.co/600x400/png?text=P"
197+
}
198+
/>
199+
))}
200+
</>
201+
))}
157202

158203
{filterTab == 1 && <>{/* TODO MENU ITEM PREVIEW */}</>}
159204
</ThemedView>

0 commit comments

Comments
 (0)