diff --git a/dev/issues/0063-kanban-stickers.md b/dev/issues/0063-kanban-stickers.md new file mode 100644 index 00000000..fb0ac5f2 --- /dev/null +++ b/dev/issues/0063-kanban-stickers.md @@ -0,0 +1,139 @@ +# 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 `{emoji}`). +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. diff --git a/dev/issues/README.md b/dev/issues/README.md index 00e6c482..7e915974 100644 --- a/dev/issues/README.md +++ b/dev/issues/README.md @@ -78,3 +78,4 @@ | [0060](0060-fn-doctor-secrets-subcommand.md) | `fn doctor secrets`: scan de secrets en TODOS los repos | pendiente | media | feature | β€” | | [0061](0061-notify-telegram-integration.md) | Integrar `notify_telegram` en deploy_server + bucle reactivo | pendiente | media | integration | 0054 | | [0062](0062-deprecate-unused-core-functions.md) | Politica de deprecacion para funciones del registry sin consumidores | pendiente | baja | research | β€” | +| [0063](0063-kanban-stickers.md) | kanban: sistema de stickers (emojis) sobre cards | pendiente | media | feature | β€” |