From a643b05368816d253d04935dab01dcbf849f844d Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Mon, 11 May 2026 12:01:49 +0800 Subject: [PATCH] fix(web): remove unsafe select value casts (#36007) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- eslint-suppressions.json | 10 --- .../app/overview/settings/index.tsx | 27 ++++--- .../field/input-type-select/index.tsx | 13 ++-- .../components/base/markdown-blocks/form.tsx | 55 +++++++------- web/app/components/base/theme-selector.tsx | 14 +++- .../subscription-list/create/index.tsx | 16 +++-- .../components/frequency-selector.tsx | 14 ++-- .../workflow/nodes/trigger-webhook/panel.tsx | 71 ++++++++++++++----- web/app/signin/invite-settings/page.tsx | 62 ++++++++++------ web/app/signin/one-more-step.tsx | 61 ++++++++++------ 10 files changed, 218 insertions(+), 125 deletions(-) diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 2de84456ee..a7512f8c66 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -1334,11 +1334,6 @@ "count": 9 } }, - "web/app/components/base/markdown-blocks/form.tsx": { - "erasable-syntax-only/enums": { - "count": 3 - } - }, "web/app/components/base/markdown-blocks/index.ts": { "no-barrel-files/no-barrel-files": { "count": 10 @@ -4435,11 +4430,6 @@ "count": 1 } }, - "web/app/signin/one-more-step.tsx": { - "ts/no-explicit-any": { - "count": 1 - } - }, "web/app/signup/layout.tsx": { "ts/no-explicit-any": { "count": 1 diff --git a/web/app/components/app/overview/settings/index.tsx b/web/app/components/app/overview/settings/index.tsx index ae772d0750..aefe339b94 100644 --- a/web/app/components/app/overview/settings/index.tsx +++ b/web/app/components/app/overview/settings/index.tsx @@ -55,10 +55,12 @@ export type ConfigParams = { const prefixSettings = 'overview.appInfo.settings' type SelectOption = { - value: string + value: Language name: string } +const LANGUAGE_OPTIONS: SelectOption[] = languages.filter(item => item.supported) + const createInputInfo = (appInfo: ISettingsModalProps['appInfo']) => { const { title, @@ -139,8 +141,13 @@ const SettingsModal: FC = ({ const { enableBilling, plan, webappCopyrightEnabled } = useProviderContext() const { setShowPricingModal, setShowAccountSettingModal } = useModalContext() const isFreePlan = plan.type === 'sandbox' - const languageOptions: SelectOption[] = languages.filter(item => item.supported) - const selectedLanguage = languageOptions.find(item => item.value === language) + const selectedLanguage = LANGUAGE_OPTIONS.find(item => item.value === language) + + const handleLanguageChange = (nextValue: string | null) => { + const nextLanguage = LANGUAGE_OPTIONS.find(item => item.value === nextValue) + if (nextLanguage) + setLanguage(nextLanguage.value) + } const handlePlanClick = useCallback(() => { if (isFreePlan) setShowPricingModal() @@ -308,17 +315,17 @@ const SettingsModal: FC = ({
{t(`${prefixSettings}.language`, { ns: 'appOverview' })}
updateValue(name, val as string)} + onValueChange={(val) => { + if (val != null) + updateValue(name, val) + }} > diff --git a/web/app/components/base/theme-selector.tsx b/web/app/components/base/theme-selector.tsx index 1676dfff72..67dc7b7b29 100644 --- a/web/app/components/base/theme-selector.tsx +++ b/web/app/components/base/theme-selector.tsx @@ -12,7 +12,12 @@ import { useTheme } from 'next-themes' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' -export type Theme = 'light' | 'dark' | 'system' +const THEMES = ['light', 'dark', 'system'] as const +export type Theme = typeof THEMES[number] + +const isTheme = (value: string): value is Theme => { + return (THEMES as readonly string[]).includes(value) +} export default function ThemeSelector() { const { t } = useTranslation() @@ -22,6 +27,11 @@ export default function ThemeSelector() { setTheme(newTheme) } + const handleThemeValueChange = (value: string) => { + if (isTheme(value)) + handleThemeChange(value) + } + const getCurrentIcon = () => { switch (theme) { case 'light': return @@ -43,7 +53,7 @@ export default function ThemeSelector() { {getCurrentIcon()} - handleThemeChange(value as Theme)}> + {t('theme.light', { ns: 'common' })} diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx index 9091cd337c..9c9a95a37b 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx @@ -186,6 +186,15 @@ export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BU } } + const handleCreateTypeChange = (value: string | null) => { + const option = visibleOptions.find(item => item.value === value) + if (!option) + return + + setIsMenuOpen(false) + void onChooseCreateType(option.value) + } + const onClickCreate = (e: React.MouseEvent) => { if (subscriptionCount >= MAX_COUNT) { e.stopPropagation() @@ -209,12 +218,7 @@ export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BU value={methodType === DEFAULT_METHOD ? null : methodType} open={shouldAllowSelect ? isMenuOpen : false} onOpenChange={setIsMenuOpen} - onValueChange={(value) => { - if (!value) - return - setIsMenuOpen(false) - void onChooseCreateType(value as SupportedCreationMethods) - }} + onValueChange={handleCreateTypeChange} > } diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/frequency-selector.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/frequency-selector.tsx index 0343e0bead..379749e3b8 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/components/frequency-selector.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/components/frequency-selector.tsx @@ -9,8 +9,6 @@ import { SelectLabel, SelectTrigger, } from '@langgenius/dify-ui/select' -import * as React from 'react' -import { useMemo } from 'react' import { useTranslation } from 'react-i18next' type FrequencyOption = { @@ -27,19 +25,25 @@ const FrequencySelector = ({ frequency, onChange }: FrequencySelectorProps) => { const { t } = useTranslation() const groupLabel = t('nodes.triggerSchedule.frequency.label', { ns: 'workflow' }) - const frequencies = useMemo(() => [ + const frequencies: FrequencyOption[] = [ { value: 'hourly', name: t('nodes.triggerSchedule.frequency.hourly', { ns: 'workflow' }) }, { value: 'daily', name: t('nodes.triggerSchedule.frequency.daily', { ns: 'workflow' }) }, { value: 'weekly', name: t('nodes.triggerSchedule.frequency.weekly', { ns: 'workflow' }) }, { value: 'monthly', name: t('nodes.triggerSchedule.frequency.monthly', { ns: 'workflow' }) }, - ], [t]) + ] const selectedFrequency = frequencies.find(item => item.value === frequency) + const handleFrequencyChange = (value: string | null) => { + const selected = frequencies.find(item => item.value === value) + if (selected) + onChange(selected.value) + } + return ( + + {selectedMethod?.name} + + + {HTTP_METHODS.map(item => ( + + {item.name} + + + ))} + + + ) +} + const Panel: FC> = ({ id, data, @@ -75,7 +120,6 @@ const Panel: FC> = ({ } }, [readOnly, inputs.webhook_url, generateWebhookUrl]) - const selectedMethod = HTTP_METHODS.find(item => item.value === inputs.method) ?? null const selectedContentType = CONTENT_TYPES.find(item => item.value === inputs.content_type) ?? null return ( @@ -86,24 +130,13 @@ const Panel: FC> = ({
- + onChange={handleMethodChange} + />
item.supported) + .map(item => ({ + value: item.value, + name: item.name, + })) + +const TIMEZONE_OPTIONS: TimezoneSelectOption[] = timezones.map(item => ({ + value: String(item.value), + name: item.name, +})) + export default function InviteSettingsPage() { const { t } = useTranslation() const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions()) @@ -35,9 +52,20 @@ export default function InviteSettingsPage() { const [name, setName] = useState('') const [language, setLanguage] = useState(LanguagesSupported[0]) const [timezone, setTimezone] = useState(() => Intl.DateTimeFormat().resolvedOptions().timeZone || 'America/Los_Angeles') - const languageOptions: SelectOption[] = languages.filter(item => item.supported) - const selectedLanguage = languageOptions.find(item => item.value === language) - const selectedTimezone = timezones.find(item => item.value === timezone) + const selectedLanguage = LANGUAGE_OPTIONS.find(item => item.value === language) + const selectedTimezone = TIMEZONE_OPTIONS.find(item => item.value === timezone) + + const handleLanguageChange = (nextValue: string | null) => { + const nextLanguage = LANGUAGE_OPTIONS.find(item => item.value === nextValue) + if (nextLanguage) + setLanguage(nextLanguage.value) + } + + const handleTimezoneChange = (nextValue: string | null) => { + const nextTimezone = TIMEZONE_OPTIONS.find(item => item.value === nextValue) + if (nextTimezone) + setTimezone(nextTimezone.value) + } const checkParams = { url: '/activate/check', @@ -123,23 +151,19 @@ export default function InviteSettingsPage() {
-
-