feat(web): telas de negócio reais no EDEN Core (Empresas, Produtos, Revendas, CRM, Ofertas)

Prioriza a parte web a pedido do usuário. 2 subagentes eden-frontend em
paralelo sobre o shell autenticado já pronto:

- Empresas/Produtos/Revendas: CRUD completo (lista + criar/editar),
  layout modelado nas páginas reais do tema DreamsERP comprado
  (purchase/vendors.tsx, inventory/products.tsx) — máscara de moeda para
  as 5 faixas de preço + implantação. Novo componente Table<T> genérico
  em packages/ui.
- CRM: Leads (lista, criar/editar, converter em oportunidade) e
  Oportunidades, modelados em crm/leads.tsx/dealsDetails.tsx.
- Ofertas/Quotes: o módulo mais rico — criação com lista dinâmica de
  itens produto/quantidade, fidelidade reduzida opcional, desconto
  opcional, totais calculados pelo servidor; detalhe com aprovar/rejeitar
  e fechar negócio. Modelado em sales/salesQuotes*.tsx. Novo componente
  Select em packages/ui.

Correção de instrução no meio da leva: usuário pediu para seguir de
perto o layout real do tema comprado, não só os tokens visuais já
extraídos — as páginas React relevantes do tema foram extraídas para
.tmp/theme-reference/ (nunca versionado) em cada worktree e os agentes
já em execução foram redirecionados via mensagem (não relançados).

Bug real do próprio agente, corrigindo meu brief: Leads/Opportunities
respondem em camelCase (mapLead/mapOpportunity), não snake_case como o
resto da API — verificado contra o código-fonte real antes de aceitar.

Integração manual: um dos 2 agentes não conseguiu git merge no próprio
sandbox e restaurou o shell existente por leitura+escrita — cada arquivo
restaurado foi comparado byte a byte contra main (diff -rq) antes de
aceitar. App.tsx/AuthenticatedLayout.tsx/packages/ui/index.ts
reconciliados manualmente juntando as rotas/exports dos dois agentes.

Verificado: pnpm turbo run typecheck build (28/28); containers
eden-api/eden-core reconstruídos; bundle de produção contém todas as
novas telas; login simulando Origin de browser real; listagens
retornando dados reais; criação de lead confirmando o formato camelCase.

Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-03 18:31:07 -03:00
parent 4d67fe5f1a
commit f96c465ee5
31 changed files with 3493 additions and 5 deletions

View File

@@ -4,6 +4,20 @@ import { AuthenticatedLayout } from "./routes/AuthenticatedLayout.js";
import { DashboardPage } from "./routes/DashboardPage.js";
import { LoginPage } from "./routes/LoginPage.js";
import { ProtectedRoute } from "./routes/ProtectedRoute.js";
import { CompaniesListPage } from "./routes/companies/CompaniesListPage.js";
import { CompanyFormPage } from "./routes/companies/CompanyFormPage.js";
import { ProductsListPage } from "./routes/products/ProductsListPage.js";
import { ProductFormPage } from "./routes/products/ProductFormPage.js";
import { ResellersListPage } from "./routes/resellers/ResellersListPage.js";
import { ResellerFormPage } from "./routes/resellers/ResellerFormPage.js";
import { LeadsListPage } from "./routes/crm/LeadsListPage.js";
import { LeadFormPage } from "./routes/crm/LeadFormPage.js";
import { ConvertLeadPage } from "./routes/crm/ConvertLeadPage.js";
import { OpportunitiesListPage } from "./routes/crm/OpportunitiesListPage.js";
import { OpportunityFormPage } from "./routes/crm/OpportunityFormPage.js";
import { QuotesListPage } from "./routes/quotes/QuotesListPage.js";
import { QuoteFormPage } from "./routes/quotes/QuoteFormPage.js";
import { QuoteDetailPage } from "./routes/quotes/QuoteDetailPage.js";
export function App() {
return (
@@ -14,6 +28,31 @@ export function App() {
<Route element={<ProtectedRoute />}>
<Route element={<AuthenticatedLayout />}>
<Route path="/" element={<DashboardPage />} />
<Route path="/companies" element={<CompaniesListPage />} />
<Route path="/companies/new" element={<CompanyFormPage />} />
<Route path="/companies/:id/edit" element={<CompanyFormPage />} />
<Route path="/products" element={<ProductsListPage />} />
<Route path="/products/new" element={<ProductFormPage />} />
<Route path="/products/:id/edit" element={<ProductFormPage />} />
<Route path="/resellers" element={<ResellersListPage />} />
<Route path="/resellers/new" element={<ResellerFormPage />} />
<Route path="/resellers/:id/edit" element={<ResellerFormPage />} />
<Route path="/leads" element={<LeadsListPage />} />
<Route path="/leads/new" element={<LeadFormPage />} />
<Route path="/leads/:id/edit" element={<LeadFormPage />} />
<Route path="/leads/:id/convert" element={<ConvertLeadPage />} />
<Route path="/opportunities" element={<OpportunitiesListPage />} />
<Route path="/opportunities/new" element={<OpportunityFormPage />} />
<Route path="/opportunities/:id/edit" element={<OpportunityFormPage />} />
<Route path="/quotes" element={<QuotesListPage />} />
<Route path="/quotes/new" element={<QuoteFormPage />} />
<Route path="/quotes/:id" element={<QuoteDetailPage />} />
</Route>
</Route>
</Routes>