feat(kanban): stickers feature + dashboard null guards (#0063)

- backend: Sticker type, idempotent stickers column, PUT /api/cards/:id/stickers, 4 tests
- frontend: emoji-mart picker, toolbar button + ESC, draggable overlay with right-click delete, % coords for resize survival
- dashboard: null guards on metrics arrays

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-08 21:00:30 +02:00
parent 2a727eb7c1
commit 656516f219
12 changed files with 552 additions and 46 deletions
+15
View File
@@ -54,6 +54,11 @@ interface Props {
onShowHistory: (card: Card) => void;
onToggleCardLock: (id: string, locked: boolean) => void;
onAssignCard: (id: string, assignee_id: string | null) => void;
activeSticker?: string | null;
onAddSticker?: (cardId: string, x: number, y: number) => void;
onRemoveSticker?: (cardId: string, index: number) => void;
onMoveSticker?: (cardId: string, index: number, x: number, y: number) => void;
onCommitSticker?: (cardId: string) => void;
users: User[];
usersById: Map<string, User>;
}
@@ -76,6 +81,11 @@ function KanbanColumnImpl({
onShowHistory,
onToggleCardLock,
onAssignCard,
activeSticker,
onAddSticker,
onRemoveSticker,
onMoveSticker,
onCommitSticker,
users,
usersById,
}: Props) {
@@ -408,6 +418,11 @@ function KanbanColumnImpl({
users={users}
assignee={c.assignee_id ? usersById.get(c.assignee_id) : undefined}
inDoneColumn={column.is_done}
activeSticker={activeSticker}
onAddSticker={onAddSticker}
onRemoveSticker={onRemoveSticker}
onMoveSticker={onMoveSticker}
onCommitSticker={onCommitSticker}
/>
))}
</Stack>