diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b333aa03a..9cd5cad75 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -5,10 +5,19 @@ 3. 새로운 모듈 설치시 PR message에 기재할 것. 4. PR 올리기전에 branch 반드시 확인할 것. --> - ## 📌 개요 - - - ## 💻 작업사항 - - - ## ✅ 변경로직 - - - + +## 📌 개요 + +- + +## 💻 작업사항 + +- + +## ✅ 변경로직 + +- + +## 💡관련 Issue + +- diff --git a/.gitignore b/.gitignore index e3a4e3ac6..8537bf7a5 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,7 @@ yarn-error.log* .eslintcache #cypressConfig -cypress.config.* \ No newline at end of file +cypress.config.* + +#siwolee: ignore local file I can't remove +*anima* \ No newline at end of file diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 263ca1299..858b3d5ed 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -2,6 +2,11 @@ import React from 'react'; import type { Preview } from '@storybook/react'; import { StoryFn } from '@storybook/react'; import { RecoilRoot } from 'recoil'; +import { QueryClient } from 'react-query'; +import { QueryClientProvider } from 'react-query'; +import 'styles/globals.css'; + +const queryClient = new QueryClient(); const preview: Preview = { parameters: { @@ -13,14 +18,39 @@ const preview: Preview = { }, }, backgrounds: { - default: 'purple', - values: [{ name: 'purple', value: '#301451' }], + default: 'light-bg', + values: [ + { name: 'purple', value: '#301451' }, + { + name: 'light-bg', + value: 'linear-gradient(180deg, #c9c9c9 0%, #6d5b93 100%)', + }, + { + name: 'dark-bg', + value: 'linear-gradient(180deg, #6d5b93 0%, #301451 100%)', + }, + ], }, }, decorators: [ (Story: StoryFn) => ( - + + + + ), ], diff --git a/Layout/AdminLayout.tsx b/Layout/AdminLayout.tsx new file mode 100644 index 000000000..9a23d7fc6 --- /dev/null +++ b/Layout/AdminLayout.tsx @@ -0,0 +1,37 @@ +import { usePathname } from 'next/navigation'; +import AdminReject from 'components/admin/AdminReject'; +import AdminLayout from 'components/admin/Layout'; +import AgendaModalProvider from 'components/agenda/modal/AgendaModalProvider'; +import ModalProvider from 'components/takgu/modal/ModalProvider'; +import { useUser } from 'hooks/agenda/Layout/useUser'; + +type AdminLayoutProps = { + children: React.ReactNode; +}; + +function AdminAppLayout({ children }: AdminLayoutProps) { + const user = useUser(); + const presentPath = usePathname(); + + // 사용자 정보가 없거나 관리자가 아닐 경우 + if (!user || !user.intraId) return null; + + if (!user.isAdmin) return ; + + // 모달 제공자 결정 + let ModalProviderComponent; + if (presentPath.includes('admin/takgu')) { + ModalProviderComponent = ModalProvider; + } else if (presentPath.includes('admin/agenda')) { + ModalProviderComponent = AgendaModalProvider; + } + + return ( + + {children} + {ModalProviderComponent && } + + ); +} + +export default AdminAppLayout; diff --git a/Layout/AgendaLayout.tsx b/Layout/AgendaLayout.tsx new file mode 100644 index 000000000..ebedb256f --- /dev/null +++ b/Layout/AgendaLayout.tsx @@ -0,0 +1,40 @@ +import { instanceInAgenda } from 'utils/axios'; +import AgendaHeader from 'components/agenda/Layout/AgendaHeader'; +import AgendaModalProvider from 'components/agenda/modal/AgendaModalProvider'; +import Footer from 'components/takgu/Layout/Footer'; +import { useUser } from 'hooks/agenda/Layout/useUser'; +import useAxiosWithToast from 'hooks/useAxiosWithToast'; +import styles from 'styles/agenda/Layout/Layout.module.scss'; + +type AgendaLayoutProps = { + children: React.ReactNode; +}; + +function AgendaAppLayout({ children }: AgendaLayoutProps) { + useAxiosWithToast(instanceInAgenda); // API의 성공 실패 스낵바로 알리는 기능 + + const user = useUser(); + + if (!user || !user.intraId) return null; + + const scrollToTop = () => { + window.scrollTo({ top: 0, behavior: 'smooth' }); + }; + + return ( + <> + +
+
+ {children} +
+ +
+ + + ); +} + +export default AgendaAppLayout; diff --git a/Layout/LayoutProvider.tsx b/Layout/LayoutProvider.tsx new file mode 100644 index 000000000..9d1ee20b9 --- /dev/null +++ b/Layout/LayoutProvider.tsx @@ -0,0 +1,27 @@ +import AdminAppLayout from 'Layout/AdminLayout'; +import AgendaAppLayout from 'Layout/AgendaLayout'; +import TakguAppLayout from 'Layout/TakguLayout'; +import { usePathname } from 'hooks/agenda/Layout/usePathname'; + +type LayoutProviderProps = { + children: React.ReactNode; +}; + +// 현재 페이지가 어떤 레이아웃을 사용할지 결정 +// 로그인 스테이트 등은 각 레이아웃에서 확인 +const LayoutProvider = ({ children }: LayoutProviderProps) => { + const app = usePathname(); + switch (app) { + case '': + case 'agenda': + return {children}; + case 'takgu': + return {children}; + case 'admin': + return {children}; + default: + return <>{children}; + } +}; + +export default LayoutProvider; diff --git a/Layout/TakguLayout.tsx b/Layout/TakguLayout.tsx new file mode 100644 index 000000000..0a781bcd0 --- /dev/null +++ b/Layout/TakguLayout.tsx @@ -0,0 +1,96 @@ +import { useRecoilValue } from 'recoil'; +import { openCurrentMatchState } from 'utils/recoil/takgu/match'; +import AdminReject from 'components/admin/AdminReject'; +import AdminLayout from 'components/admin/Layout'; +import CurrentMatch from 'components/takgu/Layout/CurrentMatch'; +import Footer from 'components/takgu/Layout/Footer'; +import Header from 'components/takgu/Layout/Header'; +import HeaderStateContext from 'components/takgu/Layout/HeaderContext'; +import MainPageProfile from 'components/takgu/Layout/MainPageProfile'; +import Megaphone from 'components/takgu/Layout/MegaPhone'; +import RecruitLayout from 'components/takgu/recruit/RecruitLayout'; +import Statistics from 'pages/takgu/statistics'; +import { usePathname } from 'hooks/agenda/Layout/usePathname'; +import useAnnouncementCheck from 'hooks/takgu/Layout/useAnnouncementCheck'; +import useGetUserSeason from 'hooks/takgu/Layout/useGetUserSeason'; +import useLiveCheck from 'hooks/takgu/Layout/useLiveCheck'; +import useSetAfterGameModal from 'hooks/takgu/Layout/useSetAfterGameModal'; +import { useUser } from 'hooks/takgu/Layout/useUser'; +import useAxiosResponse from 'hooks/useAxiosResponse'; +import styles from 'styles/takgu/Layout/Layout.module.scss'; +import PlayButton from '../components/takgu/Layout/PlayButton'; +import UserLayout from '../components/takgu/Layout/UserLayout'; +import ModalProvider from '../components/takgu/modal/ModalProvider'; +import CustomizedSnackbars from '../components/toastmsg/toastmsg'; + +type TakguLayoutProps = { + children: React.ReactNode; +}; + +function TakguLayout({ children }: TakguLayoutProps) { + const user = useUser(); + const presentPath = usePathname(); + const openCurrentMatch = useRecoilValue(openCurrentMatchState); + + useAxiosResponse(); + useGetUserSeason(presentPath); + useSetAfterGameModal(); + useLiveCheck(presentPath); + useAnnouncementCheck(presentPath); + + if (!user || !user.intraId) return null; + + switch (true) { + case presentPath.includes('takgu/admin'): + if (!user.isAdmin) return ; + return {children}; + + case presentPath.includes('takgu/recruit'): + return {children}; + + case presentPath === 'takgu/statistics' && user.isAdmin: + return ( + + + + ); + + case presentPath.includes('takgu'): + return ( + <> + + +
+ + +
+ + {openCurrentMatch && } + {presentPath === '/' && } +
+ {children} +