"use client"; import { useState, useTransition, useEffect, useCallback } from "react"; import { createPortal } from "react-dom"; import { useRouter } from "next/navigation"; import { FormField, FormLabel, FormInput } from "./ui/Form"; import { useTranslation } from "../../lib/i18n/context"; function LockButton({ locked, onToggle, disabled, }: { locked: boolean; onToggle: () => void; disabled?: boolean; }) { const { t } = useTranslation(); return ( ); } const SERIES_STATUS_VALUES = ["", "ongoing", "ended", "hiatus", "cancelled", "upcoming"] as const; interface EditSeriesFormProps { libraryId: string; seriesName: string; currentAuthors: string[]; currentPublishers: string[]; currentBookAuthor: string | null; currentBookLanguage: string | null; currentDescription: string | null; currentStartYear: number | null; currentTotalVolumes: number | null; currentStatus: string | null; currentLockedFields: Record; } export function EditSeriesForm({ libraryId, seriesName, currentAuthors, currentPublishers, currentBookAuthor, currentBookLanguage, currentDescription, currentStartYear, currentTotalVolumes, currentStatus, currentLockedFields, }: EditSeriesFormProps) { const { t } = useTranslation(); const router = useRouter(); const [isPending, startTransition] = useTransition(); const [isOpen, setIsOpen] = useState(false); const [error, setError] = useState(null); // Champs propres à la série const [newName, setNewName] = useState(seriesName === "unclassified" ? "" : seriesName); const [authors, setAuthors] = useState(currentAuthors); const [authorInput, setAuthorInput] = useState(""); const [authorInputEl, setAuthorInputEl] = useState(null); const [publishers, setPublishers] = useState(currentPublishers); const [publisherInput, setPublisherInput] = useState(""); const [publisherInputEl, setPublisherInputEl] = useState(null); const [description, setDescription] = useState(currentDescription ?? ""); const [startYear, setStartYear] = useState(currentStartYear?.toString() ?? ""); const [totalVolumes, setTotalVolumes] = useState(currentTotalVolumes?.toString() ?? ""); const [status, setStatus] = useState(currentStatus ?? ""); // Lock states const [lockedFields, setLockedFields] = useState>(currentLockedFields); // Propagation aux livres — opt-in via bouton const [bookAuthor, setBookAuthor] = useState(currentBookAuthor ?? ""); const [bookLanguage, setBookLanguage] = useState(currentBookLanguage ?? ""); const [showApplyToBooks, setShowApplyToBooks] = useState(false); const toggleLock = (field: string) => { setLockedFields((prev) => ({ ...prev, [field]: !prev[field] })); }; const addAuthor = () => { const v = authorInput.trim(); if (v && !authors.includes(v)) { setAuthors([...authors, v]); } setAuthorInput(""); authorInputEl?.focus(); }; const removeAuthor = (idx: number) => { setAuthors(authors.filter((_, i) => i !== idx)); }; const handleAuthorKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter") { e.preventDefault(); addAuthor(); } }; const addPublisher = () => { const v = publisherInput.trim(); if (v && !publishers.includes(v)) { setPublishers([...publishers, v]); } setPublisherInput(""); publisherInputEl?.focus(); }; const removePublisher = (idx: number) => { setPublishers(publishers.filter((_, i) => i !== idx)); }; const handlePublisherKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter") { e.preventDefault(); addPublisher(); } }; const handleClose = useCallback(() => { setNewName(seriesName === "unclassified" ? "" : seriesName); setAuthors(currentAuthors); setAuthorInput(""); setPublishers(currentPublishers); setPublisherInput(""); setDescription(currentDescription ?? ""); setStartYear(currentStartYear?.toString() ?? ""); setTotalVolumes(currentTotalVolumes?.toString() ?? ""); setStatus(currentStatus ?? ""); setLockedFields(currentLockedFields); setShowApplyToBooks(false); setBookAuthor(currentBookAuthor ?? ""); setBookLanguage(currentBookLanguage ?? ""); setError(null); setIsOpen(false); }, [seriesName, currentAuthors, currentPublishers, currentDescription, currentStartYear, currentTotalVolumes, currentBookAuthor, currentBookLanguage, currentLockedFields]); useEffect(() => { if (!isOpen) return; const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape" && !isPending) handleClose(); }; document.addEventListener("keydown", handleKeyDown); return () => document.removeEventListener("keydown", handleKeyDown); }, [isOpen, isPending, handleClose]); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!newName.trim() && seriesName !== "unclassified") return; setError(null); const finalAuthors = authorInput.trim() ? [...new Set([...authors, authorInput.trim()])] : authors; const finalPublishers = publisherInput.trim() ? [...new Set([...publishers, publisherInput.trim()])] : publishers; startTransition(async () => { try { const effectiveName = newName.trim() || "unclassified"; const body: Record = { new_name: effectiveName, authors: finalAuthors, publishers: finalPublishers, description: description.trim() || null, start_year: startYear.trim() ? parseInt(startYear.trim(), 10) : null, total_volumes: totalVolumes.trim() ? parseInt(totalVolumes.trim(), 10) : null, status: status || null, locked_fields: lockedFields, }; if (showApplyToBooks) { body.author = bookAuthor.trim() || null; body.language = bookLanguage.trim() || null; } const res = await fetch( `/api/libraries/${libraryId}/series/${encodeURIComponent(seriesName)}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), } ); if (!res.ok) { const data = await res.json(); setError(data.error ?? t("editBook.saveError")); return; } setIsOpen(false); if (effectiveName !== seriesName) { router.push(`/libraries/${libraryId}/series/${encodeURIComponent(effectiveName)}` as any); } else { router.refresh(); } } catch { setError(t("common.networkError")); } }); }; const modal = isOpen ? createPortal( <> {/* Backdrop */}
!isPending && handleClose()} /> {/* Modal */}
{/* Header */}

{t("editSeries.title")}

{/* Body */}
{t("editSeries.name")} setNewName(e.target.value)} disabled={isPending} placeholder={t("editSeries.namePlaceholder")} />
{t("editSeries.startYear")} toggleLock("start_year")} disabled={isPending} />
setStartYear(e.target.value)} disabled={isPending} placeholder={t("editSeries.startYearPlaceholder")} />
{t("editSeries.totalVolumes")} toggleLock("total_volumes")} disabled={isPending} />
setTotalVolumes(e.target.value)} disabled={isPending} placeholder="12" />
{t("editSeries.status")} toggleLock("status")} disabled={isPending} />
{/* Auteurs — multi-valeur */}
{t("editSeries.authors")} toggleLock("authors")} disabled={isPending} />
{authors.length > 0 && (
{authors.map((a, i) => ( {a} ))}
)}
setAuthorInput(e.target.value)} onKeyDown={handleAuthorKeyDown} disabled={isPending} placeholder={t("editBook.addAuthor")} className="flex h-10 flex-1 rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm transition-colors placeholder:text-muted-foreground/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" />
{showApplyToBooks && (
{t("editSeries.bookAuthor")} setBookAuthor(e.target.value)} disabled={isPending} placeholder={t("editSeries.bookAuthorPlaceholder")} /> {t("editSeries.bookLanguage")} setBookLanguage(e.target.value)} disabled={isPending} placeholder={t("editBook.languagePlaceholder")} />
)} {/* Éditeurs — multi-valeur */}
{t("editSeries.publishers")} toggleLock("publishers")} disabled={isPending} />
{publishers.length > 0 && (
{publishers.map((p, i) => ( {p} ))}
)}
setPublisherInput(e.target.value)} onKeyDown={handlePublisherKeyDown} disabled={isPending} placeholder={t("editSeries.addPublisher")} className="flex h-10 flex-1 rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm transition-colors placeholder:text-muted-foreground/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" />
{t("editBook.description")} toggleLock("description")} disabled={isPending} />