feat: show cached badge in search stats when results from cache

- Add 'cached' boolean field to SearchResponse model
- Set cached=True when returning Redis-cached results
- Display green 'cached' pill with database icon in stats header

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-02-23 17:29:29 +11:00
co-authored by Copilot
parent e9a5d68553
commit 08937c3b8b
6 changed files with 17 additions and 5 deletions
+1
View File
@@ -61,6 +61,7 @@ class SearchResponse(BaseModel):
timestamp: str = Field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
total_results: int = 0
has_next: bool = True
cached: bool = False
class EngineInfo(BaseModel):
+4 -3
View File
@@ -65,9 +65,10 @@ async def search(
engines_key = ",".join(sorted(engines)) if engines else ""
# Check cache first
cached = await cache.get_cached(query, category, page, image_size, engines_key)
if cached is not None:
resp = SearchResponse(**cached)
cached_data = await cache.get_cached(query, category, page, image_size, engines_key)
if cached_data is not None:
resp = SearchResponse(**cached_data)
resp.cached = True
return resp
enabled_engines = registry.get_enabled_engines()
Binary file not shown.
+2
View File
@@ -300,6 +300,7 @@ function App() {
errors={response.errors}
page={page}
totalResults={response.results.length}
cached={response.cached}
initialCollapsed
/>
</div>
@@ -347,6 +348,7 @@ function App() {
errors={response.errors}
page={page}
totalResults={response.results.length}
cached={response.cached}
/>
</div>
</aside>
+9 -2
View File
@@ -1,5 +1,5 @@
import { useState } from "react";
import { BarChart3, ChevronDown, ChevronUp, AlertCircle, CheckCircle2, Clock } from "lucide-react";
import { BarChart3, ChevronDown, ChevronUp, AlertCircle, CheckCircle2, Clock, Database } from "lucide-react";
import type { EngineStat, EngineError } from "@/lib/api";
import { cn } from "@/lib/utils";
@@ -8,10 +8,11 @@ interface SearchStatsProps {
errors: EngineError[];
page: number;
totalResults: number;
cached?: boolean;
initialCollapsed?: boolean;
}
export function SearchStats({ stats, page, totalResults, initialCollapsed = false }: SearchStatsProps) {
export function SearchStats({ stats, page, totalResults, cached = false, initialCollapsed = false }: SearchStatsProps) {
const [collapsed, setCollapsed] = useState(initialCollapsed);
if (stats.length === 0) return null;
@@ -32,6 +33,12 @@ export function SearchStats({ stats, page, totalResults, initialCollapsed = fals
Search Stats
</span>
<span className="flex items-center gap-2">
{cached && (
<span className="inline-flex items-center gap-1 rounded-full bg-emerald-100 px-2 py-0.5 text-xs font-medium text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-400">
<Database className="h-3 w-3" aria-hidden="true" />
cached
</span>
)}
<span className="text-xs text-muted-foreground">
{totalResults} result{totalResults !== 1 && "s"} · page {page}
</span>
+1
View File
@@ -48,6 +48,7 @@ export interface SearchResponse {
timestamp: string;
total_results: number;
has_next: boolean;
cached: boolean;
}
export interface EngineInfo {