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
27 changes: 27 additions & 0 deletions ringus.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEArZaqVLRApg80VuUUiOwP3NJiFgxsu0TNgR4aJ6St7YR7mkSQ
hkDBCcJIVSBfqkNDtyyuQci7JrFuAyN+m5zHkbv0X0oFKB8pFKWCGEeUn9UWJXDb
3OAp7MamsEjUhSds9SD8vtHGRhA3tWq8kbUD60YpMpfoTj0EviZj/Ik+LaSshUDE
ryazI6+xIa3uhLNmn+4O2uizu28QAgl2Iq3MYAFhrNrjIYAgRtSh+CAS7lqvtW2d
oDqeWUqh1VE+k+jlYMCB1x5lasOW8VBKA1jUm8t265jBrVKL8lB7Dk2LltMpavg3
YI3UzkXGtrS7AapEf98RTdITbtHLGwZRSFSzzQIDAQABAoIBADImkzNBmGPhhKeO
K26bdMHBbmEcWdWIvS6OedP2OeGjIuqg4HhJAPxGywr8/WZ9ZHTpTbbnvVoibLwv
ZwaiNu4dtS5Kfk7nIcE7R+in1YKP14QdpQedI7+qbMIFaJDHoSz4yMyAYp4fVVju
a2hVObqhXImnZZAlBNfC06REKliY/sbWwzzb2NuhjCG40AROIqVVeNfwuIJZtmFC
h3X2OVdI7DWNn+Fix97n08JAjvx3ov/BSvG3aJpx8ZhO/hPO1yVca4PHpv6Orb43
pXzk/dm0QC9NwweWMBZ0ZwdWvU1gsPIfNnvVGoz4qKc0QYYcLYRQibFe5/ZnGGF7
CGrEz+kCgYEA4OV1VPRquveWCeW1zyX5vOutQl0qm3BYL9oNr5DMcQOM0k5bIUlJ
jHu/M70vYetsgYLfuDXdPb7xqIc9QaX94q39vYZZT+3lxfcgJIPk1Jq5o4XqX5+z
LYmGtDFfA6oBEcTUe//AWU9Mh47/ATmvheoI4JMK5D6oeU4xUS4zvbMCgYEAxZii
jIiVw46UQwCFCc2Qw3tNc6QzgIzS4keAWXOZKoEQz79/mkT9zPZ9NMQykwkrI4Y+
QARqDi7Z2iZmijWXk0IjwI3XIxYx8BALsuO3Pd2CSsN2DS3roTFR4fY+3KvCZ2/W
p/LG9dRvUGjJNm/GV+4Lb0AVZZPZL4EEIoBUCH8CgYEA3FURycSYGErebR3nLGZp
MQS4vy7lwlmjnGYGSH4VPZebzKLFt2vEqeTG41qy5D0xFgVxR1lGQusieNjeU8Xb
YczSrm7Ea4GIPpYpoHyzoPNhcmqNv1eHxNJa7Yj9LGrPF2h+QGnFOfpt4NVg0gOB
CjLKtbJ51jno9sd7m6wnNu8CgYAKJUUrXBP8f4SjUBKEp1Zogxs99c9jKVfmoG85
qJLTuN7JG/cMT8CMVpelLvG91PmvEER/+voLEmLDLbeUHx5SRFIbn+zM82XLArfn
DoQpHAeFmTWlhZcUpriilocxw2vu7bIi23dVxfuVMFwsfF69ww45PxwaJBZBtykk
MFygNwKBgQDbRMJH6IcbOF8HWa2VQp10VnnJaw7P1zYvuIXDgHvizePxNj2OD0zS
hC8tnCBTyhUPSRPs3QIN5PQmzMfFT1HzzQMGqb5TKL4WXR8VO2YNcsvnx60sigMD
MK3GcLuMjKSPZcNNY5x81fLPSxbj2ZTmkyXqp5fIoQ9OxSZ113Litg==
-----END RSA PRIVATE KEY-----
60 changes: 60 additions & 0 deletions src/mentorship/api/fetchMentors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import axiosInstance from '@/global/api/axiosInstance';

export interface Mentor {
mentorId: number;
nickname: string;
imgUrl: string;
introduction: {
title: string;
content: string;
};
organization: {
name: string;
jobCategory: string;
detailedJob: string;
experience: number;
};
message: string;
mentoringCount: number;
}

export interface MentorResponse {
status: number;
message: string;
data: {
content: Mentor[];
sliceInfo: {
size: number;
numberOfElements: number;
last: boolean;
empty: boolean;
cursor: number;
};
};
}

// ✅ 멘토 목록 조회 API
export const fetchMentors = async (cursor?: number , size: number = 5): Promise<MentorResponse> => {
try {
// URL 객체
const nextUrl = new URL(`${import.meta.env.VITE_API_BASE_URL}/v1/mentor`);
// 파라미터 추가
nextUrl.searchParams.append('bookmarked', 'false');
nextUrl.searchParams.append('suggested', 'false');
nextUrl.searchParams.append('commissioned', 'false');
nextUrl.searchParams.append('size', size.toString());
nextUrl.searchParams.append('sort', 'mentorId,DESC');

// 커서 있을때만 추가
if (cursor !== undefined){
nextUrl.searchParams.append('cursor', cursor.toString());
}

//요청
const response = await axiosInstance.get<MentorResponse>(nextUrl.toString());
return response.data;
} catch (error: any) {
console.error('❌ 멘토 목록 조회 실패:', error.response);
throw error.response?.data?.message || '멘토 목록 조회 실패';
}
};
92 changes: 45 additions & 47 deletions src/mentorship/components/mentorlist/MentorItem.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,58 @@
import React from 'react';
import { MentorType } from '@/mentorship/pages/mentorlist/MentorshipList.types';
import React, { forwardRef } from 'react';
import { Mentor } from '../../api/fetchMentors';
import { Bookmark } from 'lucide-react';

interface MentorItemProps {
mentor: MentorType;
mentor: Mentor;
isBookmarked: boolean;
onToggleBookmark: (mName: string) => void;
onToggleBookmark: (nickname: string) => void;
}

const MentorItem: React.FC<MentorItemProps> = ({
mentor,
isBookmarked,
onToggleBookmark,
}) => {
return (
<div className="bg-white p-4 border-b relative w-full">
{/* 북마크 아이콘을 우측 상단에 절대 위치로 배치 */}
<div
className="absolute top-4 right-4 cursor-pointer"
onClick={() => onToggleBookmark(mentor.mName)}
>
<Bookmark
strokeWidth={1}
className={`w-[24px] h-[24px] ${
isBookmarked
? 'stroke-primary-4 fill-primary-4'
: 'stroke-gray-2 fill-none'
}`}
/>
</div>

{/* 프로필 + 멘토 정보 */}
<div className="w-full items-center ml-[15px]">
<div className="flex w-full items-center space-x-[13px]">
<img
src={'/assets/Profile.png'}
alt="프로필"
className="w-[78px] h-[78px] rounded-full object-cover"
const MentorItem = forwardRef<HTMLDivElement, MentorItemProps>(
({ mentor, isBookmarked, onToggleBookmark }, ref) => {
return (
<div ref={ref} className="bg-white p-4 border-b relative w-full">
{/* 북마크 아이콘을 우측 상단에 절대 위치로 배치 */}
<div
className="absolute top-4 right-4 cursor-pointer"
onClick={() => onToggleBookmark(mentor.nickname)}
>
<Bookmark
strokeWidth={1}
className={`w-[24px] h-[24px] ${
isBookmarked
? 'stroke-primary-4 fill-primary-4'
: 'stroke-gray-2 fill-none'
}`}
/>
<div>
<div className="text-[16px] font-bold">{mentor.mName}</div>
<div className="mt-[4px] text-gray-5 text-[12px]">{mentor.mcompany}</div>
<div className="text-gray-5 text-[12px]">{mentor.mJobDetail}</div>
<div className="text-gray-5 text-[12px]">{mentor.mYear}</div>
<div className="flex items-center justify-between gap-[22px] border border-gray-8 bg-gray-8 px-2 py-0.5 rounded-[5px] mt-[4px] h-[30px] w-[130px]">
<span className="text-primary-3 text-[12px]">멘토링 횟수</span>
<span className="text-gray-5 text-[12px]">{mentor.respond}회</span>
</div>

{/* 프로필 + 멘토 정보 */}
<div className="w-full items-center">
<div className="flex w-full items-start space-x-[13px]">
<img
src={'/assets/Profile.png'}
alt="프로필"
className="w-[86px] h-[86px] rounded-full object-cover"
/>
<div>
<div className="text-[20px] font-bold">{mentor.nickname}</div>
<div className="mt-[4px] text-gray-5 text-[14px]">{mentor.organization.name}</div>
<div className="text-gray-5 text-[14px]">{mentor.organization.detailedJob}</div>
<div className="flex items-center justify-between gap-[2px] border border-gray-8 bg-white px-2 py-2 rounded-[4px] mt-[4px] h-[25px] w-[106px]">
<span className="text-primary-4 text-[12px]">멘토링 횟수</span>
<span className="text-gray-5 text-[12px]">{mentor.mentoringCount}회</span>
</div>
<p className="text-gray-600 text-[12px] mt-[12px]">"{mentor.introduction.title}"</p>
</div>
</div>
</div>

{/* 멘토 소개 */}
<p className="text-gray-600 text-[12px] mt-[15px]">{mentor.intro}</p>
</div>
</div>
);
};
);
}
);

// displayName 붙이면 디버깅 편해~!
MentorItem.displayName = 'MentorItem';

export default MentorItem;
33 changes: 20 additions & 13 deletions src/mentorship/components/mentorlist/MentorList.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,41 @@
import React from 'react';
import { MentorType } from '@/mentorship/pages/mentorlist/MentorshipList.types';
import { Mentor } from '../../api/fetchMentors';
import MentorItem from '@/mentorship/components/mentorlist/MentorItem';

interface MentorListProps {
mentors: MentorType[];
mentors: Mentor[];
bookmarked: { [key: string]: boolean };
onToggleBookmark: (mName: string) => void;
onToggleBookmark: (nickname: string) => void;
lastMentorRef?: (node: Element | null) => void; // ✅ ref prop 추가
}

const MentorList: React.FC<MentorListProps> = ({
mentors,
bookmarked,
onToggleBookmark,
lastMentorRef,
}) => {
return (
<div className="w-full px-0 pt-[4px] space-y-4">
{mentors.length > 0 ? (
mentors.map((mentor) => (
<MentorItem
key={mentor.mName}
mentor={mentor}
isBookmarked={bookmarked[mentor.mName]}
onToggleBookmark={onToggleBookmark}
/>
))
mentors.map((mentor, index) => {
const isLast = index === mentors.length - 1;

return (
<MentorItem
key={mentor.nickname} // ✅ 유니크 key
mentor={mentor}
isBookmarked={bookmarked[mentor.nickname]}
onToggleBookmark={onToggleBookmark}
ref={isLast ? lastMentorRef : undefined} // ✅ 마지막 mentor에만 ref 연결
/>
);
})
) : (
<div className="text-center text-gray-500">검색 결과가 없습니다.</div>
<div className="text-center text-gray-2 mt-6">멘토 내역이 없습니다.</div>
)}
</div>
);
};

export default MentorList;
export default MentorList;
3 changes: 1 addition & 2 deletions src/mentorship/components/mentorlist/MentorshipFilterBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import { ChevronDown } from 'lucide-react';
interface MentorshipFilterBarProps {
selectedField: string | null;
selectedSubField: string | null;
selectedYear: string | null;
onFilterClick: (filterType: string) => void;
onFilterClick: (filterType: '직무' | '세부직무') => void; // ✅ 타입 명확화
}

const MentorshipFilterBar: React.FC<MentorshipFilterBarProps> = ({
Expand Down
45 changes: 17 additions & 28 deletions src/mentorship/components/mentorlist/MentorshipFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,29 @@ interface MentorshipFooterProps {
}

const MentorshipFooter: React.FC<MentorshipFooterProps> = ({ isVisible }) => {
const menuItems = [
{ menu: '홈', icon: <Home size={24} />, link: '/' },
{ menu: '멘토링 현황', icon: <ClipboardList size={24} />, link: '/mentorship' },
{ menu: '북마크', icon: <Bookmark size={24} />, link: '/bookmark' },
{ menu: '마이', icon: <User size={24} />, link: '/user' },
];

return (
<div
className="w-full max-w-[600px] mx-auto h-[50px] fixed bottom-0 bg-background p-4 pt-[7px] transition-transform duration-[1500ms] ease-in-out"
style={{
boxShadow: '0px -4px 4px rgba(210, 205, 205, 0.25)',
transform: isVisible ? 'translateY(0)' : 'translateY(100%)',
}}
className={`w-full max-w-[600px] mx-auto h-[50px] fixed bottom-0 bg-background p-4 pt-[7px] transition-transform duration-500 ease-in-out shadow-lg ${
isVisible ? 'translate-y-0' : 'translate-y-full'
}`}
>
<div className="flex justify-around">
{[
{ menu: '홈', icon: <Home size={24} />, link: '/' },
{
menu: '멘토링 현황',
icon: <ClipboardList size={24} />,
link: '/mentorship',
},
{ menu: '북마크', icon: <Bookmark size={24} />, link: '/bookmark' },
{ menu: '마이', icon: <User size={24} />, link: '/user' },
].map(({ menu, icon, link }) =>
link ? (
<Link key={menu} to={link} className="text-center w-[47px]">
<div className="w-[21px] h-[21px] mx-auto">{icon}</div>
<div className="text-[10px]">{menu}</div>
</Link>
) : (
<div key={menu} className="text-center w-[47px]">
<div className="w-[21px] h-[21px] mx-auto">{icon}</div>
<div className="text-[10px]">{menu}</div>
</div>
),
)}
{menuItems.map(({ menu, icon, link }) => (
<Link key={menu} to={link} className="text-center w-[47px]">
{icon}
<div className="text-[10px]">{menu}</div>
</Link>
))}
</div>
</div>
);
};

export default MentorshipFooter;
export default MentorshipFooter;
Loading