From 5bd63a21913dbdb749885e679952749c34d21105 Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Thu, 7 May 2026 13:01:52 +0300 Subject: [PATCH] feat(frontend): autogenerate entity names from type/provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror the providers form pattern (defaultName tied to type) across bots, targets, trackers, actions, and configs. Each form now derives form.name from the selected type or provider while the user hasn't manually edited it; switching to edit-mode flips the manualEdited flag so existing names are preserved. Defaults: bots → " Bot"; targets → type label; notification trackers → " Tracker"; command trackers → " Commands"; actions → " "; tracking/template/ command/command-template configs → " ". TargetForm and TrackerForm grew an optional onnameinput prop so parents can flag manual edits in subform inputs. --- frontend/src/routes/actions/+page.svelte | 16 +++++++++++++++- frontend/src/routes/bots/EmailBotTab.svelte | 15 ++++++++++++--- frontend/src/routes/bots/MatrixBotTab.svelte | 15 ++++++++++++--- frontend/src/routes/bots/TelegramBotTab.svelte | 16 ++++++++++++---- frontend/src/routes/command-configs/+page.svelte | 15 +++++++++++++-- .../routes/command-template-configs/+page.svelte | 13 ++++++++++++- .../src/routes/command-trackers/+page.svelte | 14 ++++++++++++-- .../routes/notification-trackers/+page.svelte | 11 +++++++++++ .../notification-trackers/TrackerForm.svelte | 4 +++- frontend/src/routes/targets/+page.svelte | 15 +++++++++++++++ frontend/src/routes/targets/TargetForm.svelte | 4 +++- .../src/routes/template-configs/+page.svelte | 13 ++++++++++++- .../src/routes/tracking-configs/+page.svelte | 13 +++++++++++-- 13 files changed, 143 insertions(+), 21 deletions(-) diff --git a/frontend/src/routes/actions/+page.svelte b/frontend/src/routes/actions/+page.svelte index 6ece433..dbc879e 100644 --- a/frontend/src/routes/actions/+page.svelte +++ b/frontend/src/routes/actions/+page.svelte @@ -40,7 +40,19 @@ schedule_type: 'interval', schedule_interval: 3600, schedule_cron: '', enabled: false, }); + let nameManuallyEdited = $state(false); let error = $state(''); + + function actionTypeLabel(at: string): string { + return at.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); + } + $effect(() => { + if (showForm && !nameManuallyEdited && !editing) { + const provider = providers.find((p: any) => p.id === form.provider_id); + const at = actionTypeLabel(form.action_type || ''); + form.name = provider ? `${provider.name} ${at}`.trim() : at || 'Action'; + } + }); let loadError = $state(''); let submitting = $state(false); let loaded = $state(false); @@ -98,6 +110,7 @@ config: {}, schedule_type: 'interval', schedule_interval: 3600, schedule_cron: '', enabled: false, }; + nameManuallyEdited = false; editing = null; showForm = true; } @@ -109,6 +122,7 @@ schedule_interval: action.schedule_interval, schedule_cron: action.schedule_cron, enabled: action.enabled, }; + nameManuallyEdited = true; editing = action.id; showForm = true; } @@ -245,7 +259,7 @@
form.icon = v} /> - nameManuallyEdited = true} required class="flex-1 px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
diff --git a/frontend/src/routes/bots/EmailBotTab.svelte b/frontend/src/routes/bots/EmailBotTab.svelte index 6964a87..d44cbee 100644 --- a/frontend/src/routes/bots/EmailBotTab.svelte +++ b/frontend/src/routes/bots/EmailBotTab.svelte @@ -30,8 +30,16 @@ smtp_username: '', smtp_password: '', smtp_use_tls: true, }); let emailForm = $state(defaultEmailForm()); + let nameManuallyEdited = $state(false); - function openNewEmail() { emailForm = defaultEmailForm(); editingEmail = null; showEmailForm = true; } + const DEFAULT_BOT_NAME = 'Email Bot'; + $effect(() => { + if (showEmailForm && !nameManuallyEdited && !editingEmail) { + emailForm.name = DEFAULT_BOT_NAME; + } + }); + + function openNewEmail() { emailForm = defaultEmailForm(); nameManuallyEdited = false; editingEmail = null; showEmailForm = true; } function editEmailBot(bot: EmailBot) { emailForm = { name: bot.name, icon: bot.icon || '', email: bot.email, @@ -39,6 +47,7 @@ smtp_username: bot.smtp_username, smtp_password: '', smtp_use_tls: bot.smtp_use_tls, }; + nameManuallyEdited = true; editingEmail = bot.id; showEmailForm = true; } @@ -54,7 +63,7 @@ await api('/email-bots', { method: 'POST', body: JSON.stringify(body) }); snackSuccess(t('snack.emailBotCreated')); } - emailForm = defaultEmailForm(); showEmailForm = false; editingEmail = null; await onreload(); + emailForm = defaultEmailForm(); nameManuallyEdited = false; showEmailForm = false; editingEmail = null; await onreload(); } catch (err: any) { error = err.message; snackError(err.message); } finally { emailSubmitting = false; } } @@ -107,7 +116,7 @@
emailForm.icon = v} /> - nameManuallyEdited = true} required placeholder={t('emailBot.namePlaceholder')} class="flex-1 px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
diff --git a/frontend/src/routes/bots/MatrixBotTab.svelte b/frontend/src/routes/bots/MatrixBotTab.svelte index 7cd335e..e441d7c 100644 --- a/frontend/src/routes/bots/MatrixBotTab.svelte +++ b/frontend/src/routes/bots/MatrixBotTab.svelte @@ -29,14 +29,23 @@ name: '', icon: '', homeserver_url: '', access_token: '', display_name: '', }); let matrixForm = $state(defaultMatrixForm()); + let nameManuallyEdited = $state(false); - function openNewMatrix() { matrixForm = defaultMatrixForm(); editingMatrix = null; showMatrixForm = true; } + const DEFAULT_BOT_NAME = 'Matrix Bot'; + $effect(() => { + if (showMatrixForm && !nameManuallyEdited && !editingMatrix) { + matrixForm.name = DEFAULT_BOT_NAME; + } + }); + + function openNewMatrix() { matrixForm = defaultMatrixForm(); nameManuallyEdited = false; editingMatrix = null; showMatrixForm = true; } function editMatrixBot(bot: MatrixBot) { matrixForm = { name: bot.name, icon: bot.icon || '', homeserver_url: bot.homeserver_url, access_token: '', display_name: bot.display_name || '', }; + nameManuallyEdited = true; editingMatrix = bot.id; showMatrixForm = true; } @@ -52,7 +61,7 @@ await api('/matrix-bots', { method: 'POST', body: JSON.stringify(body) }); snackSuccess(t('snack.matrixBotCreated')); } - matrixForm = defaultMatrixForm(); showMatrixForm = false; editingMatrix = null; await onreload(); + matrixForm = defaultMatrixForm(); nameManuallyEdited = false; showMatrixForm = false; editingMatrix = null; await onreload(); } catch (err: any) { error = err.message; snackError(err.message); } finally { matrixSubmitting = false; } } @@ -105,7 +114,7 @@
matrixForm.icon = v} /> - nameManuallyEdited = true} required placeholder={t('matrixBot.namePlaceholder')} class="flex-1 px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
diff --git a/frontend/src/routes/bots/TelegramBotTab.svelte b/frontend/src/routes/bots/TelegramBotTab.svelte index a7d0253..05b97b9 100644 --- a/frontend/src/routes/bots/TelegramBotTab.svelte +++ b/frontend/src/routes/bots/TelegramBotTab.svelte @@ -29,10 +29,18 @@ let showForm = $state(false); let editing = $state(null); let form = $state({ name: '', icon: '', token: '' }); + let nameManuallyEdited = $state(false); let error = $state(''); let submitting = $state(false); let confirmDelete = $state<{ id: number; onconfirm: () => Promise } | null>(null); + const DEFAULT_BOT_NAME = 'Telegram Bot'; + $effect(() => { + if (showForm && !nameManuallyEdited && !editing) { + form.name = DEFAULT_BOT_NAME; + } + }); + // Per-bot expandable sections let chats = $state>({}); let chatsLoading = $state>({}); @@ -52,8 +60,8 @@ let botListenerStatus = $state>({}); let botListenerLoading = $state>({}); - function openNew() { form = { name: '', icon: '', token: '' }; editing = null; showForm = true; } - function editBot(bot: TelegramBot) { form = { name: bot.name, icon: bot.icon || '', token: '' }; editing = bot.id; showForm = true; } + function openNew() { form = { name: '', icon: '', token: '' }; nameManuallyEdited = false; editing = null; showForm = true; } + function editBot(bot: TelegramBot) { form = { name: bot.name, icon: bot.icon || '', token: '' }; nameManuallyEdited = true; editing = bot.id; showForm = true; } async function saveBot(e: SubmitEvent) { e.preventDefault(); error = ''; submitting = true; @@ -65,7 +73,7 @@ await api('/telegram-bots', { method: 'POST', body: JSON.stringify(form) }); snackSuccess(t('snack.botRegistered')); } - form = { name: '', icon: '', token: '' }; showForm = false; editing = null; await onreload(); + form = { name: '', icon: '', token: '' }; nameManuallyEdited = false; showForm = false; editing = null; await onreload(); } catch (err: any) { error = err.message; snackError(err.message); } finally { submitting = false; } } @@ -312,7 +320,7 @@
form.icon = v} /> - nameManuallyEdited = true} required placeholder={t('telegramBot.namePlaceholder')} class="flex-1 px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
diff --git a/frontend/src/routes/command-configs/+page.svelte b/frontend/src/routes/command-configs/+page.svelte index 06431a3..8d71108 100644 --- a/frontend/src/routes/command-configs/+page.svelte +++ b/frontend/src/routes/command-configs/+page.svelte @@ -21,6 +21,7 @@ import { highlightFromUrl } from '$lib/highlight'; import { globalProviderFilter } from '$lib/stores/provider-filter.svelte'; import ErrorBanner from '$lib/components/ErrorBanner.svelte'; + import { getDescriptor } from '$lib/providers'; import type { CommandConfig } from '$lib/types'; function templateName(id: number | null): string { @@ -69,6 +70,14 @@ command_template_config_id: null as number | null, }); let form = $state(defaultForm()); + let nameManuallyEdited = $state(false); + + $effect(() => { + if (showForm && !nameManuallyEdited && !editing) { + const desc = getDescriptor(form.provider_type); + form.name = desc ? `${desc.defaultName} Commands` : 'Commands'; + } + }); let allCapabilities = $derived(capabilitiesCache.items); let providerCommands = $derived<{key: string, icon: string}[]>( @@ -107,6 +116,7 @@ // Auto-select first matching template for the chosen provider_type const match = cmdTemplateConfigs.find((c) => c.provider_type === form.provider_type); if (match) form.command_template_config_id = match.id; + nameManuallyEdited = false; editing = null; showForm = true; } @@ -142,6 +152,7 @@ rate_limits: { search: cfg.rate_limits?.search ?? 30, default: cfg.rate_limits?.default ?? 10 }, command_template_config_id: cfg.command_template_config_id ?? null, }; + nameManuallyEdited = true; editing = cfg.id; showForm = true; } @@ -165,7 +176,7 @@ await api('/command-configs', { method: 'POST', body }); snackSuccess(t('snack.commandConfigSaved')); } - form = defaultForm(); showForm = false; editing = null; await load(); + form = defaultForm(); nameManuallyEdited = false; showForm = false; editing = null; await load(); } catch (err: any) { error = err.message; snackError(err.message); } finally { submitting = false; } } @@ -214,7 +225,7 @@
form.icon = v} /> - nameManuallyEdited = true} required placeholder={t('commandConfig.namePlaceholder')} class="flex-1 px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
diff --git a/frontend/src/routes/command-template-configs/+page.svelte b/frontend/src/routes/command-template-configs/+page.svelte index 0fa18c5..768e6c2 100644 --- a/frontend/src/routes/command-template-configs/+page.svelte +++ b/frontend/src/routes/command-template-configs/+page.svelte @@ -26,6 +26,7 @@ import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte'; import { highlightFromUrl } from '$lib/highlight'; import { globalProviderFilter } from '$lib/stores/provider-filter.svelte'; + import { getDescriptor } from '$lib/providers'; interface CmdTemplateConfig { id: number; @@ -120,6 +121,14 @@ slots: {} as Record>, }); let form = $state(defaultForm()); + let nameManuallyEdited = $state(false); + + $effect(() => { + if (showForm && !nameManuallyEdited && !editing) { + const desc = getDescriptor(form.provider_type); + form.name = desc ? `${desc.defaultName} Command Templates` : 'Command Templates'; + } + }); // Provider capabilities let allCapabilities = $state>({}); @@ -257,6 +266,7 @@ form = defaultForm(); const typesWithCmdSlots = providerTypes.filter(t => (allCapabilities[t]?.command_slots?.length || 0) > 0); if (typesWithCmdSlots.length > 0) form.provider_type = typesWithCmdSlots[0]; + nameManuallyEdited = false; editing = null; showForm = true; activeLocale = primaryLocale; @@ -280,6 +290,7 @@ icon: c.icon || '', slots: slotsCopy, }; + nameManuallyEdited = true; editing = c.id; showForm = true; activeLocale = primaryLocale; @@ -432,7 +443,7 @@
form.icon = v} /> - nameManuallyEdited = true} required placeholder={t('cmdTemplateConfig.namePlaceholder')} class="flex-1 px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
diff --git a/frontend/src/routes/command-trackers/+page.svelte b/frontend/src/routes/command-trackers/+page.svelte index f78650b..c40f0fb 100644 --- a/frontend/src/routes/command-trackers/+page.svelte +++ b/frontend/src/routes/command-trackers/+page.svelte @@ -61,6 +61,14 @@ enabled: true, }); let form = $state(defaultForm()); + let nameManuallyEdited = $state(false); + + $effect(() => { + if (showForm && !nameManuallyEdited && !editing) { + const provider = providers.find(p => p.id === form.provider_id); + form.name = provider ? `${provider.name} Commands` : 'Commands'; + } + }); // Filter command configs by selected provider's type let filteredConfigs = $derived.by(() => { @@ -110,6 +118,7 @@ const firstCfg = commandConfigs.find(c => c.provider_type === ptype); if (firstCfg) form.command_config_id = firstCfg.id; } + nameManuallyEdited = false; editing = null; showForm = true; } @@ -141,6 +150,7 @@ command_config_id: trk.command_config_id, enabled: trk.enabled, }; + nameManuallyEdited = true; editing = trk.id; showForm = true; } @@ -156,7 +166,7 @@ await api('/command-trackers', { method: 'POST', body }); snackSuccess(t('snack.commandTrackerCreated')); } - form = defaultForm(); showForm = false; editing = null; await load(); + form = defaultForm(); nameManuallyEdited = false; showForm = false; editing = null; await load(); } catch (err: any) { error = err.message; snackError(err.message); } finally { submitting = false; } } @@ -288,7 +298,7 @@
form.icon = v} /> - nameManuallyEdited = true} required placeholder={t('commandTracker.namePlaceholder')} class="flex-1 px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
diff --git a/frontend/src/routes/notification-trackers/+page.svelte b/frontend/src/routes/notification-trackers/+page.svelte index bd7fe12..46f4d2e 100644 --- a/frontend/src/routes/notification-trackers/+page.svelte +++ b/frontend/src/routes/notification-trackers/+page.svelte @@ -70,11 +70,19 @@ filters: {} as Record, }); let form = $state(defaultForm()); + let nameManuallyEdited = $state(false); let selectedProviderType = $derived( providers.find(p => p.id === form.provider_id)?.type || '' ); let error = $state(''); + $effect(() => { + if (showForm && !nameManuallyEdited && !editing) { + const provider = providers.find(p => p.id === form.provider_id); + form.name = provider ? `${provider.name} Tracker` : 'Tracker'; + } + }); + // Linked targets management let expandedTracker = $state(null); let addingTarget = $state>({}); @@ -210,6 +218,7 @@ form = defaultForm(); // Auto-select first provider if any if (providers.length > 0) form.provider_id = providers[0].id; + nameManuallyEdited = false; editing = null; showForm = true; collections = []; users = []; previousCollectionIds = []; } @@ -224,6 +233,7 @@ filters: trk.filters || {}, }; previousCollectionIds = [...(trk.collection_ids || [])]; + nameManuallyEdited = true; editing = trk.id; showForm = true; if (form.provider_id) { await Promise.all([loadCollections(), loadUsers()]); @@ -491,6 +501,7 @@ onsave={save} ontoggleCollection={toggleCollection} {formatDate} + onnameinput={() => nameManuallyEdited = true} /> {/if} diff --git a/frontend/src/routes/notification-trackers/TrackerForm.svelte b/frontend/src/routes/notification-trackers/TrackerForm.svelte index b8ecabc..ca262e1 100644 --- a/frontend/src/routes/notification-trackers/TrackerForm.svelte +++ b/frontend/src/routes/notification-trackers/TrackerForm.svelte @@ -35,6 +35,7 @@ onsave: (e: SubmitEvent) => void; ontoggleCollection?: (collectionId: string) => void; formatDate?: (dateStr: string) => string; + onnameinput?: () => void; } let { @@ -53,6 +54,7 @@ onsave, ontoggleCollection, formatDate, + onnameinput, }: Props = $props(); let descriptor = $derived(getDescriptor(providerType)); @@ -95,7 +97,7 @@
form.icon = v} /> - + onnameinput?.()} required placeholder={t('notificationTracker.namePlaceholder')} class="flex-1 px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
diff --git a/frontend/src/routes/targets/+page.svelte b/frontend/src/routes/targets/+page.svelte index f231f2b..c3f366e 100644 --- a/frontend/src/routes/targets/+page.svelte +++ b/frontend/src/routes/targets/+page.svelte @@ -129,6 +129,7 @@ child_target_ids: [] as number[], }); let form = $state(defaultForm()); + let nameManuallyEdited = $state(false); let error = $state(''); let loaded = $state(false); let submitting = $state(false); @@ -137,6 +138,17 @@ let confirmDelete = $state(null); let formEl = $state(); + const TARGET_TYPE_DEFAULT_NAMES: Record = { + telegram: 'Telegram', webhook: 'Webhook', email: 'Email', + discord: 'Discord', slack: 'Slack', ntfy: 'ntfy', matrix: 'Matrix', + broadcast: 'Broadcast', + }; + $effect(() => { + if (showForm && !nameManuallyEdited && !editing) { + form.name = TARGET_TYPE_DEFAULT_NAMES[formType] ?? ''; + } + }); + async function scrollToForm() { await tick(); formEl?.scrollIntoView({ behavior: 'smooth', block: 'start' }); @@ -213,6 +225,7 @@ if (formType === 'telegram' && telegramBots.length > 0) form.bot_id = telegramBots[0].id; if (formType === 'email' && emailBots.length > 0) form.email_bot_id = emailBots[0].id; if (formType === 'matrix' && matrixBots.length > 0) form.matrix_bot_id = matrixBots[0].id; + nameManuallyEdited = false; editing = null; showTelegramSettings = false; showForm = true; @@ -242,6 +255,7 @@ // broadcast child_target_ids: c.child_target_ids || [], }; + nameManuallyEdited = true; editing = tgt.id; showTelegramSettings = false; showForm = true; @@ -476,6 +490,7 @@ bind:showTelegramSettings onsave={save} ontoggleTelegramSettings={() => showTelegramSettings = !showTelegramSettings} + onnameinput={() => nameManuallyEdited = true} /> {/if} diff --git a/frontend/src/routes/targets/TargetForm.svelte b/frontend/src/routes/targets/TargetForm.svelte index 133d8ba..1276711 100644 --- a/frontend/src/routes/targets/TargetForm.svelte +++ b/frontend/src/routes/targets/TargetForm.svelte @@ -49,6 +49,7 @@ showTelegramSettings: boolean; onsave: (e: SubmitEvent) => void; ontoggleTelegramSettings: () => void; + onnameinput?: () => void; } let { @@ -70,6 +71,7 @@ showTelegramSettings = $bindable(), onsave, ontoggleTelegramSettings, + onnameinput, }: Props = $props(); @@ -87,7 +89,7 @@
form.icon = v} /> - + onnameinput?.()} required placeholder={t('targets.namePlaceholder')} class="flex-1 px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
{#if formType === 'telegram'} diff --git a/frontend/src/routes/template-configs/+page.svelte b/frontend/src/routes/template-configs/+page.svelte index e29e06f..651f9f2 100644 --- a/frontend/src/routes/template-configs/+page.svelte +++ b/frontend/src/routes/template-configs/+page.svelte @@ -28,6 +28,7 @@ import { globalProviderFilter } from '$lib/stores/provider-filter.svelte'; import ErrorBanner from '$lib/components/ErrorBanner.svelte'; import Button from '$lib/components/Button.svelte'; + import { getDescriptor } from '$lib/providers'; import type { TemplateConfig } from '$lib/types'; let allTemplateConfigs = $derived(templateConfigsCache.items); @@ -194,8 +195,16 @@ date_only_format: '%d.%m.%Y', }); let form = $state(defaultForm()); + let nameManuallyEdited = $state(false); let previewTargetType = $state('telegram'); + $effect(() => { + if (showForm && !nameManuallyEdited && !editing) { + const desc = getDescriptor(form.provider_type); + form.name = desc ? `${desc.defaultName} Templates` : 'Templates'; + } + }); + // Provider capabilities: from shared cache let allCapabilities = $derived(capabilitiesCache.items); let providerTypes = $derived(Object.keys(allCapabilities)); @@ -291,6 +300,7 @@ function openNew() { form = defaultForm(); if (providerTypes.length > 0) form.provider_type = providerTypes[0]; + nameManuallyEdited = false; editing = null; showForm = true; activeLocale = primaryLocale; slotPreview = {}; slotErrors = {}; dateFormatPreview = {}; expandedSlots = new Set(); showPreviewFor = new Set(); slotFilter = ''; refreshDateFormatPreview(); } @@ -304,6 +314,7 @@ date_format: c.date_format || '%d.%m.%Y, %H:%M UTC', date_only_format: c.date_only_format || '%d.%m.%Y', }; + nameManuallyEdited = true; editing = c.id; showForm = true; activeLocale = primaryLocale; slotPreview = {}; slotErrors = {}; dateFormatPreview = {}; expandedSlots = new Set(); showPreviewFor = new Set(); slotFilter = ''; @@ -439,7 +450,7 @@
form.icon = v} /> - nameManuallyEdited = true} required placeholder={t('templateConfig.namePlaceholder')} class="flex-1 px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
diff --git a/frontend/src/routes/tracking-configs/+page.svelte b/frontend/src/routes/tracking-configs/+page.svelte index 1c232cb..6cabb1d 100644 --- a/frontend/src/routes/tracking-configs/+page.svelte +++ b/frontend/src/routes/tracking-configs/+page.svelte @@ -190,6 +190,14 @@ }); let form: Record = $state(defaultForm()); let descriptor = $derived(getDescriptor(form.provider_type)); + let nameManuallyEdited = $state(false); + + $effect(() => { + if (showForm && !nameManuallyEdited && !editing) { + const desc = getDescriptor(form.provider_type); + form.name = desc ? `${desc.defaultName} Tracking` : 'Tracking'; + } + }); onMount(() => { topbarAction.set({ @@ -230,9 +238,10 @@ window.history.replaceState(null, '', cleanUrl); } - function openNew() { form = defaultForm(); editing = null; showForm = true; } + function openNew() { form = defaultForm(); nameManuallyEdited = false; editing = null; showForm = true; } function edit(c: any) { form = { ...defaultForm(), ...c }; + nameManuallyEdited = true; editing = c.id; showForm = true; } @@ -288,7 +297,7 @@
form.icon = v} /> - nameManuallyEdited = true} required placeholder={t('trackingConfig.namePlaceholder')} class="flex-1 px-3 py-2 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />