release / govulncheck (push) Successful in 15s
release / web (push) Successful in 1m21s
release / go (push) Successful in 1m37s
release / integration (push) Successful in 5m10s
release / android (push) Successful in 5m16s
release / Build signed APK (releases and dev) (push) Successful in 5m27s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 24s
release / Verify release artifacts (tag releases only) (push) Skipped
- GET /api/admin/summary: every admin queue (requests, reported tracks, playback errors, duplicates, suspect sources, missing files) with its count and the age of its oldest item, in one query. - The admin layout drops the 11-tab strip for SectionRail, as Settings and the Collection have, with each queue's count as its badge; the counts refresh on each admin navigation and every 30s. - The Overview leads with "Nothing needs you." or "N things need you.", lists each waiting queue with "oldest 3d ago" and a link to it, counts a failed library scan as a problem, and names the empty queues on one line. The request and quarantine previews and the stat tiles are gone; those queues are worked on their own pages. A failed summary says so with Try again instead of reading as all-clear. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
48 lines
1.4 KiB
Svelte
48 lines
1.4 KiB
Svelte
<script lang="ts" generics="Id extends string">
|
|
// In-page tabs: one strip of buttons switching what a single page shows
|
|
// (#5416). The same look as DiscoverTabs, which is route
|
|
// navigation and so stays links; this is for state inside one page, such as
|
|
// Liked's Artists | Albums | Tracks or a queue's status filter.
|
|
type Item = {
|
|
id: Id;
|
|
label: string;
|
|
/** Shown as a pill after the label. Omit for no pill. */
|
|
count?: number;
|
|
};
|
|
|
|
let {
|
|
items,
|
|
value = $bindable(),
|
|
ariaLabel
|
|
}: {
|
|
items: Item[];
|
|
value: Id;
|
|
ariaLabel: string;
|
|
} = $props();
|
|
</script>
|
|
|
|
<div role="tablist" aria-label={ariaLabel} class="flex gap-2 border-b border-border">
|
|
{#each items as item (item.id)}
|
|
{@const active = value === item.id}
|
|
<button
|
|
type="button"
|
|
role="tab"
|
|
aria-selected={active}
|
|
class="border-b-2 px-3 py-2 text-sm transition-colors {active
|
|
? 'border-accent text-text-primary'
|
|
: 'border-transparent text-text-secondary hover:text-text-primary'}"
|
|
onclick={() => (value = item.id)}
|
|
>
|
|
{item.label}
|
|
{#if item.count !== undefined}
|
|
<span
|
|
class="ml-1.5 inline-flex items-center rounded-full bg-accent-tint px-2 py-0.5 text-xs text-accent-fg"
|
|
data-testid="tab-count"
|
|
>
|
|
{item.count}
|
|
</span>
|
|
{/if}
|
|
</button>
|
|
{/each}
|
|
</div>
|