@@ -104,6 +104,7 @@ import {
104104 DialogTrigger ,
105105} from "~/components/primitives/Dialog" ;
106106import { ErrorGroupActions } from "~/v3/services/errorGroupActions.server" ;
107+ import { FormError } from "~/components/primitives/FormError" ;
107108
108109export const meta : MetaFunction < typeof loader > = ( { data } ) => {
109110 return [
@@ -113,6 +114,11 @@ export const meta: MetaFunction<typeof loader> = ({ data }) => {
113114 ] ;
114115} ;
115116
117+ const emptyStringToUndefined = z . preprocess (
118+ ( v ) => ( v === "" ? undefined : v ) ,
119+ z . coerce . number ( ) . positive ( ) . optional ( )
120+ ) ;
121+
116122const actionSchema = z . discriminatedUnion ( "action" , [
117123 z . object ( {
118124 action : z . literal ( "resolve" ) ,
@@ -122,10 +128,10 @@ const actionSchema = z.discriminatedUnion("action", [
122128 z . object ( {
123129 action : z . literal ( "ignore" ) ,
124130 taskIdentifier : z . string ( ) . min ( 1 ) ,
125- duration : z . coerce . number ( ) . positive ( ) . optional ( ) ,
126- occurrenceRate : z . coerce . number ( ) . positive ( ) . optional ( ) ,
127- totalOccurrences : z . coerce . number ( ) . positive ( ) . optional ( ) ,
128- reason : z . string ( ) . optional ( ) ,
131+ duration : emptyStringToUndefined ,
132+ occurrenceRate : emptyStringToUndefined ,
133+ totalOccurrences : emptyStringToUndefined ,
134+ reason : z . preprocess ( ( v ) => ( v === "" ? undefined : v ) , z . string ( ) . optional ( ) ) ,
129135 } ) ,
130136 z . object ( {
131137 action : z . literal ( "unresolve" ) ,
@@ -684,20 +690,13 @@ function IgnoredDetails({
684690 < Paragraph variant = "extra-small/dimmed" >
685691 { state . ignoredByUserDisplayName && < > Configured by { state . ignoredByUserDisplayName } </ > }
686692 { state . ignoredByUserDisplayName && state . ignoredAt && " " }
687- { state . ignoredAt && (
688- < RelativeDateTime
689- date = { state . ignoredAt }
690- capitalize = { false }
691- />
692- ) }
693+ { state . ignoredAt && < RelativeDateTime date = { state . ignoredAt } capitalize = { false } /> }
693694 </ Paragraph >
694695 ) }
695696 </ div >
696697
697698 { state . ignoredReason && (
698- < div className = "text-text-dimmed" >
699- Reason: < span className = "text-text-bright" > { state . ignoredReason } </ span >
700- </ div >
699+ < Paragraph variant = "extra-small/dimmed" > Reason: { state . ignoredReason } </ Paragraph >
701700 ) }
702701
703702 { hasConditions && (
@@ -814,16 +813,24 @@ function ErrorStatusDropdown({
814813 </ >
815814 ) }
816815
817- { state . status === "RESOLVED" && (
818- < PopoverMenuItem
819- icon = { ArrowBackUpIcon }
820- leadingIconClassName = "text-error"
821- title = "Unresolved"
822- onClick = { ( ) => act ( { taskIdentifier, action : "unresolve" } ) }
823- />
816+ { state . status === "IGNORED" && (
817+ < >
818+ < PopoverMenuItem
819+ icon = { CheckIcon }
820+ leadingIconClassName = "text-success"
821+ title = "Resolved"
822+ onClick = { ( ) => act ( { taskIdentifier, action : "resolve" } ) }
823+ />
824+ < PopoverMenuItem
825+ icon = { ArrowBackUpIcon }
826+ leadingIconClassName = "text-error"
827+ title = "Unresolved"
828+ onClick = { ( ) => act ( { taskIdentifier, action : "unresolve" } ) }
829+ />
830+ </ >
824831 ) }
825832
826- { state . status === "IGNORED " && (
833+ { state . status === "RESOLVED " && (
827834 < PopoverMenuItem
828835 icon = { ArrowBackUpIcon }
829836 leadingIconClassName = "text-error"
@@ -859,12 +866,23 @@ function CustomIgnoreForm({
859866 const submit = useSubmit ( ) ;
860867 const navigation = useNavigation ( ) ;
861868 const isSubmitting = navigation . state !== "idle" ;
869+ const [ conditionError , setConditionError ] = useState < string | null > ( null ) ;
862870
863871 return (
864872 < Form
865873 method = "post"
866874 onSubmit = { ( e ) => {
867875 e . preventDefault ( ) ;
876+ const formData = new FormData ( e . currentTarget ) ;
877+ const rate = formData . get ( "occurrenceRate" ) ?. toString ( ) . trim ( ) ;
878+ const total = formData . get ( "totalOccurrences" ) ?. toString ( ) . trim ( ) ;
879+
880+ if ( ! rate && ! total ) {
881+ setConditionError ( "At least one unignore condition is required" ) ;
882+ return ;
883+ }
884+
885+ setConditionError ( null ) ;
868886 submit ( e . currentTarget , { method : "post" } ) ;
869887 setTimeout ( onClose , 100 ) ;
870888 } }
@@ -883,6 +901,7 @@ function CustomIgnoreForm({
883901 type = "number"
884902 min = { 1 }
885903 placeholder = "e.g. 10"
904+ onChange = { ( ) => conditionError && setConditionError ( null ) }
886905 />
887906 </ InputGroup >
888907
@@ -896,19 +915,17 @@ function CustomIgnoreForm({
896915 type = "number"
897916 min = { 1 }
898917 placeholder = "e.g. 100"
918+ onChange = { ( ) => conditionError && setConditionError ( null ) }
899919 />
900920 </ InputGroup >
901921
922+ { conditionError && < FormError > { conditionError } </ FormError > }
923+
902924 < InputGroup fullWidth >
903925 < Label htmlFor = "reason" variant = "small" required = { false } >
904926 Reason
905927 </ Label >
906- < Input
907- id = "reason"
908- name = "reason"
909- type = "text"
910- placeholder = "e.g. Known flaky test"
911- />
928+ < Input id = "reason" name = "reason" type = "text" placeholder = "e.g. Known flaky test" />
912929 </ InputGroup >
913930 </ div >
914931
0 commit comments