diff --git a/src/apis/chatting/dto.ts b/src/apis/chatting/dto.ts index ee2dc1e8..a999e7e3 100644 --- a/src/apis/chatting/dto.ts +++ b/src/apis/chatting/dto.ts @@ -11,6 +11,7 @@ export interface OtherUserDto { id: number; nickname: string; profilePictureUrl: string; + bio: string; //추가 } export interface LatestMessageDto { diff --git a/src/pages/AccountCancel/index.tsx b/src/pages/AccountCancel/index.tsx index 3f155c35..7bc428a4 100644 --- a/src/pages/AccountCancel/index.tsx +++ b/src/pages/AccountCancel/index.tsx @@ -1,16 +1,17 @@ import React, { useState } from 'react'; import { CancelContainer, SubTitle, Text, InfoBox, InfoItem, CheckboxWrapper, CheckboxInput } from './styles'; -import { StyledText } from '../../components/Text/StyledText'; -import theme from '../../styles/theme'; -import { OODDFrame } from '../../components/Frame/Frame'; +import { StyledText } from '@components/Text/StyledText'; +import theme from '@styles/theme'; +import { OODDFrame } from '@components/Frame/Frame'; import { useNavigate } from 'react-router-dom'; -import TopBar from '../../components/TopBar'; -import back from '../../assets/arrow/left.svg'; +import TopBar from '@components/TopBar'; +import back from '@assets/arrow/left.svg'; -import BottomButton from '../../components/BottomButton'; -import { patchUserWithdrawApi } from '../../apis/user'; -import Modal from '../../components/Modal'; +import BottomButton from '@components/BottomButton'; +import { patchUserWithdrawApi } from '@apis/user'; +import Modal from '@components/Modal'; +import { getCurrentUserId } from '@utils/getCurrentUserId'; const AccountCancel: React.FC = () => { const [isChecked, setIsChecked] = useState(false); @@ -35,17 +36,17 @@ const AccountCancel: React.FC = () => { return; } - const storedUserId = Number(localStorage.getItem('my_id')); + const currentUserId = getCurrentUserId(); const token = localStorage.getItem('new_jwt_token'); - if (!storedUserId || !token) { + if (!currentUserId || !token) { setModalContent('사용자 정보를 찾을 수 없습니다.'); setIsModalVisible(true); return; } // API 요청 - const response = await patchUserWithdrawApi(storedUserId); + const response = await patchUserWithdrawApi(currentUserId); if (response.isSuccess) { setModalContent('계정이 성공적으로 삭제되었습니다.'); @@ -98,7 +99,7 @@ const AccountCancel: React.FC = () => { @@ -106,8 +107,8 @@ const AccountCancel: React.FC = () => {
diff --git a/src/pages/AccountCancel/styles.tsx b/src/pages/AccountCancel/styles.tsx index ceb7c9c0..264f93c4 100644 --- a/src/pages/AccountCancel/styles.tsx +++ b/src/pages/AccountCancel/styles.tsx @@ -7,46 +7,46 @@ interface ButtonProps { export const CancelContainer = styled.div` margin: 0 auto; width: 100%; - flex-grow: 1; /* flexbox에서 공간을 채우도록 설정 */ + flex-grow: 1; display: flex; flex-direction: column; `; export const SubTitle = styled.h3` - font-size: 1rem; /* 16px */ + font-size: 1rem; font-weight: bold; - margin-bottom: 0.625rem; /* 10px */ + margin-bottom: 0.625rem; text-align: center; text-align: left; margin-top: 10px; - padding: 1.25rem; /* 20px */ + padding: 1.25rem; `; export const Text = styled.p` - font-size: 0.875rem; /* 14px */ - margin-bottom: 5px; /* 20px */ + font-size: 0.875rem; + margin-bottom: 5px; text-align: left; margin-top: 10px; - padding: 0rem 1.25rem; /* 20px */ + padding: 0rem 1.25rem; `; export const InfoBox = styled.div` - background: #f5f5f5; - padding: 70px; /* 20px */ + background: ${({ theme }) => theme.colors.gray[100]}; + padding: 70px; margin-top: 10px; border-radius: 10px; - margin: 10px 20px 1.25rem 20px; /* 10px 위 여백, 20px 좌우 여백, 20px 아래 여백 */ + margin: 10px 20px 1.25rem 20px; `; export const InfoItem = styled.p` - font-size: 0.875rem; /* 14px */ - margin-bottom: 0.625rem; /* 10px */ + font-size: 0.875rem; + margin-bottom: 0.625rem; padding: 2px 10px; display: flex; justify-content: center; align-items: center; text-align: center; - height: 100%; /* 부모 컨테이너의 높이에 맞추기 */ + height: 100%; `; export const CheckboxWrapper = styled.div` @@ -56,46 +56,48 @@ export const CheckboxWrapper = styled.div` padding: 0rem 15px; input[type='checkbox'] { - margin-right: 0.625rem; /* 10px */ + margin-right: 0.625rem; } `; export const CheckboxInput = styled.input` margin-right: 0.625rem; cursor: pointer; - appearance: none; /* 기본 스타일 제거 */ + appearance: none; width: 1.25rem; height: 1.25rem; - border: 0.125rem solid #e0e0e0; + border: 0.125rem solid ${({ theme }) => theme.colors.gray[200]}; border-radius: 0.25rem; position: relative; &:checked { - background-color: #ffbbda; - border-color: #ff2389; - } + background-color: ${({ theme }) => theme.colors.brand.primaryLight}; + border-color: ${({ theme }) => theme.colors.brand.primary}; +} + &:checked::after { content: '✓'; - color: white; + color: ${({ theme }) => theme.colors.white}; font-size: 0.875rem; position: absolute; top: 50%; left: 50%; - transform: translate(-50%, -50%); /* 정확히 중앙으로 배치 */ + transform: translate(-50%, -50%); } `; export const StyledButton = styled.button` - margin-top: 18.75rem; /* 300px */ - background: ${(props) => (props.isChecked ? 'black' : '#ccc')}; - border-radius: 0.5rem; /* 8px */ + margin-top: 18.75rem; + background: ${({ theme, isChecked }) => + isChecked ? theme.colors.black : theme.colors.gray[300]}; + border-radius: 0.5rem; border: none; - padding: 1.5625rem; /* 25px */ + padding: 1.5625rem; text-align: center; - font-size: 1rem; /* 16px */ - color: white; + font-size: 1rem; + color: ${({ theme }) => theme.colors.white}; cursor: ${(props) => (props.isChecked ? 'pointer' : 'not-allowed')}; &:disabled { - background: #00000080; + background: ${({ theme }) => `${theme.colors.black}80`}; } -`; +`; \ No newline at end of file diff --git a/src/pages/AccountEdit/index.tsx b/src/pages/AccountEdit/index.tsx index f78907b7..bc79d7ab 100644 --- a/src/pages/AccountEdit/index.tsx +++ b/src/pages/AccountEdit/index.tsx @@ -13,18 +13,18 @@ import { Label, Info, } from './styles'; -import { OODDFrame } from '../../components/Frame/Frame'; +import { OODDFrame } from '@components/Frame/Frame'; -import BottomButton from '../../components/BottomButton'; // BottomButton 컴포넌트 임포트 +import BottomButton from '@components/BottomButton'; import { useNavigate } from 'react-router-dom'; -import { StyledText } from '../../components/Text/StyledText'; -import theme from '../../styles/theme'; +import { StyledText } from '@components/Text/StyledText'; +import theme from '@styles/theme'; -import naver from '../../assets/default/snsIcon/naver.svg'; -import kakao from '../../assets/default/snsIcon/kakao.svg'; -import TopBar from '../../components/TopBar'; -import back from '../../assets/arrow/left.svg'; +import naver from '@assets/default/snsIcon/naver.svg'; +import kakao from '@assets/default/snsIcon/kakao.svg'; +import TopBar from '@components/TopBar'; +import back from '@assets/arrow/left.svg'; const AccountEdit: React.FC = () => { const navigate = useNavigate(); // useNavigate 훅 사용 @@ -47,12 +47,12 @@ const AccountEdit: React.FC = () => { - + SNS 연결 - + 연결된 SNS계정으로 로그인되었습니다. @@ -71,7 +71,7 @@ const AccountEdit: React.FC = () => { @@ -79,7 +79,7 @@ const AccountEdit: React.FC = () => { diff --git a/src/pages/AccountEdit/styles.tsx b/src/pages/AccountEdit/styles.tsx index ac5ea79b..54f56140 100644 --- a/src/pages/AccountEdit/styles.tsx +++ b/src/pages/AccountEdit/styles.tsx @@ -1,92 +1,91 @@ import styled from 'styled-components'; export const ProfileEditContainer = styled.div` - max-width: 512px; /* 32rem */ + max-width: 512px; display: flex; flex-direction: column; position: relative; `; export const Section = styled.div` - margin-top: 1.875rem; /* 30px */ - - margin-bottom: 1.875rem; /* 30px */ - width: 100%; /* Section이 부모 컨테이너의 전체 너비를 차지하도록 설정 */ + margin-top: 1.875rem; + margin-bottom: 1.875rem; + width: 100%; padding: 0px 30px; `; export const SectionTitle = styled.div` - font-size: 1.125rem; /* 18px */ + font-size: 1.125rem; font-weight: bold; - margin-bottom: 0.625rem; /* 10px */ - margin-top: 1.125rem; /* 18px */ - text-align: left; /* 텍스트를 왼쪽 정렬 */ + margin-bottom: 0.625rem; + margin-top: 1.125rem; + text-align: left; `; export const SNSInfo = styled.div` display: flex; flex-direction: column; - margin-bottom: 0.625rem; /* 10px */ - margin-top: 3.125rem; /* 50px */ + margin-bottom: 0.625rem; + margin-top: 3.125rem; `; export const SNSInfoRow = styled.div` display: flex; align-items: center; - margin-bottom: 0.625rem; /* 10px */ + margin-bottom: 0.625rem; `; export const SNSIcon = styled.img` - width: 2.5rem; /* 40px */ - height: 2.5rem; /* 40px */ - margin-right: 0.625rem; /* 10px */ + width: 2.5rem; + height: 2.5rem; + margin-right: 0.625rem; margin-top: 1.875rem; flex-shrink: 0; object-fit: cover; `; export const Text = styled.div` - font-size: 0.875rem; /* 14px */ - color: #666; + font-size: 0.875rem; + color: ${({ theme }) => theme.colors.gray[600]}; margin-top: 2.1875rem; - text-align: left; /* 텍스트를 왼쪽 정렬 */ + text-align: left; `; export const SnsConnection = styled.div` - font-size: 1rem; /* 16px */ + font-size: 1rem; font-weight: bold; - color: #333; - margin-bottom: 0.625rem; /* 10px */ - text-align: left; /* 텍스트를 왼쪽 정렬 */ + color: ${({ theme }) => theme.colors.gray[700]}; + margin-bottom: 0.625rem; + text-align: left; `; export const MemberInfo = styled.div` display: flex; flex-direction: column; margin-top: 35px; - width: 100%; /* 부모 컨테이너의 전체 너비를 차지하도록 설정 */ + width: 100%; `; export const MemberInfoRow = styled.div` display: flex; align-items: center; - justify-content: flex-start; /* 아이템들을 왼쪽으로 정렬 */ - margin-bottom: 0.625rem; /* 10px */ + justify-content: flex-start; + margin-bottom: 0.625rem; margin-top: 10px; `; export const Label = styled.div` - font-size: 0.875rem; /* 14px */ - color: #333; + font-size: 0.875rem; + color: ${({ theme }) => theme.colors.gray[700]}; display: flex; align-items: center; - width: 6.25rem; /* 100px, 라벨의 고정 너비 설정 */ + width: 6.25rem; `; export const Info = styled.div` - font-size: 0.875rem; /* 14px */ - color: #999; - margin-left: 0.625rem; /* 10px */ - flex-grow: 1; /* 라벨과 함께 라인을 맞추기 위해 넓이를 확장 */ - text-align: left; /* 텍스트를 왼쪽 정렬 */ + font-size: 0.875rem; + color: ${({ theme }) => theme.colors.gray[500]}; + margin-left: 0.625rem; + flex-grow: 1; + text-align: left; `; diff --git a/src/pages/AccountSetting/index.tsx b/src/pages/AccountSetting/index.tsx index 7da01656..541e6efb 100644 --- a/src/pages/AccountSetting/index.tsx +++ b/src/pages/AccountSetting/index.tsx @@ -2,18 +2,19 @@ import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { ProfileEditContainer, ProfilePic, ProfilePicWrapper, Label, Row, List, ListItem } from './styles'; -import { OODDFrame } from '../../components/Frame/Frame'; -import ConfirmationModal from '../../components/ConfirmationModal'; -import { StyledText } from '../../components/Text/StyledText'; -import theme from '../../styles/theme'; -import TopBar from '../../components/TopBar'; -import back from '../../assets/arrow/left.svg'; -import imageBasic from '../../assets/default/defaultProfile.svg'; -import Profile_s from './../../assets/default/my-page.svg'; -import leave from '../../assets/default/leave.svg'; -import { getUserInfoApi } from '../../apis/user'; -import { UserInfoData } from '../../apis/user/dto'; -import Loading from '../../components/Loading'; +import { OODDFrame } from '@components/Frame/Frame'; +import ConfirmationModal from '@components/ConfirmationModal'; +import { StyledText } from '@components/Text/StyledText'; +import theme from '@styles/theme'; +import TopBar from '@components/TopBar'; +import back from '@assets/arrow/left.svg'; +import imageBasic from '@assets/default/defaultProfile.svg'; +import Profile_s from '@assets/default/my-page.svg'; +import leave from '@assets/default/leave.svg'; +import { getUserInfoApi } from '@apis/user'; +import { UserInfoData } from '@apis/user/dto'; +import Loading from '@components/Loading'; +import { getCurrentUserId } from '@utils/getCurrentUserId'; const AccountSetting: React.FC = () => { const navigate = useNavigate(); @@ -25,14 +26,13 @@ const AccountSetting: React.FC = () => { useEffect(() => { const getUserInfo = async () => { try { - const storedUserId = Number(localStorage.getItem('my_id')); - if (!storedUserId) { + const currentUserId = getCurrentUserId(); + if (!currentUserId) { console.error('User is not logged in'); return; } - const userId = Number(storedUserId); - const response = await getUserInfoApi(userId); + const response = await getUserInfoApi(currentUserId); setUserProfile(response.data); } catch (error) { console.error('Error fetching user info:', error); @@ -88,7 +88,7 @@ const AccountSetting: React.FC = () => { @@ -98,13 +98,13 @@ const AccountSetting: React.FC = () => { 로그아웃 아이콘 - + Logout 회원 탈퇴 아이콘 - + 회원탈퇴 diff --git a/src/pages/AccountSetting/styles.tsx b/src/pages/AccountSetting/styles.tsx index cbf82ed4..767c195a 100644 --- a/src/pages/AccountSetting/styles.tsx +++ b/src/pages/AccountSetting/styles.tsx @@ -3,8 +3,7 @@ import styled from 'styled-components'; export const ProfileEditContainer = styled.div` margin: 0 auto; width: 100%; - flex-grow: 1; /* flexbox에서 공간을 채우도록 설정 */ - + flex-grow: 1; display: flex; flex-direction: column; align-items: center; @@ -14,18 +13,18 @@ export const ProfilePicWrapper = styled.div` display: flex; flex-direction: column; align-items: center; - margin-bottom: 1.25rem; /* 20px */ + margin-bottom: 1.25rem; margin-top: 24px; `; export const ProfilePic = styled.div` - width: 7.25rem; /* 116px */ - height: 7.25rem; /* 116px */ + width: 7.25rem; + height: 7.25rem; flex-shrink: 0; border-radius: 50%; overflow: hidden; - margin-top: 2.125rem; /* 34px */ - margin-bottom: 1.375rem; /* 22px */ + margin-top: 2.125rem; + margin-bottom: 1.375rem; img { width: 100%; @@ -61,7 +60,7 @@ export const List = styled.ul` padding: 0; margin: 0; list-style: none; - border-top: 0px solid #eee; + border-top: 0px solid ${({ theme }) => theme.colors.gray[200]}; position: absolute; bottom: 20px; `; @@ -69,24 +68,24 @@ export const List = styled.ul` export const ListItem = styled.li` display: flex; align-items: center; - padding: 15px 1.25rem; /* 15px 20px */ - border-bottom: 0px solid #eee; + padding: 15px 1.25rem; + border-bottom: 0px solid ${({ theme }) => theme.colors.gray[200]}; cursor: pointer; & img:first-child { - margin-right: 1rem; /* 첫 번째 이미지(왼쪽 아이콘)의 오른쪽 간격 설정 */ + margin-right: 1rem; } & img:last-child { - margin-left: auto; /* 마지막 이미지(오른쪽 화살표 아이콘)를 오른쪽으로 정렬 */ + margin-left: auto; } &:hover { - background: #f9f9f9; - } + background: ${({ theme }) => theme.colors.gray[50]}; +} span { flex: 1; - text-align: left; /* 텍스트 왼쪽 정렬 */ + text-align: left; } `; diff --git a/src/pages/Profile/ButtonSecondary/index.tsx b/src/pages/Profile/ButtonSecondary/index.tsx index 3ce66152..e4962140 100644 --- a/src/pages/Profile/ButtonSecondary/index.tsx +++ b/src/pages/Profile/ButtonSecondary/index.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { useNavigate } from 'react-router-dom'; import { Button } from './styles'; -import { StyledText } from '../../../components/Text/StyledText'; -import theme from '../../../styles/theme'; +import { StyledText } from '@components/Text/StyledText'; +import theme from '@styles/theme'; const ButtonSecondary: React.FC = () => { const navigate = useNavigate(); diff --git a/src/pages/Profile/ButtonSecondary/styles.tsx b/src/pages/Profile/ButtonSecondary/styles.tsx index a7c01716..5f4a576e 100644 --- a/src/pages/Profile/ButtonSecondary/styles.tsx +++ b/src/pages/Profile/ButtonSecondary/styles.tsx @@ -5,11 +5,11 @@ export const Button = styled.button` margin: 16px auto; height: 3.1rem; text-align: center; - color: #ff2389; + color: ${({ theme }) => theme.colors.brand.primary}; cursor: pointer; box-sizing: border-box; border: 1px solid; border-radius: 10px; - border-color: #ff2389; + border-color: ${({ theme }) => theme.colors.brand.primary}; padding: 10px; `; diff --git a/src/pages/Profile/NavbarProfile/index.tsx b/src/pages/Profile/NavbarProfile/index.tsx index 8807d220..f6544f55 100644 --- a/src/pages/Profile/NavbarProfile/index.tsx +++ b/src/pages/Profile/NavbarProfile/index.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { Nav, IconContainer } from './styles'; import { Link } from 'react-router-dom'; -import settingIcon from "../../../assets/default/setting.svg" -import { StyledText } from '../../../components/Text/StyledText'; -import theme from '../../../styles/theme'; +import settingIcon from '@assets/default/setting.svg'; +import { StyledText } from '@components/Text/StyledText'; +import theme from '@styles/theme'; const NavbarProfile: React.FC = () => { return ( diff --git a/src/pages/Profile/NavbarProfile/styles.tsx b/src/pages/Profile/NavbarProfile/styles.tsx index c9407397..a40211ad 100644 --- a/src/pages/Profile/NavbarProfile/styles.tsx +++ b/src/pages/Profile/NavbarProfile/styles.tsx @@ -9,9 +9,8 @@ export const Nav = styled.nav` top: 0; left: 0; width: 100%; - background-color: white; - z-index: 1000; /* 다른 요소들보다 위에 오도록 설정 */ - + background-color: ${({ theme }) => theme.colors.white}; + z-index: 1000; position: sticky; `; @@ -26,7 +25,7 @@ export const IconContainer = styled.div` } img { - width: 1.5rem; /* 24px */ - height: 1.5rem; /* 24px */ + width: 1.5rem; + height: 1.5rem; } `; diff --git a/src/pages/Profile/index.tsx b/src/pages/Profile/index.tsx index aad5dc5b..3cc13a0d 100644 --- a/src/pages/Profile/index.tsx +++ b/src/pages/Profile/index.tsx @@ -1,180 +1,190 @@ import React, { useState, useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { - ProfileContainer, - Header, - StatsContainer, - Stat, - StatNumber, - StatLabel, - PostsContainer, - AddButton, - NoPostWrapper, - Button, + ProfileContainer, + Header, + StatsContainer, + Stat, + StatNumber, + StatLabel, + PostsContainer, + AddButton, + NoPostWrapper, + Button, } from './styles'; -import { OODDFrame } from '../../components/Frame/Frame'; import NavbarProfile from './NavbarProfile'; -import NavBar from '../../components/NavBar'; import ButtonSecondary from './ButtonSecondary'; -import PostItem from '../../components/PostItem'; -import imageBasic from '../../assets/default/defaultProfile.svg'; -import Loading from '../../components/Loading'; -import UserProfile from '../../components/UserProfile'; -import { StyledText } from '../../components/Text/StyledText'; -import Modal from '../../components/Modal'; -import CommentBottomSheet from '../../components/CommentBottomSheet'; -import OptionsBottomSheet from '../../components/BottomSheet/OptionsBottomSheet'; - -import { getUserPostListApi } from '../../apis/post'; -import { getUserInfoApi } from '../../apis/user'; -import { createMatchingApi } from '../../apis/matching'; -import { UserPostSummary } from '../../apis/post/dto'; -import { UserInfoData } from '../../apis/user/dto'; -import button_plus from '../../assets/default/plus.svg'; -import TopBar from '../../components/TopBar'; -import MoreSvg from '../../assets/default/more.svg'; -import BackSvg from '../../assets/arrow/left.svg'; +import { OODDFrame } from '@components/Frame/Frame'; +import NavBar from '@components/NavBar'; +import PostItem from '@components/PostItem'; +import imageBasic from '@assets/default/defaultProfile.svg'; +import Loading from '@components/Loading'; +import UserProfile from '@components/UserProfile'; +import { StyledText } from '@components/Text/StyledText'; +import Modal from '@components/Modal'; +import CommentBottomSheet from '@components/CommentBottomSheet'; +import OptionsBottomSheet from '@components/BottomSheet/OptionsBottomSheet'; +import { getUserPostListApi } from '@apis/post'; +import { getUserInfoApi } from '@apis/user'; +import { createMatchingApi } from '@apis/matching'; +import { UserPostSummary } from '@apis/post/dto'; +import { UserInfoData } from '@apis/user/dto'; +import button_plus from '@assets/default/plus.svg'; +import TopBar from '@components/TopBar'; +import MoreSvg from '@assets/default/more.svg'; +import BackSvg from '@assets/arrow/left.svg'; +import theme from '@styles/theme'; +import { getCurrentUserId } from '@utils/getCurrentUserId'; +import { useRecoilState } from 'recoil'; +import { OtherUserAtom } from '@recoil/util/OtherUser'; const Profile: React.FC = () => { - const { userId } = useParams<{ userId: string }>(); - const profileUserId = Number(userId); - const loggedInUserId = Number(localStorage.getItem('current_user_id')); - - const [isLoading, setIsLoading] = useState(true); - const [posts, setPosts] = useState([]); - const [totalPosts, setTotalPosts] = useState(0); - const [userInfo, setUserInfo] = useState(null); - const [isBottomSheetOpen, setIsBottomSheetOpen] = useState(false); - const [isOptionsBottomSheetOpen, setIsOptionsBottomSheetOpen] = useState(false); // 추가 - const [isModalOpen, setIsModalOpen] = useState(false); - const [modalContent, setModalContent] = useState(''); - const navigate = useNavigate(); - - const isMyPage = loggedInUserId === profileUserId; - - useEffect(() => { - const fetchData = async () => { - try { - const response = await getUserInfoApi(profileUserId); - const postResponse = await getUserPostListApi(1, 10, profileUserId); - setUserInfo(response.data); - setPosts(postResponse.data.post); - setTotalPosts(postResponse.data.totalPostsCount); - } catch (error) { - console.error('데이터 가져오기 실패:', error); - } finally { - setIsLoading(false); - } - }; - fetchData(); - }, [profileUserId]); - - const createMatching = async (message: string) => { - const matchingRequestData = { - requesterId: loggedInUserId, - targetId: profileUserId, - message: message, - }; - - try { - await createMatchingApi(matchingRequestData); - handleModalOpen(`${userInfo?.nickname}님에게 대표 OOTD와 \n한 줄 메세지를 보냈어요!`); - } catch (error: any) { - console.error('매칭 신청 오류:', error); - handleModalOpen(error.response?.data?.message || '매칭 신청에 실패했습니다.'); - } - }; - - const handleModalOpen = (message: string) => { - setIsBottomSheetOpen(false); - setModalContent(message); - setIsModalOpen(true); - }; - - if (isLoading) return ; - - return ( - - - {isMyPage && ( - navigate('/image-select')}> - Add - - )} - - {isMyPage ? ( - - ) : ( - navigate(-1)} - onClickRightButton={() => setIsOptionsBottomSheetOpen(true)} // OptionsBottomSheet 열기 - /> - )} - -
- -
- - {isMyPage ? : } - - - - OOTD - {totalPosts || 0} - - {isMyPage && ( - - 코멘트 - {posts.reduce((sum, post) => sum + (post.postCommentsCount || 0), 0)} - - )} - - 좋아요 - {posts.reduce((sum, post) => sum + (post.postLikesCount || 0), 0)} - - - - - {posts.length > 0 ? ( - posts.map((post) => ) - ) : ( - - - 게시물이 없어요. - - - )} - - - {isMyPage && } - - setIsBottomSheetOpen(false)} - /> - - setIsOptionsBottomSheetOpen(false)} - /> - - {isModalOpen && setIsModalOpen(false)} />} -
-
- ); + const { userId } = useParams<{ userId: string }>(); + const currentUserId = getCurrentUserId(); + + const [isLoading, setIsLoading] = useState(true); + const [posts, setPosts] = useState([]); + const [totalPosts, setTotalPosts] = useState(0); + const [, setUserInfo] = useState(null); + const [isBottomSheetOpen, setIsBottomSheetOpen] = useState(false); // Boolean 변수 + const [isOptionsBottomSheetOpen, setIsOptionsBottomSheetOpen] = useState(false); // Boolean 변수 + const [isModalOpen, setIsModalOpen] = useState(false); // Boolean 변수 + const [modalContent, setModalContent] = useState(''); + const [otherUser, setOtherUser] = useRecoilState(OtherUserAtom); // 상태 변수 + const navigate = useNavigate(); + const isMyPage = currentUserId === otherUser?.id; // Boolean 변수 + + useEffect(() => { + const fetchData = async () => { + try { + const response = await getUserInfoApi(Number(userId)); + setUserInfo(response.data); + setOtherUser(response.data); + const postResponse = await getUserPostListApi(1, 10, Number(userId)); + setPosts(postResponse.data.post); + setTotalPosts(postResponse.data.totalPostsCount); + } catch (error) { + console.error('데이터 가져오기 실패:', error); + } finally { + setIsLoading(false); + } + }; + fetchData(); + }, [userId, setOtherUser]); + + const handleCreateMatching = async (message: string) => { + // 이벤트 핸들러 변수명 수정 + if (!otherUser?.id) { + handleModalOpen('사용자 정보를 찾을 수 없습니다.'); + return; + } + + const matchingRequestData = { + requesterId: currentUserId, + targetId: otherUser.id, + message: message, + }; + + try { + await createMatchingApi(matchingRequestData); + handleModalOpen(`${otherUser.nickname}님에게 대표 OOTD와 \n한 줄 메세지를 보냈어요!`); + } catch (error: any) { + console.error('매칭 신청 오류:', error); + handleModalOpen(error.response?.data?.message || '매칭 신청에 실패했습니다.'); + } + }; + + const handleModalOpen = (message: string) => { + // 이벤트 핸들러 변수명 유지 + setIsBottomSheetOpen(false); + setModalContent(message); + setIsModalOpen(true); + }; + + if (isLoading) return ; + + return ( + + + {isMyPage && ( + navigate('/image-select')}> + Add + + )} + + {isMyPage ? ( + + ) : ( + navigate(-1)} + onClickRightButton={() => setIsOptionsBottomSheetOpen(true)} + /> + )} + +
+ +
+ + {isMyPage ? : } + + + + OOTD + {totalPosts || 0} + + {isMyPage && ( + + 코멘트 + {posts.reduce((sum, post) => sum + (post.postCommentsCount || 0), 0)} + + )} + + 좋아요 + {posts.reduce((sum, post) => sum + (post.postLikesCount || 0), 0)} + + + + + {posts.length > 0 ? ( + posts.map((post) => ) + ) : ( + + + 게시물이 없어요. + + + )} + + + {isMyPage && } + + setIsBottomSheetOpen(false)} // 이벤트 핸들러 변수명 유지 + /> + + setIsOptionsBottomSheetOpen(false)} + /> + + {isModalOpen && setIsModalOpen(false)} />} +
+
+ ); }; export default Profile; diff --git a/src/pages/Profile/styles.tsx b/src/pages/Profile/styles.tsx index 7c093fcc..a680dced 100644 --- a/src/pages/Profile/styles.tsx +++ b/src/pages/Profile/styles.tsx @@ -3,12 +3,12 @@ import styled from 'styled-components'; export const ProfileContainer = styled.div` width: 100%; flex-grow: 1; - margin: 0 auto; /* 중앙 정렬 */ + margin: 0 auto; display: flex; flex-direction: column; align-self: center; - box-sizing: border-box; /* 패딩을 포함한 전체 크기를 설정 */ - overflow-y: auto; /* 내용이 넘칠 경우 스크롤 */ + box-sizing: border-box; + overflow-y: auto; padding-top: 0rem; `; @@ -22,9 +22,9 @@ export const Header = styled.div` export const StatsContainer = styled.div` display: flex; justify-content: space-around; - padding: 0.625rem 0; /* 10px 0 */ - border-top: 1px solid #eee; - border-bottom: 1px solid #eee; + padding: 0.625rem 0; + border-top: 1px solid ${({ theme }) => theme.colors.gray[200]}; + border-bottom: 1px solid ${({ theme }) => theme.colors.gray[200]}; `; export const Stat = styled.div` @@ -34,21 +34,22 @@ export const Stat = styled.div` `; export const StatNumber = styled.div` - color: var(--Color-gray4, #8e8e8e); + color: ${({ theme }) => theme.colors.gray[500]}; + //변경된 컬러시스템에서의 gray4가 800으로 나와있어서 적용해보면 색상이 다르게 나옵니다! + //그래서 최대한 비슷하게 맞춰놨습니다. text-align: center; - font-family: 'Pretendard'; - font-size: 1rem; /* 16px */ + font-size: 1rem; font-style: normal; font-weight: 400; line-height: normal; `; export const StatLabel = styled.div` - color: var(--Color-gray4, #8e8e8e); + color: ${({ theme }) => theme.colors.gray[500]}; text-align: center; font-family: 'Pretendard'; - font-size: 0.75rem; /* 12px */ + font-size: 0.75rem; font-style: normal; font-weight: 300; `; @@ -56,7 +57,7 @@ export const StatLabel = styled.div` export const PostsContainer = styled.div` display: flex; flex-wrap: wrap; - justify-content: space-between; /* 두 개씩 나란히 배치 */ + justify-content: space-between; gap: 15px; cursor: pointer; margin-bottom: 100px; @@ -88,10 +89,9 @@ export const Button = styled.button` margin: 1.25rem auto; height: 3.1rem; text-align: center; - color: #FFF; + color: ${({ theme }) => theme.colors.white}; cursor: pointer; box-sizing: border-box; border-radius: 10px; padding: 10px; - background: var(--Linear1, linear-gradient(93deg, #ff2389 1.22%, #f27575 99.73%)); -`; +background: ${({ theme }) => theme.colors.brand.gradient};`; diff --git a/src/pages/ProfileEdit/index.tsx b/src/pages/ProfileEdit/index.tsx index c3608fe5..e06be596 100644 --- a/src/pages/ProfileEdit/index.tsx +++ b/src/pages/ProfileEdit/index.tsx @@ -12,22 +12,24 @@ import { Username, EmailInput, } from './styles'; -import { StyledText } from '../../components/Text/StyledText'; -import theme from '../../styles/theme'; -import { OODDFrame } from '../../components/Frame/Frame'; +import { StyledText } from '@components/Text/StyledText'; +import theme from '@styles/theme'; +import { OODDFrame } from '@components/Frame/Frame'; import { useNavigate } from 'react-router-dom'; import { ref, uploadBytes, getDownloadURL } from 'firebase/storage'; -import { storage } from '../../config/firebaseConfig'; -import Modal from '../../components/Modal'; - -import TopBar from '../../components/TopBar'; -import back from '../../assets/arrow/left.svg'; -import BottomButton from '../../components/BottomButton'; -import imageBasic from '../../assets/default/defaultProfile.svg'; -import Loading from '../../components/Loading'; -import camera from '../../assets/default/camera.svg'; -import { getUserInfoApi, patchUserInfoApi } from '../../apis/user'; // API import -import { UserInfoData, PatchUserInfoRequest } from '../../apis/user/dto'; // DTO import +import { storage } from '@config/firebaseConfig'; +import Modal from '@components/Modal'; +import { getCurrentUserId } from '@utils/getCurrentUserId'; + +import TopBar from '@components/TopBar'; +import back from '@assets/arrow/left.svg'; +import BottomButton from '@components/BottomButton'; +import imageBasic from '@assets/default/defaultProfile.svg'; +import Loading from '@components/Loading'; +import camera from '@assets/default/camera.svg'; +import { getUserInfoApi, patchUserInfoApi } from '@apis/user'; +import { UserInfoData, PatchUserInfoRequest } from '@apis/user/dto'; + type ExtendedUserInfoData = UserInfoData & { birthDate?: string; // 확장된 속성 @@ -47,20 +49,18 @@ const ProfileEdit: React.FC = () => { const [modalContent, setModalContent] = useState(null); const [isModalVisible, setIsModalVisible] = useState(false); const [uploading, setUploading] = useState(false); // 업로드 상태 관리 - const userId = localStorage.getItem('my_id'); // 사용자 정보 불러오기 useEffect(() => { const getUserInfo = async () => { try { - const storedUserId = Number(localStorage.getItem('my_id')); - if (!storedUserId) { + const currentUserId = getCurrentUserId() + if (!currentUserId) { console.error('User ID not found in localStorage'); return; } - const userId = Number(storedUserId); // 문자열을 숫자로 변환 - const response = await getUserInfoApi(userId); // 사용자 정보 조회 API 호출 + const response = await getUserInfoApi(currentUserId); // 사용자 정보 조회 API 호출 const userInfo: ExtendedUserInfoData = response.data; // 확장된 타입 사용 // 상태 업데이트 @@ -110,8 +110,8 @@ const ProfileEdit: React.FC = () => { const handleSave = async () => { try { - const storedUserId = Number(localStorage.getItem('my_id')); - if (!storedUserId) { + const currentUserId = getCurrentUserId() + if (!currentUserId) { console.error('User ID not found in localStorage'); return; } @@ -128,7 +128,7 @@ const ProfileEdit: React.FC = () => { bio: bio || '', }; - const response = await patchUserInfoApi(payload, storedUserId); + const response = await patchUserInfoApi(payload, currentUserId); if (response.isSuccess) { setModalContent('프로필이 성공적으로 수정되었습니다.'); @@ -137,7 +137,7 @@ const ProfileEdit: React.FC = () => { // 모달 닫힌 후 마이페이지로 이동 setTimeout(() => { handleModalClose(); - navigate(`/profile/${userId}`); + navigate(`/profile/${currentUserId}`); }, 2000); } else { setModalContent('프로필 수정에 실패했습니다.'); @@ -178,42 +178,42 @@ const ProfileEdit: React.FC = () => { - + {nickname || '알수없음'} - + 이름 setName(e.target.value)} /> - + 닉네임 setNickname(e.target.value)} /> - + 소개글 setBio(e.target.value)} /> - + 전화번호 setPhoneNumber(e.target.value)} /> - + 생년월일 setBirthDate(e.target.value)} /> - + 이메일 setEmail(e.target.value)} /> diff --git a/src/pages/ProfileEdit/styles.tsx b/src/pages/ProfileEdit/styles.tsx index 685b8cbb..ef11e89b 100644 --- a/src/pages/ProfileEdit/styles.tsx +++ b/src/pages/ProfileEdit/styles.tsx @@ -14,23 +14,23 @@ export const ProfilePicWrapper = styled.div` display: flex; flex-direction: column; align-items: center; - margin-bottom: 10px; /* 20px */ + margin-bottom: 10px; position: relative; `; export const Label = styled.label` - font-size: 0.875rem; /* 14px */ - color: #333; + font-size: 0.875rem; + color: ${({ theme }) => theme.colors.gray[700]}; `; export const Input = styled.input` - width: 100%; /* Row의 padding에 맞춰 꽉 채우기 */ - padding: 25px; /* 10px padding */ - margin: 10px 0; /* 위아래 간격 조정 */ + width: 100%; + padding: 25px; + margin: 10px 0; border: 0px; box-sizing: border-box; border-radius: 10px; - background-color: #f0f0f0; /* 박스 내부 회색 배경 */ + background-color: ${({ theme }) => theme.colors.gray[100]}; text-align: left; `; @@ -43,20 +43,20 @@ export const Button = styled.button` height: 1.7rem; padding: 0.3rem; border-radius: 50%; - background-color: white; - box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.2); /* 그림자 효과 */ - border: 1px solid #ddd; /* 아이콘 테두리 */ + background-color: ${({ theme }) => theme.colors.white}; + box-shadow: 0px 2px 4px ${({ theme }) => `${theme.colors.black}33`}; + border: 1px solid ${({ theme }) => theme.colors.gray[300]}; cursor: pointer; `; export const ProfilePic = styled.div` - width: 7.25rem; /* 116px */ - height: 7.25rem; /* 116px */ + width: 7.25rem; + height: 7.25rem; flex-shrink: 0; border-radius: 50%; overflow: hidden; - margin-top: 2.125rem; /* 34px */ - margin-bottom: 15px; /* 20px */ + margin-top: 2.125rem; + margin-bottom: 15px; img { width: 100%; @@ -69,25 +69,25 @@ export const CameraIcon = styled.img``; export const Row = styled.div` display: flex; - flex-direction: column; /* 세로 배치 */ + flex-direction: column; align-items: stretch; width: 100%; - margin-top: 0px; /* Row 간격 10px */ - padding: 0px 20px; /* 좌우 여백 20px */ + margin-top: 0px; + padding: 0px 20px; ${Label} { - width: 6.25rem; /* 100px */ + width: 6.25rem; } `; export const EmailInput = styled.input` margin-bottom: 120px; - width: 100%; /* Row의 padding에 맞춰 꽉 채우기 */ - padding: 25px; /* 10px padding */ + width: 100%; + padding: 25px; border: 0px; box-sizing: border-box; border-radius: 10px; - background-color: #f0f0f0; /* 박스 내부 회색 배경 */ + background-color: ${({ theme }) => theme.colors.gray[100]}; text-align: left; `; @@ -98,12 +98,12 @@ export const FileInput = styled.input` export const UserInfo = styled.div``; export const Username = styled.button` - color: #000; + color: ${({ theme }) => theme.colors.black}; font-family: Pretendard; font-size: 22px; font-style: normal; font-weight: 700; - line-height: 136.4%; /* 30.008px */ + line-height: 136.4%; letter-spacing: -0.427px; `; diff --git a/src/pages/verification/index.tsx b/src/pages/verification/index.tsx index fcb2d5f3..794106b2 100644 --- a/src/pages/verification/index.tsx +++ b/src/pages/verification/index.tsx @@ -18,6 +18,7 @@ import { StyledText } from '../../components/Text/StyledText'; import TopBar from '../../components/TopBar'; import back from '../../assets/arrow/left.svg'; import { useNavigate } from 'react-router-dom'; +import theme from '@styles/theme'; const Verification: React.FC = () => { const navigate = useNavigate(); // useNavigate 훅 사용 @@ -108,7 +109,9 @@ const Verification: React.FC = () => { - <StyledText $textTheme={{ style: 'body1-medium', lineHeight: 2 }} color="7B7B7B"> + <StyledText + $textTheme={{ style: 'body1-medium' }} + color={theme.colors.gray[600]} > 휴대전화번호로 본인인증하기 </StyledText> @@ -129,7 +132,7 @@ const Verification: React.FC = () => { placeholder="전화번호" value={phone} onChange={handlePhoneChange} - data-theme-style="body2-light" + data-theme-style="body1-medium" data-theme-lineheight="1" /> {isVerificationSent && 인증번호 새로 받기} @@ -141,7 +144,7 @@ const Verification: React.FC = () => { placeholder="인증번호를 입력하세요" value={verificationCode} onChange={handleVerificationCodeChange} - data-theme-style="body2-light" + data-theme-style="body1-medium" data-theme-lineheight="1" /> {formatTime(timer)} diff --git a/src/pages/verification/styles.tsx b/src/pages/verification/styles.tsx index 48e43123..95d7fd89 100644 --- a/src/pages/verification/styles.tsx +++ b/src/pages/verification/styles.tsx @@ -3,22 +3,22 @@ import styled from 'styled-components'; export const VerificationWrapper = styled.div` margin: 0 auto; width: 100%; - flex-grow: 1; /* flexbox에서 공간을 채우도록 설정 */ - padding: 1.25rem; /* 20px */ + flex-grow: 1; + padding: 1.25rem; display: flex; flex-direction: column; align-items: center; `; export const Container = styled.div` - margin-top: 5px; /* TopBar 높이만큼 위로 밀기 */ + margin-top: 5px; padding: 1rem; `; export const Title = styled.h1` font-size: 1.125rem; margin-bottom: 1.25rem; -s`; +`; export const Form = styled.form` display: flex; @@ -28,7 +28,7 @@ export const Form = styled.form` export const Input = styled.input` padding: 0.625rem; - border: 1px solid #ccc; + border: 1px solid ${({ theme }) => theme.colors.gray[300]}; border-radius: 0.25rem; font-size: 1rem; width: 100%; @@ -45,14 +45,16 @@ export const Button = styled.button` padding: 1.25rem; margin-top: 300px; font-size: 0.875rem; - color: #fff; - background-color: ${({ disabled }) => (disabled ? '#ccc' : '#000')}; + color: ${({ theme }) => theme.colors.white}; + background-color: ${({ theme, disabled }) => + disabled ? theme.colors.gray[300] : theme.colors.black}; border: none; border-radius: 0.3125rem; cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')}; &:hover { - background-color: ${({ disabled }) => (disabled ? '#ccc' : '#333')}; + background-color: ${({ theme, disabled }) => + disabled ? theme.colors.gray[300] : theme.colors.gray[700]}; } `; @@ -64,7 +66,7 @@ export const VerificationInputWrapper = styled.div` export const VerificationInput = styled.input` padding: 0.625rem; - border: 1px solid #ccc; + border: 1px solid ${({ theme }) => theme.colors.gray[300]}; border-radius: 0.25rem; font-size: 1rem; width: 100%; @@ -76,7 +78,7 @@ export const Timer = styled.div` top: 50%; transform: translateY(-50%); font-size: 1rem; - color: red; + color: ${({ theme }) => theme.colors.red || theme.colors.brand.primary}; `; export const ResendButton = styled.button` @@ -86,7 +88,7 @@ export const ResendButton = styled.button` transform: translateY(-50%); padding: 0.625rem; font-size: 0.875rem; - color: #000; + color: ${({ theme }) => theme.colors.black}; background: none; border: none; cursor: pointer; @@ -94,29 +96,15 @@ export const ResendButton = styled.button` export const StyledInput = styled.input` padding: 0.625rem; - border: 1px solid #ccc; + border: 1px solid ${({ theme }) => theme.colors.gray[300]}; border-radius: 0.25rem; font-size: 1rem; width: 100%; - - ${({ theme }) => theme.fontStyles['heading1-regular']} - - &.body2-light { - font-family: 'Pretendard Variable'; - font-weight: 300; // light - font-size: 1rem; // 16px - } `; export const StyledVerificationInput = styled.input` padding: 0.625rem; - border: 1px solid #ccc; + border: 1px solid ${({ theme }) => theme.colors.gray[300]}; border-radius: 0.25rem; font-size: 1rem; width: 100%; - - &.body2-light { - font-family: 'Pretendard Variable'; - font-weight: 300; // light - font-size: 1rem; // 16px - } `;