diff --git a/frontend/src/lib/i18n/en.json b/frontend/src/lib/i18n/en.json index da927d7..08a3843 100644 --- a/frontend/src/lib/i18n/en.json +++ b/frontend/src/lib/i18n/en.json @@ -748,7 +748,11 @@ "syntaxError": "Syntax error", "undefinedVar": "Unknown variable", "line": "line", - "add": "Add" + "add": "Add", + "filterByName": "Filter by name...", + "allTypes": "All types", + "allProviders": "All providers", + "noFilterResults": "No items match the current filter." }, "error": { "notFound": "Page not found", diff --git a/frontend/src/lib/i18n/ru.json b/frontend/src/lib/i18n/ru.json index e304daa..e4e9f1c 100644 --- a/frontend/src/lib/i18n/ru.json +++ b/frontend/src/lib/i18n/ru.json @@ -748,7 +748,11 @@ "syntaxError": "Ошибка синтаксиса", "undefinedVar": "Неизвестная переменная", "line": "строка", - "add": "Добавить" + "add": "Добавить", + "filterByName": "Фильтр по имени...", + "allTypes": "Все типы", + "allProviders": "Все провайдеры", + "noFilterResults": "Нет элементов, соответствующих фильтру." }, "error": { "notFound": "Страница не найдена", diff --git a/frontend/src/routes/command-configs/+page.svelte b/frontend/src/routes/command-configs/+page.svelte index 86a1fde..0e3b57b 100644 --- a/frontend/src/routes/command-configs/+page.svelte +++ b/frontend/src/routes/command-configs/+page.svelte @@ -24,7 +24,13 @@ return tpl?.name || `#${id}`; } - let configs = $derived(commandConfigsCache.items); + let allCmdConfigs = $derived(commandConfigsCache.items); + let filterText = $state(''); + let filterType = $state(''); + let configs = $derived(allCmdConfigs.filter(c => + (!filterText || c.name.toLowerCase().includes(filterText.toLowerCase())) && + (!filterType || c.provider_type === filterType) + )); let cmdTemplateConfigs = $derived(commandTemplateConfigsCache.items); const templateItems = $derived(cmdTemplateConfigs .filter((c: any) => c.provider_type === form.provider_type) @@ -229,10 +235,27 @@ {/if} -{#if configs.length === 0 && !showForm} +{#if !showForm && allCmdConfigs.length > 0} +
+ + +
+{/if} + +{#if allCmdConfigs.length === 0 && !showForm} +{:else if configs.length === 0 && !showForm} + + + {:else}
{#each configs as cfg} diff --git a/frontend/src/routes/command-trackers/+page.svelte b/frontend/src/routes/command-trackers/+page.svelte index b811eb0..bf989a8 100644 --- a/frontend/src/routes/command-trackers/+page.svelte +++ b/frontend/src/routes/command-trackers/+page.svelte @@ -18,7 +18,13 @@ import { highlightFromUrl } from '$lib/highlight'; import type { ServiceProvider, TelegramBot } from '$lib/types'; - let trackers = $state([]); + let allCmdTrackers = $state([]); + let filterText = $state(''); + let filterProviderId = $state(0); + let trackers = $derived(allCmdTrackers.filter((t: any) => + (!filterText || t.name.toLowerCase().includes(filterText.toLowerCase())) && + (!filterProviderId || t.provider_id === filterProviderId) + )); let providers = $derived(providersCache.items); let commandConfigs = $derived(commandConfigsCache.items); let telegramBots = $derived(telegramBotsCache.items); @@ -60,7 +66,7 @@ onMount(load); async function load() { try { - [trackers] = await Promise.all([ + [allCmdTrackers] = await Promise.all([ api('/command-trackers'), providersCache.fetch(), commandConfigsCache.fetch(), telegramBotsCache.fetch(), @@ -212,10 +218,28 @@ {/if} -{#if trackers.length === 0 && !showForm} +{#if !showForm && allCmdTrackers.length > 0} +
+ + +
+{/if} + +{#if allCmdTrackers.length === 0 && !showForm} +{:else if trackers.length === 0 && !showForm} + + + {:else}
{#each trackers as trk} diff --git a/frontend/src/routes/notification-trackers/+page.svelte b/frontend/src/routes/notification-trackers/+page.svelte index c487f74..ab9d659 100644 --- a/frontend/src/routes/notification-trackers/+page.svelte +++ b/frontend/src/routes/notification-trackers/+page.svelte @@ -22,7 +22,13 @@ let loaded = $state(false); let loadError = $state(''); - let notificationTrackers = $state([]); + let allNotificationTrackers = $state([]); + let filterText = $state(''); + let filterProviderId = $state(0); + let notificationTrackers = $derived(allNotificationTrackers.filter(t => + (!filterText || t.name.toLowerCase().includes(filterText.toLowerCase())) && + (!filterProviderId || t.provider_id === filterProviderId) + )); let providers = $derived(providersCache.items); const providerItems = $derived(providers.map(p => ({ value: p.id, label: p.name, icon: p.icon || 'mdiServer', desc: p.type }))); let targets = $derived(targetsCache.items); @@ -91,7 +97,7 @@ async function load() { loadError = ''; try { - [notificationTrackers] = await Promise.all([ + [allNotificationTrackers] = await Promise.all([ api('/notification-trackers'), providersCache.fetch(), targetsCache.fetch(), trackingConfigsCache.fetch(), templateConfigsCache.fetch(), @@ -366,10 +372,29 @@ {/if} {#if loaded && !loadError} -{#if notificationTrackers.length === 0 && !showForm} + +{#if !showForm && allNotificationTrackers.length > 0} +
+ + +
+{/if} + +{#if allNotificationTrackers.length === 0 && !showForm} +{:else if notificationTrackers.length === 0 && !showForm} + + + {:else if !showForm}
{#each notificationTrackers as tracker} diff --git a/frontend/src/routes/targets/+page.svelte b/frontend/src/routes/targets/+page.svelte index b69ed78..906196f 100644 --- a/frontend/src/routes/targets/+page.svelte +++ b/frontend/src/routes/targets/+page.svelte @@ -93,7 +93,11 @@ let allTargets = $derived(targetsCache.items); let activeType = $derived(page.url.searchParams.get('type') as TargetType | null); - let targets = $derived(activeType ? allTargets.filter(t => t.type === activeType) : allTargets); + let filterText = $state(''); + let targets = $derived(allTargets.filter(t => + (!activeType || t.type === activeType) && + (!filterText || t.name.toLowerCase().includes(filterText.toLowerCase())) + )); let telegramBots = $derived(telegramBotsCache.items); let emailBots = $derived(emailBotsCache.items); let matrixBots = $derived(matrixBotsCache.items); @@ -479,10 +483,21 @@
{/if} -{#if targets.length === 0 && !showForm} +{#if !showForm && allTargets.length > 0} +
+ +
+{/if} + +{#if allTargets.length === 0 && !showForm} +{:else if targets.length === 0 && !showForm} + + + {:else}
{#each targets as target (target.id)} @@ -547,11 +562,14 @@
{#if target.type === 'telegram'} {@const botId = target.config?.bot_id} + {@const existingKeys = new Set((target.receivers || []).map((r: TargetReceiver) => r.receiver_key))} {@const chatItems = (receiverBotChats[botId] || []).map((c: TelegramChat) => ({ value: c.chat_id, label: c.title || c.username || c.chat_id, icon: c.type === 'private' ? 'mdiAccount' : c.type === 'channel' ? 'mdiBullhorn' : 'mdiAccountGroup', desc: `${c.type} · ${c.chat_id}`, + disabled: existingKeys.has(c.chat_id), + disabledHint: existingKeys.has(c.chat_id) ? t('targets.alreadyAdded') : undefined, }))} {#if chatItems.length > 0} diff --git a/frontend/src/routes/template-configs/+page.svelte b/frontend/src/routes/template-configs/+page.svelte index 9a17f46..dea0dcb 100644 --- a/frontend/src/routes/template-configs/+page.svelte +++ b/frontend/src/routes/template-configs/+page.svelte @@ -21,7 +21,13 @@ import { highlightFromUrl } from '$lib/highlight'; import type { TemplateConfig } from '$lib/types'; - let configs = $derived(templateConfigsCache.items); + let allTemplateConfigs = $derived(templateConfigsCache.items); + let filterText = $state(''); + let filterType = $state(''); + let configs = $derived(allTemplateConfigs.filter(c => + (!filterText || c.name.toLowerCase().includes(filterText.toLowerCase())) && + (!filterType || c.provider_type === filterType) + )); let loaded = $state(false); let varsRef = $state>({}); let showVarsFor = $state(null); @@ -337,10 +343,28 @@
{/if} -{#if configs.length === 0 && !showForm} +{#if !showForm && allTemplateConfigs.length > 0} +
+ + +
+{/if} + +{#if allTemplateConfigs.length === 0 && !showForm} +{:else if configs.length === 0 && !showForm} + + + {:else}
{#each configs as config} diff --git a/frontend/src/routes/tracking-configs/+page.svelte b/frontend/src/routes/tracking-configs/+page.svelte index 5debc22..dd2c2aa 100644 --- a/frontend/src/routes/tracking-configs/+page.svelte +++ b/frontend/src/routes/tracking-configs/+page.svelte @@ -19,7 +19,13 @@ import { highlightFromUrl } from '$lib/highlight'; import type { TrackingConfig } from '$lib/types'; - let configs = $derived(trackingConfigsCache.items); + let allConfigs = $derived(trackingConfigsCache.items); + let filterText = $state(''); + let filterType = $state(''); + let configs = $derived(allConfigs.filter(c => + (!filterText || c.name.toLowerCase().includes(filterText.toLowerCase())) && + (!filterType || c.provider_type === filterType) + )); let loaded = $state(false); let showForm = $state(false); let editing = $state(null); @@ -228,10 +234,28 @@
{/if} -{#if configs.length === 0 && !showForm} +{#if !showForm && allConfigs.length > 0} +
+ + +
+{/if} + +{#if allConfigs.length === 0 && !showForm} +{:else if configs.length === 0 && !showForm} + + + {:else}
{#each configs as config} diff --git a/packages/server/src/notify_bridge_server/api/status.py b/packages/server/src/notify_bridge_server/api/status.py index 229085e..dbf89e6 100644 --- a/packages/server/src/notify_bridge_server/api/status.py +++ b/packages/server/src/notify_bridge_server/api/status.py @@ -132,10 +132,12 @@ async def get_nav_counts( )).one() counts[key] = count - # System-owned templates (user_id=0) count as well + # System-owned entities (user_id=0) count as well for model, key in [ (TemplateConfig, "template_configs"), (CommandTemplateConfig, "command_template_configs"), + (TrackingConfig, "tracking_configs"), + (CommandConfig, "command_configs"), ]: system_count = (await session.exec( select(func.count()).select_from(model).where(model.user_id == 0)