@@ -787,11 +787,6 @@ function PublishNowButton({ notificationId }: { notificationId: string }) {
787787
788788function PublishDraftButton ( { notificationId } : { notificationId : string } ) {
789789 const [ open , setOpen ] = useState ( false ) ;
790- const fetcher = useFetcher < { success ?: boolean ; error ?: string } > ( ) ;
791-
792- useEffect ( ( ) => {
793- if ( fetcher . data ?. success ) setOpen ( false ) ;
794- } , [ fetcher . data ] ) ;
795790
796791 return (
797792 < >
@@ -803,56 +798,74 @@ function PublishDraftButton({ notificationId }: { notificationId: string }) {
803798 < DialogHeader >
804799 < DialogTitle > Publish notification</ DialogTitle >
805800 </ DialogHeader >
806- < fetcher . Form method = "post" className = "space-y-3" >
807- < input type = "hidden" name = "_action" value = "publish-draft" />
808- < input type = "hidden" name = "notificationId" value = { notificationId } />
809- < Paragraph variant = "small" className = "text-text-dimmed" >
810- Set the schedule for this notification. It becomes visible to users at the start time.
811- </ Paragraph >
812- < div className = "grid grid-cols-2 gap-3" >
813- < div >
814- < Label variant = "small" >
815- Starts at (UTC) < span className = "text-red-400" > *</ span >
816- </ Label >
817- < input
818- name = "startsAt"
819- type = "datetime-local"
820- defaultValue = { toDatetimeLocalUTC ( new Date ( ) ) }
821- className = "mt-1 block h-8 w-full rounded border border-background-bright bg-background-hover px-2 text-sm text-text-bright transition hover:border-border-bright hover:bg-secondary"
822- required
823- />
824- </ div >
825- < div >
826- < Label variant = "small" >
827- Ends at (UTC) < span className = "text-red-400" > *</ span >
828- </ Label >
829- < input
830- name = "endsAt"
831- type = "datetime-local"
832- defaultValue = { defaultEndsAt ( ) }
833- className = "mt-1 block h-8 w-full rounded border border-background-bright bg-background-hover px-2 text-sm text-text-bright transition hover:border-border-bright hover:bg-secondary"
834- required
835- />
836- </ div >
837- </ div >
838- < DialogFooter className = "items-center" >
839- { fetcher . data ?. error && (
840- < span className = "text-xs text-red-400" > { fetcher . data . error } </ span >
841- ) }
842- < Button type = "button" variant = "tertiary/medium" onClick = { ( ) => setOpen ( false ) } >
843- Cancel
844- </ Button >
845- < Button type = "submit" variant = "primary/medium" disabled = { fetcher . state !== "idle" } >
846- { fetcher . state !== "idle" ? "Publishing..." : "Publish" }
847- </ Button >
848- </ DialogFooter >
849- </ fetcher . Form >
801+ < PublishDraftForm notificationId = { notificationId } onClose = { ( ) => setOpen ( false ) } />
850802 </ DialogContent >
851803 </ Dialog >
852804 </ >
853805 ) ;
854806}
855807
808+ // Split out so the "close on success" effect calls the `onClose` prop rather than a
809+ // local state setter — the latter trips react/set-state-in-effect.
810+ function PublishDraftForm ( {
811+ notificationId,
812+ onClose,
813+ } : {
814+ notificationId : string ;
815+ onClose : ( ) => void ;
816+ } ) {
817+ const fetcher = useFetcher < { success ?: boolean ; error ?: string } > ( ) ;
818+
819+ useEffect ( ( ) => {
820+ if ( fetcher . data ?. success ) onClose ( ) ;
821+ } , [ fetcher . data , onClose ] ) ;
822+
823+ return (
824+ < fetcher . Form method = "post" className = "space-y-3" >
825+ < input type = "hidden" name = "_action" value = "publish-draft" />
826+ < input type = "hidden" name = "notificationId" value = { notificationId } />
827+ < Paragraph variant = "small" className = "text-text-dimmed" >
828+ Set the schedule for this notification. It becomes visible to users at the start time.
829+ </ Paragraph >
830+ < div className = "grid grid-cols-2 gap-3" >
831+ < div >
832+ < Label variant = "small" >
833+ Starts at (UTC) < span className = "text-red-400" > *</ span >
834+ </ Label >
835+ < input
836+ name = "startsAt"
837+ type = "datetime-local"
838+ defaultValue = { toDatetimeLocalUTC ( new Date ( ) ) }
839+ className = "mt-1 block h-8 w-full rounded border border-background-bright bg-background-hover px-2 text-sm text-text-bright transition hover:border-border-bright hover:bg-secondary"
840+ required
841+ />
842+ </ div >
843+ < div >
844+ < Label variant = "small" >
845+ Ends at (UTC) < span className = "text-red-400" > *</ span >
846+ </ Label >
847+ < input
848+ name = "endsAt"
849+ type = "datetime-local"
850+ defaultValue = { defaultEndsAt ( ) }
851+ className = "mt-1 block h-8 w-full rounded border border-background-bright bg-background-hover px-2 text-sm text-text-bright transition hover:border-border-bright hover:bg-secondary"
852+ required
853+ />
854+ </ div >
855+ </ div >
856+ < DialogFooter className = "items-center" >
857+ { fetcher . data ?. error && < span className = "text-xs text-red-400" > { fetcher . data . error } </ span > }
858+ < Button type = "button" variant = "tertiary/medium" onClick = { onClose } >
859+ Cancel
860+ </ Button >
861+ < Button type = "submit" variant = "primary/medium" disabled = { fetcher . state !== "idle" } >
862+ { fetcher . state !== "idle" ? "Publishing..." : "Publish" }
863+ </ Button >
864+ </ DialogFooter >
865+ </ fetcher . Form >
866+ ) ;
867+ }
868+
856869function DeleteConfirmationButton ( { notificationId } : { notificationId : string } ) {
857870 const [ open , setOpen ] = useState ( false ) ;
858871 const fetcher = useFetcher ( ) ;
0 commit comments