1-
1+ import { Trash2 } from 'lucide-react' ;
22import React , { useState } from 'react' ;
33import { useToast } from '@/hooks/use-toast' ;
4+ import { OperationObject } from '@/types/types' ;
45import { Button } from '@/components/ui/button' ;
5- import { useAuth } from '@/contexts/AuthContext' ;
6- import { Textarea } from '@/components/ui/textarea' ;
76import { formatDate } from '@/utils/schemaUtils' ;
8- import { Trash2 } from 'lucide-react ' ;
9- import { OperationObject } from '@/types/types ' ;
10- import { Avatar , AvatarFallback , AvatarImage } from '@/components/ui/avatar ' ;
7+ import { Textarea } from '@/components/ui/textarea ' ;
8+ import { useProject } from '@/hooks/api/useProject ' ;
9+ import { usePermissions } from '@/hooks/usePermissions ' ;
1110import { useCreateNote , useDeleteNote } from '@/hooks/api/useNotes' ;
11+ import { Avatar , AvatarFallback , AvatarImage } from '@/components/ui/avatar' ;
12+
13+ import {
14+ AlertDialog ,
15+ AlertDialogTitle ,
16+ AlertDialogAction ,
17+ AlertDialogCancel ,
18+ AlertDialogHeader ,
19+ AlertDialogFooter ,
20+ AlertDialogContent ,
21+ AlertDialogTrigger ,
22+ AlertDialogDescription ,
23+ } from '@/components/ui/alert-dialog' ;
1224
1325interface NotesSectionProps {
1426 projectId : string ;
@@ -18,11 +30,13 @@ interface NotesSectionProps {
1830}
1931
2032const NotesSection : React . FC < NotesSectionProps > = ( { projectId, path, method, operation } ) => {
21- const { user } = useAuth ( ) ;
33+ const { user, isProjectOwner } = usePermissions ( ) ;
34+ const { data : project } = useProject ( projectId ) ;
2235 const { toast } = useToast ( ) ;
23-
36+
2437 const [ newNoteContent , setNewNoteContent ] = useState ( '' ) ;
25-
38+ const [ noteToDelete , setNoteToDelete ] = useState < number | null > ( null ) ;
39+
2640 const createNoteMutation = useCreateNote ( ) ;
2741 const deleteNoteMutation = useDeleteNote ( ) ;
2842
@@ -52,7 +66,8 @@ const NotesSection: React.FC<NotesSectionProps> = ({ projectId, path, method, op
5266 } ) ;
5367
5468 setNewNoteContent ( '' ) ;
55- } catch ( error ) {
69+ }
70+ catch ( error ) {
5671 toast ( {
5772 title : 'Error' ,
5873 description : error instanceof Error ? error . message : 'Failed to add note' ,
@@ -74,7 +89,8 @@ const NotesSection: React.FC<NotesSectionProps> = ({ projectId, path, method, op
7489 title : 'Note Deleted' ,
7590 description : 'The note has been deleted successfully' ,
7691 } ) ;
77- } catch ( error ) {
92+ }
93+ catch ( error ) {
7894 toast ( {
7995 title : 'Error' ,
8096 description : error instanceof Error ? error . message : 'Failed to delete note' ,
@@ -83,6 +99,31 @@ const NotesSection: React.FC<NotesSectionProps> = ({ projectId, path, method, op
8399 }
84100 } ;
85101
102+ const confirmDeleteNote = async ( ) => {
103+ if ( noteToDelete === null ) return ;
104+
105+ try {
106+ await deleteNoteMutation . mutateAsync ( {
107+ projectId,
108+ method,
109+ path,
110+ noteIndex : noteToDelete ,
111+ } ) ;
112+
113+ toast ( { title : 'Note Deleted' , description : 'The note has been deleted successfully.' } ) ;
114+ }
115+ catch ( error ) {
116+ toast ( {
117+ title : 'Error' ,
118+ description : error instanceof Error ? error . message : 'Failed to delete note' ,
119+ variant : 'destructive' ,
120+ } ) ;
121+ }
122+ finally {
123+ setNoteToDelete ( null ) ;
124+ }
125+ } ;
126+
86127 return (
87128 < div className = "space-y-4" >
88129 { notes . length === 0 ? (
@@ -96,6 +137,7 @@ const NotesSection: React.FC<NotesSectionProps> = ({ projectId, path, method, op
96137 < p className = "whitespace-pre-line text-sm" style = { { unicodeBidi : 'plaintext' } } >
97138 { note . content }
98139 </ p >
140+
99141 < div className = "flex items-center space-x-2 mt-3 text-xs text-muted-foreground" >
100142 < Avatar className = "h-5 w-5" >
101143 < AvatarImage
@@ -104,45 +146,66 @@ const NotesSection: React.FC<NotesSectionProps> = ({ projectId, path, method, op
104146 />
105147 < AvatarFallback > { ( note . createdBy || 'U' ) . substring ( 0 , 2 ) . toUpperCase ( ) } </ AvatarFallback >
106148 </ Avatar >
149+
107150 < span >
108151 { note . createdBy || 'Unknown' } • { formatDate ( note . createdAt ) || 'Unknown date' }
109152 </ span >
110153 </ div >
111154 </ div >
112- { user ?. accessList ?. delete && (
113- < Button
114- variant = "ghost"
115- size = "sm"
116- onClick = { ( ) => handleDeleteNote ( index ) }
117- disabled = { deleteNoteMutation . isPending }
118- className = "text-red-500 hover:text-red-700 hover:bg-red-50"
119- >
120- < Trash2 className = "h-4 w-4" />
121- </ Button >
155+
156+ { ( isProjectOwner ( project ) || user ?. username === note . createdBy ) && (
157+ < AlertDialog >
158+ < AlertDialogTrigger asChild >
159+ < Button
160+ variant = "ghost"
161+ size = "sm"
162+ onClick = { ( ) => setNoteToDelete ( index ) }
163+ disabled = { deleteNoteMutation . isPending }
164+ className = "text-red-500 hover:text-red-700 hover:bg-red-50"
165+ >
166+ < Trash2 className = "h-4 w-4" />
167+ </ Button >
168+ </ AlertDialogTrigger >
169+
170+ < AlertDialogContent className = "max-w-3xl" >
171+ < AlertDialogHeader >
172+ < AlertDialogTitle > Are you sure?</ AlertDialogTitle >
173+
174+ < AlertDialogDescription className = "py-1 leading-6" >
175+ This action cannot be undone. This will permanently delete this note.
176+ </ AlertDialogDescription >
177+ </ AlertDialogHeader >
178+
179+ < AlertDialogFooter >
180+ < AlertDialogCancel onClick = { ( ) => setNoteToDelete ( null ) } > Cancel</ AlertDialogCancel >
181+ < AlertDialogAction
182+ onClick = { confirmDeleteNote }
183+ className = "bg-destructive hover:bg-destructive/90 text-white"
184+ >
185+ Delete
186+ </ AlertDialogAction >
187+ </ AlertDialogFooter >
188+ </ AlertDialogContent >
189+ </ AlertDialog >
122190 ) }
123191 </ div >
124192 </ div >
125193 ) ) }
126194 </ div >
127195 ) }
128196
129- { user ?. accessList ?. write && (
130- < div className = "border-t pt-4 space-y-3" >
131- < Textarea
132- value = { newNoteContent }
133- onChange = { e => setNewNoteContent ( e . target . value ) }
134- placeholder = "Add a note about this endpoint..."
135- className = "min-h-[100px]"
136- />
137- < Button
138- onClick = { handleAddNote }
139- disabled = { createNoteMutation . isPending || ! newNoteContent . trim ( ) }
140- className = "w-full"
141- >
142- { createNoteMutation . isPending ? 'Adding Note...' : 'Add Note' }
143- </ Button >
144- </ div >
145- ) }
197+ < div className = "border-t pt-4 space-y-3" >
198+ < Textarea
199+ value = { newNoteContent }
200+ onChange = { e => setNewNoteContent ( e . target . value ) }
201+ placeholder = "Add a note about this endpoint..."
202+ className = "min-h-[100px]"
203+ />
204+
205+ < Button onClick = { handleAddNote } disabled = { createNoteMutation . isPending || ! newNoteContent . trim ( ) } className = "w-full" >
206+ { createNoteMutation . isPending ? 'Adding Note...' : 'Add Note' }
207+ </ Button >
208+ </ div >
146209 </ div >
147210 ) ;
148211} ;
0 commit comments