Skip to content
Open
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
5 changes: 5 additions & 0 deletions src/app/routes/paths.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import MypageNoticeIcon from '@/shared/assets/mypage/loudspeaker.svg?react';
import MypagePortfolio from '@/shared/assets/mypage/portfolio.svg?react';
import MypageTermOfServiceIcon from '@/shared/assets/mypage/term-of-service.svg?react';
import MypageProfileIcon from '@/shared/assets/mypage/update-profile.svg?react';
import FixedMemoList from '@/widgets/memo/FixedMemoList';
Expand Down Expand Up @@ -29,6 +30,7 @@ export const ROUTE_SEGMENTS = {
MEMO_EDIT: 'edit/:memoId',
MY_PAGE: 'mypage',
MY_PAGE_PROFILE: 'mypage/profile',
MY_PAGE_PORTFOLIO: 'mypage/portfolio',
NOTICE: 'notice',
END: 'end',
} as const;
Expand Down Expand Up @@ -66,6 +68,8 @@ export const PATHS = {
MY_PAGE: (teamId: number) => `${basePath(teamId)}/${ROUTE_SEGMENTS.MY_PAGE}`,
PROFILE: (teamId: number) =>
`${basePath(teamId)}/${ROUTE_SEGMENTS.MY_PAGE_PROFILE}`,
PORTFOLIO: (teamId: number) =>
`${basePath(teamId)}/${ROUTE_SEGMENTS.MY_PAGE_PORTFOLIO}`,

/* 공지사항 페이지 */
NOTICE: (teamId: number) => `${basePath(teamId)}/${ROUTE_SEGMENTS.NOTICE}`,
Expand Down Expand Up @@ -114,6 +118,7 @@ export const mainRoutes: RouteConfig[] = [

export const mypageRoutes: RouteConfig[] = [
{ label: '프로필 수정', to: PATHS.PROFILE, icon: MypageProfileIcon },
{ label: '포트폴리오', to: PATHS.PORTFOLIO, icon: MypagePortfolio },
{ label: '공지사항', to: PATHS.NOTICE, icon: MypageNoticeIcon },
{
label: '이용약관 및 개인정보처리방침',
Expand Down
5 changes: 5 additions & 0 deletions src/app/routes/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import MakeTeamPage from '@/pages/MakeTeamPage/make-team';
import { ManagementPage } from '@/pages/ManagementPage';
import { ExtraMemoPage, RedirectToRootFolder } from '@/pages/MemoPage';
import { MyPage } from '@/pages/MyPage';
import Portfolio from '@/pages/MyPage/portfolio.tsx';
import ProfilePage from '@/pages/MyPage/profile';
import NoticePage from '@/pages/NoticePage/notice';
import Redirect from '@/pages/RedirectPage/redirect';
Expand Down Expand Up @@ -87,6 +88,10 @@ export default function AppRoutes() {
path={ROUTE_SEGMENTS.MY_PAGE_PROFILE}
element={<ProfilePage />}
/>
<Route
path={ROUTE_SEGMENTS.MY_PAGE_PORTFOLIO}
element={<Portfolio />}
/>

{/* 공지 페이지 */}
<Route path={ROUTE_SEGMENTS.NOTICE} element={<NoticePage />} />
Expand Down
4 changes: 0 additions & 4 deletions src/entities/management/ui/Schedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@ export const Schedule = ({

const { isOpen, setIsOpen, toggle } = useToggle();

useEffect(() => {
console.log('팀 스케줄? ', schedule);
}, [schedule]);

useEffect(() => {
if (members?.length && selectedMembers.length === 0) {
setSelectedMembers(members);
Expand Down
30 changes: 30 additions & 0 deletions src/entities/portfolio/model/portfolio.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export const MOCK_PROJECTS = [
{
id: 1,
name: '팀매니저 ver.1',
duration: '2024.07 ~ 2024.08',
},
{
id: 2,
name: '팀매니저 ver.2',
duration: '2024.09 ~ ing',
},
];

export const projectInfoMock = [
{
id: 1,
title: '프로젝트 카테고리',
tags: ['팀매니저', '팀프로젝트'],
},
{
id: 2,
title: '프로젝트 멤버',
tags: ['지유', '안지유'],
},
{
id: 3,
title: '나의 역할',
tags: ['개발자', '프론트엔드'],
},
];
78 changes: 78 additions & 0 deletions src/entities/portfolio/ui/ProjectCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import styled from 'styled-components';
import ArrowIcon from '@/shared/assets/common/arrow.svg?react';

interface ProjectCardProps {
id: number;
name: string;
duration: string;
selected: boolean;
onSelect: (id: number) => void;
}

export function ProjectCard({
id,
name,
duration,
selected,
onSelect,
}: ProjectCardProps) {
return (
<CardContainer $selected={selected} onClick={() => onSelect(id)}>
<ProjectName>
<Name $selected={selected}>{name}</Name>
<Duration>{duration}</Duration>
</ProjectName>
<ArrowWrapper>
<ArrowIcon
width={24}
height={24}
stroke="1d1d1d"
strokeWidth={selected ? 2 : 1.5}
/>
</ArrowWrapper>
</CardContainer>
);
}

const CardContainer = styled.div<{ $selected: boolean }>`
display: flex;
justify-content: space-between;
align-items: center;
width: 487px;
height: 64px;
border-radius: 7px;
border: 1px solid
${({ $selected, theme }) =>
$selected ? theme.colors.mainBlue : theme.colors.lightGray};
padding: 0 16px;
cursor: pointer;

background: ${({ $selected, theme }) =>
$selected ? theme.colors.background : 'white'};
`;

const ProjectName = styled.div`
display: flex;
align-items: center;
gap: 10px;
`;

const Name = styled.p<{ $selected: boolean }>`
font-size: 16px;
font-weight: ${({ $selected }) => ($selected ? 700 : 500)};
color: ${({ theme }) => theme.colors.black};
`;

const Duration = styled.p`
font-size: 12px;
font-weight: 400;
color: ${({ theme }) => theme.colors.gray};
`;

const ArrowWrapper = styled.div`
width: 24px;
height: 24px;
display: flex;
justify-content: center;
align-items: center;
`;
48 changes: 48 additions & 0 deletions src/entities/portfolio/ui/ProjectInfoSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import styled from 'styled-components';
import { Tag } from '@/shared/ui/Tag/Tag.tsx';

interface ProjectInfoSectionProps {
id: number;
title: string;
tags: string[];
}

export function ProjectInfoSection({ title, tags }: ProjectInfoSectionProps) {
return (
<Container>
<Title>{title}</Title>

<TagContainer>
{tags.map((tag) => (
<Tag key={tag} text={tag} />
))}
</TagContainer>
</Container>
);
}

const Container = styled.div`
display: flex;
flex-direction: column;
width: 480px;
gap: 4px;
`;

const Title = styled.div`
width: 100%;
height: 18px;
display: flex;
align-items: center;
font-size: 12px;
font-weight: 600;
color: ${({ theme }) => theme.colors.black};
`;

const TagContainer = styled.div`
width: 100%;
height: 44px;
display: flex;
align-items: center;
gap: 7px;
border-bottom: 1px solid ${({ theme }) => theme.colors.silver};
`;
45 changes: 45 additions & 0 deletions src/entities/portfolio/ui/ProjectTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import styled from 'styled-components';

interface ProjectTitleProps {
title: string;
duration: string;
}

export function ProjectTitle({ title, duration }: ProjectTitleProps) {
return (
<Container>
<TitleContainer>
<Title>{title}</Title>
<Duration>{duration}</Duration>
</TitleContainer>
</Container>
);
}

const Container = styled.div`
width: 480px;
height: 34px;
display: flex;
border-bottom: 1px solid ${({ theme }) => theme.colors.silver};
`;

const TitleContainer = styled.div`
width: 230px;
height: 27px;
gap: 19px;
display: flex;
align-items: center;
`;

const Title = styled.p`
font-size: 18px;
font-weight: 700;
color: ${({ theme }) => theme.colors.black};
margin: 4.5px 0;
`;

const Duration = styled.p`
font-size: 12px;
font-weight: 400;
color: ${({ theme }) => theme.colors.darkGray};
`;
1 change: 0 additions & 1 deletion src/features/management/model/useSchedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export const useSchedule = (
};

const submit = () => {
console.log('등록된 시간:', weeklyTimes);
onSubmit?.(weeklyTimes);
setShowRegister(false);
};
Expand Down
1 change: 0 additions & 1 deletion src/pages/ManagementPage/management.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ export function ManagementPage() {
}
const transformedMySchedule = transformScheduleData(mySchedule);
const transformedPartialSchedule = transformScheduleData(partialSchedule);
console.log(schedule, transformedPartialSchedule);

return (
<PageWrapper>
Expand Down
81 changes: 81 additions & 0 deletions src/pages/MyPage/portfolio.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { useEffect, useState } from 'react';
import styled from 'styled-components';
import {
MOCK_PROJECTS,
projectInfoMock,
} from '@/entities/portfolio/model/portfolio.mock.ts';
import Skeleton from '@/shared/components/skeleton/Skeleton.tsx';
import MypageHeader from '@/widgets/mypage/MypageHeader.tsx';
import { ProjectDetail } from '@/widgets/portfolio/ui/ProjectDetail.tsx';
import { ProjectList } from '@/widgets/portfolio/ui/ProjectList.tsx';

export default function Portfolio() {
const projects = MOCK_PROJECTS;
const [selectedProjectId, setSelectedProjectId] = useState<number | null>(
null,
);
// TODO: API 연동 후 isPending으로 변경
const [isLoading, setIsLoading] = useState(true);

// 프로젝트 존재 여부
const hasProjects = projects.length > 0;

const selectedProject =
selectedProjectId === null
? null
: (projects.find((project) => project.id === selectedProjectId) ?? null);

const handleSelectProject = (projectId: number) => {
setSelectedProjectId((prev) => (prev === projectId ? null : projectId));
};

useEffect(() => {
const timer = setTimeout(() => {
setIsLoading(false);
}, 800);
return () => clearTimeout(timer);
}, []);

return (
<Container>
<MypageHeader showBackButton>포트폴리오</MypageHeader>

<ContentWrapper>
{isLoading ? (
<>
<Skeleton width="534px" height="562px" />
<Skeleton width="534px" height="562px" />
</>
) : (
<>
<ProjectList
projects={projects}
hasProjects={hasProjects}
selectedProjectId={selectedProjectId}
onSelectProject={handleSelectProject}
/>
<ProjectDetail
hasProjects={hasProjects}
selectedProject={selectedProject}
projectInfo={projectInfoMock}
/>
</>
)}
</ContentWrapper>
</Container>
);
}

const Container = styled.div`
display: flex;
flex-direction: column;
width: 100%;
height: 100vh;
`;

const ContentWrapper = styled.div`
display: flex;
width: 95%;
margin: 46px 0 74px 5%;
gap: 20px;
`;
4 changes: 2 additions & 2 deletions src/shared/assets/common/arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/shared/assets/mypage/portfolio.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading