feat(web): foundation com brand JCS + AntD theme + Rafael painel placeholder

- Design tokens (CSS variables) espelhando brand.md v1.0:
  - Paleta JCS Blue #004a99 + estados funcionais
  - Plus Jakarta Sans Variable self-host (LGPD + perf)
  - Radius 12/20, sombra 0 4px 25px rgba(0,0,0,0.05)
  - Layout topbar 80 + sidebar 260 (brand.md canon)
- AntD ConfigProvider com tema JCS (cores, fonts, radius, shadow, motion)
- TanStack Router + Query setup com defaults conservadores
- AppShell desktop (Topbar + Sidebar) com tom canônico
- RafaelPainel placeholder com vocabulário canônico:
  meta de maio, clientes esfriando (OPENFRIOS 47 dias), próxima visita,
  comissão+FLEX, copy direta apple-inspired
- Logos copiadas para apps/web/public/
- Limpeza: removidos placeholders Nx (app.tsx, nx-welcome.tsx, styles.css)
- pt-BR locale (dayjs + AntD)
- Build OK: 878KB JS (vai code-splitar pós cockpits separados)

Refs: brand.md, design-artifacts/A-Product-Brief/03-visual-direction.md,
      design-artifacts/B-Trigger-Map/personas/02-rafael-representante.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 17:12:34 +00:00
parent 17c08e6392
commit 3a42723c71
23 changed files with 2037 additions and 893 deletions

View File

@@ -0,0 +1,102 @@
import { Menu } from 'antd';
import { useLocation, useNavigate } from '@tanstack/react-router';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faChartLine,
faClipboardList,
faFunnelDollar,
faCalendarDays,
faUsers,
faBoxesStacked,
faGear,
faPercent,
faFileInvoiceDollar,
} from '@fortawesome/free-solid-svg-icons';
import type { ItemType } from 'antd/es/menu/interface';
/**
* Sidebar canônica do SAR (260px fixa — brand.md).
* Itens mockados para Rafael cockpit. Variantes por cockpit virão depois.
*/
export function Sidebar() {
const navigate = useNavigate();
const location = useLocation();
const items: ItemType[] = [
{
key: '/',
icon: <FontAwesomeIcon icon={faChartLine} fixedWidth />,
label: 'Painel',
},
{
key: '/agenda',
icon: <FontAwesomeIcon icon={faCalendarDays} fixedWidth />,
label: 'Agenda e Rotas',
},
{
key: '/clientes',
icon: <FontAwesomeIcon icon={faUsers} fixedWidth />,
label: 'Clientes',
},
{
key: '/produtos',
icon: <FontAwesomeIcon icon={faBoxesStacked} fixedWidth />,
label: 'Catálogo',
},
{
key: '/funil',
icon: <FontAwesomeIcon icon={faFunnelDollar} fixedWidth />,
label: 'Funil de Vendas',
},
{
key: '/pedidos',
icon: <FontAwesomeIcon icon={faClipboardList} fixedWidth />,
label: 'Pedidos',
},
{
key: '/comissao',
icon: <FontAwesomeIcon icon={faPercent} fixedWidth />,
label: 'Comissão / FLEX',
},
{
type: 'divider',
},
{
key: '/relatorios',
icon: <FontAwesomeIcon icon={faFileInvoiceDollar} fixedWidth />,
label: 'Relatórios',
},
{
key: '/configuracoes',
icon: <FontAwesomeIcon icon={faGear} fixedWidth />,
label: 'Configurações',
},
];
return (
<aside
style={{
width: 'var(--layout-sidebar-width)',
flexShrink: 0,
height: 'calc(100vh - var(--layout-topbar-height))',
background: 'var(--bg-surface)',
borderRight: '1px solid var(--border-subtle)',
position: 'sticky',
top: 'var(--layout-topbar-height)',
overflowY: 'auto',
padding: 'var(--space-md) var(--space-xs)',
}}
>
<Menu
mode="inline"
selectedKeys={[location.pathname]}
items={items}
onClick={({ key }) => navigate({ to: key as string })}
style={{
border: 'none',
background: 'transparent',
}}
/>
</aside>
);
}