Skip to content

Commit

Permalink
feat: sort collection (#315)
Browse files Browse the repository at this point in the history
* feat: select-quiz-count

* feat: select min quiz count

* feat: select quiz type

* feat: select categories
  • Loading branch information
jw-r authored Dec 23, 2024
1 parent 22e6893 commit 20bd684
Show file tree
Hide file tree
Showing 12 changed files with 459 additions and 33 deletions.
47 changes: 47 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"pdfjs-dist": "^4.9.155",
"prettier": "^2.8.8",
"qs": "^6.12.1",
"query-string": "^9.1.1",
"react": "^18",
"react-canvas-confetti": "^2.0.7",
"react-day-picker": "^8.10.1",
Expand Down
34 changes: 19 additions & 15 deletions src/features/collection/components/exploration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,29 @@
import Icon from '@/shared/components/custom/icon'
import Collection from './collection'
import CollectionList from './collection-list'
import Text from '@/shared/components/ui/text'
import { useCollections } from '@/requests/collection/hooks'
import Loading from '@/shared/components/custom/loading'
import { useUser } from '@/shared/hooks/use-user'
import Link from 'next/link'
import { useScrollPosition } from '@/shared/hooks/use-scroll-position'

const controlButtons = ['분야', '퀴즈 유형', '문제 수']
import SelectMinQuizCountDrawer from './select-min-quiz-count-drawer'
import { useSearchParams } from 'next/navigation'
import { DEFAULT_COLLECTION_QUIZ_COUNT } from '../config'
import SelectQuizTypeDrawer from './select-quiz-type-drawer'
import SelectCategoryDrawer from './select-category-drawer'

const Exploration = () => {
const { data: collectionsData, isLoading } = useCollections()
const searchParams = useSearchParams()
const categories = searchParams.getAll('collection-category') as Collection.Field[]
const quizType = searchParams.get('quiz-type') as Quiz.Type
const minQuizCount = Number(searchParams.get('min-quiz-count')) || DEFAULT_COLLECTION_QUIZ_COUNT

const { data: collectionsData, isLoading } = useCollections({
collectionSortOption: 'POPULARITY',
collectionCategories: categories,
quizType,
quizCount: minQuizCount,
})
const { user } = useUser()

const scrollContainerRef = useScrollPosition({ pageKey: 'exploration' })
Expand All @@ -22,17 +34,9 @@ const Exploration = () => {
<>
<div className="flex h-[60px] items-center justify-between px-[16px]">
<div className="flex gap-[8px]">
{controlButtons.map((button) => (
<button
key={button}
className="flex items-center gap-[4px] rounded-full border bg-button-fill-outlined py-[7.5px] pl-[14px] pr-[10px]"
>
<Text typography="button4" className="text-button-label-tertiary">
{button}
</Text>
<Icon name="chevron-down" className="size-[12px] text-icon-tertiary" />
</button>
))}
<SelectCategoryDrawer categories={categories} />
<SelectQuizTypeDrawer quizType={quizType} />
<SelectMinQuizCountDrawer count={minQuizCount} />
</div>
<Icon name="sort" className="size-[16px]" />
</div>
Expand Down
117 changes: 117 additions & 0 deletions src/features/collection/components/select-category-drawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
'use client'

import QS from 'query-string'
import FixedBottom from '@/shared/components/custom/fixed-bottom'
import Icon from '@/shared/components/custom/icon'
import { Button } from '@/shared/components/ui/button'
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from '@/shared/components/ui/drawer'
import Text from '@/shared/components/ui/text'
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { cn } from '@/shared/lib/utils'
import { useState } from 'react'
import { CATEGORIES } from '@/features/category/config'
import { Checkbox } from '@/shared/components/ui/checkbox'
import Label from '@/shared/components/ui/label'

interface Props {
categories: Collection.Field[]
}

const SelectCategoryDrawer = ({ categories }: Props) => {
const [innerCategories, setInnerCategories] = useState<Collection.Field[]>(categories)
const searchParamsString = useSearchParams().toString()
const router = useRouter()
const pathname = usePathname()

const sortByCategories = (categories: Collection.Field[]) => {
const categoryOrder = CATEGORIES.map((category) => category.id)
return categories.sort((a, b) => categoryOrder.indexOf(a) - categoryOrder.indexOf(b))
}

return (
<Drawer>
<DrawerTrigger asChild>
<button
className={cn(
'h-[32px] flex items-center gap-[4px] rounded-full border bg-button-fill-outlined pl-[14px] pr-[10px] text-button-label-tertiary',
innerCategories.length > 0 && 'border-button-fill-primary text-text-accent'
)}
>
<Text typography="button4">
{innerCategories.length < 1
? '분야'
: `${CATEGORIES.find((category) => category.id === innerCategories[0])?.name}${
innerCategories.length > 1 ? ` 외 ${innerCategories.length - 1}` : ''
}`}
</Text>
<Icon
name="chevron-down"
className={cn(
'size-[12px] text-icon-tertiary',
innerCategories.length > 0 && 'text-text-accent'
)}
/>
</button>
</DrawerTrigger>
<DrawerContent className="mx-auto h-[85vh] max-w-mobile px-4">
<DrawerHeader className="px-0 py-5">
<DrawerTitle>
<Text typography="title3">분야</Text>
</DrawerTitle>
</DrawerHeader>
<div className="h-px bg-border-divider" />

<div>
{CATEGORIES.map((category) => (
<div key={category.id} className="flex items-center gap-3">
<Checkbox
id={category.id}
value={category.id}
checked={innerCategories.includes(category.id)}
onCheckedChange={() => {
if (innerCategories.includes(category.id)) {
setInnerCategories(
sortByCategories(innerCategories.filter((c) => c !== category.id))
)
} else {
setInnerCategories(sortByCategories([...innerCategories, category.id]))
}
}}
/>
<Label htmlFor={category.id} className="py-2.5">
<Text typography="text1-medium">{category.name}</Text>
</Label>
</div>
))}
</div>

<FixedBottom className="flex gap-1.5">
<DrawerClose className="w-full">
<Button
className="w-full"
onClick={() => {
const newQueryString = QS.stringify({
...QS.parse(searchParamsString),
'collection-category':
innerCategories.length > 0 ? innerCategories.join(',') : undefined,
})
router.replace(`${pathname}?${newQueryString}`)
}}
>
적용하기
</Button>
</DrawerClose>
</FixedBottom>
</DrawerContent>
</Drawer>
)
}

export default SelectCategoryDrawer
112 changes: 112 additions & 0 deletions src/features/collection/components/select-min-quiz-count-drawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
'use client'

import QS from 'query-string'
import FixedBottom from '@/shared/components/custom/fixed-bottom'
import Icon from '@/shared/components/custom/icon'
import { Button } from '@/shared/components/ui/button'
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from '@/shared/components/ui/drawer'
import { Slider } from '@/shared/components/ui/slider'
import Text from '@/shared/components/ui/text'
import { useState } from 'react'
import { DEFAULT_COLLECTION_QUIZ_COUNT } from '../config'
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { cn } from '@/shared/lib/utils'

interface Props {
count: number
}

const SelectMinQuizCountDrawer = ({ count }: Props) => {
const [innerCount, setInnerCount] = useState(count)
const searchParamsString = useSearchParams().toString()
const router = useRouter()
const pathname = usePathname()

const isDefaultCount = innerCount === DEFAULT_COLLECTION_QUIZ_COUNT

return (
<Drawer>
<DrawerTrigger asChild>
<button
className={cn(
'h-[32px] flex items-center gap-[4px] rounded-full border bg-button-fill-outlined pl-[14px] pr-[10px] text-button-label-tertiary',
!isDefaultCount && 'border-button-fill-primary text-text-accent'
)}
>
<Text typography="button4">{isDefaultCount ? '문제 수' : `${innerCount}문제`}</Text>
<Icon
name="chevron-down"
className={cn('size-[12px] text-icon-tertiary', !isDefaultCount && 'text-text-accent')}
/>
</button>
</DrawerTrigger>
<DrawerContent className="mx-auto h-[60vh] max-w-mobile px-4">
<DrawerHeader className="px-0 py-5">
<DrawerTitle>
<Text typography="title3">문제 수</Text>
</DrawerTitle>
</DrawerHeader>
<div className="h-px bg-border-divider" />
<div className="mt-[25px]">
<Text typography="text1-medium" color="sub" className="text-center">
최소 문제 수
</Text>
<Text typography="title1" color="accent" className="mt-2 text-center">
{innerCount} 문제
</Text>
<div className="mt-8">
<Slider
value={[innerCount]}
max={99}
onValueChange={(value) => {
if (value[0] == null || value[0] < DEFAULT_COLLECTION_QUIZ_COUNT) return

setInnerCount(value[0])
}}
/>
<div className="mt-2.5 flex justify-between">
<Text typography="text2-medium" color="sub">
5 문제
</Text>
<Text typography="text2-medium" color="sub">
99 문제
</Text>
</div>
</div>
</div>
<FixedBottom className="flex gap-1.5">
<Button
className="w-[35%]"
colors="tertiary"
onClick={() => setInnerCount(DEFAULT_COLLECTION_QUIZ_COUNT)}
>
초기화
</Button>
<DrawerClose>
<Button
className="flex-1"
onClick={() => {
const newQueryString = QS.stringify({
...QS.parse(searchParamsString),
'min-quiz-count': innerCount,
})
router.replace(`${pathname}?${newQueryString}`)
}}
>
적용하기
</Button>
</DrawerClose>
</FixedBottom>
</DrawerContent>
</Drawer>
)
}

export default SelectMinQuizCountDrawer
Loading

0 comments on commit 20bd684

Please sign in to comment.