Skip to content

Commit

Permalink
featL select document for create collection (#277)
Browse files Browse the repository at this point in the history
* feat: directory select

* feat: select document for collections
  • Loading branch information
jw-r authored Nov 26, 2024
1 parent a151318 commit e3fd38c
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 39 deletions.
2 changes: 1 addition & 1 deletion src/app/(routes)/collections/create/@header/default.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Text from '@/shared/components/ui/text'

const Header = () => {
return (
<header className="sticky top-0 z-50 flex h-[54px] shrink-0 items-center bg-white px-[16px]">
<header className="fixed top-0 z-50 flex h-[54px] w-full max-w-mobile shrink-0 items-center bg-white px-[16px]">
<GoBackButton icon="cancel" />
<div className="absolute right-1/2 translate-x-1/2">
<Text as="h1" typography="subtitle2-medium">
Expand Down
2 changes: 1 addition & 1 deletion src/app/(routes)/collections/create/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const Layout: FunctionComponent<LayoutProps> = ({ header, children }) => {
return (
<main>
{header}
{children}
<div className="pt-[54px]">{children}</div>
</main>
)
}
Expand Down
31 changes: 23 additions & 8 deletions src/features/collection/components/select-quiz-from-collection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,27 @@ import DirectorySelect from '../../directory/components/directory-select'
import FixedBottom from '@/shared/components/custom/fixed-bottom'
import { Button } from '@/shared/components/ui/button'
import Text from '@/shared/components/ui/text'
import { quizzes } from '@/features/quiz/config'
import SelectableQuizCard from './selectable-quiz-card'
import { Checkbox } from '@/shared/components/ui/checkbox'
import Label from '@/shared/components/ui/label'
import { useDirectories } from '@/requests/directory/hooks'
import { useDirectoryQuizzes } from '@/requests/quiz/hooks'

const SelectQuizFromCollection = () => {
// TODO: 전체 or 디렉토리 배열의 첫 번째 요소 | null이면 전체
const [selectedDirectoryId, setSelectedDirectoryId] = useState<string | null>(null)
const { data: directoriesData } = useDirectories()

const [selectedDirectoryId, setSelectedDirectoryId] = useState<number | null>(null)
const [selectedQuizIds, setSelectedQuizIds] = useState<number[]>([])
const [allChecked, setAllChecked] = useState(false)

const { data: directoryQuizzesData } = useDirectoryQuizzes(selectedDirectoryId)

const handleSelectAllClick = (check: boolean) => {
if (!directoryQuizzesData) return

setAllChecked(check)
if (check) {
const quizIds = quizzes.map((quiz) => quiz.id)
const quizIds = directoryQuizzesData.quizzes.map((quiz) => quiz.id)
const unSelectedQuizIds = quizIds.filter((quizId) => !selectedQuizIds.includes(quizId))
setSelectedQuizIds([...selectedQuizIds, ...unSelectedQuizIds])
return
Expand All @@ -36,17 +42,26 @@ const SelectQuizFromCollection = () => {
}

useEffect(() => {
selectedQuizIds.length === quizzes.length ? setAllChecked(true) : setAllChecked(false)
}, [selectedQuizIds])
if (!directoriesData) return
setSelectedDirectoryId(directoriesData.directories[0].id)
}, [directoriesData])

useEffect(() => {
if (!directoryQuizzesData) return
selectedQuizIds.length === directoryQuizzesData.quizzes.length
? setAllChecked(true)
: setAllChecked(false)
}, [selectedQuizIds.length, directoryQuizzesData])

const selectedQuizCount = selectedQuizIds.length

return (
<div className="mt-[24px] pb-[120px]">
<div className="sticky top-[54px] z-20 flex h-[44px] items-center justify-between bg-white">
<DirectorySelect
directories={directoriesData?.directories ?? []}
selectedDirectoryId={selectedDirectoryId}
selectDirectoryId={(directoryId?: string) => setSelectedDirectoryId(directoryId ?? null)}
selectDirectoryId={(directoryId: number) => setSelectedDirectoryId(directoryId)}
/>
<Text typography="text2-bold" className="text-text-accent">
{selectedQuizCount}개 선택됨
Expand All @@ -61,7 +76,7 @@ const SelectQuizFromCollection = () => {
</div>

<ul className="mt-[23px] flex flex-col gap-[8px]">
{quizzes.map((quiz) => (
{directoryQuizzesData?.quizzes.map((quiz) => (
<SelectableQuizCard
key={quiz.id}
quiz={quiz}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ const SelectableQuizCard = ({ quiz, onSelect, order, selected }: Props) => {
typography="text1-medium"
className={cn('text-text-secondary', selected && 'text-text-accent')}
>
{quiz.answer}
{quiz.quizType === 'MIX_UP' && (quiz.answer === 'correct' ? 'O' : 'X')}
{quiz.quizType === 'MULTIPLE_CHOICE' && quiz.answer}
</Text>
</div>
</div>
Expand Down
35 changes: 26 additions & 9 deletions src/features/directory/components/directory-select.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
import Icon from '@/shared/components/custom/icon'
import { Select, SelectContent, SelectItem, SelectTrigger } from '@/shared/components/ui/select'

interface Props {
selectedDirectoryId: string | null
selectDirectoryId: (categoryId?: string) => void
directories: Directory.Item[]
selectedDirectoryId: number | null
selectDirectoryId: (directoryId: number) => void
}

const DirectorySelect = ({}: Props) => {
const DirectorySelect = ({ directories, selectedDirectoryId, selectDirectoryId }: Props) => {
const curDirectory = directories.find((directory) => directory.id === selectedDirectoryId)

return (
<button className="flex items-center gap-[8px]">
<span>📚</span>
<span>전공 공부</span>
<Icon name="chevron-down" className="size-[16px] text-icon-tertiary" />
</button>
<Select
defaultValue={String(curDirectory?.id)}
onValueChange={(value) => selectDirectoryId(Number(value))}
>
<SelectTrigger className="flex w-fit items-center gap-2 border-none px-0 outline-none">
<span>{curDirectory?.emoji ?? '📁'}</span>
<span>{curDirectory?.name}</span>
</SelectTrigger>
<SelectContent className="rounded-[8px] bg-white">
{directories.map((directory) => (
<SelectItem key={directory.id} value={String(directory.id)}>
<div className="flex gap-2">
<span>{directory.emoji ?? '📁'}</span>
<span>{directory.name}</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
)
}

Expand Down
10 changes: 9 additions & 1 deletion src/requests/quiz/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
'use client'

import { useQuery } from '@tanstack/react-query'
import { fetchTodayQuizSetId } from '.'
import { fetchDirectoryQuizzes, fetchTodayQuizSetId } from '.'

export const useTodayQuizSetId = () => {
return useQuery({
queryKey: ['todayQuizSetId'],
queryFn: async () => fetchTodayQuizSetId(),
})
}

export const useDirectoryQuizzes = (directoryId: number | null) => {
return useQuery({
queryKey: ['directoryQuizzes', directoryId],
queryFn: async () => fetchDirectoryQuizzes({ directoryId: directoryId! }),
enabled: !!directoryId,
})
}
20 changes: 18 additions & 2 deletions src/requests/quiz/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export const fetchTodayQuizSetId = async () => {
)
return data
} catch (error: unknown) {
console.error(error)
throw error
}
}
Expand All @@ -37,7 +36,24 @@ export const fetchQuizSet = async ({ quizSetId }: { quizSetId: string }) => {
)
return data
} catch (error: unknown) {
console.error(error)
throw error
}
}

export const fetchDirectoryQuizzes = async ({ directoryId }: { directoryId: number }) => {
const session = await auth()

try {
const { data } = await http.get<Quiz.Response.GetDirectoryQuizzes>(
API_ENDPOINTS.QUIZ.GET.BY_DIRECTORY(directoryId),
{
headers: {
Authorization: `Bearer ${session?.user.accessToken}`,
},
}
)
return data
} catch (error: unknown) {
throw error
}
}
19 changes: 3 additions & 16 deletions src/shared/components/ui/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as SelectPrimitive from '@radix-ui/react-select'
import { Check, ChevronUp } from 'lucide-react'

import { cn } from '@/shared/lib/utils'
import Icon from '../custom/icon'

const Select = SelectPrimitive.Root

Expand All @@ -27,7 +28,7 @@ const SelectTrigger = React.forwardRef<
{children}
<SelectPrimitive.Icon asChild>
<div className="flex size-[16px] items-center justify-center">
<ChevronDown />
<Icon name="chevron-down" className="size-[16px] text-icon-tertiary" />
</div>
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
Expand Down Expand Up @@ -57,7 +58,7 @@ const SelectScrollDownButton = React.forwardRef<
className={cn('flex cursor-default items-center justify-center py-1', className)}
{...props}
>
<ChevronDown />
<Icon name="chevron-down" className="size-[16px] text-icon-tertiary" />
</SelectPrimitive.ScrollDownButton>
))
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName
Expand Down Expand Up @@ -153,17 +154,3 @@ export {
SelectScrollUpButton,
SelectScrollDownButton,
}

function ChevronDown() {
return (
<svg width="10" height="6" viewBox="0 0 10 6" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M1 1L5 5L9 1"
stroke="#A2A6AB"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)
}
2 changes: 2 additions & 0 deletions src/shared/configs/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ export const API_ENDPOINTS = {
REVIEW_PICK: (documentId: number) => `/documents/${documentId}/review-pick`,
/** GET /documents/{document_id}/quizzes - document_id에 해당하는 모든 퀴즈 가져오기 */
BY_DOCUMENT: (documentId: number) => `/documents/${documentId}/quizzes`,
/** GET /directories/{directory_id}/quizzes - 디렉토리에 생성된 모든 퀴즈 랜덤하게 가져오기 */
BY_DIRECTORY: (directoryId: number) => `/directories/${directoryId}/quizzes`,
/** GET /documents/{document_id}/download-quiz - 퀴즈 다운로드 */
DOWNLOAD: (documentId: number) => `/documents/${documentId}/download-quiz`,
},
Expand Down
9 changes: 9 additions & 0 deletions src/types/quiz.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ interface CreateQuizzesPayload {
quizCount: number
}

interface GetDirectoryQuizzesResponse {
quizzes: QuizWithMetadata[]
}

declare namespace Quiz {
type Item = CombineQuiz
type List = CombineQuiz[]
Expand Down Expand Up @@ -212,6 +216,11 @@ declare namespace Quiz {
*/
type GetDocumentQuizzes = DocumentQuizzesResponse

/** GET /api/v2/directories/{directory_id}/quizzes
* 디렉토리에 생성된 모든 퀴즈 랜덤하게 가져오기
*/
type GetDirectoryQuizzes = GetDirectoryQuizzesResponse

/** GET /api/v2/documents/{document_id}/download-quiz
* 퀴즈 다운로드
*/
Expand Down

0 comments on commit e3fd38c

Please sign in to comment.