Skip to content

Commit af583de

Browse files
authored
Merge pull request #65 from seoinah/main
feat: add gallery page
2 parents 16e0c82 + 166bbe1 commit af583de

10 files changed

Lines changed: 229 additions & 2 deletions

File tree

153 KB
Loading

src/components/Header/Header.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ const menuItems = [
3131
hasDropdown: false,
3232
path: "/coc",
3333
},
34+
{
35+
label: "GALLERY",
36+
hasDropdown: false,
37+
path: "/gallery",
38+
}
3439
/* { label: "NEWS", hasDropdown: false },
3540
{ label: "SUPPORT", hasDropdown: false },
3641
{ label: "COC", hasDropdown: false },*/

src/components/Layout/Layout.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ const Layout = () => {
88
return (
99
location.pathname === "/about" ||
1010
location.pathname === "/about/md" ||
11-
location.pathname === "/coc"
11+
location.pathname === "/coc" ||
12+
location.pathname === "/gallery"
1213
);
1314
};
1415
return (

src/pages/gallery/$id.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import * as React from "react";
2+
import { Container } from "@/components/Container";
3+
import { useGetNotice } from "@/quries/useGetNotice.ts";
4+
import { useNavigate, useParams } from "react-router-dom";
5+
6+
export const NoticeDetail: React.FC = () => {
7+
const { id } = useParams();
8+
const nav = useNavigate();
9+
const { data } = useGetNotice(id);
10+
11+
const goToList = () => {
12+
nav("/board/notice");
13+
};
14+
15+
const notice = data?.data ?? undefined;
16+
17+
return (
18+
notice && (
19+
<Container className={"px-5 py-10 md:px-40"}>
20+
<p className="px-4 py-2 text-lg font-medium text-[#2d003d]">공지사항</p>
21+
<section className="mb-8 border-y border-black px-4 py-8">
22+
<article>
23+
<header className="pb-12">
24+
<h1 className="text-4xl font-semibold text-[#2d003d]">
25+
{notice.title}
26+
</h1>
27+
<p className="py-4 text-base font-light text-[#2d003d]">
28+
{new Date(notice.created_at).toLocaleDateString()}
29+
</p>
30+
{/* REVIEW: 해시태그 컴포넌트는 없나? todo: hashtag*/}
31+
{/*<div>#컨퍼런스</div>*/}
32+
</header>
33+
<section>
34+
<p dangerouslySetInnerHTML={{ __html: notice.content }} />
35+
</section>
36+
</article>
37+
</section>
38+
<div className="flex justify-end">
39+
<button
40+
className="ml-auto inline-flex rounded-xl bg-[#302633] px-8 py-3 text-white"
41+
onClick={goToList}
42+
>
43+
목록
44+
</button>
45+
</div>
46+
</Container>
47+
)
48+
);
49+
};
50+
51+
export default NoticeDetail;

src/pages/gallery/index.tsx

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
2+
import BgImg from "@/assets/img/gallery/banner_gallery.png";
3+
4+
import { HeaderBanner } from "@/components/HeaderBanner";
5+
import { Breadcrumb } from "@/components/Breadcrumb";
6+
import { AboutSection, AboutSectionTitle } from "@/components/AboutSection";
7+
8+
import { SelectBox } from "@/components/SelectBox";
9+
import { useEffect, useState } from "react";
10+
import { Gallery, SelectValue } from "@/types/common.ts";
11+
import { useGetGallery } from "@/quries/useGetGallery";
12+
import { Pagination } from "@/components/Pagination";
13+
14+
const selectedOptions = [
15+
{ label: "2024년", value: "2024" },
16+
{ label: "2025년", value: "2025" },
17+
];
18+
19+
20+
const ActivityGallery = () => {
21+
const [yearList, setYearList] = useState<SelectValue[]>([]);
22+
const [selectedYear, setSelectedYear] = useState<number>(2025);
23+
const [page, setPage] = useState<number>(1);
24+
25+
const { data } = useGetGallery(page);
26+
27+
const galleryData: Gallery[] = data?.data ?? [];
28+
const pagination = data?.pagination;
29+
30+
useEffect(() => {
31+
setYearList(
32+
selectedOptions.map(
33+
(option) =>
34+
({
35+
...option,
36+
checked: option.value === String(selectedYear),
37+
}) as SelectValue,
38+
),
39+
);
40+
}, []);
41+
42+
const onPageChange = (val: number) => {
43+
setPage(val);
44+
};
45+
46+
const onSelectYear = (value: string) => {
47+
setSelectedYear(Number(value));
48+
setYearList(
49+
selectedOptions.map(
50+
(option) =>
51+
({ ...option, checked: option.value === value }) as SelectValue,
52+
),
53+
);
54+
};
55+
56+
return (
57+
<div className="align-center flex flex-col">
58+
<HeaderBanner
59+
backgroundImage={BgImg}
60+
title={"Community Log"}
61+
description={`HelloPY는 주니어 개발자들의 성장을 돕고 파이썬 교육 확산을 위한 커뮤니티입니다. <br/>온오프라인에서 학습하고 파이썬 기술과 트렌드를 공유하며, 다양한 외부 협업도 진행합니다. 헬로파이의 다양한 활동을 확인해보세요!`}
62+
>
63+
<Breadcrumb
64+
children={[
65+
<a href="/">Home</a>,
66+
<a href="/gallery">GALLERY</a>
67+
]}
68+
/>
69+
</HeaderBanner>
70+
71+
<AboutSection>
72+
<div className="mb-[106px] mt-[180px] flex flex-col items-center justify-start gap-2">
73+
<div className="self-stretch text-center text-[20px] font-bold text-hellopy-purple-200">
74+
HelloPY COC(Code of Conduct)
75+
</div>
76+
<div className="self-stretch text-center text-[28px] font-bold text-black">
77+
HelloPY 활동 가이드
78+
</div>
79+
<div className="self-stretch text-center text-[15px] text-black">
80+
신규 멤버는 자기소개, 기사 공유, 모각작 참여 등 기본 활동을 수행해야 합니다. <br/>각 게시판에 정보를 공유하고 질문하거나 성과를 기록할 수 있으며, 활동량에 따라 등급이 상승합니다. <br/>등급은 레몬파이부터 블루베리파이까지 나뉘며 오프라인 모임 또는 컨퍼런스 발표를 통해 추가 승급이 가능합니다.
81+
</div>
82+
</div>
83+
</AboutSection>
84+
85+
<div className={"bg-[#FCF7FF]"}>
86+
<AboutSection>
87+
88+
</AboutSection>
89+
</div>
90+
91+
92+
<AboutSection>
93+
<AboutSectionTitle
94+
title={"HelloPY Gallery"}
95+
subtitle={"HelloPY 활동 갤러리"}
96+
description={[]}
97+
/>
98+
<SelectBox options={yearList} onSelect={onSelectYear} />
99+
<div className="bg-purple-7 relative flex w-full flex-col items-start justify-start gap-[50px]">
100+
<div className="relative flex w-full flex-wrap items-start justify-start gap-[30px]">
101+
{galleryData
102+
.filter((item) => item.title?.includes(String(selectedYear)))
103+
.map((item, index) => (
104+
<div key={index} className="relative flex flex-col gap-5">
105+
<img
106+
className="w-52 object-cover"
107+
src={item.thumbnail}
108+
alt={`gallery-${item.id}`}
109+
/>
110+
<div className="flex w-full flex-col items-start justify-start gap-0">
111+
<div className="text-black-1 w-full text-left text-xl font-semibold leading-normal">
112+
{item.title}
113+
</div>
114+
<div
115+
className="text-black-1 w-full text-left text-base font-light leading-normal line-clamp-2 overflow-hidden text-ellipsis"
116+
dangerouslySetInnerHTML={{ __html: item.content }}
117+
/>
118+
</div>
119+
</div>
120+
))}
121+
122+
{galleryData && galleryData.length > 0 && pagination !== null && (
123+
<Pagination
124+
totalCount={pagination?.count ?? 0}
125+
currentPage={page}
126+
onPageChange={onPageChange}
127+
/>
128+
)}
129+
</div>
130+
</div>
131+
</AboutSection>
132+
</div>
133+
);
134+
};
135+
136+
export default ActivityGallery;

src/pages/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export const Home = () => {
6666
`}
6767
buttonText="전체 활동 보기 →"
6868
textPosition="right"
69-
buttonUrl="none"
69+
buttonUrl="/gallery"
7070
/>
7171

7272
<HomeCard

src/quries/useGetGallery.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { useQuery } from "react-query";
2+
import { GalleryService } from "@/service/galleryService";
3+
4+
export const useGetGallery = (page: number) => {
5+
return useQuery({
6+
queryKey: [GalleryService.QueryKey.getGallery, page],
7+
queryFn: () => GalleryService.getGallery(page),
8+
});
9+
};

src/quries/useGetNotice.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ export const useGetNotice = (id?: string) => {
88
enabled: !!id,
99
});
1010
};
11+
12+

src/service/galleryService.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import instance from "@/hooks/useAxios.ts";
2+
3+
export const GalleryService = {
4+
QueryKey: {
5+
getGallery: "getGallery",
6+
},
7+
8+
9+
getGallery: async (page: number) => {
10+
const { data } = await instance.get(`/activity-gallery/?page_size=10&page=${page}`);
11+
12+
return data;
13+
},
14+
};

src/types/common.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,12 @@ export type MD = {
2727
image: string;
2828
name: string;
2929
};
30+
31+
export type Gallery = {
32+
id: number,
33+
title: string,
34+
content: string,
35+
thumbnail: string,
36+
tags: {id: number, name: string}[],
37+
photos: string[]
38+
}

0 commit comments

Comments
 (0)