11'use client' ;
22
33import { useComments , useCommentWithAttachments } from '@/hooks/use-comments-api' ;
4- import { authClient } from '@/utils/auth-client' ;
54import { Button } from '@comp/ui/button' ;
5+ import {
6+ Dialog ,
7+ DialogContent ,
8+ DialogDescription ,
9+ DialogFooter ,
10+ DialogHeader ,
11+ DialogTitle ,
12+ } from '@comp/ui/dialog' ;
613import { Textarea } from '@comp/ui/textarea' ;
714import type { CommentEntityType } from '@db' ;
8- import { FileIcon , Loader2 , Paperclip , X } from 'lucide-react' ;
9- import { useParams } from 'next/navigation' ;
15+ import { Camera , FileIcon , Loader2 , Paperclip , X } from 'lucide-react' ;
1016import type React from 'react' ;
11- import { useCallback , useEffect , useRef , useState } from 'react' ;
17+ import { useCallback , useRef , useState } from 'react' ;
1218import { toast } from 'sonner' ;
1319
1420interface CommentFormProps {
1521 entityId : string ;
1622 entityType : CommentEntityType ;
1723}
1824
19- // Removed PendingAttachment interface - using File objects directly with API hooks
20-
2125export function CommentForm ( { entityId, entityType } : CommentFormProps ) {
22- const session = authClient . useSession ( ) ;
23- const params = useParams ( ) ;
2426 const [ newComment , setNewComment ] = useState ( '' ) ;
2527 const [ pendingFiles , setPendingFiles ] = useState < File [ ] > ( [ ] ) ;
2628 const [ isSubmitting , setIsSubmitting ] = useState ( false ) ;
2729 const fileInputRef = useRef < HTMLInputElement > ( null ) ;
28- const [ hasMounted , setHasMounted ] = useState ( false ) ;
30+ const [ showReminderDialog , setShowReminderDialog ] = useState ( false ) ;
31+ const [ filesToAdd , setFilesToAdd ] = useState < File [ ] > ( [ ] ) ;
2932
3033 // Use SWR hooks for generic comments
3134 const { mutate : refreshComments } = useComments ( entityId , entityType ) ;
3235 const { createCommentWithFiles } = useCommentWithAttachments ( ) ;
3336
34- useEffect ( ( ) => {
35- setHasMounted ( true ) ;
36- } , [ ] ) ;
37-
3837 const triggerFileInput = ( ) => {
3938 fileInputRef . current ?. click ( ) ;
4039 } ;
@@ -52,42 +51,38 @@ export function CommentForm({ entityId, entityType }: CommentFormProps) {
5251 for ( const file of newFiles ) {
5352 if ( file . size > MAX_FILE_SIZE_BYTES ) {
5453 toast . error ( `File "${ file . name } " exceeds the ${ MAX_FILE_SIZE_MB } MB limit.` ) ;
54+ if ( fileInputRef . current ) fileInputRef . current . value = '' ;
5555 return ;
5656 }
5757 }
5858
59- // Add files to pending list
60- setPendingFiles ( ( prev ) => [ ...prev , ...newFiles ] ) ;
59+ setFilesToAdd ( newFiles ) ;
60+ setShowReminderDialog ( true ) ;
61+ } , [ ] ) ;
62+
63+ const handleReminderConfirm = useCallback ( ( ) => {
64+ setShowReminderDialog ( false ) ;
65+ if ( filesToAdd . length > 0 ) {
66+ setPendingFiles ( ( prev ) => [ ...prev , ...filesToAdd ] ) ;
67+ filesToAdd . forEach ( ( file ) => {
68+ toast . success ( `File "${ file . name } " ready for attachment.` ) ;
69+ } ) ;
70+ setFilesToAdd ( [ ] ) ;
71+ }
6172 if ( fileInputRef . current ) fileInputRef . current . value = '' ;
73+ } , [ filesToAdd ] ) ;
6274
63- newFiles . forEach ( ( file ) => {
64- toast . success ( `File "${ file . name } " ready for attachment.` ) ;
65- } ) ;
75+ const handleReminderClose = useCallback ( ( ) => {
76+ setShowReminderDialog ( false ) ;
77+ setFilesToAdd ( [ ] ) ;
78+ if ( fileInputRef . current ) fileInputRef . current . value = '' ;
6679 } , [ ] ) ;
6780
6881 const handleRemovePendingFile = ( fileIndexToRemove : number ) => {
6982 setPendingFiles ( ( prev ) => prev . filter ( ( _ , index ) => index !== fileIndexToRemove ) ) ;
7083 toast . info ( 'File removed from comment draft.' ) ;
7184 } ;
7285
73- const handlePendingFileClick = ( fileIndex : number ) => {
74- const file = pendingFiles [ fileIndex ] ;
75- if ( ! file ) {
76- console . error ( 'Could not find pending file for index:' , fileIndex ) ;
77- toast . error ( 'Could not find file data.' ) ;
78- return ;
79- }
80-
81- // Create object URL for preview
82- const url = URL . createObjectURL ( file ) ;
83-
84- // Open in new tab
85- window . open ( url , '_blank' , 'noopener,noreferrer' ) ;
86-
87- // Clean up the object URL after a short delay
88- setTimeout ( ( ) => URL . revokeObjectURL ( url ) , 100 ) ;
89- } ;
90-
9186 const handleCommentSubmit = async ( ) => {
9287 if ( ! newComment . trim ( ) && pendingFiles . length === 0 ) return ;
9388
@@ -115,7 +110,6 @@ export function CommentForm({ entityId, entityType }: CommentFormProps) {
115110
116111 // Always show the actual form - no loading gate
117112 // Users can start typing immediately, authentication is checked on submit
118-
119113 const handleKeyDown = ( event : React . KeyboardEvent < HTMLTextAreaElement > ) => {
120114 if (
121115 ( event . metaKey || event . ctrlKey ) &&
@@ -167,7 +161,7 @@ export function CommentForm({ entityId, entityType }: CommentFormProps) {
167161 < button
168162 onClick = { ( ) => handleRemovePendingFile ( index ) }
169163 disabled = { isSubmitting }
170- className = "text-muted-foreground hover:text-destructive transition-colors flex- shrink-0"
164+ className = "text-muted-foreground hover:text-destructive transition-colors shrink-0"
171165 aria-label = { `Remove ${ file . name } ` }
172166 >
173167 < X className = "h-3 w-3" />
@@ -202,6 +196,32 @@ export function CommentForm({ entityId, entityType }: CommentFormProps) {
202196 </ div >
203197 </ div >
204198 </ div >
199+
200+ < Dialog open = { showReminderDialog } onOpenChange = { ( open ) => ! open && handleReminderClose ( ) } >
201+ < DialogContent className = "sm:max-w-[425px]" >
202+ < DialogHeader >
203+ < div className = "flex items-center gap-3" >
204+ < div className = "rounded-full bg-primary/10 p-2" >
205+ < Camera className = "h-5 w-5 text-primary" />
206+ </ div >
207+ < DialogTitle > Screenshot Requirements</ DialogTitle >
208+ </ div >
209+ < DialogDescription className = "pt-2" >
210+ Ensure your organisation name is clearly visible within the screenshot.
211+ </ DialogDescription >
212+ </ DialogHeader >
213+ < p className = "text-sm text-muted-foreground" >
214+ Auditors require this to verify the source of the data; without it, evidence may be
215+ rejected.
216+ </ p >
217+ < DialogFooter >
218+ < Button variant = "outline" onClick = { handleReminderClose } >
219+ Cancel
220+ </ Button >
221+ < Button onClick = { handleReminderConfirm } > Continue</ Button >
222+ </ DialogFooter >
223+ </ DialogContent >
224+ </ Dialog >
205225 </ div >
206226 ) ;
207227}
0 commit comments