@@ -19,6 +19,7 @@ import {
1919 useApproveSubmission ,
2020 useAuth ,
2121 useBulkApproveSubmissions ,
22+ useBulkRejectSubmissions ,
2223 useBulkTriggerWorkflow ,
2324 useRejectSubmission ,
2425 useSubmissions ,
@@ -94,6 +95,7 @@ export default function DashboardPage() {
9495 const workflowMutation = useTriggerWorkflow ( )
9596 const bulkWorkflowMutation = useBulkTriggerWorkflow ( )
9697 const bulkApproveMutation = useBulkApproveSubmissions ( )
98+ const bulkRejectMutation = useBulkRejectSubmissions ( )
9799
98100 const [ workflowUrl , setWorkflowUrl ] = React . useState < string | undefined > ( )
99101 const [ approveDialogOpen , setApproveDialogOpen ] = React . useState ( false )
@@ -102,6 +104,9 @@ export default function DashboardPage() {
102104 const [ bulkApproveDialogOpen , setBulkApproveDialogOpen ] = React . useState ( false )
103105 const [ bulkApprovingIds , setBulkApprovingIds ] = React . useState < string [ ] > ( [ ] )
104106 const [ bulkApproveAdminComment , setBulkApproveAdminComment ] = React . useState ( "" )
107+ const [ bulkRejectDialogOpen , setBulkRejectDialogOpen ] = React . useState ( false )
108+ const [ bulkRejectingIds , setBulkRejectingIds ] = React . useState < string [ ] > ( [ ] )
109+ const [ bulkRejectAdminComment , setBulkRejectAdminComment ] = React . useState ( "" )
105110 const [ rejectDialogOpen , setRejectDialogOpen ] = React . useState ( false )
106111 const [ rejectingSubmissionId , setRejectingSubmissionId ] = React . useState < string | null > ( null )
107112 const [ adminComment , setAdminComment ] = React . useState ( "" )
@@ -212,6 +217,27 @@ export default function DashboardPage() {
212217 }
213218 }
214219
220+ const handleBulkReject = ( submissionIds : string [ ] ) => {
221+ setBulkRejectingIds ( submissionIds )
222+ setBulkRejectAdminComment ( "" )
223+ setBulkRejectDialogOpen ( true )
224+ }
225+
226+ const handleBulkRejectSubmit = ( ) => {
227+ if ( bulkRejectingIds . length > 0 ) {
228+ bulkRejectMutation . mutate (
229+ { submissionIds : [ ...bulkRejectingIds ] , adminComment : bulkRejectAdminComment . trim ( ) || undefined } ,
230+ {
231+ onSuccess : ( ) => {
232+ setBulkRejectDialogOpen ( false )
233+ setBulkRejectingIds ( [ ] )
234+ setBulkRejectAdminComment ( "" )
235+ } ,
236+ } ,
237+ )
238+ }
239+ }
240+
215241 // Not authenticated
216242 if ( ! authLoading && ! isAuthenticated ) {
217243 return (
@@ -329,11 +355,13 @@ export default function DashboardPage() {
329355 onTriggerWorkflow = { handleTriggerWorkflow }
330356 onBulkTriggerWorkflow = { handleBulkTriggerWorkflow }
331357 onBulkApprove = { handleBulkApprove }
358+ onBulkReject = { handleBulkReject }
332359 isApproving = { approveMutation . isPending }
333360 isRejecting = { rejectMutation . isPending }
334361 isTriggeringWorkflow = { workflowMutation . isPending }
335362 isBulkTriggeringWorkflow = { bulkWorkflowMutation . isPending }
336363 isBulkApproving = { bulkApproveMutation . isPending }
364+ isBulkRejecting = { bulkRejectMutation . isPending }
337365 workflowUrl = { workflowUrl }
338366 hideStatusHints
339367 />
@@ -353,11 +381,13 @@ export default function DashboardPage() {
353381 onTriggerWorkflow = { handleTriggerWorkflow }
354382 onBulkTriggerWorkflow = { handleBulkTriggerWorkflow }
355383 onBulkApprove = { handleBulkApprove }
384+ onBulkReject = { handleBulkReject }
356385 isApproving = { approveMutation . isPending }
357386 isRejecting = { rejectMutation . isPending }
358387 isTriggeringWorkflow = { workflowMutation . isPending }
359388 isBulkTriggeringWorkflow = { bulkWorkflowMutation . isPending }
360389 isBulkApproving = { bulkApproveMutation . isPending }
390+ isBulkRejecting = { bulkRejectMutation . isPending }
361391 workflowUrl = { workflowUrl }
362392 hideStatusHints
363393 />
@@ -377,11 +407,13 @@ export default function DashboardPage() {
377407 onTriggerWorkflow = { handleTriggerWorkflow }
378408 onBulkTriggerWorkflow = { handleBulkTriggerWorkflow }
379409 onBulkApprove = { handleBulkApprove }
410+ onBulkReject = { handleBulkReject }
380411 isApproving = { approveMutation . isPending }
381412 isRejecting = { rejectMutation . isPending }
382413 isTriggeringWorkflow = { workflowMutation . isPending }
383414 isBulkTriggeringWorkflow = { bulkWorkflowMutation . isPending }
384415 isBulkApproving = { bulkApproveMutation . isPending }
416+ isBulkRejecting = { bulkRejectMutation . isPending }
385417 workflowUrl = { workflowUrl }
386418 />
387419 ) }
@@ -430,6 +462,20 @@ export default function DashboardPage() {
430462 onSubmit = { handleBulkApproveSubmit }
431463 isPending = { bulkApproveMutation . isPending }
432464 />
465+ < BulkRejectDialog
466+ isMobile = { isMobile }
467+ open = { bulkRejectDialogOpen }
468+ onClose = { ( ) => {
469+ setBulkRejectDialogOpen ( false )
470+ setBulkRejectingIds ( [ ] )
471+ setBulkRejectAdminComment ( "" )
472+ } }
473+ count = { bulkRejectingIds . length }
474+ comment = { bulkRejectAdminComment }
475+ onCommentChange = { setBulkRejectAdminComment }
476+ onSubmit = { handleBulkRejectSubmit }
477+ isPending = { bulkRejectMutation . isPending }
478+ />
433479 </ >
434480 )
435481 }
@@ -753,3 +799,87 @@ function BulkApproveDialog({
753799 </ Dialog >
754800 )
755801}
802+
803+ function BulkRejectDialog ( {
804+ isMobile,
805+ open,
806+ onClose,
807+ count,
808+ comment,
809+ onCommentChange,
810+ onSubmit,
811+ isPending,
812+ } : {
813+ isMobile : boolean
814+ open : boolean
815+ onClose : ( ) => void
816+ count : number
817+ comment : string
818+ onCommentChange : ( v : string ) => void
819+ onSubmit : ( ) => void
820+ isPending : boolean
821+ } ) {
822+ const label = `Reject ${ count } Submission${ count > 1 ? "s" : "" } `
823+
824+ if ( isMobile ) {
825+ return (
826+ < Drawer open = { open } onOpenChange = { ( o ) => ! o && onClose ( ) } >
827+ < DrawerContent >
828+ < DrawerHeader className = "text-left" >
829+ < DrawerTitle > { label } </ DrawerTitle >
830+ < DrawerDescription > All submitters will see the rejection reason.</ DrawerDescription >
831+ </ DrawerHeader >
832+ < div className = "px-4 pb-2 space-y-2" >
833+ < Label htmlFor = "bulk-reject-comment" > Reason</ Label >
834+ < Textarea
835+ id = "bulk-reject-comment"
836+ placeholder = "e.g. Icons don't meet quality guidelines..."
837+ value = { comment }
838+ onChange = { ( e ) => onCommentChange ( e . target . value ) }
839+ rows = { 3 }
840+ />
841+ </ div >
842+ < DrawerFooter >
843+ < Button variant = "destructive" onClick = { onSubmit } disabled = { isPending } >
844+ { isPending ? "Rejecting..." : label }
845+ </ Button >
846+ < DrawerClose asChild >
847+ < Button variant = "outline" disabled = { isPending } >
848+ Cancel
849+ </ Button >
850+ </ DrawerClose >
851+ </ DrawerFooter >
852+ </ DrawerContent >
853+ </ Drawer >
854+ )
855+ }
856+
857+ return (
858+ < Dialog open = { open } onOpenChange = { ( o ) => ! o && onClose ( ) } >
859+ < DialogContent >
860+ < DialogHeader >
861+ < DialogTitle > { label } </ DialogTitle >
862+ < DialogDescription > All submitters will see the rejection reason.</ DialogDescription >
863+ </ DialogHeader >
864+ < div className = "space-y-2 py-4" >
865+ < Label htmlFor = "bulk-reject-comment" > Reason</ Label >
866+ < Textarea
867+ id = "bulk-reject-comment"
868+ placeholder = "e.g. Icons don't meet quality guidelines..."
869+ value = { comment }
870+ onChange = { ( e ) => onCommentChange ( e . target . value ) }
871+ rows = { 3 }
872+ />
873+ </ div >
874+ < DialogFooter >
875+ < Button variant = "outline" onClick = { onClose } disabled = { isPending } >
876+ Cancel
877+ </ Button >
878+ < Button variant = "destructive" onClick = { onSubmit } disabled = { isPending } >
879+ { isPending ? "Rejecting..." : label }
880+ </ Button >
881+ </ DialogFooter >
882+ </ DialogContent >
883+ </ Dialog >
884+ )
885+ }
0 commit comments