diff --git a/docs/backend/upgrades.md b/docs/backend/upgrades.md index ef6595bb..330a3e51 100644 --- a/docs/backend/upgrades.md +++ b/docs/backend/upgrades.md @@ -16,6 +16,7 @@ letting Arr's own upgrade logic decide whether to grab them. - [Filters](#filters) - [Dynamic Filter Values](#dynamic-filter-values) - [Selectors](#selectors) +- [Filter Preview](#filter-preview) - [Scheduling](#scheduling) - [Cooldown](#cooldown) - [Dry Run](#dry-run) @@ -152,6 +153,24 @@ specifies a selector strategy and a count (items per run). Selector definitions live in `src/lib/shared/upgrades/selectors.ts`. +## Filter Preview + +The upgrades page can preview a single filter without running a dry run. The +preview fetches the current Arr library, quality profiles, file metadata where +needed, and tags, then applies the same normalization, filter evaluation, +cooldown tag check, and selector ordering used by the upgrade processor. + +Preview is read-only. It does not search indexers, does not use the dry-run +cooldown, and does not apply or reset cooldown tags. Results are grouped as +selected, selectable, cooldown, and filtered out so users can inspect the +filtered pool before triggering a real run. + +Preview library data is cached in memory for 5 minutes per Arr instance. An +expired entry is treated as a cache miss: preview fetches fresh library data +from Arr and replaces the cached entry. This keeps repeated filter edits fast +while limiting stale preview data to a short window. Real upgrade runs do not +use this preview cache. + ## Scheduling Each Arr instance has a single upgrade config with a global cron schedule. diff --git a/src/lib/client/ui/table/ExpandableTable.svelte b/src/lib/client/ui/table/ExpandableTable.svelte index 92f17d40..6c5a685f 100644 --- a/src/lib/client/ui/table/ExpandableTable.svelte +++ b/src/lib/client/ui/table/ExpandableTable.svelte @@ -21,10 +21,12 @@ export let onRowClick: ((row: T) => void) | null = null; export let primaryColumnKey: string | null = null; export let disableExpandWhen: ((row: T) => boolean) | null = null; + export let rowClass: ((row: T) => string) | null = null; // Mobile responsive mode - switches to card layout on small screens export let responsive: boolean = false; // Progressive loading - render items in batches as user scrolls export let pageSize: number | undefined = undefined; + export let fixedLayout: boolean = false; let isMobile = false; let mediaQuery: MediaQueryList | null = null; @@ -200,7 +202,9 @@ {#each displayData as row, index} {@const rowId = getRowId(row)}
@@ -305,14 +309,14 @@ ? 'rounded-b-none border-b-0' : ''}" > - +
{#if chevronPosition === 'left'} - + {/if} {#each columns as column} + {/if} @@ -412,12 +416,14 @@ handleRowClick(rowId, row)} > {#if chevronPosition === 'left'} -
{#if chevronPosition === 'right'} -
+ {#if !shouldDisableExpand(row)} + {#if !shouldDisableExpand(row)}