projetos / um projeto

demo · 2026

capacity

Um pequeno quadro de despacho: equipes e caminhões como colunas, serviços agendados dentro de janelas de horário, arrastáveis entre equipes e entre dias.

Visão geral

Um pequeno quadro de despacho: equipes e caminhões como colunas, serviços agendados dentro de janelas de horário, arrastáveis entre equipes e entre dias, com mouse, toque ou teclado. As setas movem um serviço que foi pego, Tab/Shift+Tab troca de dia no meio do arrasto, e nenhum dos dois é um remendo colado depois que o caminho do mouse ficou pronto.

É um projeto pessoal, não um produto. Eu construí pra praticar GraphQL, Apollo Client e drag and drop entre grupos que não estão todos na tela ao mesmo tempo, num fluxo real em vez de três tutoriais separados. O domínio é o despacho de uma empresa de mudanças, escolhido porque já vem com restrições de verdade (equipes finitas, caminhões finitos, janelas de horário que colidem), então os problemas interessantes aparecem sozinhos em vez de serem forçados em cima de uma lista de tarefas genérica.

O que ele prova

O dnd-kit cuida da mecânica do arrasto. A parte que importa de verdade começa quando solta:

  • Uma mutation otimista com uma resposta real e visível à rejeição. O bloco volta pra onde estava e um toast diz por quê. Uma mutation que só trata o caso em que dá certo é a demo de caminho feliz que esse projeto existe pra não ser.
  • Arrasto entre dias com troca automática de aba no hover, e o equivalente no teclado, que não é um plano B dele.
  • Todo resolver de lista passa por um DataLoader, com um teste de contagem de queries que falha de propósito se algum for removido.
  • Concorrência sem WebSocket, subscription ou polling. Duas pessoas editando o mesmo quadro se resolvem em "o servidor decide, o cliente desfaz".

Reproduzindo o conflito

Abra duas abas no mesmo quadro. Arraste um serviço pra um horário vazio na primeira aba e confirme a mudança. Sem recarregar a segunda aba, arraste outro serviço pra exatamente o mesmo horário. A prévia local de colisão não mostra nada errado, porque ela não tem como saber que a primeira aba já ocupou o lugar. Ao soltar, o servidor rejeita a mudança: o bloco volta e um toast diz o que já está ali. O apps/web/e2e/concurrency-conflict.spec.ts roda o mesmo cenário com dois contextos de navegador do Playwright.

Stack

  • Next.js, Apollo Client e dnd-kit no frontend
  • Flask, Graphene e SQLAlchemy em Postgres na API
  • Turborepo e npm workspaces juntando os dois

Rodando local

Tem uma demo no ar no link acima. Pra rodar local:

docker compose up -d postgres
npm install
cd apps/api && uv run python seed.py && cd ../..
npm run dev

Abre em localhost:3000. Não precisa de .env na configuração padrão: o Postgres está mapeado em localhost:5434, e os dois apps já caem nisso.

Testes

A pirâmide inteira: Vitest pra lógica pura (geometria, snapping, colisão local, o limite da troca automática, nada disso precisa de DOM), Testing Library pra foco e ARIA, o MockedProvider do Apollo pra loading, erro e rollback, Playwright pro arrasto de verdade e pro conflito com dois contextos, e pytest na API pros resolvers e pra contagem de queries.

https://capacity-crew.vercel.app/

https://github.com/imnotannamaria/capacity

Anna Maria

Engenheira de software do Brasil.

Eu construo as coisas de ponta a ponta: UI → front (web / mobile) → back → IA → deploy, e faço isso há uns 5 anos, em startups e em times grandes de empresa.

Este site é open source.

Há três jeitos de ler: Front, Back e IA.

A Abimaela, minha gata, mora no canto e reage ao que você faz.

Feito com Next.js, MDX e Postgres, em cima do design system entrepta.

lugares que você pode ir a partir daqui

um jogo, com placar
  • Jogo da memória: vira duas cartas, e par achado é página achada.
  • Os 10 tempos mais rápidos vão pro placar, com o nome de quem jogou.
tempo:00:00jogadas:000
Clica numa carta pra começar. O tempo começa a contar aí.

placar· top 10

Lendo o placar...

© anna maria, 2026uma linha mudou na licença: todos os direitos reservados forkáveisfaz o seu fork