Skip to content

Commit 243c5c6

Browse files
committed
✨ feat(album): add delete album api
1 parent 4ce954d commit 243c5c6

File tree

4 files changed

+160
-36
lines changed

4 files changed

+160
-36
lines changed
Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import { useState } from 'react'
12
import { Button } from '@/shared/ui/button'
23
import { Input } from '@/shared/ui/input'
3-
import { Check, X } from 'lucide-react'
4+
import { Check, Trash2, X } from 'lucide-react'
5+
import { DeleteAlbumDialog } from './DeleteAlbumDialog'
46

57
type AlbumEditItemProps = {
68
title: string
@@ -9,6 +11,7 @@ type AlbumEditItemProps = {
911
onTitleChange: (title: string) => void
1012
onUpdate: () => void
1113
onCancel: () => void
14+
onDelete: () => void
1215
onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void
1316
}
1417

@@ -19,40 +22,61 @@ export function AlbumEditItem({
1922
onTitleChange,
2023
onUpdate,
2124
onCancel,
25+
onDelete,
2226
onKeyDown,
2327
}: AlbumEditItemProps) {
28+
const [showDeleteDialog, setShowDeleteDialog] = useState(false)
29+
2430
return (
25-
<div className="flex items-center gap-1.5 px-4 py-2.5">
26-
<Input
27-
ref={inputRef}
28-
value={title}
29-
onChange={(e) => onTitleChange(e.target.value)}
30-
onKeyDown={onKeyDown}
31-
placeholder="앨범 제목 입력..."
32-
maxLength={50}
33-
className="h-8 flex-1 border-none bg-zinc-50 text-sm focus-visible:ring-0"
34-
disabled={isPending}
31+
<>
32+
<div className="flex items-center gap-1.5 px-4 py-2.5">
33+
<Input
34+
ref={inputRef}
35+
value={title}
36+
onChange={(e) => onTitleChange(e.target.value)}
37+
onKeyDown={onKeyDown}
38+
placeholder="앨범 제목 입력..."
39+
maxLength={50}
40+
className="h-8 flex-1 border-none bg-zinc-50 text-sm focus-visible:ring-0"
41+
disabled={isPending}
42+
/>
43+
<Button
44+
type="button"
45+
variant="ghost"
46+
size="icon"
47+
className="h-8 w-8 shrink-0 border-none hover:bg-zinc-100"
48+
onClick={onCancel}
49+
disabled={isPending}
50+
>
51+
<X className="size-4 text-zinc-600" />
52+
</Button>
53+
<Button
54+
type="button"
55+
variant="ghost"
56+
size="icon"
57+
className="h-8 w-8 shrink-0 border-none hover:bg-zinc-100"
58+
onClick={onUpdate}
59+
disabled={!title.trim() || isPending}
60+
>
61+
<Check className="size-4 text-blue-500" />
62+
</Button>
63+
<Button
64+
type="button"
65+
variant="ghost"
66+
size="icon"
67+
className="h-8 w-8 shrink-0 border-none hover:bg-red-50"
68+
onClick={() => setShowDeleteDialog(true)}
69+
disabled={isPending}
70+
>
71+
<Trash2 className="size-4 text-red-500" />
72+
</Button>
73+
</div>
74+
75+
<DeleteAlbumDialog
76+
open={showDeleteDialog}
77+
onOpenChange={setShowDeleteDialog}
78+
onDelete={onDelete}
3579
/>
36-
<Button
37-
type="button"
38-
variant="ghost"
39-
size="icon"
40-
className="h-8 w-8 shrink-0 border-none hover:bg-zinc-100"
41-
onClick={onCancel}
42-
disabled={isPending}
43-
>
44-
<X className="size-4 text-zinc-600" />
45-
</Button>
46-
<Button
47-
type="button"
48-
variant="ghost"
49-
size="icon"
50-
className="h-8 w-8 shrink-0 border-none hover:bg-zinc-100"
51-
onClick={onUpdate}
52-
disabled={!title.trim() || isPending}
53-
>
54-
<Check className="size-4 text-blue-500" />
55-
</Button>
56-
</div>
80+
</>
5781
)
5882
}

src/features/create-post/ui/AlbumSelectDropdown.tsx

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import {
66
DropdownMenuTrigger,
77
} from '@/shared/ui/dropdown-menu'
88
import { Button } from '@/shared/ui/button'
9+
import { toast } from 'sonner'
910
import { useCurrentUserId } from '@/shared/auth/useCurrentUser'
1011
import { useUserAlbumsQuery } from '@/entities/album/model/hooks/useUserAlbumsQuery'
12+
import { useDeleteAlbumMutation } from '@/entities/album/model/hooks/useDeleteAlbumMutation'
1113
import {
1214
useAlbumDropdownState,
1315
NO_ALBUM_VALUE,
@@ -78,13 +80,34 @@ export function AlbumSelectDropdown({
7880
onComplete: handleEditComplete,
7981
})
8082

83+
const deleteAlbumMutation = useDeleteAlbumMutation()
84+
85+
const handleDeleteAlbum = (albumId: number) => {
86+
deleteAlbumMutation.mutate(
87+
{ albumId },
88+
{
89+
onSuccess: () => {
90+
toast.success('앨범이 삭제되었습니다.')
91+
handleEditCancel()
92+
if (selectedAlbumId === albumId) {
93+
onSelect(-1)
94+
}
95+
},
96+
onError: () => {
97+
toast.error('앨범 삭제에 실패했습니다.')
98+
},
99+
}
100+
)
101+
}
102+
81103
const handleOpenChange = (open: boolean) => {
82104
if (
83105
!open &&
84106
(state.isAddingAlbum ||
85107
state.editingAlbumId !== null ||
86108
createAlbumMutation.isPending ||
87-
updateAlbumTitleMutation.isPending)
109+
updateAlbumTitleMutation.isPending ||
110+
deleteAlbumMutation.isPending)
88111
) {
89112
return
90113
}
@@ -117,7 +140,7 @@ export function AlbumSelectDropdown({
117140
</DropdownMenuTrigger>
118141
<DropdownMenuContent
119142
align="start"
120-
className="z-[130] w-80 border-none bg-white p-0 shadow-[0_2px_8px_rgba(0,0,0,0.15)]"
143+
className="z-60 w-80 border-none bg-white p-0 shadow-[0_2px_8px_rgba(0,0,0,0.15)]"
121144
>
122145
<DropdownMenuRadioGroup
123146
value={state.value}
@@ -147,10 +170,14 @@ export function AlbumSelectDropdown({
147170
key={album.albumId}
148171
title={state.editingAlbumTitle}
149172
inputRef={editInputRef}
150-
isPending={updateAlbumTitleMutation.isPending}
173+
isPending={
174+
updateAlbumTitleMutation.isPending ||
175+
deleteAlbumMutation.isPending
176+
}
151177
onTitleChange={handleEditTitleChange}
152178
onUpdate={handleUpdateAlbum}
153179
onCancel={handleEditCancel}
180+
onDelete={() => handleDeleteAlbum(album.albumId)}
154181
onKeyDown={(e) => handleEditKeyDown(e, handleEditCancel)}
155182
/>
156183
) : (
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import {
2+
Dialog,
3+
DialogContent,
4+
DialogDescription,
5+
DialogTitle,
6+
} from '@/shared/ui/dialog'
7+
8+
type DeleteAlbumDialogProps = {
9+
open: boolean
10+
onOpenChange: (open: boolean) => void
11+
onDelete: () => void
12+
}
13+
14+
export function DeleteAlbumDialog({
15+
open,
16+
onOpenChange,
17+
onDelete,
18+
}: DeleteAlbumDialogProps) {
19+
return (
20+
<Dialog open={open} onOpenChange={onOpenChange}>
21+
<DialogContent
22+
showCloseButton={false}
23+
overlayClassName="z-70"
24+
className="z-70 w-[min(92vw,520px)] gap-0 overflow-hidden rounded-3xl bg-white p-0"
25+
>
26+
<div className="px-8 pt-10 pb-8 text-center">
27+
<DialogTitle className="text-2xl leading-tight font-semibold">
28+
앨범을 삭제하시겠어요?
29+
</DialogTitle>
30+
<DialogDescription className="mt-2 text-sm text-zinc-500">
31+
앨범에 포함된 게시글은 삭제되지 않으며, '앨범 없음' 상태로
32+
변경됩니다.
33+
</DialogDescription>
34+
</div>
35+
36+
<div className="h-px w-full bg-zinc-200" />
37+
38+
<button
39+
type="button"
40+
className="w-full px-6 py-5 text-center text-base font-semibold text-red-500 hover:bg-zinc-50"
41+
onClick={onDelete}
42+
>
43+
삭제
44+
</button>
45+
46+
<div className="h-px w-full bg-zinc-200" />
47+
48+
<button
49+
type="button"
50+
className="w-full px-6 py-5 text-center text-base font-medium hover:bg-zinc-50"
51+
onClick={() => onOpenChange(false)}
52+
>
53+
취소
54+
</button>
55+
</DialogContent>
56+
</Dialog>
57+
)
58+
}

src/routes/_app/$userId/albums.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,24 @@ function RouteComponent() {
7676
/>
7777
))}
7878
</div>
79+
) : !selectedAlbum ? (
80+
<div className="space-y-4">
81+
<div className="flex w-full items-center justify-end">
82+
<button
83+
type="button"
84+
onClick={handleBackToList}
85+
className="text-sm font-medium text-blue-500 hover:underline"
86+
>
87+
← 앨범 목록으로
88+
</button>
89+
</div>
90+
<div className="flex h-96 flex-col items-center justify-center gap-2 text-sm text-gray-500">
91+
<span>해당 앨범은 삭제되었습니다.</span>
92+
</div>
93+
</div>
7994
) : (
8095
<div className="space-y-4">
81-
{isAlbumLoading || !albumDetail || !selectedAlbum ? (
96+
{isAlbumLoading || !albumDetail ? (
8297
<div className="flex justify-center text-sm text-gray-500">
8398
앨범을 불러오는 중입니다...
8499
</div>

0 commit comments

Comments
 (0)