import { useEffect, useMemo, useRef, useState } from "react" import { supabase } from "@/auth/supabase" import { Button } from "../ui/button" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "../ui/dialog" import { Textarea } from "../ui/textarea" import { Separator } from "../ui/separator" import { ScrollArea } from "../ui/scroll-area" import { Badge } from "../ui/badge" import * as Icons from "lucide-react" import { toast } from "sonner" /** * EditBibliografiaButton v3 (simple y aireado) * - Layout limpio: una sola columna + barra mínima. * - Acciones esenciales: Recortar, Dedupe, A–Z, Importar, Copiar. * - Toasts con sonner: toast.success / toast.error. */ export function EditBibliografiaButton({ asignaturaId, value, onSaved, }: { asignaturaId: string value: string[] onSaved: (refs: string[]) => void }) { const [open, setOpen] = useState(false) const [saving, setSaving] = useState(false) const [text, setText] = useState("") const initialTextRef = useRef("") const lines = useMemo(() => parseLines(text), [text]) const dirty = useMemo(() => initialTextRef.current !== text, [text]) function openEditor() { const start = (value ?? []).join("\n") setText(start) initialTextRef.current = start setOpen(true) } async function save() { try { setSaving(true) const refs = parseLines(text) const { data, error } = await supabase .from("asignaturas") .update({ bibliografia: refs }) .eq("id", asignaturaId) .select() .maybeSingle() if (error) throw error onSaved((data as any)?.bibliografia ?? refs) initialTextRef.current = refs.join("\n") toast.success(`${refs.length} referencia(s) guardada(s).`) setOpen(false) } catch (e: any) { toast.error(e?.message ?? "No se pudo guardar") } finally { setSaving(false) } } // Acciones function actionTrim() { const next = parseLines(text).map((s) => s.replace(/\s+/g, " ").trim()) setText(next.join("\n")) } function actionDedupe() { const seen = new Set() const next: string[] = [] for (const l of parseLines(text)) { const k = l.toLowerCase() if (!seen.has(k)) { seen.add(k); next.push(l) } } setText(next.join("\n")) } function actionSort() { const next = [...parseLines(text)].sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" })) setText(next.join("\n")) } async function actionImportClipboard() { try { const clip = await navigator.clipboard.readText() if (!clip) { toast("Portapapeles vacío"); return } const next = [...parseLines(text), ...parseLines(clip)] setText(next.join("\n")) toast.success("Texto importado") } catch (e: any) { toast.error(e?.message ?? "No se pudo leer el portapapeles") } } async function actionExportClipboard() { try { await navigator.clipboard.writeText(parseLines(text).join("\n")) toast.success("Copiado al portapapeles") } catch (e: any) { toast.error(e?.message ?? "No se pudo copiar") } } // Atajo guardar useEffect(() => { function onKey(e: KeyboardEvent) { if (!open) return if (e.key === "s" && (e.ctrlKey || e.metaKey)) { e.preventDefault() if (!saving && dirty) void save() } } window.addEventListener("keydown", onKey) return () => window.removeEventListener("keydown", onKey) // eslint-disable-next-line react-hooks/exhaustive-deps }, [open, saving, dirty, text]) return ( <>
Editar bibliografía Una referencia por línea. Guarda con Ctrl/⌘+S.
{lines.length} {dirty ? Sin guardar : OK}
{/* Barra de acciones (compacta) */}
{/* Editor */}