feat(analysis): admin coverage overview + per-page result inspector UI
Reworks /admin/analysis from a blind enqueue form into a coverage browser: - Loads a paginated overview of mangas with a CoverageBadge (Full/Partial/None, analyzed/total) per row; debounced search filters it. - Drill manga → chapters (coverage badge each) → page grid (chips colored done/queued/failed/none) with a legend. - Click a page chip → Modal showing the full result: status, model, time, NSFW + content-warning chips, scene description, tags, and OCR lines (labeled by kind). Unanalyzed/failed pages get a "Queue this page" action (force re-analyze). - Keeps "Queue all" + per-manga/chapter enqueue and the include-analyzed toggle; enqueue refreshes the open chapter's grid so queued pages show. API client: getAnalysisMangaCoverage / ChapterCoverage / ChapterPages / PageDetail + types. New CoverageBadge component. Tests: vitest for the four clients; Playwright for overview+badges+queue-all, drill+inspect, and queue-from-detail. svelte-check + build clean; 266 vitest. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
56
frontend/src/lib/components/CoverageBadge.svelte
Normal file
56
frontend/src/lib/components/CoverageBadge.svelte
Normal file
@@ -0,0 +1,56 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* Analysis-coverage badge: `analyzed/total` colored by state —
|
||||
* green Full, amber Partial, grey None. Reused for manga and chapter
|
||||
* rows in the admin Analysis section.
|
||||
*/
|
||||
let {
|
||||
analyzed,
|
||||
total,
|
||||
testid
|
||||
}: { analyzed: number; total: number; testid?: string } = $props();
|
||||
|
||||
const state = $derived(
|
||||
total === 0 || analyzed === 0
|
||||
? 'none'
|
||||
: analyzed >= total
|
||||
? 'full'
|
||||
: 'partial'
|
||||
);
|
||||
const word = $derived(
|
||||
state === 'full' ? 'Full' : state === 'partial' ? 'Partial' : 'None'
|
||||
);
|
||||
</script>
|
||||
|
||||
<span class="badge {state}" data-testid={testid} data-state={state}>
|
||||
{word} {analyzed}/{total}
|
||||
</span>
|
||||
|
||||
<style>
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-1);
|
||||
font-size: var(--font-xs);
|
||||
font-weight: var(--weight-semibold);
|
||||
padding: 1px var(--space-2);
|
||||
border-radius: var(--radius-pill);
|
||||
border: 1px solid transparent;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.full {
|
||||
background: color-mix(in srgb, #2e7d32 16%, transparent);
|
||||
color: #2e7d32;
|
||||
border-color: color-mix(in srgb, #2e7d32 40%, transparent);
|
||||
}
|
||||
.partial {
|
||||
background: color-mix(in srgb, #d4762a 16%, transparent);
|
||||
color: #b85e1a;
|
||||
border-color: color-mix(in srgb, #d4762a 40%, transparent);
|
||||
}
|
||||
.none {
|
||||
background: var(--surface-elevated);
|
||||
color: var(--text-muted);
|
||||
border-color: var(--border);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user