feat: NUT (Network UPS Tools) service provider + provider-agnostic UI
Add full NUT support as a polling-based service provider: - Async TCP client for upsd protocol (port 3493, configurable) - 8 event types: online, on_battery, low_battery, battery_restored, comms_lost, comms_restored, replace_battery, overload - 3 bot commands: /status, /devices, /battery - 38 Jinja2 templates (EN+RU notification + command templates) - Database: tracking config fields, migration, seeds - Frontend: provider form with host/port/credentials, grid items, i18n Provider-agnostic UI improvements: - Remove hardcoded 'immich' defaults from all config forms - Dynamic collection labels per provider type (Albums/Repos/Boards/UPS Devices) - Capability-driven test types instead of provider type checks - Template variable helpers for all providers (was Immich-only) - Guard Immich-only shared link check to Immich providers - Auto-clear stale global provider filter from localStorage - EntitySelect search placeholder shows current selection - Fix noneLabel in linked target config selectors New CLAUDE.md rule #8: no provider-specific hardcoding
This commit is contained in:
@@ -46,7 +46,16 @@
|
||||
}: Props = $props();
|
||||
|
||||
let isScheduler = $derived(providerType === 'scheduler');
|
||||
let isWebhook = $derived(providerType === 'gitea');
|
||||
let isWebhook = $derived(providerType === 'gitea' || providerType === 'planka');
|
||||
|
||||
// Collection label/icon/desc per provider type
|
||||
const collectionMeta: Record<string, { label: string; icon: string; placeholder: string; desc: (col: any) => string }> = {
|
||||
immich: { label: t('notificationTracker.albums'), icon: 'mdiImageMultiple', placeholder: t('notificationTracker.selectAlbums'), desc: (col) => `${col.assetCount ?? col.asset_count ?? 0} assets` },
|
||||
gitea: { label: t('notificationTracker.repositories'), icon: 'mdiGit', placeholder: t('notificationTracker.selectRepositories'), desc: () => '' },
|
||||
planka: { label: t('notificationTracker.boards'), icon: 'mdiViewDashboard', placeholder: t('notificationTracker.selectBoards'), desc: () => '' },
|
||||
nut: { label: t('notificationTracker.upsDevices'), icon: 'mdiBatteryCharging80', placeholder: t('notificationTracker.selectUpsDevices'), desc: (col) => col.description || '' },
|
||||
};
|
||||
let colMeta = $derived(collectionMeta[providerType] || { label: t('notificationTracker.albums'), icon: 'mdiServer', placeholder: t('notificationTracker.selectAlbums'), desc: () => '' });
|
||||
|
||||
// Custom variable management for scheduler
|
||||
function addVariable() {
|
||||
@@ -92,16 +101,16 @@
|
||||
</div>
|
||||
{#if !isScheduler && collections.length > 0}
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{t('notificationTracker.albums')}</label>
|
||||
<label class="block text-sm font-medium mb-1">{colMeta.label}</label>
|
||||
<MultiEntitySelect
|
||||
items={collections.map(col => ({
|
||||
value: col.id,
|
||||
label: col.albumName || col.name,
|
||||
icon: 'mdiImageMultiple',
|
||||
desc: `${col.assetCount ?? col.asset_count ?? 0} assets`,
|
||||
icon: colMeta.icon,
|
||||
desc: colMeta.desc(col),
|
||||
}))}
|
||||
bind:values={form.collection_ids}
|
||||
placeholder={t('notificationTracker.selectAlbums')}
|
||||
placeholder={colMeta.placeholder}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user