From ac0b111de256100b430f1de28b7a756871b0e083 Mon Sep 17 00:00:00 2001 From: shuyi320 Date: Mon, 9 Dec 2024 00:54:11 -0500 Subject: [PATCH] added favorite --- backend/routes/api.users.js | 30 ++++-- frontend/package-lock.json | 20 ++++ frontend/package.json | 2 + .../app/(users)/dashboard/favorites/page.tsx | 100 ++++++++++++++++++ .../src/app/context/.getUserContext.tsx.swp | Bin 0 -> 12288 bytes frontend/src/app/pets/[id]/page.tsx | 24 ++++- frontend/src/socket.ts | 2 +- 7 files changed, 164 insertions(+), 14 deletions(-) create mode 100644 frontend/src/app/(users)/dashboard/favorites/page.tsx create mode 100644 frontend/src/app/context/.getUserContext.tsx.swp diff --git a/backend/routes/api.users.js b/backend/routes/api.users.js index 7671617..22e7c59 100644 --- a/backend/routes/api.users.js +++ b/backend/routes/api.users.js @@ -6,7 +6,9 @@ import { addPetToFavorites, removePetFromFavorites, create, + getUserById, } from "../database/models/userInfo.js"; +import { get } from "../database/models/petModel.js"; import { createClerkClient } from "@clerk/backend"; const router = Router(); @@ -115,18 +117,28 @@ router.post("/removePetFromFavorites", async (req, res) => { }); router.get("/getFavorites/:userId", async (req, res) => { - try { - const { userId } = req.params; - const user = await getUserById(userId); + try { + const { userId } = req.params; + const user = await getUserById(userId); - if (!user) { - return res.status(404).json({ error: "User not found" }); - } + if (!user) { + return res.status(404).json({ error: "User not found" }); + } - res.status(200).json({ favorites: user.favorites }); - } catch (error) { - res.status(400).json({ error: error.message }); + const favorites = user.favorites;//array of pet ids + const pets = []; + + for (let i = 0; i < favorites.length; i++) { + const pet = await get(favorites[i]); + if (pet) { + pets.push(pet); + } } + + res.status(200).json({ pets }); + } catch (error) { + res.status(400).json({ error: error.message }); + } }); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7f76b3f..284e8a3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -12,10 +12,12 @@ "@tanstack/react-query": "^5.59.20", "@vercel/analytics": "^1.3.1", "clsx": "^2.1.1", + "lucide-react": "^0.468.0", "next": "^14.2.8", "next-themes": "^0.3.0", "react": "^18", "react-dom": "^18", + "react-icons": "^5.4.0", "socket.io-client": "^4.8.1", "tailwind-merge": "^2.5.2", "uuid": "^11.0.3" @@ -3340,6 +3342,15 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, + "node_modules/lucide-react": { + "version": "0.468.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz", + "integrity": "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, "node_modules/map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -4086,6 +4097,15 @@ "react": "^18.3.1" } }, + "node_modules/react-icons": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.4.0.tgz", + "integrity": "sha512-7eltJxgVt7X64oHh6wSWNwwbKTCtMfK35hcjvJS0yxEAhPM8oUKdS3+kqaW1vicIltw+kR2unHaa12S9pPALoQ==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 5f85faa..2aa61dd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,10 +13,12 @@ "@tanstack/react-query": "^5.59.20", "@vercel/analytics": "^1.3.1", "clsx": "^2.1.1", + "lucide-react": "^0.468.0", "next": "^14.2.8", "next-themes": "^0.3.0", "react": "^18", "react-dom": "^18", + "react-icons": "^5.4.0", "socket.io-client": "^4.8.1", "tailwind-merge": "^2.5.2", "uuid": "^11.0.3" diff --git a/frontend/src/app/(users)/dashboard/favorites/page.tsx b/frontend/src/app/(users)/dashboard/favorites/page.tsx new file mode 100644 index 0000000..908f0ac --- /dev/null +++ b/frontend/src/app/(users)/dashboard/favorites/page.tsx @@ -0,0 +1,100 @@ +'use client' +import { useState, useEffect } from "react"; +import { Heart, ChevronRight } from "lucide-react"; +import Image from "next/image"; +import { useUser } from "@clerk/nextjs"; + +interface Pet { + animalId: string; + name: string; + breed: string; + description: string; + pictureThumbnailUrl: string; + isAdoptionPending: boolean; + size: string; + health: { + isCurrentVaccinations: boolean; + }; + isFavorited: boolean; +} + +export default function FavoritesPage() { + const [pets, setPets] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const { user } = useUser(); + + useEffect(() => { + const fetchFavorites = async () => { + try { + if (!user) return; + setLoading(true); + const response = await fetch(`http://localhost:3001/api/users/getFavorites/${user.id}`); + if (!response.ok) { + throw new Error("Failed to fetch favorite pets"); + } + + const data = await response.json(); + setPets( + data.pets.map((pet: any) => ({ + id: pet.id, + name: pet.name, + breed: pet.breed, + pictureThumbnailUrl: pet.pictureThumbnailUrl, + isFavorited: true, + })) + ); + } catch (err: any) { + setError(err.message); + } finally { + setLoading(false); + } + }; + + fetchFavorites(); + }, [user]); + + if (loading) return
Loading favorite pets...
; + if (error) return
{error}
; + + return ( +
+
+
+
+

Favorites

+

+ Your perfect companions, unforgettable faces, ready to fill your heart and home with love. +

+
+
+ +
+ {pets.map((pet) => ( +
+
+ {`Photo + +
+
+

{pet.name}

+

{pet.breed}

+
+
+ ))} + +
+
+
+ ); +} diff --git a/frontend/src/app/context/.getUserContext.tsx.swp b/frontend/src/app/context/.getUserContext.tsx.swp new file mode 100644 index 0000000000000000000000000000000000000000..d4f2dc3c73d9e286fc88f30f73213275084423b0 GIT binary patch literal 12288 zcmeHNO^6&t6t309Xf!H{=*`;B5@yM4_a^>eCi|1knuOKe7_ylVS(c>JT{Bb3O!v^$ zvnH9eo<#5@9t01bRXnIi!J8K^9-@NBh!+JxQBXky{a$s~Oz&o6JVZgQhHty8`n^~0 zef{1mhOM(xCl{CbK6i@Yx|gxt51efNPRptVT5*rw{mHdLa%ytf4-Urh z`N?BR?6%{;Z*Nw4eqhjhG7fqj5v9soSX^0{e01N`l-Ee&C>2rYsU-0HZr2NFd?EFk zBCRWtEaXEsRTnl}wqwhHW#GmP>|l@V-J?r=r!&Uyzps8{6)RyGunbrRECZGS%YbFT zGGH074E$#pNV_BK1w?a85z$g{AK7#-hW2I|unbrRECZGS%YbFTGGH073|Iy%1C{~H zz`u|IKV9tR!+zP*dFtH2k))4+qkFLyHbGw>Dg zA@B;&1$F}u1Eavz?Tmd4yav1k^not04E%ZrW8VRv0q+Cv0j~p1;1sX~%mY8&4nKgG zw=woTc=;Om68Hpo8+Z$N19%2_3Lx$d1NO2ESOzQu|3e0{{fzSo`osBj`LGragRRR@ zBz)a(_ry%Uhl5-zsh^5$wjZ=)J4{5BO?z9Z%)(kC(q0nr3d3_&<*QOH#eOKlDwk?S zHX{)(MpaHm*NWA@F;r?^JOgRYcNF`jq-L7aMH$KmI?b1HE3e5HtpP8BAd#9%OT%kj@+;b|9 zDige>1IYvY3#x@+N!buhzRE^L+b-4}E7rYyxggFsRJ@ol-}46a-vmkr%;oWFHtF$t zR2%v05Zw@I&^itpAxDpgb%KswmVuwj zIP!>UXQM_-Amh_n?WL7gLqPHcG9FBv*B-Y=@g z716qT^w6mjr;pVSE-fycolxuH_iqeh`FMgd2y&&+Bm$RBDnM37d@98^)dV?(o4nW0Qb#_^m9 zyw45y^tK=2Em(6rpm;HZk(h=V6<@I13IeSqH6rO@PpJnyinf3%v;>M zY8D|>P=vIR7{f+QD<6$Rv7bqtTTU8&Aov!`XhT%V*YgK!1c^X;7XJO+Trs^r79EnZ zadnM0a`VY+{A?Vzg&)!6fN}~9JE939MI+;-Z;BX5g72b0iLDOyWbA$DVaxebzDOQu zIFG_qX(FR0ErK+ZUG$Vx)$(QZBph2rQW&vYY?rA!*mQI?s}H3oG6 zFvw&_Cr;nEj>N}V^$vGj*Q@jprke1AmY;g7GF { + setIsFavorite(!isFavorite); + if (!user) return; + const response = await fetch('http://localhost:3001/api/users/addFavorite', { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ userId: user.id, petId: id }), + }) + const data = await response.json(); + console.log(data) + } + return (
@@ -69,11 +87,9 @@ export default function Page() { />
diff --git a/frontend/src/socket.ts b/frontend/src/socket.ts index ad989e4..39f2327 100644 --- a/frontend/src/socket.ts +++ b/frontend/src/socket.ts @@ -2,4 +2,4 @@ import { io } from "socket.io-client"; -export const socket = io(`ws://${process.env.NEXT_PUBLIC_API_URL}`, { withCredentials: true });; \ No newline at end of file +export const socket = io(`ws://${process.env.NEXT_PUBLIC_WS_URL}`, { withCredentials: true });; \ No newline at end of file