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:
Milton Gonzalez
2026-09-10 08:55:58 -05:00
parent 29daff82ed
commit bb6bd6865c
25 changed files with 191 additions and 172 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "project-linkedcorp-frontend",
"private": true,
"version": "1.7.0",
"version": "1.8.0",
"type": "module",
"scripts": {
"dev": "vite",
+6 -6
View File
@@ -168,9 +168,9 @@ export const messages = {
activity_dependencies: 'Dependencias',
activity_add_dependency: 'Agregar dependencia',
activity_depends_on: 'Depende de',
subtasks_title: 'Subtareas',
subtask_new: 'Nueva subtarea',
subtask_done: 'Hecha',
tasks_title: 'Tareas',
task_new: 'Nueva tarea',
task_done: 'Hecha',
due_on_time: 'En tiempo',
due_warning: 'Proximo a vencer',
@@ -376,9 +376,9 @@ export const messages = {
activity_dependencies: 'Dependencies',
activity_add_dependency: 'Add dependency',
activity_depends_on: 'Depends on',
subtasks_title: 'Subtasks',
subtask_new: 'New subtask',
subtask_done: 'Done',
tasks_title: 'Tasks',
task_new: 'New task',
task_done: 'Done',
due_on_time: 'On time',
due_warning: 'Due soon',
@@ -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>
)}
-7
View File
@@ -1,7 +0,0 @@
import { api } from './api.js';
export const listSubtasks = (activityId) => api.get(`/activities/${activityId}/subtasks`);
export const createSubtask = (activityId, data) => api.post(`/activities/${activityId}/subtasks`, data);
export const updateSubtask = (id, data) => api.put(`/subtasks/${id}`, data);
export const setSubtaskDone = (id, isDone) => api.patch(`/subtasks/${id}/done`, { isDone });
export const deleteSubtask = (id) => api.delete(`/subtasks/${id}`);
+7
View File
@@ -0,0 +1,7 @@
import { api } from './api.js';
export const listTasks = (activityId) => api.get(`/activities/${activityId}/tasks`);
export const createTask = (activityId, data) => api.post(`/activities/${activityId}/tasks`, data);
export const updateTask = (id, data) => api.put(`/tasks/${id}`, data);
export const setTaskDone = (id, isDone) => api.patch(`/tasks/${id}/done`, { isDone });
export const deleteTask = (id) => api.delete(`/tasks/${id}`);