From 7de4f459ea2fb1b3500ff3da8d7e44f5096ed7e5 Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Sun, 24 Nov 2024 02:18:40 +0900 Subject: [PATCH 1/5] feat: implement Delete modal component --- .../features/DeleteModal/Delete.tsx | 23 +++++++++++++++++++ src/components/features/DeleteModal/index.tsx | 1 + 2 files changed, 24 insertions(+) create mode 100644 src/components/features/DeleteModal/Delete.tsx create mode 100644 src/components/features/DeleteModal/index.tsx diff --git a/src/components/features/DeleteModal/Delete.tsx b/src/components/features/DeleteModal/Delete.tsx new file mode 100644 index 0000000..4036d40 --- /dev/null +++ b/src/components/features/DeleteModal/Delete.tsx @@ -0,0 +1,23 @@ +import { Button } from '@/components/common/Button'; +import { Modal } from '@/components/common/Modal'; +import { Body1 } from '@/components/common/Typography'; + +import { Props } from './Delete.types'; + +export const Delete = ({ item, onClickDelete, isOpen, setIsOpen }: Props) => { + return ( + setIsOpen(false)}> +
+ {`${item}를 삭제하시겠습니까?`} +
+ + +
+
+
+ ); +}; diff --git a/src/components/features/DeleteModal/index.tsx b/src/components/features/DeleteModal/index.tsx new file mode 100644 index 0000000..5655900 --- /dev/null +++ b/src/components/features/DeleteModal/index.tsx @@ -0,0 +1 @@ +export { Delete } from './Delete'; From 98d3baf1389f4c2bf2ef70d1a1727370e1c609c2 Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Sun, 24 Nov 2024 02:19:06 +0900 Subject: [PATCH 2/5] feat : define type for Delete modal component --- .../features/DeleteModal/Delete.types.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/components/features/DeleteModal/Delete.types.ts diff --git a/src/components/features/DeleteModal/Delete.types.ts b/src/components/features/DeleteModal/Delete.types.ts new file mode 100644 index 0000000..2178a8a --- /dev/null +++ b/src/components/features/DeleteModal/Delete.types.ts @@ -0,0 +1,23 @@ +export type Props = { + /** + * The name or label of the item to be deleted. + * @type {string} + */ + item: string; + /** + * A callback function triggered when the delete action is confirmed. + * @type {() => void} + */ + onClickDelete: () => void; + /** + * Indicates whether the component is open or visible. + * @type {boolean} + */ + isOpen: boolean; + + /** + * update the open/close state of the component. + * @param {boolean} + */ + setIsOpen: (value: boolean) => void; +}; From 22e0d69cc2ff2330b01e7eac6bf854b327219623 Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Sun, 24 Nov 2024 02:19:41 +0900 Subject: [PATCH 3/5] feat: add useDeleteBookmark hook --- src/hooks/api/bookmarks/useDeleteBookmark.ts | 30 +++++++++++++++++++ src/hooks/api/bookmarks/useYoutubeBookmark.ts | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/hooks/api/bookmarks/useDeleteBookmark.ts diff --git a/src/hooks/api/bookmarks/useDeleteBookmark.ts b/src/hooks/api/bookmarks/useDeleteBookmark.ts new file mode 100644 index 0000000..143f90e --- /dev/null +++ b/src/hooks/api/bookmarks/useDeleteBookmark.ts @@ -0,0 +1,30 @@ +import { UseMutationResult, useMutation, useQueryClient } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; + +import { del } from '@/lib/axios'; + +type DeleteBookmarkParams = { + token: string; + bookmarkId: number; +}; + +export const useDeleteBookmark = (): UseMutationResult< + unknown, + AxiosError, + DeleteBookmarkParams +> => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ token, bookmarkId }: DeleteBookmarkParams) => { + return await del(`api/bookmarks/${bookmarkId}`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['bookmarklist'] }); + }, + }); +}; diff --git a/src/hooks/api/bookmarks/useYoutubeBookmark.ts b/src/hooks/api/bookmarks/useYoutubeBookmark.ts index 854c89b..0610001 100644 --- a/src/hooks/api/bookmarks/useYoutubeBookmark.ts +++ b/src/hooks/api/bookmarks/useYoutubeBookmark.ts @@ -7,7 +7,7 @@ import { YoutubeResponse } from '../link/useYoutubePlace'; export const useYoutubeBookmark = (token: string) => { return useMutation({ mutationFn: (youtubeData: YoutubeResponse) => - post(`/api/bookmarks/youtube`, youtubeData, { + post(`api/bookmarks/youtube`, youtubeData, { headers: { Authorization: `Bearer ${token}`, }, From b676de3a33ce3e610267a8c92b785c95611644f0 Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Sun, 24 Nov 2024 02:20:18 +0900 Subject: [PATCH 4/5] feat: add delete hook and modal to BookmarkList --- .../features/BookmarkList/BookmarkList.tsx | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/src/components/features/BookmarkList/BookmarkList.tsx b/src/components/features/BookmarkList/BookmarkList.tsx index 84aa431..f3d67eb 100644 --- a/src/components/features/BookmarkList/BookmarkList.tsx +++ b/src/components/features/BookmarkList/BookmarkList.tsx @@ -1,19 +1,51 @@ +import { useState } from 'react'; + +import { Button } from '@/components/common/Button'; import { Icon } from '@/components/common/Icon'; import { ListCard } from '@/components/common/ListCard'; import { Body1, Body2, Body3 } from '@/components/common/Typography'; import { useBookMarkList } from '@/hooks/api/bookmarks/useBookMarkList'; +import { useDeleteBookmark } from '@/hooks/api/bookmarks/useDeleteBookmark'; import { useAuth } from '@/hooks/auth/useAuth'; +import { Delete } from '../DeleteModal'; + type Props = { onNext: (bookmarkId: number) => void }; export const BookmarkList = ({ onNext }: Props) => { + const [isOpen, setIsOpen] = useState(false); + const [isEditing, setIsEditing] = useState(false); + const [selectedId, setSelectedId] = useState(0); + const { token } = useAuth(); const { data } = useBookMarkList(token); + const deleteBookmarkMutation = useDeleteBookmark(); const handleBookmarkClick = (bookmarkId: number) => { + if (isEditing) { + return setSelectedId((prev) => (prev === bookmarkId ? 0 : bookmarkId)); + } onNext(bookmarkId); }; + + const handleDelete = () => { + if (selectedId !== null) { + deleteBookmarkMutation.mutate({ token, bookmarkId: selectedId }); + setSelectedId(0); + setIsOpen(false); + setIsEditing(false); + } + }; + + const handleCancelEditing = () => { + setIsEditing(false); + setSelectedId(0); + }; + + const selectedItemName = + data?.data.find((item) => item.bookmarkId === selectedId)?.name || '북마크'; + return ( -
+ <> 나의 핀디 리스트 @@ -43,11 +75,45 @@ export const BookmarkList = ({ onNext }: Props) => {
+ {isEditing && ( + + )} {index < data.data.length - 1 &&
} ))} - +
+ {isEditing ? ( + <> + + + + ) : ( + + )} +
+ + ); }; From 64c55e3a379eb1dac44ff7f053e0b82caa533f6c Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Sun, 24 Nov 2024 02:42:31 +0900 Subject: [PATCH 5/5] fix: update setIsOpen parameter to handle modal state --- src/components/features/BookmarkList/BookmarkList.tsx | 2 +- src/components/features/DeleteModal/Delete.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/features/BookmarkList/BookmarkList.tsx b/src/components/features/BookmarkList/BookmarkList.tsx index f3d67eb..52a7c3e 100644 --- a/src/components/features/BookmarkList/BookmarkList.tsx +++ b/src/components/features/BookmarkList/BookmarkList.tsx @@ -28,7 +28,7 @@ export const BookmarkList = ({ onNext }: Props) => { }; const handleDelete = () => { - if (selectedId !== null) { + if (selectedId !== 0) { deleteBookmarkMutation.mutate({ token, bookmarkId: selectedId }); setSelectedId(0); setIsOpen(false); diff --git a/src/components/features/DeleteModal/Delete.tsx b/src/components/features/DeleteModal/Delete.tsx index 4036d40..e657e65 100644 --- a/src/components/features/DeleteModal/Delete.tsx +++ b/src/components/features/DeleteModal/Delete.tsx @@ -10,7 +10,7 @@ export const Delete = ({ item, onClickDelete, isOpen, setIsOpen }: Props) => {
{`${item}를 삭제하시겠습니까?`}
-