Tasks s56: multi-asignado, notas vinculadas y mejoras de UI
Resumen
Sprint 56 (11-05-2026) introduce cuatro mejoras coordinadas en el módulo de Tareas del Workspace interno de CreaRack:
| # | Mejora | Archivos principales |
|---|---|---|
| 1 | Vinculación tarea ↔ nota del Cuaderno (linked_note_ids) | migrations/0023_tasks_linked_notes.sql, functions/api/tasks/index.ts, functions/api/tasks/[id]/index.ts |
| 2 | Multi-asignado (selector de toggle por persona) | src/components/tasks/TaskModal.tsx, src/lib/constants.ts |
| 3 | TasksWidget ampliado (limit 5→20, scroll contenedor) | src/components/dashboard/TasksWidget.tsx |
| 4 | Fix font-size: Tailwind overrides para que la preferencia de tamaño escale en todas las páginas | src/styles/globals.css |
1. Vinculación tarea ↔ nota del Cuaderno
Migración D1 — 0023_tasks_linked_notes.sql
ALTER TABLE tasks ADD COLUMN linked_note_ids TEXT DEFAULT '[]';
- Tipo TEXT con JSON array de IDs (
number[]). D1/SQLite no tiene tipo nativo de array. - Default
'[]'→ tareas existentes siguen siendo válidas sin migración de datos. - Privacidad intencional: las notas son scope-user (cada miembro solo ve las suyas). Una tarea vinculada a una nota de Edu se mostrará a Dani como
"Nota privada #N"— el endpointGET /api/notebook/<id>ya rechaza al no-owner.
API
POST /api/tasks — nuevo campo opcional linked_note_ids?: number[]. Se serializa a JSON antes de insertar:
const linkedNoteIdsJson = JSON.stringify(
Array.isArray(body.linked_note_ids)
? body.linked_note_ids.filter((n) => typeof n === 'number' && Number.isFinite(n))
: [],
);
// INSERT ... linked_note_ids VALUES (?, ..., linkedNoteIdsJson)
PUT /api/tasks/:id — mismo campo. Lógica COALESCE: si el caller no envía linked_note_ids (undefined), se pasa null a COALESCE(?, linked_note_ids) y la columna no se toca.
UI — TaskModal.tsx
- Estado
linkedNoteIds: number[]— normalizado desde el raw del backend (puede llegar como string JSON o como array). - Carga de notas:
useEffectllamaGET /api/notebookal montar el modal (omitido en modo local/sin servidor). - Vincular nota existente:
<select>filtrado por notas del usuario no ya vinculadas. - Crear y vincular inline: botón
+ Crear nota nueva→POST /api/notebookcon título"Notas · <título de la tarea>"→ añade automáticamente el ID al array. - Chip de nota vinculada: muestra título si la nota es del usuario (con enlace a
/tools/notebook?id=<id>) o"Nota privada #N"en itálica si pertenece a otro miembro. - Desvincular: botón
×en cada chip filtra el ID del array.
2. Multi-asignado
src/lib/constants.ts
TEAM_MEMBERSpasa a['Edu', 'Dani', 'Txell']— se elimina'Equipo'como opción seleccionable nueva.'Equipo'se mantiene enASSIGNEE_COLORSpara compat visual con tareas históricas.- Nueva función exportada
parseAssignees(assignee):
// 'Equipo' → ['Edu', 'Dani', 'Txell'] (legacy expand)
// 'Edu, Dani' → ['Edu', 'Dani'] (multi)
// 'Edu' → ['Edu'] (single)
// null / '' → []
TaskModal.tsx
- Estado
selectedAssignees: string[]reemplaza aassignee: string. - UI: botones toggle por persona (
aria-pressed) con color por asignado (ASSIGNEE_COLORS). - Al guardar:
assignee = selectedAssignees.join(', ')→ mantiene compat con la columna string del backend. created_byyuploaded_byusanselectedAssignees[0](primer asignado).
TaskManager.tsx — filtro de asignado
El filtro filterAssignee ahora hace matching por nombre contenido en la cadena CSV:
const isLegacyTeam = t.assignee === 'Equipo';
const explicitNames = (t.assignee || '').split(',').map(s => s.trim());
const matches = isLegacyTeam || explicitNames.includes(filterAssignee);
Además, el borderRight del segmentado de filtros usa TEAM_FILTER[TEAM_FILTER.length - 1] en lugar del hardcoded 'Equipo'.
3. TasksWidget — límite ampliado y scroll
src/components/dashboard/TasksWidget.tsx:
GET /api/tasks?status=pending&limit=**20**(antes 5).setTasks(pending)— ya no hay.slice(0, 4).- El contenedor de lista tiene
maxHeight: calc(7 * 1.875rem)+overflowY: autopara contener la lista más larga sin desbordar el widget del dashboard.
4. Fix font-size — Tailwind overrides
src/styles/globals.css:
Problema: la preferencia de tamaño de fuente (data-font-size) solo afectaba a font-size del body base. Las páginas que usan clases Tailwind explícitas (text-sm, text-base, etc.) quedaban con el tamaño fijo de Tailwind, ignorando la preferencia.
Solución: overrides CSS por data-font-size × clase Tailwind:
body[data-font-size="sm"] .text-sm { font-size: 0.8125rem; }
body[data-font-size="lg"] .text-sm { font-size: 0.95rem; }
body[data-font-size="xl"] .text-sm { font-size: 1.05rem; }
/* ... idem para text-xs, text-base, text-lg, text-xl */
Los valores base también se ajustaron (sm: 15→13 px, lg: 15.5→16 px, xl: 17→19 px) para una escala más coherente.
Notas de compatibilidad
- BD backward-compat:
linked_note_ids DEFAULT '[]'→ cero data migrations. - Assignee backward-compat:
parseAssignees('Equipo')expande a los 3 miembros al editar tareas antiguas; al guardar se serializa como'Edu, Dani, Txell'. - API backward-compat:
linked_note_idses opcional en POST y PUT; si no se envía,COALESCEpreserva el valor existente.
Véase también
- [[workspace—tareas-notas]]
- [[workspace—que-es-workspace]]