diff --git a/index.html b/index.html index 306d0ed..6fd2c3b 100644 --- a/index.html +++ b/index.html @@ -6,14 +6,15 @@ + content="https://github.com/capstone-five-ai/Qtudy-FE/assets/87255791/7baf8987-f3ae-4730-ac4e-f820d7c539bc" /> - + + Qtudy diff --git a/src/assets/logo/qtudy.svg b/src/assets/logo/qtudy.svg index d2f5d2e..31601aa 100644 --- a/src/assets/logo/qtudy.svg +++ b/src/assets/logo/qtudy.svg @@ -1,19 +1,5 @@ - - - - - - - - - - - - - - - - - - + + + + diff --git a/src/components/Button/GenerateButton.tsx b/src/components/Button/GenerateButton.tsx index 140e9d0..3c9c807 100644 --- a/src/components/Button/GenerateButton.tsx +++ b/src/components/Button/GenerateButton.tsx @@ -5,7 +5,7 @@ function GenerateButton(props: React.ButtonHTMLAttributes) { return ( - Generate + 생성하기 ); } diff --git a/src/components/Button/PlainButton.tsx b/src/components/Button/PlainButton.tsx index 93f36ec..ee282de 100644 --- a/src/components/Button/PlainButton.tsx +++ b/src/components/Button/PlainButton.tsx @@ -50,12 +50,12 @@ const getVariantStyle = ($variant: PlainButtonVariant) => { case 'primary': return css` color: ${({ theme }) => theme.colors.grayScale09}; - background: ${({ theme }) => theme.colors.mainMintGra}; - box-shadow: ${({ theme }) => - `4px 2px 16px 0px ${theme.colors.mainMintShadow}`}; + background: ${({ theme }) => theme.colors.mainMint}; &:hover { - background: ${({ theme }) => theme.colors.mainMintDarkGra}; + background: ${({ theme }) => theme.colors.mainMintMedium}; + box-shadow: 0px 4px 12px 0px + ${({ theme }) => theme.colors.mainMintShadow}; } `; @@ -63,7 +63,6 @@ const getVariantStyle = ($variant: PlainButtonVariant) => { return css` color: ${({ theme }) => theme.colors.grayScale03}; background: ${({ theme }) => theme.colors.grayScale08}; - box-shadow: 4px 2px 16px 0px rgba(189, 189, 189, 0.28); border: 0.8px solid ${({ theme }) => theme.colors.grayScale06}; &:hover { @@ -88,6 +87,8 @@ const StyledButton = styled.button<{ ${({ $size }) => getSizeStyle($size)} ${({ $variant }) => getVariantStyle($variant)} + transition: all 0.3s ease; + &:disabled { background: ${({ theme }) => theme.colors.grayScale06}; color: ${({ theme }) => theme.colors.grayScale09}; diff --git a/src/components/Button/SaveToCategoryButton.tsx b/src/components/Button/SaveToCategoryButton.tsx index a8ecb4a..985cbb8 100644 --- a/src/components/Button/SaveToCategoryButton.tsx +++ b/src/components/Button/SaveToCategoryButton.tsx @@ -1,13 +1,21 @@ import { ReactComponent as SaveIcon } from '@/assets/icons/save.svg'; import PlainButton from '@/components/Button/PlainButton'; +import { CATEGORY_TYPE } from '@/constants'; +import { ServiceType } from '@/types/category.type'; -function SaveToCategoryButton( - props: React.ButtonHTMLAttributes -) { +interface SaveToCategoryButtonProps + extends React.ButtonHTMLAttributes { + generateType: ServiceType; +} + +function SaveToCategoryButton({ + generateType, + ...props +}: SaveToCategoryButtonProps) { return ( - Save to Category + 카테고리에 {CATEGORY_TYPE[generateType]} 추가 ); } diff --git a/src/components/Card/GenerateMethodCard.tsx b/src/components/Card/GenerateMethodCard.tsx index de5384e..fc8870b 100644 --- a/src/components/Card/GenerateMethodCard.tsx +++ b/src/components/Card/GenerateMethodCard.tsx @@ -57,11 +57,11 @@ const StyledCard = styled.button` align-items: center; border-radius: 12px; + border: 1px solid rgba(224, 224, 224, 0.8); background: ${({ theme }) => theme.colors.grayScale09}; - box-shadow: 0px 0px 8px 0px rgba(189, 189, 189, 0.2); &:hover { - box-shadow: 0px 0px 8px 0px rgba(54, 189, 180, 0.24); + border-color: ${({ theme }) => theme.colors.mainMint}; .upload-icon { path { diff --git a/src/components/Form/QuizGenerationForm.tsx b/src/components/Form/QuizGenerationForm.tsx index 1d7ec8e..16b95ff 100644 --- a/src/components/Form/QuizGenerationForm.tsx +++ b/src/components/Form/QuizGenerationForm.tsx @@ -32,6 +32,10 @@ function QuizGenerationForm({ setIsCommentOpen(false); }, [quizType]); + useEffect(() => { + if (quizContent.problemCommentary === '') setIsCommentOpen(true); + }, [showWarning]); + const handleChangeQuestion = ( e: React.ChangeEvent, _: number diff --git a/src/components/Loader/GenerateLoader.tsx b/src/components/Loader/GenerateLoader.tsx index ad865aa..9579cb0 100644 --- a/src/components/Loader/GenerateLoader.tsx +++ b/src/components/Loader/GenerateLoader.tsx @@ -121,7 +121,7 @@ const StyledImageContainer = styled.div` } .logo { - height: 53px; + height: 29px; position: absolute; top: 50%; left: 50%; diff --git a/src/components/Loader/SuspenseLoading.tsx b/src/components/Loader/SuspenseLoading.tsx new file mode 100644 index 0000000..024220c --- /dev/null +++ b/src/components/Loader/SuspenseLoading.tsx @@ -0,0 +1,5 @@ +function SuspenseLoading() { + return
Loading...
; +} + +export default SuspenseLoading; diff --git a/src/components/Modal/DeleteItemModal.tsx b/src/components/Modal/DeleteItemModal.tsx index 6fb5830..9e86f54 100644 --- a/src/components/Modal/DeleteItemModal.tsx +++ b/src/components/Modal/DeleteItemModal.tsx @@ -55,7 +55,7 @@ const StyledLinkIcon = styled.div` border-radius: 18px; background: ${({ theme }) => theme.colors.grayScale09}; - box-shadow: 0px 4px 8px 0px rgba(54, 189, 180, 0.24); + box-shadow: 0px 4px 8px 0px ${({ theme }) => theme.colors.mainMintShadow}; svg { width: 24px; diff --git a/src/components/Modal/ShareLinkModal.tsx b/src/components/Modal/ShareLinkModal.tsx index 21e2230..0877971 100644 --- a/src/components/Modal/ShareLinkModal.tsx +++ b/src/components/Modal/ShareLinkModal.tsx @@ -69,7 +69,7 @@ const StyledLinkIcon = styled.div` border-radius: 18px; background: ${({ theme }) => theme.colors.mainMint}; - box-shadow: 0px 4px 8px 0px rgba(54, 189, 180, 0.24); + box-shadow: 0px 4px 8px 0px ${({ theme }) => theme.colors.mainMintShadow}; svg { width: 24px; diff --git a/src/components/Navigation/MenuBar.tsx b/src/components/Navigation/MenuBar.tsx index c676bc1..17c6dea 100644 --- a/src/components/Navigation/MenuBar.tsx +++ b/src/components/Navigation/MenuBar.tsx @@ -93,7 +93,7 @@ const StyledActiveIcon = styled.div<{ $isActive: boolean }>` $isActive && css` background: ${theme.colors.mainMint}; - box-shadow: 2px 1px 4px rgba(54, 189, 180, 0.24); + box-shadow: 2px 1px 4px ${({ theme }) => theme.colors.mainMintShadow}; `} `; @@ -110,7 +110,7 @@ const StyledMenuButton = styled.button<{ $isActive: boolean }>` color: ${({ theme }) => theme.colors.grayScale02}; ${StyledActiveIcon} { background: ${(props) => props.theme.colors.mainMint}; - box-shadow: 2px 1px 4px rgba(54, 189, 180, 0.24); + box-shadow: 2px 1px 4px ${({ theme }) => theme.colors.mainMintShadow}; } } `; diff --git a/src/components/Navigation/TopNavigation.tsx b/src/components/Navigation/TopNavigation.tsx index 1880450..f038bf6 100644 --- a/src/components/Navigation/TopNavigation.tsx +++ b/src/components/Navigation/TopNavigation.tsx @@ -35,7 +35,7 @@ function TopNavigation() {
- +
@@ -55,7 +55,6 @@ export default TopNavigation; const StyledContainer = styled.div` width: 100%; - background: ${({ theme }) => theme.colors.mainMintLight}; `; const StyledInnerContainer = styled.div` diff --git a/src/containers/CategoryDetailPage/CategorySidebar.tsx b/src/containers/CategoryDetailPage/CategorySidebar.tsx index f7853da..e81bbc5 100644 --- a/src/containers/CategoryDetailPage/CategorySidebar.tsx +++ b/src/containers/CategoryDetailPage/CategorySidebar.tsx @@ -159,6 +159,7 @@ function CategorySidebar({ setShowAddCategoryModal(true)} disabled={!isAuthenticated} /> diff --git a/src/containers/CategoryPage/CategoryItem/CategoryItem.style.ts b/src/containers/CategoryPage/CategoryItem/CategoryItem.style.ts index b3c8df5..ada141b 100644 --- a/src/containers/CategoryPage/CategoryItem/CategoryItem.style.ts +++ b/src/containers/CategoryPage/CategoryItem/CategoryItem.style.ts @@ -12,8 +12,7 @@ export const StyledCategoryItemContainer = styled.button<{ padding: ${(props) => props.$itemType === 'QUIZ' ? '18.5px' : '19.5px 19px'}; border-radius: 8px; - border: 1px solid transparent; - box-shadow: 0px 0px 12px 0px rgba(189, 189, 189, 0.2); + border: 1px solid rgba(224, 224, 224, 0.8); background: ${(props) => props.theme.colors.grayScale09}; .icon { @@ -25,8 +24,7 @@ export const StyledCategoryItemContainer = styled.button<{ } &:hover { - border-color: rgba(62, 215, 205, 0.4); - box-shadow: 0px 0px 12px 0px rgba(62, 215, 205, 0.12); + border-color: ${(props) => props.theme.colors.mainMint}; path { stroke: ${(props) => props.theme.colors.grayScale04}; diff --git a/src/containers/LoginPage/BrandMotion.tsx b/src/containers/LoginPage/BrandMotion.tsx index 5562df9..fefe5c9 100644 --- a/src/containers/LoginPage/BrandMotion.tsx +++ b/src/containers/LoginPage/BrandMotion.tsx @@ -15,7 +15,7 @@ function Brand() {
AI가 생성한 퀴즈와 요약으로 효율적인 학습을 돕는
- + ); } @@ -41,6 +41,7 @@ const Wrapper = styled.div` ${({ theme }) => theme.typography.h4}; font-size: 18px; color: ${({ theme }) => theme.colors.mainMintDark}; + margin-bottom: 28px; } `; diff --git a/src/containers/QuizAIPage/ResultSection.tsx b/src/containers/QuizAIPage/ResultSection.tsx index 092b75f..3c52b2f 100644 --- a/src/containers/QuizAIPage/ResultSection.tsx +++ b/src/containers/QuizAIPage/ResultSection.tsx @@ -85,6 +85,7 @@ function ResultSection() { )} setShowModal(true)} /> diff --git a/src/containers/QuizUserPage/ResultSection.tsx b/src/containers/QuizUserPage/ResultSection.tsx index c806861..ad376d3 100644 --- a/src/containers/QuizUserPage/ResultSection.tsx +++ b/src/containers/QuizUserPage/ResultSection.tsx @@ -50,6 +50,7 @@ function ResultSection() {
setShowModal(true)} /> diff --git a/src/containers/SelectServicePage/Card.tsx b/src/containers/SelectServicePage/Card.tsx index ae26cc9..5877867 100644 --- a/src/containers/SelectServicePage/Card.tsx +++ b/src/containers/SelectServicePage/Card.tsx @@ -1,4 +1,3 @@ -import { ReactComponent as Arrow } from '@/assets/icons/long-arrow.svg'; import Typography from '@/components/Typography/Typography'; import { cloneElement } from 'react'; import { useNavigate } from 'react-router-dom'; @@ -57,11 +56,6 @@ function Card({ - {selected && ( - - - - )} ); } @@ -136,10 +130,4 @@ const TextContainer = styled.div` } `; -const ArrowWrapper = styled.div` - position: absolute; - bottom: 24px; - right: 24px; -`; - export default Card; diff --git a/src/containers/SummaryAIPage/ResultSection.tsx b/src/containers/SummaryAIPage/ResultSection.tsx index ef4f14a..5ed5f1e 100644 --- a/src/containers/SummaryAIPage/ResultSection.tsx +++ b/src/containers/SummaryAIPage/ResultSection.tsx @@ -64,6 +64,7 @@ function ResultSection() { )} setShowModal(true)} /> diff --git a/src/containers/SummaryUserPage/ResultSection.tsx b/src/containers/SummaryUserPage/ResultSection.tsx index 60dd0ae..bf7729d 100644 --- a/src/containers/SummaryUserPage/ResultSection.tsx +++ b/src/containers/SummaryUserPage/ResultSection.tsx @@ -65,6 +65,7 @@ function ResultSection() { )} setShowModal(true)} /> diff --git a/src/pages/CategoryPage.tsx b/src/pages/CategoryPage.tsx index ce252ce..44e577f 100644 --- a/src/pages/CategoryPage.tsx +++ b/src/pages/CategoryPage.tsx @@ -1,11 +1,18 @@ -import CategorySection from '@/containers/CategoryPage/CategorySection'; -import NoCategorySection from '@/containers/CategoryPage/NoCategorySection'; +import SuspenseLoading from '@/components/Loader/SuspenseLoading'; import { useGetCategoryList } from '@/hooks/useGetCategory'; import useRedirect from '@/hooks/useRedirect'; import { ServiceType } from '@/types/category.type'; -import { useEffect } from 'react'; +import { lazy, Suspense, useEffect } from 'react'; import { useSearchParams } from 'react-router-dom'; +// Lazy load the components +const CategorySection = lazy( + () => import('@/containers/CategoryPage/CategorySection') +); +const NoCategorySection = lazy( + () => import('@/containers/CategoryPage/NoCategorySection') +); + function CategoryPage() { const redirect = useRedirect(); const [params] = useSearchParams(); @@ -29,25 +36,32 @@ function CategoryPage() { redirect('/management/category'); }, [type, fetchedQuizCategoryList, fetchedSummaryCategoryList]); - if (!fetchedQuizCategoryList || !fetchedSummaryCategoryList) return null; - - if ( - !type && - (fetchedQuizCategoryList?.data?.length || 0) + - (fetchedSummaryCategoryList?.data?.length || 0) === - 0 - ) - return ; - - if (type === 'QUIZ' || type === 'SUMMARY') - return ( - - ); + const renderContent = () => { + if (!fetchedQuizCategoryList || !fetchedSummaryCategoryList) + return
Loading data...
; + + if ( + !type && + fetchedQuizCategoryList?.data?.length + + fetchedSummaryCategoryList?.data?.length === + 0 + ) + return ; + + if (type === 'QUIZ' || type === 'SUMMARY') + return ( + + ); + + return null; + }; + + return }>{renderContent()}; } export default CategoryPage; diff --git a/src/pages/QuizAIPage.tsx b/src/pages/QuizAIPage.tsx index e637b5d..2b3fe41 100644 --- a/src/pages/QuizAIPage.tsx +++ b/src/pages/QuizAIPage.tsx @@ -1,15 +1,25 @@ +import SuspenseLoading from '@/components/Loader/SuspenseLoading'; import ContentWrapper from '@/components/Wrapper/ContentWrapper'; -import GenerateSection from '@/containers/QuizAIPage/GenerateSection'; -import ResultSection from '@/containers/QuizAIPage/ResultSection'; +import { lazy, Suspense } from 'react'; import { useSearchParams } from 'react-router-dom'; +// 동적으로 로드할 컴포넌트 +const ResultSection = lazy( + () => import('@/containers/QuizAIPage/ResultSection') +); +const GenerateSection = lazy( + () => import('@/containers/QuizAIPage/GenerateSection') +); + function QuizAIPage() { const [searchParams] = useSearchParams(); const complete = searchParams.get('complete'); return ( - {complete === 'true' ? : } + }> + {complete === 'true' ? : } + ); } diff --git a/src/pages/QuizUserPage.tsx b/src/pages/QuizUserPage.tsx index 3235c68..51ec1e1 100644 --- a/src/pages/QuizUserPage.tsx +++ b/src/pages/QuizUserPage.tsx @@ -1,15 +1,24 @@ +import SuspenseLoading from '@/components/Loader/SuspenseLoading'; import ContentWrapper from '@/components/Wrapper/ContentWrapper'; -import GenerateSection from '@/containers/QuizUserPage/GenerateSection'; -import ResultSection from '@/containers/QuizUserPage/ResultSection'; +import { Suspense, lazy } from 'react'; import { useSearchParams } from 'react-router-dom'; +const ResultSection = lazy( + () => import('@/containers/QuizUserPage/ResultSection') +); +const GenerateSection = lazy( + () => import('@/containers/QuizUserPage/GenerateSection') +); + const QuizUserPage = () => { const [searchParams] = useSearchParams(); const complete = searchParams.get('complete'); return ( - {complete === 'true' ? : } + }> + {complete === 'true' ? : } + ); }; diff --git a/src/pages/SummaryAIPage.tsx b/src/pages/SummaryAIPage.tsx index 7c5489c..f3936c6 100644 --- a/src/pages/SummaryAIPage.tsx +++ b/src/pages/SummaryAIPage.tsx @@ -1,15 +1,24 @@ +import SuspenseLoading from '@/components/Loader/SuspenseLoading'; import ContentWrapper from '@/components/Wrapper/ContentWrapper'; -import GenerateSection from '@/containers/SummaryAIPage/GenerateSection'; -import ResultSection from '@/containers/SummaryAIPage/ResultSection'; +import { Suspense, lazy } from 'react'; import { useSearchParams } from 'react-router-dom'; +const ResultSection = lazy( + () => import('@/containers/SummaryAIPage/ResultSection') +); +const GenerateSection = lazy( + () => import('@/containers/SummaryAIPage/GenerateSection') +); + function SummaryAIPage() { const [searchParams] = useSearchParams(); const complete = searchParams.get('complete'); return ( - {complete === 'true' ? : } + }> + {complete === 'true' ? : } + ); } diff --git a/src/pages/SummaryUserPage.tsx b/src/pages/SummaryUserPage.tsx index 48b59b3..8bbce0f 100644 --- a/src/pages/SummaryUserPage.tsx +++ b/src/pages/SummaryUserPage.tsx @@ -1,15 +1,24 @@ +import SuspenseLoading from '@/components/Loader/SuspenseLoading'; import ContentWrapper from '@/components/Wrapper/ContentWrapper'; -import GenerateSection from '@/containers/SummaryUserPage/GenerateSection'; -import ResultSection from '@/containers/SummaryUserPage/ResultSection'; +import { lazy, Suspense } from 'react'; import { useSearchParams } from 'react-router-dom'; +const ResultSection = lazy( + () => import('@/containers/SummaryUserPage/ResultSection') +); +const GenerateSection = lazy( + () => import('@/containers/SummaryUserPage/GenerateSection') +); + function SummaryUserPage() { const [searchParams] = useSearchParams(); const complete = searchParams.get('complete'); return ( - {complete === 'true' ? : } + }> + {complete === 'true' ? : } + ); } diff --git a/src/routers/router.tsx b/src/routers/router.tsx index 2461f98..7f2b7d3 100644 --- a/src/routers/router.tsx +++ b/src/routers/router.tsx @@ -1,19 +1,32 @@ import MainLayout from '@/components/Layout/MainLayout'; -import CategoryPage from '@/pages/CategoryPage'; -import CategoryQuizDetailPage from '@/pages/CategoryQuizDetailPage'; -import CategoryQuizEditPage from '@/pages/CategoryQuizEditPage'; -import CategorySummaryDetailPage from '@/pages/CategorySummaryDetailPage'; -import CategorySummaryEditPage from '@/pages/CategorySummaryEditPage'; -import HistoryPage from '@/pages/HistoryPage'; -import LoginPage from '@/pages/LoginPage'; -import QuizAIPage from '@/pages/QuizAIPage'; -import QuizUserPage from '@/pages/QuizUserPage'; -import RedirectPage from '@/pages/RedirectPage'; -import SelectServicePage from '@/pages/SelectServicePage'; -import SummaryAIPage from '@/pages/SummaryAIPage'; -import SummaryUserPage from '@/pages/SummaryUserPage'; +import SuspenseLoading from '@/components/Loader/SuspenseLoading'; import ProtectedRoute from '@/routers/ProtectedRoute'; -import { Navigate, createBrowserRouter } from 'react-router-dom'; +import React, { lazy, Suspense } from 'react'; +import { createBrowserRouter, Navigate } from 'react-router-dom'; + +const LoginPage = lazy(() => import('@/pages/LoginPage')); +const RedirectPage = lazy(() => import('@/pages/RedirectPage')); +const SelectServicePage = lazy(() => import('@/pages/SelectServicePage')); +const QuizAIPage = lazy(() => import('@/pages/QuizAIPage')); +const QuizUserPage = lazy(() => import('@/pages/QuizUserPage')); +const SummaryAIPage = lazy(() => import('@/pages/SummaryAIPage')); +const SummaryUserPage = lazy(() => import('@/pages/SummaryUserPage')); +const HistoryPage = lazy(() => import('@/pages/HistoryPage')); +const CategoryPage = lazy(() => import('@/pages/CategoryPage')); +const CategoryQuizDetailPage = lazy( + () => import('@/pages/CategoryQuizDetailPage') +); +const CategoryQuizEditPage = lazy(() => import('@/pages/CategoryQuizEditPage')); +const CategorySummaryDetailPage = lazy( + () => import('@/pages/CategorySummaryDetailPage') +); +const CategorySummaryEditPage = lazy( + () => import('@/pages/CategorySummaryEditPage') +); + +const AppWrapper = ({ children }: { children: React.ReactNode }) => ( + }>{children} +); const routes = [ { @@ -75,6 +88,11 @@ const routes = [ { path: '*', element: }, ]; -const router = createBrowserRouter(routes); +const router = createBrowserRouter( + routes.map((route) => ({ + ...route, + element: {route.element}, + })) +); export default router; diff --git a/src/styles/color.ts b/src/styles/color.ts index 629a91f..d12cedc 100644 --- a/src/styles/color.ts +++ b/src/styles/color.ts @@ -6,7 +6,8 @@ const colors = { mainMintDarkGra: 'linear-gradient(0deg, #36BDB4 0%, rgba(54, 189, 180, 0.60) 100%)', mainMintLight: '#FBFFFF', - mainMintShadow: 'rgba(54, 189, 180, 0.28)', + mainMintShadow: 'rgba(54, 189, 180, 0.24)', + mainMintMedium: '#29CCC2', grayScale01: '#000000', grayScale02: '#424242',