mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 08:17:11 +00:00
fix(ui/profiles): latch extraModels touch state for explicit-clear
The previous fix used react-hook-form's `dirtyFields.extraModels` to gate forwarding of the empty value. RHF compares each value to its `defaultValues` entry, so typing "x" and erasing back to "" reverts the dirty flag to false — making it impossible to clear a saved ANTHROPIC_EXTRA_MODELS through the dashboard. Track a local boolean that latches true on the first onChange and resets when the dialog closes. The latch survives a "type then delete" round-trip, so the empty-string delete signal still reaches the server, while a never-touched field still gets skipped to preserve the existing saved value.
This commit is contained in:
@@ -54,10 +54,17 @@ export function ProfileDialog({ open, onClose, profile }: ProfileDialogProps) {
|
||||
const updateMutation = useUpdateProfile();
|
||||
const [showModelMapping, setShowModelMapping] = useState(false);
|
||||
|
||||
// Tracks whether the user has interacted with the Extra Models field in
|
||||
// edit mode. RHF's `dirtyFields` compares the current value against
|
||||
// `defaultValues`, so typing "x" and erasing back to "" reverts dirty to
|
||||
// false. We need a latch so an explicit-clear (type then delete) still
|
||||
// forwards the empty-string delete signal to the backend.
|
||||
const [extraModelsTouched, setExtraModelsTouched] = useState(false);
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors, dirtyFields },
|
||||
formState: { errors },
|
||||
reset,
|
||||
control,
|
||||
} = useForm<FormData>({
|
||||
@@ -91,6 +98,7 @@ export function ProfileDialog({ open, onClose, profile }: ProfileDialogProps) {
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
setShowModelMapping(false);
|
||||
setExtraModelsTouched(false);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
@@ -99,18 +107,19 @@ export function ProfileDialog({ open, onClose, profile }: ProfileDialogProps) {
|
||||
if (profile) {
|
||||
// Update mode. The dialog cannot pre-populate `extraModels` from the
|
||||
// existing profile (Profile type carries no env data), so the field
|
||||
// always starts blank in edit mode. Forwarding that blank value
|
||||
// unconditionally would clobber any saved ANTHROPIC_EXTRA_MODELS via
|
||||
// the route's "empty string deletes" semantics. Only forward the
|
||||
// field if the user actually edited it — typing then clearing still
|
||||
// marks it dirty, preserving the explicit-clear UX.
|
||||
// always starts blank. Forwarding that blank value unconditionally
|
||||
// would clobber any saved ANTHROPIC_EXTRA_MODELS, since the PUT route
|
||||
// treats an empty string as a delete signal. Use a latch flipped on
|
||||
// the first onChange so we can distinguish "user never touched it"
|
||||
// (skip → preserve existing value) from "user typed then cleared"
|
||||
// (forward "" → explicit delete).
|
||||
await updateMutation.mutateAsync({
|
||||
name: profile.name,
|
||||
data: {
|
||||
baseUrl: data.baseUrl,
|
||||
apiKey: data.apiKey,
|
||||
model: data.model,
|
||||
...(dirtyFields.extraModels ? { extraModels: data.extraModels } : {}),
|
||||
...(extraModelsTouched ? { extraModels: data.extraModels } : {}),
|
||||
opusModel: data.opusModel,
|
||||
sonnetModel: data.sonnetModel,
|
||||
haikuModel: data.haikuModel,
|
||||
@@ -178,7 +187,9 @@ export function ProfileDialog({ open, onClose, profile }: ProfileDialogProps) {
|
||||
<Label htmlFor="extraModels">Extra Models</Label>
|
||||
<Input
|
||||
id="extraModels"
|
||||
{...register('extraModels')}
|
||||
{...register('extraModels', {
|
||||
onChange: () => setExtraModelsTouched(true),
|
||||
})}
|
||||
placeholder="model-a,model-b,model-c"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
|
||||
Reference in New Issue
Block a user