From f91e7b8173a4beeddd49cad5e728b8035811b4c1 Mon Sep 17 00:00:00 2001 From: ChanLee_KR Date: Sat, 20 Jul 2024 22:56:06 +0900 Subject: [PATCH 1/3] =?UTF-8?q?refactor:=20=ED=95=84=ED=84=B0=20UI=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=A0=81=EC=9A=A9=20(#ATR-571)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../filter-article/ui/FilterButton.tsx | 30 +++++++++++++++++++ .../filter-article/ui/FilterDropdownBtn.tsx | 14 +++------ .../filter-article/ui/HideReadToggleBtn.tsx | 14 ++++----- .../features/filter-article/ui/SearchBar.tsx | 10 ++----- 4 files changed, 42 insertions(+), 26 deletions(-) create mode 100644 apps/service/src/features/filter-article/ui/FilterButton.tsx diff --git a/apps/service/src/features/filter-article/ui/FilterButton.tsx b/apps/service/src/features/filter-article/ui/FilterButton.tsx new file mode 100644 index 00000000..b43ddd75 --- /dev/null +++ b/apps/service/src/features/filter-article/ui/FilterButton.tsx @@ -0,0 +1,30 @@ +'use client' + +import React, { + DetailedHTMLProps, + InputHTMLAttributes, + LegacyRef, + forwardRef, +} from 'react' + +const FilterButton = forwardRef< + HTMLInputElement, + DetailedHTMLProps< + InputHTMLAttributes, + HTMLButtonElement + > & { active?: boolean } +>(({ type, className, active: isActive = false, ...props }, ref) => ( + + 카테고리 diff --git a/apps/service/src/features/filter-article/ui/HideReadToggleBtn.tsx b/apps/service/src/features/filter-article/ui/HideReadToggleBtn.tsx index 4362c2c6..44bacd4e 100644 --- a/apps/service/src/features/filter-article/ui/HideReadToggleBtn.tsx +++ b/apps/service/src/features/filter-article/ui/HideReadToggleBtn.tsx @@ -1,7 +1,7 @@ 'use client' -import { Button } from '@attraction/design-system/dist' import { CheckOutline } from '@attraction/icons' +import FilterButton from './FilterButton' interface HideReadToggleBtnProps { isRead: boolean @@ -13,18 +13,14 @@ export default function HideReadToggleBtn({ toggleHideFn, }: HideReadToggleBtnProps) { return ( - + ) } diff --git a/apps/service/src/features/filter-article/ui/SearchBar.tsx b/apps/service/src/features/filter-article/ui/SearchBar.tsx index 647d052d..6e23a6de 100644 --- a/apps/service/src/features/filter-article/ui/SearchBar.tsx +++ b/apps/service/src/features/filter-article/ui/SearchBar.tsx @@ -1,9 +1,7 @@ 'use client' import { useEffect, useState } from 'react' -import { Input } from '@attraction/design-system/dist' import { BackspaceOutline, MagnifyingGlassOutline } from '@attraction/icons' - import { useDebounce } from '@/shared/lib' interface SearchBarProps { @@ -17,11 +15,9 @@ export default function SearchBar({ setValue }: SearchBarProps) { useEffect(() => setValue(debouncedValue), [debouncedValue, setValue]) return ( -
- - - - + + Date: Sun, 21 Jul 2024 21:53:21 +0900 Subject: [PATCH 2/3] =?UTF-8?q?refactor:=20=ED=95=84=ED=84=B0=20UI=20?= =?UTF-8?q?=EB=94=94=EC=9E=90=EC=9D=B8=20=EA=B0=9C=ED=8E=B8=20(#ATR-571)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entities/user-article/constant/index.ts | 1 + .../user-article/constant/sortButton.ts | 11 + .../src/entities/user-article/model/index.ts | 2 +- .../model/useUserCategoriesQuery.ts | 14 +- .../filter-article/ui/CategoryButton.tsx | 80 +++++++ .../filter-article/ui/CategoryDropdown.tsx | 72 +++--- .../ui/CategorySelectForDrawer.tsx | 72 ++++++ .../filter-article/ui/FilterButton.tsx | 10 +- .../filter-article/ui/FilterDropdownBtn.tsx | 74 ------ .../filter-article/ui/HideReadToggleBtn.tsx | 2 +- .../filter-article/ui/ResetButton.tsx | 19 ++ .../features/filter-article/ui/SearchBar.tsx | 2 +- .../features/filter-article/ui/SortButton.tsx | 108 +++++++++ .../filter-article/ui/ViewTypeButton.tsx | 51 ++++ .../filter-article/ui/ViewTypeTabMenu.tsx | 53 ----- .../src/features/filter-article/ui/index.ts | 6 +- .../src/widgets/inbox/ui/UserInbox.tsx | 220 ++++++++++-------- 17 files changed, 527 insertions(+), 270 deletions(-) create mode 100644 apps/service/src/entities/user-article/constant/sortButton.ts create mode 100644 apps/service/src/features/filter-article/ui/CategoryButton.tsx create mode 100644 apps/service/src/features/filter-article/ui/CategorySelectForDrawer.tsx delete mode 100644 apps/service/src/features/filter-article/ui/FilterDropdownBtn.tsx create mode 100644 apps/service/src/features/filter-article/ui/ResetButton.tsx create mode 100644 apps/service/src/features/filter-article/ui/SortButton.tsx create mode 100644 apps/service/src/features/filter-article/ui/ViewTypeButton.tsx delete mode 100644 apps/service/src/features/filter-article/ui/ViewTypeTabMenu.tsx diff --git a/apps/service/src/entities/user-article/constant/index.ts b/apps/service/src/entities/user-article/constant/index.ts index 2ac71b8d..0a638efb 100644 --- a/apps/service/src/entities/user-article/constant/index.ts +++ b/apps/service/src/entities/user-article/constant/index.ts @@ -1 +1,2 @@ export { default as DEFAULT_LIST_SIZE } from './defaultListSize' +export * from './sortButton' diff --git a/apps/service/src/entities/user-article/constant/sortButton.ts b/apps/service/src/entities/user-article/constant/sortButton.ts new file mode 100644 index 00000000..fa5a5631 --- /dev/null +++ b/apps/service/src/entities/user-article/constant/sortButton.ts @@ -0,0 +1,11 @@ +import type { SortType } from '../model' + +export const SORT_LABEL: Record = { + 'receivedAt,desc': '최신 순', + 'receivedAt,asc': '오래된 순', +} + +export const SORT_MENU: Array<[SortType, string]> = [ + ['receivedAt,desc', SORT_LABEL['receivedAt,desc']], + ['receivedAt,asc', SORT_LABEL['receivedAt,asc']], +] diff --git a/apps/service/src/entities/user-article/model/index.ts b/apps/service/src/entities/user-article/model/index.ts index c2ec010c..cc9119a3 100644 --- a/apps/service/src/entities/user-article/model/index.ts +++ b/apps/service/src/entities/user-article/model/index.ts @@ -4,4 +4,4 @@ export { default as useCheckUserArticleBookmarkQuery } from './useCheckUserArtic export { default as useInfiniteUserArticlesQuery } from './useInfiniteUserArticlesQuery' export { default as useTrackingUserArticleScroll } from './useTrackingUserArticleScroll' export { default as useUserArticleQuery } from './useUserArticleQuery' -export { default as useUserCategoriesQuery } from './useUserCategoriesQuery' +export * from './useUserCategoriesQuery' diff --git a/apps/service/src/entities/user-article/model/useUserCategoriesQuery.ts b/apps/service/src/entities/user-article/model/useUserCategoriesQuery.ts index 8cc33cdf..a42e97e6 100644 --- a/apps/service/src/entities/user-article/model/useUserCategoriesQuery.ts +++ b/apps/service/src/entities/user-article/model/useUserCategoriesQuery.ts @@ -1,15 +1,25 @@ 'use client' -import { useQuery } from '@tanstack/react-query' +import { useQuery, useSuspenseQuery } from '@tanstack/react-query' import { useAuth } from '@/entities/auth' import { getUserCategories } from '../api' import userArticleQueryKeys from './userArticleQueryKeys' -export default function useUserCategoriesQuery() { +export function useUserCategoriesQuery({ enabled }: { enabled?: boolean }) { const { userEmail } = useAuth() return useQuery({ queryKey: userArticleQueryKeys.userCategories({ userEmail }), queryFn: () => getUserCategories({ userEmail }), + enabled, + }) +} + +export function useUserCategoriesSuspenseQuery() { + const { userEmail } = useAuth() + + return useSuspenseQuery({ + queryKey: userArticleQueryKeys.userCategories({ userEmail }), + queryFn: () => getUserCategories({ userEmail }), }) } diff --git a/apps/service/src/features/filter-article/ui/CategoryButton.tsx b/apps/service/src/features/filter-article/ui/CategoryButton.tsx new file mode 100644 index 00000000..a7b5a929 --- /dev/null +++ b/apps/service/src/features/filter-article/ui/CategoryButton.tsx @@ -0,0 +1,80 @@ +'use client' + +import { PropsWithChildren, useState } from 'react' +import { ChevronDownOutline, TagOutline } from '@attraction/icons' +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from '@attraction/design-system' +import { useCheckDevice } from '@/shared/lib' +import type { NewsletterCategory } from '@/shared/type' +import { NEWSLETTER_CATEGORY } from '@/shared/constant' +import FilterButton from './FilterButton' +import CategoryDropdown from './CategoryDropdown' +import CategorySelectForDrawer from './CategorySelectForDrawer' + +export interface CategorySelectProps { + selectedCategory?: NewsletterCategory + setCategory: (category?: NewsletterCategory) => void +} + +function DropdownToDrawer({ + children, + ...props +}: PropsWithChildren) { + const { isMobileView } = useCheckDevice() + const [isOpen, setOpen] = useState(false) + const close = () => setOpen(false) + + if (isMobileView) { + return ( + + {children} + + + 카테고리 선택 + + + + + + + + + + ) + } + + return ( + + {children} + + ) +} + +export default function CategoryButton({ ...props }: CategorySelectProps) { + return ( + + + + + {props.selectedCategory + ? NEWSLETTER_CATEGORY[props.selectedCategory] + : '카테고리'} + + + + + ) +} diff --git a/apps/service/src/features/filter-article/ui/CategoryDropdown.tsx b/apps/service/src/features/filter-article/ui/CategoryDropdown.tsx index a04f6843..6d67202a 100644 --- a/apps/service/src/features/filter-article/ui/CategoryDropdown.tsx +++ b/apps/service/src/features/filter-article/ui/CategoryDropdown.tsx @@ -1,57 +1,67 @@ 'use client' +import type { PropsWithChildren } from 'react' import { DropdownMenuRadioItem, DropdownMenuRadioGroup, + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, } from '@attraction/design-system/dist' import { useUserCategoriesQuery } from '@/entities/user-article' -import { NewsletterCategory } from '@/shared/type' import { NEWSLETTER_CATEGORY } from '@/shared/constant' import { LoadingSpinner, WarnTxt } from '@/shared/ui' +import type { CategorySelectProps } from './CategoryButton' -export interface CategoryDropdownProps { - selectedCategory?: NewsletterCategory - setCategory: (category?: NewsletterCategory) => void +interface CategoryDropdownProps extends PropsWithChildren { + open?: boolean + onOpenChange?: (status: boolean) => void } export default function CategoryDropdown({ selectedCategory, setCategory, + open, + onOpenChange, + children, }: CategoryDropdownProps) { - const { data, isLoading, isError } = useUserCategoriesQuery() + const { data, isLoading, isError } = useUserCategoriesQuery({ enabled: open }) return ( - <> - {isLoading && } - {isError && ( -
- -
- )} - {data && ( - <> - {data.length === 0 && } - - {data.length > 0 && ( + + {children} + + {isLoading && } + {isError && ( +
+ +
+ )} + {data && ( + <> + {data.length === 0 && ( + + )} + setCategory(undefined)}> 전체 - )} - {data.map((category) => ( - setCategory(category)}> - {NEWSLETTER_CATEGORY[category]} - - ))} - - - )} - + {data.map((category) => ( + setCategory(category)}> + {NEWSLETTER_CATEGORY[category]} + + ))} +
+ + )} + + ) } diff --git a/apps/service/src/features/filter-article/ui/CategorySelectForDrawer.tsx b/apps/service/src/features/filter-article/ui/CategorySelectForDrawer.tsx new file mode 100644 index 00000000..9a09698a --- /dev/null +++ b/apps/service/src/features/filter-article/ui/CategorySelectForDrawer.tsx @@ -0,0 +1,72 @@ +'use client' + +import { Suspense } from 'react' +import { ErrorBoundary } from 'react-error-boundary' +import { Checkbox } from '@attraction/design-system' +import { useUserCategoriesSuspenseQuery } from '@/entities/user-article' +import { NEWSLETTER_CATEGORY } from '@/shared/constant' +import { GuideTxt, LoadingSpinner } from '@/shared/ui' +import type { CategorySelectProps } from './CategoryButton' + +interface CategorySelectForDrawerProps extends CategorySelectProps { + close: () => void +} + +function UserCategorySelect({ + selectedCategory, + setCategory, + close, +}: CategorySelectForDrawerProps) { + const { data } = useUserCategoriesSuspenseQuery() + + return ( + <> +
  • + { + setCategory(undefined) + close() + }} + label="전체" + /> +
  • + {data.map((category) => ( +
  • + { + setCategory(category) + close() + }} + label={NEWSLETTER_CATEGORY[category]} + /> +
  • + ))} + + ) +} + +export default function CategorySelectForDrawer( + props: CategorySelectForDrawerProps, +) { + return ( + + +
    + }> +
      + }> + + +
    + + ) +} diff --git a/apps/service/src/features/filter-article/ui/FilterButton.tsx b/apps/service/src/features/filter-article/ui/FilterButton.tsx index b43ddd75..a75744bd 100644 --- a/apps/service/src/features/filter-article/ui/FilterButton.tsx +++ b/apps/service/src/features/filter-article/ui/FilterButton.tsx @@ -13,15 +13,19 @@ const FilterButton = forwardRef< InputHTMLAttributes, HTMLButtonElement > & { active?: boolean } ->(({ type, className, active: isActive = false, ...props }, ref) => ( +>(({ type, className, active: isActive = false, onClick, ...props }, ref) => ( + + + + + ) + } + + return ( + + {children} + + + {SORT_MENU.map(([type, label]) => ( + setSortType(type)}> + {label} + + ))} + + + + ) +} + +export default function SortButton({ ...props }: SortButtonProps) { + return ( + + + + {SORT_LABEL[props.sortType]} + + + + + ) +} diff --git a/apps/service/src/features/filter-article/ui/ViewTypeButton.tsx b/apps/service/src/features/filter-article/ui/ViewTypeButton.tsx new file mode 100644 index 00000000..e9c144e1 --- /dev/null +++ b/apps/service/src/features/filter-article/ui/ViewTypeButton.tsx @@ -0,0 +1,51 @@ +'use client' + +import { BarsLeftOutline, GridOutline } from '@attraction/icons' +import type { ViewType } from '@/entities/user-article' + +interface ViewTypeButtonProps { + viewType: ViewType + setViewType: (type: ViewType) => void +} + +const viewTypeBtns: Array<[ViewType, string, typeof GridOutline]> = [ + ['gallery', '갤러리형 보기', GridOutline], + ['list', '리스트형 보기', BarsLeftOutline], +] + +export default function ViewTypeButton({ + viewType, + setViewType, +}: ViewTypeButtonProps) { + return ( +

    + {viewTypeBtns.map(([type, label, Icon]) => ( + + ))} + +

    + ) +} diff --git a/apps/service/src/features/filter-article/ui/ViewTypeTabMenu.tsx b/apps/service/src/features/filter-article/ui/ViewTypeTabMenu.tsx deleted file mode 100644 index 2466a9eb..00000000 --- a/apps/service/src/features/filter-article/ui/ViewTypeTabMenu.tsx +++ /dev/null @@ -1,53 +0,0 @@ -'use client' - -import React from 'react' -import { Button } from '@attraction/design-system/dist' -import { ViewType } from '@/entities/user-article' -import { BarsLeftOutline, GridOutline } from '@attraction/icons' - -interface ViewTypeTabMenuProps { - type: ViewType - setType: (type: ViewType) => void - isArticleView?: boolean -} - -const btns: Array<[ViewType, string, React.FC]> = [ - ['gallery', '갤러리형', GridOutline], - ['list', '리스트형', BarsLeftOutline], -] - -export default function ViewTypeTabMenu({ - type, - setType, - isArticleView, -}: ViewTypeTabMenuProps) { - return ( -
    - {btns.map(([viewType, label, Icon]) => ( - - ))} -
    - ) -} diff --git a/apps/service/src/features/filter-article/ui/index.ts b/apps/service/src/features/filter-article/ui/index.ts index b99f194b..6144c1f5 100644 --- a/apps/service/src/features/filter-article/ui/index.ts +++ b/apps/service/src/features/filter-article/ui/index.ts @@ -1,4 +1,6 @@ export { default as HideReadToggleBtn } from './HideReadToggleBtn' -export { default as ViewTypeTabMenu } from './ViewTypeTabMenu' export { default as SearchBar } from './SearchBar' -export { default as FilterDropdownBtn } from './FilterDropdownBtn' +export { default as SortButton } from './SortButton' +export { default as ResetButton } from './ResetButton' +export { default as CategoryButton } from './CategoryButton' +export { default as ViewTypeButton } from './ViewTypeButton' diff --git a/apps/service/src/widgets/inbox/ui/UserInbox.tsx b/apps/service/src/widgets/inbox/ui/UserInbox.tsx index 1f8b2ef2..3858a43e 100644 --- a/apps/service/src/widgets/inbox/ui/UserInbox.tsx +++ b/apps/service/src/widgets/inbox/ui/UserInbox.tsx @@ -1,12 +1,14 @@ 'use client' -import { useEffect } from 'react' +import React, { useCallback, useEffect } from 'react' import { useArticleFilter, HideReadToggleBtn, SearchBar, - ViewTypeTabMenu, - FilterDropdownBtn, + CategoryButton, + SortButton, + ViewTypeButton, + ResetButton, } from '@/features/filter-article' import { useInfiniteUserArticlesQuery, @@ -60,6 +62,18 @@ export default function UserInbox({ isArticleView, pageType }: InboxProps) { if (hasNextPage) fetchNextPage() }) + const isReset = + !!selectedCategory || + isHideReadArticles || + currentSortType !== 'receivedAt,desc' + const reset = useCallback(() => { + setCategory(undefined) + setSortType('receivedAt,desc') + if (isHideReadArticles) { + toggleHideReadArticles() + } + }, [setCategory, setSortType, isHideReadArticles, toggleHideReadArticles]) + useEffect(() => { if (isArticleView) { setViewType('list') @@ -70,126 +84,128 @@ export default function UserInbox({ isArticleView, pageType }: InboxProps) { return (
    + className={`relative ${isArticleView ? 'hidden max-h-fit transition-all xl:sticky xl:top-10 xl:block xl:w-[360px]' : ''}`}>
    {isArticleView && ( -
    +
    )}
    -
    - - {pageType !== 'bookmark' && ( - - )} + className={`flex flex-col-reverse gap-3 ${isArticleView ? 'pb-5' : 'pb-4 lg:mb-3 lg:flex-row lg:items-center lg:justify-between lg:gap-5 lg:pb-0'}`}> +
    +
    +
    + + + {isReset && } + + + {pageType !== 'bookmark' && ( + + )} +
    +
    +
    +
    +
    -
    -
    -
    +
    -
    - {isLoading && ( -
    - + {isLoading && ( +
    + +
    + )} + {data && + (data.pages.length === 0 ? ( +
    + {searchValue ? ( + + ) : ( + + )}
    - )} - {data && - (data.pages.length === 0 ? ( -
    - {searchValue ? ( - - ) : ( - - )} -
    - ) : ( - <> -
    - -
    - +
    + - - ))} - {isError && ( -
    - + -
    - )} - {hasNextPage && ( -
    { - scrollRef.current = node - }} - className="h-1 w-full" + + ))} + {isError && ( +
    + - )} - {isFetchingNextPage && ( -
    - -
    - )} -
    +
    + )} + {hasNextPage && ( +
    { + scrollRef.current = node + }} + className="h-1 w-full" + /> + )} + {isFetchingNextPage && ( +
    + +
    + )}
    From cfcabfb28cd4e9d86c67acde2da296c01d7d1044 Mon Sep 17 00:00:00 2001 From: ChanLee_KR Date: Sun, 21 Jul 2024 22:18:30 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20ci=20prettier=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20(#ATR-571)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 706f8e82..81271971 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "description": "", "scripts": { "preinstall": "npx only-allow pnpm", - "postinstall": "husky install && pnpm build:icons && pnpm build:ds", + "postinstall": "husky install && pnpm build:icons && pnpm build:ds && prettier --write . --ignore-unknown", "prepare": "husky install", "lint": "pnpm -r run lint", "type": "pnpm -r run type",