From 5b5a91b28e561400bd229c00ee332dcfb4d25015 Mon Sep 17 00:00:00 2001 From: sunminnnnn Date: Mon, 9 Dec 2024 12:01:00 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20PP=20News=20Page=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A1=A4=20=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Pagination/NewsPagination.tsx | 25 ++-- .../PromotionAdmin/News/NewsList.tsx | 38 ++--- .../NewsPage/NewsViewPage/NewsEditPage.tsx | 139 +++++++++--------- .../NewsPage/NewsViewPage/NewsViewPage.tsx | 57 ++++--- src/pages/PromotionAdmin/NewsPage/index.tsx | 20 +-- .../PromotionPage/NewsPage/NewsBoardPage.tsx | 94 ++++-------- .../PromotionPage/NewsPage/NewsSection.tsx | 110 ++++++++++---- 7 files changed, 247 insertions(+), 236 deletions(-) diff --git a/src/components/Pagination/NewsPagination.tsx b/src/components/Pagination/NewsPagination.tsx index a5e3c03d..0c1bedef 100644 --- a/src/components/Pagination/NewsPagination.tsx +++ b/src/components/Pagination/NewsPagination.tsx @@ -14,6 +14,7 @@ const NewsPagination = ({ postsPerPage, totalPosts, paginate }: IPaginationProps const navigate = useNavigate(); const [currentPage, setCurrentPage] = useState(1); const [currentPageRange, setCurrentPageRange] = useState(0); + const [shouldScroll, setShouldScroll] = useState(false); const location = useLocation(); const pageNumbers = []; @@ -37,37 +38,41 @@ const NewsPagination = ({ postsPerPage, totalPosts, paginate }: IPaginationProps useEffect(() => { const queryParams = new URLSearchParams(location.search); - const page = parseInt(queryParams.get('page') || '1', 10); + const page = parseInt(queryParams.get("page") || "1", 10); setCurrentPage(page); setIndex(page - 1); const pageRange = Math.ceil(page / 10) - 1; setCurrentPageRange(pageRange); }, [location]); - // useEffect(() => { - // window.scrollTo({ top: 900, behavior: 'smooth' }); - // }, [currentPage, currentPageRange]); + useEffect(() => { + if (shouldScroll) { + window.scrollTo({ top: 900, behavior: "smooth" }); + setShouldScroll(false); + } + }, [shouldScroll]); return ( {currentPageRange > 0 && ( <> setCurrentPageRange(0)} selected={false}> - {'<<'} + {"<<"} - {'<'} + {"<"} )} {displayedPages.map((number) => ( { - paginate(number + 1); // 페이지 인덱스를 1부터 시작하게 조정 + paginate(number + 1); setIndex(number); navigate(`?page=${number + 1}`); + setShouldScroll(true); }} selected={number === index ? true : false} > @@ -77,10 +82,10 @@ const NewsPagination = ({ postsPerPage, totalPosts, paginate }: IPaginationProps {currentPageRange < totalPageRanges - 1 && ( <> - {'>'} + {">"} setCurrentPageRange(totalPageRanges - 1)} selected={false}> - {'>>'} + {">>"} )} diff --git a/src/components/PromotionAdmin/News/NewsList.tsx b/src/components/PromotionAdmin/News/NewsList.tsx index e9ba101d..ee63f571 100644 --- a/src/components/PromotionAdmin/News/NewsList.tsx +++ b/src/components/PromotionAdmin/News/NewsList.tsx @@ -6,7 +6,7 @@ import { getNews } from '@/apis/PromotionAdmin/news'; import Pagination from '@/components/Pagination/Pagination'; import { useNavigate } from 'react-router-dom'; -const NewsList = ({handler}:{handler:(id:number)=>void}) => { +const NewsList = ({ handler }: { handler: (id: number) => void }) => { const { data, isLoading, error, refetch } = useQuery('newsList', getNews); // const newsData: INEWS[]=data?data:[]; @@ -19,29 +19,29 @@ const NewsList = ({handler}:{handler:(id:number)=>void}) => { setCurrentPage(pageNumber - 1); // 페이지 인덱스를 0부터 시작하게 조정 navigate(`?page=${pageNumber}`); // URL 쿼리 매개변수 업데이트 }; - const currentArtworks = data?data.slice(indexOfFirstPost, indexOfLastPost):[]; + const currentArtworks = data ? data.slice(indexOfFirstPost, indexOfLastPost) : []; - const handleClick=(id:number)=>{ + const handleClick = (id: number) => { handler(id) }; return ( -
- {isLoading?

Loading...

: - data? - <> - {currentArtworks.map((i)=>( -
handleClick(i.id)} - data-cy={`news-item-${i.id}`} > - -
- ))} - - - :<>😊 뉴스 데이터가 존재하지 않습니다.} +
+ {isLoading ?

Loading...

: + data ? + <> + {currentArtworks.map((i) => ( +
handleClick(i.id)} + data-cy={`news-item-${i.id}`} > + +
+ ))} + + + : <>😊 뉴스 데이터가 존재하지 않습니다.}
); }; diff --git a/src/pages/PromotionAdmin/NewsPage/NewsViewPage/NewsEditPage.tsx b/src/pages/PromotionAdmin/NewsPage/NewsViewPage/NewsEditPage.tsx index e2bd5410..e77d6051 100644 --- a/src/pages/PromotionAdmin/NewsPage/NewsViewPage/NewsEditPage.tsx +++ b/src/pages/PromotionAdmin/NewsPage/NewsViewPage/NewsEditPage.tsx @@ -16,7 +16,7 @@ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs/AdapterDayjs'; const NewsEditPage = () => { const { id } = useParams(); const [errorMessage, setErrorMessage] = useState(''); //데이터 업데이트 할 때 - const news=useOutletContext<{news:INEWS,setIsEditing:(React.Dispatch>)}>(); + const news = useOutletContext<{ news: INEWS, setIsEditing: (React.Dispatch>) }>(); const setIsEditing = useSetRecoilState(dataUpdateState); const navigator = useNavigate(); @@ -24,8 +24,8 @@ const NewsEditPage = () => { const currentPath = location.pathname; const listPath = currentPath.substring(0, currentPath.lastIndexOf('/')); - const {register,getValues,setValue,watch, - }= useForm({ + const { register, getValues, setValue, watch, + } = useForm({ defaultValues: { title: news.news.title, source: news.news.source, @@ -44,8 +44,8 @@ const NewsEditPage = () => { const handleChange = (e: React.ChangeEvent | React.ChangeEvent) => { setIsEditing(true); const { name, value } = e.target; - if (/^\s|[~!@#$%^&*(),.?":{}|<>]/.test(value.charAt(0))) {return;} - setPutData((prevData) => ({...prevData,[name]: value,})); + if (/^\s|[~!@#$%^&*(),.?":{}|<>]/.test(value.charAt(0))) { return; } + setPutData((prevData) => ({ ...prevData, [name]: value, })); }; const visibility = watch('visibility'); const handleChangeVisibility = (value: boolean) => { @@ -59,15 +59,15 @@ const NewsEditPage = () => { // return `${year}-${month}-${day}`; // }; - const handleCancelWriting=()=>{ - if(window.confirm(MSG.CONFIRM_MSG.CANCLE)){ + const handleCancelWriting = () => { + if (window.confirm(MSG.CONFIRM_MSG.CANCLE)) { news.setIsEditing(false) setIsEditing(false) navigator(listPath); } } - const handlePut=async()=>{ + const handlePut = async () => { // const formData = new FormData(); const requestData = { id: Number(id), @@ -79,17 +79,17 @@ const NewsEditPage = () => { }; // formData.append('dto', new Blob([JSON.stringify(requestData)], { type: 'application/json' })); - if(window.confirm(MSG.CONFIRM_MSG.SAVE)){ - try{ - const response=await putNews(requestData) + if (window.confirm(MSG.CONFIRM_MSG.SAVE)) { + try { + const response = await putNews(requestData) // if (response.code === 400 && response.data === null) { //에러메시지 있을 때 - alert(MSG.ALERT_MSG.SAVE) - news.setIsEditing(false) - setIsEditing(false) - navigator(listPath) - return; + alert(MSG.ALERT_MSG.SAVE) + news.setIsEditing(false) + setIsEditing(false) + navigator(listPath) + return; // } - }catch (error: any) { + } catch (error: any) { alert(MSG.CONFIRM_MSG.FAILED) } } @@ -98,38 +98,38 @@ const NewsEditPage = () => { //---------------------------------------- return ( -
+
제목 -
- 완료 - 취소 +
+ 완료 + 취소
- + {...register('title')} + name='title' + value={putData.title} + onChange={handleChange} + placeholder='News 제목' + data-cy='news-title-input' + style={{ borderRadius: "5px", fontFamily: "pretendard-semiBold", marginRight: 10 }} /> +
출처/작성자 + {...register('source')} + name='source' + value={putData.source} + onChange={handleChange} + placeholder='출처/작성자' + data-cy='news-source-input' + style={{ borderRadius: "5px", fontFamily: "pretendard-semiBold" }} />
-
+
원문 날짜 - { textField: { sx: { backgroundColor: '#ffffff', - borderRadius:'5px', - fontFamily:'pretendard', + borderRadius: '5px', + fontFamily: 'pretendard', fontSize: '14px', margin: 'auto 0 auto 5px', boxShadow: '1px 1px 4px 0.3px #c6c6c6', - '.MuiInputBase-input':{ + '.MuiInputBase-input': { padding: '10px', }, '.MuiOutlinedInput-root': { @@ -169,38 +169,38 @@ const NewsEditPage = () => { />
-
+
공개 여부 - handleChangeVisibility(true)} - className='public' - selected={visibility} - data-cy="news-visibility-public" - > - 공개 - - handleChangeVisibility(false)} - className='private' - selected={!visibility} - data-cy="news-visibility-private" - > - 비공개 - + handleChangeVisibility(true)} + className='public' + selected={visibility} + data-cy="news-visibility-public" + > + 공개 + + handleChangeVisibility(false)} + className='private' + selected={!visibility} + data-cy="news-visibility-private" + > + 비공개 +
링크 + {...register('url')} + name='url' + value={putData.url} + onChange={handleChange} + placeholder='기사 링크' + data-cy='news-link-input' + style={{ borderRadius: "5px", fontFamily: "pretendard-semiBold" }} />
); @@ -208,7 +208,7 @@ const NewsEditPage = () => { export default NewsEditPage; -const Container=styled.div` +const Container = styled.div` min-width: 300px; width: 100%; @@ -220,10 +220,9 @@ border-radius: 5px; overflow:hidden; // white-space:nowrap; text-overflow: ellipsis; -} -` +`; -const Title=styled.div` +const Title = styled.div` padding: 5px; font-size: 1em; white-space: nowrap; @@ -249,7 +248,7 @@ const SendButton = styled.button` } `; -const InputBlock=styled.input` +const InputBlock = styled.input` outline: none; font-family: pretendard; font-size: 14px; diff --git a/src/pages/PromotionAdmin/NewsPage/NewsViewPage/NewsViewPage.tsx b/src/pages/PromotionAdmin/NewsPage/NewsViewPage/NewsViewPage.tsx index dbe51b75..78c98790 100644 --- a/src/pages/PromotionAdmin/NewsPage/NewsViewPage/NewsViewPage.tsx +++ b/src/pages/PromotionAdmin/NewsPage/NewsViewPage/NewsViewPage.tsx @@ -4,25 +4,25 @@ import React, { useEffect, useRef, useState } from 'react'; import { useQuery } from 'react-query'; import { Outlet, useLocation, useNavigate, useParams } from 'react-router-dom'; import styled from 'styled-components'; -import {ReactComponent as DOTS} from '@/assets/images/PA/3dot_Column.svg'; +import { ReactComponent as DOTS } from '@/assets/images/PA/3dot_Column.svg'; import { MSG } from '@/constants/messages'; const NewsViewPage = () => { const { id } = useParams(); - const { data, isLoading, error, refetch } = useQuery(['newsDetail',id], ()=>getNewsDetail(Number(id))); - const news=data; + const { data, isLoading, error, refetch } = useQuery(['newsDetail', id], () => getNewsDetail(Number(id))); + const news = data; const navigator = useNavigate(); const location = useLocation(); const currentPath = location.pathname; const listPath = currentPath.substring(0, currentPath.lastIndexOf('/')); - const [isEditing,setIsEditing]=useState(false) - const handleEditNews=()=>{ + const [isEditing, setIsEditing] = useState(false) + const handleEditNews = () => { setIsEditing(true) setMore(false) navigator(`edit`) } - const sharedNews={news:news,setIsEditing} + const sharedNews = { news: news, setIsEditing } // const formatDate = (date: Date): string => { // const year = date.getFullYear(); @@ -31,25 +31,25 @@ const NewsViewPage = () => { // return `${year}-${month}-${day}`; // }; - const handleDelete=async ()=>{ - if(window.confirm(MSG.CONFIRM_MSG.DELETE)){ - try{ - const response=await deleteNews(Number(id)) + const handleDelete = async () => { + if (window.confirm(MSG.CONFIRM_MSG.DELETE)) { + try { + const response = await deleteNews(Number(id)) // if (response.code === 400 && response.data === null) { //에러메시지 있을 때 - alert(MSG.ALERT_MSG.DELETE) - navigator(listPath) - return; + alert(MSG.ALERT_MSG.DELETE) + navigator(listPath) + return; // } - }catch (error: any) { + } catch (error: any) { alert(MSG.CONFIRM_MSG.FAILED) } - }else{ + } else { setMore(false) } } //more 버튼 관련---------------------------------------- - const [more,setMore]=useState(false); + const [more, setMore] = useState(false); const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 }); const buttonRef = useRef(null); // 버튼의 위치를 가져오기 위한 ref const menuRef = useRef(null); // 메뉴의 ref 타입 @@ -57,8 +57,8 @@ const NewsViewPage = () => { if (buttonRef.current) { const rect = buttonRef.current.getBoundingClientRect(); setMenuPosition({ - top: rect.bottom + window.scrollY+5, // 버튼의 하단 위치 - left: rect.right + window.scrollX-90, // 버튼의 왼쪽 위치 + top: rect.bottom + window.scrollY + 5, // 버튼의 하단 위치 + left: rect.right + window.scrollX - 90, // 버튼의 왼쪽 위치 }); } setMore((prev) => !prev); @@ -80,7 +80,7 @@ const NewsViewPage = () => { }; }, []); -//링크 유효성 검사---------------------------------------- + //링크 유효성 검사---------------------------------------- function isValidUrl(url: string) { try { new URL(url); @@ -142,7 +142,7 @@ const NewsViewPage = () => { export default NewsViewPage; -const Container=styled.div` +const Container = styled.div` margin-left: 10px; width: 500px; height: fit-content; @@ -156,10 +156,9 @@ border-radius: 5px; overflow:hidden; // white-space:nowrap; text-overflow: ellipsis; -} -` +`; -const Title=styled.div` +const Title = styled.div` padding: 0 5px 5px 5px; font-size: 1.5em; font-family: 'pretendard-bold'; @@ -175,21 +174,21 @@ const Description = styled.div` margin-top:10px; `; -const Day=styled.div` +const Day = styled.div` padding: 5px; font-size: 1.1em; font-family: 'pretendard-semibold'; ` -const Visibility=styled.div<{visibility: boolean|null}>` +const Visibility = styled.div<{ visibility: boolean | null }>` border-radius: 5px; -background-color: ${({visibility})=>visibility===true?'#ffaa007d':'#33333321'}; +background-color: ${({ visibility }) => visibility === true ? '#ffaa007d' : '#33333321'}; width: fit-content; padding: 4px; font-family: pretendard-medium; font-size: 1.1em; margin: 5px; ` -const Content=styled.a` +const Content = styled.a` padding: 5px; font-family: 'pretendard'; color: #2c2ff2; @@ -198,7 +197,7 @@ text-decoration:underline; cursor: pointer; } ` -const More=styled(DOTS)` +const More = styled(DOTS)` width:18px; height:18px; margin-top:auto; @@ -206,7 +205,7 @@ margin-bottom:auto; margin-right:10px; cursor: pointer; ` -const Menu=styled.ul<{ top: number; left: number }>` +const Menu = styled.ul<{ top: number; left: number }>` position: absolute; top: ${({ top }) => top}px; left: ${({ left }) => left}px; diff --git a/src/pages/PromotionAdmin/NewsPage/index.tsx b/src/pages/PromotionAdmin/NewsPage/index.tsx index 2b594239..156b3f18 100644 --- a/src/pages/PromotionAdmin/NewsPage/index.tsx +++ b/src/pages/PromotionAdmin/NewsPage/index.tsx @@ -12,28 +12,28 @@ const Index = () => { const navigator = useNavigate(); useEffect(() => { - + }, [producingIsOpend]); - const handleWritingNews=()=>{ + const handleWritingNews = () => { setProducingIsOpened(!producingIsOpend); } - const handleViewNews=(id:number)=>{ + const handleViewNews = (id: number) => { navigator(`${id}`); } return ( <> - {producingIsOpend && } isOpen={producingIsOpend} />} - + {producingIsOpend && } isOpen={producingIsOpend} />} + - News 목록 - 글쓰기 + News 목록 + 글쓰기 -
- +
+
- + ); }; diff --git a/src/pages/PromotionPage/NewsPage/NewsBoardPage.tsx b/src/pages/PromotionPage/NewsPage/NewsBoardPage.tsx index ffd2f3f1..10330beb 100644 --- a/src/pages/PromotionPage/NewsPage/NewsBoardPage.tsx +++ b/src/pages/PromotionPage/NewsPage/NewsBoardPage.tsx @@ -1,11 +1,13 @@ import { getAllNewsData } from "@/apis/PromotionPage/news"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import styled from "styled-components"; import IntroSection from "./IntroSection"; import NewsSection from "./NewsSection"; import NewsPagination from "@/components/Pagination/NewsPagination"; import { theme } from "@/styles/theme"; import { useQuery } from "react-query"; +import { useLocation, useNavigate } from "react-router-dom"; +import ScrollToTop from "@/hooks/useScrollToTop"; interface INewsCardProps { id: number; @@ -19,8 +21,21 @@ const NewsBoardPage: React.FC = () => { const [currentPage, setCurrentPage] = useState(1); const [postsPerPage] = useState(6); + const navigate = useNavigate(); + const location = useLocation(); + + useEffect(() => { + const queryParams = new URLSearchParams(location.search); + const page = queryParams.get('page'); + + if (!page) { + navigate('?page=1', { replace: true }); + } else { + setCurrentPage(parseInt(page, 10)); + } + }, [location, navigate]); - const { data: newsData, isLoading, error } = useQuery( + const { data: newsData } = useQuery( 'newsData', async () => { const response = await getAllNewsData(); @@ -48,45 +63,22 @@ const NewsBoardPage: React.FC = () => { return ( - {isLoading ? ( - <> - - 로딩 중... - ) : error ? ( - {error.message} - ) : ( - <> - window.open(url)} - /> - - - )} + window.open(url)} + /> + ); }; export default NewsBoardPage; -const EmptyState = styled.div` - display: flex; - justify-content: center; - align-items: center; - min-height: 100vh; - font-family: 'pretendard-bold'; - font-size: 2rem; - color: gray; - text-align: center; - padding: 0.75rem; - word-break: keep-all; -`; - const Container = styled.div` overflow-x: hidden; @@ -104,36 +96,4 @@ const Container = styled.div` padding: 0; margin: auto; } -`; - -const LoadingModal = styled.div` - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(255, 255, 255, 0.036); - backdrop-filter: blur(3px); - display: flex; - justify-content: center; - align-items: center; - z-index: 1000; -`; - -const LoadingIcon = styled.div` - border: 8px solid rgba(255, 255, 255, 0.3); - border-top: 8px solid ${theme.color.white.bold}; - border-radius: 50%; - width: 50px; - height: 50px; - animation: spin 1s linear infinite; - - @keyframes spin { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } - } `; \ No newline at end of file diff --git a/src/pages/PromotionPage/NewsPage/NewsSection.tsx b/src/pages/PromotionPage/NewsPage/NewsSection.tsx index 573c19c3..d0d637e6 100644 --- a/src/pages/PromotionPage/NewsPage/NewsSection.tsx +++ b/src/pages/PromotionPage/NewsPage/NewsSection.tsx @@ -58,30 +58,37 @@ const NewsSection: React.FC = ({ currentNewsData, onNewsClick return ( - 스튜디오아이 관련 뉴스 보기 - { - currentNewsData.map((news) => ( - handleNewsClick(news.url, news.id)} - onMouseEnter={() => !isMobile && setIsClicked(news.id)} - data-cy={`news-item-${news.id}`} - > - - {news.title} - - {news.source} | {new Date(news.pubDate).toLocaleDateString()} - - - - - - - )) - } + + 스튜디오아이 관련 뉴스 보기 + + {currentNewsData.length === 0 ? ( + 현재 올라온 뉴스가 없습니다. + ) : ( + currentNewsData.map((news) => ( + handleNewsClick(news.url, news.id)} + onMouseEnter={() => !isMobile && setIsClicked(news.id)} + data-cy={`news-item-${news.id}`} + > + + {news.title} + + {news.source} | {new Date(news.pubDate).toLocaleDateString()} + + + + + + + )) + )} + + ); + } export default NewsSection; @@ -103,16 +110,14 @@ const Container = styled.div` `; const NewsSectionIntro = styled.h3` - font-size: 21px; - font-weight: 700; + font-size: clamp(0.8rem, 3vw, 1.6rem); color: white; - margin-bottom: 20px; - max-width: 1200px; + margin-bottom: 1rem; width: 100%; + font-family: ${theme.font.medium}; - @media ${theme.media.mobile} { - font-size: 1rem; - margin-left: 1rem; + @media ${theme.media.large_tablet} { + margin-bottom: 0.5rem; } `; @@ -161,14 +166,55 @@ const ArrowIcon = styled.div` } `; +const Grid = styled.div` + width: 100%; + max-width: 75rem; + padding: 1rem; + box-sizing: border-box; + overflow-x: hidden; +`; + +const BorderLine = styled.div` + border-top: 1.5px solid white; + border-bottom: 1.5px solid white; +`; + +const NoDataMessage = styled.div` + color: ${(props) => props.theme.color.black.light}; + font-size: clamp(1.5rem, 3vw, 2.2rem); + text-align: center; + margin: 3rem 0; + line-height: 1.8; + padding: 1.5rem 2rem; + word-break: keep-all; + + @media ${(props) => props.theme.media.large_tablet} { + font-size: clamp(1.1rem, 2.5vw, 1.5rem); + margin: 2.5rem 0; + padding: 1.2rem 1.8rem; + } + + @media ${(props) => props.theme.media.tablet} { + font-size: clamp(1rem, 2vw, 1.4rem); + margin: 2rem 0; + padding: 1rem 1.5rem; + } + + @media ${(props) => props.theme.media.mobile} { + font-size: clamp(0.9rem, 2vw, 1.3rem); + margin: 1.5rem 0; + padding: 0.8rem 1.2rem; + } +`; + const NewsCard = styled.div` width: 100%; max-width: 1200px; padding: 20px 0; margin-bottom: 10px; background-color: black; - border-top: 1px solid white; - border-bottom: 1px solid white; + border-top: 1.5px solid #ccc; + cursor: pointer; display: flex; flex-direction: row; @@ -202,3 +248,5 @@ const NewsCard = styled.div` } } `; + +