Skip to content

Commit

Permalink
Merge pull request #51 from uju-in/fix/save-qa
Browse files Browse the repository at this point in the history
fix: 찜 목록 QA 수정
  • Loading branch information
mintmin0320 authored Apr 2, 2024
2 parents edf8ce2 + bd03960 commit 4efc2d8
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import useDeleteSave from '@/app/_hook/api/saves/useDeleteSave'
import { useChangeSaveFolderName } from '@/app/_hook/api/saves/useChangeSaveFolderName'
import { useSetRecoilState } from 'recoil'
import { saveModeState } from '@/app/_atoms/saveModeState'
import renderToast from '@/app/_utils/toast'
import { validateSaveFolderName } from '../../_utils/validation'

/**
* 찜 폴더 내부 페이지 Header
Expand Down Expand Up @@ -121,6 +123,8 @@ export namespace SaveFolderHeader {
const setMode = useSetRecoilState(saveModeState)

const handleChangeName = useCallback(async () => {
if (!validateSaveFolderName(newFolderName)) return

await changeName({ folderId, folderName: newFolderName })
router.replace(`/saves/${folderId}?name=${newFolderName}`)
setMode(SavePageMode.DEFAULT)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import Modal from '@/app/_components/modal'
import useAddSaveFolder from '@/app/_hook/api/saves/useAddSaveFolder'
import renderToast from '@/app/_utils/toast'
import { cn } from '@/app/_utils/twMerge'
import Image from 'next/image'
import React, { useCallback, useState } from 'react'
import { validateSaveFolderName } from '../_utils/validation'

interface Props {
setShowAddFolderModal: React.Dispatch<React.SetStateAction<boolean>>
Expand All @@ -14,6 +16,8 @@ export default function AddFolderModal({ setShowAddFolderModal }: Props) {
const { mutateAsync: addFolder } = useAddSaveFolder()

const handleAddFolder = useCallback(async () => {
if (!validateSaveFolderName(folderName)) return

await addFolder(folderName)
setShowAddFolderModal(false)
}, [folderName, setShowAddFolderModal, addFolder])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { cn } from '@/app/_utils/twMerge'
import renderToast from '@/app/_utils/toast'
import { useChangeSaveFolderName } from '@/app/_hook/api/saves/useChangeSaveFolderName'
import { useParams, useRouter } from 'next/navigation'
import { validateSaveFolderName } from '../_utils/validation'

interface Props {
originFolderName: string
Expand All @@ -21,10 +22,7 @@ export default function MoChangeFolderNameModal(props: Props) {
const router = useRouter()

const handleChangeName = useCallback(async () => {
if (!newFolderName) {
renderToast({ type: 'error', message: '폴더 이름을 입력해주세요.' })
return
}
if (!validateSaveFolderName(newFolderName)) return

await changeName({ folderId: fId, folderName: newFolderName })
router.replace(`/saves/${fId}?name=${newFolderName}`)
Expand Down
234 changes: 141 additions & 93 deletions src/app/(route)/(withLayout)/saves/_component/MoveFolderModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { saveModeState } from '@/app/_atoms/saveModeState'
import Modal from '@/app/_components/modal'
import useMoveSaveItems, {
MoveSaveItemsRequest,
} from '@/app/_hook/api/saves/useMoveSaveItems'
Expand All @@ -8,7 +7,7 @@ import { SaveFolderType, SavePageMode } from '@/app/_types/save.type'
import renderToast from '@/app/_utils/toast'
import { cn } from '@/app/_utils/twMerge'
import Image from 'next/image'
import React, { useCallback, useState } from 'react'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { useSetRecoilState } from 'recoil'

interface Props {
Expand All @@ -26,11 +25,47 @@ export default function MoveFolderModal(props: Props) {
setShowMoveFolderModal,
setShowAddFolderModal,
} = props

const [selectFolderId, setSelectFolderId] = useState<number | null>(null)
const { saveInfo, isLoading, isError } = useSaveList('folder')
const { mutateAsync: moveItems } = useMoveSaveItems()
const setMode = useSetRecoilState(saveModeState)

const [startY, setStartY] = useState(0)
const [moveY, setMoveY] = useState(0)
const [endY, setEndY] = useState(0)

const ref = useRef<HTMLDivElement>(null)

const onTouchStart = (e: TouchEvent) => {
setStartY(e.touches[0].clientY)
}

const onTouchMove = (e: TouchEvent) => {
setMoveY(e.targetTouches[0].clientY)
}

const onTouchEnd = (e: TouchEvent) => {
setEndY(e.changedTouches[0].clientY)
}

useEffect(() => {
ref.current?.addEventListener('touchstart', onTouchStart)
ref.current?.addEventListener('touchmove', onTouchMove)
ref.current?.addEventListener('touchend', onTouchEnd)
return () => {
ref.current?.removeEventListener('touchstart', onTouchStart)
ref.current?.removeEventListener('touchmove', onTouchMove)
ref.current?.removeEventListener('touchend', onTouchEnd)
}
}, [onTouchEnd, onTouchMove, onTouchStart, ref])

useEffect(() => {
if (endY - startY > 50) {
setShowMoveFolderModal(false)
}
}, [endY, startY, setShowMoveFolderModal])

const handleMoveItems = useCallback(async () => {
if (!selectFolderId) return
if (currentFolderId === selectFolderId) {
Expand Down Expand Up @@ -63,107 +98,120 @@ export default function MoveFolderModal(props: Props) {
const folderList = saveInfo.favoriteInfos as SaveFolderType[]

return (
<Modal innerClassNames="mo:fixed mo:flex mo:flex-col mo:h-[90%] mo:top-auto mo:max-w-full mo:w-full mo:bottom-0 mo:left-0 mo:translate-x-0 mo:translate-y-0">
<div
id="ModalContainer"
className="fixed bottom-0 left-0 right-0 top-0 z-[500] h-screen min-h-screen w-full bg-[rgba(0,0,0,0.7)]"
>
<div
id="ModalInner"
className={cn(
'flex flex-col gap-[25px] p-[26px_46px]',
'mo:h-full mo:gap-0 mo:p-0',
'absolute left-[50%] top-[50%] max-h-[75vh] max-w-[90vw] -translate-x-1/2 -translate-y-1/2 rounded-[8px] bg-white',
'mo:fixed mo:bottom-0 mo:left-0 mo:top-auto mo:flex mo:h-[90%] mo:w-full mo:max-w-full mo:translate-x-0 mo:translate-y-0 mo:flex-col',
)}
style={{ height: `calc(100% - ${moveY}px)` }}
onClick={(e) => e.stopPropagation()}
>
{/* <div className="hidden mo:block">
<div className="mx-auto my-[17px] h-[5px] w-[40px] rounded-full bg-[#d2d2d2]" />
<h1 className="border-b py-[11.5px] text-center text-[16px] font-semibold">
폴더 이동
</h1>
</div> */}
<div className="mo:border-b mo:p-[20px_18px]">
{/* 닫기 버튼 */}
<button
className="absolute right-[20px]"
type="button"
onClick={() => {
setShowMoveFolderModal(false)
}}
>
<Image
src="/image/icon/icon-close.svg"
alt="close"
width={24}
height={24}
/>
</button>

{/* 타이틀 */}
<h1 className="text-[24px] font-semibold">폴더 이동</h1>
</div>
{/* 폴더 목록 */}
<section
<div
className={cn(
'h-[410px] w-[380px] divide-y overflow-y-scroll rounded-l-[8px] border border-[#DADADA]',
'mo:h-auto mo:w-auto mo:flex-1 mo:border-0',
'flex flex-col gap-[25px] p-[26px_46px]',
'mo:h-full mo:gap-0 mo:p-0',
)}
>
{folderList.map((item) => {
const { favoriteId, originalName, metadata } = item
const { imageUrls } = metadata.folderMetadata
return (
<div
key={favoriteId}
onClick={() => {
if (selectFolderId === favoriteId) setSelectFolderId(null)
else setSelectFolderId(favoriteId)
}}
className={cn(
'flex cursor-pointer items-center gap-[16px] p-[12px_18px]',
{
'bg-[#F1F1F1]': selectFolderId === favoriteId,
'bg-white': selectFolderId !== favoriteId,
},
)}
>
<div ref={ref} className="hidden mo:block">
<div className="mx-auto my-[17px] h-[5px] w-[40px] rounded-full bg-[#d2d2d2]" />
<h1 className="border-b py-[11.5px] text-center text-[16px] font-semibold">
폴더 이동
</h1>
</div>
<div className="mo:hidden mo:border-b">
{/* 닫기 버튼 */}
<button
className="absolute right-[20px]"
type="button"
onClick={() => {
setShowMoveFolderModal(false)
}}
>
<Image
src="/image/icon/icon-close.svg"
alt="close"
width={24}
height={24}
/>
</button>

{/* 타이틀 */}
<h1 className="text-[24px] font-semibold">폴더 이동</h1>
</div>
{/* 폴더 목록 */}
<section
className={cn(
'h-[410px] w-[380px] divide-y overflow-y-scroll rounded-l-[8px] border border-[#DADADA]',
'mo:h-auto mo:w-auto mo:flex-1 mo:border-0',
)}
>
{folderList.map((item) => {
const { favoriteId, originalName, metadata } = item
const { imageUrls } = metadata.folderMetadata
return (
<div
style={{
backgroundImage:
imageUrls.length > 0 ? `url('${imageUrls[0]}')` : '',
key={favoriteId}
onClick={() => {
if (selectFolderId === favoriteId) setSelectFolderId(null)
else setSelectFolderId(favoriteId)
}}
className="h-[52px] w-[52px] rounded-[4px] bg-[#dadada] bg-contain bg-center bg-no-repeat"
/>
<div>{originalName}</div>
</div>
)
})}
</section>

{/* 하단 버튼 */}
<section
className={cn(
'flex items-center justify-between',
'mo:p-[21px_23px]',
)}
>
<button
type="button"
className="flex cursor-pointer items-center gap-[10px]"
onClick={() => setShowAddFolderModal(true)}
>
<Image
src="/image/icon/icon-add_circle.svg"
width={36}
height={36}
alt="add"
/>
<span className="text-[16px] font-semibold">새 폴더</span>
</button>
<button
type="button"
disabled={!selectFolderId}
onClick={handleMoveItems}
className="rounded-full bg-black p-[10px_27px] text-white disabled:bg-[#b1b1b1]"
className={cn(
'flex cursor-pointer items-center gap-[16px] p-[12px_18px]',
{
'bg-[#F1F1F1]': selectFolderId === favoriteId,
'bg-white': selectFolderId !== favoriteId,
},
)}
>
<div
style={{
backgroundImage:
imageUrls.length > 0 ? `url('${imageUrls[0]}')` : '',
}}
className="h-[52px] w-[52px] rounded-[4px] bg-[#dadada] bg-contain bg-center bg-no-repeat"
/>
<div>{originalName}</div>
</div>
)
})}
</section>

{/* 하단 버튼 */}
<section
className={cn(
'flex items-center justify-between',
'mo:p-[21px_23px]',
)}
>
이동하기
</button>
</section>
<button
type="button"
className="flex cursor-pointer items-center gap-[10px]"
onClick={() => setShowAddFolderModal(true)}
>
<Image
src="/image/icon/icon-add_circle.svg"
width={36}
height={36}
alt="add"
/>
<span className="text-[16px] font-semibold">새 폴더</span>
</button>
<button
type="button"
disabled={!selectFolderId}
onClick={handleMoveItems}
className="rounded-full bg-black p-[10px_27px] text-white disabled:bg-[#b1b1b1]"
>
이동하기
</button>
</section>
</div>
</div>
</Modal>
</div>
)
}
14 changes: 12 additions & 2 deletions src/app/(route)/(withLayout)/saves/_component/SaveComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
'use client'

import React, { useState } from 'react'
import { SaveFolderType } from '@/app/_types/save.type'
import React, { useEffect, useState } from 'react'
import { SaveFolderType, SavePageMode } from '@/app/_types/save.type'
import useSaveList from '@/app/_hook/api/saves/useSavesList'
import { cn } from '@/app/_utils/twMerge'
import { useSetRecoilState } from 'recoil'
import { saveModeState } from '@/app/_atoms/saveModeState'

import { SaveHeader } from './SaveHeader'
import SaveList from './SaveList'
import AddFolderModal from './AddFolderModal'

export default function SaveComponent() {
const [showAddFolderModal, setShowAddFolderModal] = useState(false)
const setMode = useSetRecoilState(saveModeState)

const { saveInfo, isLoading, isError } = useSaveList('all')

useEffect(
function resetMode() {
setMode(SavePageMode.DEFAULT)
},
[setMode],
)

if (isLoading) return <div>...loading</div>
if (isError) return null

Expand Down
17 changes: 17 additions & 0 deletions src/app/(route)/(withLayout)/saves/_utils/validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import renderToast from '@/app/_utils/toast'

export const validateSaveFolderName = (folderName: string): boolean => {
if (folderName === 'default') {
renderToast({ type: 'error', message: '생성 불가능한 이름입니다.' })
return false
}
if (folderName.length > 20 || folderName.length === 0) {
renderToast({
type: 'error',
message: '1-20자로 설정해주세요.',
})
return false
}

return true
}

0 comments on commit 4efc2d8

Please sign in to comment.