feat: boton editar (lapiz) en cada tarea de una actividad
El backend ya soportaba renombrar una tarea (PUT /api/tasks/:id), solo faltaba el boton en la UI. Edicion en linea: input + guardar (Enter o icono) / cancelar (Escape o icono). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import { Pencil, Save, X, Plus, Trash2, Link2, Repeat } from 'lucide-react';
|
||||
import { useUi } from '../../context/ui-context.jsx';
|
||||
import { useAsync, useAction } from '../../hooks/useAsync.js';
|
||||
import { updateActivity, addDependency, removeDependency } from '../../services/activities.js';
|
||||
import { listTasks, createTask, setTaskDone, deleteTask } from '../../services/tasks.js';
|
||||
import { listTasks, createTask, updateTask, setTaskDone, deleteTask } from '../../services/tasks.js';
|
||||
import { promoteTaskToRecurring } from '../../services/recurringTasks.js';
|
||||
import { listCatalog } from '../../services/catalogs.js';
|
||||
import { listUsers } from '../../services/users.js';
|
||||
@@ -39,6 +39,8 @@ export default function ActivityDetailPanel({ activity, projectId, siblingActivi
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [form, setForm] = useState(() => toForm(activity));
|
||||
const [newTask, setNewTask] = useState('');
|
||||
const [editingTaskId, setEditingTaskId] = useState(null);
|
||||
const [editingTaskName, setEditingTaskName] = useState('');
|
||||
const [addingDep, setAddingDep] = useState('');
|
||||
const [promoting, setPromoting] = useState(null);
|
||||
const [promoteForm, setPromoteForm] = useState(null);
|
||||
@@ -80,6 +82,26 @@ export default function ActivityDetailPanel({ activity, projectId, siblingActivi
|
||||
await runAction(async () => { await deleteTask(task.id); await reloadTasks(); onChanged(); });
|
||||
}
|
||||
|
||||
function startEditTask(task) {
|
||||
setEditingTaskId(task.id);
|
||||
setEditingTaskName(task.name);
|
||||
}
|
||||
|
||||
function cancelEditTask() {
|
||||
setEditingTaskId(null);
|
||||
setEditingTaskName('');
|
||||
}
|
||||
|
||||
async function saveEditTask() {
|
||||
if (!editingTaskName.trim()) return;
|
||||
await runAction(async () => {
|
||||
await updateTask(editingTaskId, { name: editingTaskName.trim() });
|
||||
cancelEditTask();
|
||||
await reloadTasks();
|
||||
onChanged();
|
||||
});
|
||||
}
|
||||
|
||||
function startPromote(task) {
|
||||
setPromoting(task);
|
||||
setPromoteForm({
|
||||
@@ -208,10 +230,25 @@ export default function ActivityDetailPanel({ activity, projectId, siblingActivi
|
||||
<div>
|
||||
{(tasks || []).map((task) => (
|
||||
<div key={task.id} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '5px 0' }}>
|
||||
<input type="checkbox" checked={!!task.is_done} onChange={() => toggleTask(task)} />
|
||||
<span style={{ flex: 1, fontSize: '.8rem', textDecoration: task.is_done ? 'line-through' : 'none', opacity: task.is_done ? .6 : 1 }}>{task.name}</span>
|
||||
<button className="icon-btn" title={t('recurring_task_promote')} onClick={() => startPromote(task)}><Repeat size={15} /></button>
|
||||
<button className="icon-btn" onClick={() => removeTaskItem(task)}><Trash2 size={15} /></button>
|
||||
{editingTaskId === task.id ? (
|
||||
<>
|
||||
<input
|
||||
className="input" style={{ flex: 1 }} value={editingTaskName} autoFocus
|
||||
onChange={(e) => setEditingTaskName(e.target.value)}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') saveEditTask(); if (e.key === 'Escape') cancelEditTask(); }}
|
||||
/>
|
||||
<button className="icon-btn" title={t('common_save')} onClick={saveEditTask}><Save size={15} /></button>
|
||||
<button className="icon-btn" title={t('common_cancel')} onClick={cancelEditTask}><X size={15} /></button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<input type="checkbox" checked={!!task.is_done} onChange={() => toggleTask(task)} />
|
||||
<span style={{ flex: 1, fontSize: '.8rem', textDecoration: task.is_done ? 'line-through' : 'none', opacity: task.is_done ? .6 : 1 }}>{task.name}</span>
|
||||
<button className="icon-btn" title={t('common_edit')} onClick={() => startEditTask(task)}><Pencil size={15} /></button>
|
||||
<button className="icon-btn" title={t('recurring_task_promote')} onClick={() => startPromote(task)}><Repeat size={15} /></button>
|
||||
<button className="icon-btn" onClick={() => removeTaskItem(task)}><Trash2 size={15} /></button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{(!tasks || tasks.length === 0) && <div className="text-muted" style={{ fontSize: '.78rem' }}>{t('common_empty')}</div>}
|
||||
|
||||
Reference in New Issue
Block a user