From aa687778c35ef832dbb8f0dbc2490fb67bff1c13 Mon Sep 17 00:00:00 2001 From: Kim-jaeyeon Date: Mon, 16 Sep 2024 21:31:10 +0900 Subject: [PATCH 1/4] =?UTF-8?q?Feat(firebase=20=EA=B2=8C=EC=8B=9C=EA=B8=80?= =?UTF-8?q?=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0,=20header=20z-index)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -firebase에서 게시글 조회하기 구현 완료 -header가 공지사항,qna에서 가려지는 버그 z-index 수정으로 해결 완료 --- src/components/Header/.css.ts | 1 + src/pages/Main/index.tsx | 2 + src/pages/Notice/index.tsx | 159 ++++++++-------------- src/pages/Notice/notice.css.ts | 34 ++--- src/pages/QnA/index.tsx | 8 +- src/pages/QnA/qna.css.ts | 17 +-- src/shared/firebase/test/FirebaseTest.tsx | 1 + 7 files changed, 80 insertions(+), 142 deletions(-) diff --git a/src/components/Header/.css.ts b/src/components/Header/.css.ts index e2b1e15..c6c507d 100644 --- a/src/components/Header/.css.ts +++ b/src/components/Header/.css.ts @@ -44,6 +44,7 @@ export const toggleBtnStyles = style({ const menuTransition = "transform 0.3s ease-out, opacity 0.3s ease-out"; export const menuStyles = style({ + zIndex:1, position: "absolute", top: "100%", left: 0, diff --git a/src/pages/Main/index.tsx b/src/pages/Main/index.tsx index 1fb0c37..18bb714 100644 --- a/src/pages/Main/index.tsx +++ b/src/pages/Main/index.tsx @@ -1,10 +1,12 @@ import logo from "../../assets/logo.png"; +import FirebaseTest from "../../shared/firebase/test/FirebaseTest"; export default function Main() { return (

Main

logo +
); } diff --git a/src/pages/Notice/index.tsx b/src/pages/Notice/index.tsx index 5a36881..036b85f 100644 --- a/src/pages/Notice/index.tsx +++ b/src/pages/Notice/index.tsx @@ -1,12 +1,10 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { - container, - titleText, mid, notice, noticeList, noticeContentWrapper, - noticeContent, + noticeTitle, noticeContainer, searchbar, searchContainer, @@ -16,107 +14,57 @@ import { newBadge, pageButton, activePageButton, + emptyListAlert, } from "./notice.css.ts"; // 스타일 가져오기 - -// NoticeItem 타입 정의: 제목은 string, 콘텐츠는 JSX.Element로 지정 -interface NoticeItem { - id: number; - title: string; - content: string; - detail: string; -} - -// 임시 데이터 예시 -const noticeData: NoticeItem[] = [ - { - id: 1, - title: "2024 강원대학교 백령대동제 개최 안내", - content: "This 강원대 the content for notice 1.", - detail: "여기는 세부내용입니다. Notice 1에 대한 정보가 더 있습니다.", - }, - { - id: 2, - title: "2024 강원대학교 백령대동제 개최 안내", - content: "This 강원대 the content for notice 1.", - detail: "여기는 세부내용입니다. Notice 1에 대한 정보가 더 있습니다.", - }, - { - id: 3, - title: "랄랄라 신규 게시물", - content: "This 강원대 the content for notice 1.", - detail: "여기는 세부내용입니다. Notice 1에 대한 정보가 더 있습니다.", - }, - { - id: 4, - title: "2024 강원대학교 백령대동제 개최 안내", - content: "This 강원대 the content for notice 1.", - detail: "여기는 세부내용입니다. Notice 1에 대한 정보가 더 있습니다.", - }, - { - id: 5, - title: "랄랄라 신규 게시물", - content: "This 강원대 the content for notice 1.", - detail: "여기는 세부내용입니다. Notice 1에 대한 정보가 더 있습니다.", - }, - { - id: 6, - title: "2024 강원대학교 백령대동제 개최 안내", - content: "This 강원대 the content for notice 1.", - detail: "여기는 세부내용입니다. Notice 1에 대한 정보가 더 있습니다.", - }, - { - id: 7, - title: "랄랄라 신규 게시물", - content: "This 강원대 the content for notice 1.", - detail: "여기는 세부내용입니다. Notice 1에 대한 정보가 더 있습니다.", - }, - { - id: 8, - title: "2024 강원대학교 백령대동제 개최 안내", - content: "This 강원대 the content for notice 1.", - detail: "여기는 룰루입니다. Notice 1에 대한 정보가 더 있습니다.", - }, - { - id: 9, - title: "랄랄라 신규 게시물", - content: "This 강원대 the content for notice 1.", - detail: "여기는 부스내용입니다. Notice 1에 대한 정보가 더 있습니다.", - }, - // 나머지 공지사항 추가 -]; +import { getNoticeList } from "../../shared/firebase/noticeService"; // Firebase에서 getNoticeList 가져오기 +import { NoticeDto } from "../../shared/types/notice"; // NoticeDto 타입 정의 function Notice() { - const [query, setQuery] = useState(""); // 검색어의 타입을 string으로 지정 + const [notices, setNotices] = useState([]); // 공지사항 목록 상태 + const [loading, setLoading] = useState(true); // 로딩 상태 관리 + const [query, setQuery] = useState(""); // 검색어 상태 const [currentPage, setCurrentPage] = useState(1); // 현재 페이지 상태 - const [expandedNotices, setExpandedNotices] = useState([]); // 확장된 공지 ID들을 관리 - const itemsPerPage = 6; + const [expandedNoticeId, setExpandedNoticeId] = useState(null); // 확장된 공지 ID를 관리 + const itemsPerPage = 6; // 페이지당 항목 수 + + // Firebase에서 공지사항 데이터를 가져오는 함수 + const fetchNotices = async () => { + try { + const noticeList = await getNoticeList(); // Firebase에서 데이터 가져오기 + setNotices(noticeList); // 데이터를 상태에 저장 + setLoading(false); // 로딩 완료 + } catch (error) { + console.error("Error fetching notices:", error); + setLoading(false); // 에러가 발생해도 로딩 완료로 변경 + } + }; - // 가장 최근 게시물의 ID 찾기 (ID가 제일 큰 것이 최신 게시물이라고 가정) - const mostRecentNoticeId = Math.max(...noticeData.map((notice) => notice.id)); + // 컴포넌트가 처음 렌더링될 때 Firebase에서 데이터를 가져옴 + useEffect(() => { + fetchNotices(); + }, []); // 검색어 입력 시 상태 업데이트 const handleSearch = (event: React.ChangeEvent) => { setQuery(event.target.value); - setCurrentPage(1); + setCurrentPage(1); // 검색 시 첫 페이지로 이동 }; - // downbtn 클릭 시 상세 내용을 보여주는 로직 - const toggleNotice = (id: number) => { - setExpandedNotices((prev) => - prev.includes(id) ? prev.filter((noticeId) => noticeId !== id) : [...prev, id], - ); + // 공지사항 토글 로직 (한 게시글만 확장되도록 수정) + const toggleNotice = (id: string) => { + setExpandedNoticeId((prevId) => (prevId === id ? null : id)); // 같은 게시글 클릭 시 축소, 다른 게시글 클릭 시 확장 }; - // 필터링 로직 - const filteredNotices = noticeData + // 필터링된 공지사항 목록 (검색어 적용 및 내림차순 정렬) + const filteredNotices = notices .filter((noticeItem) => { const searchText = query.toLowerCase(); return ( noticeItem.title.toLowerCase().includes(searchText) || - noticeItem.detail.toLowerCase().includes(searchText) + noticeItem.contents.toLowerCase().includes(searchText) ); }) - .reverse(); // 공지사항을 내림차순으로 정렬 (마지막 공지사항이 제일 위로 오게) + .reverse(); // 공지사항을 내림차순으로 정렬 (최신 공지사항이 제일 위로 오게) // 현재 페이지에 맞는 데이터 추출 const indexOfLastItem = currentPage * itemsPerPage; @@ -129,11 +77,13 @@ function Notice() { // 페이지 변경 함수 const paginate = (pageNumber: number) => setCurrentPage(pageNumber); + // 로딩 중일 때 로딩 메시지 표시 + if (loading) { + return
Loading...
; + } + return (
-
-

공지사항

-
- {/* noticeList 렌더링 */} + {/* 공지사항 목록 렌더링 */}
{filteredNotices.length === 0 ? ( -

+

해당하는 게시글이 없어요!

) : ( currentNotices.map((noticeItem, index) => (
- {/* 번호와 제목 표시 */}
-

{index + 1 + (currentPage - 1) * itemsPerPage}

- {/* 가장 최근 게시물에만 'New' 표시 */} - {noticeItem.id === mostRecentNoticeId && New} +

+ {index + 1 + (currentPage - 1) * itemsPerPage} +

+ {/* 공지사항의 renewal이 true일 경우 'New' 배지를 표시 */} + {noticeItem.renewal && New}
-

{noticeItem.title}

+

{noticeItem.title}

+ {/* downbtn을 클릭하면 토글 */} -
{/* 상세 내용 표시 - 확장 시만 보여줌 */} - {expandedNotices.includes(noticeItem.id) && ( -
{noticeItem.detail}
+ {expandedNoticeId === noticeItem.id && ( +
{noticeItem.contents}
)}
)) @@ -199,10 +154,8 @@ function Notice() {
)}
- -
); } -export default Notice; +export default Notice; \ No newline at end of file diff --git a/src/pages/Notice/notice.css.ts b/src/pages/Notice/notice.css.ts index ed2c642..4e09767 100644 --- a/src/pages/Notice/notice.css.ts +++ b/src/pages/Notice/notice.css.ts @@ -1,13 +1,8 @@ import { style } from "@vanilla-extract/css"; import { vars } from "../../shared/styles/vars.css"; -export const titleText = style({ - fontSize: 20, - fontWeight: 600, - color: "white", - marginTop: 60, -}); -// 검색 필드와 버튼을 감싸는 컨테이너 + +// 검색 필드를 감싸는 컨테이너 export const searchContainer = style({ display: "flex", alignItems: "center", @@ -33,13 +28,6 @@ export const searchbar = style({ fontWeight: 500, }); -// 상단의 빨간 컨테이너 -export const container = style({ - width: "100%", - height: 110, - display: "flex", - justifyContent: "center", -}); // 중간에 검색창을 배치하는 컨테이너 export const mid = style({ @@ -88,7 +76,7 @@ export const notice = style({ export const noticeNumber = style({ fontSize: 25, fontWeight: 700, - color: `${vars.color.blue1}`, + color: `${vars.color.blue2}`, marginLeft: 20, }); @@ -99,9 +87,9 @@ export const noticeContentWrapper = style({ marginLeft: 10, }); -export const noticeContent = style({ - display: "inline-block", // 텍스트가 한 줄로 스크롤되도록 설정 - whiteSpace: "nowrap", // 텍스트를 한 줄로 유지 +export const noticeTitle = style({ + display:'inline-block', + whiteSpace: 'nowrap', fontSize: 16, fontWeight: 500, color: "black", @@ -109,8 +97,8 @@ export const noticeContent = style({ }); export const noticeDetail = style({ + whiteSpace:'pre-wrap', overflow: "hidden", - maxHeight: "500px", // 최대 높이를 설정 backgroundColor: "rgba(255,255,255,0.6)", // 배경색 padding: "10px", marginTop: -12, @@ -118,6 +106,14 @@ export const noticeDetail = style({ width: "90%", }); +//검색한 키워드가 없을 경우 뜨는 글귀 +export const emptyListAlert = style({ + color:'white', + fontSize:18, + fontWeight:700, + textAlign:'center', +}); + //새 게시물에 뜨는 new 태그 export const newBadge = style({ marginLeft: "10px", diff --git a/src/pages/QnA/index.tsx b/src/pages/QnA/index.tsx index b440ff8..47f71dc 100644 --- a/src/pages/QnA/index.tsx +++ b/src/pages/QnA/index.tsx @@ -1,7 +1,5 @@ import { useState } from "react"; import { - container, - titleText, mid, qna, qnaList, @@ -103,9 +101,7 @@ function QnA() { return (
-
-

FAQ

-
+

자주 하는 질문

@@ -165,7 +161,7 @@ function QnA() { )}
-
+
); } diff --git a/src/pages/QnA/qna.css.ts b/src/pages/QnA/qna.css.ts index ad5ca36..15d0c6c 100644 --- a/src/pages/QnA/qna.css.ts +++ b/src/pages/QnA/qna.css.ts @@ -1,12 +1,7 @@ import { style } from "@vanilla-extract/css"; import { vars } from "../../shared/styles/vars.css"; -export const titleText = style({ - fontSize: 30, - fontWeight: 500, - color: "white", - marginTop: 60, -}); + export const subTitleContainer = style({ marginBottom: 20, @@ -23,13 +18,7 @@ export const subTitleText = style({ fontFamily: vars.font.pyeongChangBold, }); -// 상단의 빨간 컨테이너 -export const container = style({ - width: "100%", - height: 110, - display: "flex", - justifyContent: "center", -}); + // 중간에 검색창을 배치하는 컨테이너 export const mid = style({ @@ -78,7 +67,7 @@ export const qna = style({ export const qnaNumber = style({ fontSize: 25, fontWeight: 700, - color: `${vars.color.blue1}`, + color: `${vars.color.blue2}`, marginLeft: 20, }); diff --git a/src/shared/firebase/test/FirebaseTest.tsx b/src/shared/firebase/test/FirebaseTest.tsx index 180cbc1..864a236 100644 --- a/src/shared/firebase/test/FirebaseTest.tsx +++ b/src/shared/firebase/test/FirebaseTest.tsx @@ -46,6 +46,7 @@ function FirebaseTest() { title, contents, }); + }} > 생성 From 5d8c7302af77ae9faac6583b09f87be30844f1d7 Mon Sep 17 00:00:00 2001 From: Kim-jaeyeon Date: Mon, 16 Sep 2024 21:35:59 +0900 Subject: [PATCH 2/4] Hotfix(prettier) run prettier --- src/components/Header/.css.ts | 2 +- src/pages/Main/index.tsx | 3 --- src/pages/Notice/index.tsx | 19 +++++-------------- src/pages/Notice/notice.css.ts | 16 +++++++--------- src/pages/QnA/index.tsx | 3 --- src/pages/QnA/qna.css.ts | 4 ---- src/shared/firebase/test/FirebaseTest.tsx | 1 - 7 files changed, 13 insertions(+), 35 deletions(-) diff --git a/src/components/Header/.css.ts b/src/components/Header/.css.ts index c6c507d..7f620fa 100644 --- a/src/components/Header/.css.ts +++ b/src/components/Header/.css.ts @@ -44,7 +44,7 @@ export const toggleBtnStyles = style({ const menuTransition = "transform 0.3s ease-out, opacity 0.3s ease-out"; export const menuStyles = style({ - zIndex:1, + zIndex: 1, position: "absolute", top: "100%", left: 0, diff --git a/src/pages/Main/index.tsx b/src/pages/Main/index.tsx index 18bb714..105cb20 100644 --- a/src/pages/Main/index.tsx +++ b/src/pages/Main/index.tsx @@ -1,12 +1,9 @@ import logo from "../../assets/logo.png"; -import FirebaseTest from "../../shared/firebase/test/FirebaseTest"; - export default function Main() { return (

Main

logo -
); } diff --git a/src/pages/Notice/index.tsx b/src/pages/Notice/index.tsx index 036b85f..5df47ef 100644 --- a/src/pages/Notice/index.tsx +++ b/src/pages/Notice/index.tsx @@ -98,17 +98,13 @@ function Notice() { {/* 공지사항 목록 렌더링 */}
{filteredNotices.length === 0 ? ( -

- 해당하는 게시글이 없어요! -

+

해당하는 게시글이 없어요!

) : ( currentNotices.map((noticeItem, index) => (
-

- {index + 1 + (currentPage - 1) * itemsPerPage} -

+

{index + 1 + (currentPage - 1) * itemsPerPage}

{/* 공지사항의 renewal이 true일 경우 'New' 배지를 표시 */} {noticeItem.renewal && New}
@@ -118,14 +114,9 @@ function Notice() {
{/* downbtn을 클릭하면 토글 */} -
@@ -158,4 +149,4 @@ function Notice() { ); } -export default Notice; \ No newline at end of file +export default Notice; diff --git a/src/pages/Notice/notice.css.ts b/src/pages/Notice/notice.css.ts index 4e09767..6ced85a 100644 --- a/src/pages/Notice/notice.css.ts +++ b/src/pages/Notice/notice.css.ts @@ -1,7 +1,6 @@ import { style } from "@vanilla-extract/css"; import { vars } from "../../shared/styles/vars.css"; - // 검색 필드를 감싸는 컨테이너 export const searchContainer = style({ display: "flex", @@ -28,7 +27,6 @@ export const searchbar = style({ fontWeight: 500, }); - // 중간에 검색창을 배치하는 컨테이너 export const mid = style({ position: "relative", @@ -88,8 +86,8 @@ export const noticeContentWrapper = style({ }); export const noticeTitle = style({ - display:'inline-block', - whiteSpace: 'nowrap', + display: "inline-block", + whiteSpace: "nowrap", fontSize: 16, fontWeight: 500, color: "black", @@ -97,7 +95,7 @@ export const noticeTitle = style({ }); export const noticeDetail = style({ - whiteSpace:'pre-wrap', + whiteSpace: "pre-wrap", overflow: "hidden", backgroundColor: "rgba(255,255,255,0.6)", // 배경색 padding: "10px", @@ -108,10 +106,10 @@ export const noticeDetail = style({ //검색한 키워드가 없을 경우 뜨는 글귀 export const emptyListAlert = style({ - color:'white', - fontSize:18, - fontWeight:700, - textAlign:'center', + color: "white", + fontSize: 18, + fontWeight: 700, + textAlign: "center", }); //새 게시물에 뜨는 new 태그 diff --git a/src/pages/QnA/index.tsx b/src/pages/QnA/index.tsx index 47f71dc..0efbc21 100644 --- a/src/pages/QnA/index.tsx +++ b/src/pages/QnA/index.tsx @@ -101,7 +101,6 @@ function QnA() { return (
-

자주 하는 질문

@@ -160,8 +159,6 @@ function QnA() {
)}
- -
); } diff --git a/src/pages/QnA/qna.css.ts b/src/pages/QnA/qna.css.ts index 15d0c6c..d447e20 100644 --- a/src/pages/QnA/qna.css.ts +++ b/src/pages/QnA/qna.css.ts @@ -1,8 +1,6 @@ import { style } from "@vanilla-extract/css"; import { vars } from "../../shared/styles/vars.css"; - - export const subTitleContainer = style({ marginBottom: 20, position: "absolute", @@ -18,8 +16,6 @@ export const subTitleText = style({ fontFamily: vars.font.pyeongChangBold, }); - - // 중간에 검색창을 배치하는 컨테이너 export const mid = style({ position: "relative", diff --git a/src/shared/firebase/test/FirebaseTest.tsx b/src/shared/firebase/test/FirebaseTest.tsx index 864a236..180cbc1 100644 --- a/src/shared/firebase/test/FirebaseTest.tsx +++ b/src/shared/firebase/test/FirebaseTest.tsx @@ -46,7 +46,6 @@ function FirebaseTest() { title, contents, }); - }} > 생성 From 34335bd6f234f228c17364c96c2a483a6156a863 Mon Sep 17 00:00:00 2001 From: Kim-jaeyeon Date: Mon, 16 Sep 2024 21:46:30 +0900 Subject: [PATCH 3/4] =?UTF-8?q?Hotfix(zindex=20=EC=82=AD=EC=A0=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Header/.css.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Header/.css.ts b/src/components/Header/.css.ts index 7f620fa..e2b1e15 100644 --- a/src/components/Header/.css.ts +++ b/src/components/Header/.css.ts @@ -44,7 +44,6 @@ export const toggleBtnStyles = style({ const menuTransition = "transform 0.3s ease-out, opacity 0.3s ease-out"; export const menuStyles = style({ - zIndex: 1, position: "absolute", top: "100%", left: 0, From 33ce0fba06c7170bf55d56562396f9d3dc037373 Mon Sep 17 00:00:00 2001 From: Kim-jaeyeon Date: Tue, 17 Sep 2024 23:31:03 +0900 Subject: [PATCH 4/4] =?UTF-8?q?Fix(vars=20blue1=20=EC=88=98=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Notice/index.tsx | 16 +++++++--------- src/pages/Notice/notice.css.ts | 2 +- src/pages/QnA/qna.css.ts | 2 +- src/shared/styles/vars.css.ts | 2 +- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/pages/Notice/index.tsx b/src/pages/Notice/index.tsx index 5df47ef..4a6dfe1 100644 --- a/src/pages/Notice/index.tsx +++ b/src/pages/Notice/index.tsx @@ -56,15 +56,13 @@ function Notice() { }; // 필터링된 공지사항 목록 (검색어 적용 및 내림차순 정렬) - const filteredNotices = notices - .filter((noticeItem) => { - const searchText = query.toLowerCase(); - return ( - noticeItem.title.toLowerCase().includes(searchText) || - noticeItem.contents.toLowerCase().includes(searchText) - ); - }) - .reverse(); // 공지사항을 내림차순으로 정렬 (최신 공지사항이 제일 위로 오게) + const filteredNotices = notices.filter((noticeItem) => { + const searchText = query.toLowerCase(); + return ( + noticeItem.title.toLowerCase().includes(searchText) || + noticeItem.contents.toLowerCase().includes(searchText) + ); + }); // 현재 페이지에 맞는 데이터 추출 const indexOfLastItem = currentPage * itemsPerPage; diff --git a/src/pages/Notice/notice.css.ts b/src/pages/Notice/notice.css.ts index 6ced85a..ce421ad 100644 --- a/src/pages/Notice/notice.css.ts +++ b/src/pages/Notice/notice.css.ts @@ -74,7 +74,7 @@ export const notice = style({ export const noticeNumber = style({ fontSize: 25, fontWeight: 700, - color: `${vars.color.blue2}`, + color: `${vars.color.blue1}`, marginLeft: 20, }); diff --git a/src/pages/QnA/qna.css.ts b/src/pages/QnA/qna.css.ts index d447e20..b4ef796 100644 --- a/src/pages/QnA/qna.css.ts +++ b/src/pages/QnA/qna.css.ts @@ -63,7 +63,7 @@ export const qna = style({ export const qnaNumber = style({ fontSize: 25, fontWeight: 700, - color: `${vars.color.blue2}`, + color: `${vars.color.blue1}`, marginLeft: 20, }); diff --git a/src/shared/styles/vars.css.ts b/src/shared/styles/vars.css.ts index 67b342d..722b43c 100644 --- a/src/shared/styles/vars.css.ts +++ b/src/shared/styles/vars.css.ts @@ -13,7 +13,7 @@ const pyeongChangLight = fontFace({ export const vars = createGlobalTheme("#root", { color: { - blue1: "#CFCECE", + blue1: "#0781CD", blue2: "#029FD7", blue3: "#C9CEE1", green1: "#04D1C3",