Skip to content

Commit fcaa2e6

Browse files
authored
Merge pull request #284 from DguFarmSystem/feat/#283
[Feat] 프로젝트 단일 페이지 디테일 수정 + 프로젝트 모바일 뷰 세로 정렬
2 parents 392bebe + 50d5061 commit fcaa2e6

File tree

8 files changed

+139
-45
lines changed

8 files changed

+139
-45
lines changed
1.14 KB
Loading

apps/website/src/assets/home.png

-31.2 KB
Binary file not shown.
2.29 KB
Loading

apps/website/src/layouts/DetailLayout/ProjectDetailLayout.styled.ts

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,22 @@ export const TitleContainer = styled.div<LayoutProps>`
5858

5959
export const ParticipantsAndTagContainer = styled.div<LayoutProps>`
6060
display: flex;
61-
padding: 10px 0px;
62-
flex-direction: row;
63-
justify-content: space-between;
61+
padding: 0px 0px;
62+
gap: 5px;
63+
flex-direction: column;
64+
justify-content: flex-end;
6465
align-items: center;
6566
flex: 1 0 0;
66-
align-self: stretch;
67+
align-self: stretch;
6768
width: 100%;
69+
max-width: 800px;
70+
height: 60px;
71+
6872
`;
6973

7074
export const Link = styled.a<LayoutProps>`
7175
display: flex;
7276
color: var(--FarmSystem_Black, #191919);
73-
font-size: ${({ $isMobile }) => ($isMobile ? "12px": "16px")};
7477
font-style: normal;
7578
font-weight: 400;
7679
line-height: 30px; /* 150% */
@@ -80,17 +83,17 @@ export const Link = styled.a<LayoutProps>`
8083

8184
export const Tag = styled.p<LayoutProps>`
8285
display: flex;
83-
height: ${({ $isMobile }) => ($isMobile ? "32px": "40px")};
86+
height: ${({ $isMobile }) => ($isMobile ? "34px": "40px")};
8487
padding: 5px 20px;
85-
justify-content: center;
88+
justify-content: flex-end;
8689
align-items: center;
8790
8891
border-radius: 15px;
8992
background: var(--FarmSystem_Green06, #006811);
9093
9194
color: var(--FarmSystem_White, #FCFCFC);
9295
text-align: center;
93-
font-size: ${({ $isMobile }) => ($isMobile ? "16px": "20px")};
96+
font-size: ${({ $isMobile }) => ($isMobile ? "12px": "20px")};
9497
font-style: normal;
9598
font-weight: 400;
9699
line-height: 20px; /* 100% */
@@ -104,7 +107,7 @@ export const Participant = styled.p<LayoutProps>`
104107
color: var(--FarmSystem_Black, #191919);
105108
106109
107-
font-size: ${({ $isMobile }) => ($isMobile ? "16px": "20px")};
110+
font-size: ${({ $isMobile }) => ($isMobile ? "12px": "20px")};
108111
font-style: normal;
109112
font-weight: 400;
110113
line-height: 30px; /* 150% */
@@ -113,10 +116,8 @@ export const Participant = styled.p<LayoutProps>`
113116

114117
export const Title = styled.h2<LayoutProps>`
115118
display: flex;
116-
padding: 10px 0px;
117119
justify-content: start;
118120
align-items: center;
119-
gap: 10px;
120121
align-self: stretch;
121122
122123
color: var(--FarmSystem_Black, #191919);
@@ -131,6 +132,29 @@ export const Title = styled.h2<LayoutProps>`
131132
max-width: 800px;
132133
`;
133134

135+
export const Introduction = styled.p<LayoutProps>`
136+
display: flex;
137+
justify-content: start;
138+
align-items: center;
139+
align-self: stretch;
140+
141+
color: var(--FarmSystem_Black, #191919);
142+
font-family: "Pretendard Variable";
143+
font-size: ${({ $isMobile }) => ($isMobile ? "16px": "20px")};
144+
font-style: normal;
145+
font-weight: 500;
146+
line-height: 30px; /* 125% */
147+
letter-spacing: -0.24px;
148+
149+
/* 한 줄 말줄임표 처리 */
150+
overflow: hidden;
151+
text-overflow: ellipsis;
152+
white-space: nowrap;
153+
154+
width: 50%;
155+
max-width: 800px;
156+
`;
157+
134158
export const ImageContainer = styled.div<LayoutProps>`
135159
display: flex;
136160
width: 100%;
@@ -169,16 +193,47 @@ export const LinkContainer = styled.div<LayoutProps>`
169193
gap: 10px;
170194
justify-content: flex-start;
171195
max-width: 800px;
172-
width: 100%;
196+
width: 30%;
173197
`;
174198

175199
export const LinkIcon = styled.img<LayoutProps>`
176-
width: ${({ $isMobile }) => ($isMobile ? "30px": "50px")};
177-
height: ${({ $isMobile }) => ($isMobile ? "30px": "50px")};
200+
width: ${({ $isMobile }) => ($isMobile ? "24px": "48px")};
201+
height: ${({ $isMobile }) => ($isMobile ? "24px": "48px")};
178202
transition: transform 0.2s ease-in-out;
179203
cursor: pointer;
180204
object-fit: cover;
181205
&:hover {
182206
transform: scale(1.1);
183207
}
208+
`;
209+
210+
export const ContentContainer = styled.div<LayoutProps>`
211+
display: flex;
212+
margin: 0 auto;
213+
padding: 10px 0px;
214+
flex-direction: row;
215+
justify-content: flex-end;
216+
align-items: center;
217+
flex: 1 0 0;
218+
align-self: stretch;
219+
width: 100%;
220+
height: 70px;
221+
max-width: 800px;
222+
223+
`;
224+
225+
export const TagContainer = styled.div<LayoutProps>`
226+
display: flex;
227+
flex-direction: row;
228+
justify-content: flex-end;
229+
width: 100%;
230+
min-width: 70%;
231+
`;
232+
233+
export const ParticipantContainer = styled.div<LayoutProps>`
234+
display: flex;
235+
flex-direction: row;
236+
justify-content: flex-end;
237+
width: 100%;
238+
min-width: 70%;
184239
`;

apps/website/src/layouts/DetailLayout/ProjectDetailLayout.tsx

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import * as S from "./ProjectDetailLayout.styled";
22
import GoBackArrow from "@/assets/LeftArrow.png";
33
import useMediaQueries from "@/hooks/useMediaQueries";
44
import GithubIcon from "@/assets/githubLogo.png";
5-
import DeploymentIcon from "@/assets/home.png";
6-
import ResourceIcon from "@/assets/resource.png";
5+
import DeploymentIcon from "@/assets/black_link.png";
6+
import ResourceIcon from "@/assets/pink_link.png";
77

88
interface ProjectDetailLayoutProps {
99
title?: string;
10+
introduction?: string;
1011
content?: string;
1112
tag?: string;
1213
thumbnailUrl?: string;
@@ -20,12 +21,13 @@ interface ProjectDetailLayoutProps {
2021
export default function ProjectDetailLayout({
2122
title = "(임시) 제목",
2223
content = "(임시) 내용",
24+
introduction = "(임시) 소개",
2325
tag = "(임시) 태그",
2426
thumbnailUrl = "",
2527
githubLink = "(임시) 링크",
2628
deploymentLink = "(임시) 링크",
2729
resourceLink = "(임시) 링크",
28-
participants = ["(임시) 참여자1","(임시) 참여자2"],
30+
participants = ["참여자1","참여자2"],
2931
}: ProjectDetailLayoutProps) {
3032
const { isMobile, isTablet, isDesktop } = useMediaQueries();
3133

@@ -45,38 +47,55 @@ export default function ProjectDetailLayout({
4547
</S.GoBackButton>
4648
</S.GoBackContainer >
4749
<S.TitleContainer $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
48-
<S.ParticipantsAndTagContainer $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
49-
<S.Participant $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
50-
{participants.map((participant, index) => (
51-
<span key={index}>{participant}</span>
52-
))}
53-
</S.Participant>
54-
<S.Tag $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
55-
{tag}
56-
</S.Tag>
57-
</S.ParticipantsAndTagContainer>
5850
<S.Title $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
5951
{title}
6052
</S.Title>
61-
</S.TitleContainer>
53+
<S.Introduction $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
54+
{introduction}
55+
</S.Introduction>
56+
</S.TitleContainer>
57+
<S.ContentContainer $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
58+
<S.LinkContainer $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
59+
<S.Link $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop} href={githubLink} target="_blank">
60+
<S.LinkIcon $isMobile={isMobile} src={GithubIcon} alt="Github"/>
61+
</S.Link>
62+
{resourceLink && resourceLink !== "(임시) 링크" && (
63+
<S.Link $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop} href={resourceLink} target="_blank">
64+
<S.LinkIcon $isMobile={isMobile} src={ResourceIcon} alt="Resource"/>
65+
</S.Link>
66+
)}
67+
{deploymentLink && deploymentLink !== "(임시) 링크" && (
68+
<S.Link $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop} href={deploymentLink} target="_blank">
69+
<S.LinkIcon $isMobile={isMobile} src={DeploymentIcon} alt="Deployment"/>
70+
</S.Link>
71+
)}
72+
</S.LinkContainer>
73+
<S.ParticipantsAndTagContainer $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
74+
<S.TagContainer $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
75+
<S.Tag $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
76+
{tag}
77+
</S.Tag>
78+
</S.TagContainer>
79+
<S.ParticipantContainer $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
80+
<S.Participant $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
81+
{participants.length > 0 && (<>
82+
{`참여 팀원:`}
83+
{participants.map((participant, index) => (
84+
<span key={index}>{participant}</span>
85+
))}
86+
</>
87+
)}
88+
</S.Participant>
89+
</S.ParticipantContainer>
90+
</S.ParticipantsAndTagContainer>
91+
</S.ContentContainer>
6292
<S.ImageContainer $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
6393
<S.Thumbnail
6494
$isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}
6595
src={thumbnailUrl}
6696
alt={title}
6797
/>
6898
</S.ImageContainer>
69-
<S.LinkContainer $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
70-
<S.Link $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop} href={githubLink} target="_blank">
71-
<S.LinkIcon src={GithubIcon} alt="Github"/>
72-
</S.Link>
73-
<S.Link $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop} href={resourceLink} target="_blank">
74-
<S.LinkIcon src={ResourceIcon} alt="Resource"/>
75-
</S.Link>
76-
<S.Link $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop} href={deploymentLink} target="_blank">
77-
<S.LinkIcon src={DeploymentIcon} alt="Deployment"/>
78-
</S.Link>
79-
</S.LinkContainer>
8099
<S.ContentBox $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
81100
{content}
82101
</S.ContentBox>

apps/website/src/pages/Blog/Project/ProjectDetail.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ const ProjectDetail: React.FC = () => {
2020
try {
2121
if (!projectId) return;
2222
const response = await getProjectById(parseInt(projectId));
23-
console.log('Fetched Project Data:', response);
2423

2524
if (response.data) {
2625
const projectData = response.data;
@@ -32,6 +31,11 @@ const ProjectDetail: React.FC = () => {
3231
Logger.error(err);
3332
} finally {
3433
setLoading(false);
34+
return(
35+
<S.Container $isMobile={isMobile} $isTablet={isTablet} $isDesktop={isDesktop}>
36+
로딩에 실패했습니다.
37+
</S.Container>
38+
)
3539
}
3640
};
3741

@@ -61,6 +65,7 @@ const ProjectDetail: React.FC = () => {
6165
</S.ProjectPageTitle>
6266
<DetailLayout
6367
title={project.title}
68+
introduction={project.introduction}
6469
content={`${project.introduction}\n\n${project.content}`}
6570
tag={getTrackName(project.track)}
6671
thumbnailUrl={project.thumbnailImageUrl}

apps/website/src/pages/Blog/Project/ProjectList.styles.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const Container = styled.div`
77
align-items: center;
88
gap: 20px;
99
width: 100%;
10+
justify-content: flex-start;
1011
`;
1112

1213
/** 필터 영역 전체 컨테이너 */
@@ -101,14 +102,28 @@ export const DropdownItem = styled.div<{$isMobile: boolean; $isTablet: boolean;}
101102

102103
/** 프로젝트 리스트(카드)들을 감싸는 컨테이너 */
103104

104-
export const ListContainer = styled.div<{$isTablet: boolean; $isBig: boolean;}>`
105+
export const ListContainer = styled.div<{$isTablet: boolean; $isBig: boolean; $isMobile: boolean;}>`
105106
width: 100%;
106107
margin: 20px auto;
107-
min-width: ${(props) => (props.$isTablet ? '500px' : '800px')};
108+
min-width: ${(props) => (props.$isMobile ? '240px' : props.$isTablet ? '400px' : '800px')};
109+
max-width: ${(props) => (props.$isMobile ? '100%' : props.$isTablet ? '600px' : '1200px')};
108110
109111
display: grid;
110-
grid-template-columns: repeat(auto-fit, 300px); /*자동 너비 조정 */
111-
gap: 20px ${(props) => (props.$isTablet ? "1vw": props.$isBig ? "4vw": "10vw")};
112+
grid-template-columns: ${(props) => {
113+
if (props.$isMobile) return '1fr'; // 모바일: 한 컬럼
114+
if (props.$isTablet) return '1fr'; // 태블릿: 한 컬럼
115+
return 'repeat(auto-fit, 300px)'; // 데스크탑: 자동 너비 조정
116+
}};
117+
118+
gap: ${(props) => (props.$isMobile ? '15px' : props.$isTablet ? '20px' : '20px')}
119+
${(props) => (props.$isMobile ? '0px' : props.$isTablet ? '0px' : props.$isBig ? '4vw' : '10vw')};
120+
121+
justify-items: ${(props) => (props.$isMobile || props.$isTablet ? 'start' : 'start')};
122+
123+
/* 태블릿에서 카드들이 중앙에 정렬되도록 */
124+
${(props) => props.$isTablet && `
125+
padding: 0 20px;
126+
`}
112127
`;
113128

114129
/* 비어 있을 떄 출력하는 레이아웃 잡는 컨테이너 */

apps/website/src/pages/Blog/Project/ProjectList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ const ProjectList: React.FC = () => {
124124
{/* 프로젝트 카드 리스트 */}
125125
{projects && projects.length > 0 ? (
126126
<>
127-
<S.ListContainer $isTablet={isTablet} $isBig={isBig}>
127+
<S.ListContainer $isTablet={isTablet} $isBig={isBig} $isMobile={isMobile}>
128128
{projects.map((project) => (
129129
<ProjectItem
130130
key={project.projectId}

0 commit comments

Comments
 (0)