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:
204
apps/web/src/cockpits/rafael/RafaelPainel.tsx
Normal file
204
apps/web/src/cockpits/rafael/RafaelPainel.tsx
Normal file
@@ -0,0 +1,204 @@
|
||||
import { Card, Col, Flex, Progress, Row, Space, Tag, Typography } from 'antd';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import {
|
||||
faArrowTrendUp,
|
||||
faClipboardCheck,
|
||||
faCircleExclamation,
|
||||
faRoute,
|
||||
faMessage,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
/**
|
||||
* Painel do Rafael (Representante) — PRIMARY persona.
|
||||
* MOCK data — substituir por TanStack Query quando API estiver pronta.
|
||||
* Tom canônico: Direto · Confiante · Específico (vocabulário: meta, carteira, inativo, pedido).
|
||||
*/
|
||||
export function RafaelPainel() {
|
||||
// Mock — em produção vem de TanStack Query
|
||||
const metaMes = { atingido: 47600, total: 60000 };
|
||||
const metaPct = Math.round((metaMes.atingido / metaMes.total) * 100);
|
||||
const falta = metaMes.total - metaMes.atingido;
|
||||
|
||||
return (
|
||||
<Flex vertical gap={24} style={{ maxWidth: 1280, margin: '0 auto' }}>
|
||||
{/* Saudação canon (tom: Direto, Específico) */}
|
||||
<Flex vertical gap={4}>
|
||||
<Title level={2} style={{ margin: 0 }}>
|
||||
Bom dia, Rafael
|
||||
</Title>
|
||||
<Text type="secondary" style={{ fontSize: 'var(--text-lg)' }}>
|
||||
27 de maio · 4 visitas na agenda · 2 propostas pra avançar
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
{/* Linha 1 — Meta + KPIs rápidos */}
|
||||
<Row gutter={[24, 24]}>
|
||||
<Col xs={24} md={12}>
|
||||
<Card style={{ height: '100%' }}>
|
||||
<Flex vertical gap={16}>
|
||||
<Flex justify="space-between" align="flex-start">
|
||||
<Space direction="vertical" size={0}>
|
||||
<Text type="secondary" style={{ fontSize: 'var(--text-sm)' }}>
|
||||
META DE MAIO
|
||||
</Text>
|
||||
<Title level={3} style={{ margin: 0 }} className="tabular-nums">
|
||||
R$ {metaMes.atingido.toLocaleString('pt-BR')}
|
||||
</Title>
|
||||
<Text type="secondary">
|
||||
de R${' '}
|
||||
<span className="tabular-nums">
|
||||
{metaMes.total.toLocaleString('pt-BR')}
|
||||
</span>
|
||||
</Text>
|
||||
</Space>
|
||||
<Tag color={metaPct >= 80 ? 'success' : 'processing'}>
|
||||
{metaPct}% atingido
|
||||
</Tag>
|
||||
</Flex>
|
||||
<Progress
|
||||
percent={metaPct}
|
||||
showInfo={false}
|
||||
strokeColor="var(--jcs-blue)"
|
||||
trailColor="var(--jcs-blue-light)"
|
||||
/>
|
||||
<Text style={{ fontSize: 'var(--text-md)' }}>
|
||||
Faltam{' '}
|
||||
<strong className="tabular-nums">
|
||||
R$ {falta.toLocaleString('pt-BR')}
|
||||
</strong>{' '}
|
||||
pra fechar maio.
|
||||
</Text>
|
||||
</Flex>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<Col xs={12} md={6}>
|
||||
<Card>
|
||||
<Space direction="vertical" size={4}>
|
||||
<Text type="secondary" style={{ fontSize: 'var(--text-sm)' }}>
|
||||
PEDIDOS NO MÊS
|
||||
</Text>
|
||||
<Title level={3} style={{ margin: 0 }} className="tabular-nums">
|
||||
28
|
||||
</Title>
|
||||
<Text type="success" style={{ fontSize: 'var(--text-sm)' }}>
|
||||
<FontAwesomeIcon icon={faArrowTrendUp} /> +18% vs abril
|
||||
</Text>
|
||||
</Space>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<Col xs={12} md={6}>
|
||||
<Card>
|
||||
<Space direction="vertical" size={4}>
|
||||
<Text type="secondary" style={{ fontSize: 'var(--text-sm)' }}>
|
||||
COMISSÃO ACUMULADA
|
||||
</Text>
|
||||
<Title level={3} style={{ margin: 0 }} className="tabular-nums">
|
||||
R$ 2.540
|
||||
</Title>
|
||||
<Text type="secondary" style={{ fontSize: 'var(--text-sm)' }}>
|
||||
FLEX: R$ 380
|
||||
</Text>
|
||||
</Space>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* Linha 2 — Alertas + Próxima visita */}
|
||||
<Row gutter={[24, 24]}>
|
||||
<Col xs={24} lg={12}>
|
||||
<Card
|
||||
title={
|
||||
<Space>
|
||||
<FontAwesomeIcon
|
||||
icon={faCircleExclamation}
|
||||
style={{ color: 'var(--orange)' }}
|
||||
/>
|
||||
Clientes esfriando
|
||||
</Space>
|
||||
}
|
||||
extra={<Text type="secondary">3 hoje</Text>}
|
||||
>
|
||||
<Flex vertical gap={12}>
|
||||
<ClienteInativoItem nome="OPENFRIOS" dias={47} ultimaCompra="R$ 3.200" />
|
||||
<ClienteInativoItem nome="DISTRIBUIDORA NORTE" dias={62} ultimaCompra="R$ 1.880" />
|
||||
<ClienteInativoItem nome="MERCADO SÃO PAULO" dias={71} ultimaCompra="R$ 980" />
|
||||
</Flex>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} lg={12}>
|
||||
<Card
|
||||
title={
|
||||
<Space>
|
||||
<FontAwesomeIcon icon={faRoute} style={{ color: 'var(--jcs-blue)' }} />
|
||||
Próxima visita
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Flex vertical gap={12}>
|
||||
<Space direction="vertical" size={4}>
|
||||
<Title level={4} style={{ margin: 0, color: 'var(--jcs-blue)' }}>
|
||||
OPENFRIOS
|
||||
</Title>
|
||||
<Text type="secondary">
|
||||
Rua das Indústrias, 1.245 · São Paulo, SP · 14:30
|
||||
</Text>
|
||||
</Space>
|
||||
<Flex gap={12} wrap="wrap">
|
||||
<Tag icon={<FontAwesomeIcon icon={faClipboardCheck} />} color="processing">
|
||||
3 pedidos em andamento
|
||||
</Tag>
|
||||
<Tag icon={<FontAwesomeIcon icon={faMessage} />} color="success">
|
||||
WhatsApp atualizado
|
||||
</Tag>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* Footer informativo (sem ruído — tom Apple clean) */}
|
||||
<Flex justify="center" style={{ paddingTop: 16 }}>
|
||||
<Text type="secondary" style={{ fontSize: 'var(--text-xs)' }}>
|
||||
SAR · Força de Vendas · Powered by JCS Sistemas
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
function ClienteInativoItem({
|
||||
nome,
|
||||
dias,
|
||||
ultimaCompra,
|
||||
}: {
|
||||
nome: string;
|
||||
dias: number;
|
||||
ultimaCompra: string;
|
||||
}) {
|
||||
return (
|
||||
<Flex
|
||||
justify="space-between"
|
||||
align="center"
|
||||
style={{
|
||||
padding: 'var(--space-sm) var(--space-md)',
|
||||
borderRadius: 12,
|
||||
background: 'var(--bg-surface-alt)',
|
||||
}}
|
||||
>
|
||||
<Space direction="vertical" size={0}>
|
||||
<Text strong>{nome}</Text>
|
||||
<Text type="secondary" style={{ fontSize: 'var(--text-xs)' }}>
|
||||
Última compra: <span className="tabular-nums">{ultimaCompra}</span>
|
||||
</Text>
|
||||
</Space>
|
||||
<Tag color="warning" className="tabular-nums">
|
||||
{dias} dias
|
||||
</Tag>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user