diff --git a/web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx b/web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx index 6b26cd17..25258d92 100644 --- a/web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx +++ b/web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx @@ -24,7 +24,7 @@ import { useCallback, useRef, } from 'react' -import { useForm } from 'react-hook-form' +import { type SubmitErrorHandler, useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { useQuery, useQueryClient } from '@tanstack/react-query' import { @@ -140,6 +140,7 @@ import { hasModelConfigChanged, findMissingModelsInMapping, validateModelMappingJson, + hasAdvancedSettingsErrors, } from '../../lib' import { collectInvalidStatusCodeEntries, @@ -204,7 +205,6 @@ function readAdvancedSettingsPreference(): boolean { function hasAdvancedSettingsValues(values: ChannelFormValues): boolean { return Boolean( - values.model_mapping?.trim() || values.param_override?.trim() || values.header_override?.trim() || values.status_code_mapping?.trim() || @@ -1008,6 +1008,26 @@ export function ChannelMutateDrawer({ ] ) + const handleAdvancedSettingsOpenChange = useCallback((nextOpen: boolean) => { + setAdvancedSettingsOpen(nextOpen) + if (typeof window !== 'undefined') { + window.localStorage.setItem( + ADVANCED_SETTINGS_EXPANDED_KEY, + String(nextOpen) + ) + } + }, []) + + const onInvalid: SubmitErrorHandler = useCallback( + (errors) => { + if (hasAdvancedSettingsErrors(errors)) { + handleAdvancedSettingsOpenChange(true) + } + toast.error(t('Please fix the highlighted fields before saving')) + }, + [handleAdvancedSettingsOpenChange, t] + ) + // Handle drawer close const handleOpenChange = useCallback( (v: boolean) => { @@ -1020,16 +1040,6 @@ export function ChannelMutateDrawer({ [onOpenChange, form] ) - const handleAdvancedSettingsOpenChange = useCallback((nextOpen: boolean) => { - setAdvancedSettingsOpen(nextOpen) - if (typeof window !== 'undefined') { - window.localStorage.setItem( - ADVANCED_SETTINGS_EXPANDED_KEY, - String(nextOpen) - ) - } - }, []) - return ( <> @@ -1060,7 +1070,7 @@ export function ChannelMutateDrawer({
{isChannelDetailLoading ? ( diff --git a/web/default/src/features/channels/lib/channel-form-errors.ts b/web/default/src/features/channels/lib/channel-form-errors.ts new file mode 100644 index 00000000..5f3a5bfd --- /dev/null +++ b/web/default/src/features/channels/lib/channel-form-errors.ts @@ -0,0 +1,66 @@ +/* +Copyright (C) 2023-2026 QuantumNous + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +For commercial licensing, please contact support@quantumnous.com +*/ +import type { FieldPath } from 'react-hook-form' +import type { ChannelFormValues } from './channel-form' + +type ChannelFormErrorMap = Partial< + Record, unknown> +> + +const ADVANCED_SETTINGS_FIELDS = new Set>([ + 'priority', + 'weight', + 'test_model', + 'auto_ban', + 'tag', + 'remark', + 'param_override', + 'header_override', + 'status_code_mapping', + 'force_format', + 'thinking_to_content', + 'pass_through_body_enabled', + 'proxy', + 'system_prompt', + 'system_prompt_override', + 'allow_service_tier', + 'disable_store', + 'allow_safety_identifier', + 'allow_include_obfuscation', + 'allow_inference_geo', + 'allow_speed', + 'claude_beta_query', + 'upstream_model_update_check_enabled', + 'upstream_model_update_auto_sync_enabled', + 'upstream_model_update_ignored_models', +]) + +export function isAdvancedSettingsField( + fieldName: string +): fieldName is FieldPath { + return ADVANCED_SETTINGS_FIELDS.has(fieldName as FieldPath) +} + +export function hasAdvancedSettingsErrors( + errors: ChannelFormErrorMap +): boolean { + return Object.keys(errors).some((fieldName) => + isAdvancedSettingsField(fieldName) + ) +} diff --git a/web/default/src/features/channels/lib/index.ts b/web/default/src/features/channels/lib/index.ts index c22feb66..32059281 100644 --- a/web/default/src/features/channels/lib/index.ts +++ b/web/default/src/features/channels/lib/index.ts @@ -18,6 +18,7 @@ For commercial licensing, please contact support@quantumnous.com */ // Re-export all library functions export * from './channel-actions' +export * from './channel-form-errors' export * from './channel-form' export * from './channel-type-config' export * from './channel-utils'