Files
fn_registry/dev/issues/0063-kanban-stickers.md

8.1 KiB

id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
id title status type domain scope priority depends blocks related created updated tags
0063 kanban: sistema de stickers (emojis) sobre cards pendiente feature
kanban
multi-app media
2026-05-17 2026-05-17

0063 — kanban: sistema de stickers (emojis) sobre cards

APP Metadata

Campo Valor
ID 0063
Estado pendiente
Prioridad media
Tipo feature — apps/kanban/

Dependencias

  • Ninguna. Aplica TBD obligatorio (.claude/rules/apps_tbd.md): trabajar en issue/0063-stickers, merge --no-ff a master.

Objetivo

Permitir que el usuario "decore" visualmente las cards del tablero pegandoles emojis (stickers) con transparencia. Util para marcar estados visualmente sin abusar de tags ni colores: 🔥 (urgente), (revisado), ⚠️ (cuidado), 🚀 (lanzado), ❤️ (importante), 💀 (bloqueado), etc.

Contexto

apps/kanban/ es una app Go que embebe un frontend React + Mantine v9 + dnd-kit. El tablero esta en frontend/src/App.tsx y cada card se renderiza en frontend/src/components/KanbanCard.tsx. Las cards ya tienen color (color), tags, asignado, bloqueo, historial y descripcion. Faltan stickers como capa decorativa libre, separada del modelo de tags.

UX deseada:

  1. Usuario hace clic en boton "Stickers" de la toolbar del board (App.tsx:920).
  2. Aparece picker con lista de emojis predefinidos.
  3. Usuario selecciona uno → entra en "modo pegar sticker" (cursor cambia, indicador visible).
  4. Hace clic sobre cualquier card → el emoji se pinta encima de la card con opacidad (~0.6).
  5. ESC sale del modo. Click derecho sobre un sticker existente lo borra.
  6. Stickers persisten en BD para que sobrevivan recargas y todos los usuarios los vean.

Decisiones de diseno (a confirmar antes de implementar)

Decision Recomendacion default Alternativas
Persistencia BD (campo stickers JSON en cards) localStorage por usuario
Cantidad por card Multiples apilados Uno solo
Posicion Arrastrable dentro de la card Esquina/centrado fijo
Tamaño / opacidad Fijos: 48px, 0.6 Configurables por sticker
Fuente emojis Lista hardcoded ~20 emoji-mart, picker nativo
UX modo activo Cursor especial + ESC sale Toggle persistente
Borrado Click derecho sobre sticker Modo "quitar sticker"

Confirmar con usuario antes de Fase 1. Si elige defaults, proceder.

Arquitectura

Archivos afectados

Backend Go (apps/kanban/):

  • migrations/00X_add_stickers.sql (NEW) — ALTER TABLE cards ADD COLUMN stickers TEXT NOT NULL DEFAULT '[]'.
  • db.go — leer/escribir columna stickers en Card.
  • handlers.go — endpoint PATCH /api/cards/:id/stickers (body: {"stickers":[{emoji,x,y}]}). Reusar el patron de updateCard.
  • tools.go — exponer mutacion al chat agent si aplica (opcional).

Frontend (apps/kanban/frontend/src/):

  • types.ts — añadir Sticker { emoji: string; x: number; y: number; } y Card.stickers: Sticker[].
  • api.ts (NEW endpoint helper) — updateCardStickers(id, stickers).
  • App.tsx — boton "Stickers" en toolbar (:920), estado pickerOpen, activeSticker (emoji seleccionado en modo pegar), handler global ESC, handler onCardSticker(cardId, x, y).
  • components/KanbanCard.tsx — render overlay absoluto de card.stickers (z-index alto, pointer-events: none salvo en modo borrado), cuando hay activeSticker cambiar cursor + capturar onClick para añadir sticker en coords relativas.
  • components/StickerPicker.tsx (NEW) — Popover con grid de emojis predefinidos.

Pure / impure split:

  • Pure: util relativeCoords(event, cardEl) → {x,y} (frontend, en components/format.ts o nuevo stickers.ts).
  • Impure: handler de PATCH HTTP, mutacion de BD, broadcast de cambio si hay realtime.

Funciones del registry a reutilizar

Auditar antes de escribir:

sqlite3 registry.db "SELECT id, description FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'name:emoji OR description:emoji OR name:sticker OR description:sticker');"

Probable: nada existe. Si relativeCoords resulta reusable → delegar a fn-constructor y crear dom_relative_coords_ts_core o similar (evaluar tras decision UX).

Tareas

Fase 1 — confirmar diseno

1.1 Confirmar con usuario las 7 decisiones de la tabla. Si elige defaults, seguir. 1.2 Auditar registry: fn search "emoji", fn search "sticker". Documentar reutilizacion en este issue.

Fase 2 — backend

2.1 Crear migracion migrations/00X_add_stickers.sql. 2.2 Tipo Go Sticker { Emoji string; X, Y float64 } en db.go. Card.Stickers []Sticker con custom marshalling JSON ↔ TEXT. 2.3 Endpoint PATCH /api/cards/:id/stickers en handlers.go. Validar payload (emoji no vacio, x/y en [0,1]). 2.4 Aplicar migracion al arrancar (mecanismo existente de apps/kanban/migrations).

Fase 3 — frontend tipos + api

3.1 Añadir Sticker y Card.stickers en types.ts. 3.2 Añadir updateCardStickers(id, stickers) en api.ts.

Fase 4 — UI: picker + modo activo

4.1 Crear components/StickerPicker.tsx con lista hardcoded (≈20 emojis: 🔥⚠️🚀💀🎯❤️👀💡📌🐛🎉🙏🤔😅🚧🟢🔴). 4.2 En App.tsx: nuevo state activeSticker: string | null. Boton "Stickers" con IconMoodSmile o similar de @tabler/icons-react. Popover con StickerPicker. Listener global keydown ESC → setActiveSticker(null). 4.3 Indicador visual del modo activo (banner o cursor custom).

Fase 5 — render + interaccion en cards

5.1 En KanbanCard.tsx: render overlay absoluto de card.stickers (cada uno como <span style={{position:'absolute', left:x*100+'%', top:y*100+'%', fontSize:48, opacity:.6, pointerEvents: deleteMode ? 'auto' : 'none'}}>{emoji}</span>). 5.2 Cuando activeSticker !== null, en onClick de la Paper de la card calcular coords relativas (event.offsetX / cardEl.offsetWidth, idem Y, clamp [0,1]), pushear nuevo sticker a card.stickers, llamar updateCardStickers, optimistic update. 5.3 Click derecho sobre un sticker → quitar de la lista + persistir.

Fase 6 — tests + verificacion

6.1 tools_test.go: extender (o nuevo stickers_test.go) con test de PATCH /api/cards/:id/stickers (insertar 2, borrar 1, leer card). 6.2 Manual smoke test: boton aparece, picker funciona, sticker se pega, persiste tras F5, click derecho borra, ESC sale del modo. 6.3 fn doctor uses-functions no debe regresionar para kanban_go_tools.

Fase 7 — docs y cleanup

7.1 Si se reutilizo o creo funcion del registry → declararla en apps/kanban/app.md (uses_functions). 7.2 Una linea en apps/kanban/app.md describiendo la feature. 7.3 fn index y verificar fn show kanban_go_tools.

Ejemplo de uso

1. Usuario clica "Stickers" en toolbar del board
2. Picker abre, usuario clica 🔥
3. Cursor pasa a "modo pegar", banner: "Modo sticker: 🔥 — ESC para salir"
4. Usuario clica en card "Migrar usuarios"
   → 🔥 aparece donde clico, opacidad 0.6, persistido
5. Usuario sigue clicando → mas 🔥 se acumulan
6. Pulsa ESC → modo off, cursor normal
7. F5 → stickers siguen ahi
8. Click derecho sobre 🔥 → desaparece

Riesgos

  • Hit-test confuso: si los stickers absorben pointer events, rompen drag-and-drop de la card. Mitigacion: pointerEvents: 'none' salvo cuando estamos en modo borrado.
  • Coords absolutas vs %: si guardamos px y la card cambia de ancho (resize de columna), el sticker se sale. Guardar como % (x,y ∈ [0,1]).
  • Coleccion en chat tool agent: si el chat agent expone tools que hacen updateCard con payload completo, debe respetar el campo stickers y no sobrescribirlo a null.
  • Migracion de BD en prod (laptop + casa): la migracion corre en cada arranque idempotente, OK. Pero confirmar antes en prod local del usuario.
  • WIP en kanban (issue 0058): este issue toca app.md igualmente — coordinar para no pisar el sync pendiente de uses_functions.

Prerequisitos

  • Confirmar las 7 decisiones de diseno con el usuario (Fase 1).
  • TBD: rama issue/0063-stickers desde master actualizado.