Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat(Create Notice Frame): 공지사항 페이지 뼈대 제작 #10

Closed
wants to merge 4 commits into from
Closed
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
3 changes: 1 addition & 2 deletions src/App.tsx
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

원격의 develop 브렌치와 동기화 부탁드립니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵!

Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const RouterPath = [
element: <Notice />,
},
{
paht: "QnA",
path: "QnA",
element: <QnA />,
},
{
Expand All @@ -65,5 +65,4 @@ const router = createBrowserRouter([
function App() {
return <RouterProvider router={router} />;
}

export default App;
80 changes: 80 additions & 0 deletions src/assets/background.svg
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

배경의 경우에는 그라데이션이랑 문양이 많아서 svg 형식보다는 png가 좋을 것 같습니다!

상태변화가 날때마다 svg도 다시 로딩하는지 제 노트북에서는 엄청 느리게 돌아가게 되네요..

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 그렇군요 수정하겠습니다!

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions src/assets/copyright.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions src/assets/downbtn.svg
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거랑 서치 svg의 경우에는 구글 metrial icon에서 제공하는걸 써서, 삭제하고 바꾸는게 좋을 것 같아요.

이건 제가 마지막에 수정해놓을테니 확인해주세요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네! 그런데 search 버튼은 삭제해도 될 것 같아서 아예 지워도 될 것 같아요!

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/assets/search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
224 changes: 222 additions & 2 deletions src/pages/Notice/index.tsx
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다연님의 추가적인 리뷰가 필요할거 같습니다.

  1. 재연님께 수정 요청
  2. 머지 후 fix 브렌치로 수정

Original file line number Diff line number Diff line change
@@ -1,7 +1,227 @@
export default function Notice() {
import React, { useState } from "react";
import {
container,
titleText,
mid,
notice,
noticeList,
noticeContentWrapper,
noticeContent,
noticeContainer,
searchbar,
searchContainer,
copyright,
noticeNumber,
noticeDownBtn,
noticeDetail,
} from "./notice.css.ts"; // 스타일 가져오기

// NoticeItem 타입 정의: 제목은 string, 콘텐츠는 JSX.Element로 지정
interface NoticeItem {
id: number;
title: string;
content: JSX.Element;
detail: JSX.Element;
}

// 임시 데이터 예시
const noticeData: NoticeItem[] = [
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id값은 string으로 들어갈 예정이고
order가 number로 들어갈 예정입니다

아직 구조가 확정난건 아니라 지금은 이대로 개발하셔도 됩니다

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵!

{
id: 1,
title: "2024 강원대학교 백령대동제 개최 안내",
content: <p>This 강원대 the content for notice 1.</p>,
detail: <p>여기는 마지막 내용입니다. Notice 1에 대한 정보가 더 있습니다.</p>,
Comment on lines +32 to +33
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string 값으로 HTML 태그가 들어갈 예정입니다
요 부분은 string으로 바꿔서 작성후 컴포넌트도 수정해주세요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵!

},
{
id: 2,
title: "2024 강원대학교 백령대동제 개최 안내",
content: <p>This is the content for notice 2.</p>,
detail: <p>여기는 룰루 내용입니다. </p>,
},
{
id: 3,
title: "랄랄라 신규 게시물",
content: <p>This is the content for notice 2.</p>,
detail: <p>여기는 상세 내용입니다. Notice 2에 대한 정보가 더 있습니다잇.</p>,
},
{
id: 4,
title: "2024 강원대학교 백령대동제 개최 안내",
content: <p>This is the content for notice 2.</p>,
detail: <p>여기는 룰루 내용입니다. </p>,
},
{
id: 5,
title: "랄랄라 신규 게시물",
content: <p>This is the content for notice 2.</p>,
detail: <p>여기는 상세 내용입니다. Notice 2에 대한 정보가 더 있습니다잇.</p>,
},
{
id: 6,
title: "2024 강원대학교 백령대동제 개최 안내",
content: <p>This is the content for notice 2.</p>,
detail: <p>여기는 룰루 내용입니다. </p>,
},
{
id: 7,
title: "랄랄라 신규 게시물",
content: <p>This is the content for notice 2.</p>,
detail: <p>여기는 상세 내용입니다. Notice 2에 대한 정보가 더 있습니다잇.</p>,
},
{
id: 8,
title: "2024 강원대학교 백령대동제 개최 안내",
content: <p>This is the content for notice 2.</p>,
detail: <p>여기는 룰루 내용입니다. </p>,
},
{
id: 9,
title: "랄랄라 신규 게시물",
content: <p>This is the content for notice 2.</p>,
detail: <p>여기는 상세 내용입니다. Notice 2에 대한 정보가 더 있습니다잇.</p>,
},
// 나머지 공지사항 추가
];

function Notice() {
const [query, setQuery] = useState<string>(""); // 검색어의 타입을 string으로 지정
const [currentPage, setCurrentPage] = useState<number>(1); // 현재 페이지 상태
const [expandedNotices, setExpandedNotices] = useState<number[]>([]); // 확장된 공지 ID들을 관리
const itemsPerPage = 6;

// 가장 최근 게시물의 ID 찾기 (ID가 제일 큰 것이 최신 게시물이라고 가정)
const mostRecentNoticeId = Math.max(...noticeData.map((notice) => notice.id));

// 검색어 입력 시 상태 업데이트
const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
setQuery(event.target.value);
setCurrentPage(1);
};

// downbtn 클릭 시 상세 내용을 보여주는 로직
const toggleNotice = (id: number) => {
setExpandedNotices((prev) =>
prev.includes(id) ? prev.filter((noticeId) => noticeId !== id) : [...prev, id],
);
};

// 필터링 로직
const filteredNotices = noticeData
.filter((noticeItem) => {
const searchText = query.toLowerCase();
return (
noticeItem.title.toLowerCase().includes(searchText) ||
noticeItem.detail.props.children.toString().toLowerCase().includes(searchText)
);
})
.reverse(); // 공지사항을 내림차순으로 정렬 (마지막 공지사항이 제일 위로 오게)

// 현재 페이지에 맞는 데이터 추출
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const currentNotices = filteredNotices.slice(indexOfFirstItem, indexOfLastItem);

// 페이지 수 계산
const totalPages = Math.ceil(filteredNotices.length / itemsPerPage);

// 페이지 변경 함수
const paginate = (pageNumber: number) => setCurrentPage(pageNumber);

return (
<div>
<h1>Notice</h1>
<div className={container}>
<p className={titleText}>공지사항</p>
</div>
<div className={mid}>
<div className={searchContainer}>
<input
placeholder="키워드 검색"
type="text"
value={query}
onChange={handleSearch}
className={searchbar}
/>
</div>

{/* noticeList 렌더링 */}
<div className={noticeList}>
{filteredNotices.length === 0 ? (
<p style={{ textAlign: "center", color: "white", fontSize: "18px" }}>
해당하는 게시글이 없어요!
</p>
) : (
currentNotices.map((noticeItem, index) => (
<div className={noticeContainer} key={noticeItem.id}>
<div className={notice}>
{/* 번호와 제목 표시 */}
<div style={{ display: "flex", alignItems: "center" }}>
<p className={noticeNumber}>{index + 1 + (currentPage - 1) * itemsPerPage}</p>
{/* 가장 최근 게시물에만 'New' 표시 */}
{noticeItem.id === mostRecentNoticeId && (
<span
style={{
marginLeft: "10px",
padding: "3px 6px",
backgroundColor: "#e74c3c",
color: "#fff",
borderRadius: "3px",
fontSize: "12px",
fontWeight: "bold",
}}
>
New
</span>
)}
</div>

<div className={noticeContentWrapper}>
<p className={noticeContent}>{noticeItem.title}</p>
</div>
{/* downbtn을 클릭하면 토글 */}
<button
className={noticeDownBtn}
onClick={() => toggleNotice(noticeItem.id)}
></button>
</div>

{/* 상세 내용 표시 - 확장 시만 보여줌 */}
{expandedNotices.includes(noticeItem.id) && (
<div className={noticeDetail}>{noticeItem.detail}</div>
)}
</div>
))
)}
</div>

{/* 페이지네이션 */}
{filteredNotices.length > 0 && (
<div>
{Array.from({ length: totalPages }, (_, i) => i + 1).map((pageNumber) => (
<button
key={pageNumber}
onClick={() => paginate(pageNumber)}
style={{
margin: "5px",
marginTop: "15px",
padding: "5px 10px",
backgroundColor: currentPage === pageNumber ? "#3498db" : "#ddd",
color: currentPage === pageNumber ? "#fff" : "#000",
border: "none",
borderRadius: "3px",
}}
>
{pageNumber}
</button>
))}
</div>
)}
</div>

<div className={container}>
<div className={copyright}></div>
</div>
Comment on lines +220 to +222
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이건 Footar에 해당하는 컴포넌트여서 준환님이 만들어주시는거 나중에 가져다 쓰시면 됩니다.

지금은 삭제해두시면 됩니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵!

</div>
);
}

export default Notice;
Loading