Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/api/axios.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import axios from "axios";
import useAuthStore from "@/store/useAuthStore";

const instance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
Expand All @@ -8,4 +9,33 @@ const instance = axios.create({
}
});

// 인증 절차가 필요한 API는 authInstance로 HTTP요청
const authInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000,
headers: {
"Content-Type": "application/json"
}
});

// authInstance 헤더에 accessToken 추가하는 로직
authInstance.interceptors.request.use(
(config) => {
if (!config.headers) {
config.headers = {};
}

const { accessToken } = useAuthStore.getState();

if (accessToken) {
config.headers.Authorization = `Bearer ${accessToken}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);

export default instance;
export { authInstance };
77 changes: 57 additions & 20 deletions src/components/Profile.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,68 @@
const Profile = () => {
return (
<div className="w-[157px] h-[192px] bg-white rounded-[8px] overflow-hidden relative border border-[#EEEEEE]">
<img
src="/public/icon/dustbin.svg"
alt="Delete"
className="absolute top-3 right-3 w-5 h-5 cursor-pointer"
width={16}
height={16}
/>
import { SetStateAction } from "react";
import { useNavigate } from "react-router-dom";
import { authInstance } from "@/api/axios";
import { VirtualFriend } from "@/types/virtualFreind";

interface ProfileProps {
info: VirtualFriend;
deleteIndex: number;
setVirtualFriendList: React.Dispatch<SetStateAction<VirtualFriend[]>>;
}
const Profile = ({ info, deleteIndex, setVirtualFriendList }: ProfileProps) => {
const navigate = useNavigate();

const handleDelete = async () => {
const res = await authInstance.delete(
`/api/virtual-friend/${info.virtualFriendId}`
);
if (res.status === 200) {
setVirtualFriendList((prevList) =>
prevList.filter((_, index) => index !== deleteIndex)
);
}
};

const handleNavigate = () => {
navigate("/chat", {
state: {
mode: "virtualFriend",
mbti: info.mbti,
id: info.virtualFriendId
}
});
};

return (
<div className="relative h-[192px] w-[157px] overflow-hidden rounded-[8px] border border-[#EEEEEE] bg-white lg:w-[200px]">
<button onClick={handleDelete}>
<img
src="/public/icon/dustbin.svg"
alt="Delete"
className="absolute top-3 right-3 h-5 w-5 cursor-pointer"
width={16}
height={16}
/>
</button>
<img
src="/public/image/ENTP.png"
src={`/public/image/${info.mbti}_profile.png`}
alt="Profile"
className="absolute top-[12px] left-[11px] w-12 h-12 object-cover rounded-full"
className="absolute top-[12px] left-[11px] h-12 w-12 rounded-full object-cover"
/>

<div className="pt-[69px] px-4">
<h2 className="text-base flex items-center space-x-1">
<span className="font-bold">김엠비</span>
<span className="font-light text-gray-600">ENTP</span>
<div className="px-4 pt-[69px]">
<h2 className="flex items-center space-x-1 text-base">
<span className="font-bold">{info.virtualFriendName}</span>
<span className="font-light text-gray-600">{info.mbti}</span>
</h2>
<p className="text-xs text-gray-600 mt-2 font-light">
20대 · 여자 · 직장동료 · 여행 · 사회생활
<p className="mt-2 text-xs font-light text-gray-600">
{info.virtualFriendAge} · {info.virtualFriendSex} ·{" "}
{info.virtualFriendRelationship}
</p>
</div>

<button className="w-full h-[41px] bg-primary-pale text-primary-normal font-bold py-2 text-sm absolute bottom-0">
<button
className="absolute bottom-0 h-[41px] w-full bg-primary-pale py-2 text-sm font-bold text-primary-normal"
onClick={handleNavigate}
>
바로 대화하기
</button>
</div>
Expand Down
27 changes: 27 additions & 0 deletions src/components/ProfileContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Profile from "@/components/Profile";
import { VirtualFriend } from "@/types/virtualFreind";
import { SetStateAction } from "react";

interface ProfileContainerProps {
list: VirtualFriend[];
setVirtualFriendList: React.Dispatch<SetStateAction<VirtualFriend[]>>;
}
const ProfileContainer = ({
list,
setVirtualFriendList
}: ProfileContainerProps) => {
return (
<div className="grid grid-cols-2 gap-7">
{list.map((el, index) => (
<Profile
key={index}
info={el}
deleteIndex={index}
setVirtualFriendList={setVirtualFriendList}
/>
))}
</div>
);
};

export default ProfileContainer;
4 changes: 2 additions & 2 deletions src/components/SubTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ const SubTitle = ({ mode }: { mode: "빠른대화" | "친구목록" }) => {
};

const handleNavigate = () => {
const mode = "virtualFriend";
navigate("/select-info", { state: mode });
const type = mode === "빠른대화" ? "fastFriend" : "virtualFriend";
navigate("/select-info", { state: { type: type } });
};

return (
Expand Down
46 changes: 40 additions & 6 deletions src/pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,66 @@
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import type { AxiosResponse } from "axios";
import { authInstance } from "@/api/axios";
import { VirtualFriend } from "@/types/virtualFreind";
import Banner from "@/components/Banner";
import StrokeBanner from "@/components/StrokeBanner";
import SubTitle from "@/components/SubTitle";
import ChatStartButton from "@/components/button/ChatStartButton";
import Header from "@/components/header/Header";
import useAuthStore from "@/store/useAuthStore";
import ProfileContainer from "@/components/ProfileContainer";

const Home = () => {
const navigate = useNavigate();
const { isLoggedIn } = useAuthStore();
const [virtualFreindList, setVirtualFriendList] = useState<VirtualFriend[]>(
[]
);

useEffect(() => {
const fetchFriendList = async () => {
try {
const res: AxiosResponse<{ data: VirtualFriend[] }> =
await authInstance.get("/api/virtual-friend");
setVirtualFriendList(res.data.data);
} catch (err) {
console.error("친구 목록을 불러오지 못했습니다.", err);
navigate("/error");
}
};

if (isLoggedIn) fetchFriendList();
}, [isLoggedIn]);

return (
<div className="flex flex-col bg-white w-[360px] md:w-[375px] lg:w-[500px]">
<Header/>
<div className="flex w-[360px] flex-col bg-white md:w-[375px] lg:w-[500px]">
<Header />
<main>
<section aria-label="콘텐츠 배너">
<Banner />
</section>
<section className="mt-5 w-full" aria-label="빠른 대화">
<div className="px-[20px] py-[13px] w-full">
<div className="w-full px-[20px] py-[13px]">
<SubTitle mode="빠른대화" />
</div>
<div className="px-5 py-3">
<ChatStartButton mode={"go-fast"}/>
<ChatStartButton mode={"go-fast"} />
</div>
</section>
<section aria-label="친구 목록">
<div className="px-[20px] py-[21px] w-full">
<div className="w-full px-[20px] py-[21px]">
<SubTitle mode="친구목록" />
</div>
<div className="flex justify-center pb-[127px]">
<StrokeBanner />
{isLoggedIn && virtualFreindList.length > 0 ? (
<ProfileContainer
list={virtualFreindList}
setVirtualFriendList={setVirtualFriendList}
/>
) : (
<StrokeBanner />
)}
</div>
</section>
</main>
Expand Down