feat(kanban): badges done/locked + drag locked en mismo column + migrations + UX stickers
- badges: locked → tiempo bloqueado; done → fecha completion + total lead time; otherwise → tiempo en columna - locked cards: drag permitido dentro de mismo column (cross-column rejected con notification) - card field: locked_at desde JOIN card_lock_history (open period) - migrations: refactor a embed.FS, archivos 002-005 extraidos de ensureColumns; ensureColumns queda como backstop - stickers UX: opacidad 1, debajo del texto, picker estable (useRef), boton entra directo a modo con 😀, popover cierra outside, cards done filter brightness - format: formatDateTimeShort Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+13
-1
@@ -423,6 +423,12 @@ export function App() {
|
||||
// Card drag
|
||||
const destCol = resolveColumnId(overId);
|
||||
if (!destCol) return;
|
||||
const activeCard = board.cards.find((c) => c.id === activeId);
|
||||
if (activeCard?.locked && activeCard.column_id !== destCol) {
|
||||
notifications.show({ color: "yellow", message: "Card bloqueada: no se puede mover entre columnas" });
|
||||
reload();
|
||||
return;
|
||||
}
|
||||
const destCards = board.cards
|
||||
.filter((c) => c.column_id === destCol)
|
||||
.sort((a, b) => a.position - b.position);
|
||||
@@ -1108,7 +1114,13 @@ export function App() {
|
||||
variant={activeSticker ? "filled" : "default"}
|
||||
color={activeSticker ? "yellow" : undefined}
|
||||
leftSection={<IconMoodSmile size={14} />}
|
||||
onClick={() => setStickerPickerOpen((v) => !v)}
|
||||
onClick={() => {
|
||||
if (!activeSticker) {
|
||||
setActiveSticker("😀");
|
||||
} else {
|
||||
setStickerPickerOpen((v) => !v);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{activeSticker ? `Modo sticker: ${activeSticker}` : "Stickers"}
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user