A partir da v0.3.0 o sistema tem uma tela visual para distribuir convidados em mesas. Acesse em /dashboard/wedding-day/seating.
Acesso na UI: item “Mapa de assentos” no menu lateral (grupo Casamento, logo abaixo de “Dia D”) e card de atalho no topo da página /dashboard/wedding-day.
SeatingTable — uma mesa do salão. Tem name, capacity (assentos disponíveis), shape (ROUND |
RECT |
SQUARE), sortOrder (ordem de exibição no grid), x/y (reservados para um futuro canvas livre — não usados pela UI atual) e notes. Soft delete via deletedAt. |
Guest.tableId — FK opcional para SeatingTable. Quando o convidado é desalocado, vira null.O campo legado
Guest.tableNumber(string) continua existindo mas não é mais usado para alocação. Considere migrar entrada por entrada e remover em versão futura.
rsvpStatus = CONFIRMED e tableId = null, ordenados por nome. Arraste o chip para uma mesa.plusOnesConfirmed = 2 ocupa 3 assentos. Drop em mesa cheia falha silenciosamente com toast.GripVertical no cabeçalho). Arraste pela alça para mudar a ordem das mesas no grid; a nova ordem é persistida em sortOrder. Só a alça inicia o arrasto da mesa, então os chips de convidado dentro do card continuam clicáveis/arrastáveis.tableId volta a ser null.createSeatingTable(_, formData) — cria mesa.updateSeatingTable(_, formData) — edita nome/capacidade/formato/notas.deleteSeatingTable(tableId) — soft delete + desaloca convidados em transação.reorderSeatingTables(orderedIds) — grava sortOrder sequencial (índice no array) para cada mesa, em transação. Valida a lista com Zod (1–200 ids, cada um ≤ 64 chars).updateTablePosition(tableId, x, y) — reservada para um futuro canvas livre; não é usada pela UI atual.assignGuestToTable(guestId, tableId | null) — valida capacidade antes de gravar.Todas exigem permissão de edição (denyIfNoEdit) e gravam AuditLog (entity SeatingTable ou Guest, action CREATE / UPDATE / DELETE / REORDER / ASSIGN_TABLE / UNASSIGN_TABLE).
A ordenação inicial em page.tsx é orderBy: [{ sortOrder: "asc" }, { createdAt: "asc" }] — mesas antigas (todas com sortOrder = 0) caem no desempate por data de criação.
@dnd-kit/core, @dnd-kit/sortable, @dnd-kit/utilities — biblioteca de drag-and-drop. Sensores: PointerSensor + KeyboardSensor para acessibilidade.DndContextCada card de mesa é, ao mesmo tempo, um item sortable (reordenável) e um droppable que recebe convidados. Para o over nunca ficar ambíguo, o DndContext usa uma collisionDetection customizada (seatingCollision em seating-client.tsx) que filtra os candidatos pelo tipo do item ativo:
type: "guest") → considera só droppables de convidado (type: "table" e type: "pool") via pointerWithin;type: "table-sort") → considera só os itens sortable via closestCenter.x/y existem no schema mas ficam reservados para uma evolução futura.