From 8079f8bae4ad276bd368050f81c975e83b42aa9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=97=A4=EC=9D=B8?= <157036488+Hain-tain@users.noreply.github.com> Date: Sun, 13 Oct 2024 14:45:17 +0900 Subject: [PATCH 01/11] =?UTF-8?q?refactor(components):=20ScrollTopButton?= =?UTF-8?q?=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ScrollTopButton/ScrollTopButton.style.ts | 21 +++++++++++++++++++ .../ScrollTopButton/ScrollTopButton.tsx | 16 ++++++++++++++ frontend/src/components/index.ts | 1 + 3 files changed, 38 insertions(+) create mode 100644 frontend/src/components/ScrollTopButton/ScrollTopButton.style.ts create mode 100644 frontend/src/components/ScrollTopButton/ScrollTopButton.tsx diff --git a/frontend/src/components/ScrollTopButton/ScrollTopButton.style.ts b/frontend/src/components/ScrollTopButton/ScrollTopButton.style.ts new file mode 100644 index 000000000..7061e015d --- /dev/null +++ b/frontend/src/components/ScrollTopButton/ScrollTopButton.style.ts @@ -0,0 +1,21 @@ +import styled from '@emotion/styled'; + +import { theme } from '@/style/theme'; + +export const ScrollTopButton = styled.button` + cursor: pointer; + + position: fixed; + right: 2rem; + bottom: 2rem; + + display: flex; + align-items: center; + justify-content: center; + + padding: 0.75rem; + + background-color: ${theme.color.light.primary_500}; + border: none; + border-radius: 100%; +`; diff --git a/frontend/src/components/ScrollTopButton/ScrollTopButton.tsx b/frontend/src/components/ScrollTopButton/ScrollTopButton.tsx new file mode 100644 index 000000000..7f51523ef --- /dev/null +++ b/frontend/src/components/ScrollTopButton/ScrollTopButton.tsx @@ -0,0 +1,16 @@ +import { ArrowUpIcon } from '@/assets/images'; +import { scroll } from '@/utils'; + +import * as S from './ScrollTopButton.style'; + +const ScrollTopButton = () => ( + { + scroll.top('smooth'); + }} + > + + +); + +export default ScrollTopButton; diff --git a/frontend/src/components/index.ts b/frontend/src/components/index.ts index 7d3500417..27b9ab5dc 100644 --- a/frontend/src/components/index.ts +++ b/frontend/src/components/index.ts @@ -27,6 +27,7 @@ export { default as NewCategoryInput } from './NewCategoryInput/NewCategoryInput export { default as NoSearchResults } from './NoSearchResults/NoSearchResults'; export { default as Textarea } from './Textarea/Textarea'; export { default as ContactUs } from './ContactUs/ContactUs'; +export { default as ScrollTopButton } from './ScrollTopButton/ScrollTopButton'; // Skeleton UI export { default as LoadingBall } from './LoadingBall/LoadingBall'; From 6890fd602a7e23f93ae61619bc4d6506f0402185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=97=A4=EC=9D=B8?= <157036488+Hain-tain@users.noreply.github.com> Date: Sun, 13 Oct 2024 14:53:24 +0900 Subject: [PATCH 02/11] =?UTF-8?q?refactor(MyTemplatePage):=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20?= =?UTF-8?q?=ED=83=9C=EA=B7=B8=EB=AA=A9=EB=A1=9D,=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EB=AA=A9=EB=A1=9D=20=EC=84=9C=EC=8A=A4?= =?UTF-8?q?=ED=8E=9C=EC=8A=A4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyTemplatesPage/MyTemplatePage.style.ts | 35 ----- .../pages/MyTemplatesPage/MyTemplatePage.tsx | 137 +++++------------- .../CategoryListSection.tsx | 21 +++ .../CategoryListSectionSkeleton.tsx | 52 +++++++ .../ConfirmDeleteModal/ConfirmDeleteModal.tsx | 28 ++++ .../NewTemplateButton.style.ts | 23 +++ .../NewTemplateButton/NewTemplateButton.tsx | 22 +++ .../TagListSection/TagListSection.tsx | 23 +++ .../TagListSection/TagListSectionSkeleton.tsx | 28 ++++ .../TemplateListSection.tsx | 35 +++++ .../components/TopBanner/TopBanner.style.ts | 18 +++ .../components/TopBanner/TopBanner.tsx | 21 +++ .../pages/MyTemplatesPage/components/index.ts | 8 + .../categories/useCategoryListQuery.ts | 4 +- frontend/src/queries/tags/useTagListQuery.ts | 5 +- frontend/src/routes/router.tsx | 6 +- 16 files changed, 318 insertions(+), 148 deletions(-) create mode 100644 frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSection.tsx create mode 100644 frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSectionSkeleton.tsx create mode 100644 frontend/src/pages/MyTemplatesPage/components/ConfirmDeleteModal/ConfirmDeleteModal.tsx create mode 100644 frontend/src/pages/MyTemplatesPage/components/NewTemplateButton/NewTemplateButton.style.ts create mode 100644 frontend/src/pages/MyTemplatesPage/components/NewTemplateButton/NewTemplateButton.tsx create mode 100644 frontend/src/pages/MyTemplatesPage/components/TagListSection/TagListSection.tsx create mode 100644 frontend/src/pages/MyTemplatesPage/components/TagListSection/TagListSectionSkeleton.tsx create mode 100644 frontend/src/pages/MyTemplatesPage/components/TemplateListSection/TemplateListSection.tsx create mode 100644 frontend/src/pages/MyTemplatesPage/components/TopBanner/TopBanner.style.ts create mode 100644 frontend/src/pages/MyTemplatesPage/components/TopBanner/TopBanner.tsx diff --git a/frontend/src/pages/MyTemplatesPage/MyTemplatePage.style.ts b/frontend/src/pages/MyTemplatesPage/MyTemplatePage.style.ts index 7c5f3fec8..321f2d9cb 100644 --- a/frontend/src/pages/MyTemplatesPage/MyTemplatePage.style.ts +++ b/frontend/src/pages/MyTemplatesPage/MyTemplatePage.style.ts @@ -21,23 +21,6 @@ export const MainContainer = styled.main` } `; -export const TopBannerContainer = styled.div` - display: flex; - align-items: center; - - width: 100%; - height: 10.25rem; - - white-space: nowrap; -`; - -export const TopBannerTextWrapper = styled.div` - display: flex; - gap: 0.5rem; - align-items: center; - margin-left: calc(12.5rem + clamp(1rem, calc(0.0888 * 100vw - 3.2618rem), 4.375rem)); -`; - export const SearchInput = styled(Input)` box-shadow: inset 1px 2px 8px #00000030; `; @@ -61,21 +44,3 @@ export const NewTemplateButton = styled.button` background-color: ${theme.color.light.primary_50}; } `; - -export const ScrollTopButton = styled.button` - cursor: pointer; - - position: fixed; - right: 2rem; - bottom: 2rem; - - display: flex; - align-items: center; - justify-content: center; - - padding: 0.75rem; - - background-color: ${theme.color.light.primary_500}; - border: none; - border-radius: 100%; -`; diff --git a/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx b/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx index f067383fb..09aaca629 100644 --- a/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx +++ b/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx @@ -1,22 +1,25 @@ -import { useState, useCallback } from 'react'; +import { useState, useCallback, Suspense } from 'react'; import { DEFAULT_SORTING_OPTION, SORTING_OPTIONS } from '@/api'; -import { ArrowUpIcon, PlusIcon, SearchIcon } from '@/assets/images'; -import { Flex, Heading, Input, PagingButtons, Dropdown, Button, Modal, Text, NoSearchResults } from '@/components'; -import { useWindowWidth, useDebounce, useToggle, useDropdown, useInput, useCustomNavigate } from '@/hooks'; +import { SearchIcon } from '@/assets/images'; +import { Flex, Input, PagingButtons, Dropdown, Button, ScrollTopButton, LoadingBall } from '@/components'; +import { useDebounce, useToggle, useDropdown, useInput } from '@/hooks'; import { useAuth } from '@/hooks/authentication'; -import { useTemplateDeleteMutation, useTemplateCategoryTagQueries } from '@/queries/templates'; -import { END_POINTS } from '@/routes'; -import { theme } from '@/style/theme'; +import { useTemplateDeleteMutation, useTemplateListQuery } from '@/queries/templates'; import { scroll } from '@/utils'; -import { CategoryFilterMenu, TemplateGrid, TagFilterMenu } from './components'; +import { + TopBanner, + ConfirmDeleteModal, + CategoryListSection, + CategoryListSectionSkeleton, + TagListSection, + TagListSectionSkeleton, + TemplateListSection, +} from './components'; import * as S from './MyTemplatePage.style'; -const getGridCols = (windowWidth: number) => (windowWidth <= 1024 ? 1 : 2); - const MyTemplatePage = () => { - const windowWidth = useWindowWidth(); const { memberInfo: { name }, } = useAuth(); @@ -33,7 +36,7 @@ const MyTemplatePage = () => { const [page, setPage] = useState(1); - const [{ data: templateData }, { data: categoryData }, { data: tagData }] = useTemplateCategoryTagQueries({ + const { data: templateData, isFetching: isTemplateListLoading } = useTemplateListQuery({ keyword: debouncedKeyword, categoryId: selectedCategoryId, tagIds: selectedTagIds, @@ -42,8 +45,6 @@ const MyTemplatePage = () => { }); const templateList = templateData?.templates || []; - const categoryList = categoryData?.categories || []; - const tagList = tagData?.tags || []; const totalPages = templateData?.totalPages || 0; const { mutateAsync: deleteTemplates } = useTemplateDeleteMutation(selectedList); @@ -84,33 +85,13 @@ const MyTemplatePage = () => { toggleDeleteModal(); }; - const renderTemplateContent = () => { - if (templateList.length === 0) { - if (debouncedKeyword !== '') { - return ; - } else { - return ; - } - } - - return ( - - ); - }; - return ( - + - - - + }> + + @@ -155,10 +136,21 @@ const MyTemplatePage = () => { getOptionLabel={(option) => option.value} /> - {tagList.length !== 0 && ( - + + }> + + + {isTemplateListLoading ? ( + + ) : ( + )} - {renderTemplateContent()} {templateList.length !== 0 && ( @@ -181,67 +173,4 @@ const MyTemplatePage = () => { ); }; -interface TopBannerProps { - name: string; -} - -const TopBanner = ({ name }: TopBannerProps) => ( - - - {name} - - {`${name ? '님' : ''}의 템플릿 입니다 :)`} - - - -); - -const NewTemplateButton = () => { - const navigate = useCustomNavigate(); - - return ( - navigate(END_POINTS.TEMPLATES_UPLOAD)}> - - - 이곳을 눌러 새 템플릿을 추가해보세요 :) - - - ); -}; - export default MyTemplatePage; - -interface ConfirmDeleteModalProps { - isDeleteModalOpen: boolean; - toggleDeleteModal: () => void; - handleDelete: () => void; -} - -const ConfirmDeleteModal = ({ isDeleteModalOpen, toggleDeleteModal, handleDelete }: ConfirmDeleteModalProps) => ( - - - - - 정말 삭제하시겠습니까? - - 삭제된 템플릿은 복구할 수 없습니다. - - - - - - - -); - -const ScrollTopButton = () => ( - { - scroll.top('smooth'); - }} - > - - -); diff --git a/frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSection.tsx b/frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSection.tsx new file mode 100644 index 000000000..9bd8a6844 --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSection.tsx @@ -0,0 +1,21 @@ +import { Flex } from '@/components'; +import { useCategoryListQuery } from '@/queries/categories'; + +import { CategoryFilterMenu } from '..'; + +interface Props { + onSelectCategory: (selectedCategoryId: number) => void; +} + +const CategoryListSection = ({ onSelectCategory }: Props) => { + const { data: categoryData } = useCategoryListQuery(); + const categoryList = categoryData?.categories || []; + + return ( + + + + ); +}; + +export default CategoryListSection; diff --git a/frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSectionSkeleton.tsx b/frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSectionSkeleton.tsx new file mode 100644 index 000000000..3b54d0595 --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSectionSkeleton.tsx @@ -0,0 +1,52 @@ +import styled from '@emotion/styled'; + +import { SettingIcon } from '@/assets/images'; +import { Flex } from '@/components'; +import { useWindowWidth } from '@/hooks'; + +const SkeletonButtonWrapper = styled.div` + display: flex; + align-items: center; + justify-content: center; + + width: 12.5rem; + height: 3rem; /* CategoryButton과 동일한 높이 */ + padding: 0.5rem; + + background: linear-gradient(90deg, #e0e0e0 25%, #c0c0c0 50%, #e0e0e0 75%); + background-size: 200% 100%; + border-radius: 8px; + box-shadow: 1px 2px 8px #00000020; /* CategoryButton과 동일한 그림자 */ + + animation: skeleton-loading 1.5s infinite ease-in-out; + + @keyframes skeleton-loading { + 0% { + background-position: -200% 0; + } + 100% { + background-position: 200% 0; + } + } +`; + +const CategoryListSectionSkeleton = () => { + const windowWidth = useWindowWidth(); + + if (windowWidth <= 768) { + return null; + } + + return ( + + + + + + + + + ); +}; + +export default CategoryListSectionSkeleton; diff --git a/frontend/src/pages/MyTemplatesPage/components/ConfirmDeleteModal/ConfirmDeleteModal.tsx b/frontend/src/pages/MyTemplatesPage/components/ConfirmDeleteModal/ConfirmDeleteModal.tsx new file mode 100644 index 000000000..7080fba72 --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/components/ConfirmDeleteModal/ConfirmDeleteModal.tsx @@ -0,0 +1,28 @@ +import { Button, Flex, Modal, Text } from '@/components'; + +interface ConfirmDeleteModalProps { + isDeleteModalOpen: boolean; + toggleDeleteModal: () => void; + handleDelete: () => void; +} + +const ConfirmDeleteModal = ({ isDeleteModalOpen, toggleDeleteModal, handleDelete }: ConfirmDeleteModalProps) => ( + + + + + 정말 삭제하시겠습니까? + + 삭제된 템플릿은 복구할 수 없습니다. + + + + + + + +); + +export default ConfirmDeleteModal; diff --git a/frontend/src/pages/MyTemplatesPage/components/NewTemplateButton/NewTemplateButton.style.ts b/frontend/src/pages/MyTemplatesPage/components/NewTemplateButton/NewTemplateButton.style.ts new file mode 100644 index 000000000..b0e6911b3 --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/components/NewTemplateButton/NewTemplateButton.style.ts @@ -0,0 +1,23 @@ +import styled from '@emotion/styled'; + +import { theme } from '@/style/theme'; + +export const NewTemplateButtonWrapper = styled.button` + cursor: pointer; + + display: flex; + flex-direction: column; + gap: 1.5rem; + align-items: center; + justify-content: center; + + width: 100%; + height: 19.75rem; + padding: 1.5rem; + + border: 3px dashed ${theme.color.light.primary_500}; + border-radius: 8px; + &:hover { + background-color: ${theme.color.light.primary_50}; + } +`; diff --git a/frontend/src/pages/MyTemplatesPage/components/NewTemplateButton/NewTemplateButton.tsx b/frontend/src/pages/MyTemplatesPage/components/NewTemplateButton/NewTemplateButton.tsx new file mode 100644 index 000000000..c0c9c197c --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/components/NewTemplateButton/NewTemplateButton.tsx @@ -0,0 +1,22 @@ +import { PlusIcon } from '@/assets/images'; +import { Text } from '@/components'; +import { useCustomNavigate } from '@/hooks'; +import { END_POINTS } from '@/routes'; +import { theme } from '@/style/theme'; + +import * as S from './NewTemplateButton.style'; + +const NewTemplateButton = () => { + const navigate = useCustomNavigate(); + + return ( + navigate(END_POINTS.TEMPLATES_UPLOAD)}> + + + 이곳을 눌러 새 템플릿을 추가해보세요 :) + + + ); +}; + +export default NewTemplateButton; diff --git a/frontend/src/pages/MyTemplatesPage/components/TagListSection/TagListSection.tsx b/frontend/src/pages/MyTemplatesPage/components/TagListSection/TagListSection.tsx new file mode 100644 index 000000000..f90f5f50a --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/components/TagListSection/TagListSection.tsx @@ -0,0 +1,23 @@ +import { useTagListQuery } from '@/queries/tags'; + +import { TagFilterMenu } from '../'; + +interface Props { + selectedTagIds: number[]; + handleTagMenuClick: (selectedTagIds: number[]) => void; +} + +const TagListSection = ({ selectedTagIds, handleTagMenuClick }: Props) => { + const { data: tagData } = useTagListQuery(); + const tagList = tagData?.tags || []; + + return ( +
+ {tagList.length !== 0 && ( + + )} +
+ ); +}; + +export default TagListSection; diff --git a/frontend/src/pages/MyTemplatesPage/components/TagListSection/TagListSectionSkeleton.tsx b/frontend/src/pages/MyTemplatesPage/components/TagListSection/TagListSectionSkeleton.tsx new file mode 100644 index 000000000..cd6c36a31 --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/components/TagListSection/TagListSectionSkeleton.tsx @@ -0,0 +1,28 @@ +import styled from '@emotion/styled'; + +const skeletonAnimation = ` + @keyframes skeleton-loading { + 0% { + background-position: -200% 0; + } + 100% { + background-position: 200% 0; + } + } +`; + +const TagListSectionSkeleton = styled.div` + ${skeletonAnimation} + box-sizing: content-box; + width: 100%; + height: 3.875rem; + + background: linear-gradient(90deg, #e0e0e0 25%, #c0c0c0 50%, #e0e0e0 75%); + background-size: 200% 100%; + border: 1px solid #c0c0c0; + border-radius: 8px; + + animation: skeleton-loading 2.5s infinite ease-in-out; +`; + +export default TagListSectionSkeleton; diff --git a/frontend/src/pages/MyTemplatesPage/components/TemplateListSection/TemplateListSection.tsx b/frontend/src/pages/MyTemplatesPage/components/TemplateListSection/TemplateListSection.tsx new file mode 100644 index 000000000..0764865a9 --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/components/TemplateListSection/TemplateListSection.tsx @@ -0,0 +1,35 @@ +import { NoSearchResults } from '@/components'; +import { useWindowWidth } from '@/hooks'; +import { TemplateListItem } from '@/types'; + +import { NewTemplateButton, TemplateGrid } from '../'; + +interface Props { + templateList: TemplateListItem[]; + isEditMode: boolean; + isSearching: boolean; + selectedList: number[]; + setSelectedList: React.Dispatch>; +} + +const getGridCols = (windowWidth: number) => (windowWidth <= 1024 ? 1 : 2); + +const TemplateListSection = ({ templateList, isSearching, isEditMode, selectedList, setSelectedList }: Props) => { + const windowWidth = useWindowWidth(); + + if (templateList.length === 0) { + return isSearching ? : ; + } + + return ( + + ); +}; + +export default TemplateListSection; diff --git a/frontend/src/pages/MyTemplatesPage/components/TopBanner/TopBanner.style.ts b/frontend/src/pages/MyTemplatesPage/components/TopBanner/TopBanner.style.ts new file mode 100644 index 000000000..1b0dc70bb --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/components/TopBanner/TopBanner.style.ts @@ -0,0 +1,18 @@ +import styled from '@emotion/styled'; + +export const TopBannerContainer = styled.div` + display: flex; + align-items: center; + + width: 100%; + height: 10.25rem; + + white-space: nowrap; +`; + +export const TopBannerTextWrapper = styled.div` + display: flex; + gap: 0.5rem; + align-items: center; + margin-left: calc(12.5rem + clamp(1rem, calc(0.0888 * 100vw - 3.2618rem), 4.375rem)); +`; diff --git a/frontend/src/pages/MyTemplatesPage/components/TopBanner/TopBanner.tsx b/frontend/src/pages/MyTemplatesPage/components/TopBanner/TopBanner.tsx new file mode 100644 index 000000000..4c2cf5b25 --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/components/TopBanner/TopBanner.tsx @@ -0,0 +1,21 @@ +import { Heading } from '@/components'; +import { theme } from '@/style/theme'; + +import * as S from './TopBanner.style'; + +interface Props { + name: string; +} + +const TopBanner = ({ name }: Props) => ( + + + {name} + + {`${name ? '님' : ''}의 템플릿 입니다 :)`} + + + +); + +export default TopBanner; diff --git a/frontend/src/pages/MyTemplatesPage/components/index.ts b/frontend/src/pages/MyTemplatesPage/components/index.ts index e5621c200..c9726763b 100644 --- a/frontend/src/pages/MyTemplatesPage/components/index.ts +++ b/frontend/src/pages/MyTemplatesPage/components/index.ts @@ -2,3 +2,11 @@ export { default as CategoryEditModal } from './CategoryEditModal/CategoryEditMo export { default as CategoryFilterMenu } from './CategoryFilterMenu/CategoryFilterMenu'; export { default as TagFilterMenu } from './TagFilterMenu/TagFilterMenu'; export { default as TemplateGrid } from './TemplateGrid/TemplateGrid'; +export { default as TopBanner } from './TopBanner/TopBanner'; +export { default as ConfirmDeleteModal } from './ConfirmDeleteModal/ConfirmDeleteModal'; +export { default as NewTemplateButton } from './NewTemplateButton/NewTemplateButton'; +export { default as CategoryListSection } from './CategoryListSection/CategoryListSection'; +export { default as CategoryListSectionSkeleton } from './CategoryListSection/CategoryListSectionSkeleton'; +export { default as TagListSection } from './TagListSection/TagListSection'; +export { default as TagListSectionSkeleton } from './TagListSection/TagListSectionSkeleton'; +export { default as TemplateListSection } from './TemplateListSection/TemplateListSection'; diff --git a/frontend/src/queries/categories/useCategoryListQuery.ts b/frontend/src/queries/categories/useCategoryListQuery.ts index 56da9ec05..57b122914 100644 --- a/frontend/src/queries/categories/useCategoryListQuery.ts +++ b/frontend/src/queries/categories/useCategoryListQuery.ts @@ -1,4 +1,4 @@ -import { useQuery } from '@tanstack/react-query'; +import { useSuspenseQuery } from '@tanstack/react-query'; import { QUERY_KEY, getCategoryList } from '@/api'; import { useAuth } from '@/hooks/authentication/useAuth'; @@ -9,7 +9,7 @@ export const useCategoryListQuery = () => { memberInfo: { memberId }, } = useAuth(); - return useQuery({ + return useSuspenseQuery({ queryKey: [QUERY_KEY.CATEGORY_LIST], queryFn: () => getCategoryList({ memberId }), }); diff --git a/frontend/src/queries/tags/useTagListQuery.ts b/frontend/src/queries/tags/useTagListQuery.ts index 62e9d3cce..0237fbe5c 100644 --- a/frontend/src/queries/tags/useTagListQuery.ts +++ b/frontend/src/queries/tags/useTagListQuery.ts @@ -1,4 +1,4 @@ -import { useQuery } from '@tanstack/react-query'; +import { useSuspenseQuery } from '@tanstack/react-query'; import { QUERY_KEY, getTagList } from '@/api'; import { useAuth } from '@/hooks/authentication/useAuth'; @@ -9,9 +9,8 @@ export const useTagListQuery = () => { memberInfo: { memberId }, } = useAuth(); - return useQuery({ + return useSuspenseQuery({ queryKey: [QUERY_KEY.TAG_LIST], queryFn: () => getTagList({ memberId }), - throwOnError: true, }); }; diff --git a/frontend/src/routes/router.tsx b/frontend/src/routes/router.tsx index ca7de82dc..3f3073a2c 100644 --- a/frontend/src/routes/router.tsx +++ b/frontend/src/routes/router.tsx @@ -2,7 +2,7 @@ import { ErrorBoundary } from '@sentry/react'; import { lazy, Suspense } from 'react'; import { createBrowserRouter } from 'react-router-dom'; -import { Layout, LoadingFallback } from '@/components'; +import { Layout } from '@/components'; import RouteGuard from './RouteGuard'; import { END_POINTS } from './endPoints'; @@ -46,9 +46,7 @@ const router = createBrowserRouter([ element: ( }> - }> - - + ), From 07f77e3e300f25bd52213558875a2a7eccd3fc74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=97=A4=EC=9D=B8?= <157036488+Hain-tain@users.noreply.github.com> Date: Mon, 14 Oct 2024 20:20:37 +0900 Subject: [PATCH 03/11] =?UTF-8?q?refactor(components):=20TemplateDeleteSel?= =?UTF-8?q?ection,=20TemplateListSectionLoading=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TemplateDeleteSelection.tsx | 60 +++++++++++++++++++ .../TemplateListSectionLoading.style.ts | 23 +++++++ .../TemplateListSectionLoading.tsx | 13 ++++ .../pages/MyTemplatesPage/components/index.ts | 2 + 4 files changed, 98 insertions(+) create mode 100644 frontend/src/pages/MyTemplatesPage/components/TemplateDeleteSelection/TemplateDeleteSelection.tsx create mode 100644 frontend/src/pages/MyTemplatesPage/components/TemplateListSection/TemplateListSectionLoading.style.ts create mode 100644 frontend/src/pages/MyTemplatesPage/components/TemplateListSection/TemplateListSectionLoading.tsx diff --git a/frontend/src/pages/MyTemplatesPage/components/TemplateDeleteSelection/TemplateDeleteSelection.tsx b/frontend/src/pages/MyTemplatesPage/components/TemplateDeleteSelection/TemplateDeleteSelection.tsx new file mode 100644 index 000000000..3c27729b2 --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/components/TemplateDeleteSelection/TemplateDeleteSelection.tsx @@ -0,0 +1,60 @@ +import { Button, Flex } from '@/components'; + +import { ConfirmDeleteModal } from '../'; + +interface Props { + isEditMode: boolean; + toggleIsEditMode: () => void; + isDeleteModalOpen: boolean; + toggleDeleteModal: () => void; + handleAllSelected: () => void; + selectedListLength: number; + templateListLength: number; + handleDelete: () => void; +} + +const TemplateDeleteSelection = ({ + isEditMode, + toggleIsEditMode, + isDeleteModalOpen, + toggleDeleteModal, + handleAllSelected, + selectedListLength, + templateListLength, + handleDelete, +}: Props) => ( + <> + + {isEditMode ? ( + + + + + + ) : ( + + )} + + {isDeleteModalOpen && ( + + )} + +); + +export default TemplateDeleteSelection; diff --git a/frontend/src/pages/MyTemplatesPage/components/TemplateListSection/TemplateListSectionLoading.style.ts b/frontend/src/pages/MyTemplatesPage/components/TemplateListSection/TemplateListSectionLoading.style.ts new file mode 100644 index 000000000..3de81a5b4 --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/components/TemplateListSection/TemplateListSectionLoading.style.ts @@ -0,0 +1,23 @@ +import styled from '@emotion/styled'; + +export const LoadingOverlay = styled.div` + position: absolute; + z-index: 1000; + top: 0; + right: 0; + bottom: 0; + left: 0; + + display: flex; + align-items: flex-start; + justify-content: center; + + padding-top: 1rem; + + background-color: rgba(255, 255, 255, 0.7); +`; + +export const LoadingBallWrapper = styled.div` + width: 100%; + margin-top: 10rem; +`; diff --git a/frontend/src/pages/MyTemplatesPage/components/TemplateListSection/TemplateListSectionLoading.tsx b/frontend/src/pages/MyTemplatesPage/components/TemplateListSection/TemplateListSectionLoading.tsx new file mode 100644 index 000000000..383eed2b9 --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/components/TemplateListSection/TemplateListSectionLoading.tsx @@ -0,0 +1,13 @@ +import { LoadingBall } from '@/components'; + +import * as S from './TemplateListSectionLoading.style'; + +const TemplateListSectionLoading = () => ( + + + + + +); + +export default TemplateListSectionLoading; diff --git a/frontend/src/pages/MyTemplatesPage/components/index.ts b/frontend/src/pages/MyTemplatesPage/components/index.ts index c9726763b..f93c5f729 100644 --- a/frontend/src/pages/MyTemplatesPage/components/index.ts +++ b/frontend/src/pages/MyTemplatesPage/components/index.ts @@ -10,3 +10,5 @@ export { default as CategoryListSectionSkeleton } from './CategoryListSection/Ca export { default as TagListSection } from './TagListSection/TagListSection'; export { default as TagListSectionSkeleton } from './TagListSection/TagListSectionSkeleton'; export { default as TemplateListSection } from './TemplateListSection/TemplateListSection'; +export { default as TemplateListSectionLoading } from './TemplateListSection/TemplateListSectionLoading'; +export { default as TemplateDeleteSelection } from './TemplateDeleteSelection/TemplateDeleteSelection'; From 7c02ab2ce3bba32fe7f18bf3d7863f474b93a7f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=97=A4=EC=9D=B8?= <157036488+Hain-tain@users.noreply.github.com> Date: Mon, 14 Oct 2024 20:22:00 +0900 Subject: [PATCH 04/11] =?UTF-8?q?refactor(src):=20useKeyword,=20useShowTem?= =?UTF-8?q?plateList,=20useSelectAndDeleteTemplateList=20=ED=9B=85=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20MyTemplatePage=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/hooks/template/index.ts | 1 + frontend/src/hooks/template/useKeyword.ts | 8 + .../MyTemplatesPage/MyTemplatePage.style.ts | 5 + .../pages/MyTemplatesPage/MyTemplatePage.tsx | 161 ++++++------------ .../src/pages/MyTemplatesPage/hooks/.gitkeep | 0 .../src/pages/MyTemplatesPage/hooks/index.ts | 2 + .../hooks/useSelectAndDeleteTemplateList.ts | 44 +++++ .../hooks/useShowTemplateList.ts | 69 ++++++++ 8 files changed, 183 insertions(+), 107 deletions(-) create mode 100644 frontend/src/hooks/template/useKeyword.ts delete mode 100644 frontend/src/pages/MyTemplatesPage/hooks/.gitkeep create mode 100644 frontend/src/pages/MyTemplatesPage/hooks/index.ts create mode 100644 frontend/src/pages/MyTemplatesPage/hooks/useSelectAndDeleteTemplateList.ts create mode 100644 frontend/src/pages/MyTemplatesPage/hooks/useShowTemplateList.ts diff --git a/frontend/src/hooks/template/index.ts b/frontend/src/hooks/template/index.ts index d071d71ba..5e3764e86 100644 --- a/frontend/src/hooks/template/index.ts +++ b/frontend/src/hooks/template/index.ts @@ -1,2 +1,3 @@ export { useSourceCode } from './useSourceCode'; export { useTag } from './useTag'; +export { useKeyword } from './useKeyword'; diff --git a/frontend/src/hooks/template/useKeyword.ts b/frontend/src/hooks/template/useKeyword.ts new file mode 100644 index 000000000..2bc3fc9e4 --- /dev/null +++ b/frontend/src/hooks/template/useKeyword.ts @@ -0,0 +1,8 @@ +import { useInput, useDebounce } from '../'; + +export const useKeyword = () => { + const [keyword, handleKeywordChange] = useInput(''); + const debouncedKeyword = useDebounce(keyword, 300); + + return { keyword, debouncedKeyword, handleKeywordChange }; +}; diff --git a/frontend/src/pages/MyTemplatesPage/MyTemplatePage.style.ts b/frontend/src/pages/MyTemplatesPage/MyTemplatePage.style.ts index 321f2d9cb..f55c62df4 100644 --- a/frontend/src/pages/MyTemplatesPage/MyTemplatePage.style.ts +++ b/frontend/src/pages/MyTemplatesPage/MyTemplatePage.style.ts @@ -44,3 +44,8 @@ export const NewTemplateButton = styled.button` background-color: ${theme.color.light.primary_50}; } `; + +export const TemplateListSectionWrapper = styled.div` + position: relative; + width: 100%; +`; diff --git a/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx b/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx index 09aaca629..b29cd85f5 100644 --- a/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx +++ b/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx @@ -1,22 +1,21 @@ -import { useState, useCallback, Suspense } from 'react'; +import { Suspense } from 'react'; -import { DEFAULT_SORTING_OPTION, SORTING_OPTIONS } from '@/api'; +import { SORTING_OPTIONS } from '@/api'; import { SearchIcon } from '@/assets/images'; -import { Flex, Input, PagingButtons, Dropdown, Button, ScrollTopButton, LoadingBall } from '@/components'; -import { useDebounce, useToggle, useDropdown, useInput } from '@/hooks'; +import { Flex, Input, PagingButtons, Dropdown, ScrollTopButton } from '@/components'; import { useAuth } from '@/hooks/authentication'; -import { useTemplateDeleteMutation, useTemplateListQuery } from '@/queries/templates'; -import { scroll } from '@/utils'; import { TopBanner, - ConfirmDeleteModal, CategoryListSection, CategoryListSectionSkeleton, TagListSection, TagListSectionSkeleton, TemplateListSection, + TemplateDeleteSelection, + TemplateListSectionLoading, } from './components'; +import { useSelectAndDeleteTemplateList, useShowTemplateList } from './hooks'; import * as S from './MyTemplatePage.style'; const MyTemplatePage = () => { @@ -24,66 +23,33 @@ const MyTemplatePage = () => { memberInfo: { name }, } = useAuth(); - const [isEditMode, toggleIsEditMode] = useToggle(); - const [selectedList, setSelectedList] = useState([]); - const [isDeleteModalOpen, toggleDeleteModal] = useToggle(); - - const [keyword, handleKeywordChange] = useInput(''); - const debouncedKeyword = useDebounce(keyword, 300); - const [selectedCategoryId, setSelectedCategoryId] = useState(undefined); - const [selectedTagIds, setSelectedTagIds] = useState([]); - const { currentValue: sortingOption, ...dropdownProps } = useDropdown(DEFAULT_SORTING_OPTION); - - const [page, setPage] = useState(1); - - const { data: templateData, isFetching: isTemplateListLoading } = useTemplateListQuery({ - keyword: debouncedKeyword, - categoryId: selectedCategoryId, - tagIds: selectedTagIds, - sort: sortingOption.key, + const { + templateList, + isTemplateListFetching, + isTemplateListLoading, + totalPages, + dropdownProps, + keyword, page, - }); - - const templateList = templateData?.templates || []; - const totalPages = templateData?.totalPages || 0; - - const { mutateAsync: deleteTemplates } = useTemplateDeleteMutation(selectedList); - - const handleCategoryMenuClick = useCallback((selectedCategoryId: number) => { - setSelectedCategoryId(selectedCategoryId); - setPage(1); - }, []); - - const handleTagMenuClick = useCallback((selectedTagIds: number[]) => { - setSelectedTagIds(selectedTagIds); - }, []); + sortingOption, + selectedTagIds, + handleKeywordChange, + handleCategoryMenuClick, + handleTagMenuClick, + handleSearchSubmit, + handlePageChange, + } = useShowTemplateList(); - const handleSearchSubmit = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { - setPage(1); - } - }; - - const handlePageChange = (page: number) => { - scroll.top(); - setPage(page); - }; - - const handleAllSelected = () => { - if (selectedList.length === templateList.length) { - setSelectedList([]); - - return; - } - - setSelectedList(templateList.map((template) => template.id)); - }; - - const handleDelete = () => { - deleteTemplates(); - toggleIsEditMode(); - toggleDeleteModal(); - }; + const { + isEditMode, + toggleIsEditMode, + isDeleteModalOpen, + toggleDeleteModal, + selectedList, + setSelectedList, + handleAllSelected, + handleDelete, + } = useSelectAndDeleteTemplateList({ templateList }); return ( @@ -94,29 +60,16 @@ const MyTemplatePage = () => { - - {isEditMode ? ( - - - - - - ) : ( - - )} - + @@ -140,17 +93,19 @@ const MyTemplatePage = () => { }> - {isTemplateListLoading ? ( - - ) : ( - - )} + + + {isTemplateListFetching && } + {!isTemplateListLoading && ( + + )} + {templateList.length !== 0 && ( @@ -158,14 +113,6 @@ const MyTemplatePage = () => { )} - - {isDeleteModalOpen && ( - - )}
diff --git a/frontend/src/pages/MyTemplatesPage/hooks/.gitkeep b/frontend/src/pages/MyTemplatesPage/hooks/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/src/pages/MyTemplatesPage/hooks/index.ts b/frontend/src/pages/MyTemplatesPage/hooks/index.ts new file mode 100644 index 000000000..c67cea69c --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/hooks/index.ts @@ -0,0 +1,2 @@ +export { useShowTemplateList } from './useShowTemplateList'; +export { useSelectAndDeleteTemplateList } from './useSelectAndDeleteTemplateList'; diff --git a/frontend/src/pages/MyTemplatesPage/hooks/useSelectAndDeleteTemplateList.ts b/frontend/src/pages/MyTemplatesPage/hooks/useSelectAndDeleteTemplateList.ts new file mode 100644 index 000000000..59751cd13 --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/hooks/useSelectAndDeleteTemplateList.ts @@ -0,0 +1,44 @@ +import { useState } from 'react'; + +import { useToggle } from '@/hooks'; +import { useTemplateDeleteMutation } from '@/queries/templates'; +import { TemplateListItem } from '@/types'; + +interface Props { + templateList: TemplateListItem[]; +} + +export const useSelectAndDeleteTemplateList = ({ templateList }: Props) => { + const [isEditMode, toggleIsEditMode] = useToggle(); + const [selectedList, setSelectedList] = useState([]); + const [isDeleteModalOpen, toggleDeleteModal] = useToggle(); + + const { mutateAsync: deleteTemplates } = useTemplateDeleteMutation(selectedList); + + const handleAllSelected = () => { + if (selectedList.length === templateList.length) { + setSelectedList([]); + + return; + } + + setSelectedList(templateList.map((template) => template.id)); + }; + + const handleDelete = () => { + deleteTemplates(); + toggleIsEditMode(); + toggleDeleteModal(); + }; + + return { + isEditMode, + toggleIsEditMode, + isDeleteModalOpen, + toggleDeleteModal, + selectedList, + setSelectedList, + handleAllSelected, + handleDelete, + }; +}; diff --git a/frontend/src/pages/MyTemplatesPage/hooks/useShowTemplateList.ts b/frontend/src/pages/MyTemplatesPage/hooks/useShowTemplateList.ts new file mode 100644 index 000000000..5a2781e95 --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/hooks/useShowTemplateList.ts @@ -0,0 +1,69 @@ +import { useCallback, useState } from 'react'; + +import { DEFAULT_SORTING_OPTION } from '@/api'; +import { useDropdown } from '@/hooks'; +import { useKeyword } from '@/hooks/template'; +import { useTemplateListQuery } from '@/queries/templates'; +import { scroll } from '@/utils'; + +const FIRST_PAGE = 1; + +export const useShowTemplateList = () => { + const [selectedCategoryId, setSelectedCategoryId] = useState(undefined); + const [selectedTagIds, setSelectedTagIds] = useState([]); + const { keyword, debouncedKeyword, handleKeywordChange } = useKeyword(); + const { currentValue: sortingOption, ...dropdownProps } = useDropdown(DEFAULT_SORTING_OPTION); + const [page, setPage] = useState(FIRST_PAGE); + + const { + data: templateData, + isFetching: isTemplateListFetching, + isLoading: isTemplateListLoading, + } = useTemplateListQuery({ + categoryId: selectedCategoryId, + tagIds: selectedTagIds, + keyword: debouncedKeyword, + sort: sortingOption.key, + page, + }); + + const templateList = templateData?.templates || []; + const totalPages = templateData?.totalPages || 0; + + const handleCategoryMenuClick = useCallback((selectedCategoryId: number) => { + setSelectedCategoryId(selectedCategoryId); + handlePageChange(FIRST_PAGE); + }, []); + + const handleTagMenuClick = useCallback((selectedTagIds: number[]) => { + setSelectedTagIds(selectedTagIds); + }, []); + + const handleSearchSubmit = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + handlePageChange(FIRST_PAGE); + } + }; + + const handlePageChange = (page: number) => { + scroll.top('smooth'); + setPage(page); + }; + + return { + templateList, + isTemplateListFetching, + isTemplateListLoading, + totalPages, + dropdownProps, + keyword, + page, + sortingOption, + selectedTagIds, + handleKeywordChange, + handleCategoryMenuClick, + handleTagMenuClick, + handleSearchSubmit, + handlePageChange, + }; +}; From 1f7e8a255406f63cdb97da09a73f37c64d79e304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=97=A4=EC=9D=B8?= <157036488+Hain-tain@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:36:57 +0900 Subject: [PATCH 05/11] =?UTF-8?q?refactor(templates):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20useTemplateCategoryTa?= =?UTF-8?q?gQueries=20=ED=9B=85=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/queries/templates/index.ts | 1 - .../useTemplateCategoryTagQueries.ts | 44 ------------------- 2 files changed, 45 deletions(-) delete mode 100644 frontend/src/queries/templates/useTemplateCategoryTagQueries.ts diff --git a/frontend/src/queries/templates/index.ts b/frontend/src/queries/templates/index.ts index 9a21c0c1a..f3e5707d6 100644 --- a/frontend/src/queries/templates/index.ts +++ b/frontend/src/queries/templates/index.ts @@ -4,4 +4,3 @@ export { useTemplateQuery } from './useTemplateQuery'; export { useTemplateUploadMutation } from './useTemplateUploadMutation'; export { useTemplateEditMutation } from './useTemplateEditMutation'; export { useTemplateDeleteMutation } from './useTemplateDeleteMutation'; -export { useTemplateCategoryTagQueries } from './useTemplateCategoryTagQueries'; diff --git a/frontend/src/queries/templates/useTemplateCategoryTagQueries.ts b/frontend/src/queries/templates/useTemplateCategoryTagQueries.ts deleted file mode 100644 index 4b047567d..000000000 --- a/frontend/src/queries/templates/useTemplateCategoryTagQueries.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { useSuspenseQueries } from '@tanstack/react-query'; - -import { QUERY_KEY, getTemplateList, getTagList, getCategoryList, DEFAULT_SORTING_OPTION, PAGE_SIZE } from '@/api'; -import { useAuth } from '@/hooks/authentication/useAuth'; -import type { SortingKey } from '@/types'; - -interface Props { - keyword?: string; - categoryId?: number; - tagIds?: number[]; - sort?: SortingKey; - page?: number; - size?: number; -} - -export const useTemplateCategoryTagQueries = ({ - keyword, - categoryId, - tagIds, - sort = DEFAULT_SORTING_OPTION.key, - page = 1, - size = PAGE_SIZE, -}: Props) => { - const { - memberInfo: { memberId }, - } = useAuth(); - - return useSuspenseQueries({ - queries: [ - { - queryKey: [QUERY_KEY.TEMPLATE_LIST, keyword, categoryId, tagIds, sort, page, size, memberId], - queryFn: () => getTemplateList({ keyword, categoryId, tagIds, sort, page, size, memberId }), - }, - { - queryKey: [QUERY_KEY.CATEGORY_LIST], - queryFn: () => getCategoryList({ memberId }), - }, - { - queryKey: [QUERY_KEY.TAG_LIST], - queryFn: () => getTagList({ memberId }), - }, - ], - }); -}; From 00c231be5a028cf71b41fb2ba119153a5ed85cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=97=A4=EC=9D=B8?= <157036488+Hain-tain@users.noreply.github.com> Date: Thu, 17 Oct 2024 19:07:32 +0900 Subject: [PATCH 06/11] =?UTF-8?q?refactor(pages):=20useShowTemplateList=20?= =?UTF-8?q?=3D>=20useFilteredTemplateList=EB=A1=9C=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx | 4 ++-- frontend/src/pages/MyTemplatesPage/hooks/index.ts | 2 +- .../{useShowTemplateList.ts => useFilteredTemplateList.ts} | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename frontend/src/pages/MyTemplatesPage/hooks/{useShowTemplateList.ts => useFilteredTemplateList.ts} (97%) diff --git a/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx b/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx index b29cd85f5..a74fd014b 100644 --- a/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx +++ b/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx @@ -15,7 +15,7 @@ import { TemplateDeleteSelection, TemplateListSectionLoading, } from './components'; -import { useSelectAndDeleteTemplateList, useShowTemplateList } from './hooks'; +import { useSelectAndDeleteTemplateList, useFilteredTemplateList } from './hooks'; import * as S from './MyTemplatePage.style'; const MyTemplatePage = () => { @@ -38,7 +38,7 @@ const MyTemplatePage = () => { handleTagMenuClick, handleSearchSubmit, handlePageChange, - } = useShowTemplateList(); + } = useFilteredTemplateList(); const { isEditMode, diff --git a/frontend/src/pages/MyTemplatesPage/hooks/index.ts b/frontend/src/pages/MyTemplatesPage/hooks/index.ts index c67cea69c..4825988ac 100644 --- a/frontend/src/pages/MyTemplatesPage/hooks/index.ts +++ b/frontend/src/pages/MyTemplatesPage/hooks/index.ts @@ -1,2 +1,2 @@ -export { useShowTemplateList } from './useShowTemplateList'; +export { useFilteredTemplateList } from './useFilteredTemplateList'; export { useSelectAndDeleteTemplateList } from './useSelectAndDeleteTemplateList'; diff --git a/frontend/src/pages/MyTemplatesPage/hooks/useShowTemplateList.ts b/frontend/src/pages/MyTemplatesPage/hooks/useFilteredTemplateList.ts similarity index 97% rename from frontend/src/pages/MyTemplatesPage/hooks/useShowTemplateList.ts rename to frontend/src/pages/MyTemplatesPage/hooks/useFilteredTemplateList.ts index 5a2781e95..405d9acf5 100644 --- a/frontend/src/pages/MyTemplatesPage/hooks/useShowTemplateList.ts +++ b/frontend/src/pages/MyTemplatesPage/hooks/useFilteredTemplateList.ts @@ -8,7 +8,7 @@ import { scroll } from '@/utils'; const FIRST_PAGE = 1; -export const useShowTemplateList = () => { +export const useFilteredTemplateList = () => { const [selectedCategoryId, setSelectedCategoryId] = useState(undefined); const [selectedTagIds, setSelectedTagIds] = useState([]); const { keyword, debouncedKeyword, handleKeywordChange } = useKeyword(); From 5cd916002394d36a04e5d1717aaaf8816a017bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=97=A4=EC=9D=B8?= <157036488+Hain-tain@users.noreply.github.com> Date: Thu, 17 Oct 2024 19:12:42 +0900 Subject: [PATCH 07/11] =?UTF-8?q?refactor(src):=20useKeyword=20=3D>=20useS?= =?UTF-8?q?earchKeyword=EB=A1=9C=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/hooks/template/index.ts | 2 +- .../src/hooks/template/{useKeyword.ts => useSearchKeyword.ts} | 4 ++-- .../pages/MyTemplatesPage/hooks/useFilteredTemplateList.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename frontend/src/hooks/template/{useKeyword.ts => useSearchKeyword.ts} (67%) diff --git a/frontend/src/hooks/template/index.ts b/frontend/src/hooks/template/index.ts index 5e3764e86..56cdbcf6e 100644 --- a/frontend/src/hooks/template/index.ts +++ b/frontend/src/hooks/template/index.ts @@ -1,3 +1,3 @@ export { useSourceCode } from './useSourceCode'; export { useTag } from './useTag'; -export { useKeyword } from './useKeyword'; +export { useSearchKeyword } from './useSearchKeyword'; diff --git a/frontend/src/hooks/template/useKeyword.ts b/frontend/src/hooks/template/useSearchKeyword.ts similarity index 67% rename from frontend/src/hooks/template/useKeyword.ts rename to frontend/src/hooks/template/useSearchKeyword.ts index 2bc3fc9e4..63c2c0394 100644 --- a/frontend/src/hooks/template/useKeyword.ts +++ b/frontend/src/hooks/template/useSearchKeyword.ts @@ -1,6 +1,6 @@ -import { useInput, useDebounce } from '../'; +import { useInput, useDebounce } from '..'; -export const useKeyword = () => { +export const useSearchKeyword = () => { const [keyword, handleKeywordChange] = useInput(''); const debouncedKeyword = useDebounce(keyword, 300); diff --git a/frontend/src/pages/MyTemplatesPage/hooks/useFilteredTemplateList.ts b/frontend/src/pages/MyTemplatesPage/hooks/useFilteredTemplateList.ts index 405d9acf5..e7082fdf7 100644 --- a/frontend/src/pages/MyTemplatesPage/hooks/useFilteredTemplateList.ts +++ b/frontend/src/pages/MyTemplatesPage/hooks/useFilteredTemplateList.ts @@ -2,7 +2,7 @@ import { useCallback, useState } from 'react'; import { DEFAULT_SORTING_OPTION } from '@/api'; import { useDropdown } from '@/hooks'; -import { useKeyword } from '@/hooks/template'; +import { useSearchKeyword } from '@/hooks/template'; import { useTemplateListQuery } from '@/queries/templates'; import { scroll } from '@/utils'; @@ -11,7 +11,7 @@ const FIRST_PAGE = 1; export const useFilteredTemplateList = () => { const [selectedCategoryId, setSelectedCategoryId] = useState(undefined); const [selectedTagIds, setSelectedTagIds] = useState([]); - const { keyword, debouncedKeyword, handleKeywordChange } = useKeyword(); + const { keyword, debouncedKeyword, handleKeywordChange } = useSearchKeyword(); const { currentValue: sortingOption, ...dropdownProps } = useDropdown(DEFAULT_SORTING_OPTION); const [page, setPage] = useState(FIRST_PAGE); From 8e8086da48350e48ac6cd1df433a8c9575d9988b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=97=A4=EC=9D=B8?= <157036488+Hain-tain@users.noreply.github.com> Date: Thu, 17 Oct 2024 21:07:23 +0900 Subject: [PATCH 08/11] =?UTF-8?q?refactor(ConfirmDeleteModal):=20ConfirmDe?= =?UTF-8?q?leteModalProps=20=3D>Props=20=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ConfirmDeleteModal/ConfirmDeleteModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/MyTemplatesPage/components/ConfirmDeleteModal/ConfirmDeleteModal.tsx b/frontend/src/pages/MyTemplatesPage/components/ConfirmDeleteModal/ConfirmDeleteModal.tsx index 7080fba72..e9e3a3206 100644 --- a/frontend/src/pages/MyTemplatesPage/components/ConfirmDeleteModal/ConfirmDeleteModal.tsx +++ b/frontend/src/pages/MyTemplatesPage/components/ConfirmDeleteModal/ConfirmDeleteModal.tsx @@ -1,12 +1,12 @@ import { Button, Flex, Modal, Text } from '@/components'; -interface ConfirmDeleteModalProps { +interface Props { isDeleteModalOpen: boolean; toggleDeleteModal: () => void; handleDelete: () => void; } -const ConfirmDeleteModal = ({ isDeleteModalOpen, toggleDeleteModal, handleDelete }: ConfirmDeleteModalProps) => ( +const ConfirmDeleteModal = ({ isDeleteModalOpen, toggleDeleteModal, handleDelete }: Props) => ( From ce63a7abb793c0f572c1ddcd8bd77537ef5ae12c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=97=A4=EC=9D=B8?= <157036488+Hain-tain@users.noreply.github.com> Date: Thu, 17 Oct 2024 21:48:49 +0900 Subject: [PATCH 09/11] =?UTF-8?q?refactor(CategoryListSection):=20Flex=20?= =?UTF-8?q?=EB=8C=80=EC=8B=A0=20=EC=8A=A4=ED=83=80=EC=9D=BC=EB=93=9C=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CategoryListSection/CategoryListSection.style.ts | 8 ++++++++ .../CategoryListSection/CategoryListSection.tsx | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSection.style.ts diff --git a/frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSection.style.ts b/frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSection.style.ts new file mode 100644 index 000000000..0bd0d77ad --- /dev/null +++ b/frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSection.style.ts @@ -0,0 +1,8 @@ +import styled from '@emotion/styled'; + +export const CategoryListSectionContainer = styled.div` + display: flex; + flex-direction: column; + gap: 2.5rem; + margin-top: 4.5rem; +`; diff --git a/frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSection.tsx b/frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSection.tsx index 9bd8a6844..153b7a22b 100644 --- a/frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSection.tsx +++ b/frontend/src/pages/MyTemplatesPage/components/CategoryListSection/CategoryListSection.tsx @@ -1,7 +1,7 @@ -import { Flex } from '@/components'; import { useCategoryListQuery } from '@/queries/categories'; import { CategoryFilterMenu } from '..'; +import * as S from './CategoryListSection.style'; interface Props { onSelectCategory: (selectedCategoryId: number) => void; @@ -12,9 +12,9 @@ const CategoryListSection = ({ onSelectCategory }: Props) => { const categoryList = categoryData?.categories || []; return ( - + - + ); }; From 21ca869e1a41c86c4feb51df4f3c15e7e07fbd53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=97=A4=EC=9D=B8?= <157036488+Hain-tain@users.noreply.github.com> Date: Thu, 17 Oct 2024 21:50:47 +0900 Subject: [PATCH 10/11] =?UTF-8?q?refactor(ConfirmDeleteModal):=20Modal=20?= =?UTF-8?q?=ED=95=A9=EC=84=B1=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8?= =?UTF-8?q?=EC=9D=98=20=ED=95=98=EC=9C=84=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ConfirmDeleteModal/ConfirmDeleteModal.tsx | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/frontend/src/pages/MyTemplatesPage/components/ConfirmDeleteModal/ConfirmDeleteModal.tsx b/frontend/src/pages/MyTemplatesPage/components/ConfirmDeleteModal/ConfirmDeleteModal.tsx index e9e3a3206..ca721c98b 100644 --- a/frontend/src/pages/MyTemplatesPage/components/ConfirmDeleteModal/ConfirmDeleteModal.tsx +++ b/frontend/src/pages/MyTemplatesPage/components/ConfirmDeleteModal/ConfirmDeleteModal.tsx @@ -1,4 +1,4 @@ -import { Button, Flex, Modal, Text } from '@/components'; +import { Button, Modal, Text } from '@/components'; interface Props { isDeleteModalOpen: boolean; @@ -8,20 +8,18 @@ interface Props { const ConfirmDeleteModal = ({ isDeleteModalOpen, toggleDeleteModal, handleDelete }: Props) => ( - - - - 정말 삭제하시겠습니까? - - 삭제된 템플릿은 복구할 수 없습니다. - - - - - - + + + 정말 삭제하시겠습니까? + + 삭제된 템플릿은 복구할 수 없습니다. + + + + + ); From e0adebf47ecfb2a26b48904f53d7f942df205818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=97=A4=EC=9D=B8?= <157036488+Hain-tain@users.noreply.github.com> Date: Thu, 17 Oct 2024 21:51:58 +0900 Subject: [PATCH 11/11] =?UTF-8?q?refactor(queries):=20useSuspenseQuery?= =?UTF-8?q?=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=20=ED=9B=85?= =?UTF-8?q?=EB=93=A4=20=EC=88=98=EB=8F=99=EC=9C=BC=EB=A1=9C=20error=20?= =?UTF-8?q?=EC=A0=84=ED=8C=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/queries/categories/useCategoryListQuery.ts | 8 +++++++- frontend/src/queries/tags/useTagListQuery.ts | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/frontend/src/queries/categories/useCategoryListQuery.ts b/frontend/src/queries/categories/useCategoryListQuery.ts index 57b122914..2acc44b92 100644 --- a/frontend/src/queries/categories/useCategoryListQuery.ts +++ b/frontend/src/queries/categories/useCategoryListQuery.ts @@ -9,8 +9,14 @@ export const useCategoryListQuery = () => { memberInfo: { memberId }, } = useAuth(); - return useSuspenseQuery({ + const result = useSuspenseQuery({ queryKey: [QUERY_KEY.CATEGORY_LIST], queryFn: () => getCategoryList({ memberId }), }); + + if (result.error && !result.isFetching) { + throw result.error; + } + + return result; }; diff --git a/frontend/src/queries/tags/useTagListQuery.ts b/frontend/src/queries/tags/useTagListQuery.ts index 0237fbe5c..2163bdade 100644 --- a/frontend/src/queries/tags/useTagListQuery.ts +++ b/frontend/src/queries/tags/useTagListQuery.ts @@ -9,8 +9,14 @@ export const useTagListQuery = () => { memberInfo: { memberId }, } = useAuth(); - return useSuspenseQuery({ + const result = useSuspenseQuery({ queryKey: [QUERY_KEY.TAG_LIST], queryFn: () => getTagList({ memberId }), }); + + if (result.error && !result.isFetching) { + throw result.error; + } + + return result; };