diff --git a/src/components/features/BookmarkList/BookmarkList.tsx b/src/components/features/BookmarkList/BookmarkList.tsx index 4ad4579..d6c38fc 100644 --- a/src/components/features/BookmarkList/BookmarkList.tsx +++ b/src/components/features/BookmarkList/BookmarkList.tsx @@ -9,16 +9,22 @@ import { Modal } from '@/components/common/Modal'; import { Body1, Body2, Body3 } from '@/components/common/Typography'; import { findyIconNames } from '@/constants/findyIcons'; import { useBookMarkList } from '@/hooks/api/bookmarks/useBookMarkList'; +import { useDeleteBookmark } from '@/hooks/api/bookmarks/useDeleteBookmark'; import { useNewBookMark } from '@/hooks/api/bookmarks/useNewBookMark'; 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 [bookmarkName, setBookmarkName] = useState(''); const { token } = useAuth(); const { data } = useBookMarkList(token); + const deleteBookmarkMutation = useDeleteBookmark(); const newBookmarkMutation = useNewBookMark(token); const handleAddBookmark = () => { @@ -35,8 +41,29 @@ export const BookmarkList = ({ onNext }: Props) => { }; const handleBookmarkClick = (bookmarkId: number) => { + if (isEditing) { + return setSelectedId((prev) => (prev === bookmarkId ? 0 : bookmarkId)); + } onNext(bookmarkId); }; + + const handleDelete = () => { + if (selectedId !== 0) { + 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 ( <> @@ -80,11 +107,46 @@ export const BookmarkList = ({ onNext }: Props) => { + {isEditing && ( + + )} {index < data.data.length - 1 &&
} ))} +
+ {isEditing ? ( + <> + + + + ) : ( + + )} +
+ +
diff --git a/src/components/features/DeleteModal/Delete.tsx b/src/components/features/DeleteModal/Delete.tsx new file mode 100644 index 0000000..e657e65 --- /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/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; +}; 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'; 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}`, },