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
2 changes: 1 addition & 1 deletion src/app/routes/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const Router = () => {
</Route>
<Route path="/mentorship" element={<MentorshipLayout />}>
<Route path="" element={<MentorList />} />
<Route path="info" element={<MentorInfoView />} />
<Route path="info/:mentorId" element={<MentorInfoView />} />
<Route path="suggestion" element={<Suggestion />} />
</Route>
</Routes>
Expand Down
26 changes: 26 additions & 0 deletions src/mentorship/api/MentorViewApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import axiosInstance from '@/global/api/axiosInstance';
import { MentorData } from '@/user/types';

export const getMentorById = async (mentorId: number): Promise<MentorData> => {
const response = await axiosInstance.get(`/v1/mentor/${mentorId}`);
const data = response.data.data;

return {
nickname: data.nickname ?? '',
education: data.education ?? { schoolName: '', major: '' },
organization: data.organization ?? {
name: '',
jobCategory: '',
detailedJob: '',
experience: 0,
},
introduction: data.introduction ?? { title: '', content: '' },
timezone: data.timezone ?? { days: [], startTime: '', endTime: '' },
mentoringField: data.mentoringField ?? [],
hashtags: data.hashtags ?? [],
message: data.message ?? '',
portfolio: data.portfolio ?? { url: '', description: '', size: 0 },
image: data.image ?? { fileName: '', filePath: '' },
count: data.count ?? 0,
};
};
88 changes: 43 additions & 45 deletions src/mentorship/pages/MentorInfoView.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate, useParams } from 'react-router-dom';
import { ArrowLeft } from 'lucide-react';
import { Bookmark } from 'lucide-react';
import { GlobalButton } from '@/global/ui/GlobalButton';

import { getMentorById } from '../api/MentorViewApi';
import { MentorData } from '@/user/types';
import { reverseJobCategoryMapping, reverseDetailedJobMapping } from '@/user/components/Mapping';
import { reverseMentoringFieldMapping, reverseDayMapping } from '@/user/components/Mapping';

import MentorInfoProfile from '../../user/components/profileInfo/MentorInfoProfile';
import MentorInfoBio from '../../user/components/profileInfo/MentorInfoBio';
import MentorInfoFieldsHashtags from '../../user/components/profileInfo/MentorInfoFieldsHashtags';
Expand All @@ -12,50 +17,40 @@ import MentorInfoMessage from '../../user/components/profileInfo/MentorInfoMessa
import MentorInfoPortfolio from '../../user/components/profileInfo/MentorInfoPortfolio';

const MentorInfoView = () => {
const { mentorId } = useParams<{ mentorId: string }>();
const navigate = useNavigate();
const [isBookmarked, setIsBookmarked] = useState(false);
const [mentorData, setMentorData] = useState<MentorData | null>(null);

// 테스트용 멘티 데이터 && localStorage에서 데이터 불러오기 (api 연결시 제거)
const [mentorData, setMentorData] = useState(() => {
const savedData = localStorage.getItem("mentorData");
return savedData ? JSON.parse(savedData) : {
nickname: "바이",
email: "abcd@gmail.com",
education: {
schoolName: "경북대학교",
major: "컴퓨터공학",
},
organization: {
name: "OO주식회사",
role: "브랜드 마케팅/카피라이팅",
experience: 6,
},
count: 716,
image: {
fileName: "",
filePath: "",
},
introduction: {
summary: "브랜드 마케팅에 대한 모든 것을 알려드립니다.",
bio: "안녕하세요!\n저는 OO대학교 경영학과를 졸업하고 현재 XXXX에 다니고 있는 ‘바이’입니다.",
},
availableDays: ["월", "목"],
timezone: {
startTime: { period: "오전", hour: "10", minute: "00"},
endTime: { period: "오후", hour: "18", minute: "00"},
},
mentoringField: ["취업 준비", "커리어 고민"],
hashtags: ["마케팅", "브랜드마케팅", "이직", "취준", "진로고민상담", "면접노하우"],
message: "안녕하세요, 멘티 여러분!\n브랜드 마케팅 경험을 바탕으로 여러분의 성장을 지원하고 싶습니다.",
portfolio: [
{
url: "https://example.com/portfolio1.pdf",
description: "브랜드 마케팅 포트폴리오.pdf",
size: 25,
},
],
useEffect(() => {
const fetchData = async () => {
try {
if (!mentorId || isNaN(Number(mentorId))) return;
const data = await getMentorById(Number(mentorId));

// 매핑
const localizedData: MentorData = {
...data,
mentoringField: data.mentoringField.map((f) => reverseMentoringFieldMapping[f] || f),
timezone: {
...data.timezone,
days: data.timezone.days.map((d) => reverseDayMapping[d] || d),
},
organization: {
...data.organization,
jobCategory: reverseJobCategoryMapping[data.organization.jobCategory] || data.organization.jobCategory,
detailedJob: reverseDetailedJobMapping[data.organization.detailedJob] || data.organization.detailedJob,
},
};

setMentorData(localizedData);
} catch (err) {
console.error("멘토 상세 불러오기 실패:", err);
}
};
});

fetchData();
}, [mentorId]);

useEffect(() => {
const storedBookmark = localStorage.getItem("isBookmarked");
Expand All @@ -68,6 +63,8 @@ const MentorInfoView = () => {
localStorage.setItem("isBookmarked", newBookmarkState.toString());
};

if (!mentorData) return <div className="p-4">Loading...</div>;

return (
<div className="h-screen flex flex-col relative overflow-hidden">

Expand Down Expand Up @@ -98,7 +95,8 @@ const MentorInfoView = () => {
<MentorInfoProfile
nickname={mentorData.nickname}
name={mentorData.organization.name}
role={mentorData.organization.role}
jobCategory={mentorData.organization.jobCategory}
detailedJob={mentorData.organization.detailedJob}
experience={mentorData.organization.experience}
count={mentorData.count}
image={mentorData?.image?.filePath || "/assets/ringusprofile.png"}
Expand All @@ -109,13 +107,13 @@ const MentorInfoView = () => {

{/* 자기소개 */}
<MentorInfoBio
summary={mentorData.introduction.summary}
bio={mentorData.introduction.bio}
title={mentorData.introduction.title}
content={mentorData.introduction.content}
/>

{/* 선호 시간대 */}
<MentorInfoTime
availableDays={mentorData.availableDays}
days={mentorData.timezone.days}
startTime={mentorData.timezone.startTime}
endTime={mentorData.timezone.endTime}
/>
Expand Down
28 changes: 28 additions & 0 deletions src/user/api/MenteeInfoApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { MenteeData } from "../menteetypes";
import axiosInstance from "@/global/api/axiosInstance";

// 멘티 프로필 조회 API
export const getMenteeProfile = async (): Promise<MenteeData> => {
const response = await axiosInstance.get('/v1/mentee/me');
const data = response.data.data;

console.log('멘티 프로필 응답:', response.data);

return {
nickname: data.nickname ?? '',
education: data.education ?? {
schoolName: '',
major: '',
},
introduction: data.introduction ?? '',
image: data.image ?? {
filePath: '',
},
};
};

// 멘티 프로필 수정 API
export const updateMenteeProfile = async (menteeData: MenteeData): Promise<any> => {
const response = await axiosInstance.put('/v1/mentee', menteeData);
return response.data;
};
33 changes: 33 additions & 0 deletions src/user/api/MentorInfoApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { MentorData } from '../types';
import axiosInstance from '@/global/api/axiosInstance';

// 멘토 프로필 조회 API
export const getMentorProfile = async (): Promise<MentorData> => {
const response = await axiosInstance.get('/v1/mentor/me');
const data = response.data.data;

return {
nickname: data.nickname ?? '',
education: data.education ?? { schoolName: '', major: '' },
organization: data.organization ?? {
name: '',
jobCategory: '',
detailedJob: '',
experience: 0,
},
introduction: data.introduction ?? { title: '', content: '' },
timezone: data.timezone ?? { days: [], startTime: '', endTime: '' },
mentoringField: data.mentoringField ?? [],
hashtags: data.hashtags ?? [],
message: data.message ?? '',
portfolio: data.portfolio ?? { url: '', description: '', size: 0 },
image: data.image ?? { fileName: '', filePath: '' },
count: data.count ?? 0,
};
};

// 멘토 프로필 수정 API
export const updateMentorProfile = async (mentorData: MentorData): Promise<any> => {
const response = await axiosInstance.put('/v1/mentor', mentorData);
return response.data;
};
51 changes: 51 additions & 0 deletions src/user/components/Mapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { jobCategoryMapping, detailedJobMapping } from "@/global/components/JobCategories";

// 직무 (한글로)
export const reverseJobCategoryMapping: { [key: string]: string } = Object.entries(jobCategoryMapping)
.reduce((acc, [kor, eng]) => {
acc[eng] = kor;
return acc;
}, {} as { [key: string]: string });


// 세부 직무 (한글로)
export const reverseDetailedJobMapping: { [key: string]: string } = Object.entries(detailedJobMapping)
.reduce((acc, [kor, eng]) => {
acc[eng] = kor;
return acc;
}, {} as { [key: string]: string });



// 멘토링 분야 (영어로)
export const mentoringFieldMapping: Record<string, string> = {
'취업 준비': 'JOB_PREPARATION',
'면접 대비': 'INTERVIEW_PREPARATION',
'업계 동향': 'PRACTICAL_SKILLS',
'커리어 고민': 'PORTFOLIO',
};

// 멘토링 분야 (한글로)
export const reverseMentoringFieldMapping: Record<string, string> = Object.entries(mentoringFieldMapping).reduce((acc, [kor, eng]) => {
acc[eng] = kor;
return acc;
}, {} as Record<string, string>);



// 요일 (영어로)
export const dayMapping: Record<string, string> = {
'월': 'MON',
'화': 'TUE',
'수': 'WED',
'목': 'THU',
'금': 'FRI',
'토': 'SAT',
'일': 'SUN',
};

// 요일 (한글로)
export const reverseDayMapping: Record<string, string> = Object.entries(dayMapping).reduce((acc, [kor, eng]) => {
acc[eng] = kor;
return acc;
}, {} as Record<string, string>);
60 changes: 26 additions & 34 deletions src/user/components/ProfileEdit/EditBio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,67 +3,59 @@ import { MenteeData } from '@/user/menteetypes';

interface EditBioProps {
mentorData?: MentorData;
setMentorData?: React.Dispatch<React.SetStateAction<MentorData>>;
setMentorData?: React.Dispatch<React.SetStateAction<MentorData | null>>;
menteeData?: MenteeData;
setMenteeData?: React.Dispatch<React.SetStateAction<MenteeData>>;
}

const EditBio = ({ mentorData, setMentorData, menteeData, setMenteeData }: EditBioProps) => {

if (!mentorData && !menteeData) return null;

return (
<div className="px-4 my-2">
<div className="font-bold text-[16px] my-4">자기소개</div>

{/* 한줄소개 */}
<textarea
className="w-full px-5 py-4 border-[1px] border-[#D9D7E0] rounded-[10px] text-[14px] resize-none outline-none focus:border-primary-1"
rows={1}
value={
mentorData?.introduction.summary ||
menteeData?.introduction.summary ||
''
}
onChange={(e) => {
if (mentorData && setMentorData) {
{/* 한줄소개 (멘토 전용) */}
{mentorData && setMentorData && (
<textarea
className="w-full px-5 py-4 border-[1px] border-[#D9D7E0] rounded-[10px] text-[14px] resize-none outline-none focus:border-primary-1"
rows={1}
value={mentorData.introduction.title}
onChange={(e) =>
setMentorData({
...mentorData,
introduction: {
...mentorData.introduction,
summary: e.target.value,
title: e.target.value,
},
});
} else if (menteeData && setMenteeData) {
setMenteeData({
...menteeData,
introduction: {
...menteeData.introduction,
summary: e.target.value,
},
});
})
}
}}
placeholder="한줄 소개를 작성하세요."
maxLength={30}
/>
placeholder="한줄 소개를 작성하세요."
maxLength={30}
/>
)}

{/* 자기소개 */}
<textarea
className="w-full min-h-[250px] px-5 py-4 mt-2 border-[1px] border-[#D9D7E0] rounded-[10px] text-[14px] resize-none outline-none focus:border-primary-1"
value={
mentorData?.introduction.bio || menteeData?.introduction.bio || ''
mentorData?.introduction.content ?? menteeData?.introduction ?? ''
}
onChange={(e) => {
const value = e.target.value;

if (mentorData && setMentorData) {
setMentorData({
...mentorData,
introduction: { ...mentorData.introduction, bio: e.target.value },
introduction: {
...mentorData.introduction,
content: value,
},
});
} else if (menteeData && setMenteeData) {
setMenteeData({
...menteeData,
introduction: { ...menteeData.introduction, bio: e.target.value },
introduction: value,
});
}
}}
Expand All @@ -72,13 +64,13 @@ const EditBio = ({ mentorData, setMentorData, menteeData, setMenteeData }: EditB
/>

<p className="text-right text-[#94939B] text-[14px] mt-1">
{mentorData?.introduction.bio.length ||
menteeData?.introduction.bio.length ||
0}{' '}
{(mentorData?.introduction?.content?.length ??
menteeData?.introduction?.length ??
0)}{' '}
/ 500
</p>
</div>
);
};

export default EditBio;
export default EditBio;
Loading