Baseline commit of the existing Maquina Comercial Pessoal project
Tracks the app that was already in place (capture, treatment, outreach, WhatsApp integration, server, frontend, tests, docs) so future feature work can branch and use isolated worktrees.
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
# Redesign visual: paleta SaaS dark (app inteiro) Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Trocar a identidade visual inteira do app (login + 4 abas + modais) de uma paleta clara/laranja para uma paleta escura única estilo SaaS (Linear/Vercel), com o card de campanha ganhando um tratamento "Dispatch Board" (borda de status colorida + números tabulares monoespaçados), e o toast passando a indicar sucesso/erro por cor.
|
||||
|
||||
**Architecture:** `public/css/app.css` é quase inteiramente orientado por variáveis CSS (`:root`); a troca de paleta é uma substituição do bloco de tokens + remoção do bloco `@media (prefers-color-scheme: dark)` (o app deixa de ter modo claro). Três gradientes hardcoded (parceiros do laranja atual) viram um novo token `--accent-light`. Tratamento "Dispatch Board" é CSS adicional em `.campaign-card` e nos números de estatística. Toast colorido é uma pequena mudança em `public/js/api.js` (inferência pelo prefixo "Erro" na própria mensagem) + 2 classes CSS novas — sem tocar nos ~40 call-sites de `toast(...)` espalhados pelos arquivos JS.
|
||||
|
||||
**Tech Stack:** HTML/CSS/JS vanilla (sem framework de build), Node `--test`, Playwright.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Este repositório NÃO tem HEAD/commits e commits não são permitidos neste projeto — nenhuma etapa deste plano inclui `git commit`. Ignore os passos de commit do template padrão de planos.
|
||||
- Não há testes automatizados de aparência visual. A verificação "passou" de cada tarefa é: (a) suíte existente 100% verde, (b) checagem visual ao vivo no navegador via Claude Browser.
|
||||
- Servidor de dev roda em `http://127.0.0.1:3910` via `node scripts/servidor.mjs`, já ativo nesta sessão — arquivos estáticos (`public/**`) são servidos direto do disco, um `navigate` com `force: true` já pega mudanças de CSS/JS sem precisar reiniciar o processo Node. Só reiniciar o processo Node se mudar algo em `src/**`.
|
||||
- Design aprovado está em `docs/superpowers/specs/2026-07-30-dark-saas-redesign-design.md` — os valores exatos de cor vêm de lá, não reinvente.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Trocar os tokens de cor em `app.css` para a paleta SaaS dark única
|
||||
|
||||
**Files:**
|
||||
- Modify: `public/css/app.css:1-47` (bloco `:root` e `@media (prefers-color-scheme: dark)`)
|
||||
- Modify: `public/css/app.css:80` (`.brand-monogram`)
|
||||
- Modify: `public/css/app.css:189` (`.btn-primary`)
|
||||
- Modify: `public/css/app.css:520` (`.progress-fill`)
|
||||
- Modify: `public/css/app.css:587` (`.rank-fill, .funil-fill`)
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: todo o resto do CSS (que já usa `var(--x)` em quase tudo) herda a nova paleta automaticamente. Nenhuma outra tarefa precisa saber os valores exatos — só que os tokens `--page`, `--surface`, `--ink`, `--accent`, `--good`, `--warning`, `--critical`, `--radius*`, `--shadow*` continuam existindo com os mesmos nomes.
|
||||
|
||||
- [ ] **Step 1: Substituir o bloco `:root` (linhas 2-26) por estes valores**
|
||||
|
||||
```css
|
||||
:root {
|
||||
--page: #0b0c10;
|
||||
--page-2: #0d0e13;
|
||||
--surface: #16171d;
|
||||
--surface-2: #1d1f27;
|
||||
--ink: #f4f4f6;
|
||||
--ink-2: #a4a6b0;
|
||||
--muted: #6c6e79;
|
||||
--grid: #2a2c35;
|
||||
--border: rgba(255, 255, 255, 0.08);
|
||||
--accent: #6a63f6;
|
||||
--accent-light: #9d97ff;
|
||||
--accent-2: #4f8cff;
|
||||
--accent-ink: #ffffff;
|
||||
--bar: #6a63f6;
|
||||
--good: #22c55e;
|
||||
--good-text: #4ade80;
|
||||
--warning: #f5a524;
|
||||
--critical: #fb5b7c;
|
||||
--critical-bg: rgba(251, 91, 124, 0.12);
|
||||
--radius-sm: 8px;
|
||||
--radius: 12px;
|
||||
--radius-lg: 16px;
|
||||
--shadow: 0 20px 50px rgba(0, 0, 0, 0.45);
|
||||
--shadow-soft: 0 8px 24px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Apagar inteiramente o bloco `@media (prefers-color-scheme: dark) { ... }` que vem logo depois (linhas 28-47 do arquivo original)**
|
||||
|
||||
O app passa a ter uma única aparência, sempre escura — não há mais um segundo bloco de tokens para modo claro/escuro.
|
||||
|
||||
- [ ] **Step 3: Trocar as 3 cores hardcoded que combinavam com o laranja antigo pelo novo `--accent-light`**
|
||||
|
||||
Em `.brand-monogram`:
|
||||
```css
|
||||
.brand-monogram { background: linear-gradient(135deg, var(--accent), var(--accent-light)); color: #fff; font-weight: 900; letter-spacing: -0.06em; box-shadow: var(--shadow-soft); }
|
||||
```
|
||||
|
||||
Em `.btn-primary`:
|
||||
```css
|
||||
.btn-primary { background: linear-gradient(135deg, var(--accent), var(--accent-light)); border-color: transparent; color: var(--accent-ink); }
|
||||
```
|
||||
|
||||
Em `.progress-fill`:
|
||||
```css
|
||||
.progress-fill { height: 100%; width: 3%; background: linear-gradient(90deg, var(--accent), var(--accent-light)); border-radius: 999px; transition: width 0.4s; }
|
||||
```
|
||||
|
||||
Em `.rank-fill, .funil-fill`:
|
||||
```css
|
||||
.rank-fill, .funil-fill { height: 100%; min-width: 3px; border-radius: 999px; background: linear-gradient(90deg, var(--bar), var(--accent-light)); }
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Rodar a suíte de testes existente para confirmar que nada de lógica quebrou**
|
||||
|
||||
Run: `cd "D:/PROJETOS/COMERCIAL GTI/MAQUINA COMERCIAL PESSOAL" && node --test`
|
||||
Expected: todos os testes continuam passando (a suíte não valida cor/CSS, só comportamento — uma troca de paleta não deveria derrubar nenhum). Preste atenção especial ao teste `interface e arquivos operacionais usam somente a identidade pessoal` (`tests/personal-branding.test.mjs`) — ele lê `public/css/app.css` procurando strings de marca "Grupo GTI"; os valores hex novos não contêm essas strings, então deve continuar passando.
|
||||
|
||||
- [ ] **Step 5: Checagem visual ao vivo — página de login**
|
||||
|
||||
Usando o Claude Browser: `navigate` para `http://127.0.0.1:3910` com `force: true`, tirar um `read_page`/`get_page_text` e confirmar visualmente (screenshot, se a pane permitir) que o fundo está escuro (`#0b0c10`-ish), o card de login com fundo `#16171d`-ish, texto claro legível, sem nenhum resquício de fundo claro/branco aparecendo.
|
||||
|
||||
- [ ] **Step 6: Checagem visual ao vivo — aba Capturar Leads (já vem ativa por padrão) e aba Tratar Leads**
|
||||
|
||||
Clicar na aba "Tratar Leads", conferir que os `stat-tiles`, tabela de leads, badges de classificação (excelente/bom/médio/baixo) e gráficos de ranking estão legíveis e com contraste adequado no fundo escuro novo. Não deve haver texto escuro sobre fundo escuro (herança de alguma cor que não passou a usar variável).
|
||||
|
||||
Não há commit nesta etapa (projeto sem git commits).
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Tratamento "Dispatch Board" nos cards de campanha e números de estatística
|
||||
|
||||
**Files:**
|
||||
- Modify: `public/css/app.css` (seção `/* campanhas */`, a partir de `.campaign-cards`)
|
||||
- Modify: `public/css/app.css:522-525` (`.stat-row .mini b`)
|
||||
- Modify: `public/css/app.css:562` (`.tile .valor`)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: tokens `--muted`, `--critical`, `--good`, `--radius`, `--border` de Task 1.
|
||||
- Produces: nada que outra tarefa dependa — mudança visual isolada.
|
||||
|
||||
- [ ] **Step 1: Adicionar borda de status colorida à esquerda do card de campanha**
|
||||
|
||||
Na seção `/* campanhas */` de `app.css`, depois da regra `.campaign-card { margin-bottom: 0; }`, adicionar:
|
||||
|
||||
```css
|
||||
.campaign-card {
|
||||
border-left: 3px solid var(--muted);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
.campaign-card:has(.chip.warn) { border-left-color: var(--critical); }
|
||||
.campaign-card:has(.chip.ok) { border-left-color: var(--good); }
|
||||
```
|
||||
|
||||
(`:has()` funciona nos navegadores modernos — Chrome/Edge 105+, Firefox 121+ — e o card já usa `.chip.warn` para pausada e `.chip.ok` para ativa, ver `public/js/campanhas.js` função `renderCard`, então não precisa mexer no JS.)
|
||||
|
||||
- [ ] **Step 2: Números tabulares monoespaçados nos contadores de uso/limite e nas estatísticas**
|
||||
|
||||
```css
|
||||
.campaign-usage span,
|
||||
.stat-row .mini b,
|
||||
.tile .valor {
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Rodar a suíte de testes existente**
|
||||
|
||||
Run: `node --test`
|
||||
Expected: todos os testes continuam passando (mudança é puramente visual/CSS, nenhuma classe foi removida, `.chip.warn`/`.chip.ok` continuam existindo do mesmo jeito).
|
||||
|
||||
- [ ] **Step 4: Checagem visual ao vivo — aba Campanhas**
|
||||
|
||||
Navegar para a aba Campanhas, conferir visualmente: cada card de campanha tem uma borda colorida na lateral esquerda (cinza-neutro para pronta, vermelha para pausada, verde para ativa/rodando); os números de "Manual: X/Y" e "Automático: X/Y" e o bloco de métricas (Visualizados/Aprovados/Enviados/Responderam, visível clicando "Ver fila" numa campanha) estão em fonte monoespaçada tabular. Confirmar que o texto "Pausado: ..." e os botões continuam legíveis e clicáveis.
|
||||
|
||||
Não há commit nesta etapa.
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Toast colorido por sucesso/erro
|
||||
|
||||
**Files:**
|
||||
- Modify: `public/js/api.js:82-94` (função `toast`)
|
||||
- Modify: `public/css/app.css:682-696` (`.toast`)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `document.getElementById('toast')` (já existe em `index.html`/`login.html`, id `toast`).
|
||||
- Produces: `toast(message, ms)` continua com a mesma assinatura pública — nenhum call-site em `app.js`/`tratamento.js`/`captura.js`/`campanhas.js` precisa mudar.
|
||||
|
||||
**Por que não tem um teste automatizado nesta tarefa:** `tests/campaign-ui.test.mjs` roda `app.js`/`campanhas.js` dentro de um `vm.createContext` cujo objeto de contexto já define `API`, `el`, `h`, `dataBr`, `fmt` como mocks manuais (não carregados de `api.js`). Se `public/js/api.js` fosse executado nesse mesmo contexto via `vm.runInContext` para testar o `toast()` real, o `const API = {...}` de dentro de `api.js` sombrearia/substituiria o `API` mockado que os outros ~15 testes desse arquivo dependem para interceptar chamadas de rede — quebrando a suíte inteira. Isolar só a função `toast()` do resto de `api.js` exigiria transformar o arquivo num módulo ES (`export`), o que está fora do escopo desta tarefa (o frontend inteiro usa `<script>` simples, sem `import`/`export`). Por isso a verificação aqui é só a checagem visual ao vivo do Step 3, como o próprio spec já previa.
|
||||
|
||||
- [ ] **Step 1: Implementar a inferência de tipo em `toast()`**
|
||||
|
||||
Em `public/js/api.js`, substituir a função `toast` inteira:
|
||||
|
||||
```javascript
|
||||
function toast(message, ms = 3500) {
|
||||
const box = el('toast');
|
||||
box.textContent = message;
|
||||
box.classList.remove('hidden', 'erro', 'sucesso');
|
||||
box.classList.add(/^erro/i.test(message) ? 'erro' : 'sucesso');
|
||||
clearTimeout(toastTimer);
|
||||
toastTimer = setTimeout(() => box.classList.add('hidden'), ms);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Rodar a suíte inteira, confirmando que a mudança em `api.js` não quebrou nada**
|
||||
|
||||
Run: `cd "D:/PROJETOS/COMERCIAL GTI/MAQUINA COMERCIAL PESSOAL" && node --test`
|
||||
Expected: todos os testes continuam passando — `api.js` não é carregado pelo harness de teste (ver nota acima), então essa mudança não deveria afetar nenhum teste existente; isso só confirma que nada mais no repo depende do comportamento antigo de `toast()`.
|
||||
|
||||
- [ ] **Step 3: Estilizar as duas variantes no CSS**
|
||||
|
||||
Em `app.css`, na regra `.toast { ... }`, adicionar logo depois:
|
||||
|
||||
```css
|
||||
.toast.sucesso { background: var(--surface-2); color: var(--good-text); border: 1px solid color-mix(in srgb, var(--good) 45%, transparent); }
|
||||
.toast.erro { background: var(--surface-2); color: var(--critical); border: 1px solid color-mix(in srgb, var(--critical) 45%, transparent); }
|
||||
```
|
||||
|
||||
(A regra base `.toast { background: var(--ink); color: var(--page); }` continua existindo em `app.css` — essas duas classes sobrescrevem `background`/`color` especificamente para os dois estados coloridos; sem `.sucesso`/`.erro`, nunca deve sobrar um toast sem uma das duas, já que `toast()` sempre aplica uma.)
|
||||
|
||||
- [ ] **Step 4: Checagem visual ao vivo de um toast de cada tipo**
|
||||
|
||||
No navegador (Claude Browser), disparar uma ação que gera um toast de sucesso real (ex.: clicar em "Preparar fila" numa campanha) e conferir visualmente que o toast aparece esverdeado; depois provocar um erro real (ex.: tentar uma ação inválida) e conferir que fica avermelhado. Usar `document.getElementById('toast').className` via `javascript_tool` pra confirmar a classe aplicada sem depender só do visual.
|
||||
|
||||
Não há commit nesta etapa.
|
||||
|
||||
---
|
||||
|
||||
## Verificação final (depois das 3 tarefas)
|
||||
|
||||
- [ ] Rodar `node --test` uma última vez — suíte completa 100% verde.
|
||||
- [ ] Rodar `npx playwright test tests/browser/campaign-pilot.spec.mjs --reporter=line` — 42/42.
|
||||
- [ ] Passar pelas 4 abas + modal de Configurações no navegador real, conferindo que não sobrou nenhum resquício de fundo/texto claro (herança de alguma regra que não usava variável).
|
||||
- [ ] Confirmar que as campanhas reais do usuário continuam com o mesmo `status`/`pause_reason` de antes desta sessão (nenhuma ação de estado foi tocada, só CSS/JS de apresentação) — checar via `fetch('/api/campaigns')`.
|
||||
Reference in New Issue
Block a user