99} from "@heroicons/react/20/solid" ;
1010import { useFetcher , useNavigate } from "@remix-run/react" ;
1111import { SlackIcon } from "@trigger.dev/companyicons" ;
12- import { Fragment , useEffect , useRef , useState } from "react" ;
12+ import { Fragment , useEffect , useMemo , useRef , useState } from "react" ;
13+ import stableStringify from "json-stable-stringify" ;
1314import { z } from "zod" ;
1415import { Button , LinkButton } from "~/components/primitives/Buttons" ;
1516import { Callout , variantClasses } from "~/components/primitives/Callout" ;
@@ -104,6 +105,35 @@ export function ConfigureErrorAlerts({
104105 existingWebhooks . length > 0 ? [ ...existingWebhooks . map ( ( w ) => w . url ) , "" ] : [ "" ]
105106 ) ;
106107
108+ const [ formChangeTick , setFormChangeTick ] = useState ( 0 ) ;
109+ const bumpFormChange = ( ) => setFormChangeTick ( ( n ) => n + 1 ) ;
110+
111+ const isDirty = useMemo ( ( ) => {
112+ const initialSlackValue = existingSlackChannel
113+ ? `${ existingSlackChannel . channelId } /${ existingSlackChannel . channelName } `
114+ : "" ;
115+ if ( ( selectedSlackChannelValue ?? "" ) !== initialSlackValue ) return true ;
116+ const currentEmails = emailFieldValues . current . filter ( ( v ) => v !== "" ) ;
117+ if (
118+ stableStringify ( currentEmails ) !==
119+ stableStringify ( existingEmails . map ( ( e ) => e . email ) )
120+ )
121+ return true ;
122+ const currentWebhooks = webhookFieldValues . current . filter ( ( v ) => v !== "" ) ;
123+ if (
124+ stableStringify ( currentWebhooks ) !==
125+ stableStringify ( existingWebhooks . map ( ( w ) => w . url ) )
126+ )
127+ return true ;
128+ return false ;
129+ } , [
130+ selectedSlackChannelValue ,
131+ existingSlackChannel ,
132+ existingEmails ,
133+ existingWebhooks ,
134+ formChangeTick ,
135+ ] ) ;
136+
107137 const [ form , { emails, webhooks, slackChannel, slackIntegrationId } ] = useForm ( {
108138 id : "configure-error-alerts" ,
109139 onValidate ( { formData } ) {
@@ -165,6 +195,7 @@ export function ConfigureErrorAlerts({
165195 icon = { EnvelopeIcon }
166196 onChange = { ( e ) => {
167197 emailFieldValues . current [ index ] = e . target . value ;
198+ bumpFormChange ( ) ;
168199 if (
169200 emailFields . length === emailFieldValues . current . length &&
170201 emailFieldValues . current . every ( ( v ) => v !== "" )
@@ -218,6 +249,15 @@ export function ConfigureErrorAlerts({
218249 >
219250 { ( matches ) => (
220251 < >
252+ < SelectItem
253+ value = ""
254+ className = "border-b border-grid-bright text-text-dimmed"
255+ >
256+ < div className = "flex items-center gap-1.5" >
257+ < XMarkIcon className = "size-4" />
258+ < span > No channel</ span >
259+ </ div >
260+ </ SelectItem >
221261 { matches ?. map ( ( channel ) => (
222262 < SelectItem
223263 key = { channel . id }
@@ -243,15 +283,6 @@ export function ConfigureErrorAlerts({
243283 </ Callout >
244284 ) }
245285 < Hint >
246- { selectedSlackChannelValue ? (
247- < button
248- type = "button"
249- onClick = { ( ) => setSelectedSlackChannelValue ( "" ) }
250- className = "mr-3 text-indigo-500 transition hover:text-indigo-400 focus-visible:focus-custom"
251- >
252- Remove channel
253- </ button >
254- ) : null }
255286 < TextLink to = { organizationSlackIntegrationPath ( organization ) } >
256287 Manage Slack connection
257288 </ TextLink >
@@ -327,6 +358,7 @@ export function ConfigureErrorAlerts({
327358 icon = { GlobeAltIcon }
328359 onChange = { ( e ) => {
329360 webhookFieldValues . current [ index ] = e . target . value ;
361+ bumpFormChange ( ) ;
330362 if (
331363 webhookFields . length === webhookFieldValues . current . length &&
332364 webhookFieldValues . current . every ( ( v ) => v !== "" )
@@ -353,7 +385,7 @@ export function ConfigureErrorAlerts({
353385 < Button
354386 variant = "primary/medium"
355387 type = "submit"
356- disabled = { isSubmitting }
388+ disabled = { ! isDirty || isSubmitting }
357389 isLoading = { isSubmitting }
358390 >
359391 { isSubmitting ? "Saving…" : "Save" }
0 commit comments