feat(web): redesign NewOrderPage e OrdersPage + botão Novo Pedido global

NewOrderPage:
- Layout de página única com cards (remove wizard em steps)
- AutoComplete de cliente com busca na API
- Badge de confirmação ao selecionar cliente
- Select de Pauta (API real) e Condição de Pagamento (mock)
- Campos Contato e Nº OC
- AutoComplete de produto por catálogo com pauta aplicada
- Soma qty automaticamente se produto já está no carrinho
- Tabela de itens com qty/desconto editáveis inline
- Rodapé fixo com total e botão Finalizar verde

OrdersPage:
- Cards de métricas (total, vendido, pendentes, aprovados, ticket médio)
- Filtros por status e período (hoje / 7d / 30d)
- Tabela com row-click colorido por status
- Drawer lateral com detalhes, itens e timeline de histórico
- Menu de ações por linha (ver, duplicar, PDF, cancelar)
- Cards mobile responsivos

Layout global:
- Botão Novo Pedido na Topbar (sempre visível)
- FAB verde fixo (bottom-right) no AppShell

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-29 18:48:01 +00:00
parent 7fad03475e
commit fb6df551b7
4 changed files with 1415 additions and 413 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,142 +1,776 @@
import { useState } from 'react';
import { Table, Tag, Input, Select, Space, Typography, Badge } from 'antd';
import {
Button,
Card,
Col,
Drawer,
Dropdown,
Empty,
Grid,
Row,
Select,
Space,
Spin,
Table,
Tag,
Timeline,
Typography,
} from 'antd';
import type { TableColumnsType } from 'antd';
import { Link } from '@tanstack/react-router';
import type { MenuProps } from 'antd';
import {
CheckCircleOutlined,
ClockCircleOutlined,
CloseCircleOutlined,
CopyOutlined,
DollarOutlined,
EllipsisOutlined,
EyeOutlined,
FilePdfOutlined,
PlusOutlined,
ShoppingCartOutlined,
} from '@ant-design/icons';
import { Link, useNavigate } from '@tanstack/react-router';
import type { PedidoSummary } from '@sar/api-interface';
import { SITUA_LABEL } from '@sar/api-interface';
import { useOrderList } from '../../lib/queries/orders';
import { useOrderList, useOrderDetail } from '../../lib/queries/orders';
const { Title } = Typography;
const { Search } = Input;
const { Title, Text } = Typography;
const { useBreakpoint } = Grid;
const SITUA_COLOR: Record<number, string> = {
1: 'warning',
2: 'processing',
3: 'error',
4: 'success',
// ─── Helpers ──────────────────────────────────────────────────────────────────
function fmt(v: number | string) {
return Number(v).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' });
}
function fmtDate(v: string) {
return new Date(v).toLocaleDateString('pt-BR');
}
function toISO(d: Date) {
return d.toISOString().split('T')[0];
}
function periodRange(p: string): { from?: string; to?: string } {
const today = new Date();
if (p === 'today') return { from: toISO(today), to: toISO(today) };
if (p === '7d') {
const d = new Date(today);
d.setDate(d.getDate() - 7);
return { from: toISO(d), to: toISO(today) };
}
if (p === '30d') {
const d = new Date(today);
d.setDate(d.getDate() - 30);
return { from: toISO(d), to: toISO(today) };
}
return {};
}
// ─── Status ───────────────────────────────────────────────────────────────────
const STATUS: Record<number, { label: string; color: string; rowBg: string; tagColor: string }> = {
1: { label: 'Ag. Aprovação', color: '#d46b08', rowBg: '#fffbe6', tagColor: 'orange' },
2: { label: 'Aprovado', color: '#389e0d', rowBg: '#f6ffed', tagColor: 'green' },
3: { label: 'Cancelado', color: '#cf1322', rowBg: '#fff1f0', tagColor: 'red' },
4: { label: 'Faturado', color: '#1d39c4', rowBg: '#f0f5ff', tagColor: 'geekblue' },
};
const columns: TableColumnsType<PedidoSummary> = [
{
title: 'Nº',
dataIndex: 'numero',
width: 120,
render: (_: number, row: PedidoSummary) => {
const label = row.numero ? String(row.numero) : row.numPedSar || row.id;
return row.fonte === 'erp' ? (
<span style={{ fontVariantNumeric: 'tabular-nums' }}>{label}</span>
// ─── OrderStatusBadge ─────────────────────────────────────────────────────────
function OrderStatusBadge({ situa, descr }: { situa: number; descr?: string }) {
const cfg = STATUS[situa];
const label = descr ?? cfg?.label ?? SITUA_LABEL[situa] ?? String(situa);
return (
<Tag
color={cfg?.tagColor ?? 'default'}
style={{ borderRadius: 20, fontWeight: 600, fontSize: 11, padding: '1px 10px' }}
>
{label}
</Tag>
);
}
// ─── OrdersMetrics ────────────────────────────────────────────────────────────
function OrdersMetrics({ data }: { data: PedidoSummary[] }) {
const total = data.reduce((a, o) => a + Number(o.total), 0);
const pendentes = data.filter((o) => o.situa === 1).length;
const aprovados = data.filter((o) => o.situa === 2).length;
const ticket = data.length > 0 ? total / data.length : 0;
const metrics = [
{
label: 'Total de Pedidos',
value: String(data.length),
icon: <ShoppingCartOutlined />,
color: '#003B8E',
},
{ label: 'Total Vendido', value: fmt(total), icon: <DollarOutlined />, color: '#389e0d' },
{
label: 'Ag. Aprovação',
value: String(pendentes),
icon: <ClockCircleOutlined />,
color: '#d46b08',
},
{
label: 'Aprovados',
value: String(aprovados),
icon: <CheckCircleOutlined />,
color: '#389e0d',
},
{ label: 'Ticket Médio', value: fmt(ticket), icon: <DollarOutlined />, color: '#1d39c4' },
];
return (
<Row gutter={[12, 12]} style={{ marginBottom: 16 }}>
{metrics.map((m) => (
<Col key={m.label} xs={12} sm={8} md={6} lg={24 / metrics.length}>
<Card
style={{
borderRadius: 10,
border: '1px solid #EBF0F5',
boxShadow: '0 1px 4px rgba(0,0,0,0.06)',
}}
styles={{ body: { padding: '14px 18px' } }}
>
<Space size={10} align="center">
<span style={{ fontSize: 20, color: m.color }}>{m.icon}</span>
<div>
<Text
style={{
fontSize: 11,
color: '#64748B',
fontWeight: 600,
textTransform: 'uppercase',
letterSpacing: '0.06em',
display: 'block',
}}
>
{m.label}
</Text>
<Text strong style={{ fontSize: 18, color: '#1F2937', lineHeight: 1.2 }}>
{m.value}
</Text>
</div>
</Space>
</Card>
</Col>
))}
</Row>
);
}
// ─── OrderActionsMenu ─────────────────────────────────────────────────────────
function OrderActionsMenu({
order,
onView,
}: {
order: PedidoSummary;
onView: (id: string) => void;
}) {
const navigate = useNavigate();
const canDetail = order.fonte !== 'erp';
const items: MenuProps['items'] = [
canDetail
? {
key: 'view',
icon: <EyeOutlined />,
label: 'Ver detalhes',
onClick: () => onView(order.id),
}
: { key: 'view', icon: <EyeOutlined />, label: 'Ver detalhes', disabled: true },
{
key: 'duplicate',
icon: <CopyOutlined style={{ color: '#0057D9' }} />,
label: <span style={{ color: '#0057D9', fontWeight: 600 }}>Duplicar pedido</span>,
onClick: () =>
void navigate({ to: '/pedidos/novo', search: { clientId: String(order.idCliente) } }),
},
{ type: 'divider' },
{
key: 'pdf',
icon: <FilePdfOutlined />,
label: 'Gerar PDF',
onClick: () => alert('PDF em breve'),
},
{
key: 'cancel',
icon: <CloseCircleOutlined />,
label: 'Cancelar pedido',
danger: true,
disabled: order.situa === 3,
onClick: () => alert('Cancelamento em breve'),
},
];
return (
<Dropdown menu={{ items }} trigger={['click']} placement="bottomRight">
<Button type="text" icon={<EllipsisOutlined />} size="small" />
</Dropdown>
);
}
// ─── OrderDetailDrawer ────────────────────────────────────────────────────────
function OrderDetailDrawer({ id, onClose }: { id: string | null; onClose: () => void }) {
const { data, isLoading } = useOrderDetail(id ?? undefined);
const timelineItems = (data?.historico ?? []).map((h) => ({
dot:
h.situaNova === 2 ? (
<CheckCircleOutlined style={{ color: '#389e0d' }} />
) : h.situaNova === 3 ? (
<CloseCircleOutlined style={{ color: '#cf1322' }} />
) : (
<Link to="/pedidos/$id" params={{ id: row.id }}>
{label}
</Link>
);
},
},
{
title: 'Status',
dataIndex: 'situa',
width: 150,
render: (s: number, row: PedidoSummary) => {
const label = row.statusDescr ?? SITUA_LABEL[s] ?? String(s);
return (
<Badge
status={
(SITUA_COLOR[s] ?? 'default') as
| 'default'
| 'warning'
| 'processing'
| 'success'
| 'error'
<ClockCircleOutlined style={{ color: '#d46b08' }} />
),
children: (
<span style={{ fontSize: 13 }}>
<Text type="secondary">{new Date(h.changedAt).toLocaleString('pt-BR')}</Text>
{' — '}
{h.situaAnterior === null ? (
<strong>Pedido criado</strong>
) : (
<>
Status alterado para{' '}
<strong>{STATUS[h.situaNova]?.label ?? SITUA_LABEL[h.situaNova] ?? h.situaNova}</strong>
</>
)}
{h.nota && <Text type="secondary"> · {h.nota}</Text>}
</span>
),
}));
const label: React.CSSProperties = {
fontSize: 11,
fontWeight: 700,
letterSpacing: '0.08em',
textTransform: 'uppercase',
color: '#64748B',
marginBottom: 2,
display: 'block',
};
return (
<Drawer
title={data ? `Pedido ${data.numPedSar}` : 'Detalhes do Pedido'}
open={!!id}
onClose={onClose}
width={520}
styles={{ body: { padding: '16px 24px' } }}
footer={
<Space>
<Button onClick={onClose}>Fechar</Button>
{data && (
<Button type="primary" onClick={() => alert('PDF em breve')}>
Gerar PDF
</Button>
)}
</Space>
}
>
{isLoading && <Spin style={{ display: 'block', marginTop: 48, textAlign: 'center' }} />}
{data && (
<Space direction="vertical" size={20} style={{ width: '100%' }}>
{/* Status */}
<div>
<OrderStatusBadge situa={data.situa} descr={data.statusDescr} />
</div>
{/* Dados principais */}
<Card
styles={{ body: { padding: '14px 16px' } }}
style={{ borderRadius: 8, background: '#F8FAFC', border: '1px solid #EBF0F5' }}
>
<Row gutter={[16, 10]}>
<Col span={12}>
<span style={label}>Pedido</span>
<Text strong>{data.numPedSar}</Text>
</Col>
<Col span={12}>
<span style={label}>Data</span>
<Text>{fmtDate(data.dtPedido)}</Text>
</Col>
<Col span={12}>
<span style={label}>Cód. Cliente</span>
<Text>{data.idCliente}</Text>
</Col>
<Col span={12}>
<span style={label}>Total</span>
<Text strong style={{ color: '#003B8E', fontSize: 16 }}>
{fmt(data.total)}
</Text>
</Col>
{data.obs && (
<Col span={24}>
<span style={label}>Observações</span>
<Text type="secondary">{data.obs}</Text>
</Col>
)}
</Row>
</Card>
{/* Itens */}
{data.itens?.length > 0 && (
<div>
<Text style={{ ...label, marginBottom: 8 }}>
Itens do Pedido ({data.itens.length})
</Text>
{data.itens.map((item) => (
<div
key={item.id}
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '8px 12px',
borderRadius: 8,
background: '#F8FAFC',
border: '1px solid #EBF0F5',
marginBottom: 6,
}}
>
<Space direction="vertical" size={0}>
<Text style={{ fontSize: 12, color: '#64748B' }}>{item.codProduto}</Text>
<Text style={{ fontWeight: 500 }}>{item.descProduto}</Text>
<Text type="secondary" style={{ fontSize: 12 }}>
{Number(item.qtd)} un × {fmt(Number(item.precoUnitario))}
</Text>
</Space>
<Text strong className="tabular-nums">
{fmt(Number(item.total))}
</Text>
</div>
))}
<div
style={{
display: 'flex',
justifyContent: 'flex-end',
marginTop: 8,
paddingTop: 8,
borderTop: '1px solid #EBF0F5',
}}
>
<Text strong style={{ fontSize: 15, color: '#003B8E' }}>
Total: {fmt(data.total)}
</Text>
</div>
</div>
)}
{/* Histórico */}
{timelineItems.length > 0 && (
<div>
<Text style={{ ...label, marginBottom: 10 }}>Histórico</Text>
<Timeline items={timelineItems} />
</div>
)}
</Space>
)}
</Drawer>
);
}
// ─── MobileOrderCard ──────────────────────────────────────────────────────────
function MobileOrderCard({
order,
onView,
}: {
order: PedidoSummary;
onView: (id: string) => void;
}) {
const navigate = useNavigate();
const cfg = STATUS[order.situa];
return (
<Card
style={{
borderRadius: 10,
marginBottom: 10,
border: `1px solid ${cfg?.rowBg ?? '#EBF0F5'}`,
background: cfg?.rowBg ?? '#fff',
boxShadow: '0 1px 4px rgba(0,0,0,0.06)',
}}
styles={{ body: { padding: '14px 16px' } }}
>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
<Text strong style={{ fontSize: 15 }}>
{order.numPedSar}
</Text>
<OrderStatusBadge situa={order.situa} descr={order.statusDescr} />
</div>
<Text type="secondary" style={{ fontSize: 12, display: 'block', marginBottom: 4 }}>
Cód. cliente {order.idCliente} · {fmtDate(order.dtPedido)}
</Text>
<Text strong style={{ fontSize: 16, color: '#003B8E' }}>
{fmt(order.total)}
</Text>
<div style={{ marginTop: 10, display: 'flex', gap: 8 }}>
<Button
size="small"
icon={<EyeOutlined />}
disabled={order.fonte === 'erp'}
onClick={() => onView(order.id)}
>
Ver
</Button>
<Button
size="small"
icon={<CopyOutlined />}
onClick={() =>
void navigate({ to: '/pedidos/novo', search: { clientId: String(order.idCliente) } })
}
text={<Tag color={SITUA_COLOR[s] ?? 'default'}>{label}</Tag>}
/>
);
},
},
{
title: 'Total',
dataIndex: 'total',
width: 130,
align: 'right',
render: (v: string) =>
Number(v).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' }),
},
{
title: 'Data',
dataIndex: 'dtPedido',
width: 130,
render: (v: string) => new Date(v).toLocaleDateString('pt-BR'),
},
];
>
Duplicar
</Button>
</div>
</Card>
);
}
// ─── EmptyState ───────────────────────────────────────────────────────────────
function EmptyOrders({ onNew }: { onNew: () => void }) {
return (
<Empty
image={<ShoppingCartOutlined style={{ fontSize: 56, color: '#D9E2EC' }} />}
imageStyle={{ height: 64 }}
description={
<Space direction="vertical" size={4}>
<Text strong style={{ fontSize: 15 }}>
Nenhum pedido encontrado
</Text>
<Text type="secondary">Tente alterar os filtros ou crie um novo pedido.</Text>
</Space>
}
style={{ padding: '48px 0' }}
>
<Button type="primary" icon={<PlusOutlined />} onClick={onNew}>
Novo Pedido
</Button>
</Empty>
);
}
// ─── OrdersPage ───────────────────────────────────────────────────────────────
export function OrdersPage() {
const [numFilter, setNumFilter] = useState('');
const navigate = useNavigate();
const screens = useBreakpoint();
const isMobile = !screens.md;
const [search, setSearch] = useState('');
const [situaFilter, setSituaFilter] = useState<number | undefined>();
const [period, setPeriod] = useState('');
const [page, setPage] = useState(1);
const limit = 50;
const [drawerOrderId, setDrawerOrderId] = useState<string | null>(null);
const limit = 20;
const { from, to } = period ? periodRange(period) : {};
const { data, isLoading } = useOrderList({
numPedSar: numFilter || undefined,
numPedSar: search || undefined,
situa: situaFilter,
from,
to,
page,
limit,
});
const rows = data?.data ?? [];
const total = data?.total ?? 0;
function clearFilters() {
setSearch('');
setSituaFilter(undefined);
setPeriod('');
setPage(1);
}
// ── Tabela desktop ─────────────────────────────────────────────────────────
const columns: TableColumnsType<PedidoSummary> = [
{
title: 'Nº Pedido',
dataIndex: 'numPedSar',
width: 140,
render: (_: string, row: PedidoSummary) => {
const label = row.numero ? String(row.numero) : row.numPedSar;
return row.fonte === 'erp' ? (
<Text strong className="tabular-nums">
{label}
</Text>
) : (
<Link to="/pedidos/$id" params={{ id: row.id }}>
<Text strong className="tabular-nums" style={{ color: '#0057D9' }}>
{label}
</Text>
</Link>
);
},
},
{
title: 'Cliente',
key: 'cliente',
render: (_: unknown, row: PedidoSummary) => (
<Space direction="vertical" size={0}>
<Text style={{ fontWeight: 500 }}>Cód. {row.idCliente}</Text>
{row.obs && (
<Text type="secondary" style={{ fontSize: 11 }} ellipsis={{ tooltip: row.obs }}>
{row.obs.slice(0, 40)}
</Text>
)}
</Space>
),
},
{
title: 'Status',
dataIndex: 'situa',
width: 140,
render: (s: number, row: PedidoSummary) => (
<OrderStatusBadge situa={s} descr={row.statusDescr} />
),
},
{
title: 'Total',
dataIndex: 'total',
width: 130,
align: 'right',
render: (v: string) => (
<Text strong className="tabular-nums">
{fmt(v)}
</Text>
),
},
{
title: 'Data',
dataIndex: 'dtPedido',
width: 110,
render: (v: string) => <Text type="secondary">{fmtDate(v)}</Text>,
},
{
title: '',
key: 'actions',
width: 48,
render: (_: unknown, row: PedidoSummary) => (
<OrderActionsMenu order={row} onView={(id) => setDrawerOrderId(id)} />
),
},
];
return (
<div style={{ padding: 24 }}>
<Title level={3} style={{ marginBottom: 16 }}>
Pedidos
</Title>
<Space style={{ marginBottom: 16 }} wrap>
<Search
placeholder="Buscar por número (SAR-NNNNN)..."
allowClear
style={{ width: 240 }}
onSearch={(v) => {
setNumFilter(v);
setPage(1);
}}
onChange={(e) => {
if (!e.target.value) {
setNumFilter('');
setPage(1);
}
}}
/>
<Select
placeholder="Status"
allowClear
style={{ width: 160 }}
onChange={(v) => {
setSituaFilter(v as number | undefined);
setPage(1);
}}
options={[
{ value: 1, label: 'Ag. Aprovação' },
{ value: 2, label: 'Aprovado' },
{ value: 3, label: 'Cancelado' },
{ value: 4, label: 'Faturado' },
]}
/>
</Space>
<Table<PedidoSummary>
rowKey="id"
columns={columns}
dataSource={data?.data ?? []}
loading={isLoading}
rowClassName={(row) => (row.situa === 1 ? 'row-pending' : '')}
pagination={{
current: page,
pageSize: limit,
total: data?.total ?? 0,
showSizeChanger: false,
onChange: (p) => setPage(p),
<div style={{ maxWidth: 1200, margin: '0 auto' }}>
{/* ── Cabeçalho ───────────────────────────────────────────────── */}
<div
style={{
display: 'flex',
alignItems: 'flex-start',
justifyContent: 'space-between',
marginBottom: 20,
}}
/>
>
<div>
<Title level={3} style={{ margin: 0, color: '#003B8E' }}>
Pedidos
</Title>
<p style={{ margin: '4px 0 0', color: '#64748B', fontSize: 14 }}>
Acompanhe seus pedidos, status de envio e histórico comercial.
</p>
</div>
{!isMobile && (
<Button
type="primary"
icon={<PlusOutlined />}
size="large"
onClick={() => void navigate({ to: '/pedidos/novo' })}
style={{ borderRadius: 8, fontWeight: 600 }}
>
Novo Pedido
</Button>
)}
</div>
<style>{`.row-pending td { background: #fffbe6 !important; }`}</style>
{/* ── Métricas ────────────────────────────────────────────────── */}
<OrdersMetrics data={rows} />
{/* ── Filtros ─────────────────────────────────────────────────── */}
<Card
style={{
borderRadius: 10,
border: '1px solid #EBF0F5',
boxShadow: '0 1px 4px rgba(0,0,0,0.06)',
marginBottom: 16,
}}
styles={{ body: { padding: '14px 20px' } }}
>
<Row gutter={[12, 12]} align="middle">
<Col xs={24} sm={24} md={8}>
<input
value={search}
onChange={(e) => {
setSearch(e.target.value);
setPage(1);
}}
placeholder="Buscar por nº do pedido..."
style={{
width: '100%',
height: 32,
padding: '0 11px',
border: '1px solid #d9d9d9',
borderRadius: 6,
fontSize: 14,
outline: 'none',
color: '#1F2937',
boxSizing: 'border-box',
}}
/>
</Col>
<Col xs={12} sm={8} md={5}>
<Select
style={{ width: '100%' }}
placeholder="Status"
allowClear
value={situaFilter}
onChange={(v) => {
setSituaFilter(v);
setPage(1);
}}
options={[
{ value: 1, label: 'Ag. Aprovação' },
{ value: 2, label: 'Aprovado' },
{ value: 3, label: 'Cancelado' },
{ value: 4, label: 'Faturado' },
]}
/>
</Col>
<Col xs={12} sm={8} md={5}>
<Select
style={{ width: '100%' }}
placeholder="Período"
allowClear
value={period || undefined}
onChange={(v) => {
setPeriod(v ?? '');
setPage(1);
}}
options={[
{ value: 'today', label: 'Hoje' },
{ value: '7d', label: 'Últimos 7 dias' },
{ value: '30d', label: 'Últimos 30 dias' },
]}
/>
</Col>
<Col xs={24} sm={8} md={4}>
<Button
style={{ width: '100%', borderRadius: 6 }}
onClick={clearFilters}
disabled={!search && !situaFilter && !period}
>
Limpar filtros
</Button>
</Col>
</Row>
</Card>
{/* ── Conteúdo principal ──────────────────────────────────────── */}
{isLoading ? (
<div style={{ textAlign: 'center', padding: 64 }}>
<Spin size="large" />
</div>
) : rows.length === 0 ? (
<Card
style={{ borderRadius: 10, border: '1px solid #EBF0F5' }}
styles={{ body: { padding: 0 } }}
>
<EmptyOrders onNew={() => void navigate({ to: '/pedidos/novo' })} />
</Card>
) : isMobile ? (
/* ── Mobile: cards ─────────────────────────────────────────── */
<div>
{rows.map((o) => (
<MobileOrderCard key={o.id} order={o} onView={(id) => setDrawerOrderId(id)} />
))}
<div
style={{ textAlign: 'center', padding: '8px 0 16px', color: '#64748B', fontSize: 13 }}
>
Mostrando {rows.length} de {total} pedidos
</div>
</div>
) : (
/* ── Desktop: tabela ────────────────────────────────────────── */
<Card
style={{
borderRadius: 10,
border: '1px solid #EBF0F5',
boxShadow: '0 1px 4px rgba(0,0,0,0.06)',
}}
styles={{ body: { padding: 0 } }}
>
<Table<PedidoSummary>
rowKey="id"
columns={columns}
dataSource={rows}
size="middle"
onRow={(row) => ({
onClick: () => {
if (row.fonte !== 'erp') setDrawerOrderId(row.id);
},
style: {
background: STATUS[row.situa]?.rowBg ?? '#fff',
cursor: row.fonte !== 'erp' ? 'pointer' : 'default',
},
})}
pagination={{
current: page,
pageSize: limit,
total,
showSizeChanger: false,
showTotal: (t, [s, e]) => `Mostrando ${s}${e} de ${t} pedidos`,
onChange: (p) => setPage(p),
style: { padding: '12px 24px' },
}}
style={{ borderRadius: 10, overflow: 'hidden' }}
/>
</Card>
)}
{/* ── Drawer de detalhe ───────────────────────────────────────── */}
<OrderDetailDrawer id={drawerOrderId} onClose={() => setDrawerOrderId(null)} />
{/* FAB mobile */}
{isMobile && (
<Button
type="primary"
shape="circle"
icon={<PlusOutlined />}
size="large"
onClick={() => void navigate({ to: '/pedidos/novo' })}
style={{
position: 'fixed',
bottom: 24,
right: 24,
width: 52,
height: 52,
fontSize: 22,
backgroundColor: '#389e0d',
borderColor: '#389e0d',
boxShadow: '0 4px 16px rgba(56,158,13,0.45)',
zIndex: 1000,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
/>
)}
<style>{`
.ant-table-row:hover td { background: inherit !important; filter: brightness(0.97); }
`}</style>
</div>
);
}