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:
@@ -85,12 +85,12 @@
|
||||
<div class="flex items-center gap-2 flex-wrap justify-end">
|
||||
<div class="min-w-[140px]">
|
||||
<EntitySelect items={trackingConfigItems} value={tt.tracking_config_id}
|
||||
placeholder={t('trackingConfig.title')} size="sm" allowNone noneLabel={t('trackingConfig.title')}
|
||||
placeholder={t('trackingConfig.title')} size="sm" allowNone noneLabel={t('common.noneDefault')}
|
||||
onselect={(v) => onupdateLink(tt, 'tracking_config_id', Number(v) || null)} />
|
||||
</div>
|
||||
<div class="min-w-[140px]">
|
||||
<EntitySelect items={templateConfigItems} value={tt.template_config_id}
|
||||
placeholder={t('templateConfig.title')} size="sm" allowNone noneLabel={t('templateConfig.title')}
|
||||
placeholder={t('templateConfig.title')} size="sm" allowNone noneLabel={t('common.noneDefault')}
|
||||
onselect={(v) => onupdateLink(tt, 'template_config_id', Number(v) || null)} />
|
||||
</div>
|
||||
<div class="relative">
|
||||
@@ -118,12 +118,12 @@
|
||||
</div>
|
||||
<div class="min-w-[140px]">
|
||||
<EntitySelect items={trackingConfigItems} value={newLinkTrackingConfigId || null}
|
||||
placeholder={t('trackingConfig.title')} size="sm" allowNone noneLabel={t('trackingConfig.title')}
|
||||
placeholder={t('trackingConfig.title')} size="sm" allowNone noneLabel={t('common.noneDefault')}
|
||||
onselect={(v) => onchangeNewTrackingConfig(Number(v) || 0)} />
|
||||
</div>
|
||||
<div class="min-w-[140px]">
|
||||
<EntitySelect items={templateConfigItems} value={newLinkTemplateConfigId || null}
|
||||
placeholder={t('templateConfig.title')} size="sm" allowNone noneLabel={t('templateConfig.title')}
|
||||
placeholder={t('templateConfig.title')} size="sm" allowNone noneLabel={t('common.noneDefault')}
|
||||
onselect={(v) => onchangeNewTemplateConfig(Number(v) || 0)} />
|
||||
</div>
|
||||
<button onclick={onaddLink}
|
||||
|
||||
Reference in New Issue
Block a user