import { useState } from 'react'; import { Plus, Save, Trash2, Share2, UserPlus, GripVertical } from 'lucide-react'; import { useUi } from '../../context/ui-context.jsx'; import { useAsync, useAction } from '../../hooks/useAsync.js'; import { useDragReorder } from '../../hooks/useDragReorder.js'; import { listNotes, createNote, updateNote, deleteNote, reorderNotes, listNoteShares, addNoteShare, removeNoteShare, } from '../../services/notes.js'; import { listUsers } from '../../services/users.js'; import { Button } from '../../components/Button.jsx'; import { Field } from '../../components/Field.jsx'; import { ComboBox } from '../../components/ComboBox.jsx'; import { DetailPopup } from '../../components/DetailPopup.jsx'; import { SearchInput } from '../../components/SearchInput.jsx'; function formatDateTime(value) { return String(value).slice(0, 16).replace('T', ' '); } export default function NotasPage() { const { t, pushToast, openDialog } = useUi(); const runAction = useAction(); const { data: notes, reload } = useAsync(listNotes, []); const { data: users } = useAsync(listUsers, []); const [selected, setSelected] = useState(null); // nota abierta en el popup (null = cerrado) const [creating, setCreating] = useState(false); const [content, setContent] = useState(''); const [shares, setShares] = useState([]); const [sharingWith, setSharingWith] = useState(''); const [search, setSearch] = useState(''); function openCreate() { setSelected(null); setCreating(true); setContent(''); setShares([]); setSharingWith(''); } async function openNote(note) { setSelected(note); setCreating(false); setContent(note.content); setSharingWith(''); setShares(note.is_owner ? await listNoteShares(note.id) : []); } function closePopup() { setSelected(null); setCreating(false); } async function save() { if (!content.trim()) return; await runAction(async () => { if (creating) { await createNote(content); } else { await updateNote(selected.id, content); } closePopup(); await reload(); }); } function confirmDelete() { 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 deleteNote(selected.id); closePopup(); await reload(); pushToast({ tone: 'success', message: t('common_saved') }); }), }, ], }); } const userOptions = (users || []) .filter((u) => !shares.some((s) => s.user_id === u.id)) .map((u) => ({ value: u.id, label: u.name })); async function addShare() { if (!sharingWith) return; await runAction(async () => { setShares(await addNoteShare(selected.id, Number(sharingWith))); setSharingWith(''); await reload(); // el ícono de compartida de la tarjeta usa share_count de la lista principal }); } function removeShare(userId) { runAction(async () => { setShares(await removeNoteShare(selected.id, userId)); await reload(); }); } const query = search.trim().toLowerCase(); const filteredNotes = query ? (notes || []).filter((n) => n.content.toLowerCase().includes(query)) : (notes || []); const ownNotes = filteredNotes.filter((n) => n.is_owner); const sharedNotes = filteredNotes.filter((n) => !n.is_owner); // Solo se puede arrastrar lo propio, y no mientras hay un filtro de busqueda activo (el orden // de un subconjunto filtrado no tiene un significado claro que persistir). const dragEnabled = !query; const { order: orderedOwnNotes, getHandleProps, getRowProps, isDragging } = useDragReorder(ownNotes, async (ids) => { await reorderNotes(ids); await reload(); }); const displayedOwnNotes = dragEnabled ? orderedOwnNotes : ownNotes; if (!notes) return