Skip to content

Commit 45ad34c

Browse files
authored
[feat] 공모전 상세 페이지 (모집중인 팀) 제작 완료 (#20)
* feat(#18) : 라우팅, Contest.tsx 제작 * feat(#18) : 공모전 세부정보페이지 완료 * feat(#18) : 팀장, 팀원박스 및 팀원정보 노출 완료 * feat(#18) : 리더infobox 컴포넌트 완료 * feat(#18) : 팀원infobox 컴포넌트 및 스크롤 커스텀 완료 * feat(#18) : 팀 생성버튼 라우팅 및 navigate추가
1 parent 6242834 commit 45ad34c

File tree

8 files changed

+553
-0
lines changed

8 files changed

+553
-0
lines changed

src/Router.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Profile from './pages/profile/Profile';
88
import Recommendation from './pages/recommendation/Recommendation';
99
import MultipleChoice from './pages/recommendation/MultipleChoice';
1010
import Subjective from './pages/recommendation/Subjective';
11+
import Contest from './pages/contest/Contest';
1112
import CompetitionList from './pages/competitionList/CompetitionList';
1213

1314
function Router() {
@@ -24,6 +25,8 @@ function Router() {
2425
<Route path="multipleChoice" element={<MultipleChoice />} />
2526
</Route>
2627
<Route path="/profile/:userId" element={<Profile />} />
28+
<Route path='/list/:postId' element={<Contest/>}/>
29+
<Route path='/list/:postId/:teamId' element={<div>팀 생성페이지입니다</div>}/>
2730
<Route path="/list" element={<CompetitionList />} />
2831
</Routes>
2932
<Footer />
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import styled from 'styled-components';
2+
import { CONTEST_DATA } from '../../constants/Contest';
3+
4+
const ContestInfo = () => {
5+
return (
6+
<ContestInfoLayout>
7+
<ContestInfoTitle>{CONTEST_DATA.title}</ContestInfoTitle>
8+
<ContestContainer>
9+
<ContestImg src={CONTEST_DATA.images[0]} />
10+
<ContestTextBox>
11+
<Dday>D-{CONTEST_DATA.remainDay}</Dday>
12+
{CONTEST_DATA.data}
13+
</ContestTextBox>
14+
</ContestContainer>
15+
</ContestInfoLayout>
16+
);
17+
};
18+
const ContestInfoLayout = styled.div`
19+
display: flex;
20+
flex-direction: column;
21+
22+
max-width: 122.4rem;
23+
margin: auto;
24+
`;
25+
const ContestInfoTitle = styled.div`
26+
${(props) => props.theme.fonts.heading2_1};
27+
color: ${(props) => props.theme.colors.gray90};
28+
29+
margin-top: 3rem;
30+
`;
31+
const ContestContainer = styled.div`
32+
display: flex;
33+
gap: 2rem;
34+
margin: 2rem 0;
35+
`;
36+
const ContestImg = styled.img`
37+
width: 36.8rem;
38+
height: 45.4rem;
39+
border-radius: 1.2rem;
40+
`;
41+
42+
const ContestTextBox = styled.div`
43+
${(props) => props.theme.fonts.subtitleM};
44+
background-color: ${(props) => props.theme.colors.gray5};
45+
46+
border: 1px solid ${(props) => props.theme.colors.primary20};
47+
border-radius: 1.2rem;
48+
49+
height: 45.4rem;
50+
width: 100%;
51+
/* display: flex; */
52+
53+
padding: 1.9rem 3.6rem;
54+
55+
white-space: break-spaces;
56+
57+
overflow-y: scroll;
58+
59+
&::-webkit-scrollbar {
60+
width: 8px;
61+
height: 8px;
62+
border-radius: 6px;
63+
background: rgba(255, 255, 255, 0.4);
64+
}
65+
&::-webkit-scrollbar-thumb {
66+
background: ${(props) => props.theme.colors.gray20};
67+
border-radius: 6px;
68+
}
69+
`;
70+
const Dday = styled.div`
71+
${(props) => props.theme.fonts.heading5};
72+
color: ${(props) => props.theme.colors.error90};
73+
74+
margin-bottom: 2rem;
75+
`;
76+
export default ContestInfo;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import styled from 'styled-components';
2+
3+
const LeaderInfoBox = ({ infoData }: { infoData: any }) => {
4+
return (
5+
<Container>
6+
<LeaderImg src={infoData.image} />{' '}
7+
<hr style={{ width: '100%', margin: '1rem 0', borderColor: '#898BF8' }} />
8+
<Name>{infoData.name}</Name>
9+
<Part>{infoData.part}</Part>
10+
<Part>{infoData.major}</Part>
11+
</Container>
12+
);
13+
};
14+
const Container = styled.div`
15+
display: flex;
16+
flex-direction: column;
17+
18+
justify-content: center;
19+
align-items: center;
20+
21+
padding: 3rem 4.5rem 2rem 4.5rem;
22+
23+
color: ${(props) => props.theme.colors.primary40};
24+
`;
25+
const LeaderImg = styled.img`
26+
width: 8rem;
27+
height: 8rem;
28+
29+
border: 1px solid ${(props) => props.theme.colors.primary20};
30+
border-radius: 4rem;
31+
32+
object-fit: cover;
33+
`;
34+
const Name = styled.div`
35+
${(props) => props.theme.fonts.heading5};
36+
color: ${(props) => props.theme.colors.gray90};
37+
/* margin: 1rem 0; */
38+
`;
39+
const Part = styled.div`
40+
${(props) => props.theme.fonts.bodyS};
41+
color: ${(props) => props.theme.colors.gray70};
42+
`;
43+
export default LeaderInfoBox;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import styled from 'styled-components';
2+
3+
const MemberInfoBox = ({ infoData }: { infoData: any }) => {
4+
return (
5+
<Container>
6+
<MemberImg src={infoData.image} /> <Name>{infoData.name}</Name>
7+
<Part>{infoData.part}</Part>
8+
<Part>{infoData.major}</Part>
9+
</Container>
10+
);
11+
};
12+
const Container = styled.div`
13+
max-width: 13.9rem;
14+
15+
display: flex;
16+
flex-direction: column;
17+
18+
justify-content: center;
19+
align-items: center;
20+
21+
border: 1px solid ${(props) => props.theme.colors.gray20};
22+
border-radius: 0.8rem;
23+
background-color: ${(props) => props.theme.colors.primary20};
24+
25+
padding: 2rem 3rem 2rem 3rem;
26+
`;
27+
const MemberImg = styled.img`
28+
width: 8rem;
29+
height: 8rem;
30+
31+
border: 1px solid ${(props) => props.theme.colors.primary20};
32+
border-radius: 4rem;
33+
34+
object-fit: cover;
35+
`;
36+
const Name = styled.div`
37+
${(props) => props.theme.fonts.subtitleS};
38+
color: ${(props) => props.theme.colors.gray90};
39+
/* margin: 1rem 0; */
40+
`;
41+
const Part = styled.div`
42+
${(props) => props.theme.fonts.bodyXS};
43+
color: ${(props) => props.theme.colors.gray70};
44+
45+
text-align: center;
46+
47+
white-space: nowrap;
48+
`;
49+
export default MemberInfoBox;
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import styled from 'styled-components';
2+
import LeaderInfoBox from './LeaderInfoBox';
3+
import MemberInfoBox from './MemberInfoBox';
4+
5+
const RecruitTeamItem = ({ data }: { data: any }) => {
6+
return (
7+
<ItemLayout>
8+
<LeaderBox>
9+
<Role>팀장</Role>
10+
<LeaderInfoBox infoData={data.leader} />
11+
</LeaderBox>
12+
<MemberBox>
13+
<Role>팀원</Role>
14+
<TeamInfoBox>
15+
<TO>
16+
모집 현황 : {data.cur}/{data.max}
17+
</TO>
18+
<IntroduceTitle>팀장의 한 마디</IntroduceTitle>
19+
<IntroduceContent>{data.talk}</IntroduceContent>
20+
<GoTeamButton>팀 자세히 보러가기</GoTeamButton>
21+
</TeamInfoBox>
22+
<MemberInfoContainer>
23+
{data.members.map((member: any, index: any) => (
24+
<MemberInfoBox infoData={member} key={index} />
25+
))}
26+
</MemberInfoContainer>
27+
</MemberBox>
28+
</ItemLayout>
29+
);
30+
};
31+
const ItemLayout = styled.div`
32+
display: flex;
33+
gap: 2.4rem;
34+
`;
35+
const Role = styled.div`
36+
position: absolute;
37+
left: -1px;
38+
top: -1.5rem;
39+
40+
color: ${(props) => props.theme.colors.white};
41+
background-color: ${(props) => props.theme.colors.primary40};
42+
43+
border-radius: 0.8rem;
44+
45+
width: 4.1rem;
46+
height: 3rem;
47+
padding: 0.4rem 0.8rem;
48+
`;
49+
const LeaderBox = styled.div`
50+
position: relative;
51+
52+
${(props) => props.theme.fonts.subtitleS};
53+
color: ${(props) => props.theme.colors.white};
54+
background-color: ${(props) => props.theme.colors.primary10};
55+
56+
border: 1px solid ${(props) => props.theme.colors.primary40};
57+
border-radius: 0.8rem;
58+
59+
width: 17.1rem;
60+
/* height: 23rem; */
61+
`;
62+
const MemberBox = styled.div`
63+
position: relative;
64+
display: flex;
65+
66+
${(props) => props.theme.fonts.subtitleS};
67+
color: ${(props) => props.theme.colors.white};
68+
background-color: ${(props) => props.theme.colors.primary10};
69+
70+
border: 1px solid ${(props) => props.theme.colors.primary40};
71+
border-radius: 0.8rem;
72+
73+
width: 100%;
74+
/* height: 23rem; */
75+
`;
76+
const TeamInfoBox = styled.div`
77+
display: flex;
78+
flex-direction: column;
79+
justify-content: center;
80+
align-items: center;
81+
gap: 1rem;
82+
83+
width: 25rem;
84+
85+
padding: 2rem;
86+
`;
87+
const TO = styled.div`
88+
${(props) => props.theme.fonts.subtitleXS};
89+
font-weight: 700;
90+
color: ${(props) => props.theme.colors.primary40};
91+
92+
width: 100%;
93+
`;
94+
const IntroduceTitle = styled.div`
95+
${(props) => props.theme.fonts.subtitleS};
96+
color: ${(props) => props.theme.colors.gray90};
97+
`;
98+
const IntroduceContent = styled.div`
99+
${(props) => props.theme.fonts.subtitleM};
100+
color: ${(props) => props.theme.colors.primary90};
101+
background-color: ${(props) => props.theme.colors.white};
102+
103+
border: 1px solid ${(props) => props.theme.colors.primary40};
104+
border-radius: 0.8rem;
105+
106+
width: 100%;
107+
padding: 3.5rem;
108+
109+
text-align: center;
110+
`;
111+
const GoTeamButton = styled.button`
112+
${(props) => props.theme.fonts.subtitleXS};
113+
color: rgba(255, 255, 255, 0.8);
114+
background-color: ${(props) => props.theme.colors.primary40};
115+
116+
border: 1px solid ${(props) => props.theme.colors.primary40};
117+
border-radius: 0.8rem;
118+
119+
width: 100%;
120+
padding: 0.5rem;
121+
`;
122+
const MemberInfoContainer = styled.div`
123+
display: flex;
124+
gap: 1.2rem;
125+
overflow-x: scroll;
126+
127+
max-width: 72rem;
128+
/* max-width: 100%; */
129+
130+
padding: 2.8rem;
131+
132+
&::-webkit-scrollbar {
133+
width: 8px;
134+
height: 8px;
135+
border-radius: 6px;
136+
background: rgba(255, 255, 255, 0.4);
137+
}
138+
&::-webkit-scrollbar-thumb {
139+
background: ${(props) => props.theme.colors.primary60};
140+
border-radius: 6px;
141+
}
142+
`;
143+
export default RecruitTeamItem;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import styled from 'styled-components';
2+
import { TEAMS_DATA } from '../../constants/Contest';
3+
import RecruitTeamItem from './RecruitTeamItem';
4+
import { useNavigate } from 'react-router-dom';
5+
6+
const RecruitTeamList = () => {
7+
const navigate = useNavigate();
8+
const handleBtnClicked = () => {
9+
navigate('3');
10+
};
11+
return (
12+
<RecruitTeamListLayout>
13+
<RecruitTeamListTopContainer>
14+
<RecruitTeamListTitle>모집 중인 팀</RecruitTeamListTitle>
15+
<RecruitTeamButton onClick={handleBtnClicked}>
16+
+팀 오픈하러 가기
17+
</RecruitTeamButton>
18+
</RecruitTeamListTopContainer>{' '}
19+
<RecruitTeamContainer>
20+
{TEAMS_DATA.map((data, index) => {
21+
return <RecruitTeamItem data={data} key={index} />;
22+
})}
23+
</RecruitTeamContainer>
24+
</RecruitTeamListLayout>
25+
);
26+
};
27+
28+
const RecruitTeamListLayout = styled.div`
29+
margin-left: 3rem;
30+
`;
31+
const RecruitTeamListTopContainer = styled.div`
32+
display: flex;
33+
justify-content: space-between;
34+
align-items: center;
35+
margin: 3rem 0 4.5rem 0;
36+
`;
37+
38+
const RecruitTeamListTitle = styled.div`
39+
${(props) => props.theme.fonts.heading3};
40+
color: ${(props) => props.theme.colors.gray100};
41+
`;
42+
const RecruitTeamButton = styled.button`
43+
${(props) => props.theme.fonts.buttonL};
44+
color: ${(props) => props.theme.colors.white};
45+
background-color: ${(props) => props.theme.colors.primary60};
46+
47+
display: inline-block;
48+
49+
width: 20.8rem;
50+
height: 6.4rem;
51+
border-radius: 3.2rem;
52+
`;
53+
const RecruitTeamContainer = styled.div`
54+
display: flex;
55+
flex-direction: column;
56+
gap: 4rem;
57+
`;
58+
export default RecruitTeamList;

0 commit comments

Comments
 (0)