Component props.
The rendered cache management section.
export function CacheManagementSection({
cachesToClear,
isClearing,
isCacheCleared,
searchQuery,
highlightedSectionId,
onCachesToClearChange,
onClearCaches,
}: Readonly<CacheManagementSectionProps>) {
const cacheTypes = [
{
id: "shouldClearAuthCache",
label: "Auth cache",
description: "Authentication tokens and login state",
icon: Lock,
color: "text-amber-500",
bg: "bg-amber-500/10",
},
{
id: "shouldClearSettingsCache",
label: "Settings cache",
description: "User settings and sync configuration",
icon: Settings,
color: "text-slate-500",
bg: "bg-slate-500/10",
},
{
id: "shouldClearSyncCache",
label: "Sync cache",
description: "Synchronization history and operation records",
icon: ArrowLeftRight,
color: "text-blue-500",
bg: "bg-blue-500/10",
},
{
id: "shouldClearImportCache",
label: "Import cache",
description: "Import operation history and results",
icon: Download,
color: "text-green-500",
bg: "bg-green-500/10",
},
{
id: "shouldClearReviewCache",
label: "Review cache",
description: "Matching results and review data",
icon: FileText,
color: "text-purple-500",
bg: "bg-purple-500/10",
},
{
id: "shouldClearMangaCache",
label: "Manga cache",
description: "Cached manga titles, details, and metadata",
icon: BookOpen,
color: "text-pink-500",
bg: "bg-pink-500/10",
},
{
id: "shouldClearSearchCache",
label: "Search cache",
description: "Cached search queries and API results",
icon: Search,
color: "text-cyan-500",
bg: "bg-cyan-500/10",
},
{
id: "shouldClearOtherCache",
label: "Other caches",
description: "Miscellaneous cache data and temporary storage",
icon: Database,
color: "text-orange-500",
bg: "bg-orange-500/10",
},
] as const;
const handleToggle = (id: keyof CachesToClear) => {
onCachesToClearChange({
...cachesToClear,
[id]: !cachesToClear[id],
});
};
const handleSelectAll = () => {
const allSelected = Object.keys(cachesToClear).reduce(
(acc, key) => ({ ...acc, [key]: true }),
{} as CachesToClear,
);
onCachesToClearChange(allSelected);
};
const handleDeselectAll = () => {
const noneSelected = Object.keys(cachesToClear).reduce(
(acc, key) => ({ ...acc, [key]: false }),
{} as CachesToClear,
);
onCachesToClearChange(noneSelected);
};
const selectedCount = Object.values(cachesToClear).filter(Boolean).length;
return (
<motion.div
id="data-cache"
className={cn(
"bg-muted/40 space-y-6 rounded-xl border p-6",
highlightedSectionId === "data-cache" &&
"ring-2 ring-blue-500 ring-offset-2 ring-offset-white dark:ring-blue-400 dark:ring-offset-slate-950",
)}
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2, duration: 0.4 }}
>
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div>
<h3 className="flex items-center gap-2 text-sm font-medium">
<Trash2 className="h-4 w-4 text-blue-500" />
{renderHighlightedText("Clear local cache", searchQuery)}
</h3>
<p className="text-muted-foreground mt-1 text-xs">
{renderHighlightedText(
"Select which cached data types to clear and reset.",
searchQuery,
)}
</p>
</div>
<div className="flex items-center gap-2">
<Button
variant="ghost"
size="sm"
onClick={handleSelectAll}
className="h-8 text-xs"
>
Select all
</Button>
<Separator orientation="vertical" className="h-4" />
<Button
variant="ghost"
size="sm"
onClick={handleDeselectAll}
className="h-8 text-xs"
>
Deselect all
</Button>
</div>
</div>
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
{cacheTypes.map((type) => {
const isSelected = cachesToClear[type.id];
return (
<button
key={type.id}
onClick={() => handleToggle(type.id)}
className={cn(
"group relative flex flex-col items-start gap-3 rounded-xl border p-4 text-left transition-all duration-200 hover:shadow-md",
isSelected
? "border-blue-500/50 bg-blue-50/50 dark:border-blue-400/30 dark:bg-blue-900/10"
: "border-slate-200 bg-white hover:border-blue-300/50 hover:bg-slate-50 dark:border-slate-800 dark:bg-slate-950 dark:hover:border-slate-700 dark:hover:bg-slate-900",
)}
>
<div className="flex w-full items-start justify-between">
<div
className={cn(
"flex h-8 w-8 items-center justify-center rounded-lg transition-colors",
isSelected
? "bg-blue-500 text-white dark:bg-blue-400 dark:text-slate-900"
: cn(type.bg, type.color),
)}
>
<type.icon className="h-4 w-4" />
</div>
<div
className={cn(
"flex h-5 w-5 items-center justify-center rounded-full border transition-all",
isSelected
? "border-blue-500 bg-blue-500 text-white dark:border-blue-400 dark:bg-blue-400 dark:text-slate-900"
: "border-slate-300 bg-transparent dark:border-slate-600",
)}
>
{isSelected && <Check className="h-3 w-3" />}
</div>
</div>
<div>
<span className="block text-sm font-medium text-slate-900 dark:text-slate-100">
{renderHighlightedText(type.label, searchQuery)}
</span>
<p className="text-muted-foreground mt-1 text-xs leading-relaxed">
{renderHighlightedText(type.description, searchQuery)}
</p>
</div>
</button>
);
})}
</div>
<Separator />
<div className="flex items-center justify-between gap-4">
<p className="text-muted-foreground text-xs">
{(() => {
if (selectedCount === 0) return "No caches selected";
const plural = selectedCount === 1 ? "" : "s";
return `${selectedCount} cache type${plural} selected`;
})()}
</p>
<Button
onClick={onClearCaches}
variant={isCacheCleared ? "outline" : "default"}
disabled={isClearing || selectedCount === 0}
className={cn(
"min-w-[180px] transition-all",
isCacheCleared &&
"border-green-200 bg-green-50 text-green-700 hover:bg-green-100 hover:text-green-800 dark:border-green-900/50 dark:bg-green-900/20 dark:text-green-400 dark:hover:bg-green-900/30",
)}
>
{(() => {
if (isClearing) {
return (
<>
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
Clearing...
</>
);
}
if (isCacheCleared) {
return (
<>
<CheckCircle className="mr-2 h-4 w-4" />
Cleared!
</>
);
}
return (
<>
<Trash2 className="mr-2 h-4 w-4" />
Clear Selected
</>
);
})()}
</Button>
</div>
</motion.div>
);
}
Cache management section component. Allows users to select and clear various types of cached data.