Add filter input to source manager for searching cached sources by name
All checks were successful
CI / check (push) Successful in 1m22s
CI / build-image (push) Successful in 18s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-16 12:05:45 +01:00
parent b6e882add2
commit 6e10238fe0
2 changed files with 32 additions and 5 deletions

View File

@@ -1,8 +1,15 @@
import { Database, Trash2 } from "lucide-react";
import { useCallback, useEffect, useOptimistic, useState } from "react";
import { Database, Search, Trash2 } from "lucide-react";
import {
useCallback,
useEffect,
useMemo,
useOptimistic,
useState,
} from "react";
import type { CachedSourceInfo } from "../adapters/bestiary-cache.js";
import * as bestiaryCache from "../adapters/bestiary-cache.js";
import { Button } from "./ui/button.js";
import { Input } from "./ui/input.js";
interface SourceManagerProps {
onCacheCleared: () => void;
@@ -12,6 +19,7 @@ export function SourceManager({
onCacheCleared,
}: Readonly<SourceManagerProps>) {
const [sources, setSources] = useState<CachedSourceInfo[]>([]);
const [filter, setFilter] = useState("");
const [optimisticSources, applyOptimistic] = useOptimistic(
sources,
(
@@ -46,6 +54,15 @@ export function SourceManager({
onCacheCleared();
};
const filteredSources = useMemo(() => {
const term = filter.toLowerCase();
return term
? optimisticSources.filter((s) =>
s.displayName.toLowerCase().includes(term),
)
: optimisticSources;
}, [optimisticSources, filter]);
if (optimisticSources.length === 0) {
return (
<div className="flex flex-col items-center gap-2 py-8 text-center">
@@ -70,8 +87,17 @@ export function SourceManager({
Clear All
</Button>
</div>
<div className="relative">
<Search className="pointer-events-none absolute top-1/2 left-3 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" />
<Input
placeholder="Filter sources…"
value={filter}
onChange={(e) => setFilter(e.target.value)}
className="pl-8"
/>
</div>
<ul className="flex flex-col gap-1">
{optimisticSources.map((source) => (
{filteredSources.map((source) => (
<li
key={source.sourceCode}
className="flex items-center justify-between rounded-md border border-border px-3 py-2"

View File

@@ -148,7 +148,7 @@ While the bulk import is in progress, the user sees a text counter ("Loading sou
If the user closes the side panel while a bulk import is still in progress, a persistent toast notification appears at the bottom-center of the screen showing the same progress text and progress bar.
**US-M6 — Manage Cached Sources (P4)**
A DM wants to see which sources are cached, clear a specific source's cache, or clear all cached data. A management UI provides this visibility and control.
A DM wants to see which sources are cached, find a specific source, clear a specific source's cache, or clear all cached data. A management UI provides this visibility and control, including a filter input to quickly locate sources by name when many are cached.
### Requirements
@@ -174,7 +174,7 @@ A DM wants to see which sources are cached, clear a specific source's cache, or
- **FR-044**: The bulk import MUST run asynchronously and not block the rest of the app.
- **FR-045**: The user MUST explicitly provide/confirm the URL before any fetches occur — the app never auto-fetches content.
- **FR-046**: The "Load All" button MUST be disabled when the URL field is empty or while a bulk import is already in progress.
- **FR-047**: The app MUST provide a management UI showing cached sources with options to clear individual sources or all cached data.
- **FR-047**: The app MUST provide a management UI showing cached sources with a filter input for searching by display name and options to clear individual sources or all cached data.
- **FR-048**: The normalization adapter and tag-stripping utility MUST remain the canonical pipeline for all fetched and uploaded data.
- **FR-049**: The distributed app bundle MUST contain zero copyrighted prose content — only mechanical facts and creature names in the search index.
@@ -198,6 +198,7 @@ A DM wants to see which sources are cached, clear a specific source's cache, or
16. **Given** two sources have been cached, **When** the DM opens the source management UI, **Then** both sources are listed with their display names.
17. **Given** the source management UI is open, **When** the DM clears a single source, **Then** that source's data is removed; stat blocks for its creatures require re-fetching, while other cached sources remain.
18. **Given** the source management UI is open, **When** the DM clears all cached data, **Then** all source data is removed and all stat blocks require re-fetching.
19. **Given** many sources are cached, **When** the DM types a partial name in the filter input, **Then** only sources whose display name matches (case-insensitive) are shown.
### Edge Cases