diff --git a/src/App.jsx b/src/App.jsx index 100d5d1..ffa4934 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -59,7 +59,7 @@ function App() { } /> } /> } /> - } /> + } /> } /> } /> } /> diff --git a/src/api/index.js b/src/api/index.js index 71e265d..b30acc4 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -21,7 +21,6 @@ api.interceptors.request.use( async (config) => { try { const tokenData = localStorage.getItem("access_token"); - console.log(tokenData); if (tokenData) { config.headers["Authorization"] = `Bearer ${tokenData}`; } @@ -83,4 +82,4 @@ export const put = (url, data) => api.put(url, data); export const del = (url, data) => api.delete(url, data); // PATCH 요청 -export const patch = (url, data) => api.patch(url, data); +export const patch = (url, data, config = {}) => api.patch(url, data, config); diff --git a/src/api/mypage-controller/profileGet.js b/src/api/mypage-controller/profileGet.js new file mode 100644 index 0000000..2e285c4 --- /dev/null +++ b/src/api/mypage-controller/profileGet.js @@ -0,0 +1,12 @@ +import { get } from "./../index"; +// 장소 데이터 가져오기 +export const getProfile = async () => { + try { + const response = await get("/api/v1/user/mypage/me"); + + return response.data; + } catch (error) { + console.error("getPlaces API 실패:", error); + throw error; + } +}; diff --git a/src/api/mypage-controller/profilePatch.js b/src/api/mypage-controller/profilePatch.js new file mode 100644 index 0000000..da368fb --- /dev/null +++ b/src/api/mypage-controller/profilePatch.js @@ -0,0 +1,25 @@ +import { patch } from "../index"; + +// 프로필 업데이트 +export const patchProfile = async (name, introduction, profileImage) => { + try { + const formData = new FormData(); + formData.append("name", name); + formData.append("introduction", introduction); + if (profileImage) { + formData.append("profileImage", profileImage); + } + console.log(name, introduction, profileImage); + + const response = await patch("/user/my/update-profile", formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }); + + return response.message; + } catch (error) { + console.error("프로필 업데이트 실패:", error); + throw error; + } +}; diff --git a/src/components/ItemModal.jsx b/src/components/ItemModal.jsx index 6c64da0..3f1bc46 100644 --- a/src/components/ItemModal.jsx +++ b/src/components/ItemModal.jsx @@ -37,23 +37,23 @@ const ItemModal = ({ isOpen, onClose, item }) => { if (liked) { // 이미 좋아요 상태라면 → 취소 요청 - setLiked(false); - // const res = await unlikeSpace(spaceId); + //setLiked(false); + const res = await unlikeSpace(spaceId); - // if (res?.data?.isLiked !== undefined) { - // setLiked(res.data.isLiked); - // } else { - // setLiked(false); - // } + if (res?.data?.isLiked !== undefined) { + setLiked(res.data.isLiked); + } else { + setLiked(false); + } } else { - setLiked(true); - // 좋아요 상태가 아니라면 → 생성 요청 - // const res = await likeSpace(spaceId); - // if (res?.data?.isLiked !== undefined) { - // setLiked(res.data.isLiked); - // } else { - // setLiked(true); - // } + //setLiked(true); + //좋아요 상태가 아니라면 → 생성 요청 + const res = await likeSpace(spaceId); + if (res?.data?.isLiked !== undefined) { + setLiked(res.data.isLiked); + } else { + setLiked(true); + } } } catch (error) { console.error("좋아요 토글 에러:", error); diff --git a/src/pages/chatPage/ChatPage.jsx b/src/pages/chatPage/ChatPage.jsx index e78acff..90aa2f6 100644 --- a/src/pages/chatPage/ChatPage.jsx +++ b/src/pages/chatPage/ChatPage.jsx @@ -2,44 +2,21 @@ import React, { useState } from "react"; import styled from "styled-components"; import { useNavigate } from "react-router-dom"; - -// 더미 데이터 -const dummyData = [ - { - id: 1, - name: "홍길동", - lastMessage: "안녕하세요! 오늘 보시나요?", - time: "방금 전", - unreadCount: 3, - profile: "https://via.placeholder.com/40", - buildingImage: "https://via.placeholder.com/50", - timestamp: new Date(2024, 7, 15, 10, 20), // 정렬용 timestamp - }, - { - id: 2, - name: "김철수", - lastMessage: "네 내일 괜찮습니다.", - time: "1시간 전", - unreadCount: 0, - profile: "https://via.placeholder.com/40", - buildingImage: "https://via.placeholder.com/50", - timestamp: new Date(2024, 7, 15, 9, 10), - }, -]; - +import { useChatStore } from "../../stores/chatStore"; const ChatPage = () => { const navigate = useNavigate(); const [activeSort, setActiveSort] = useState("newest"); + const { chats, setUnreadCount } = useChatStore(); // 정렬 함수 const getSortedList = () => { switch (activeSort) { case "newest": // 최신순 - return [...dummyData].sort((a, b) => b.timestamp - a.timestamp); + return [...chats].sort((a, b) => b.timestamp - a.timestamp); case "oldest": // 오래된 순 - return [...dummyData].sort((a, b) => a.timestamp - b.timestamp); + return [...chats].sort((a, b) => a.timestamp - b.timestamp); default: - return dummyData; + return chats; } }; @@ -101,7 +78,10 @@ const ChatPage = () => { {sortedList.map((item) => ( navigate(`/chat/room/${item.id}`)} + onClick={() => { + navigate(`/chat/room/${item.id}`); + setUnreadCount(item.id); // ✅ 함수 호출 + }} > diff --git a/src/pages/chatPage/ChatRoomBuyPage.jsx b/src/pages/chatPage/ChatRoomBuyPage.jsx index 6335299..2042adf 100644 --- a/src/pages/chatPage/ChatRoomBuyPage.jsx +++ b/src/pages/chatPage/ChatRoomBuyPage.jsx @@ -240,7 +240,7 @@ const ChatRoomBuyPage = () => { setStep((prev) => prev + 1); } if (step === 2) { - navigate("/chat/room/final"); + navigate(`/chat/room/final/${id}`); } }} > diff --git a/src/pages/chatPage/ChatRoomFinalPage.jsx b/src/pages/chatPage/ChatRoomFinalPage.jsx index 6320f6f..20bd51f 100644 --- a/src/pages/chatPage/ChatRoomFinalPage.jsx +++ b/src/pages/chatPage/ChatRoomFinalPage.jsx @@ -1,9 +1,11 @@ -import { useNavigate } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import styled from "styled-components"; import LeftArrowIcon from "../../assets/icons/leftArrowIconBlack.svg"; import BuyCheckIcon from "../../assets/icons/buyCheckIcon.svg"; - +import useHistoryStore from "../../stores/useHistoryStore"; const ChatRoomFinalPage = () => { + const { reverseAddReservation, moveToOngoing } = useHistoryStore(); + const { roomId } = useParams(); const navigate = useNavigate(); return ( @@ -26,7 +28,9 @@ const ChatRoomFinalPage = () => { { - navigate("/home"); + navigate("/home", { state: { roomId } }); + reverseAddReservation(); + moveToOngoing(); }} > 홈으로 가기 diff --git a/src/pages/chatPage/ChatRoomPage.jsx b/src/pages/chatPage/ChatRoomPage.jsx index 2f96aa4..9a0c6b2 100644 --- a/src/pages/chatPage/ChatRoomPage.jsx +++ b/src/pages/chatPage/ChatRoomPage.jsx @@ -4,7 +4,170 @@ import { useState, useEffect, useRef } from "react"; import LeftArrowIcon from "../../assets/icons/leftArrowIconBlack.svg"; import GrayIcon from "../../assets/icons/grayMarker.svg"; import SendIcon from "../../assets/icons/sendIcon.svg"; - +import useHistoryStore from "../../stores/useHistoryStore"; +const chatRoomsDummy = [ + { + room_id: 10, + messages: [ + { + id: 1, + text: "안녕하세요! 혹시 지금 거래 가능할까요?", + sender: "other", + time: "오후 5:35", + }, + { id: 2, text: "네 가능합니다 😀", sender: "me", time: "오후 5:36" }, + { + id: 3, + text: "좋아요! 위치는 어디신가요?", + sender: "other", + time: "오후 5:37", + }, + { + id: 4, + text: "대구 북구 대학로 71 2층입니다.", + sender: "me", + time: "오후 5:38", + }, + { + id: 5, + text: "알겠습니다. 근처 카페에서 만나는 건 어떨까요?", + sender: "other", + time: "오후 5:39", + }, + { + id: 6, + text: "네 좋아요. 혹시 몇 시쯤 가능하세요?", + sender: "me", + time: "오후 5:40", + }, + { + id: 7, + text: "6시쯤 괜찮으신가요?", + sender: "other", + time: "오후 5:41", + }, + { + id: 8, + text: "네 6시 좋습니다!", + sender: "me", + time: "오후 5:41", + }, + { + id: 9, + text: "물건 상태는 괜찮은가요?", + sender: "other", + time: "오후 5:42", + }, + { + id: 10, + text: "네, 사용감은 거의 없고 깨끗합니다 🙂", + sender: "me", + time: "오후 5:43", + }, + { + id: 11, + text: "오 상태가 정말 좋은가 보네요 👍", + sender: "other", + time: "오후 5:46", + }, + { + id: 12, + text: "감사합니다 ㅎㅎ 바로 거래 진행하실까요?", + sender: "me", + time: "오후 5:47", + }, + { + id: 13, + text: "네 그럼 오늘 6시에 뵙겠습니다.", + sender: "other", + time: "오후 5:48", + }, + ], + }, + { + room_id: 11, + messages: [ + { + id: 1, + text: "안녕하세요! 거래 가능할까요?", + sender: "other", + time: "오후 4:30", + }, + { + id: 2, + text: "네, 가능합니다 😀", + sender: "me", + time: "오후 4:31", + }, + { + id: 3, + text: "좋아요! 위치는 어디신가요?", + sender: "other", + time: "오후 4:32", + }, + { + id: 4, + text: "대구 북구 대학로 71 2층입니다.", + sender: "me", + time: "오후 4:33", + }, + { + id: 5, + text: "근처 카페에서 만나는 건 어떨까요?", + sender: "other", + time: "오후 4:34", + }, + { + id: 6, + text: "좋아요, 몇 시쯤 가능하세요?", + sender: "me", + time: "오후 4:35", + }, + { + id: 7, + text: "6시쯤 괜찮으신가요?", + sender: "other", + time: "오후 4:36", + }, + { + id: 8, + text: "네, 6시 좋습니다!", + sender: "me", + time: "오후 4:37", + }, + { + id: 9, + text: "물건 상태는 괜찮은가요?", + sender: "other", + time: "오후 4:38", + }, + { + id: 10, + text: "네, 거의 새것과 같아요 🙂", + sender: "me", + time: "오후 4:39", + }, + { + id: 11, + text: "오 상태가 좋군요 👍", + sender: "other", + time: "오후 4:40", + }, + { + id: 12, + text: "감사합니다 ㅎㅎ 바로 진행할까요?", + sender: "other", + time: "오후 4:41", + }, + { + id: 13, + text: "내일 괜찮습니다.", + sender: "me", + time: "오후 4:42", + }, + ], + }, +]; const dummyPlace = { id: 1, price: 100000, @@ -16,110 +179,38 @@ const dummyPlace = { }; const ChatRoomPage = () => { + const { addReservation } = useHistoryStore(); + const [chatRooms, setChatRooms] = useState(chatRoomsDummy); const { id } = useParams(); const navigate = useNavigate(); const location = useLocation(); const { item } = location.state || {}; // state 없으면 {} 처리 const chatEndRef = useRef(null); - const [messages, setMessages] = useState([ - { - id: 1, - text: "안녕하세요! 혹시 지금 거래 가능할까요?", - sender: "other", - time: "오후 5:35", - }, - { id: 2, text: "네 가능합니다 😀", sender: "me", time: "오후 5:36" }, - { - id: 3, - text: "좋아요! 위치는 어디신가요?", - sender: "other", - time: "오후 5:37", - }, - { - id: 4, - text: "대구 북구 대학로 71 2층입니다.", - sender: "me", - time: "오후 5:38", - }, - { - id: 5, - text: "알겠습니다. 근처 카페에서 만나는 건 어떨까요?", - sender: "other", - time: "오후 5:39", - }, - { - id: 6, - text: "네 좋아요. 혹시 몇 시쯤 가능하세요?", - sender: "me", - time: "오후 5:40", - }, - { - id: 7, - text: "6시쯤 괜찮으신가요?", - sender: "other", - time: "오후 5:41", - }, - { - id: 8, - text: "네 6시 좋습니다!", - sender: "me", - time: "오후 5:41", - }, - { - id: 9, - text: "물건 상태는 괜찮은가요?", - sender: "other", - time: "오후 5:42", - }, - { - id: 10, - text: "네, 사용감은 거의 없고 깨끗합니다 🙂", - sender: "me", - time: "오후 5:43", - }, - { - id: 11, - text: "혹시 실물 사진도 받아볼 수 있을까요?", - sender: "other", - time: "오후 5:44", - }, - { - id: 12, - text: "네 잠시만요, 지금 보내드릴게요.", - sender: "me", - time: "오후 5:44", - }, - { - id: 13, - text: "[사진]", - sender: "me", - time: "오후 5:45", - }, - { - id: 14, - text: "오 상태가 정말 좋네요 👍", - sender: "other", - time: "오후 5:46", - }, - { - id: 15, - text: "감사합니다 ㅎㅎ 바로 거래 진행하실까요?", - sender: "me", - time: "오후 5:47", - }, - { - id: 16, - text: "네 그럼 오늘 6시에 뵙겠습니다.", - sender: "other", - time: "오후 5:48", - }, - ]); + // room_id 기준으로 메시지 가져오기 + const roomMessages = + chatRooms.find( + (room) => room.room_id === Number(id) // id 문자열이면 Number 처리 + )?.messages || []; + + console.log(roomMessages); + const [messages, setMessages] = useState(roomMessages); const [input, setInput] = useState(""); useEffect(() => { scrollToBottom(); }, [messages]); + useEffect(() => { + if (id) { + const roomId = Number(id); + const exists = chatRooms.some((room) => room.room_id === roomId); + + // ✅ 채팅방이 존재하지 않으면 예약 내역 +1 + if (!exists) { + addReservation(); + } + } + }, []); const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes().toString().padStart(2, "0"); diff --git a/src/pages/myPage/EditProfilePage.jsx b/src/pages/myPage/EditProfilePage.jsx index 9e053d6..6055f85 100644 --- a/src/pages/myPage/EditProfilePage.jsx +++ b/src/pages/myPage/EditProfilePage.jsx @@ -3,6 +3,7 @@ import styled from "styled-components"; import { useLocation, useNavigate } from "react-router-dom"; import LeftArrowIcon from "../../assets/icons/leftArrowIconBlack.svg"; import EditProfileIcon from "../../assets/icons/editProfileIcon.svg"; +import { patchProfile } from "../../api/mypage-controller/profilePatch"; const EditProfilePage = () => { const { state } = useLocation(); // MyPage에서 넘겨받은 값 const navigate = useNavigate(); @@ -24,13 +25,37 @@ const EditProfilePage = () => { } }; - const handleSubmit = (e) => { + // Base64 -> File 변환 + const dataURLtoFile = (dataurl, filename) => { + const arr = dataurl.split(","); + const mime = arr[0].match(/:(.*?);/)[1]; + const bstr = atob(arr[1]); + let n = bstr.length; + const u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); + } + return new File([u8arr], filename, { type: mime }); + }; + + const handleSubmit = async (e) => { e.preventDefault(); + console.log("Sdfsdf"); + let resultProfile = profileImage; + if (resultProfile != state?.profileImage) { + resultProfile = dataURLtoFile(profileImage, "profile.png"); + } - // TODO: 서버로 PATCH 요청 or Zustand 업데이트 - console.log("저장 데이터:", { name, introduction, profileImage }); + console.log(resultProfile); + try { + const response = await patchProfile(name, introduction, resultProfile); + console.log("프로필 업데이트 성공:", response); - navigate(-1); // 저장 후 마이페이지로 이동 + navigate(-1); // 저장 후 이전 페이지로 이동 + } catch (error) { + console.error("프로필 업데이트 실패:", error); + alert("프로필 업데이트에 실패했습니다."); + } }; return ( diff --git a/src/pages/myPage/MyPage.jsx b/src/pages/myPage/MyPage.jsx index bd3e510..23eba89 100644 --- a/src/pages/myPage/MyPage.jsx +++ b/src/pages/myPage/MyPage.jsx @@ -1,19 +1,11 @@ import styled from "styled-components"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import UpArrowIcon from "../../assets/icons/upArrowIcon.svg"; // svg 불러오기 import Star from "../../assets/icons/star.svg"; import GrayMarker from "../../assets/icons/grayMarker.svg"; -import useSignupStore from "../../stores/useSignupStore"; -const dummyHost = { - id: 1, - name: "홍길동", - profileImage: "https://via.placeholder.com/150", - introduction: "안녕하세요! 성실하게 임대 관리해드립니다.", - listingCount: 5, - host: true, -}; - +import { getProfile } from "../../api/mypage-controller/profileGet"; +import useHistoryStore from "../../stores/useHistoryStore"; // 더미 데이터 (10개) const dummyData = [ { @@ -102,12 +94,42 @@ const MyPage = () => { const [likeOpen, setLikeOpen] = useState(false); const [hostOpen, setHostOpen] = useState(false); const [selectedItem, setSelectedItem] = useState(null); - const [reserveCount, setReserveCount] = useState(2); - const [progressCount, setProgressCount] = useState(0); - const [doneCount, setDoneCount] = useState(0); - const { userType } = useSignupStore(); - const isHost = userType === "host"; + const { reservationHistory, ongoingHistory, completedHistory } = + useHistoryStore(); + const [userRole, setUserRole] = useState(""); + const isHost = userRole === "HOST"; + const [dummyHost, setDummyHost] = useState({ + id: 0, + name: "", + profileImage: "https://via.placeholder.com/150", + introduction: "", + }); + + useEffect(() => { + const fetchProfile = async () => { + try { + const response = await getProfile(); + + console.log("프로필 불러오기 성공:", response.data); + setUserRole(response.data.userRole); + // 서버 데이터 → dummyHost 구조로 변환 + const transformed = { + id: response.data.userId, + name: response.data.name, + profileImage: + response.data.profileImageUrl || "https://via.placeholder.com/150", + introduction: response.data.introduce, + }; + + setDummyHost(transformed); + // 상태 저장 등 원하는 작업 + } catch (error) { + console.error("프로필 불러오기 실패:", error); + } + }; + fetchProfile(); + }, []); return ( @@ -157,13 +179,13 @@ const MyPage = () => {
- 예약 내역 {reserveCount}건 + 예약 내역 {reservationHistory}건 - 진행중 내역 {progressCount}건 + 진행중 내역 {ongoingHistory}건 - 완료 내역 {doneCount}건 + 완료 내역 {completedHistory}건
diff --git a/src/stores/chatStore.js b/src/stores/chatStore.js new file mode 100644 index 0000000..348e2e7 --- /dev/null +++ b/src/stores/chatStore.js @@ -0,0 +1,45 @@ +import { create } from "zustand"; + +export const useChatStore = create((set) => ({ + chats: [ + { + id: 10, + name: "김태민", + lastMessage: "네 그럼 오늘 6시에 뵙겠습니다.", + time: "방금 전", + unreadCount: 1, + profile: "https://via.placeholder.com/40", + buildingImage: "https://via.placeholder.com/50", + timestamp: new Date(2024, 7, 15, 10, 20), // 정렬용 timestamp + }, + { + id: 11, + name: "김지범", + lastMessage: "네 내일 괜찮습니다.", + time: "1시간 전", + unreadCount: 0, + profile: "https://via.placeholder.com/40", + buildingImage: "https://via.placeholder.com/50", + timestamp: new Date(2024, 7, 15, 9, 10), + }, + ], + setChats: (newChats) => set({ chats: newChats }), + addChat: (chat) => set((state) => ({ chats: [...state.chats, chat] })), + updateChat: (id, updated) => + set((state) => ({ + chats: state.chats.map((c) => (c.id === id ? { ...c, ...updated } : c)), + })), + removeChat: (id) => + set((state) => ({ + chats: state.chats.filter((c) => c.id !== id), + })), + // 읽지 않은 메시지 카운트 초기화 (id 기준) + setUnreadCount: (id) => + set((state) => ({ + chats: state.chats.map((chat) => + chat.id === id && chat.unreadCount !== 0 + ? { ...chat, unreadCount: 0 } + : chat + ), + })), +})); diff --git a/src/stores/useHistoryStore.js b/src/stores/useHistoryStore.js index aec7c1d..37cc43b 100644 --- a/src/stores/useHistoryStore.js +++ b/src/stores/useHistoryStore.js @@ -2,45 +2,35 @@ import { create } from "zustand"; const useHistoryStore = create((set) => ({ // 예약 내역 - reservationHistory: [], + reservationHistory: 2, // 진행중 내역 - ongoingHistory: [], + ongoingHistory: 0, // 완료 내역 - completedHistory: [], + completedHistory: 0, - // 추가 (예: 예약 추가) - addReservation: (item) => + // 예약 추가 (+1) + addReservation: () => set((state) => ({ - reservationHistory: [...state.reservationHistory, item], + reservationHistory: state.reservationHistory + 1, + })), + reverseAddReservation: () => + set((state) => ({ + reservationHistory: state.reservationHistory - 1, })), - // 상태 이동 (예약 → 진행중) - moveToOngoing: (id) => - set((state) => { - const item = state.reservationHistory.find((r) => r.id === id); - return { - reservationHistory: state.reservationHistory.filter((r) => r.id !== id), - ongoingHistory: [ - ...state.ongoingHistory, - { ...item, status: "ongoing" }, - ], - }; - }), + // 예약 → 진행중 (+1) + moveToOngoing: () => + set((state) => ({ + ongoingHistory: state.ongoingHistory + 1, + })), - // 상태 이동 (진행중 → 완료) - completeReservation: (id) => - set((state) => { - const item = state.ongoingHistory.find((r) => r.id === id); - return { - ongoingHistory: state.ongoingHistory.filter((r) => r.id !== id), - completedHistory: [ - ...state.completedHistory, - { ...item, status: "completed" }, - ], - }; - }), + // 진행중 → 완료 (+1) + completeReservation: () => + set((state) => ({ + completedHistory: state.completedHistory + 1, + })), })); export default useHistoryStore;