Files
notify-bridge/frontend/src/routes/notification-trackers/TestMenu.svelte
T
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

53 lines
2.1 KiB
Svelte

<script lang="ts">
import { t } from '$lib/i18n';
import MdiIcon from '$lib/components/MdiIcon.svelte';
interface Props {
testMenuOpen: string | null;
testMenuStyle: string;
ttTesting: Record<string, string>;
/**
* When `disabledReason` is set, the button is rendered greyed out with a
* tooltip pointing the user at the missing setting (e.g. "Enable Periodic
* Summary in Tracking Config first"). Clicking is blocked — clicking an
* unconfigured test would have surfaced as a silent server-side skip.
*/
testTypes: { key: string; icon: string; labelKey: string; disabledReason?: string }[];
ontest: (ttId: number, testType: string) => void;
onclose: () => void;
}
let { testMenuOpen, testMenuStyle, ttTesting, testTypes, ontest, onclose }: Props = $props();
</script>
{#if testMenuOpen}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div style="position:fixed; top:0; left:0; right:0; bottom:0; z-index:9998;"
onclick={onclose}
onkeydown={(e) => { if (e.key === 'Escape') onclose(); }}>
</div>
<div style="{testMenuStyle} background:var(--color-card); border:1px solid var(--color-border); border-radius:0.5rem; box-shadow:0 10px 25px rgba(0,0,0,0.3); padding:0.25rem; min-width:12rem;">
{#each testTypes as tt}
{@const busy = !!ttTesting[`${testMenuOpen}_${tt.key}`]}
{@const blocked = !!tt.disabledReason}
<button
onclick={() => { if (!blocked) ontest(Number(testMenuOpen), tt.key); }}
disabled={busy || blocked}
title={blocked ? t(tt.disabledReason!) : ''}
class="flex items-center gap-2 w-full px-3 py-1.5 text-sm rounded hover:bg-[var(--color-muted)] transition-colors disabled:opacity-50 text-left">
<MdiIcon name={tt.icon} size={14} />
{t(tt.labelKey)}
{#if blocked}
<MdiIcon name="mdiLock" size={12} />
{/if}
{#if busy}
<span class="ml-auto text-xs text-[var(--color-muted-foreground)]">...</span>
{/if}
</button>
{#if blocked}
<p class="px-3 pb-1 text-[10px]" style="color: var(--color-muted-foreground);">{t(tt.disabledReason!)}</p>
{/if}
{/each}
</div>
{/if}