Skip to content

Commit 4b6aff0

Browse files
manNomi한만욱
andauthored
feat : Mentor 페이지 API 붙이는 작업 진행했습니다 + customConfirm 구현 (#202)
* feat : serverFetch 구현했습니다 * fix: 컴포넌트 비삭제 된 요소 삭제했습니다 * refactor : MyMentoSection 분리했습니다 * refactor : MyMentorSection 으로 수정했습니다 * refactor : Mentor 로 통일 및 폴더구조 개선했습니다 * refactor : 컴포넌트 영역의 하드코딩된 값 모두 테일윈드 값으로 수정했습니다 * refactor: body 단일화·any 제거·HttpError 추가 했습니다 * refactor : decode 함수 분리했습니다 * fix : 일관성 위해 Buffer -> atob로 수정했습니다 * refactor : MentorCard 분리했습니다 * refactor : MentorCard 분리했습니다 * refactor : component -> ui 로 수정했습니다 * refactor : Article Card 분리했습니다: * refactor : ChannelSelect 숨겨진 input 삭제했습니다 * refactor : MentorApply 삭제 및 clsx 추가했습니다 * feat : 멘토 api 폴더 구조 정립했습니다 * feat : MentorFindSection 무한스크롤 구현했습니다 * feat : useGetMyMentorProfile 구현했습니다 * feat : 특정 멘토 페이지 구현했습니다 * feat : useGetArticle 연결했습니다 * feat : ArticlePanel useDeleteArticle 연결했습니다 * feat : MentoModify 구현했습니다 * fix : 멘토 챗 카드 디자인수정했습니다 * feat : MentorApplyCountModal 구현했습니다 " * feat : RootModal 및 신규 신청 알림 구현했습니다 * fix : 디자인 및 RootModal 구조 수정했습니다 * feat : RootModal의 클라이언트 , 서버 모달 나눴습니다 * feat : zustnad로 커스텀 confirm 함수 구현했습니다 * fix : IconConfirmModal 수정 두개 메시지 받도록 수정및 customConfirm 으로 수정했습니다 * fix : ClientModal에 useclient 추가했습니다 * refactor : PR 코멘트 내용이었던 set함수와 controller 의 증복을 제거했습니다 * refactor : PR 코멘트 반영했습니다 Link 임포트가 잘못된 부분 제거했습니다 * refactor : 불필요 컴포넌트 삭제 했습니다 * refactor : PR 코멘트 반영했습니다 likeicon 오타 수정했습니다 * fix : serverFetch 글로벌 세마포어를 key로 관리하도록 수정했습니다 * Refactor : 멘토페이지 PR 코멘트 사항 반영 및 불필요한 컴포넌트 제거했습니다 (#201) * refactor : MyMentoSection 분리했습니다 * refactor : MyMentorSection 으로 수정했습니다 * refactor : Mentor 로 통일 및 폴더구조 개선했습니다 * refactor : 컴포넌트 영역의 하드코딩된 값 모두 테일윈드 값으로 수정했습니다 * refactor : component -> ui 로 수정했습니다 * refactor : Article Card 분리했습니다: * refactor : ChannelSelect 숨겨진 input 삭제했습니다 * refactor : MentorApply 삭제 및 clsx 추가했습니다 * feat : 멘토 api 폴더 구조 정립했습니다 --------- Co-authored-by: 한만욱 <manwook-han@hanman-ug-ui-MacBookPro.local> --------- Co-authored-by: 한만욱 <manwook-han@hanman-ug-ui-MacBookPro.local>
1 parent ebcc5cf commit 4b6aff0

45 files changed

Lines changed: 1046 additions & 418 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package-lock.json

Lines changed: 31 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
"react-linkify": "^1.0.0-alpha",
3131
"tailwind-merge": "^3.0.2",
3232
"tailwindcss-animate": "^1.0.7",
33-
"zod": "^4.0.5"
33+
"zod": "^4.0.5",
34+
"zustand": "^5.0.6"
3435
},
3536
"devDependencies": {
3637
"@svgr/webpack": "^8.1.0",

public/svgs/mentor/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import IconDirectionRight from "./direction-right.svg";
88
import IconDirectionUp from "./direction-up.svg";
99
import IconGraduation from "./graduation.svg";
1010
import IconLikeFill from "./like-fill.svg";
11-
import IconLkieNotFill from "./like-not-fill.svg";
11+
import IconLikeNotFill from "./like-not-fill.svg";
1212
import IconModify from "./modify.svg";
1313
import IconPencil from "./pencil.svg";
1414
import IconPlus from "./plus.svg";
@@ -37,7 +37,7 @@ export {
3737
IconSetting,
3838
IconCamera,
3939
IconDirectMessage,
40-
IconLkieNotFill,
40+
IconLikeNotFill,
4141
IconLikeFill,
4242
IconUnSmile,
4343
IconSmile,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import useFetch from "@/utils/apiUtils";
2+
3+
const useDeleteArticle = (articleId: number | null) => {
4+
const { loading, error, fetchData } = useFetch<void>();
5+
6+
const deleteArticle = () => {
7+
if (articleId === null) return;
8+
9+
fetchData({
10+
method: "delete",
11+
url: `/news/${articleId}`,
12+
body: undefined,
13+
isToken: true,
14+
});
15+
};
16+
17+
return { deleteArticle, loading, error };
18+
};
19+
20+
export default useDeleteArticle;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { useEffect, useState } from "react";
2+
3+
import useFetch from "@/utils/apiUtils";
4+
5+
import { ArticleResponse } from "../type/response";
6+
7+
/* ---------- 타입 ---------- */
8+
9+
interface ArticleListResponse {
10+
news: ArticleResponse[]; // 최대 5개
11+
}
12+
13+
const useGetArticleList = (userId: number | null) => {
14+
const { result, loading, error, fetchData } = useFetch<ArticleListResponse>();
15+
16+
const [articleList, setArticleList] = useState<ArticleResponse[]>([]);
17+
18+
/* 페이지 변경 시 데이터 요청 */
19+
useEffect(() => {
20+
if (userId === null) return;
21+
22+
fetchData({
23+
method: "get",
24+
url: `/news?site-user-id=${userId}`,
25+
body: undefined,
26+
isToken: true,
27+
});
28+
}, [userId, fetchData]);
29+
30+
/* 응답 처리 */
31+
useEffect(() => {
32+
if (result) {
33+
setArticleList(result.data.news);
34+
}
35+
}, [result]);
36+
37+
return { articleList, loading, error };
38+
};
39+
40+
export default useGetArticleList;

src/api/article/type/response.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export interface ArticleResponse {
2+
title: string;
3+
description: string; // 최대 30자, 멘토가 작성
4+
url: string;
5+
thumbnailUrl: string;
6+
updatedAt: string;
7+
isLiked: boolean;
8+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { useEffect, useState } from "react";
2+
3+
import useFetch from "@/utils/apiUtils";
4+
5+
import { MentorCardDetail } from "../type/response";
6+
7+
const useGetMentorDetailPage = (mentorId: number | null) => {
8+
const { result, loading, error, fetchData } = useFetch<MentorCardDetail>();
9+
const [mentorDetail, setMentorDetail] = useState<MentorCardDetail | null>(null);
10+
11+
useEffect(() => {
12+
if (mentorId === null) return;
13+
// 멘토 상세 정보 요청
14+
fetchData({
15+
method: "get",
16+
url: `/mentors/${mentorId}`,
17+
body: null,
18+
isToken: true,
19+
});
20+
}, [mentorId, fetchData]);
21+
22+
useEffect(() => {
23+
if (result) {
24+
setMentorDetail(result.data);
25+
}
26+
}, [result]);
27+
28+
return { mentorDetail, loading, error };
29+
};
30+
31+
export default useGetMentorDetailPage;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { useEffect, useState } from "react";
2+
3+
import useFetch from "@/utils/apiUtils";
4+
5+
import { MentorCardDetail } from "../type/response";
6+
7+
interface UseGetMentorListProps {
8+
region?: string;
9+
page?: number;
10+
}
11+
12+
interface UseGetMentorListReturn {
13+
mentorList: MentorCardDetail[];
14+
loading: boolean;
15+
error: unknown;
16+
}
17+
18+
interface MentorListResponse {
19+
/** 다음 페이지 번호. 다음 페이지가 없으면 -1 */
20+
nextPageNumber: number;
21+
content: MentorCardDetail[];
22+
}
23+
24+
const OFFSET = 10; // 기본 페이지 크기
25+
26+
const useGetMentorList = ({ region = "전체", page = 0 }: UseGetMentorListProps = {}): UseGetMentorListReturn => {
27+
const { result, loading, error, fetchData } = useFetch<MentorListResponse>();
28+
const [mentorList, setMentorList] = useState<MentorCardDetail[]>([]);
29+
const [nextPageNumber, setNextPageNumber] = useState<number>(0);
30+
31+
useEffect(() => {
32+
// nextPageNumber가 -1이면 더 이상 호출하지 않음
33+
if (nextPageNumber === -1) return;
34+
35+
fetchData({
36+
method: "GET",
37+
url: `/mentor?region=${region}&page=${page}&size=${OFFSET}`,
38+
body: undefined,
39+
isToken: true,
40+
});
41+
42+
// eslint-disable-next-line react-hooks/exhaustive-deps
43+
}, [region, page, nextPageNumber, fetchData]);
44+
45+
useEffect(() => {
46+
if (result) {
47+
// 추가 로딩된 콘텐츠를 기존 배열에 병합
48+
setMentorList((prev) => [...prev, ...result.data.content]);
49+
setNextPageNumber(result.data.nextPageNumber);
50+
}
51+
}, [result]);
52+
53+
return {
54+
mentorList,
55+
loading,
56+
error,
57+
};
58+
};
59+
60+
export default useGetMentorList;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useEffect, useState } from "react";
2+
3+
import useFetch from "@/utils/apiUtils";
4+
5+
import { MentorCardPreview } from "../type/response";
6+
7+
interface UseGetMyMentorDataReturn {
8+
myMentorProfile: MentorCardPreview;
9+
loading: boolean;
10+
error: unknown;
11+
}
12+
13+
const useGetMyMentorProfile = (): UseGetMyMentorDataReturn => {
14+
const { result, loading, error, fetchData } = useFetch<MentorCardPreview>();
15+
const [myMentorProfile, setMyMentorProfile] = useState<MentorCardPreview>({} as MentorCardPreview);
16+
17+
useEffect(() => {
18+
fetchData({
19+
method: "get",
20+
url: "/mentor/my",
21+
body: undefined,
22+
isToken: true,
23+
});
24+
}, [fetchData]);
25+
26+
useEffect(() => {
27+
if (result) {
28+
setMyMentorProfile(result.data);
29+
}
30+
}, [result]);
31+
32+
return {
33+
myMentorProfile,
34+
loading,
35+
error,
36+
};
37+
};
38+
39+
export default useGetMyMentorProfile;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import useFetch from "@/utils/apiUtils";
2+
3+
/* ---------- 타입 ---------- */
4+
export interface ChannelPayload {
5+
type: string;
6+
url: string;
7+
}
8+
9+
export interface PutMyMentorProfileBody {
10+
channels: ChannelPayload[];
11+
passTip: string;
12+
introduction: string;
13+
}
14+
15+
const usePutMyMentorProfile = () => {
16+
const { loading, error, fetchData } = useFetch<void>();
17+
18+
const putMyMentorProfile = (body: PutMyMentorProfileBody) => {
19+
fetchData({
20+
method: "put",
21+
url: "/mentors/me",
22+
body,
23+
isToken: true,
24+
});
25+
};
26+
27+
return { putMyMentorProfile, loading, error };
28+
};
29+
30+
export default usePutMyMentorProfile;

0 commit comments

Comments
 (0)