Portal do Cliente — Veggi Connect Hub (seller-share-vault)¶
Uso interno
Este documento contém referências à estrutura interna de sistemas. Não compartilhar externamente.
Versão: 1.1 · Atualizado: Abril 2026
Visão Geral¶
O seller-share-vault é o frontend React do Veggi Connect Hub — o portal onde o cliente visualiza e baixa as fotos dos produtos do seu pedido. O cliente acessa via link recebido por e-mail ou WhatsApp no formato:
O portal é estático (sem backend próprio) — toda a lógica de dados vem do Worker drive via API REST.
Stack Técnica¶
| Item | Tecnologia |
|---|---|
| Framework | React 18 + TypeScript |
| Build | Vite |
| Estilo | Tailwind CSS + shadcn/ui (Radix UI) |
| Roteamento | React Router DOM |
| Estado | React Query (TanStack) |
| Virtualização | @tanstack/react-virtual |
| Ícones | Lucide React |
| Notificações | Sonner |
| Geração de ZIP | client-zip |
| Origem | Criado no Lovable |
| Repositório | veggidocs/seller-share-vault (GitHub) |
| Deploy | Cloudflare Pages — branch main → automático |
| Domínio | hub.grupoveggi.com.br |
Estrutura de Componentes¶
src/
├── pages/
│ ├── Index.tsx ← Página principal — lê o token e decide qual estado mostrar
│ └── NotFound.tsx ← Página 404
├── components/
│ ├── DownloadPortal.tsx ← Galeria principal com seleção e download
│ ├── PortalHeader.tsx ← Cabeçalho com nome do cliente, NF e validade
│ ├── FilterBar.tsx ← Filtros por categoria (primeira palavra do nome do produto)
│ ├── SearchBar.tsx ← Busca por nome de produto
│ ├── MediaCard.tsx ← Card individual de cada foto
│ ├── Lightbox.tsx ← Visualização em tela cheia ao clicar na foto
│ ├── ExpiredLink.tsx ← Tela de link expirado com botão de WhatsApp
│ ├── LoadingState.tsx ← Tela de carregamento
│ ├── ErrorState.tsx ← Tela de erro genérico
│ └── ui/ ← Componentes shadcn/ui (não editar diretamente)
├── types/
│ └── portal.ts ← Tipos TypeScript + constantes de URL + funções utilitárias
└── hooks/
└── use-mobile.tsx ← Hook para detectar mobile
Fluxo de Funcionamento¶
1. Cliente abre hub.grupoveggi.com.br/?token=abc123...
│
▼
2. Index.tsx lê o ?token da URL
│
▼
3. Faz GET https://drive.grupoveggi.com.br/api/token/{token}
│
├── 404 → token não existe ou expirou → mostra ExpiredLink
├── Erro de rede → mostra ErrorState
└── 200 OK → recebe JSON com dados do pedido
│
▼
4. Valida: expires_at no futuro? Tem arquivos?
│
├── Não → mostra ExpiredLink
└── Sim → monta PortalData e renderiza DownloadPortal
│
▼
5. Cliente vê galeria de fotos agrupadas por produto
│
├── Pode filtrar por categoria (primeira palavra do nome_produto)
├── Pode buscar por texto
├── Pode clicar em uma foto para ver em Lightbox
├── Pode baixar uma foto individualmente (ícone de download no card)
└── Pode selecionar fotos e clicar "Baixar Seleção"
│
▼
6. Ao clicar "Baixar Seleção":
POST https://drive.grupoveggi.com.br/entrega
Header: X-Drive-Secret: veggi-drive-2026-xpto
Body: { token, numero_nf, selecionados: [{path, nome_produto}] }
│
▼
7. Worker drive gera token temporário (30 min) e retorna download_url
│
▼
8. Portal abre drive.grupoveggi.com.br/{token_temp}?download=1
ZIP é gerado no browser do cliente via client-zip
Estados Possíveis da Página¶
| Estado | Quando acontece | Componente exibido |
|---|---|---|
loading |
Aguardando resposta da API | LoadingState |
no-token |
URL sem ?token= |
ErrorState com mensagem específica |
error |
Falha de rede ou erro HTTP | ErrorState genérico |
expired |
Token expirado, inválido ou sem arquivos | ExpiredLink — exibe botão de WhatsApp para solicitar novo acesso |
ready |
Tudo OK | DownloadPortal — galeria completa |
Info
Na tela ExpiredLink, o cliente vê um botão "Solicitar novo acesso" que abre o WhatsApp com uma mensagem pré-preenchida informando o token expirado.
Constantes Importantes no Código¶
Estas constantes estão hardcoded no código e precisam ser atualizadas se os serviços mudarem:
| Arquivo | Constante | Valor atual | O que é |
|---|---|---|---|
src/types/portal.ts |
~~BACKBLAZE_BASE_URL~~ |
~~https://f005.backblazeb2.com/file/drive-inteligente/~~ |
Removida em Abril/2026 — substituída por buildImageUrl via /file-proxy |
src/types/portal.ts |
API_BASE_URL |
https://n8n.nerd2nerd.com.br/webhook/entrega |
URL legada — não está sendo usada pelo portal atual |
src/components/DownloadPortal.tsx |
DOWNLOAD_ENDPOINT |
https://drive.grupoveggi.com.br/entrega |
Endpoint do Worker drive para gerar o ZIP |
src/components/DownloadPortal.tsx |
DRIVE_SECRET |
veggi-drive-2026-xpto |
Segredo de autenticação enviado no header X-Drive-Secret |
Warning
O DRIVE_SECRET está hardcoded no frontend — qualquer pessoa com acesso ao código-fonte pode ver esse valor. Para o contexto atual (portal restrito por token), o risco é baixo, mas idealmente esse segredo deveria ser tratado como uma variável de ambiente de build.
Warning
A constante API_BASE_URL em portal.ts aponta para um endereço n8n antigo (nerd2nerd.com.br) e não está sendo utilizada pelo fluxo atual. Pode ser removida ou atualizada em manutenção futura.
Como Fazer Deploy de Atualização¶
O deploy é 100% automático. Qualquer push no branch main do repositório veggidocs/seller-share-vault dispara um novo build no Cloudflare Pages.
Passos para atualizar:
- Editar o arquivo desejado no repositório
- Fazer commit e push no branch
main - Aguardar 1–2 minutos
- Verificar o resultado em Workers & Pages →
connect-hub→ aba Implantações
Para atualizações maiores (recomendado):
- Criar um branch separado (ex:
fix/constante-url) - Fazer as alterações e abrir um Pull Request
- O Cloudflare Pages cria automaticamente um preview URL para testar
- Após validar, fazer merge no
main
Histórico de Mudanças¶
Abril 2026 — Otimizações de Performance (INP e LCP)¶
Contexto: Análise via Cloudflare Web Analytics e Chrome DevTools identificou INP de 2.600 ms e LCP de 4.287 ms no portal. As mudanças abaixo reduziram o INP para 45 ms (medido em janela anônima) e eliminaram o tráfego direto ao Backblaze B2 nos cards.
| Arquivo | Mudança | Motivo |
|---|---|---|
src/components/MediaCard.tsx |
Adicionado decoding="async" no <img> |
Evita bloqueio do thread principal durante decodificação de imagens |
src/components/DownloadPortal.tsx |
Substituído let globalIndex = 0 solto no render por useMemo com indexedFiles |
globalIndex no render causava índices instáveis a cada re-render, quebrando o Lightbox |
src/components/DownloadPortal.tsx |
Adicionada virtualização com @tanstack/react-virtual |
Com 66+ imagens, o DOM completo travava o browser no carregamento inicial. A virtualização renderiza apenas os cards visíveis na tela |
src/types/portal.ts |
buildImageUrl alterado para usar /file-proxy do Worker drive em vez de f005.backblazeb2.com diretamente |
Ativa o cache Cloudflare já configurado no Worker (cacheTtl: 3600). Imagens passam a ser servidas do edge após a primeira visita |
src/types/portal.ts |
Constante BACKBLAZE_BASE_URL removida |
Não é mais utilizada após mudança do buildImageUrl |
Resultado medido (Chrome DevTools, janela anônima):
| Métrica | Antes | Depois |
|---|---|---|
| INP | ~98 ms (sessão normal) / 2.600 ms (Cloudflare Analytics) | 45 ms |
| CLS | 0.02 | 0 |
| Origem das imagens nos cards | f005.backblazeb2.com (sem cache) |
drive.grupoveggi.com.br/file-proxy (cache Cloudflare 1h) |
Info
O INP de 2.600 ms registrado no Cloudflare Analytics era influenciado por extensões de browser (Coupert) instaladas na máquina de teste. A medição em janela anônima reflete o valor real do site.
Como Rodar Localmente¶
Para testar alterações antes de fazer deploy:
# Clonar o repositório
git clone https://github.com/veggidocs/seller-share-vault.git
cd seller-share-vault
# Instalar dependências
npm install
# Rodar em modo desenvolvimento
npm run dev
O portal abre em http://localhost:8080. Para testar com um token real, adicione na URL:
Info
O portal em desenvolvimento faz chamadas reais para drive.grupoveggi.com.br — não há mock de API local.