diff --git a/src/assets/icons/ic-error.svg b/src/assets/icons/ic-error.svg new file mode 100644 index 00000000..9b70ebb4 --- /dev/null +++ b/src/assets/icons/ic-error.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/icons/ic-reload.svg b/src/assets/icons/ic-reload.svg new file mode 100644 index 00000000..229831a8 --- /dev/null +++ b/src/assets/icons/ic-reload.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/Report/LeftSection/Graph/SelectYear/index.tsx b/src/components/Report/LeftSection/Graph/SelectYear/index.tsx index ef3bddda..56e4f1f8 100644 --- a/src/components/Report/LeftSection/Graph/SelectYear/index.tsx +++ b/src/components/Report/LeftSection/Graph/SelectYear/index.tsx @@ -1,14 +1,20 @@ -import { useRecoilState } from 'recoil'; +import { useEffect } from 'react'; +import { useRecoilState, useRecoilValue } from 'recoil'; import { Dropdown, DropdownProps } from 'semantic-ui-react'; import styled from '@emotion/styled'; -import { selectedYearState } from '@recoil/index'; +import { headerAccommodationState, selectedYearState } from '@recoil/index'; import { initYearSelectList } from '@utils/index'; import theme from '@styles/theme'; const SelectYear = () => { const selectYearReport = initYearSelectList(); const [selectedYear, setSelectedYear] = useRecoilState(selectedYearState); + const headerAccommodation = useRecoilValue(headerAccommodationState); + + useEffect(() => { + setSelectedYear({ year: selectYearReport[0] }); + }, [headerAccommodation]); const handleSelect = ( _e: React.SyntheticEvent, diff --git a/src/components/Report/LeftSection/Graph/index.tsx b/src/components/Report/LeftSection/Graph/index.tsx index 26539ca1..1a2f2e73 100644 --- a/src/components/Report/LeftSection/Graph/index.tsx +++ b/src/components/Report/LeftSection/Graph/index.tsx @@ -1,14 +1,33 @@ import styled from '@emotion/styled'; -import { Bar } from 'react-chartjs-2'; +import { Chart } from 'react-chartjs-2'; +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + BarElement, + LineElement, + Legend, + Tooltip, + Filler, + BarController +} from 'chart.js'; +ChartJS.register( + CategoryScale, + LinearScale, + BarElement, + LineElement, + Legend, + Tooltip, + Filler, + BarController +); import { ReportGraphProps } from '@/types/report'; import SelectYear from './SelectYear'; import theme from '@styles/theme'; +import reloadIcon from '@assets/icons/ic-dashboard-reload.svg'; const Graph = ({ graphData }: { graphData: ReportGraphProps }) => { - // HACK: Notice 업데이트 정책 변경 - // utils/calculation.ts 파일 사용하여 마지막 날짜 불러오기 - const chartData = { labels: graphData.map(data => `${data.statistics_month} 월`), datasets: [ @@ -34,7 +53,12 @@ const Graph = ({ graphData }: { graphData: ReportGraphProps }) => { }; const options = { - maintainAspectRatio: false + maintainAspectRatio: false, + plugins: { + legend: { + onClick: () => undefined + } + } }; return ( @@ -44,15 +68,17 @@ const Graph = ({ graphData }: { graphData: ReportGraphProps }) => { 누적 리포트 프로모션 적용 이후 예약 현황을 알려드립니다. - (23.12.29 업데이트) + + 매월 1일 00시 00분에 업데이트 @@ -61,6 +87,10 @@ const Graph = ({ graphData }: { graphData: ReportGraphProps }) => { export default Graph; +const ReloadIcon = styled.img` + margin-left: 10px; +`; + const Container = styled.div` width: 100%; @@ -108,6 +138,9 @@ const Notice = styled.p` font-size: 12.5px; font-weight: 700; + display: flex; + align-items: flex-end; + & > span { color: #8e8e8e; font-size: 10.5px; @@ -120,7 +153,7 @@ const Notice = styled.p` `; const Update = styled.span` - margin-left: 10px; + margin-left: 3px; `; const GraphContainer = styled.div` @@ -142,7 +175,7 @@ const GraphContainer = styled.div` } `; -const BarGraph = styled(Bar)` +const BarGraph = styled(Chart)` border-radius: 16px; padding: 10px 20px; diff --git a/src/components/Report/LeftSection/index.error.tsx b/src/components/Report/LeftSection/index.error.tsx index 0f30bfaa..7be28502 100644 --- a/src/components/Report/LeftSection/index.error.tsx +++ b/src/components/Report/LeftSection/index.error.tsx @@ -1,12 +1,25 @@ import { FallbackProps } from 'react-error-boundary'; import styled from '@emotion/styled'; + import theme from '@styles/theme'; +import errorIcon from '@assets/icons/ic-error.svg'; +import reloadIcon from '@assets/icons/ic-reload.svg'; const ErrorFallback = ({ resetErrorBoundary }: FallbackProps) => { return ( - 누적 리포트 차트 에러 발생 - + + 누적 리포트 차트를 불러올 수 없습니다. + + + 다시 시도 하기 + ); }; @@ -14,6 +27,7 @@ const ErrorFallback = ({ resetErrorBoundary }: FallbackProps) => { export default ErrorFallback; const Container = styled.div` + width: 100%; min-width: 1016px; height: 880px; @@ -29,14 +43,71 @@ const Container = styled.div` background-color: white; ${theme.response.tablet} { + width: 95%; min-width: auto; - height: 100vw; + height: 300px; - margin: 30px 15px; + margin: 20px 0 30px 0; padding: 12px 15px; - justify-content: center; - align-items: center; + align-self: center; background-color: #f2f4f5; } `; + +const ErrorIcon = styled.img` + width: 60px; + height: 60px; + + ${theme.response.tablet} { + width: 40px; + height: 40px; + } +`; + +const ErrorWord = styled.span` + font-size: 18px; + font-weight: 500; + + ${theme.response.tablet} { + font-size: 13px; + } +`; + +const ReLoadButton = styled.button` + margin-top: 15px; + margin-left: 3px; + border: none; + + display: flex; + justify-content: center; + align-items: center; + + background-color: transparent; + font-size: 15px; + text-align: center; + + transition: all 0.5s; + + &:hover { + color: gray; + } + + ${theme.response.tablet} { + margin-top: 10px; + + font-size: 10px; + } +`; + +const ReloadIcon = styled.img` + width: 25px; + height: 25px; + + margin-right: 10px; + + ${theme.response.tablet} { + width: 15px; + height: 15px; + } +`; diff --git a/src/components/Report/RightSection/TotalReport/index.error.tsx b/src/components/Report/RightSection/TotalReport/index.error.tsx index 8c7752c4..7ebb542a 100644 --- a/src/components/Report/RightSection/TotalReport/index.error.tsx +++ b/src/components/Report/RightSection/TotalReport/index.error.tsx @@ -2,12 +2,24 @@ import { FallbackProps } from 'react-error-boundary'; import styled from '@emotion/styled'; import theme from '@styles/theme'; +import errorIcon from '@assets/icons/ic-error.svg'; +import reloadIcon from '@assets/icons/ic-reload.svg'; const ErrorFallback = ({ resetErrorBoundary }: FallbackProps) => { return ( - 누적 똑똑 현황 에러 발생 - + + 똑똑 누적 차트를 불러올 수 없습니다. + + + 다시 시도 하기 + ); }; @@ -34,9 +46,69 @@ const Container = styled.div` width: 90%; max-height: 250px; + margin: 20px 0; padding: 15px 15px; border-radius: 10px; + gap: 15px; + background-color: #f2f4f5; } `; + +const ErrorIcon = styled.img` + width: 35px; + height: 35px; + + ${theme.response.tablet} { + width: 40px; + height: 40px; + } +`; + +const ErrorWord = styled.span` + font-size: 14px; + font-weight: 500; + + ${theme.response.tablet} { + font-size: 13px; + } +`; + +const ReLoadButton = styled.button` + margin-top: 15px; + margin-left: 3px; + border: none; + + display: flex; + justify-content: center; + align-items: center; + + background-color: transparent; + font-size: 12px; + text-align: center; + + transition: all 0.5s; + + &:hover { + color: gray; + } + + ${theme.response.tablet} { + margin: 0; + + font-size: 10px; + } +`; + +const ReloadIcon = styled.img` + width: 20px; + height: 20px; + + margin-right: 10px; + + ${theme.response.tablet} { + width: 15px; + height: 15px; + } +`; diff --git a/src/components/Report/RightSection/TotalReport/index.tsx b/src/components/Report/RightSection/TotalReport/index.tsx index 8f1c5da6..5b1ea48f 100644 --- a/src/components/Report/RightSection/TotalReport/index.tsx +++ b/src/components/Report/RightSection/TotalReport/index.tsx @@ -31,7 +31,7 @@ export default TotalReport; const Container = styled.div` width: 100%; - height: 100%; + height: auto; padding: 30px 15px; border-radius: 20px; diff --git a/src/components/common/Layout/Header/User/UserModal/index.tsx b/src/components/common/Layout/Header/User/UserModal/index.tsx index e0acb458..a0a5e6b9 100644 --- a/src/components/common/Layout/Header/User/UserModal/index.tsx +++ b/src/components/common/Layout/Header/User/UserModal/index.tsx @@ -1,23 +1,15 @@ -import { useRef } from 'react'; import styled from '@emotion/styled'; import { UserModal, UserModalStyleProps } from '@/types/layout'; -import { useOutsideClick } from '@hooks/index'; import { getCookies } from '@utils/lib/cookies'; import theme from '@styles/theme'; -const UserModal = ({ isOpen, setIsUserModalOpen }: UserModal) => { - const modalRef = useRef(null); +const UserModal = ({ isOpen }: UserModal) => { const userName = getCookies('userName'); const userEmail = getCookies('userEmail'); - useOutsideClick(modalRef, () => setIsUserModalOpen(false)); - return ( - + {userName} {userEmail} diff --git a/src/components/common/Layout/Header/User/index.tsx b/src/components/common/Layout/Header/User/index.tsx index 27515dd4..b2a093e7 100644 --- a/src/components/common/Layout/Header/User/index.tsx +++ b/src/components/common/Layout/Header/User/index.tsx @@ -1,25 +1,28 @@ -import { useState } from 'react'; +import { useState, useRef } from 'react'; import styled from '@emotion/styled'; import user from '@assets/icons/ic-header-user.svg'; import UserModal from './UserModal'; +import { useOutsideClick } from '@hooks/index'; import theme from '@styles/theme'; const User = () => { + const modalRef = useRef(null); const [isUserModalOpen, setIsUserModalOpen] = useState(false); + useOutsideClick(modalRef, () => setIsUserModalOpen(false)); + return ( - <> +
setIsUserModalOpen(prev => !prev)} - /> - { + setIsUserModalOpen(prev => !prev); + }} /> - + +
); }; diff --git a/src/components/common/MobileDashboardHeader/index.tsx b/src/components/common/MobileDashboardHeader/index.tsx index 0436607f..a237643f 100644 --- a/src/components/common/MobileDashboardHeader/index.tsx +++ b/src/components/common/MobileDashboardHeader/index.tsx @@ -56,6 +56,7 @@ const Container = styled.div` font-size: 11px; font-weight: 700; + z-index: 100; } `; diff --git a/src/pages/Report/index.tsx b/src/pages/Report/index.tsx index dd3c8002..c4bd0437 100644 --- a/src/pages/Report/index.tsx +++ b/src/pages/Report/index.tsx @@ -30,13 +30,19 @@ export default Report; const Container = styled.div` width: 100%; + height: auto; display: flex; - gap: 10px; + justify-content: space-between; + gap: 25px; - background-color: white; + background-color: #f2f3f5; ${theme.response.tablet} { + height: auto; + flex-direction: column-reverse; + + background-color: white; } `; diff --git a/src/recoil/atoms/selectedYearState.ts b/src/recoil/atoms/selectedYearState.ts index 7e29632f..b3c567c0 100644 --- a/src/recoil/atoms/selectedYearState.ts +++ b/src/recoil/atoms/selectedYearState.ts @@ -1,12 +1,8 @@ import { atom } from 'recoil'; -import { recoilPersist } from 'recoil-persist'; - -const { persistAtom } = recoilPersist(); const selectedYearState = atom<{ year: number }>({ key: 'selectYearState', - default: { year: 0 }, - effects_UNSTABLE: [persistAtom] + default: { year: new Date().getFullYear() } }); export default selectedYearState; diff --git a/src/types/layout.ts b/src/types/layout.ts index aa24c354..e3889131 100644 --- a/src/types/layout.ts +++ b/src/types/layout.ts @@ -46,7 +46,6 @@ export type CustomNavLink = { // User export type UserModal = { isOpen: boolean; - setIsUserModalOpen: React.Dispatch>; }; export type UserModalStyleProps = {