From 6424fab4892fbb6014bf1f5d6ccb34f66c1c43ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A5=98=EC=A0=95=EC=9A=B0?= Date: Thu, 21 Nov 2024 23:19:52 +0900 Subject: [PATCH 1/4] feat: select directory --- src/app/(routes)/document/(create)/layout.tsx | 5 +- .../directory/contexts/directory-context.tsx | 47 ++++++++++++++++ .../directory-select-drawer/index.tsx | 54 ++++++++----------- 3 files changed, 73 insertions(+), 33 deletions(-) create mode 100644 src/features/directory/contexts/directory-context.tsx diff --git a/src/app/(routes)/document/(create)/layout.tsx b/src/app/(routes)/document/(create)/layout.tsx index 8b1116bf..c411afa2 100644 --- a/src/app/(routes)/document/(create)/layout.tsx +++ b/src/app/(routes)/document/(create)/layout.tsx @@ -1,5 +1,6 @@ import { FunctionComponent, PropsWithChildren } from 'react' import type { Metadata } from 'next' +import { DirectoryProvider } from '@/features/directory/contexts/directory-context' export const metadata: Metadata = {} @@ -9,10 +10,10 @@ interface LayoutProps extends PropsWithChildren { const Layout: FunctionComponent = ({ header, children }) => { return ( - <> + {header} {children} - + ) } diff --git a/src/features/directory/contexts/directory-context.tsx b/src/features/directory/contexts/directory-context.tsx new file mode 100644 index 00000000..d742aed9 --- /dev/null +++ b/src/features/directory/contexts/directory-context.tsx @@ -0,0 +1,47 @@ +'use client' + +import { useDirectories } from '@/requests/directory/hooks' +import { PropsWithChildren, createContext, useContext, useEffect, useMemo, useState } from 'react' + +interface DirectoryContextValues { + directories: Directory.List + selectedDirectory: Directory.Item | null + + selectedDirectoryId: number | null + selectDirectoryId: (id: number) => void +} + +const DirectoryContext = createContext(null) + +export function DirectoryProvider({ children }: PropsWithChildren) { + const { data } = useDirectories() + const [selectedDirectoryId, selectDirectoryId] = useState(null) + + useEffect(() => { + if (!data) return + selectDirectoryId(data.directories[0].id) + }, [data]) + + const values = useMemo( + () => ({ + directories: data?.directories || [], + selectedDirectory: + data?.directories.find((directory) => directory.id === selectedDirectoryId) || null, + selectedDirectoryId, + selectDirectoryId, + }), + [data, selectedDirectoryId, selectDirectoryId] + ) + + return {children} +} + +export const useDirectoryContext = () => { + const values = useContext(DirectoryContext) + + if (values == null) { + throw new Error('DirectoryProvider 내에서 사용해주세요.') + } + + return values +} diff --git a/src/features/document/components/directory-select-drawer/index.tsx b/src/features/document/components/directory-select-drawer/index.tsx index 4035dfbc..faf53faa 100644 --- a/src/features/document/components/directory-select-drawer/index.tsx +++ b/src/features/document/components/directory-select-drawer/index.tsx @@ -1,6 +1,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ 'use client' +import CreateDirectoryDialog from '@/features/directory/components/create-directory-dialog' +import { useDirectoryContext } from '@/features/directory/contexts/directory-context' import Icon from '@/shared/components/custom/icon' import { Drawer, @@ -13,34 +15,23 @@ import Text from '@/shared/components/ui/text' import { cn } from '@/shared/lib/utils' import { useState } from 'react' -// MoveDocumentDrawer 컴포넌트 const DirectorySelectDrawer = () => { - const [selectedDirectoryId, setSelectedDirectoryId] = useState('0') + const [open, setOpen] = useState(false) + const { directories, selectDirectoryId, selectedDirectoryId, selectedDirectory } = + useDirectoryContext() - // 목데이터 - const directoryList = [ - { - id: '0', - directoryName: '📊 전공 공부', - documentCount: 3, - }, - { - id: '1', - directoryName: '📊 전공 공부', - documentCount: 12, - }, - { - id: '2', - directoryName: '📊 전공 공부', - documentCount: 15, - }, - ] + const handleDirectorySelect = (id: number) => { + selectDirectoryId(id) + setOpen(false) + } + + const totalNotes = directories.reduce((acc, directory) => acc + directory.documentCount, 0) return ( - + @@ -64,13 +55,16 @@ const DirectorySelectDrawer = () => { 전체 노트 - 노트 30개 + 노트 {totalNotes}개
- {/* 폴더 개수만큼 렌더링 */} - {directoryList.map((directory) => ( -
- + +
From e58ad7d2b949a9e8cd5403f4660cfb855c6dc518 Mon Sep 17 00:00:00 2001 From: GeunaNa <155413929+rabyeoljji@users.noreply.github.com> Date: Fri, 22 Nov 2024 21:53:21 +0900 Subject: [PATCH 2/4] feat: document (#268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: document card check * feat: query client for storybook * fix: merge and fix error * feat: directory id에 따른 documents 가져오기 + queries 적용 * fix: 불필요한 코드 삭제 * fix: build error * fix: import * fix: storybook * fix: expend buttons bug --- .storybook/{preview.ts => preview.tsx} | 8 + .../document/(create)/@header/default.tsx | 2 +- .../document/(create)/file/layout.tsx | 3 +- .../(routes)/document/(create)/file/page.tsx | 4 +- .../document/(create)/notion/page.tsx | 2 +- .../(routes)/document/(create)/ui/header.tsx | 2 +- .../document/(main)/@header/default.tsx | 31 ++-- src/app/(routes)/document/(main)/layout.tsx | 15 +- .../document/[id]/(main)/@header/default.tsx | 5 +- .../document/[id]/modify/@header/default.tsx | 8 +- .../(routes)/document/[id]/modify/layout.tsx | 2 +- src/app/(routes)/main/page.tsx | 6 +- .../directory-menu-dots/index.stories.tsx | 6 +- .../components/directory-menu-dots/index.tsx | 6 +- .../directory-select-drawer/index.tsx | 0 .../directory/contexts/directory-context.tsx | 2 +- .../document/components/add-document-menu.tsx | 4 +- .../components/add-first-document/index.tsx | 8 +- .../animate-buttons/index.stories.tsx | 8 +- .../components/animate-buttons/index.tsx | 4 +- .../directory-select-drawer/index.stories.tsx | 18 -- .../document/components/document-list.tsx | 14 +- .../swipeable-document-card/index.stories.tsx | 15 +- .../swipeable-document-card/index.tsx | 29 ++- ...ctory-context.tsx => document-context.tsx} | 34 ++-- .../document/screens/document-content.tsx | 5 +- src/features/editor/index.tsx | 59 ------ .../components/edit-cancel-dialog.tsx | 0 .../components/title-input.tsx | 0 .../components/visual-editor.tsx | 0 .../context/edit-document-context.tsx | 0 .../{editor => modify}/libs/extensions.ts | 0 .../styles/remirror-custom.css | 0 src/features/pages/documents-in-directory.tsx | 24 ++- src/features/pages/modify-document.tsx | 11 +- src/requests/document/hooks.ts | 18 +- src/shared/hooks/use-check-list-ignore-ids.ts | 169 ++++++++++++++++++ src/shared/hooks/use-check-list.ts | 55 +----- 38 files changed, 336 insertions(+), 241 deletions(-) rename .storybook/{preview.ts => preview.tsx} (66%) rename src/features/{document => directory}/components/directory-menu-dots/index.stories.tsx (81%) rename src/features/{document => directory}/components/directory-menu-dots/index.tsx (93%) rename src/features/{document => directory}/components/directory-select-drawer/index.tsx (100%) delete mode 100644 src/features/document/components/directory-select-drawer/index.stories.tsx rename src/features/document/contexts/{directory-context.tsx => document-context.tsx} (56%) delete mode 100644 src/features/editor/index.tsx rename src/features/{document => modify}/components/edit-cancel-dialog.tsx (100%) rename src/features/{editor => modify}/components/title-input.tsx (100%) rename src/features/{editor => modify}/components/visual-editor.tsx (100%) rename src/features/{editor => modify}/context/edit-document-context.tsx (100%) rename src/features/{editor => modify}/libs/extensions.ts (100%) rename src/features/{editor => modify}/styles/remirror-custom.css (100%) create mode 100644 src/shared/hooks/use-check-list-ignore-ids.ts diff --git a/.storybook/preview.ts b/.storybook/preview.tsx similarity index 66% rename from .storybook/preview.ts rename to .storybook/preview.tsx index 5905f704..c6c97c87 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.tsx @@ -1,4 +1,5 @@ import React from 'react' +import { Providers } from '../src/providers' import type { Preview } from '@storybook/react' import '../src/app/globals.css' @@ -11,6 +12,13 @@ const preview: Preview = { }, }, }, + decorators: [ + (Story) => ( + + + + ), + ], } export default preview diff --git a/src/app/(routes)/document/(create)/@header/default.tsx b/src/app/(routes)/document/(create)/@header/default.tsx index 847784a3..99113684 100644 --- a/src/app/(routes)/document/(create)/@header/default.tsx +++ b/src/app/(routes)/document/(create)/@header/default.tsx @@ -1,4 +1,4 @@ -import DirectorySelectDrawer from '@/features/document/components/directory-select-drawer' +import DirectorySelectDrawer from '@/features/directory/components/directory-select-drawer' import GoBackButton from '@/shared/components/custom/go-back-button' import Text from '@/shared/components/ui/text' diff --git a/src/app/(routes)/document/(create)/file/layout.tsx b/src/app/(routes)/document/(create)/file/layout.tsx index 01240bb6..279e9491 100644 --- a/src/app/(routes)/document/(create)/file/layout.tsx +++ b/src/app/(routes)/document/(create)/file/layout.tsx @@ -1,12 +1,13 @@ import { FunctionComponent, PropsWithChildren } from 'react' import type { Metadata } from 'next' +import { DirectoryProvider } from '@/features/directory/contexts/directory-context' export const metadata: Metadata = {} interface LayoutProps extends PropsWithChildren {} const Layout: FunctionComponent = ({ children }) => { - return
{children}
+ return {children} } export default Layout diff --git a/src/app/(routes)/document/(create)/file/page.tsx b/src/app/(routes)/document/(create)/file/page.tsx index 3ffc8927..d75c325e 100644 --- a/src/app/(routes)/document/(create)/file/page.tsx +++ b/src/app/(routes)/document/(create)/file/page.tsx @@ -1,7 +1,7 @@ import Icon from '@/shared/components/custom/icon' import Text from '@/shared/components/ui/text' -import { EditDocumentProvider } from '@/features/editor/context/edit-document-context' -import TitleInput from '@/features/editor/components/title-input' +import { EditDocumentProvider } from '@/features/modify/context/edit-document-context' +import TitleInput from '@/features/modify/components/title-input' import FixedBottom from '@/shared/components/custom/fixed-bottom' import NewQuizDrawer from '@/features/quiz/components/new-quiz-drawer' import { Button } from '@/shared/components/ui/button' diff --git a/src/app/(routes)/document/(create)/notion/page.tsx b/src/app/(routes)/document/(create)/notion/page.tsx index c9f91559..952a1d49 100644 --- a/src/app/(routes)/document/(create)/notion/page.tsx +++ b/src/app/(routes)/document/(create)/notion/page.tsx @@ -1,4 +1,4 @@ -import { EditDocumentProvider } from '@/features/editor/context/edit-document-context' +import { EditDocumentProvider } from '@/features/modify/context/edit-document-context' import NewQuizDrawer from '@/features/quiz/components/new-quiz-drawer' import FixedBottom from '@/shared/components/custom/fixed-bottom' import Icon from '@/shared/components/custom/icon' diff --git a/src/app/(routes)/document/(create)/ui/header.tsx b/src/app/(routes)/document/(create)/ui/header.tsx index 847784a3..99113684 100644 --- a/src/app/(routes)/document/(create)/ui/header.tsx +++ b/src/app/(routes)/document/(create)/ui/header.tsx @@ -1,4 +1,4 @@ -import DirectorySelectDrawer from '@/features/document/components/directory-select-drawer' +import DirectorySelectDrawer from '@/features/directory/components/directory-select-drawer' import GoBackButton from '@/shared/components/custom/go-back-button' import Text from '@/shared/components/ui/text' diff --git a/src/app/(routes)/document/(main)/@header/default.tsx b/src/app/(routes)/document/(main)/@header/default.tsx index 9cb53bfd..f1886bf1 100644 --- a/src/app/(routes)/document/(main)/@header/default.tsx +++ b/src/app/(routes)/document/(main)/@header/default.tsx @@ -5,18 +5,20 @@ import Text from '@/shared/components/ui/text' import { useEffect, useState } from 'react' import { cn } from '@/shared/lib/utils' import Link from 'next/link' -import { useDirectoryContext } from '@/features/document/contexts/directory-context' import { Drawer, DrawerContent, DrawerTitle, DrawerTrigger } from '@/shared/components/ui/drawer' import SortIconBtn from '@/features/document/components/sort-icon-button' -import DirectoryMenuDots from '@/features/document/components/directory-menu-dots' +import DirectoryMenuDots from '@/features/directory/components/directory-menu-dots' import GoBackButton from '@/shared/components/custom/go-back-button' import { useDirectories } from '@/requests/directory/hooks' import CreateDirectoryDialog from '@/features/directory/components/create-directory-dialog' +import { useDocumentContext } from '@/features/document/contexts/document-context' +import { useDirectoryContext } from '@/features/directory/contexts/directory-context' // Header 컴포넌트 const Header = () => { const { data } = useDirectories() - const { isSelectMode, setIsSelectMode } = useDirectoryContext() + const { selectedDirectory } = useDirectoryContext() + const { isSelectMode, setIsSelectMode } = useDocumentContext() const [isDrawerOpen, setIsDrawerOpen] = useState(false) return ( @@ -33,7 +35,7 @@ const Header = () => { setIsSelectMode(false)} /> - 전공 공부 + {selectedDirectory?.name} 전체 선택 @@ -75,7 +77,8 @@ interface Props { } const DirectorySelectDrawer = ({ isDrawerOpen, setIsDrawerOpen, directories }: Props) => { - const { selectedDirectoryId, setButtonHidden, setSelectedDirectoryId } = useDirectoryContext() + const { selectedDirectory, selectedDirectoryId, selectDirectoryId } = useDirectoryContext() + const { setButtonHidden } = useDocumentContext() useEffect(() => { if (isDrawerOpen) { @@ -85,8 +88,10 @@ const DirectorySelectDrawer = ({ isDrawerOpen, setIsDrawerOpen, directories }: P } }, [isDrawerOpen, setButtonHidden]) - const currentDirectory = directories.find((directory) => directory.id === selectedDirectoryId) - const totalNotes = directories.reduce((acc, directory) => acc + directory.documentCount, 0) + const handleDirectorySelect = (id: number | null) => { + selectDirectoryId(id) + setIsDrawerOpen(false) + } return ( <> @@ -94,8 +99,8 @@ const DirectorySelectDrawer = ({ isDrawerOpen, setIsDrawerOpen, directories }: P @@ -126,7 +131,7 @@ const DirectorySelectDrawer = ({ isDrawerOpen, setIsDrawerOpen, directories }: P - } - /> - - - - - ) -} - -export default CreateWithEditor diff --git a/src/features/document/components/edit-cancel-dialog.tsx b/src/features/modify/components/edit-cancel-dialog.tsx similarity index 100% rename from src/features/document/components/edit-cancel-dialog.tsx rename to src/features/modify/components/edit-cancel-dialog.tsx diff --git a/src/features/editor/components/title-input.tsx b/src/features/modify/components/title-input.tsx similarity index 100% rename from src/features/editor/components/title-input.tsx rename to src/features/modify/components/title-input.tsx diff --git a/src/features/editor/components/visual-editor.tsx b/src/features/modify/components/visual-editor.tsx similarity index 100% rename from src/features/editor/components/visual-editor.tsx rename to src/features/modify/components/visual-editor.tsx diff --git a/src/features/editor/context/edit-document-context.tsx b/src/features/modify/context/edit-document-context.tsx similarity index 100% rename from src/features/editor/context/edit-document-context.tsx rename to src/features/modify/context/edit-document-context.tsx diff --git a/src/features/editor/libs/extensions.ts b/src/features/modify/libs/extensions.ts similarity index 100% rename from src/features/editor/libs/extensions.ts rename to src/features/modify/libs/extensions.ts diff --git a/src/features/editor/styles/remirror-custom.css b/src/features/modify/styles/remirror-custom.css similarity index 100% rename from src/features/editor/styles/remirror-custom.css rename to src/features/modify/styles/remirror-custom.css diff --git a/src/features/pages/documents-in-directory.tsx b/src/features/pages/documents-in-directory.tsx index cd1fdb11..171d89da 100644 --- a/src/features/pages/documents-in-directory.tsx +++ b/src/features/pages/documents-in-directory.tsx @@ -7,11 +7,29 @@ import AddDocumentMenu from '../document/components/add-document-menu' import Image from 'next/image' import Text from '@/shared/components/ui/text' import note_img from '@/../../public/assets/note.png' -import { useGetDocuments } from '@/requests/document/hooks' import Loading from '@/shared/components/custom/loading' +import { useDirectoryContext } from '../directory/contexts/directory-context' +import { useQuery } from '@tanstack/react-query' +import { queries } from '@/shared/lib/tanstack-query/query-keys' +import { useDocumentContext } from '../document/contexts/document-context' +import { useEffect } from 'react' const DocumentsInDirectory = () => { - const { data, isPending } = useGetDocuments() + const { selectedDirectoryId } = useDirectoryContext() + const { checkDoc } = useDocumentContext() + + const params = + selectedDirectoryId !== null ? { directoryId: String(selectedDirectoryId) } : undefined + const { data, isPending } = useQuery(queries.document.list(params)) + + useEffect(() => { + if (data) { + const documentCheckList = + data.documents.map((document) => ({ id: document.id, checked: false })) ?? [] + + checkDoc.set(documentCheckList) + } + }, [data]) if (isPending) { return @@ -33,8 +51,6 @@ const DocumentsInDirectory = () => { ) : ( - // 노트 리스트 렌더링 - // todo: useCheckList 훅 이용해 체크 구현 {data.documents.map((document, idx) => ( { const { id } = useParams() - const { data, isPending } = useGetDocumentDetail(Number(id[0])) + const { data, isPending } = useQuery(queries.document.item(Number(id[0]))) const { editorMarkdownContent: content } = useEditDocumentContext() if (isPending) { diff --git a/src/requests/document/hooks.ts b/src/requests/document/hooks.ts index 9524d8de..36f6505b 100644 --- a/src/requests/document/hooks.ts +++ b/src/requests/document/hooks.ts @@ -3,8 +3,7 @@ import { useMutation } from '@tanstack/react-query' import { useSession } from 'next-auth/react' import { createDocument } from './create-document' -import { useQuery } from '@tanstack/react-query' -import { fetchDocumentDetail, fetchDocuments, updateDocument } from '.' +import { updateDocument } from '.' export const useCreateDocument = () => { const { data: session } = useSession() @@ -23,18 +22,3 @@ export const useUpdateDocument = () => { updateDocument(params.documentId, params.request, session?.user.accessToken || ''), }) } - -export const useGetDocuments = (params?: { directoryId?: string; sortOption?: Document.Sort }) => { - return useQuery({ - queryKey: ['getDocuments', params?.directoryId, params?.sortOption], - queryFn: async () => fetchDocuments(params), - }) -} - -export const useGetDocumentDetail = (documentId: number) => { - return useQuery({ - queryKey: ['getDocumentDetail', documentId], - queryFn: async () => fetchDocumentDetail(documentId), - enabled: !!documentId, - }) -} diff --git a/src/shared/hooks/use-check-list-ignore-ids.ts b/src/shared/hooks/use-check-list-ignore-ids.ts new file mode 100644 index 00000000..0bbff64e --- /dev/null +++ b/src/shared/hooks/use-check-list-ignore-ids.ts @@ -0,0 +1,169 @@ +import { useCallback, useRef } from 'react' +import { useForceUpdate } from './use-force-update' + +interface Item { + id: string | number + checked?: boolean +} + +export function useCheckListIgnoreIds( + initialItems: T[], + options?: { ignoreIds: Item['id'][] } +) { + type IdType = T['id'] + const listRef = useRef(initialItems) + const forceUpdate = useForceUpdate() + + const findItem = useCallback( + (id: IdType) => listRef.current.find(({ id: _id }) => _id === id), + [] + ) + + const findIndex = useCallback( + (id: IdType) => listRef.current.findIndex(({ id: _id }) => _id === id), + [] + ) + + const isChecked = useCallback((id: IdType) => findItem(id)?.checked, [findItem]) + + const isAllChecked = useCallback(() => listRef.current.every(({ checked }) => checked), []) + + const isAllCheckedWithoutIgnored = useCallback(() => { + if (options?.ignoreIds == null || options?.ignoreIds.length === 0) { + return isAllChecked() + } + + return listRef.current.every(({ id, checked }) => { + if (options.ignoreIds.includes(id)) { + return true + } + + return checked + }) + }, [options, isAllChecked]) + + const set = useCallback( + (items: T[]) => { + listRef.current = items + forceUpdate() + }, + [forceUpdate] + ) + + const updateItem = useCallback( + (id: IdType, checked: boolean) => { + const idx = findIndex(id) + if (idx > -1) { + const item = listRef.current[idx] + + if (item.checked !== checked) { + const arr = [...listRef.current] + arr[idx] = { ...item, id, checked } + set(arr) + } + } + }, + [findIndex, set] + ) + + const toggle = useCallback( + (id: IdType) => updateItem(id, !isChecked(id)), + [isChecked, updateItem] + ) + + const check = useCallback( + (id: IdType) => { + updateItem(id, true) + }, + [updateItem] + ) + + const unCheck = useCallback( + (id: IdType) => { + updateItem(id, false) + }, + [updateItem] + ) + + const toggleAll = useCallback(() => { + const toggled = !isAllChecked() + const arr = listRef.current.map((item) => ({ ...item, checked: toggled })) + + set(arr) + }, [isAllChecked, set]) + + const updateAll = useCallback( + (checked: boolean) => { + if (listRef.current.every((item) => item.checked === checked)) { + return + } + set(listRef.current.map((item) => ({ ...item, checked }))) + }, + [set] + ) + + const updateAllWithoutIgnored = useCallback( + (checked: boolean) => { + if ( + listRef.current.every((item) => { + if (options?.ignoreIds.includes(item.id)) return true + + return item.checked === checked + }) + ) { + return + } + set( + listRef.current.map((item) => ({ + ...item, + checked: options?.ignoreIds.includes(item.id) ? false : checked, + })) + ) + }, + [set, options] + ) + + const checkAll = useCallback(() => { + updateAll(true) + }, [updateAll]) + + const checkAllWithoutIgnored = useCallback(() => { + updateAllWithoutIgnored(true) + }, [updateAllWithoutIgnored]) + + const unCheckAll = useCallback(() => { + updateAll(false) + }, [updateAll]) + + const unCheckAllWithoutIgnored = useCallback(() => { + updateAllWithoutIgnored(false) + }, [updateAllWithoutIgnored]) + + const getCheckedList = useCallback(() => { + return listRef.current.filter((item) => item.checked) + }, []) + + const getCheckedIds = useCallback(() => { + return getCheckedList().map(({ id }) => id) + }, [getCheckedList]) + + return { + list: listRef.current, + set, + isChecked, + isAllChecked, + check, + unCheck, + toggle, + updateItem, + toggleAll, + checkAll, + unCheckAll, + updateAll, + getCheckedList, + getCheckedIds, + isAllCheckedWithoutIgnored, + checkAllWithoutIgnored, + unCheckAllWithoutIgnored, + } +} diff --git a/src/shared/hooks/use-check-list.ts b/src/shared/hooks/use-check-list.ts index 14828ff7..794afc7a 100644 --- a/src/shared/hooks/use-check-list.ts +++ b/src/shared/hooks/use-check-list.ts @@ -1,15 +1,14 @@ import { useCallback, useRef } from 'react' import { useForceUpdate } from './use-force-update' +export type UseCheckListReturn = ReturnType> + interface Item { id: string | number checked?: boolean } -export function useCheckList( - initialItems: T[], - options?: { ignoreIds: Item['id'][] } -) { +export function useCheckList(initialItems: T[]) { type IdType = T['id'] const listRef = useRef(initialItems) const forceUpdate = useForceUpdate() @@ -28,20 +27,6 @@ export function useCheckList( const isAllChecked = useCallback(() => listRef.current.every(({ checked }) => checked), []) - const isAllCheckedWithoutIgnored = useCallback(() => { - if (options?.ignoreIds == null || options?.ignoreIds.length === 0) { - return isAllChecked() - } - - return listRef.current.every(({ id, checked }) => { - if (options.ignoreIds.includes(id)) { - return true - } - - return checked - }) - }, [options, isAllChecked]) - const set = useCallback( (items: T[]) => { listRef.current = items @@ -58,7 +43,7 @@ export function useCheckList( if (item.checked !== checked) { const arr = [...listRef.current] - arr[idx] = { ...item, id, checked } + arr[idx] = { ...item, checked } set(arr) } } @@ -102,43 +87,14 @@ export function useCheckList( [set] ) - const updateAllWithoutIgnored = useCallback( - (checked: boolean) => { - if ( - listRef.current.every((item) => { - if (options?.ignoreIds.includes(item.id)) return true - - return item.checked === checked - }) - ) { - return - } - set( - listRef.current.map((item) => ({ - ...item, - checked: options?.ignoreIds.includes(item.id) ? false : checked, - })) - ) - }, - [set, options] - ) - const checkAll = useCallback(() => { updateAll(true) }, [updateAll]) - const checkAllWithoutIgnored = useCallback(() => { - updateAllWithoutIgnored(true) - }, [updateAllWithoutIgnored]) - const unCheckAll = useCallback(() => { updateAll(false) }, [updateAll]) - const unCheckAllWithoutIgnored = useCallback(() => { - updateAllWithoutIgnored(false) - }, [updateAllWithoutIgnored]) - const getCheckedList = useCallback(() => { return listRef.current.filter((item) => item.checked) }, []) @@ -162,8 +118,5 @@ export function useCheckList( updateAll, getCheckedList, getCheckedIds, - isAllCheckedWithoutIgnored, - checkAllWithoutIgnored, - unCheckAllWithoutIgnored, } } From 5c5014c0675bd55c3c774494c1c0b49b3eeae699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A5=98=EC=A0=95=EC=9A=B0?= <88191233+jw-r@users.noreply.github.com> Date: Fri, 22 Nov 2024 22:47:47 +0900 Subject: [PATCH 3/4] feat: create document (#269) * feat: select directory * feat: create document --- .../write/components/create-quiz-drawer.tsx | 135 ++++++++++++++++++ .../write/pages/write-document-page.tsx | 39 ++--- src/types/quiz.d.ts | 77 +--------- 3 files changed, 157 insertions(+), 94 deletions(-) create mode 100644 src/features/write/components/create-quiz-drawer.tsx diff --git a/src/features/write/components/create-quiz-drawer.tsx b/src/features/write/components/create-quiz-drawer.tsx new file mode 100644 index 00000000..bdce5894 --- /dev/null +++ b/src/features/write/components/create-quiz-drawer.tsx @@ -0,0 +1,135 @@ +'use client' + +import FixedBottom from '@/shared/components/custom/fixed-bottom' +import Icon from '@/shared/components/custom/icon' +import { Button } from '@/shared/components/ui/button' +import { Drawer, DrawerContent, DrawerTitle, DrawerTrigger } from '@/shared/components/ui/drawer' +import { Slider } from '@/shared/components/ui/slider' +import Text from '@/shared/components/ui/text' +import { cn } from '@/shared/lib/utils' +import { useState } from 'react' + +interface Props { + handleCreateDocument: (params: { quizType: Quiz.Type; star: number }) => void +} + +const CreateQuizDrawer = ({ handleCreateDocument }: Props) => { + const totalQuizCount = 40 + const [selectedQuizCount, setSelectedQuizCount] = useState(10) + const [selectedQuizType, setSelectedQuizType] = useState('MULTIPLE_CHOICE') + + const handleSliderChange = (value: number[]) => { + const newCount = Math.round((value[0] / 100) * totalQuizCount) + setSelectedQuizCount(newCount) + } + + const handleClickQuizType = (quizType: Quiz.Type) => { + setSelectedQuizType(quizType) + } + + return ( + + + + + + + + + 원하는 유형과 문제 수를 선택해주세요 + + + +
+ + +
+ +
+ +
+ + 만들 문제 + + + {selectedQuizCount} 문제 + + +
+ + {selectedQuizCount} 문제 + + + {totalQuizCount} 문제 + +
+
+ +
+ + 현재 나의 별: 16개 + + +
+ + + ) +} + +export default CreateQuizDrawer diff --git a/src/features/write/pages/write-document-page.tsx b/src/features/write/pages/write-document-page.tsx index d3581905..17ab5e7e 100644 --- a/src/features/write/pages/write-document-page.tsx +++ b/src/features/write/pages/write-document-page.tsx @@ -6,36 +6,46 @@ import Icon from '@/shared/components/custom/icon' import Text from '@/shared/components/ui/text' import dynamic from 'next/dynamic' import FixedBottom from '@/shared/components/custom/fixed-bottom' -import { Button } from '@/shared/components/ui/button' import { useCreateDocument } from '@/requests/document/hooks' import { MAX_CHARACTERS, MIN_CHARACTERS } from '@/features/document/config' import { useDirectoryContext } from '@/features/directory/contexts/directory-context' +import CreateQuizDrawer from '../components/create-quiz-drawer' +import { useRouter } from 'next/navigation' const Editor = dynamic(() => import('../components/editor'), { ssr: false, }) const WriteDocumentPage = () => { + const router = useRouter() + const { selectedDirectory } = useDirectoryContext() const [title, setTitle] = useState('') const [content, setContent] = useState('') const { mutate: createDocumentMutate } = useCreateDocument() - const handleCreateDocument = () => { + const handleCreateDocument = ({ quizType, star }: { quizType: Quiz.Type; star: number }) => { // TODO: validation if (!selectedDirectory) { return } - createDocumentMutate({ - directoryId: selectedDirectory.id, - documentName: title, - file: content, - quizType: 'MULTIPLE_CHOICE', - star: 5, - documentType: 'TEXT', - }) + createDocumentMutate( + { + directoryId: selectedDirectory.id, + documentName: title, + file: content, + quizType, + star, + documentType: 'TEXT', + }, + { + onSuccess: ({ id }) => { + router.push(`/document/${id}`) + }, + } + ) } return ( @@ -67,14 +77,7 @@ const WriteDocumentPage = () => { setContent(value)} /> - +
) diff --git a/src/types/quiz.d.ts b/src/types/quiz.d.ts index 69ad2eaa..542b8db1 100644 --- a/src/types/quiz.d.ts +++ b/src/types/quiz.d.ts @@ -150,61 +150,11 @@ declare namespace Quiz { type Item = CombineQuiz type List = CombineQuiz[] type ItemWithMetadata = QuizWithMetadata - type QuizType = QuizType + type Type = QuizType type Record = QuizRecord type Result = UpdateQuizResultPayload['quizzes'][number] declare namespace Request { - /** GET /api/v2/today-quiz-info - * 오늘의 퀴즈 현황 - */ - type GetTodayInfo = void - - /** GET /api/v2/quizzes - * 생성된 모든 퀴즈 가져오기(전체 문서) - */ - type GetAllQuizzes = void - - /** GET /api/v2/quizzes/{quiz_set_id}/quiz-record - * 퀴즈 세트에 대한 상세 기록 - */ - type GetQuizSetRecord = void - - /** GET /api/v2/quizzes/quiz-records - * 전체 퀴즈 기록 - */ - type GetQuizRecords = void - - /** GET /api/v2/quiz-sets/{quiz_set_id} - * quizSet_id로 퀴즈 가져오기 - */ - type GetQuizSet = void - - /** GET /api/v2/quiz-sets/today - * 오늘의 퀴즈 세트 정보 가져오기 - */ - type GetTodayQuizSet = void - - /** GET /api/v2/quiz-analysis - * 퀴즈 분석 - */ - type GetQuizAnalysis = void - - /** GET /api/v2/documents/{document_id}/review-pick - * document_id로 복습 pick 가져오기 - */ - type GetReviewPick = void - - /** GET /api/v2/documents/{document_id}/quizzes - * document_id에 해당하는 모든 퀴즈 가져오기 - */ - type GetDocumentQuizzes = void - - /** GET /api/v2/documents/{document_id}/download-quiz - * 퀴즈 다운로드 - */ - type DownloadQuiz = void - /** PATCH /api/v2/quiz/result * 퀴즈 결과 업데이트 */ @@ -214,16 +164,6 @@ declare namespace Quiz { * 사용자가 생성한 문서에서 직접 퀴즈 생성(랜덤, OX, 객관식) */ type CreateQuizzes = CreateQuizzesPayload - - /** DELETE /api/v2/quizzes/{quiz_id}/delete-quiz - * 퀴즈 삭제 - */ - type DeleteQuiz = void - - /** DELETE /api/v2/quizzes/{quiz_id}/delete-invalid-quiz - * 잘못된 퀴즈 삭제 - */ - type DeleteInvalidQuiz = void } declare namespace Response { @@ -281,20 +221,5 @@ declare namespace Quiz { * 퀴즈 결과 업데이트 */ type UpdateQuizResult = UpdateQuizResultResponse - - /** POST /api/v2/quizzes/documents/{document_id}/create-quizzes - * 사용자가 생성한 문서에서 직접 퀴즈 생성(랜덤, OX, 객관식) - */ - type CreateQuizzes = void - - /** DELETE /api/v2/quizzes/{quiz_id}/delete-quiz - * 퀴즈 삭제 - */ - type DeleteQuiz = void - - /** DELETE /api/v2/quizzes/{quiz_id}/delete-invalid-quiz - * 잘못된 퀴즈 삭제 - */ - type DeleteInvalidQuiz = void } } From 8d9944f69d2160017d1381f0a3f9712a4f64cd4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A5=98=EC=A0=95=EC=9A=B0?= Date: Fri, 22 Nov 2024 22:49:10 +0900 Subject: [PATCH 4/4] fix: lint error --- src/features/write/components/create-quiz-drawer.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/features/write/components/create-quiz-drawer.tsx b/src/features/write/components/create-quiz-drawer.tsx index bdce5894..19a4a3a8 100644 --- a/src/features/write/components/create-quiz-drawer.tsx +++ b/src/features/write/components/create-quiz-drawer.tsx @@ -1,6 +1,5 @@ 'use client' -import FixedBottom from '@/shared/components/custom/fixed-bottom' import Icon from '@/shared/components/custom/icon' import { Button } from '@/shared/components/ui/button' import { Drawer, DrawerContent, DrawerTitle, DrawerTrigger } from '@/shared/components/ui/drawer'