From 83b5f9335eec1fae0c5da18e19c30e9c69c7dab1 Mon Sep 17 00:00:00 2001 From: kangsuyeong Date: Fri, 23 Aug 2024 11:27:12 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20[#100]=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=A2=8B=EC=95=84?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EC=98=B7,=20=EA=B4=80=EC=8B=AC=EC=9E=88?= =?UTF-8?q?=EB=8A=94=20=EC=98=B7=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FITple-Frontend/data/GetProfileApi.jsx | 9 +- FITple-Frontend/data/ProfileFavorApi.jsx | 21 +++++ FITple-Frontend/data/ProfileLoveApi.jsx | 21 +++++ .../src/components/Infom/Infom.jsx | 51 +++++++++-- .../src/components/Infom/Infom.style.js | 34 ++++++++ .../components/ProfileFavor/ProfileFavor.jsx | 18 +++- .../components/ProfileLove/ProfileLove.jsx | 16 +++- .../src/components/UserItem/UserItem.jsx | 86 ++++++++++--------- .../src/pages/ProfilePage/ProfilePage.jsx | 15 ++-- 9 files changed, 207 insertions(+), 64 deletions(-) create mode 100644 FITple-Frontend/data/ProfileFavorApi.jsx create mode 100644 FITple-Frontend/data/ProfileLoveApi.jsx diff --git a/FITple-Frontend/data/GetProfileApi.jsx b/FITple-Frontend/data/GetProfileApi.jsx index 02934b8..2800efd 100644 --- a/FITple-Frontend/data/GetProfileApi.jsx +++ b/FITple-Frontend/data/GetProfileApi.jsx @@ -1,13 +1,8 @@ const localhost = "http://localhost:3000"; -export const getProfile = async (category, cursorId, size) => { +export const getProfile = async () => { try { - const url = new URL(`${localhost}/FITple/my/closet/main`); - - // 쿼리 파라미터 추가 - if (category !== undefined) url.searchParams.append("category", category); - if (cursorId !== undefined) url.searchParams.append("cursorId", cursorId); - if (size !== undefined) url.searchParams.append("size", size); + const url = new URL(`${localhost}/FITple/profile`); const response = await fetch(url, { method: "GET", diff --git a/FITple-Frontend/data/ProfileFavorApi.jsx b/FITple-Frontend/data/ProfileFavorApi.jsx new file mode 100644 index 0000000..361c10b --- /dev/null +++ b/FITple-Frontend/data/ProfileFavorApi.jsx @@ -0,0 +1,21 @@ +const localhost = "http://localhost:3000"; + +export const ProfileFavorApi = async (category) => { + try { + const url = new URL(`${localhost}/FITple/profile/wish`); + if (category) url.searchParams.append("category", category); + const response = await fetch(url, { + method: "GET", + credentials: "include", + }); + + if (!response.ok) { + throw new Error(`서버 오류: ${response.status}`); + } + + return await response.json(); + } catch (error) { + console.error("검색 요청 중 오류가 발생했습니다.", error); + throw new Error("네트워크 오류가 발생했습니다. 잠시 후 다시 시도해주세요."); + } +}; diff --git a/FITple-Frontend/data/ProfileLoveApi.jsx b/FITple-Frontend/data/ProfileLoveApi.jsx new file mode 100644 index 0000000..6108052 --- /dev/null +++ b/FITple-Frontend/data/ProfileLoveApi.jsx @@ -0,0 +1,21 @@ +const localhost = "http://localhost:3000"; + +export const ProfileLoveApi = async (category) => { + try { + const url = new URL(`${localhost}/FITple/profile/likes`); + if (category) url.searchParams.append("category", category); + const response = await fetch(url, { + method: "GET", + credentials: "include", + }); + + if (!response.ok) { + throw new Error(`서버 오류: ${response.status}`); + } + + return await response.json(); + } catch (error) { + console.error("검색 요청 중 오류가 발생했습니다.", error); + throw new Error("네트워크 오류가 발생했습니다. 잠시 후 다시 시도해주세요."); + } +}; diff --git a/FITple-Frontend/src/components/Infom/Infom.jsx b/FITple-Frontend/src/components/Infom/Infom.jsx index 6d6f554..5b9d426 100644 --- a/FITple-Frontend/src/components/Infom/Infom.jsx +++ b/FITple-Frontend/src/components/Infom/Infom.jsx @@ -1,4 +1,16 @@ -import { Container, Wrap, SubWrap, UserImg, UserName } from "./Infom.style"; +import { + Container, + Wrap, + SubWrap, + UserImg, + UserName, + InformContainer, + Items, + SubItems, + FollowContainer, + FollowerNum, + Text, +} from "./Infom.style"; import SetUserImg from "/assets/UserImg.svg"; import Profile from "../../components/Profile/Profile"; @@ -7,20 +19,45 @@ import Follower from "../../components/Follower/Follower"; import OneLine from "../../components/OneLine/OneLine"; import FollowButton from "../../components/FollowButton/FollowButton"; -function Infom({ showFollowButton = true }) { +function Infom({ showFollowButton = true, data }) { return ( - + 0 && data.userData[0].user_image} + > - 핏플3 + + {data?.userData?.length > 0 && data.userData[0].nickname} + - + + + {data?.userData?.length > 0 && data.userData[0].height}cm{" "} + {data?.userData?.length > 0 && data.userData[0].weight}kg + + + + {data?.fit?.length > 0 && data.fit[0]},{" "} + {data?.fit?.length > 0 && data.fit[1]} + + + + {data?.style?.length > 0 && data.style[0]},{" "} + {data?.style?.length > 0 && data.style[1]} + + - - + + {data?.follower} + 팔로워 + + + {data?.following} + 팔로잉 + diff --git a/FITple-Frontend/src/components/Infom/Infom.style.js b/FITple-Frontend/src/components/Infom/Infom.style.js index 9d60473..e18644a 100644 --- a/FITple-Frontend/src/components/Infom/Infom.style.js +++ b/FITple-Frontend/src/components/Infom/Infom.style.js @@ -23,6 +23,40 @@ export const SubWrap = styled.div` gap: 28px; `; +export const InformContainer = styled.div` + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; +`; +export const FollowContainer = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + gap: 10px; + align-items: center; +`; + +export const FollowerNum = styled.div` + font-size: 32px; + font-weight: 600; + color: #0276fe; +`; + +export const Text = styled.div` + font-size: 23px; + font-weight: 600; +`; + +export const Items = styled.div` + font-size: 24px; +`; +export const SubItems = styled.div` + font-size: 16px; + color: #838383; +`; export const UserImg = styled.img` width: 140px; height: 140px; diff --git a/FITple-Frontend/src/components/ProfileFavor/ProfileFavor.jsx b/FITple-Frontend/src/components/ProfileFavor/ProfileFavor.jsx index 6c8c345..fcaee33 100644 --- a/FITple-Frontend/src/components/ProfileFavor/ProfileFavor.jsx +++ b/FITple-Frontend/src/components/ProfileFavor/ProfileFavor.jsx @@ -7,16 +7,28 @@ import { } from "./ProfileFavor.style"; import SideBar from "../SideBar/SideBar"; import ItemList from "../ItemList/ItemList"; - +import { useState } from "react"; +import { ProfileFavorApi } from "../../../data/ProfileFavorApi"; +import { useEffect } from "react"; const ProfileFavor = () => { + const [category, setCategory] = useState(undefined); + const [profileFavorData, setProfileFavorData] = useState([]); + const getProfileClothFavorData = async () => { + const response = await ProfileFavorApi(category); + setProfileFavorData(response.result.clothData); + console.log(response.result.clothData); + }; + useEffect(() => { + getProfileClothFavorData(); + }, [category]); return ( - + - + diff --git a/FITple-Frontend/src/components/ProfileLove/ProfileLove.jsx b/FITple-Frontend/src/components/ProfileLove/ProfileLove.jsx index 7cd42f0..314b8d1 100644 --- a/FITple-Frontend/src/components/ProfileLove/ProfileLove.jsx +++ b/FITple-Frontend/src/components/ProfileLove/ProfileLove.jsx @@ -7,16 +7,28 @@ import { } from "./ProfileLove.style"; import SideBar from "../SideBar/SideBar"; import ItemList from "../ItemList/ItemList"; +import { useState } from "react"; +import { ProfileLoveApi } from "../../../data/ProfileLoveApi"; +import { useEffect } from "react"; const ProfileLove = () => { + const [category, setCategory] = useState(undefined); + const [profileLoveData, setProfileLoveData] = useState([]); + const getProfileClothLoveData = async () => { + const response = await ProfileLoveApi(category); + setProfileLoveData(response.result.clothData); + }; + useEffect(() => { + getProfileClothLoveData(); + }, [category]); return ( - + - + diff --git a/FITple-Frontend/src/components/UserItem/UserItem.jsx b/FITple-Frontend/src/components/UserItem/UserItem.jsx index 8e29e9c..9e239a3 100644 --- a/FITple-Frontend/src/components/UserItem/UserItem.jsx +++ b/FITple-Frontend/src/components/UserItem/UserItem.jsx @@ -41,47 +41,53 @@ const UserItem = ({ ...props }) => { console.log(isOpen); }; return ( - - {/* 아이템 이미지 */} - - - {item.likes == 1 && } - + <> + {props.data[0] !== "해당 제품은 등록되어 있지 않아요." ? ( + + {/* 아이템 이미지 */} + + + {item.likes == 1 && props.$main && } + - {/* 유저정보 */} - - {/* 유저 프로필 */} - -

{item.nickname}

-
- - - {item.brand} - showOptionBox()}> - - - - 옷 정보 수정하기 - 옷 정보 삭제하기 - - - {item.cloth_name} - - {item.size} ㆍ - {item.fit} - - -
+ {/* 유저정보 */} + + {/* 유저 프로필 */} + +

{item.nickname}

+
+ + + {item.brand} + showOptionBox()}> + + + + 옷 정보 수정하기 + 옷 정보 삭제하기 + + + {item.cloth_name} + + {item.size} ㆍ + {item.fit} + + +
+ ) : ( +
상품이 없습니다
+ )} + ); }; diff --git a/FITple-Frontend/src/pages/ProfilePage/ProfilePage.jsx b/FITple-Frontend/src/pages/ProfilePage/ProfilePage.jsx index 3aae137..212e08c 100644 --- a/FITple-Frontend/src/pages/ProfilePage/ProfilePage.jsx +++ b/FITple-Frontend/src/pages/ProfilePage/ProfilePage.jsx @@ -17,11 +17,15 @@ import Infom from "../../components/Infom/Infom"; import { useNavigate } from "react-router-dom"; import { getProfile } from "../../../data/GetProfileApi"; import useAuthStore from "../../../data/store/userAuthStore"; +import { useEffect } from "react"; const ProfilePage = () => { const navigate = useNavigate(); const [selectItem, setSelectItem] = useState(0); const [isOpen, setIsOpen] = useState(false); + const [profileData, setProfileData] = useState({}); + const [profileLoveData, setProfileLoveData] = useState([]); + const { token } = useAuthStore(); // render @@ -30,7 +34,7 @@ const ProfilePage = () => { case 0: return ; case 1: - return ; + return ; case 2: return ; } @@ -46,11 +50,12 @@ const ProfilePage = () => { }; const getProfileData = async () => { - console.log("토큰확인", token); const response = await getProfile(); - console.log(response); + setProfileData(response.result); }; - getProfileData(); + useEffect(() => { + getProfileData(); + }, []); return ( <> @@ -58,7 +63,7 @@ const ProfilePage = () => { {/* 컴포넌트 사용 */} - + {/* 옵션 버튼 */} showOptionBox()}> From 7a6e839b184d819bfa14c8ea90903ea20af00308 Mon Sep 17 00:00:00 2001 From: kangsuyeong Date: Fri, 23 Aug 2024 11:55:16 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20[#100]=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=B2=B4=ED=98=95?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20API=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FITple-Frontend/data/ProfileBodyApi.jsx | 21 ++ .../ProfileMyBody/ProfileMyBody.jsx | 16 +- .../UserInfoBody2/UserInfoBody2.jsx | 86 ++++++ .../UserInfoBody2/UserInfoBody2.style.js | 255 ++++++++++++++++++ 4 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 FITple-Frontend/data/ProfileBodyApi.jsx create mode 100644 FITple-Frontend/src/components/UserInfoBody2/UserInfoBody2.jsx create mode 100644 FITple-Frontend/src/components/UserInfoBody2/UserInfoBody2.style.js diff --git a/FITple-Frontend/data/ProfileBodyApi.jsx b/FITple-Frontend/data/ProfileBodyApi.jsx new file mode 100644 index 0000000..f1bdb3e --- /dev/null +++ b/FITple-Frontend/data/ProfileBodyApi.jsx @@ -0,0 +1,21 @@ +const localhost = "http://localhost:3000"; + +export const ProfileBodyApi = async () => { + try { + const url = new URL(`${localhost}/FITple/profile/body_info`); + + const response = await fetch(url, { + method: "GET", + credentials: "include", + }); + + if (!response.ok) { + throw new Error(`서버 오류: ${response.status}`); + } + + return await response.json(); + } catch (error) { + console.error("검색 요청 중 오류가 발생했습니다.", error); + throw new Error("네트워크 오류가 발생했습니다. 잠시 후 다시 시도해주세요."); + } +}; diff --git a/FITple-Frontend/src/components/ProfileMyBody/ProfileMyBody.jsx b/FITple-Frontend/src/components/ProfileMyBody/ProfileMyBody.jsx index fd6c277..c50b4d6 100644 --- a/FITple-Frontend/src/components/ProfileMyBody/ProfileMyBody.jsx +++ b/FITple-Frontend/src/components/ProfileMyBody/ProfileMyBody.jsx @@ -1,11 +1,25 @@ import React from "react"; import UserBodyInfo from "../UserInfoBody/UserInfoBody"; import { Container } from "./ProfileMyBody.style"; +import { ProfileBodyApi } from "../../../data/ProfileBodyApi"; +import { useState } from "react"; +import { useEffect } from "react"; +import UserInfoBody2 from "../UserInfoBody2/UserInfoBody2"; const ProfileMyBody = () => { + const [bodyData, setBodyData] = useState({}); + const getProfileBodyData = async () => { + const response = await ProfileBodyApi(); + console.log("response", response.result); + setBodyData(response.result); + }; + useEffect(() => { + getProfileBodyData(); + }, []); return ( - + + {/* */} ); }; diff --git a/FITple-Frontend/src/components/UserInfoBody2/UserInfoBody2.jsx b/FITple-Frontend/src/components/UserInfoBody2/UserInfoBody2.jsx new file mode 100644 index 0000000..91cbc2a --- /dev/null +++ b/FITple-Frontend/src/components/UserInfoBody2/UserInfoBody2.jsx @@ -0,0 +1,86 @@ +import React from "react"; +import { + MainText, + InfoContainer, + BodyInfoContainer, + InputSet, + SubText, + BodyInputWrapper, + BodyInputBox, + UnitText, + MainBox, + EditBTN, +} from "./UserInfoBody2.style"; + +const UserInfoBody2 = ({ ...props }) => { + return ( + + {/* 컴포넌트 작업을 위해서 div하나 추가 */} + + 체형 정보 + 수정 + + + + + + + {props.data.height} + cm + + + + 몸무게 + + {props.data.weight} + kg + + + + 어깨 너비 + + {props.data.shoulder_width} + cm + + + + 가슴둘레 + + {props.data.chest_circumference} + cm + + + + 팔 길이 + + {props.data.arm_length} + cm + + + + 허리 둘레 + + {props.data.waist_circumference} + cm + + + + 허벅지 둘레 + + {props.data.thigh_circumference} + cm + + + + 엉덩이 둘레 + + {props.data.hip_circumference} + cm + + + + + ); +}; + +export default UserInfoBody2; diff --git a/FITple-Frontend/src/components/UserInfoBody2/UserInfoBody2.style.js b/FITple-Frontend/src/components/UserInfoBody2/UserInfoBody2.style.js new file mode 100644 index 0000000..48e1617 --- /dev/null +++ b/FITple-Frontend/src/components/UserInfoBody2/UserInfoBody2.style.js @@ -0,0 +1,255 @@ +import { styled } from "styled-components"; + +export const UserInfoPageWrapper = styled.div` + width: 100vw; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: start; + margin: 30px 0; +`; +// 컴포넌트 작업을 위해서 추가 +export const MainBox = styled.div` + display: flex; + align-items: center; + /* justify-content: space-between; */ + justify-content: ${(props) => (props.$profile ? "space-between" : "center")}; + width: 100%; + margin-bottom: 40px; + box-sizing: border-box; + padding: 0 30px; +`; + +export const ProfileImageWrapper = styled.div` + width: 150px; + height: 150px; + border-radius: 50%; + overflow: hidden; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + // border: 2px solid #0276FE; + margin-bottom: 20px; +`; + +export const ProfileImage = styled.img` + width: 100%; + height: 100%; + object-fit: cover; +`; + +export const MainText = styled.p` + font-size: 28px; + font-weight: bold; + text-align: center; +`; + +export const EditBTN = styled.button` + height: fit-content; + display: ${(props) => (props.$profile ? "block" : "none")}; + background-color: #0075ff; + color: white; + border-radius: 30px; + white-space: nowrap; + font-size: 16px; + font-weight: 500; + padding: 9px 36px; + border: none; + filter: drop-shadow(0px 16px 7px rgba(0, 0, 0, 0.01)) + drop-shadow(0px 9px 6px rgba(0, 0, 0, 0.05)) + drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.09)) + drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.1)); +`; + +export const SubText = styled.p` + width: auto; + text-align: left; + font-size: 17px; + color: #0276fe; + font-weight: 550; + margin-top: 10px; +`; + +export const Container = styled.div` + height: 135px; +`; + +export const InfoContainer = styled.div` + width: 55%; + height: auto; + display: flex; + flex-direction: column; + border: ${(props) => (props.$profile ? "none" : "1px solid #838383")}; + border-radius: 20px; + margin-top: 30px; + margin-bottom: 10px; + padding: 40px 0px; + align-items: center; +`; + +export const InfoWrapper = styled.div` + width: 100%; + display: flex; + flex-direction: row; + justify-content: center; + margin-top: 40px; + margin-left: -30px; +`; + +export const TextWrapper = styled.div` + width: 80px; + display: flex; + flex-direction: column; + align-items: end; +`; + +export const InputWrapper = styled.div` + width: 78%; + display: flex; + flex-direction: column; +`; + +export const InputBox = styled.input.attrs((props) => ({ + type: props.type || "text", // 기본값을 'text'로 설정 + id: props.id, +}))` + width: 100%; + height: 30px; + margin-top: 10px; + margin-bottom: 20px; + border: 1px solid #838383; + border-radius: 8px; + background-color: white; + padding: 10px 15px; + font-size: 17px; + outline-color: #0075ff; +`; + +export const GenderWrapper = styled.div` + width: 105%; + display: flex; + justify-content: center; + gap: 10px; + margin-top: 10px; + margin-bottom: 20px; +`; + +export const GenderButton = styled.button` + width: 100%; + padding: 15px 20px; + border: ${({ isSelected }) => (isSelected ? "none" : "1px solid #838383")}; + border-radius: 10px; + background-color: ${({ isSelected }) => (isSelected ? "#0075FF" : "white")}; + color: ${({ isSelected }) => (isSelected ? "white" : "black")}; + cursor: pointer; + font-size: 15px; + &:focus { + outline: none; + } +`; + +export const FitWrapper = styled.div` + width: 100%; + display: flex; + justify-content: start; + gap: 10px; + margin-top: 10px; + margin-bottom: 20px; +`; + +export const FitButton = styled.button` + width: auto; + padding: 10px 20px; + border: 1px solid #0276fe; + border-radius: 25px; + background-color: ${({ isSelected }) => (isSelected ? "#0075FF" : "white")}; + color: ${({ isSelected }) => (isSelected ? "white" : "black")}; + cursor: pointer; + font-size: 12px; + &:focus { + outline: none; + } +`; + +export const StyleWrapper = styled.div` + width: 100%; + display: flex; + justify-content: start; + gap: 5px; + margin-top: 10px; + margin-bottom: 20px; + flex-wrap: wrap; +`; + +export const StyleButton = styled.button` + width: auto; + padding: 10px 20px; + border: 1px solid #0276fe; + border-radius: 25px; + background-color: ${({ isSelected }) => (isSelected ? "#0075FF" : "white")}; + color: ${({ isSelected }) => (isSelected ? "white" : "black")}; + cursor: pointer; + margin: 5px; + font-size: 12px; + &:focus { + outline: none; + } +`; + +export const BodyInfoContainer = styled.div` + width: 100%; + display: flex; + flex-direction: row; + justify-content: center; + flex-wrap: wrap; + gap: 30px; +`; + +export const InputSet = styled.div` + width: auto; + display: flex; + flex-direction: column; + align-items: start; + margin-bottom: 10px; +`; + +export const BodyInputWrapper = styled.div` + width: 350px; + height: 50px; + border: 1px solid #838383; + border-radius: 8px; + background-color: white; + padding: 0px 50px; + font-size: 17px; + outline-color: #0075ff; + box-sizing: border-box; + position: relative; + margin-top: 10px; + display: flex; + justify-content: flex-end; + align-items: center; +`; + +export const BodyInputBox = styled.div``; + +export const UnitText = styled.span` + position: absolute; + right: 15px; + top: 50%; + transform: translateY(-50%); + font-size: 17px; + pointer-events: none; +`; + +export const SubmitButton = styled.button` + width: 150px; + height: 50px; + background-color: black; + border-radius: 10px; + font-size: 17px; + color: white; + margin-top: 25px; + cursor: pointer; +`;