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:
@@ -1,14 +1,28 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Link, Outlet } from "react-router-dom";
|
||||
import { Link, Outlet, useLocation } from "react-router-dom";
|
||||
import { AppShell } from "@eden/ui";
|
||||
import type { AppShellNavItem } from "@eden/ui";
|
||||
import { useAuth } from "../lib/auth/AuthContext.js";
|
||||
|
||||
const NAV_ITEMS: AppShellNavItem[] = [{ key: "dashboard", label: "Painel", href: "/", active: true }];
|
||||
/** `href === "/"` only matches the exact dashboard path — every other item matches by exact path or a `/` prefix boundary (so `/companies` never lights up for `/companies-other`). */
|
||||
function isNavItemActive(href: string, pathname: string): boolean {
|
||||
if (href === "/") return pathname === "/";
|
||||
return pathname === href || pathname.startsWith(`${href}/`);
|
||||
}
|
||||
|
||||
export function AuthenticatedLayout() {
|
||||
const { user, logout } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const navItems: AppShellNavItem[] = [
|
||||
{ key: "dashboard", label: "Painel", href: "/" },
|
||||
{ key: "companies", label: "Empresas", href: "/companies" },
|
||||
{ key: "products", label: "Produtos", href: "/products" },
|
||||
{ key: "resellers", label: "Revendas", href: "/resellers" },
|
||||
{ key: "leads", label: "CRM", href: "/leads" },
|
||||
{ key: "quotes", label: "Ofertas", href: "/quotes" },
|
||||
].map((item) => ({ ...item, active: isNavItemActive(item.href, location.pathname) }));
|
||||
|
||||
async function handleLogout() {
|
||||
await logout();
|
||||
@@ -19,7 +33,7 @@ export function AuthenticatedLayout() {
|
||||
<AppShell
|
||||
appName="EDEN Core"
|
||||
logoSrc="/eden-logo-horizontal.png"
|
||||
navItems={NAV_ITEMS}
|
||||
navItems={navItems}
|
||||
userName={user?.fullName ?? user?.email}
|
||||
userRole={user?.roleKey}
|
||||
onLogout={handleLogout}
|
||||
|
||||
Reference in New Issue
Block a user