@@ -17,9 +17,9 @@ import {
1717 type WatchDraft ,
1818 type WatchKind ,
1919} from "@internal/dashboard-agent-contracts" ;
20- import { useState } from "react" ;
20+ import { useId , useState } from "react" ;
2121import { Button } from "~/components/primitives/Buttons" ;
22- import { CheckboxWithLabel } from "~/components/primitives/Checkbox" ;
22+ import { Checkbox } from "~/components/primitives/Checkbox" ;
2323import { Input } from "~/components/primitives/Input" ;
2424import { AgentSpinner } from "~/components/primitives/Spinner" ;
2525import { cn } from "~/utils/cn" ;
@@ -62,26 +62,65 @@ function ButtonSpinner() {
6262 return < AgentSpinner size = { 14 } /> ;
6363}
6464
65+ /** Controlled, unlike `CheckboxWithLabel`: the draft is the only thing that says what's on. */
66+ function Toggle ( {
67+ label,
68+ checked,
69+ disabled,
70+ onChange,
71+ } : {
72+ label : string ;
73+ checked : boolean ;
74+ disabled : boolean ;
75+ onChange : ( checked : boolean ) => void ;
76+ } ) {
77+ const id = useId ( ) ;
78+ return (
79+ < div className = { cn ( "group flex w-fit items-start gap-x-2" , disabled && "opacity-70" ) } >
80+ < Checkbox
81+ id = { id }
82+ checked = { checked }
83+ disabled = { disabled }
84+ onChange = { ( event ) => onChange ( event . target . checked ) }
85+ className = "mt-1"
86+ />
87+ < label
88+ htmlFor = { id }
89+ className = { cn (
90+ "mt-0.5 select-none text-sm text-text-bright" ,
91+ disabled ? "cursor-default" : "cursor-pointer"
92+ ) }
93+ >
94+ { label }
95+ </ label >
96+ </ div >
97+ ) ;
98+ }
99+
65100/** One choice in an inline picker. */
66101function Choice ( {
67102 selected,
103+ disabled,
68104 onSelect,
69105 children,
70106} : {
71107 selected : boolean ;
108+ disabled : boolean ;
72109 onSelect : ( ) => void ;
73110 children : React . ReactNode ;
74111} ) {
75112 return (
76113 < button
77114 type = "button"
78115 aria-pressed = { selected }
116+ disabled = { disabled }
79117 onClick = { onSelect }
80118 className = { cn (
81119 "rounded-full border px-2 py-0.5 text-xs transition focus-custom" ,
82120 selected
83121 ? "border-border-brightest bg-background-bright text-text-bright"
84- : "border-border-bright text-text-dimmed hover:text-text-bright"
122+ : "border-border-bright text-text-dimmed hover:text-text-bright" ,
123+ disabled && "cursor-default opacity-70 hover:text-text-dimmed"
85124 ) }
86125 >
87126 { children }
@@ -178,6 +217,7 @@ export function WatchCard({
178217 < Choice
179218 key = { kind }
180219 selected = { kind === spec . kind }
220+ disabled = { pending }
181221 onSelect = { ( ) => {
182222 if ( kind !== spec . kind ) onChange ( withVariant ( draft , kind ) ) ;
183223 } }
@@ -200,6 +240,7 @@ export function WatchCard({
200240 min = { 0 }
201241 variant = "small"
202242 className = "w-28"
243+ disabled = { pending }
203244 // A half-typed field must show empty, not "NaN"; `watchDraftError`
204245 // is what refuses to submit it.
205246 value = { Number . isFinite ( spec . threshold ) ? String ( spec . threshold ) : "" }
@@ -218,6 +259,7 @@ export function WatchCard({
218259 min = { 1 }
219260 variant = "small"
220261 className = "w-28"
262+ disabled = { pending }
221263 value = { Number . isFinite ( spec . thresholdMinutes ) ? String ( spec . thresholdMinutes ) : "" }
222264 onChange = { ( event ) =>
223265 onChange ( withAgeMinutes ( draft , Number . parseInt ( event . target . value , 10 ) ) )
@@ -233,6 +275,7 @@ export function WatchCard({
233275 < Choice
234276 key = { hours }
235277 selected = { spec . maxHours === hours }
278+ disabled = { pending }
236279 onSelect = { ( ) => onChange ( withWindow ( draft , hours ) ) }
237280 >
238281 { formatWatchWindow ( hours ) }
@@ -247,6 +290,7 @@ export function WatchCard({
247290 < Choice
248291 key = { minutes }
249292 selected = { spec . checkEveryMinutes === minutes }
293+ disabled = { pending }
250294 onSelect = { ( ) => onChange ( withCadence ( draft , minutes ) ) }
251295 >
252296 { formatWatchCadence ( minutes ) }
@@ -258,18 +302,18 @@ export function WatchCard({
258302 group, so "email instead of chat" is not expressible. */ }
259303 < Field label = "When there's an answer" >
260304 < div className = "flex flex-col gap-1.5" >
261- < CheckboxWithLabel
262- variant = "simple/small"
305+ < Toggle
263306 label = "Investigate attention outcomes"
264- defaultChecked = { draft . followUp . investigateOnAttention }
307+ checked = { draft . followUp . investigateOnAttention }
308+ disabled = { pending }
265309 onChange = { ( checked ) =>
266310 onChange ( withFollowUp ( draft , { investigateOnAttention : checked } ) )
267311 }
268312 />
269- < CheckboxWithLabel
270- variant = "simple/small"
313+ < Toggle
271314 label = "Also notify me externally"
272- defaultChecked = { draft . followUp . notifyExternally }
315+ checked = { draft . followUp . notifyExternally }
316+ disabled = { pending }
273317 onChange = { ( checked ) => onChange ( withFollowUp ( draft , { notifyExternally : checked } ) ) }
274318 />
275319 </ div >
0 commit comments