import { useEffect, useState } from 'react'; import { Send, Pencil, Trash2, Save, X } from 'lucide-react'; import { useUi } from '../context/ui-context.jsx'; import { useAuth } from '../context/auth-context.jsx'; import { useAsync, useAction } from '../hooks/useAsync.js'; import { listComments, createComment, updateComment, deleteComment } from '../services/comments.js'; import { Button } from './Button.jsx'; import { useDraftForm, useUserDrafts } from '../hooks/useDraftForm.js'; const MANAGE_ROLES = ['superadmin', 'admin']; export function CommentsPanel({ entityType, entityId, onChanged }) { const { t, openDialog } = useUi(); const { user: currentUser } = useAuth(); const runAction = useAction(); const { data: comments, reload } = useAsync(() => listComments(entityType, entityId), [entityType, entityId]); const [editingId, setEditingId] = useState(null); // Borradores: comentario nuevo (por entidad) y edicion de un comentario existente. const scope = `${entityType}-${entityId}`; const newDraft = useDraftForm({ formId: 'comment-new', entityId: scope, initial: { message: '' }, label: t('comment_placeholder') }); const message = newDraft.values.message; const setMessage = (v) => newDraft.setField('message', v); const editingComment = (comments || []).find((c) => c.id === editingId); const editDraft = useDraftForm({ formId: 'comment-edit', entityId: editingId, initial: { message: editingComment?.message || '', editVersion: editingComment?.edit_version }, label: `${t('common_edit')}: ${(editingComment?.message || '').slice(0, 40)}`, enabled: !!editingComment, onDiscard: () => setEditingId(null), }); const editingText = editDraft.values.message; const setEditingText = (v) => editDraft.setField('message', v); // Al volver con un borrador de edicion de un comentario de esta entidad, se reabre la edicion. const drafts = useUserDrafts(); useEffect(() => { if (!comments || editingId) return; const d = drafts.find((x) => x.formId === 'comment-edit' && comments.some((c) => String(c.id) === String(x.entityId))); if (d) { setEditingId(Number(d.entityId)); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [comments]); async function send() { if (!message.trim()) return; await runAction(async () => { await createComment(entityType, entityId, message.trim()); newDraft.clear(); await reload(); onChanged?.(); // refresca el contador de la pestaña "Comentarios" en el padre }); } function canManage(c) { return c.user_id === currentUser?.id || MANAGE_ROLES.includes(currentUser?.roleCode); } function startEdit(c) { setEditingId(c.id); } function cancelEdit() { editDraft.discard(); setEditingId(null); } async function saveEdit(id) { if (!editingText.trim()) return; await editDraft.guardSave(() => runAction(async () => { await updateComment(id, editingText.trim(), editDraft.values.editVersion); editDraft.clear(); setEditingId(null); await reload(); })); } function confirmDelete(c) { openDialog({ tone: 'warning', title: t('common_confirm_delete_title'), message: t('common_confirm_delete_msg'), actions: [ { label: t('common_cancel'), variant: 'standard' }, { label: t('common_delete'), variant: 'danger', onClick: () => runAction(async () => { await deleteComment(c.id); await reload(); onChanged?.(); }), }, ], }); } if (!comments) return
{t('common_loading')}
; return (
{comments.length === 0 &&
{t('common_empty')}
} {comments.map((c) => (
{c.user_name} · {c.created_at}{c.updated_at !== c.created_at ? ` · ${t('notes_updated')}` : ''}
{editingId !== c.id && canManage(c) && ( <> )}
{editingId === c.id ? (
setEditingText(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') saveEdit(c.id); if (e.key === 'Escape') cancelEdit(); }} />
) : (
{c.message}
)}
))}
setMessage(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && send()} />
); }