-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat: 콜렉션 상세페이지 및 폴더 이름 변경, 삭제 기능 개발 #260
Merged
ParkSohyunee
merged 20 commits into
8-Sprinters:dev
from
ParkSohyunee:feature/collection-detail-page
Oct 27, 2024
Merged
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
696a116
Rename: 콜렉션 상세페이지 Ver2.0 파일 구분을 위해 이름 수정
ParkSohyunee 81e1dde
Feat: 콜렉션 폴더 Link 태그로 변경 및 href 설정
ParkSohyunee c4e1774
Feat: 콜렉션 리스트조회 API 연동 및 폴더 상세페이지 레이아웃 구현
ParkSohyunee 2d5defa
Design: 콜렉션 상세페이지 퍼블리싱
ParkSohyunee 0c9a5a4
Design: 배경이미지 유무에 따른 스타일 variants 적용
ParkSohyunee 4e93d94
Feat: Header 수정버튼 클릭시 공통 BottomSheet 나오도록 적용
ParkSohyunee a036988
Refactor: BottomSheet 컴포넌트 Compound Component 패턴으로 리팩토링
ParkSohyunee ba2e859
Fix: unauthorized 에러일때 로그인 모달 대신 토스트 메세지로 변경
ParkSohyunee 293dd41
Feat: 폴더 이름 수정하기 기능 구현
ParkSohyunee f34e116
Feat: BottomSheet Button 이름을 children으로 받도록 수정 및 삭제일때 폰트색상 수정
ParkSohyunee 506f30b
Feat: 폴더 삭제하기 기능 구현
ParkSohyunee c70188f
Feat: 폴더페이지에서 뒤로가기 클릭시 경로 수정
ParkSohyunee 1c90379
Fix: 폴더 콜렉션 리스트 배경이미지 url 값 수정
ParkSohyunee fcc6738
Design: 콜렉션 페이지 스타일 수정
ParkSohyunee 0abbd6e
Chore: 빌드 에러 수정 (기존 폴더 rename으로 인한 import 경로 오류)
ParkSohyunee 9a135bd
Design: (코드리뷰 반영) 콜렉션 리스트 스타일 수정
ParkSohyunee 50d4ea7
Refactor: 콜렉션 심플 리스트 공통 컴포넌트로 리팩토링
ParkSohyunee 5e23041
Rename: BottomSheet 컴포넌트 Ver3.0 공통컴포넌트로 분리
ParkSohyunee d8d77fc
Refactor: BottomSheet 컴포넌트에 Content 부분 추가
ParkSohyunee 49abc5e
Refactor: BottomSheet 컴포넌트 버튼 children prop 타입 수정
ParkSohyunee File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
import { CollectionType } from '@/lib/types/listType'; | ||
|
||
interface GetCollectionType { | ||
folderId: string; | ||
cursorId?: number; | ||
} | ||
|
||
interface ResponseType { | ||
collectionLists: CollectionType[]; | ||
cursorId: number; | ||
hasNext: boolean; | ||
} | ||
|
||
async function getCollection({ folderId, cursorId }: GetCollectionType) { | ||
const params = new URLSearchParams({ | ||
size: '8', | ||
}); | ||
|
||
if (cursorId) { | ||
params.append('cursorId', cursorId.toString()); | ||
} | ||
|
||
const response = await axiosInstance.get<ResponseType>(`/folder/${folderId}/collections?${params.toString()}`); | ||
|
||
return response.data; | ||
} | ||
|
||
export default getCollection; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
|
||
const deleteFolder = async (folderId: string) => { | ||
await axiosInstance.delete(`/folders/${folderId}`); | ||
}; | ||
|
||
export default deleteFolder; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
|
||
interface CollectionFolderProps { | ||
folderId: string; | ||
folderName: string; | ||
} | ||
|
||
const updateCollectionFolder = async ({ folderId, folderName }: CollectionFolderProps) => { | ||
await axiosInstance.put(`/folders/${folderId}`, { folderName }); | ||
}; | ||
|
||
export default updateCollectionFolder; |
13 changes: 13 additions & 0 deletions
13
src/app/collection/[folderId]/_components/Collections.css.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { style } from '@vanilla-extract/css'; | ||
import { vars } from '@/styles/theme.css'; | ||
|
||
export const container = style({ | ||
padding: '2.4rem 1.6rem', | ||
display: 'grid', | ||
gridTemplateColumns: '1fr 1fr', | ||
gridTemplateRows: 'max-content', | ||
gridGap: 12, | ||
alignContent: 'flex-start', | ||
justifyItems: 'center', | ||
backgroundColor: vars.color.bggray, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
'use client'; | ||
|
||
import { useQuery } from '@tanstack/react-query'; | ||
|
||
import * as styles from './Collections.css'; | ||
|
||
import ShapeSimpleList from '@/components/ShapeSimpleList/ShapeSimpleList'; | ||
import getCollection from '@/app/_api/collect/getCollection'; | ||
import { QUERY_KEYS } from '@/lib/constants/queryKeys'; | ||
|
||
interface CollectionsProps { | ||
folderId: string; | ||
} | ||
|
||
// TODO 무한스크롤 | ||
|
||
export default function Collections({ folderId }: CollectionsProps) { | ||
const { data } = useQuery({ | ||
queryKey: [QUERY_KEYS.getCollection], | ||
queryFn: () => getCollection({ folderId }), | ||
}); | ||
|
||
return ( | ||
<ul className={styles.container}> | ||
{data?.collectionLists.map(({ list, id }) => { | ||
const hasImage = !!list.representativeImageUrl; | ||
return <ShapeSimpleList list={list} hasImage={hasImage} key={id} />; | ||
})} | ||
</ul> | ||
); | ||
} |
42 changes: 42 additions & 0 deletions
42
src/app/collection/[folderId]/_components/HeaderContainer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
'use client'; | ||
|
||
import BottomSheet from '@/components/BottomSheet/BottomSheet'; | ||
import Header from '@/components/Header/Header'; | ||
|
||
import useBooleanOutput from '@/hooks/useBooleanOutput'; | ||
|
||
interface HeaderContainerProps { | ||
handleSetOnBottomSheet: () => void; | ||
handleSetOnDeleteOption: () => void; | ||
} | ||
|
||
export default function HeaderContainer({ handleSetOnBottomSheet, handleSetOnDeleteOption }: HeaderContainerProps) { | ||
const { isOn, handleSetOn, handleSetOff } = useBooleanOutput(); | ||
|
||
const bottomSheetOptionList = [ | ||
{ | ||
key: 'editFolder', | ||
title: '폴더 이름 바꾸기', // TODO locale 적용 | ||
onClick: handleSetOnBottomSheet, | ||
}, | ||
{ | ||
key: 'deleteFolder', | ||
title: '폴더 삭제하기', | ||
onClick: handleSetOnDeleteOption, | ||
}, | ||
]; | ||
|
||
const RightButton = () => { | ||
const handleClickOption = () => { | ||
handleSetOn(); | ||
}; | ||
return <button onClick={handleClickOption}>수정</button>; | ||
}; | ||
|
||
return ( | ||
<> | ||
<Header title="콜렉션" left="back" right={<RightButton />} leftClick={() => history.back()} /> | ||
{isOn && <BottomSheet onClose={handleSetOff} optionList={bottomSheetOptionList} isActive />} | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { style } from '@vanilla-extract/css'; | ||
import { vars } from '@/styles/theme.css'; | ||
import { Subtitle } from '@/styles/font.css'; | ||
|
||
export const container = style({ | ||
height: '100vh', | ||
backgroundColor: vars.color.bggray, | ||
}); | ||
|
||
// BottomSheet Input | ||
export const contentInput = style({ | ||
padding: '2rem 2.4rem', | ||
backgroundColor: '#F5F6FA', | ||
borderRadius: 18, | ||
|
||
color: vars.color.black, | ||
fontSize: '1.6rem', | ||
fontWeight: 400, | ||
}); | ||
|
||
// BottomSheet Description | ||
export const content = style([ | ||
Subtitle, | ||
{ | ||
paddingTop: 30, | ||
fontWeight: 600, | ||
color: vars.color.black, | ||
|
||
display: 'flex', | ||
flexDirection: 'column', | ||
gap: 8, | ||
alignItems: 'center', | ||
}, | ||
]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
'use client'; | ||
|
||
import { useRouter } from 'next/navigation'; | ||
import { ChangeEvent, useState } from 'react'; | ||
import { useMutation, useQueryClient } from '@tanstack/react-query'; | ||
import { isAxiosError } from 'axios'; | ||
|
||
import HeaderContainer from './_components/HeaderContainer'; | ||
import Collections from './_components/Collections'; | ||
import BottomSheet from '../_components/BottomSheet'; | ||
|
||
import * as styles from './page.css'; | ||
|
||
import useBooleanOutput from '@/hooks/useBooleanOutput'; | ||
import { useLanguage } from '@/store/useLanguage'; | ||
import toasting from '@/lib/utils/toasting'; | ||
import toastMessage from '@/lib/constants/toastMessage'; | ||
import { QUERY_KEYS } from '@/lib/constants/queryKeys'; | ||
import updateCollectionFolder from '@/app/_api/folder/updateFolder'; | ||
import deleteFolder from '@/app/_api/folder/deleteFolder'; | ||
|
||
interface ParamType { | ||
params: { folderId: string }; | ||
} | ||
|
||
// TODO API에 FolderName 필드 추가 요청 => input value에 보여주기 & 헤더 타이틀 | ||
export default function CollectionDetailPage({ params }: ParamType) { | ||
const folderId = params.folderId; | ||
const { isOn, handleSetOn, handleSetOff } = useBooleanOutput(); | ||
const { | ||
isOn: isDeleteOption, | ||
handleSetOn: handleSetOnDeleteOption, | ||
handleSetOff: handleSetOffDeleteOption, | ||
} = useBooleanOutput(); | ||
const queryClient = useQueryClient(); | ||
const { language } = useLanguage(); | ||
const router = useRouter(); | ||
|
||
const [value, setValue] = useState(''); | ||
|
||
// 폴더 수정하기 mutation | ||
const editFolderMutation = useMutation({ | ||
mutationFn: updateCollectionFolder, | ||
onSuccess: () => { | ||
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.getFolders] }); | ||
setValue(''); | ||
handleSetOff(); | ||
}, | ||
onError: (error) => { | ||
if (isAxiosError(error)) { | ||
const errorData = error.response?.data; | ||
if (errorData.error === 'UNAUTHORIZED') { | ||
toasting({ type: 'error', txt: toastMessage[language].requiredLogin }); | ||
return; | ||
} | ||
if (errorData.code.split('_')[0] === 'DUPLICATE') { | ||
toasting({ type: 'error', txt: toastMessage[language].duplicatedFolderName }); | ||
return; | ||
} | ||
} | ||
toasting({ type: 'error', txt: toastMessage[language].failedFolder }); | ||
}, | ||
}); | ||
|
||
// 폴더 삭제하기 mutation | ||
const deleteFolderMutation = useMutation({ | ||
mutationFn: deleteFolder, | ||
onSuccess: () => { | ||
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.getFolders] }); | ||
handleSetOffDeleteOption(); | ||
router.push('/collection'); | ||
}, | ||
onError: (error) => { | ||
if (isAxiosError(error)) { | ||
const errorData = error.response?.data; | ||
if (errorData.error === 'UNAUTHORIZED') { | ||
toasting({ type: 'error', txt: toastMessage[language].requiredLogin }); | ||
return; | ||
} | ||
} | ||
}, | ||
}); | ||
|
||
const handleChangeInput = (e: ChangeEvent<HTMLInputElement>) => { | ||
setValue(e.target.value); | ||
}; | ||
|
||
const handleEditFolder = () => { | ||
if (!value.trim()) { | ||
toasting({ type: 'warning', txt: toastMessage[language].emptyFolderName }); | ||
return; | ||
} | ||
editFolderMutation.mutate({ folderId, folderName: value }); | ||
}; | ||
|
||
const handleDeleteFolder = () => { | ||
deleteFolderMutation.mutate(folderId); | ||
}; | ||
|
||
return ( | ||
<section className={styles.container}> | ||
<HeaderContainer handleSetOnBottomSheet={handleSetOn} handleSetOnDeleteOption={handleSetOnDeleteOption} /> | ||
<Collections folderId={folderId} /> | ||
<BottomSheet isOn={isOn}> | ||
<BottomSheet.Title>폴더 이름 바꾸기</BottomSheet.Title> | ||
<input | ||
autoFocus | ||
placeholder="폴더명을 작성해 주세요" | ||
value={value} | ||
onChange={handleChangeInput} | ||
className={styles.contentInput} | ||
/> | ||
<BottomSheet.Button onClose={handleSetOff} onClick={handleEditFolder}> | ||
만들기 | ||
</BottomSheet.Button> | ||
</BottomSheet> | ||
<BottomSheet isOn={isDeleteOption}> | ||
<div className={styles.content}> | ||
<p>정말 삭제하시나요?</p> | ||
<p>폴더와 안에 있었던 리스트가 모두 삭제돼요</p> | ||
</div> | ||
<BottomSheet.Button onClose={handleSetOffDeleteOption} isDelete={true} onClick={handleDeleteFolder}> | ||
삭제 | ||
</BottomSheet.Button> | ||
</BottomSheet> | ||
</section> | ||
); | ||
} |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@seoyoung-min 서영님, ver3.0 BottomSheet 컴포넌트를 공통 컴포넌트로 만들어 두었습니다. Contents, Button 등 필요에따라 사용하시면 될 것 같습니다~!