chore: update various components and services for improved functionality and consistency, including formatting adjustments and minor refactors

This commit is contained in:
Julien Froidefond
2025-12-07 09:54:05 +01:00
parent 4f5724c0ff
commit 39e3328123
141 changed files with 5292 additions and 3243 deletions

View File

@@ -129,7 +129,7 @@ export function CacheSettings({ initialTTLConfig }: CacheSettingsProps) {
if (response) {
const blob = await response.clone().blob();
totalSize += blob.size;
// Calculer la taille du cache API séparément
if (cacheName.includes("api")) {
apiSize += blob.size;
@@ -214,19 +214,24 @@ export function CacheSettings({ initialTTLConfig }: CacheSettingsProps) {
try {
const urlObj = new URL(url);
const path = urlObj.pathname;
const segments = path.split('/').filter(Boolean);
if (segments.length === 0) return '/';
const segments = path.split("/").filter(Boolean);
if (segments.length === 0) return "/";
// Pour /api/komga/images, grouper par type (series/books)
if (segments[0] === 'api' && segments[1] === 'komga' && segments[2] === 'images' && segments[3]) {
if (
segments[0] === "api" &&
segments[1] === "komga" &&
segments[2] === "images" &&
segments[3]
) {
return `/${segments[0]}/${segments[1]}/${segments[2]}/${segments[3]}`;
}
// Pour les autres, garder juste le premier segment
return `/${segments[0]}`;
} catch {
return 'Autres';
return "Autres";
}
};
@@ -255,8 +260,8 @@ export function CacheSettings({ initialTTLConfig }: CacheSettingsProps) {
// Trier par date (le plus récent en premier) basé sur le paramètre v
Object.keys(grouped).forEach((key) => {
grouped[key].sort((a, b) => {
const aVersion = new URL(a.url).searchParams.get('v') || '0';
const bVersion = new URL(b.url).searchParams.get('v') || '0';
const aVersion = new URL(a.url).searchParams.get("v") || "0";
const bVersion = new URL(b.url).searchParams.get("v") || "0";
return Number(bVersion) - Number(aVersion);
});
});
@@ -363,13 +368,13 @@ export function CacheSettings({ initialTTLConfig }: CacheSettingsProps) {
if ("serviceWorker" in navigator && "caches" in window) {
const cacheNames = await caches.keys();
await Promise.all(cacheNames.map((cacheName) => caches.delete(cacheName)));
// Forcer la mise à jour du service worker
const registrations = await navigator.serviceWorker.getRegistrations();
for (const registration of registrations) {
await registration.unregister();
}
toast({
title: t("settings.cache.title"),
description: t("settings.cache.messages.serviceWorkerCleared"),
@@ -383,7 +388,7 @@ export function CacheSettings({ initialTTLConfig }: CacheSettingsProps) {
if (showSwEntries) {
await fetchSwCacheEntries();
}
// Recharger la page après 1 seconde pour réenregistrer le SW
setTimeout(() => {
window.location.reload();
@@ -458,7 +463,6 @@ export function CacheSettings({ initialTTLConfig }: CacheSettingsProps) {
<CardDescription>{t("settings.cache.description")}</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between mb-4">
<div className="space-y-0.5">
<Label htmlFor="cache-mode">{t("settings.cache.mode.label")}</Label>
@@ -488,7 +492,9 @@ export function CacheSettings({ initialTTLConfig }: CacheSettingsProps) {
</div>
</div>
) : (
<div className="text-sm text-muted-foreground">{t("settings.cache.size.error")}</div>
<div className="text-sm text-muted-foreground">
{t("settings.cache.size.error")}
</div>
)}
</div>
@@ -497,7 +503,9 @@ export function CacheSettings({ initialTTLConfig }: CacheSettingsProps) {
{swCacheSize !== null ? (
<div className="text-sm text-muted-foreground">{formatBytes(swCacheSize)}</div>
) : (
<div className="text-sm text-muted-foreground">{t("settings.cache.size.error")}</div>
<div className="text-sm text-muted-foreground">
{t("settings.cache.size.error")}
</div>
)}
</div>
@@ -506,7 +514,9 @@ export function CacheSettings({ initialTTLConfig }: CacheSettingsProps) {
{apiCacheSize !== null ? (
<div className="text-sm text-muted-foreground">{formatBytes(apiCacheSize)}</div>
) : (
<div className="text-sm text-muted-foreground">{t("settings.cache.size.error")}</div>
<div className="text-sm text-muted-foreground">
{t("settings.cache.size.error")}
</div>
)}
</div>
</div>
@@ -525,11 +535,7 @@ export function CacheSettings({ initialTTLConfig }: CacheSettingsProps) {
<List className="h-4 w-4" />
{t("settings.cache.entries.serverTitle")}
</span>
{showEntries ? (
<ChevronUp className="h-4 w-4" />
) : (
<ChevronDown className="h-4 w-4" />
)}
{showEntries ? <ChevronUp className="h-4 w-4" /> : <ChevronDown className="h-4 w-4" />}
</Button>
{showEntries && (
@@ -569,7 +575,10 @@ export function CacheSettings({ initialTTLConfig }: CacheSettingsProps) {
>
{getTimeRemaining(entry.expiry)}
</div>
<div className="text-muted-foreground/70" title={formatDate(entry.expiry)}>
<div
className="text-muted-foreground/70"
title={formatDate(entry.expiry)}
>
{new Date(entry.expiry).toLocaleDateString()}
</div>
</div>
@@ -649,72 +658,90 @@ export function CacheSettings({ initialTTLConfig }: CacheSettingsProps) {
<div className="space-y-1 pl-2">
{(() => {
const versionGroups = groupVersions(entries);
return Object.entries(versionGroups).map(([baseUrl, versions]) => {
const hasMultipleVersions = versions.length > 1;
const isVersionExpanded = expandedVersions[baseUrl];
const totalSize = versions.reduce((sum, v) => sum + v.size, 0);
return Object.entries(versionGroups).map(
([baseUrl, versions]) => {
const hasMultipleVersions = versions.length > 1;
const isVersionExpanded = expandedVersions[baseUrl];
const totalSize = versions.reduce(
(sum, v) => sum + v.size,
0
);
if (!hasMultipleVersions) {
const entry = versions[0];
return (
<div key={baseUrl} className="py-1">
<div className="flex items-start justify-between gap-2">
<div className="flex-1 min-w-0">
<div className="font-mono text-xs truncate text-muted-foreground" title={entry.url}>
{entry.url.replace(/^https?:\/\/[^/]+/, "")}
if (!hasMultipleVersions) {
const entry = versions[0];
return (
<div key={baseUrl} className="py-1">
<div className="flex items-start justify-between gap-2">
<div className="flex-1 min-w-0">
<div
className="font-mono text-xs truncate text-muted-foreground"
title={entry.url}
>
{entry.url.replace(/^https?:\/\/[^/]+/, "")}
</div>
</div>
<div className="text-xs text-muted-foreground whitespace-nowrap">
{formatBytes(entry.size)}
</div>
</div>
<div className="text-xs text-muted-foreground whitespace-nowrap">
{formatBytes(entry.size)}
</div>
</div>
);
}
return (
<div key={baseUrl} className="py-1">
<button
type="button"
onClick={() => toggleVersions(baseUrl)}
className="w-full flex items-start justify-between gap-2 hover:bg-muted/30 rounded p-1 -m-1 transition-colors"
>
<div className="flex-1 min-w-0 flex items-center gap-1">
{isVersionExpanded ? (
<ChevronDown className="h-3 w-3 flex-shrink-0" />
) : (
<ChevronUp className="h-3 w-3 flex-shrink-0" />
)}
<div
className="font-mono text-xs truncate text-muted-foreground"
title={baseUrl}
>
{baseUrl}
</div>
<span className="inline-flex items-center rounded-full bg-orange-500/10 px-1.5 py-0.5 text-xs font-medium text-orange-600 dark:text-orange-400 flex-shrink-0">
{versions.length} versions
</span>
</div>
<div className="text-xs text-muted-foreground whitespace-nowrap font-medium">
{formatBytes(totalSize)}
</div>
</button>
{isVersionExpanded && (
<div className="pl-4 mt-1 space-y-1">
{versions.map((version, vIdx) => (
<div
key={vIdx}
className="py-0.5 flex items-start justify-between gap-2"
>
<div className="flex-1 min-w-0">
<div
className="font-mono text-xs truncate text-muted-foreground/70"
title={version.url}
>
{new URL(version.url).search ||
"(no version)"}
</div>
</div>
<div className="text-xs text-muted-foreground/70 whitespace-nowrap">
{formatBytes(version.size)}
</div>
</div>
))}
</div>
)}
</div>
);
}
return (
<div key={baseUrl} className="py-1">
<button
type="button"
onClick={() => toggleVersions(baseUrl)}
className="w-full flex items-start justify-between gap-2 hover:bg-muted/30 rounded p-1 -m-1 transition-colors"
>
<div className="flex-1 min-w-0 flex items-center gap-1">
{isVersionExpanded ? (
<ChevronDown className="h-3 w-3 flex-shrink-0" />
) : (
<ChevronUp className="h-3 w-3 flex-shrink-0" />
)}
<div className="font-mono text-xs truncate text-muted-foreground" title={baseUrl}>
{baseUrl}
</div>
<span className="inline-flex items-center rounded-full bg-orange-500/10 px-1.5 py-0.5 text-xs font-medium text-orange-600 dark:text-orange-400 flex-shrink-0">
{versions.length} versions
</span>
</div>
<div className="text-xs text-muted-foreground whitespace-nowrap font-medium">
{formatBytes(totalSize)}
</div>
</button>
{isVersionExpanded && (
<div className="pl-4 mt-1 space-y-1">
{versions.map((version, vIdx) => (
<div key={vIdx} className="py-0.5 flex items-start justify-between gap-2">
<div className="flex-1 min-w-0">
<div className="font-mono text-xs truncate text-muted-foreground/70" title={version.url}>
{new URL(version.url).search || "(no version)"}
</div>
</div>
<div className="text-xs text-muted-foreground/70 whitespace-nowrap">
{formatBytes(version.size)}
</div>
</div>
))}
</div>
)}
</div>
);
});
);
})()}
</div>
)}
@@ -833,12 +860,24 @@ export function CacheSettings({ initialTTLConfig }: CacheSettingsProps) {
onChange={handleTTLChange}
className="flex h-9 w-full rounded-md border border-input bg-background/70 backdrop-blur-md px-3 py-1 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
>
<option value="0">{t("settings.cache.ttl.imageCacheMaxAge.options.noCache")}</option>
<option value="3600">{t("settings.cache.ttl.imageCacheMaxAge.options.oneHour")}</option>
<option value="86400">{t("settings.cache.ttl.imageCacheMaxAge.options.oneDay")}</option>
<option value="604800">{t("settings.cache.ttl.imageCacheMaxAge.options.oneWeek")}</option>
<option value="2592000">{t("settings.cache.ttl.imageCacheMaxAge.options.oneMonth")}</option>
<option value="31536000">{t("settings.cache.ttl.imageCacheMaxAge.options.oneYear")}</option>
<option value="0">
{t("settings.cache.ttl.imageCacheMaxAge.options.noCache")}
</option>
<option value="3600">
{t("settings.cache.ttl.imageCacheMaxAge.options.oneHour")}
</option>
<option value="86400">
{t("settings.cache.ttl.imageCacheMaxAge.options.oneDay")}
</option>
<option value="604800">
{t("settings.cache.ttl.imageCacheMaxAge.options.oneWeek")}
</option>
<option value="2592000">
{t("settings.cache.ttl.imageCacheMaxAge.options.oneMonth")}
</option>
<option value="31536000">
{t("settings.cache.ttl.imageCacheMaxAge.options.oneYear")}
</option>
</select>
</div>
</div>