fad4006f60
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
156 lines
8.1 KiB
Markdown
156 lines
8.1 KiB
Markdown
---
|
|
id: "0063"
|
|
title: "kanban: sistema de stickers (emojis) sobre cards"
|
|
status: pendiente
|
|
type: feature
|
|
domain:
|
|
- kanban
|
|
scope: multi-app
|
|
priority: media
|
|
depends: []
|
|
blocks: []
|
|
related: []
|
|
created: 2026-05-17
|
|
updated: 2026-05-17
|
|
tags: []
|
|
---
|
|
# 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:
|
|
|
|
```bash
|
|
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.
|