CreaRack-SL

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:

#MejoraArchivos principales
1Vinculació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
2Multi-asignado (selector de toggle por persona)src/components/tasks/TaskModal.tsx, src/lib/constants.ts
3TasksWidget ampliado (limit 5→20, scroll contenedor)src/components/dashboard/TasksWidget.tsx
4Fix font-size: Tailwind overrides para que la preferencia de tamaño escale en todas las páginassrc/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 endpoint GET /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: useEffect llama GET /api/notebook al 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/notebook con 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_MEMBERS pasa a ['Edu', 'Dani', 'Txell'] — se elimina 'Equipo' como opción seleccionable nueva.
  • 'Equipo' se mantiene en ASSIGNEE_COLORS para 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 a assignee: 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_by y uploaded_by usan selectedAssignees[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: auto para 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_ids es opcional en POST y PUT; si no se envía, COALESCE preserva el valor existente.

Véase también

  • [[workspace—tareas-notas]]
  • [[workspace—que-es-workspace]]