"use client"; import { useState, useEffect, useCallback } from "react"; import { useTranslate } from "@/hooks/useTranslate"; import { useToast } from "@/components/ui/use-toast"; import { useServiceWorker } from "@/contexts/ServiceWorkerContext"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Progress } from "@/components/ui/progress"; import { Badge } from "@/components/ui/badge"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Database, Trash2, RefreshCw, HardDrive, Image as ImageIcon, FileJson, BookOpen, CheckCircle2, XCircle, Loader2, ChevronDown, ChevronRight, } from "lucide-react"; interface CacheStats { static: { size: number; entries: number }; api: { size: number; entries: number }; images: { size: number; entries: number }; books: { size: number; entries: number }; total: number; } interface CacheEntry { url: string; size: number; } type CacheType = "static" | "api" | "images" | "books"; function formatBytes(bytes: number): string { if (bytes === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`; } function extractPathFromUrl(url: string): string { try { const urlObj = new URL(url); return urlObj.pathname + urlObj.search; } catch { return url; } } interface CacheItemProps { icon: React.ReactNode; label: string; size: number; entries: number; cacheType: CacheType; onClear?: () => void; isClearing?: boolean; description?: string; onLoadEntries: (cacheType: CacheType) => Promise; } function CacheItem({ icon, label, size, entries, cacheType, onClear, isClearing, description, onLoadEntries, }: CacheItemProps) { const { t } = useTranslate(); const [isOpen, setIsOpen] = useState(false); const [cacheEntries, setCacheEntries] = useState(null); const [isLoadingEntries, setIsLoadingEntries] = useState(false); const handleToggle = async (open: boolean) => { setIsOpen(open); if (open && !cacheEntries && !isLoadingEntries) { setIsLoadingEntries(true); const loadedEntries = await onLoadEntries(cacheType); setCacheEntries(loadedEntries); setIsLoadingEntries(false); } }; return (

{formatBytes(size)}

{entries} {entries === 1 ? t("settings.cache.entry") : t("settings.cache.entries")}

{onClear && ( )}
{isLoadingEntries ? (
{t("settings.cache.loadingEntries")}
) : cacheEntries ? (
{cacheEntries.map((entry, index) => (
{extractPathFromUrl(entry.url)} {formatBytes(entry.size)}
))} {cacheEntries.length === 0 && (

{t("settings.cache.noEntries")}

)}
) : (

{t("settings.cache.loadError")}

)}
); } export function CacheSettings() { const { t } = useTranslate(); const { toast } = useToast(); const { isSupported, isReady, version, getCacheStats, getCacheEntries, clearCache } = useServiceWorker(); const [stats, setStats] = useState(null); const [isLoading, setIsLoading] = useState(false); const [clearingCache, setClearingCache] = useState(null); const loadStats = useCallback(async () => { if (!isReady) return; setIsLoading(true); try { const cacheStats = await getCacheStats(); setStats(cacheStats); } finally { setIsLoading(false); } }, [isReady, getCacheStats]); useEffect(() => { loadStats(); }, [loadStats]); const handleClearCache = async (cacheType: "all" | "static" | "api" | "images") => { setClearingCache(cacheType); try { const success = await clearCache(cacheType); if (success) { toast({ title: t("settings.cache.cleared"), description: t("settings.cache.clearedDesc"), }); await loadStats(); } else { toast({ variant: "destructive", title: t("settings.error.title"), description: t("settings.cache.clearError"), }); } } finally { setClearingCache(null); } }; const handleLoadEntries = useCallback( async (cacheType: CacheType): Promise => { return getCacheEntries(cacheType); }, [getCacheEntries] ); // Calculer le pourcentage du cache utilisé (basé sur 100MB limite images) const maxCacheSize = 100 * 1024 * 1024; // 100MB const usagePercent = stats ? Math.min((stats.images.size / maxCacheSize) * 100, 100) : 0; if (!isSupported) { return (
{t("settings.cache.title")}
{t("settings.cache.notSupported")}
); } return (
{t("settings.cache.title")}
{isReady ? ( {version || "Active"} ) : ( {t("settings.cache.initializing")} )}
{t("settings.cache.description")}
{/* Barre de progression globale */} {stats && (
{t("settings.cache.totalStorage")} {formatBytes(stats.total)}

{t("settings.cache.imagesQuota", { used: Math.round(usagePercent) })}

)} {/* Liste des caches */}
{stats ? ( <> } label={t("settings.cache.static")} size={stats.static.size} entries={stats.static.entries} cacheType="static" description={t("settings.cache.staticDesc")} onClear={() => handleClearCache("static")} isClearing={clearingCache === "static"} onLoadEntries={handleLoadEntries} /> } label={t("settings.cache.api")} size={stats.api.size} entries={stats.api.entries} cacheType="api" description={t("settings.cache.apiDesc")} onClear={() => handleClearCache("api")} isClearing={clearingCache === "api"} onLoadEntries={handleLoadEntries} /> } label={t("settings.cache.images")} size={stats.images.size} entries={stats.images.entries} cacheType="images" description={t("settings.cache.imagesDesc")} onClear={() => handleClearCache("images")} isClearing={clearingCache === "images"} onLoadEntries={handleLoadEntries} /> } label={t("settings.cache.books")} size={stats.books.size} entries={stats.books.entries} cacheType="books" description={t("settings.cache.booksDesc")} onLoadEntries={handleLoadEntries} /> ) : isLoading ? (
) : (

{t("settings.cache.unavailable")}

)}
{/* Bouton vider tout */} {stats && stats.total > 0 && ( )}
); }