O Embalo é um aplicativo web (PWA) para motoristas profissionais — Uber, 99, iFood, táxi e particulares — que querem ver o lucro do dia, não só o faturamento da plataforma. Reúne receitas e despesas, veículo, jornada, rotas, recibos, vouchers e relatórios. Roda no navegador do celular ou do computador, em português, no domínio embalomotorista.com.br. O código é JavaScript vanilla com Supabase (PostgreSQL, Auth e Storage) e deploy na Vercel.
Este caso técnico mostra o que o código faz, as decisões que o moldaram e as falhas reais que viraram regra. Os números foram conferidos no repositório na versão de 28/10/2026 (commit c07ce82).
Transparência: o Embalo foi projetado e é desenvolvido por Jeremias Cardoso, autor deste blog. O produto está em implementação; este texto não afirma frota paga nem métricas de usuários.
Nota de atualização (02/11/2026): este post foi reescrito como caso técnico. A versão de 01/10/2026 era uma apresentação de produto. Esta traz arquitetura, números, incidentes, limites e fontes conferidos no repositório.
O problema: o extrato da plataforma não é o lucro
O valor de uma corrida não é o que fica com o motorista. Combustível, manutenção, pedágio e o tempo parado entre chamadas saem da mesma conta, mas não aparecem no extrato da Uber ou da 99. Sem esses números lado a lado, a semana termina com o faturamento conhecido e o resultado desconhecido.
Planilha resolve enquanto cabe numa pessoa e num arquivo que não some. O problema começa quando o lançamento precisa acontecer no celular, com rede ruim, foto do comprovante, recibo para empresa e histórico que sobreviva a trocar de aparelho. É a mesma fronteira em que planilhas de controle deixam de bastar e um sistema com login e regras passa a fazer sentido — a mesma lógica que aparece no EscalaOper para escala de turno.
O sistema em números
Medição local em 02/11/2026 sobre o commit c07ce82 (último commit do repositório em 28/10/2026).
| Medida | Valor |
|---|---|
| Aplicação (JS/CSS/HTML principais, sem tests/docs/assets) | 28 arquivos; cerca de 22,7 mil linhas de código (não brancas e sem comentário) |
API serverless (api/) | 12 arquivos; cerca de 1,5 mil linhas de código |
Pasta tests/ | 232 arquivos (inclui fixtures e relatórios; não usar o total bruto de linhas como “código de teste”) |
| Histórico no Git | cerca de 681 commits |
| Domínio público | embalomotorista.com.br |
A contagem exclui node_modules, documentação, estado, mockups e assets. O arquivo central app.js sozinho passa de 500 KB no disco.
Uma conta, dois acessos
A decisão de produto é tratar aplicativo e portal como o mesmo produto: mesma conta, mesmos dados, mesmas regras. O celular não é uma versão reduzida. A stack escolhida foi deliberadamente fina no front: HTML, CSS com tokens claro/escuro e JavaScript sem framework, para caber no bolso e no 4G.
Os dados moram no Supabase. Autenticação, tabelas e fotos de comprovante passam por lá. Parte das regras de plano e de uso mensal foi migrada para funções e rotas serverless em api/, porque deixar o navegador como única fonte da verdade do plano abre buraco: dá para virar Pro pelo console do navegador. A auditoria de junho de 2026 já marcava isso como bloqueador antes de cobrar.
O Embalo não importa automaticamente os ganhos das plataformas. O motorista lança à mão, por voz ou com apoio de leitura do valor na foto do comprovante. A vantagem está em reunir, no mesmo lugar, o que cada plataforma paga e o que o carro consome.
O que o código entrega
Financeiro. Receitas, despesas e resultado líquido por período (mês, últimos 30 dias, ano). Meta diária, filtros por categoria e anexo de foto. Os totais saem dos lançamentos do próprio motorista.
Meu Veículo. Cadastro, quilometragem, manutenções com previsão da próxima troca e checklist diário. A previsão parte do último registro de cada tipo de serviço.
Jornada e produtividade. Início e fim de turno com quilometragem; ganho por hora, custo por quilômetro, horas trabalhadas e a categoria de receita que mais rendeu no mês. Gráficos dos últimos sete dias e por turno.
Rotas. Paradas ordenáveis; abertura no Google Maps ou no Waze; otimização da ordem em versão beta, que pode falhar ao geocodificar um endereço.
Recibos, clientes e vouchers. Recibos numerados em PDF, compartilháveis no WhatsApp; vouchers com situação (pendente, enviado, em análise, pago). O mesmo eixo de “artefato pronto, relação humana” aparece no ZapOrca e no post sobre custo do WhatsApp com IA.
Planos Free, Pro e Max. Limites de lançamentos, fotos, recibos, vouchers e veículos; mensagem em português quando o teto estoura; trial e assinatura por Pix no desenho atual.
Arquitetura em restrição: o que cabe no celular
O front não usa React nem Vue de propósito. A lógica de negócio mora em app.js e em módulos satélite (voz-parser.js, assistente-voz.js, business-ui.js, report-ui.js). O service worker e o manifesto fecham o PWA. A escolha reduz o payload e evita uma camada de build no caminho crítico do motorista; o preço é um arquivo monolítico grande e a disciplina de não misturar estado de sessão entre contas.
No servidor, as rotas em api/ concentram o que não pode confiar no cliente: consumir cota de comando de voz, reconciliar uso mensal e endurecer o plano. O SQL versionado em sql/ documenta a reconstituição da base e os rollbacks das migrações de plano. A auditoria de junho já separava “fase de teste grátis” (aprovável com ressalvas) de “começar a cobrar” (reprovado até o servidor ser a fonte da verdade e a exclusão de conta existir no app).
São Paulo aparece no desenho do produto porque o fuso UTC−3 e o motorista noturno são o público típico do bug de data — não como marketing de cidade. O domínio público embalomotorista.com.br é a vitrine e o manual; este caso descreve o repositório, não um laudo do que está deployado em cada momento no painel da Vercel.
Incidentes que viraram regra
1. O limite do plano que virava “Algo deu errado”
Quando o Free batia no teto, o app lançava um erro com texto claro em português (“Limite atingido… faça upgrade”). O catch passava por traduzirErro(), feito para mensagens em inglês do Supabase. Nenhum match batia, e o motorista via o fallback genérico. Quem mais precisava ver o convite de upgrade não via. A correção marcou os erros do próprio app com jaAmigavel e passou a devolver a mensagem original. Em app.js, as chaves LIMITE_PLANO:* e o ramo err.jaAmigavel ainda estão aí.
2. Duplo toque, segundo veículo, jornada fantasma
No celular, um segundo toque nos 700–1500 ms entre o sucesso e a troca de tela reenviava o formulário. Em veículo, o segundo insert quebrava a aba: carregarVeiculo() usa .maybeSingle() e a tela ficava em erro permanente. Em jornada, nascia uma jornada aberta “fantasma”. A regra que ficou: não reabilitar o botão no finally do sucesso enquanto a navegação não terminou, e setar modo edição assim que o veículo é criado.
3. Depois das 21h em São Paulo, o dia virava amanhã
Datas de calendário usavam toISOString().slice(0,10) (UTC). Em SP (UTC−3), depois das 21h isso já é o dia seguinte — exatamente quando o motorista noturno fecha o dia. Gráfico de sete dias e relatórios de jornada deslocavam. O helper dataISOLocal / dataHojeISO passou a usar o fuso do aparelho. O comentário em app.js registra o bug clássico de SP de propósito.
4. Mil linhas sem aviso
Consultas que somam dinheiro pediam “tudo” sem .limit(). O PostgREST devolve no máximo 1.000 linhas e não avisa o corte. Um motorista que lança cada corrida passa desse teto em cerca de um ano e o relatório anual mente em silêncio. O mínimo é detectar o teto e avisar; paginar de verdade ficou como dívida.
5. Copiloto de voz: offline, período e cota
Três lições conferidas no código em outubro de 2026: (a) “resiliência offline” só vale para a interpretação do texto — reconhecimento de fala e gravação no Supabase precisam de rede, e não há fila de reenvio; (b) o período da consulta por lista de exclusão aceitava “em 2025” ou “sábado” e caía no dia errado — a guarda segura é por inclusão do que o app sabe calcular; (c) cota de comandos no localStorage não é fonte da verdade — o custo da IA e a cota passam pelo servidor.
6. “Concluída” e “100% dos testes” sem prova
A tarefa do servidor como fonte da verdade dos planos chegou marcada concluída sem cumprir o critério. “100% dos testes” valia só para uma bateria do copiloto; a linha de base da bateria inteira deu 18 PASS e 21 FAIL já existentes. A regra que ficou na memória do projeto: alegação sem evidência apontável é hipótese.
7. Keep-alive que consultava coluna inexistente
O robô que aquecia o Supabase a cada três dias consultava configuracoes?select=id, mas a chave da tabela é chave, e o papel anon não lê essa tabela. A resposta 4xx era tratada como sucesso. A consulta foi trocada; atividade no painel não se prova com status HTTP enganoso.
Testes: a máquina importa
Na conferência R19 (28/10/2026), o run-all --fechamento na máquina do dono com Edge deu 43/44 (1.060 casos PASS; um FAIL por TEMPO_CASO / Edge que não fechou a tempo; o mesmo caso passou no reteste 16/16). Na nuvem, a mesma rodada deu 38/44: a nuvem não tem Edge e troca por Chromium com calço. A memória do projeto registra: bateria na nuvem não é a bateria da máquina para tela e zoom.
Isso não prova que o Embalo está “pronto para cobrar”. Prova que a prova de interface depende do ambiente, e que timeout de navegador não vira defeito de produto sem repetir o caso.
O que ainda falta / o que este caso não prova
- Adoção. O repositório e este texto não afirmam quantos motoristas usam o produto em produção. Recrutar testadores e o acesso à loja aparecem como tarefas bloqueadas no estado do projeto, não como fatos concluídos.
- Cobrança plena. Antes de cobrar de fato, a auditoria e as tarefas ainda tratam servidor como fonte da verdade dos planos, exclusão de conta pelo app (LGPD) e revisão jurídica da política e dos termos como pendências de go-live. A fase de teste grátis e a fase de cobrar são vereditos diferentes no relatório de junho.
- Import das plataformas. Continua manual. Não há conector Uber/99/iFood no código desta versão.
- Offline real. Não há fila de lançamentos para quando a rede volta. O copiloto interpreta texto localmente; gravar e ouvir dependem de internet.
- Paginação dos totais. O teto de 1.000 linhas do PostgREST continua sendo risco estrutural se o uso crescer sem
.range()ou soma no banco. - Segredos e painéis. Detalhes de chaves, políticas RLS completas e configuração de SMTP ficam de fora deste texto de propósito.
- Bibliotecas de PDF e planilha. A memória registra versões e avisos conhecidos (jsPDF, SheetJS); este caso não relê a cadeia de supply de cada dependência de front.
Lições que valem fora do app de motorista
- Mensagem amigável que o
catchengole não existe. Marque o erro do próprio app ou o usuário vê genérico. - Fuso é requisito de produto para quem trabalha à noite.
toISOStringem data de calendário mente depois das 21h em SP. - Teto silencioso de API é mentira contábil. Se a soma pode cortar, o corte precisa aparecer.
- “Offline” e “100% dos testes” pedem definição escrita. Sem ela, viram marketing interno.
- Prova de tela na nuvem ≠ prova na máquina do usuário. Ambiente faz parte da evidência.
Fontes
- Código, documentação, auditoria pré-produção, memória do projeto e histórico: repositório
motorista-pro(Embalo) no commitc07ce82(28/10/2026), lido pelo autor; extrato com trechos, método da contagem e limites, verificado em 2026-11-02. A suíte completa não foi reexecutada nesta redação; os resultados de bateria citados vêm do relatório R19 de 28/10/2026 versionado no repositório. - Manual público do produto em embalomotorista.com.br (apresentação e capturas de demonstração), conferido na versão de lançamento do post original.



