Página /tasks — 4 mejoras s56 (PR#11)
Página /tasks — 4 mejoras s56 (PR#11)
Conjunto de cuatro mejoras funcionales en la página /tasks del workspace, implementadas en el sprint 56 (s56) a petición de Edu (PM). Mergeado el 2026-05-11.
Resumen de cambios
| # | Commit | Área | Descripción |
|---|---|---|---|
| 1 | 3fd6925 | Dashboard widget | Scroll en TasksWidget + fetch 5→20 pending |
| 2 | 1954b0f | Preferencias UI | Fix fontSize para clases Tailwind (data-font-size) |
| 3 | b6091a6 | TaskModal / TaskManager | Multi-select asignado (Edu/Dani/Txell toggle) |
| 4 | fc6d178 | TaskModal + API + D1 | Vinculación de notas del Cuaderno (linked_note_ids) |
Cambio 1 · Widget Tareas Dashboard — Scroll + Fetch ampliado
Archivo: src/components/dashboard/TasksWidget.tsx
El widget del dashboard que muestra tareas pendientes crecía indefinidamente si había muchas tareas. Se añadió:
maxHeight: calc(7 * 1.875rem)+overflowY: autoen el contenedor de la lista.- El fetch de tareas pendientes sube de
limit=5alimit=20para que el scroll tenga contenido real. - Se elimina el
.slice(0, 4)que recortaba aún más el resultado.
Antes: lista sin altura máxima, mostraba solo 4 items sin scroll.
Después: hasta 20 items, scroll interno, altura máxima equivalente a 7 filas.
Cambio 2 · Fix Preferencias fontSize con Tailwind
Archivo: src/styles/globals.css
Las preferencias de tamaño de texto del workspace (data-font-size="sm|md|lg|xl") solo escalaban el font-size del body. Los componentes que usaban clases Tailwind (text-xs, text-sm, text-base, text-lg, text-xl) ignoraban la preferencia porque Tailwind genera valores estáticos.
Solución: overrides CSS explícitos por cada combinación [data-font-size] .text-{size}:
| Atributo | text-xs | text-sm | text-base | text-lg | text-xl |
|---|---|---|---|---|---|
sm | 0.7rem | 0.8125rem | 0.9375rem | 1.05rem | 1.2rem |
md | (Tailwind default) | — | — | — | — |
lg | 0.825rem | 0.95rem | 1.0625rem | 1.225rem | 1.4rem |
xl | 0.9rem | 1.05rem | 1.2rem | 1.4rem | 1.6rem |
Rango ampliado: 13–19 px (antes 14–17 px). .card-title escala de forma independiente (diferencial más suave).
Feedback original de Edu: “los ajustes solo afectan a títulos, el cuerpo de tasks no cambia”.
Cambio 3 · Multi-select Asignado
Archivos: src/components/tasks/TaskModal.tsx, src/components/tasks/TaskManager.tsx, src/lib/constants.ts
Cambios en constants.ts
TEAM_MEMBERSpasa de['Edu', 'Dani', 'Txell', 'Equipo']a['Edu', 'Dani', 'Txell'].TEAM_FILTERqueda como['Todos', 'Edu', 'Dani', 'Txell'].- Nueva función exportada
parseAssignees(assignee: string | null | undefined): string[]:null/''→[]'Equipo'(legacy) →['Edu', 'Dani', 'Txell']'Edu, Dani'→['Edu', 'Dani']'Edu'→['Edu']
ASSIGNEE_COLORS['Equipo']se mantiene para compatibilidad visual con datos históricos.
Cambios en TaskModal.tsx
El <select> de asignado se reemplaza por 3 botones toggle (uno por miembro del equipo):
- Estado:
selectedAssignees: string[](antesassignee: string). - Visual: fondo coloreado + checkmark
✓cuando está activo,aria-pressedpara accesibilidad. - Al guardar, se serializa:
assignee: selectedAssignees.join(', ')→ el backend sigue recibiendo unstring. created_byusaselectedAssignees[0]en lugar deassignee.- Tareas con
assignee='Equipo'legacy se expanden a los 3 al editar.
Cambios en TaskManager.tsx
El filtro por asignado ahora soporta multi-asignación:
// Una tarea matchea 'Edu' si su campo assignee contiene ese nombre
if (filterAssignee !== 'Todos') {
const isLegacyTeam = t.assignee === 'Equipo';
const explicitNames = (t.assignee || '').split(',').map((s) => s.trim());
const matches = isLegacyTeam || explicitNames.includes(filterAssignee);
if (!matches) return false;
}
El borde derecho del último botón del filtro se calcula dinámicamente: a === TEAM_FILTER[TEAM_FILTER.length - 1].
Decisión de diseño
‘Equipo’ eliminado como opción nueva: si se quiere asignar a todos, se marcan los 3 individualmente. Legacy en BD y
ASSIGNEE_COLORSse preserva. Decisión Edu (s56, punto 2).
Cambio 4 · Notas del Cuaderno vinculadas a Tareas
Archivos: src/components/tasks/TaskModal.tsx, functions/api/tasks/index.ts, functions/api/tasks/[id]/index.ts, migrations/0023_tasks_linked_notes.sql
Modelo de datos
Nueva columna en la tabla tasks de D1/SQLite:
-- migrations/0023_tasks_linked_notes.sql
ALTER TABLE tasks ADD COLUMN linked_note_ids TEXT DEFAULT '[]';
- Tipo
TEXTporque D1/SQLite no tiene array nativo. - Valor: JSON array de enteros (
[1, 5, 12]). - Default
'[]'→ tareas existentes siguen válidas sin migración de datos.
Interfaz TypeScript
// TaskModal.tsx — Task interface
linked_note_ids?: number[]; // optional, compat con tareas pre-migration 0023
// NotebookNote interface (nueva)
export interface NotebookNote {
id: number;
title: string;
pinned: number;
updated_at: string;
}
API: POST y PUT /api/tasks
Ambos endpoints (index.ts y [id]/index.ts) reciben linked_note_ids?: number[] en el body:
const linkedNoteIdsJson = JSON.stringify(
Array.isArray(body.linked_note_ids)
? body.linked_note_ids.filter((n) => typeof n === 'number' && Number.isFinite(n))
: [],
);
En el PUT, si linked_note_ids es undefined, la columna no se modifica (COALESCE(?, linked_note_ids) con null).
UX en TaskModal
Nuevo bloque “Notas del Cuaderno” en el modal:
- Chips de notas vinculadas: cada nota vinculada aparece como chip con enlace a
/tools/notebook?id=Ny botón×para desvincular.- Si la nota no es del usuario actual (scope user), aparece como
"Nota privada #N"en cursiva y sin enlace.
- Si la nota no es del usuario actual (scope user), aparece como
- Select para vincular existentes: carga las notas del usuario vía
GET /api/notebookal abrir el modal. En modoisLocal(tarea nueva sin ID), la lista queda vacía. - Botón ”+ Crear nota nueva”:
POST /api/notebookcon título"Notas · {task.title}"y vinculación automática.
Privacidad de notas
Las notas son scope user: GET /api/notebook solo devuelve las del usuario autenticado, y GET /api/notebook/:id rechaza al no-propietario. Una task compartida puede tener vinculada una nota de Edu; Dani la verá como "Nota privada #N" sin acceso al contenido. Comportamiento intencional y coherente con el modelo de privacidad del Cuaderno.
Archivos modificados
| Archivo | Tipo de cambio |
|---|---|
src/components/dashboard/TasksWidget.tsx | UI: scroll + fetch |
src/components/tasks/TaskModal.tsx | UI: multi-select + notas vinculadas |
src/components/tasks/TaskManager.tsx | Lógica: filtro multi-asignado |
src/lib/constants.ts | Constantes: TEAM_MEMBERS, parseAssignees |
src/styles/globals.css | CSS: fontSize overrides para Tailwind |
functions/api/tasks/index.ts | API: POST con linked_note_ids |
functions/api/tasks/[id]/index.ts | API: PUT con linked_note_ids |
migrations/0023_tasks_linked_notes.sql | D1: nueva columna |
Véase también
- [[workspace—tareas-notas]]
- [[workspace—que-es-workspace]]
- [[workspace—noticias-alertas]]