chore: estructura inicial del MVP
Backend (Express + MariaDB) y frontend (React + Vite, identidad Fluent/Windows) con el nucleo funcional: seguridad, organizacion, proyectos, actividades, subtareas, dependencias, dashboard, calendario, gantt, comentarios, adjuntos, notificaciones in-app y catalogos. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import { useState } from 'react';
|
||||
import { Send } from 'lucide-react';
|
||||
import { useUi } from '../context/ui-context.jsx';
|
||||
import { useAsync, useAction } from '../hooks/useAsync.js';
|
||||
import { listComments, createComment } from '../services/comments.js';
|
||||
import { Button } from './Button.jsx';
|
||||
|
||||
export function CommentsPanel({ entityType, entityId }) {
|
||||
const { t } = useUi();
|
||||
const runAction = useAction();
|
||||
const { data: comments, loading, reload } = useAsync(() => listComments(entityType, entityId), [entityType, entityId]);
|
||||
const [message, setMessage] = useState('');
|
||||
|
||||
async function send() {
|
||||
if (!message.trim()) return;
|
||||
await runAction(async () => {
|
||||
await createComment(entityType, entityId, message.trim());
|
||||
setMessage('');
|
||||
await reload();
|
||||
});
|
||||
}
|
||||
|
||||
if (loading || !comments) return <div className="text-muted">{t('common_loading')}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{comments.length === 0 && <div className="text-muted" style={{ marginBottom: 12 }}>{t('common_empty')}</div>}
|
||||
{comments.map((c) => (
|
||||
<div key={c.id} style={{ padding: '8px 0', borderBottom: '1px solid rgb(var(--line) / .5)' }}>
|
||||
<div style={{ fontSize: '.78rem', fontWeight: 700 }}>{c.user_name} <span className="text-muted" style={{ fontWeight: 400 }}>· {c.created_at}</span></div>
|
||||
<div style={{ fontSize: '.82rem', marginTop: 2 }}>{c.message}</div>
|
||||
</div>
|
||||
))}
|
||||
<div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
|
||||
<input className="input" value={message} placeholder={t('comment_placeholder')} onChange={(e) => setMessage(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && send()} />
|
||||
<Button variant="accent" icon={Send} onClick={send}>{t('comment_send')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user