diff --git a/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/DashboardCard.module.css b/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/DashboardCard.module.css new file mode 100644 index 0000000..96fd49f --- /dev/null +++ b/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/DashboardCard.module.css @@ -0,0 +1,48 @@ +.dashboardCard.dashboardCard { + padding: 15px 20px; + background: var(--white); + border: 1px solid var(--gray-300); + border-radius: 8px; + transition: background-color 0.3s ease; +} + +.dashboardCard.dashboardCard:hover { + background: var(--violet-light); +} + +.titleContainer { + display: flex; + width: 100%; + justify-content: space-between; + align-items: center; + gap: 12px; +} + +.dot { + width: 8px; + height: 8px; + border-radius: 999px; +} + +.title, +.crown { + font-size: 18px; + font-weight: 500; +} + +.title { + color: var(--black-100); + font-size: 14px; + font-weight: 600; +} + +.arrowWrapper { + flex: 1; + text-align: right; +} + +@media screen and (min-width: 768px) { + .title { + font-size: 16px; + } +} diff --git a/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/DashboardCard.tsx b/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/DashboardCard.tsx new file mode 100644 index 0000000..d37c556 --- /dev/null +++ b/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/DashboardCard.tsx @@ -0,0 +1,39 @@ +import type { Dashboard } from '@/app/(with-header-sidebar)/mydashboard/_types/dashboards'; +import Image from 'next/image'; +import { useRouter } from 'next/navigation'; +import Button from '@/components/Button'; +import styles from './DashboardCard.module.css'; + +export default function DashboardCard({ + id, + color, + title, + createdByMe, +}: Dashboard) { + const router = useRouter(); + + return ( + + ); +} diff --git a/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/Dashboards.module.css b/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/Dashboards.module.css new file mode 100644 index 0000000..98fae96 --- /dev/null +++ b/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/Dashboards.module.css @@ -0,0 +1,46 @@ +.dashboardsWrapper { + display: grid; + grid-template-rows: repeat(6, 1fr); + grid-template-columns: 1fr; + gap: 8px; +} + +.addDashboard.addDashboard { + background: var(--white); + color: var(--black-100); + font-size: 14px; + font-weight: 600; + padding: 15px 20px; + background: var(--white); + border: 1px solid var(--gray-300); + border-radius: 8px; + display: flex; + justify-content: center; + align-items: center; + gap: 12px; + transition: background-color 0.3s ease; +} + +.addDashboard.addDashboard:hover { + background-color: var(--violet-light); +} + +.addIconWrapper { + border-radius: 4px; + background: var(--violet-light); + display: inline-flex; + justify-content: center; + align-items: center; +} + +@media screen and (min-width: 768px) { + .dashboardsWrapper { + grid-template-rows: repeat(3, 1fr); + grid-template-columns: repeat(2, 1fr); + gap: 10px; + } + + .addDashboard.addDashboard { + font-size: 16px; + } +} diff --git a/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/Dashboards.tsx b/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/Dashboards.tsx new file mode 100644 index 0000000..8320cd2 --- /dev/null +++ b/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/Dashboards.tsx @@ -0,0 +1,44 @@ +'use client'; + +import useDashboards from '../../_hooks/useDashboards'; +import Pagination from './Pagination'; +import DashboardCard from './DashboardCard'; +import Button from '@/components/Button'; +import Image from 'next/image'; +import styles from './Dashboards.module.css'; + +const PAGE_SIZE = 5; + +export default function Dashboards() { + const { page, dashboards, totalPages, handlePageChange } = useDashboards({ + pageSize: PAGE_SIZE, + }); + + return ( +
+
+ + {dashboards.map((board) => ( + + ))} +
+ {dashboards.length > 0 && ( + + )} +
+ ); +} diff --git a/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/Pagination.module.css b/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/Pagination.module.css new file mode 100644 index 0000000..280ae13 --- /dev/null +++ b/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/Pagination.module.css @@ -0,0 +1,41 @@ +.pagination { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 16px; + margin-top: 16px; +} + +.pageNavigation { + color: var(--black-100); + font-size: 14px; + font-weight: 400; +} + +.arrowLeft.arrowLeft, +.arrowRight.arrowRight { + border: 1px solid var(--gray-300); + background: var(--white); + width: 40px; + height: 40px; + transition: background-color 0.3s ease; +} + +.arrowLeft.arrowLeft { + border-radius: 4px 0px 0px 4px; +} + +.arrowRight.arrowRight { + border-radius: 0px 4px 4px 0px; + border-left: none; +} + +.arrowLeft.arrowLeft:hover, +.arrowRight.arrowRight:hover { + background: var(--violet-light); +} + +.arrowLeft.arrowLeft:disabled, +.arrowRight.arrowRight:disabled { + background: var(--white); +} diff --git a/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/Pagination.tsx b/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/Pagination.tsx new file mode 100644 index 0000000..26a8a17 --- /dev/null +++ b/src/app/(with-header-sidebar)/mydashboard/_components/dashboards/Pagination.tsx @@ -0,0 +1,58 @@ +import Image from 'next/image'; +import Button from '@/components/Button'; +import styles from './Pagination.module.css'; + +export default function Pagination({ + currentPage, + totalPages, + onPageChange, +}: { + currentPage: number; + totalPages: number; + onPageChange: (direction: 'next' | 'prev') => void; +}) { + const isFirstPage = currentPage === 1; + const isLastPage = currentPage >= totalPages; + + const leftArrowSrc = isFirstPage + ? '/icons/arrow_left_light.svg' + : '/icons/arrow_left.svg'; + + const rightArrowSrc = isLastPage + ? '/icons/arrow_right_light.svg' + : '/icons/arrow_right.svg'; + + return ( +
+ {`${totalPages} 페이지 중 ${currentPage}`} +
+ + +
+
+ ); +} diff --git a/src/app/(with-header-sidebar)/mydashboard/_components/invitations/Invitations.module.css b/src/app/(with-header-sidebar)/mydashboard/_components/invitations/Invitations.module.css new file mode 100644 index 0000000..d1a3a48 --- /dev/null +++ b/src/app/(with-header-sidebar)/mydashboard/_components/invitations/Invitations.module.css @@ -0,0 +1,43 @@ +.invitations { + background-color: var(--white); + border-radius: 8px; + min-height: 260px; + margin-top: 16px; + padding: 24px 20px 80px 20px; +} + +.title { + color: var(--black-100); + font-size: 14px; + font-weight: 700; +} + +.descriptionWrapper { + text-align: center; + margin-top: 60px; +} + +.description { + color: var(--gray-400); + font-size: 12px; + font-weight: 400; +} + +@media screen and (min-width: 768px) { + .invitations { + margin-top: 40px; + } + + .title { + font-size: 24px; + } + + .descriptionWrapper { + text-align: center; + margin-top: 64px; + } + + .description { + font-size: 18px; + } +} diff --git a/src/app/(with-header-sidebar)/mydashboard/_components/invitations/Invitations.tsx b/src/app/(with-header-sidebar)/mydashboard/_components/invitations/Invitations.tsx new file mode 100644 index 0000000..12f1b0d --- /dev/null +++ b/src/app/(with-header-sidebar)/mydashboard/_components/invitations/Invitations.tsx @@ -0,0 +1,19 @@ +import Image from 'next/image'; +import styles from './Invitations.module.css'; + +export default function Invitations() { + return ( +
+

초대받은 대시보드

+
+ 메일없음 이미지 +

아직 초대받은 대시보드가 없어요

+
+
+ ); +} diff --git a/src/app/(with-header-sidebar)/mydashboard/_hooks/useApi.ts b/src/app/(with-header-sidebar)/mydashboard/_hooks/useApi.ts index b392917..db41251 100644 --- a/src/app/(with-header-sidebar)/mydashboard/_hooks/useApi.ts +++ b/src/app/(with-header-sidebar)/mydashboard/_hooks/useApi.ts @@ -1,6 +1,6 @@ import { useState, useEffect, useCallback } from 'react'; import { AxiosResponse, AxiosError } from 'axios'; -import axiosInstance from '../_utils/axiosInstance'; +import axiosInstance from '../_lib/axiosInstance'; type UseApiFetchReturnType = { data: T | null; diff --git a/src/app/(with-header-sidebar)/mydashboard/_hooks/useDashboards.ts b/src/app/(with-header-sidebar)/mydashboard/_hooks/useDashboards.ts new file mode 100644 index 0000000..9904687 --- /dev/null +++ b/src/app/(with-header-sidebar)/mydashboard/_hooks/useDashboards.ts @@ -0,0 +1,29 @@ +import { useState } from 'react'; +import useApi from '@/app/(with-header-sidebar)/mydashboard/_hooks/useApi'; +import type { GetDashboardsResponse } from '@/app/(with-header-sidebar)/mydashboard/_types/dashboards'; + +interface UseDashboardsParams { + pageSize: number; +} + +export default function useDashboards({ pageSize }: UseDashboardsParams) { + const [page, setPage] = useState(1); + const { data } = useApi('/dashboards', { + method: 'GET', + params: { navigationMethod: 'pagination', page, size: pageSize }, + }); + + const dashboards = data?.dashboards ?? []; + const totalCount = data?.totalCount ?? 0; + const totalPages = Math.ceil(totalCount / pageSize); + + const handlePageChange = (direction: 'next' | 'prev') => { + setPage((prevPage) => { + if (direction === 'next' && prevPage < totalPages) return prevPage + 1; + if (direction === 'prev' && prevPage > 1) return prevPage - 1; + return prevPage; + }); + }; + + return { page, dashboards, totalPages, handlePageChange }; +} diff --git a/src/app/(with-header-sidebar)/mydashboard/_utils/axiosInstance.ts b/src/app/(with-header-sidebar)/mydashboard/_lib/axiosInstance.ts similarity index 79% rename from src/app/(with-header-sidebar)/mydashboard/_utils/axiosInstance.ts rename to src/app/(with-header-sidebar)/mydashboard/_lib/axiosInstance.ts index 858fb8c..0ab2c3a 100644 --- a/src/app/(with-header-sidebar)/mydashboard/_utils/axiosInstance.ts +++ b/src/app/(with-header-sidebar)/mydashboard/_lib/axiosInstance.ts @@ -1,4 +1,4 @@ -import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'; +import axios, { AxiosInstance } from 'axios'; import { BASE_URL } from '@/constants/urls'; const axiosInstance: AxiosInstance = axios.create({ @@ -24,10 +24,4 @@ axiosInstance.interceptors.request.use( } ); -export const apiCall = async ( - config: AxiosRequestConfig -): Promise> => { - return axiosInstance.request(config); -}; - export default axiosInstance; diff --git a/src/app/(with-header-sidebar)/mydashboard/_lib/dashboardsApi.ts b/src/app/(with-header-sidebar)/mydashboard/_lib/dashboardsApi.ts new file mode 100644 index 0000000..981abbe --- /dev/null +++ b/src/app/(with-header-sidebar)/mydashboard/_lib/dashboardsApi.ts @@ -0,0 +1,21 @@ +import axiosInstance from './axiosInstance'; +import { GetDashboardsRequestParams } from '../_types/dashboards'; + +export const dashboardsApi = { + // getMyDashboards: (params: GetDashboardsRequestParams) => + // axiosInstance.get('/dashboards'), + // upcoming: () => axiosInstance.get('movie/upcoming'), + + getMyDashboards: (params: GetDashboardsRequestParams) => + axiosInstance.get('/dashboards', { + params, + }), + + // 첫번째 인자 url 두번째에 쿼리 prams를 넘겨준다. + // searchMovie: (terms) => + // axiosInstance.get(`search/movie`, { + // params: { + // query: terms, + // }, + // }), +}; diff --git a/src/app/(with-header-sidebar)/mydashboard/page.module.css b/src/app/(with-header-sidebar)/mydashboard/page.module.css new file mode 100644 index 0000000..38f72a7 --- /dev/null +++ b/src/app/(with-header-sidebar)/mydashboard/page.module.css @@ -0,0 +1,5 @@ +.mydashboard { + height: 100%; + background: var(--gray-100); + padding: 20px 24px; +} diff --git a/src/app/(with-header-sidebar)/mydashboard/page.tsx b/src/app/(with-header-sidebar)/mydashboard/page.tsx index 6540575..4c4304e 100644 --- a/src/app/(with-header-sidebar)/mydashboard/page.tsx +++ b/src/app/(with-header-sidebar)/mydashboard/page.tsx @@ -1,3 +1,12 @@ +import Dashboards from './_components/dashboards/Dashboards'; +import Invitations from './_components/invitations/Invitations'; +import styles from './page.module.css'; + export default function Page() { - return
my page
; + return ( +
+ + +
+ ); } diff --git a/src/components/MainContainer.module.css b/src/components/MainContainer.module.css index e69de29..d466598 100644 --- a/src/components/MainContainer.module.css +++ b/src/components/MainContainer.module.css @@ -0,0 +1,3 @@ +.main { + height: 100%; +} diff --git a/src/components/MainContainer.tsx b/src/components/MainContainer.tsx index edf065c..5b4e4d3 100644 --- a/src/components/MainContainer.tsx +++ b/src/components/MainContainer.tsx @@ -6,7 +6,7 @@ export default function MainContainer({ children }: { children: ReactNode }) { return ( <>
-
{children}
+
{children}
); } diff --git a/src/components/header/Header.module.css b/src/components/header/Header.module.css index d801a97..6f3a97d 100644 --- a/src/components/header/Header.module.css +++ b/src/components/header/Header.module.css @@ -21,7 +21,7 @@ gap: 6px; } -.button { +.button.button { padding: 6px 12px; display: flex; align-items: center; @@ -32,6 +32,11 @@ color: var(--gray-500); font-size: 14px; font-weight: 500; + transition: background-color 0.3s ease; +} + +.button.button:hover { + background-color: var(--violet-light); } .icon { @@ -72,6 +77,7 @@ display: flex; flex-direction: column; gap: 5px; + background: var(--white); } .myMenu div { @@ -81,7 +87,7 @@ .myMenu div:hover { cursor: pointer; - background: var(--gray-200); + background: var(--violet-light); } @media screen and (min-width: 768px) { diff --git a/src/components/sidebar/Dashboards.module.css b/src/components/sidebar/Dashboards.module.css index 9a5290c..d30729a 100644 --- a/src/components/sidebar/Dashboards.module.css +++ b/src/components/sidebar/Dashboards.module.css @@ -32,21 +32,27 @@ font-weight: 500; } -.arrowLeft, -.arrowRight { +.arrowLeft.arrowLeft, +.arrowRight.arrowRight { border: 1px solid var(--gray-300); border-right: none; border-radius: 0; background: var(--white); height: 35px; + transition: background-color 0.3s ease; } -.arrowRight { +.arrowRight.arrowRight { border-top: none; } -.arrowLeft:disabled, -.arrowRight:disabled { +.arrowLeft.arrowLeft:hover, +.arrowRight.arrowRight:hover { + background-color: var(--violet-light); +} + +.arrowLeft.arrowLeft:disabled, +.arrowRight.arrowRight:disabled { background: var(--white); } @@ -55,7 +61,12 @@ margin-top: 10px; display: flex; flex-direction: column; - gap: 15px; + } + + .titleContainer { + justify-content: flex-start; + gap: 16px; + padding: 20px 10px; } .title, @@ -67,28 +78,22 @@ margin-left: -8px; } - .titleContainer { - justify-content: flex-start; - gap: 16px; - padding: 8px 10px; - } - .arrowWrapper { margin-top: 20px; } - .arrowLeft, - .arrowRight { + .arrowLeft.arrowLeft, + .arrowRight.arrowRight { border: 1px solid var(--gray-300); width: 40px; height: 40px; } - .arrowLeft { + .arrowLeft.arrowLeft { border-radius: 4px 0px 0px 4px; } - .arrowRight { + .arrowRight.arrowRight { border-radius: 0px 4px 4px 0px; } } diff --git a/src/components/sidebar/Dashboards.tsx b/src/components/sidebar/Dashboards.tsx index c028efc..7e359f6 100644 --- a/src/components/sidebar/Dashboards.tsx +++ b/src/components/sidebar/Dashboards.tsx @@ -1,35 +1,21 @@ import Link from 'next/link'; import { usePathname } from 'next/navigation'; -import type { - Dashboard, - GetDashboardsResponse, -} from '@/app/(with-header-sidebar)/mydashboard/_types/dashboards'; -import useApi from '@/app/(with-header-sidebar)/mydashboard/_hooks/useApi'; +import type { Dashboard } from '@/app/(with-header-sidebar)/mydashboard/_types/dashboards'; import Image from 'next/image'; import Button from '../Button'; -import { useState } from 'react'; import styles from './Dashboards.module.css'; +import useDashboards from '@/app/(with-header-sidebar)/mydashboard/_hooks/useDashboards'; const PAGE_SIZE = 12; export default function Dashboards() { - const [page, setPage] = useState(1); - const { data } = useApi('/dashboards', { - method: 'GET', - params: { navigationMethod: 'pagination', page, size: PAGE_SIZE }, + const { page, dashboards, totalPages, handlePageChange } = useDashboards({ + pageSize: PAGE_SIZE, }); - const dashboards = data?.dashboards ?? []; - const totalCount = data?.totalCount ?? 0; - const totalPages = Math.ceil(totalCount / PAGE_SIZE); - - const handlePageChange = (direction: 'next' | 'prev') => { - setPage((prevPage) => { - if (direction === 'next' && prevPage < totalPages) return prevPage + 1; - if (direction === 'prev' && prevPage > 1) return prevPage - 1; - return prevPage; - }); - }; + if (dashboards.length === 0) { + return null; + } return (
diff --git a/src/components/sidebar/SideBar.module.css b/src/components/sidebar/SideBar.module.css index 5fa9178..f54fec4 100644 --- a/src/components/sidebar/SideBar.module.css +++ b/src/components/sidebar/SideBar.module.css @@ -5,7 +5,7 @@ gap: 15px; } -.logo { +.logo.logo { background-color: transparent; padding-top: 22px; margin-bottom: 10px; @@ -20,7 +20,7 @@ display: none; } -.addButton { +.addButton.addButton { background-color: transparent; width: 20px; }