feat: refresh metadata ciblé par série après import et dans la modale
- Après import torrent, refresh automatique des métadonnées uniquement sur la série importée (via refresh_link) au lieu d'un job complet - Nouvel endpoint POST /metadata/refresh-link/:id pour rafraîchir un seul lien metadata approuvé - Bouton "Rafraîchir" dans la modale metadata (état linked) avec spinner et confirmation visuelle Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
13
apps/backoffice/app/api/metadata/refresh-link/[id]/route.ts
Normal file
13
apps/backoffice/app/api/metadata/refresh-link/[id]/route.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { apiFetch } from "@/lib/api";
|
||||
|
||||
export async function POST(_request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const data = await apiFetch(`/metadata/refresh-link/${id}`, { method: "POST" });
|
||||
return NextResponse.json(data);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "Failed to refresh metadata";
|
||||
return NextResponse.json({ error: message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,8 @@ export function MetadataSearchModal({
|
||||
const [missing, setMissing] = useState<MissingBooksDto | null>(initialMissing);
|
||||
const [showMissingList, setShowMissingList] = useState(false);
|
||||
const [syncReport, setSyncReport] = useState<SyncReport | null>(null);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [refreshDone, setRefreshDone] = useState(false);
|
||||
|
||||
// Provider selector: empty string = library default
|
||||
const [searchProvider, setSearchProvider] = useState("");
|
||||
@@ -655,6 +657,37 @@ export function MetadataSearchModal({
|
||||
>
|
||||
{t("metadata.searchAgain")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
disabled={refreshing}
|
||||
onClick={async () => {
|
||||
if (!linkId) return;
|
||||
setRefreshing(true);
|
||||
setRefreshDone(false);
|
||||
try {
|
||||
const resp = await fetch(`/api/metadata/refresh-link/${linkId}`, { method: "POST" });
|
||||
if (resp.ok) {
|
||||
setRefreshDone(true);
|
||||
setTimeout(() => setRefreshDone(false), 3000);
|
||||
}
|
||||
} finally {
|
||||
setRefreshing(false);
|
||||
}
|
||||
}}
|
||||
className={`p-2.5 rounded-lg border text-sm font-medium transition-colors ${
|
||||
refreshDone
|
||||
? "border-success/30 bg-success/5 text-success"
|
||||
: "border-primary/30 bg-primary/5 text-primary hover:bg-primary/10"
|
||||
}`}
|
||||
>
|
||||
{refreshing ? (
|
||||
<Icon name="spinner" size="sm" className="animate-spin" />
|
||||
) : refreshDone ? (
|
||||
<Icon name="check" size="sm" />
|
||||
) : (
|
||||
t("metadata.refreshLink")
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleUnlink}
|
||||
|
||||
Reference in New Issue
Block a user