Commit Graph

26 Commits

Author SHA1 Message Date
alexei.dolgolyov 5bd63a2191 feat(frontend): autogenerate entity names from type/provider
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 → "<Type> Bot"; targets → type label; notification
trackers → "<provider> Tracker"; command trackers → "<provider>
Commands"; actions → "<provider> <Action Type>"; tracking/template/
command/command-template configs → "<descriptor.defaultName>
<Suffix>". TargetForm and TrackerForm grew an optional onnameinput
prop so parents can flag manual edits in subform inputs.
2026-05-07 13:01:52 +03:00
alexei.dolgolyov 711f218622 fix(redesign): a11y, mobile, perf polish for production push
Comprehensive pre-production sweep across the Aurora redesign — drives
svelte-check to 0 errors / 0 warnings (was 61) without changing visual
intent. Highlights:

- Mobile: hero title shrinks at 480px, signal-list stacks timestamp
  under sentence below 640px, sidebar icon buttons bumped to 40x40
- Light theme: muted-foreground darkened to #3a3560 to clear WCAG AA
  on glass surfaces and the modal close button
- Perf: topbar backdrop-filter 28→14px, mobile-more sheet 28→12px to
  cut concurrent blur layers on mid-tier mobile
- a11y: prefers-reduced-motion mute for aurora drift / pulses /
  shimmer / stagger; aria-label on every icon-only button;
  aria-describedby on Hint; combobox/listbox/aria-activedescendant on
  SearchPalette; modal dialog tabindex; 47 label-without-control
  warnings across 14 form pages cleaned up via for=/id= or label→div
- Dashboard derived state split into topology- vs status-bound layers
  so polling no longer re-runs the full provider/wires computation
- Mobile bottom nav derived from baseNavEntries by key lookup so
  adding a top-level nav entry keeps the two trees in sync
- Bug: template-configs page now respects the global provider filter
  for both the count meter and the type pill (was reading the
  unfiltered cache)
- Misc: portal EventChart tooltip and switch its swatches to Aurora
  tokens; CollapsibleSlot warning state uses warning-fg/-bg tokens
  instead of #d97706; Hint z-index 99999→9999; element refs across
  Modal/EntitySelect/MultiEntitySelect/SearchPalette/IconGridSelect/
  Hint/targets converted to \$state for reactivity; 4 dead
  .topbar-cta selectors removed
2026-04-25 14:41:12 +03:00
alexei.dolgolyov d662b50925 feat(redesign): roll subpage hero across all pages + Aurora Button + JinjaEditor + pulse fix
Big batch — every secondary page now wears the same glass-card hero
that landed on Providers earlier:

- notification-trackers, tracking-configs, template-configs
- command-trackers, command-configs, command-template-configs
- targets (with active-tab title), actions
- bots (telegram / email / matrix tabs)
- settings, settings/backup, users

Each page picks an italic-em emphasis word, an editorial crumb (e.g.
'Routing · Notification', 'Operators · Bots', 'System · Maintenance'),
a count meter, and entity-specific status pills derived from live
data: 'X armed / Y paused' for trackers and actions, 'X types' for
configs/templates, 'X channels' or '$N receivers' for targets.

Other changes in this commit:

- Button.svelte: redesigned. Primary variant becomes a real Aurora
  CTA — gradient lavender → orchid pill, 40px tall md / 34px sm,
  inset highlight, lift + glow on hover. Secondary, danger, ghost
  variants reworked to match. The 'Add <Type>' button on every page
  now reads as the page's primary action instead of a flat lavender
  rectangle.
- JinjaEditor: overrode oneDark's hardcoded background with
  !important so the editor surface picks up var(--color-input-bg).
  Gutters / scroller / selection / autocomplete tooltip all match
  Aurora glass tokens now. Template editors stop visually clashing
  with the surrounding panel.
- Aurora pulse dot: rewritten as a self-contained box-shadow glow
  pulse (no transform, no pseudo-element). The dot's bounding box is
  now stable so ancestors with overflow:hidden can never clip the
  visible dot — only the (decorative) outer glow halo. Fixes the
  'half-moon clipping' on the dashboard 'On watch' deck.
- topbar-action.svelte.ts left in tree but unused (topbar CTA was
  reverted per your call). Will clean up in a later commit.
- Form input baseline styling moved into app.css (rounded 0.625rem,
  glass background, hover/focus rings) so untouched filter inputs
  on the per-type pages stop looking out of place.

i18n: emphasis / countLabel / armed / paused / receiver / receivers
/ channelsCount keys added across en + ru.

Build clean: 0 errors, 61 pre-existing a11y warnings unchanged.
2026-04-25 02:52:01 +03:00
alexei.dolgolyov ab621b6abc feat: wire tracking-config display filters + per-tracker adaptive polling
Display filters (Immich tracking config):
- favorites_only drops events with no favorited new assets, or filters
  added_assets to favorites only
- assets_order_by/assets_order sort the rendered list
  (date / name / rating / random / none)
- max_assets_to_show caps rendered+attached media (default 5 -> 10)
- include_tags strips people from event extras and tags from each asset
- include_asset_details strips city/country/state/lat/lon/is_favorite/
  rating/description; load-bearing fields (thumbhash, file_size,
  playback_size, cache keys) preserved
- New apply_tracking_display_filters helper in dispatch_helpers; wired
  into watcher, webhooks, scheduled/periodic/memory, and manual
  test-dispatch
- Targets sharing a TrackingConfig dispatch together; targets with
  different TCs each see their own shaped event

Adaptive polling:
- Replace NotificationTracker.batch_duration with adaptive_max_skip
- Per-tracker opt-in: NULL/0 disables back-off (every tick runs);
  positive N caps the skip factor at (N-1)-in-N after long idle
- Scheduler caches the cap in module state for the tick fast-path
- Migration adds the new column; API schemas/responses, frontend types,
  i18n, and the tracker form updated to match
2026-04-24 21:12:10 +03:00
alexei.dolgolyov b61394f057 feat(immich): per-album scheduled/memory dispatch + template tooling
Dispatch: honor {kind}_collection_mode on TrackingConfig — "per_collection"
fans out one event per album; "combined" pools assets as before. Extract
build_immich_dispatch_events shared by cron and test paths.

Assets: collect_scheduled_assets attaches album_name/album_url/album_public_url
to each asset so combined-mode templates can attribute rows to their source
album. Default scheduled_assets templates render a multi-album header with
inline album list and per-row album link; memory_mode follows the same pattern.

UI: "Reset to default" buttons on notification and command template slots
(per-slot and whole-template), backed by new GET /*-template-configs/defaults
endpoints. tracking-configs "Preview template" now opens an inline preview
modal with locale tabs instead of navigating away; Edit button deep-links
with ?edit_slot=<name> so the destination auto-opens the config and scrolls
to the slot. Reset confirmations use ConfirmModal instead of window.confirm.

Fixes:
* NotificationDispatcher._session_ctx infinite recursion when no shared
  aiohttp.ClientSession was passed — broke test dispatch for periodic/
  scheduled/memory (cron path was unaffected).
* telegram-bots /chats/{id}/test now resolves chat.language_override /
  language_code instead of using the raw ?locale query param, matching
  the resolution the tracker-target test endpoint already used.
* scheduled_assets default template no longer emits a blank line between
  header and the first asset when the multi-album branch is taken.
2026-04-24 19:15:54 +03:00
alexei.dolgolyov 6c3dd67c1b feat(tracking): per-config quiet hours with app-level IANA timezone
Add quiet_hours_enabled/start/end to TrackingConfig (HH:MM strings
interpreted in the app-level timezone AppSetting). The dispatch path
loads the app timezone once per run and passes it through
event_allowed_by_config -> in_quiet_hours, so overnight windows like
22:00-07:00 work correctly in any IANA tz.

Frontend exposes a Timezone field under Settings and a Quiet Hours
section on the Immich tracking-config form with time-picker inputs.
2026-04-22 02:31:48 +03:00
alexei.dolgolyov a7a2b4efa4 feat: large polish pass — UX fixes, per-chat scope, restore/backup, action events
Backend
- Per-chat album scope for Immich commands (search/latest/memory/...): new
  allowed_album_ids on CommandTrackerListener, threaded listener/page kwargs
  through ProviderCommandHandler.handle; PATCH listener-scope endpoint.
- /search and /find accept a trailing page number; Immich client search_smart
  / search_metadata take a page param.
- Immich person-asset lookup switched from removed GET /api/people/{id}/assets
  to POST /api/search/metadata with personIds (fixes /person command and
  auto_organize rules silently returning zero candidates on Immich 1.106+).
- Auto_organize rule now sets the target album's thumbnail to the first added
  image when missing (falls back to any asset type); failures do not fail the
  rule. add_assets_to_album surfaces the Immich error body on non-2xx.
- EventLog.user_id / action_id / action_name columns with defensive migration
  + backfill. Status query filters by user_id directly; Immich/webhook paths
  emit user_id explicitly. action_runner writes an action_success/partial/
  failed event on each non-dry-run.
- Dashboard DELETE /api/status/events (scoped to user_id) + rendering live
  tracker/provider/action names via FK join with snapshot fallback.
- PATCH /api/users/{id} for username/role change with last-admin guard.
- Deletion protection returns structured {message, entity, blocked_by}
  (ApiError carries .blockedBy; frontend opens BlockedByModal).
- Backup prepare-restore → AppSetting markers + atomic write of
  pending_restore.json; lifespan hook applies on next startup and archives
  under data/applied_restores/. apply-restart sends SIGTERM so the lifespan
  shutdown runs; NOTIFY_BRIDGE_SUPERVISED env override gates the button.
  Manual POST /api/backup/files (same format as scheduled).
- New periodic-summary test path reuses shared collect_scheduled_assets
  (limit=0) so test and future production code go through one primitive.
- Per-receiver locale for Telegram test messages (resolves
  TelegramChat.language_override per chat instead of applying the first
  receiver's locale to everyone).
- Bounded concurrency (semaphores) in NotificationDispatcher._preload_asset_data
  and _refresh_telegram_chat_titles; chat title sweep extended to 24h since
  save_chat_from_webhook covers active chats opportunistically.
- Telegram poller detects the \"webhook is active\" 409 and auto-calls
  deleteWebhook for bots whose DB update_mode is polling (throttled per bot).
- TelegramClient.get_chat added (CLAUDE.md rule 6); set_album_thumbnail added.
- Seeds: rename \"Default Commands\" → \"Default Immich Commands\";
  track_assets_removed default False.

Frontend
- Global provider selector visible when there is only one provider.
- Clear-events button + i18n + ConfirmModal on the dashboard; new icons/
  labels/filters/colors for action_success / action_partial / action_failed.
- Auto-select first available tracking/template/command/config + bot on
  create forms (trackers, command-trackers, targets, template/command
  configs).
- Telegram target disable_url_preview defaults to true.
- BlockedByModal wired into 8 deletion flows; fetchAuth helper for
  multipart/binary calls (reuses api()'s refresh + ApiError mapping).
- Immich tracker 'Checking links' parallelised (concurrency cap 6).
- Backup page: pending-restore banner + Apply-now / Apply-later modal,
  restarting overlay polling /api/health, manual 'Create backup' button.
- Command-trackers listener row gets an 'Edit album scope' modal with
  inherit/explicit multiselect.
- Users page: Edit user modal (username + role).
- parseDate helper for consistent UTC date rendering.

Migrations / schema
- event_log: + user_id, action_id, action_name (+ backfill user_id from
  notification_tracker).
- command_tracker_listener: + allowed_album_ids.
2026-04-22 01:13:11 +03:00
alexei.dolgolyov 6e51164f8e refactor: comprehensive consistency review — UI/UX, code quality, functional parity
Fix 19 issues across 3 priority tiers found during full-codebase review:

CRITICAL:
- Fix undefined --color-secondary CSS variable causing invisible UI elements
- Fix Google Photos command templates using nonexistent asset.originalFileName
- Fix scheduler template variable docs (tracker_name → schedule_name)
- Add missing admin guard on notification template update endpoint

HIGH:
- Fix 5 hardcoded English strings missing i18n (MultiEntitySelect, actions,
  settings, TelegramBotTab, users)
- Replace 17 raw <button> elements with shared <Button> component
- Replace 5 raw error divs with shared <ErrorBanner> component
- Refactor webhook handler duplication into shared _dispatch_webhook_event()
- Add 30+ provider-specific fields to TrackingConfig TypeScript type
- Add default TrackingConfig seeds for immich and google_photos
- Add provider-specific command variable docs (Gitea, Planka, NUT, GP, Webhook)

MEDIUM:
- Replace hardcoded hex colors and Tailwind classes with CSS variable tokens
- Remove dead code (unused imports, orphaned check_notification_tracker)
- Fix Svelte 5 patterns ($state for _prevProviderId, remove unnecessary as any)
- Fix inconsistent POST response shape (targets now returns full response)
- Fix Google Photos template dead asset.year branches, clarify album_url docs
2026-03-31 23:27:35 +03:00
alexei.dolgolyov 1a8c95e942 refactor: replace favorites checkbox with toggle switch in grid layout
Move "Favorites only" from a separate checkboxes array into the regular
fields grid as a toggle switch, aligning Scheduled Assets and Memory Mode
sections visually. Memory source moved to last position.
2026-03-24 17:26:02 +03:00
alexei.dolgolyov 8cb836e16c refactor: provider descriptor registry — eliminate provider-specific hardcoding
Replace all if/else chains keyed on provider type strings with a
descriptor-driven architecture. Each provider type (immich, gitea,
planka, scheduler, nut, google_photos) has a descriptor in
frontend/src/lib/providers/ that declares config fields, event
tracking fields, collection metadata, validation, and hooks.

Components now use getDescriptor(type) and render dynamically.
Dashboard provider card shows provider name + type when global
filter is active. Grid-items derived from registry.
2026-03-24 12:40:33 +03:00
alexei.dolgolyov 2cc4bf699a fix: NUT template preview + tracking config event checkboxes
- Add NUT variables to _SAMPLE_CONTEXT (fixes ups_name undefined in preview)
- Add NUT event tracking checkboxes to tracking config form
- Add NUT event i18n keys (EN + RU)
2026-03-24 00:09:11 +03:00
alexei.dolgolyov 68ac13b452 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
2026-03-23 23:23:58 +03:00
alexei.dolgolyov 37388c430c feat: locale-aware notification templates + UX improvements
- Add locale support to notification templates (matching command template
  pattern): TemplateSlot now has locale field with (config_id, slot_name,
  locale) uniqueness, nested API format {slot: {locale: template}}
- Migration merges separate EN/RU system configs into unified per-provider
  configs; seeds create one config per provider with multi-locale slots
- Locale-aware dispatch with EN fallback in NotificationDispatcher
- Frontend locale tabs (EN/RU) on template config editor
- Fix tracking config cards not showing default provider icons
- Global provider filter, search palette, and various UX polish
2026-03-23 19:08:48 +03:00
alexei.dolgolyov 0fde3c6b3d feat: add Planka service provider with full notification and command support
Webhook-based provider for Planka (self-hosted Kanban board) with:
- 15 event types (cards, boards, lists, comments, tasks, attachments, labels)
- Bearer token webhook authentication
- Async API client for boards/cards/lists
- 30 notification templates (en/ru) + 26 command templates (en/ru)
- Bot commands: /status, /boards, /cards, /lists
- Default tracking config, template config, command config seeded on startup
- DB migration for 15 new tracking_config columns
- Frontend: provider config UI with auto-name, Planka-specific hints
- Frontend: tracking config event toggles for all 15 Planka events
2026-03-23 15:54:00 +03:00
alexei.dolgolyov 31584c5d31 feat: consistent IconGridSelect sizing + descriptions + filter upgrades
- Added desc text to all 40+ grid items (EN + RU)
- compact prop on all IconGridSelect in compact form sections
- Fixed compact width to fill grid cells (removed width:auto)
- Replaced <select> filter dropdowns with IconGridSelect on config pages
- Replaced <select> provider filters with EntitySelect on tracker pages
- Dashboard filters constrained to fixed widths (not full row)
- Added filtering to command-template-configs and providers pages
- providerTypeFilterItems() with "All" option for filter contexts
2026-03-23 01:05:59 +03:00
alexei.dolgolyov 7cbba9d3fd feat: add filtering to all entity list pages
- Tracking configs: filter by name + provider type
- Template configs: filter by name + provider type
- Command configs: filter by name + provider type
- Notification trackers: filter by name + provider
- Command trackers: filter by name + provider
- Targets: filter by name (type filtering already existed)
- Nav badge counts include system-owned entities (user_id=0)
- Shows "no items match filter" vs "no items yet" empty states
2026-03-22 20:22:53 +03:00
alexei.dolgolyov 0562f78b35 feat: add Scheduler provider + multi-provider UX fixes
Scheduler provider:
- Virtual provider (no external service) that emits SCHEDULED_MESSAGE
  events on user-defined intervals or cron expressions
- Custom variables stored in tracker filters, flattened into template context
- fire_count persists across triggers via tracker state
- APScheduler CronTrigger support for cron-mode schedules
- Default templates (EN+RU), seeded on startup

Multi-provider UX fixes:
- Tracking config hides Immich-specific sections (periodic, scheduled,
  memory, asset display) for non-Immich providers
- Command config driven by provider capabilities — hides commands/settings
  for providers without bot commands
- Template config hides empty "Scheduled Messages" group
- Test menu on tracker targets is provider-aware (Immich shows all 4 test
  types, others show only basic)
- Removed redundant Test button from tracker card
- System-owned tracking configs (user_id=0) seeded for Gitea + Scheduler
- Fixed ownership checks to allow system configs in tracker-target links
- Capabilities cache shared across template-configs and command-configs
- Command tracker bot selector uses EntitySelect instead of raw select
- Sample context includes Gitea + Scheduler variables for template preview
2026-03-22 15:50:51 +03:00
alexei.dolgolyov 6d28cfb8d8 feat: add Gitea as webhook-based service provider
First webhook-based provider integration (Immich uses polling).
Gitea pushes events via POST /api/webhooks/gitea/{provider_id} with
HMAC-SHA256 signature validation.

- 9 event types: push, issue opened/closed/commented, PR opened/closed/merged/commented, release published
- Generic filters system on NotificationTracker (collections, senders, exclude_senders)
- Provider capabilities include supported_filters and webhook_based flag
- Gitea API client for connection testing and repository listing
- 18 default Jinja2 notification templates (EN + RU)
- Frontend: conditional provider forms, Gitea event toggles in tracking config
- Auto-migration for filters column and Gitea tracking flags
2026-03-22 12:58:35 +03:00
alexei.dolgolyov a9bb912c30 feat: replace all select dropdowns with IconGridSelect, fix EN template seed
Shared grid-items.ts with reusable item definitions for: sort by/order,
album mode, asset type, memory source, locale, response mode, event type
filter, chat action, preview target type, provider type.

Replaced selects on:
- Dashboard: event type, provider, sort (compact mode — auto-width)
- Tracking configs: sort by/order, album modes, asset types, memory source
- Command configs: locale, response mode, provider type
- Targets: chat action
- Template configs: preview target type, provider type
- Command template configs: provider type
- Providers: type selector (read-only during edit)

IconGridSelect: added compact prop for inline filter bars (auto-width,
smaller padding, shows icon + label text).

Backend: template seed now re-creates deleted system templates on startup
using raw SQL to handle legacy NOT NULL columns.

Added i18n: trackingConfig.providerType, trackingConfig.sortRandom
Added provider_type badge to tracking config cards.
2026-03-22 01:17:06 +03:00
alexei.dolgolyov 9d3abd9fa0 feat: add provider type selector to tracking-configs, use IconGridSelect everywhere
- Tracking Configs: add provider type field (was missing entirely)
- All 4 config pages: provider type uses IconGridSelect during creation,
  shown as read-only text during editing (provider type is immutable)
- Pages: tracking-configs, command-configs, template-configs,
  command-template-configs
2026-03-22 00:27:47 +03:00
alexei.dolgolyov f0f49db21e feat: card highlight system for cross-entity navigation
When clicking a CrossLink, the target entity ID is passed as
?highlight=<id> in the URL. The destination page:
1. Shows a semi-transparent dim overlay (z-index: 10)
2. Finds the card with data-entity-id matching the ID
3. Scrolls to it smoothly (block: center)
4. Applies a pulsing primary-color box-shadow animation (z-index: 11)
5. Cleans up overlay + animation after 2 seconds

If the card isn't in DOM yet (data still loading), a MutationObserver
waits up to 5 seconds for it to appear.

Changes:
- New highlight.ts utility with highlightFromUrl(), MutationObserver,
  dim overlay management
- Card component accepts entityId prop → data-entity-id attribute
- CrossLink accepts entityId prop → appends ?highlight=<id> to href
- All 9 entity pages: Card elements have entityId, highlightFromUrl()
  called after data loads
- CSS: cardHighlight keyframe animation + nav-dim-overlay styles
2026-03-21 23:59:25 +03:00
alexei.dolgolyov 563716fa76 feat: entity cache system, nav UX improvements, split CLAUDE.md
- Add $state-based entity cache layer with 30s TTL, request deduplication,
  and local mutation helpers (entity-cache.svelte.ts + caches.svelte.ts)
- Wire all 10 page components to use shared caches for cross-page data
- Add slide animation for nav tree expand/collapse with rotating chevron
- Remove aggregate count badges from container nav nodes (keep on leaves)
- Convert Targets from flat leaf to group with per-type children
  (Telegram, Webhook, Email, Discord, Slack, ntfy, Matrix)
- Add URL-based type filtering on Targets page with per-type descriptions
- Add Bots group children for Email and Matrix alongside Telegram
- Tab-based routing for bots page (?tab=telegram/email/matrix)
- Add per-type target counts and email/matrix bot counts to /status/counts
- Split CLAUDE.md into focused context files under .claude/docs/
- Fix .gitignore: scope lib/ to root, allow .claude/docs/ tracking
- Clear all caches on logout
- Reset form state when switching target type tabs
2026-03-21 23:35:50 +03:00
alexei.dolgolyov 03ec9b3c86 feat: telegram commands, app settings, bot polling, webhook handling, UI improvements
Adds telegram bot command system with 13 commands (search, latest, random, etc.),
webhook/polling handlers, rate limiting, app settings page, and various UI/UX
improvements across all entity pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 23:11:42 +03:00
alexei.dolgolyov 91e5cd58e9 fix: comprehensive API/UI review — 26 bug fixes and improvements
Backend:
- Scheduler lifecycle sync: create/update/delete tracker now syncs APScheduler jobs
- Test-periodic/test-memory endpoints render actual Jinja2 templates with sample data
- Cascade cleanup on tracker delete (TrackerState removed, EventLog nullified)
- Fix user_id=0 FK violation for system-owned TemplateConfig (removed FK constraint)
- Fix API key leak: only attach x-api-key header for internal provider URLs
- Validate config ownership in tracker_targets create/update
- Fix _response() double-emit of created_at in template/tracking configs
- Add per-target-link test endpoints (test, test-periodic, test-memory)

Frontend:
- Fix orphaned provider on test exception in providers/new
- Add submitting guard + disabled state to targets save button
- Move test buttons from tracker card to per-target-link rows
- Fix Svelte 5 async $state reactivity (spread reassignment for all Record mutations)
- i18n for dashboard timeAgo and event type badges (EN + RU)
- Add required attribute to chat select dropdown in targets
- Fix font CSS vars to prioritize imported DM Sans / JetBrains Mono
- Standardize empty states with centered icon + text across all 6 list pages
- Add stagger-children animation class to all list containers
- Fix slide transition duration consistency (200ms everywhere)
- Standardize border-radius to rounded-md across all form inputs
- Fix providers/new page structure (h2 + mb-8 spacing)
- Fix tracker card action row overflow (flex-wrap justify-end)
- JinjaEditor dark mode reactivity (recreate editor on theme change)
- Add aria-labels to mobile nav items
- Make ConfirmModal confirm button label/icon configurable
- Remove double error reporting on providers page
- Add telegram bot edit functionality (name editing via PUT)
- i18n for External Domain label on provider forms

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:26:20 +03:00
alexei.dolgolyov 9eec21a5b2 feat: port full CRUD API routes and frontend pages from Immich Watcher
Backend API (38 routes):
- providers: full CRUD + test connection + list collections + API key masking
- trackers: full CRUD + trigger + history + test-periodic/memory
- tracking-configs: full CRUD with Pydantic models, provider_type filter
- template-configs: full CRUD + preview + preview-raw with two-pass validation
- targets: full CRUD + test notification + config masking
- telegram-bots: full CRUD + chat discovery + token endpoint
- users: full admin CRUD + password reset + self-delete protection
- status: dashboard endpoint with providers/trackers/targets/events counts

Frontend pages updated:
- Dashboard with animated stat cards and event timeline
- Providers with proper components, delete confirm, snackbar
- Trackers/targets/tracking-configs/template-configs/telegram-bots/users
  all use PageHeader, Card, Loading, MdiIcon with correct i18n keys

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 00:49:40 +03:00
alexei.dolgolyov 9dfd1b79cd feat(notify-bridge): phase 7 - frontend restructuring
Notify Bridge frontend with SvelteKit 2 + Svelte 5 + Tailwind CSS v4:
- Auth: login page, setup page, auth state management with $state runes
- Theme: dark/light toggle with localStorage persistence
- i18n: EN/RU translations with reactive $state-based t() function
- Routes: dashboard, providers, trackers, targets, tracking-configs,
  template-configs, telegram-bots, users (stubs for configs pages)
- Providers page: list with card grid, "Add Provider" button
- API client: JWT auth, auto-redirect on 401, typed request helpers
- Branding: "Notify Bridge" throughout, Observatory theme colors

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 23:53:37 +03:00