refactor: renombra Subtareas a Tareas en todo el sistema
Actividad -> Tarea, sin nivel "sub" intermedio (no hay una entidad "Tarea" de la que estas fueran sub-, asi que el prefijo sobraba). Tabla subtasks -> tasks (RENAME TABLE idempotente en la migracion 005; 001_initial.sql tambien actualizado para que una instalacion nueva cree la tabla ya con el nombre final, sin pasar por el nombre viejo). Modulo backend, rutas, servicio frontend, pestaña del detalle de actividad, i18n y documentacion actualizados. Sin cambios de comportamiento, solo nombres. 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 } 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 { listSubtasks, createSubtask, setSubtaskDone, deleteSubtask } from '../../services/subtasks.js';
|
||||
import { listTasks, createTask, setTaskDone, deleteTask } from '../../services/tasks.js';
|
||||
import { listCatalog } from '../../services/catalogs.js';
|
||||
import { listUsers } from '../../services/users.js';
|
||||
import { Button } from '../../components/Button.jsx';
|
||||
@@ -29,17 +29,17 @@ export default function ActivityDetailPanel({ activity, projectId, siblingActivi
|
||||
const { data: priorities } = useAsync(() => listCatalog('priorities'), []);
|
||||
const { data: statuses } = useAsync(() => listCatalog('activity-statuses'), []);
|
||||
const { data: users } = useAsync(listUsers, []);
|
||||
const { data: subtasks, reload: reloadSubtasks } = useAsync(() => listSubtasks(activity.id), [activity.id]);
|
||||
const { data: tasks, reload: reloadTasks } = useAsync(() => listTasks(activity.id), [activity.id]);
|
||||
|
||||
const [tab, setTab] = useState('detail');
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [form, setForm] = useState(() => toForm(activity));
|
||||
const [newSubtask, setNewSubtask] = useState('');
|
||||
const [newTask, setNewTask] = useState('');
|
||||
const [addingDep, setAddingDep] = useState('');
|
||||
|
||||
useEffect(() => { setForm(toForm(activity)); setEditing(false); }, [activity]);
|
||||
|
||||
const hasSubtasks = subtasks && subtasks.length > 0;
|
||||
const hasTasks = tasks && tasks.length > 0;
|
||||
const priorityOptions = (priorities || []).map((p) => ({ value: p.id, label: p.name }));
|
||||
const statusOptions = (statuses || []).map((s) => ({ value: s.id, label: s.name }));
|
||||
const userOptions = (users || []).map((u) => ({ value: u.id, label: u.name }));
|
||||
@@ -56,22 +56,22 @@ export default function ActivityDetailPanel({ activity, projectId, siblingActivi
|
||||
});
|
||||
}
|
||||
|
||||
async function addSubtaskItem() {
|
||||
if (!newSubtask.trim()) return;
|
||||
async function addTaskItem() {
|
||||
if (!newTask.trim()) return;
|
||||
await runAction(async () => {
|
||||
await createSubtask(activity.id, { name: newSubtask.trim() });
|
||||
setNewSubtask('');
|
||||
await reloadSubtasks();
|
||||
await createTask(activity.id, { name: newTask.trim() });
|
||||
setNewTask('');
|
||||
await reloadTasks();
|
||||
onChanged();
|
||||
});
|
||||
}
|
||||
|
||||
async function toggleSubtask(s) {
|
||||
await runAction(async () => { await setSubtaskDone(s.id, !s.is_done); await reloadSubtasks(); onChanged(); });
|
||||
async function toggleTask(task) {
|
||||
await runAction(async () => { await setTaskDone(task.id, !task.is_done); await reloadTasks(); onChanged(); });
|
||||
}
|
||||
|
||||
async function removeSubtaskItem(s) {
|
||||
await runAction(async () => { await deleteSubtask(s.id); await reloadSubtasks(); onChanged(); });
|
||||
async function removeTaskItem(task) {
|
||||
await runAction(async () => { await deleteTask(task.id); await reloadTasks(); onChanged(); });
|
||||
}
|
||||
|
||||
async function addDep() {
|
||||
@@ -88,7 +88,7 @@ export default function ActivityDetailPanel({ activity, projectId, siblingActivi
|
||||
<Tabs
|
||||
tabs={[
|
||||
{ key: 'detail', label: t('tab_info') },
|
||||
{ key: 'subtasks', label: t('subtasks_title') },
|
||||
{ key: 'tasks', label: t('tasks_title') },
|
||||
{ key: 'dependencies', label: t('activity_dependencies') },
|
||||
{ key: 'comments', label: t('tab_comments') },
|
||||
{ key: 'documents', label: t('tab_documents') },
|
||||
@@ -167,7 +167,7 @@ export default function ActivityDetailPanel({ activity, projectId, siblingActivi
|
||||
{t('activity_milestone')}
|
||||
</label>
|
||||
</Field>
|
||||
{!hasSubtasks && (
|
||||
{!hasTasks && (
|
||||
<Field label={t('activity_progress')}>
|
||||
<input className="input" type="number" min={0} max={100} value={form.progressPct} onChange={(e) => setForm((f) => ({ ...f, progressPct: e.target.value }))} />
|
||||
</Field>
|
||||
@@ -178,19 +178,19 @@ export default function ActivityDetailPanel({ activity, projectId, siblingActivi
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tab === 'subtasks' && (
|
||||
{tab === 'tasks' && (
|
||||
<div>
|
||||
{(subtasks || []).map((s) => (
|
||||
<div key={s.id} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '5px 0' }}>
|
||||
<input type="checkbox" checked={!!s.is_done} onChange={() => toggleSubtask(s)} />
|
||||
<span style={{ flex: 1, fontSize: '.8rem', textDecoration: s.is_done ? 'line-through' : 'none', opacity: s.is_done ? .6 : 1 }}>{s.name}</span>
|
||||
<button className="icon-btn" onClick={() => removeSubtaskItem(s)}><Trash2 size={15} /></button>
|
||||
{(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" onClick={() => removeTaskItem(task)}><Trash2 size={15} /></button>
|
||||
</div>
|
||||
))}
|
||||
{(!subtasks || subtasks.length === 0) && <div className="text-muted" style={{ fontSize: '.78rem' }}>{t('common_empty')}</div>}
|
||||
{(!tasks || tasks.length === 0) && <div className="text-muted" style={{ fontSize: '.78rem' }}>{t('common_empty')}</div>}
|
||||
<div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
|
||||
<input className="input" value={newSubtask} placeholder={t('subtask_new')} onChange={(e) => setNewSubtask(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && addSubtaskItem()} />
|
||||
<Button variant="standard" icon={Plus} onClick={addSubtaskItem}>{t('common_new')}</Button>
|
||||
<input className="input" value={newTask} placeholder={t('task_new')} onChange={(e) => setNewTask(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && addTaskItem()} />
|
||||
<Button variant="standard" icon={Plus} onClick={addTaskItem}>{t('common_new')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user