Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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 |
|
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 enissue/0063-stickers, merge--no-ffa 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:
- Usuario hace clic en boton "Stickers" de la toolbar del board (
App.tsx:920). - Aparece picker con lista de emojis predefinidos.
- Usuario selecciona uno → entra en "modo pegar sticker" (cursor cambia, indicador visible).
- Hace clic sobre cualquier card → el emoji se pinta encima de la card con opacidad (~0.6).
- ESC sale del modo. Click derecho sobre un sticker existente lo borra.
- 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 columnastickersenCard.handlers.go— endpointPATCH /api/cards/:id/stickers(body:{"stickers":[{emoji,x,y}]}). Reusar el patron deupdateCard.tools.go— exponer mutacion al chat agent si aplica (opcional).
Frontend (apps/kanban/frontend/src/):
types.ts— añadirSticker { emoji: string; x: number; y: number; }yCard.stickers: Sticker[].api.ts(NEW endpoint helper) —updateCardStickers(id, stickers).App.tsx— boton "Stickers" en toolbar (:920), estadopickerOpen,activeSticker(emoji seleccionado en modo pegar), handler global ESC, handleronCardSticker(cardId, x, y).components/KanbanCard.tsx— render overlay absoluto decard.stickers(z-index alto, pointer-events: none salvo en modo borrado), cuando hayactiveStickercambiar 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, encomponents/format.tso nuevostickers.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
updateCardcon payload completo, debe respetar el campostickersy 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.mdigualmente — coordinar para no pisar el sync pendiente deuses_functions.
Prerequisitos
- Confirmar las 7 decisiones de diseno con el usuario (Fase 1).
- TBD: rama
issue/0063-stickersdesdemasteractualizado.