Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:picktoss/pick-toss-next into dev…
Browse files Browse the repository at this point in the history
…elop
  • Loading branch information
rabyeoljji committed Dec 27, 2024
2 parents 3d57150 + 1b5f449 commit 121334d
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 1 deletion.
17 changes: 17 additions & 0 deletions src/app/(routes)/collections/edit-info/[id]/@header/default.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import GoBackButton from '@/shared/components/custom/go-back-button'
import Text from '@/shared/components/ui/text'

const Header = () => {
return (
<header className="fixed top-0 z-50 flex h-[54px] w-full max-w-mobile shrink-0 items-center bg-white px-[16px]">
<GoBackButton />
<div className="absolute right-1/2 translate-x-1/2">
<Text as="h1" typography="subtitle2-medium">
컬렉션 정보수정
</Text>
</div>
</header>
)
}

export default Header
19 changes: 19 additions & 0 deletions src/app/(routes)/collections/edit-info/[id]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { FunctionComponent, PropsWithChildren } from 'react'
import type { Metadata } from 'next'

export const metadata: Metadata = {}

interface LayoutProps extends PropsWithChildren {
header: React.ReactNode
}

const Layout: FunctionComponent<LayoutProps> = ({ header, children }) => {
return (
<main>
{header}
<div className="pt-[54px]">{children}</div>
</main>
)
}

export default Layout
11 changes: 11 additions & 0 deletions src/app/(routes)/collections/edit-info/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import EditCollectionInfoForm from '@/features/collection/components/edit-collection-info-form'

const EditCollectionInfoPage = () => {
return (
<div className="px-[20px]">
<EditCollectionInfoForm />
</div>
)
}

export default EditCollectionInfoPage
166 changes: 166 additions & 0 deletions src/features/collection/components/edit-collection-info-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
'use client'

import { CATEGORIES } from '@/features/category/config'
import { useCollectionInfo, useUpdateCollectionInfo } from '@/requests/collection/hooks'
import CategoryTag from '@/shared/components/custom/category-tag'
import FixedBottom from '@/shared/components/custom/fixed-bottom'
import Loading from '@/shared/components/custom/loading'
import { Button } from '@/shared/components/ui/button'
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from '@/shared/components/ui/drawer'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
} from '@/shared/components/ui/dropdown-menu'
import Text from '@/shared/components/ui/text'
import { Textarea } from '@/shared/components/ui/textarea'
import EmojiPicker from 'emoji-picker-react'
import { useParams, useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'

const EditCollectionInfoForm = () => {
const router = useRouter()

const { id } = useParams()
const { data } = useCollectionInfo(Number(id))

const [emoji, setEmoji] = useState('')
const [title, setTitle] = useState('')
const [description, setDescription] = useState('')
const [categoryId, setCategoryId] = useState<Collection.Field>(CATEGORIES[0]!.id)

const { mutate: editCollectionInfoMutate, isPending: isEditCollectionInfoPending } =
useUpdateCollectionInfo()

const handleEditCollectionInfo = () => {
if (isEditCollectionInfoPending) {
return
}

editCollectionInfoMutate(
{
collectionId: Number(id),
payload: {
name: title,
emoji,
description,
collectionCategory: CATEGORIES.find((category) => category.id === categoryId)!.id,
},
},
{
onSuccess: () => {
router.replace(`/collections/${Number(id)}`)
},
}
)
}

useEffect(() => {
if (!data) return

setEmoji(data.emoji)
setTitle(data.name)
setDescription(data.description)
setCategoryId(CATEGORIES.find((category) => category.name === data.collectionCategory)!.id)
}, [data])

if (!data) {
return <Loading center />
}

return (
<div className="mt-3">
<div>
{/* 이모지 선택 및 컬렉션 이름 입력 */}
<div className="flex items-center gap-[20px]">
<DropdownMenu>
<DropdownMenuTrigger className="outline-none">
<div className="flex-center size-[48px] rounded-[12px] bg-background-base-02 text-3xl">
{emoji}
</div>
</DropdownMenuTrigger>

<DropdownMenuContent>
<EmojiPicker
skinTonesDisabled
height={'60vh'}
onEmojiClick={(emojiData) => {
setEmoji(emojiData.emoji)
}}
/>
</DropdownMenuContent>
</DropdownMenu>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="새로운 컬렉션"
className="flex-1 bg-transparent text-title2 placeholder:text-text-placeholder-02 focus:outline-none"
autoFocus
/>
</div>

{/* 분야 선택 */}
<div className="mt-[25px] flex items-center gap-[5px]">
<Text typography="text1-medium" color="secondary">
분야<span className="text-text-accent">*</span>
</Text>
<Drawer>
<DrawerTrigger>
<div className="rounded-full bg-background-base-02 px-[14px] py-[5px]">
<CategoryTag
title={CATEGORIES.find((category) => category.id === categoryId)?.name ?? ''}
/>
</div>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>카테고리를 선택해주세요.</DrawerTitle>
</DrawerHeader>
<div className="flex flex-col gap-2 p-4">
{CATEGORIES.map((category) => (
<DrawerClose key={category.id}>
<CategoryTag title={category.name} onClick={() => setCategoryId(category.id)} />
</DrawerClose>
))}
</div>
</DrawerContent>
</Drawer>
</div>

{/* 컬렉션 설명 */}
<div className="mt-[27px]">
<Text typography="text1-medium" color="secondary">
컬렉션 설명<span className="text-text-accent">*</span>
</Text>
<Textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
className="mt-2 min-h-[130px] rounded-[8px] border-none bg-background-base-02"
/>
<Text typography="text2-medium" color="caption" className="mt-2">
200자 이내로 입력해주세요 ({description.length}/200)
</Text>
</div>
</div>
<FixedBottom className="flex gap-[6px]">
<Button
variant={'largeRound'}
className="w-full"
onClick={() => handleEditCollectionInfo()}
disabled={isEditCollectionInfoPending}
>
만들기
</Button>
</FixedBottom>
</div>
)
}

export default EditCollectionInfoForm
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client'

import Icon from '@/shared/components/custom/icon'
import {
DropdownMenu,
Expand All @@ -6,19 +8,25 @@ import {
DropdownMenuTrigger,
} from '@/shared/components/ui/dropdown-menu'
import Text from '@/shared/components/ui/text'
import { useRouter } from 'next/navigation'

interface Props {
collectionId: number
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const MyCollectionInfoDropdownMenu = ({ collectionId }: Props) => {
const router = useRouter()

return (
<DropdownMenu>
<DropdownMenuTrigger>
<Icon name="menu-dots" className="size-[24px]" />
</DropdownMenuTrigger>
<DropdownMenuContent className="min-w-[240px] bg-white p-0 *:cursor-pointer">
<DropdownMenuContent
className="min-w-[240px] bg-white p-0 *:cursor-pointer"
onClick={() => router.push(`/collections/edit-info/${collectionId}`)}
>
<DropdownMenuItem className="flex justify-between px-5 py-4 hover:bg-gray-100">
<Text typography="subtitle2-medium">컬렉션 정보 수정</Text>
<Icon name="write-line" className="size-[20px]" />
Expand All @@ -27,6 +35,7 @@ const MyCollectionInfoDropdownMenu = ({ collectionId }: Props) => {
<Text typography="subtitle2-medium">문제 편집</Text>
<Icon name="write-line" className="size-[20px]" />
</DropdownMenuItem>
{/** TODO: Delete Dialog */}
<DropdownMenuItem className="flex justify-between px-5 py-4 text-text-critical hover:bg-gray-100">
<Text typography="subtitle2-medium">컬렉션 삭제</Text>
<Icon name="bin" className="size-[20px]" />
Expand Down
22 changes: 22 additions & 0 deletions src/requests/collection/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,25 @@ export const getRandomCollectionQuizzes = async ({ categoryId }: { categoryId: s
throw error
}
}

export const deleteCollection = async (collectionId: number) => {
try {
await http.delete(API_ENDPOINTS.COLLECTION.DELETE.COLLECTION(collectionId))
} catch (error) {
throw error
}
}

export const updateCollectionInfo = async ({
collectionId,
payload,
}: {
collectionId: number
payload: Collection.Request.UpdateInfo
}) => {
try {
await http.patch(API_ENDPOINTS.COLLECTION.PATCH.UPDATE_INFO(collectionId), payload)
} catch (error) {
throw error
}
}
32 changes: 32 additions & 0 deletions src/requests/collection/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
getMyCollections,
getCollectionInfo,
getRandomCollectionQuizzes,
deleteCollection,
updateCollectionInfo,
} from './client'

export const useCollections = (props?: {
Expand Down Expand Up @@ -215,3 +217,33 @@ export const useBookmarkMutation = () => {
},
})
}

export const useDeleteCollection = () => {
const queryClient = getQueryClient()

return useMutation({
mutationFn: deleteCollection,
onSettled: async () => {
await Promise.all([
queryClient.invalidateQueries({ queryKey: ['collections'] }),
queryClient.invalidateQueries({ queryKey: ['myCollections'] }),
queryClient.invalidateQueries({ queryKey: ['collectionInfo'] }),
])
},
})
}

export const useUpdateCollectionInfo = () => {
const queryClient = getQueryClient()

return useMutation({
mutationFn: updateCollectionInfo,
onSuccess: async () => {
await Promise.all([
queryClient.invalidateQueries({ queryKey: ['collections'] }),
queryClient.invalidateQueries({ queryKey: ['myCollections'] }),
queryClient.invalidateQueries({ queryKey: ['collectionInfo'] }),
])
},
})
}

0 comments on commit 121334d

Please sign in to comment.