@snksergio/design-system 0.62.0 → 0.64.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/ai/blocos/chart/budget-breakdown.tsx +174 -0
- package/dist-lib/ai/blocos/indice.md +43 -0
- package/dist-lib/ai/blocos/paneldetail/detalhe-com-tabela.tsx +450 -0
- package/dist-lib/ai/blocos/paneldetail/detalhe-de-tarefa-com-abas.tsx +570 -0
- package/dist-lib/ai/blocos/paneldetail/detalhe-do-registro.tsx +555 -0
- package/dist-lib/ai/componentes/DataTable.md +2 -1
- package/dist-lib/ai/componentes/TableToolbar.md +1 -1
- package/dist-lib/ai/exemplos/app-shell/app-shell-example.tsx +140 -0
- package/dist-lib/ai/exemplos/app-shell/index.ts +3 -0
- package/dist-lib/ai/exemplos/app-shell/nav-data.ts +97 -0
- package/dist-lib/ai/exemplos/app-shell/routes.tsx +75 -0
- package/dist-lib/ai/exemplos/chat/chat-screen.tsx +152 -0
- package/dist-lib/ai/exemplos/chat/chat-v2-mocks.ts +171 -0
- package/dist-lib/ai/exemplos/chat/chat-v2.styles.ts +23 -0
- package/dist-lib/ai/exemplos/chat/chat-v2.types.ts +81 -0
- package/dist-lib/ai/exemplos/chat/components/ChannelDot/channel-dot.tsx +28 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationActionsMenu/conversation-actions-menu.tsx +83 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationActionsMenu/index.ts +4 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.styles.ts +22 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.tsx +203 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.types.ts +10 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationColumn/index.ts +2 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.styles.ts +55 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.tsx +52 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.types.ts +7 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationListItem/index.ts +2 -0
- package/dist-lib/ai/exemplos/chat/components/DateSeparator/date-separator.tsx +18 -0
- package/dist-lib/ai/exemplos/chat/components/DetailField/detail-field.tsx +23 -0
- package/dist-lib/ai/exemplos/chat/components/DetailSection/detail-section.tsx +41 -0
- package/dist-lib/ai/exemplos/chat/components/DetailSection/index.ts +1 -0
- package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.styles.ts +30 -0
- package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.tsx +142 -0
- package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.types.ts +16 -0
- package/dist-lib/ai/exemplos/chat/components/DetailsColumn/index.ts +5 -0
- package/dist-lib/ai/exemplos/chat/components/FilterRow/filter-row.styles.ts +37 -0
- package/dist-lib/ai/exemplos/chat/components/FilterRow/filter-row.tsx +45 -0
- package/dist-lib/ai/exemplos/chat/components/FilterRow/index.ts +1 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.styles.ts +27 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.tsx +66 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.types.ts +13 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-rail.tsx +38 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/index.ts +6 -0
- package/dist-lib/ai/exemplos/chat/components/MessageBubble/index.ts +2 -0
- package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.styles.ts +37 -0
- package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.tsx +29 -0
- package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.types.ts +5 -0
- package/dist-lib/ai/exemplos/chat/components/PersonAvatar/index.ts +5 -0
- package/dist-lib/ai/exemplos/chat/components/PersonAvatar/person-avatar.tsx +31 -0
- package/dist-lib/ai/exemplos/chat/components/QueueColumn/index.ts +2 -0
- package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.styles.ts +19 -0
- package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.tsx +140 -0
- package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.types.ts +13 -0
- package/dist-lib/ai/exemplos/chat/components/RailItem/index.ts +1 -0
- package/dist-lib/ai/exemplos/chat/components/RailItem/rail-item.styles.ts +30 -0
- package/dist-lib/ai/exemplos/chat/components/RailItem/rail-item.tsx +42 -0
- package/dist-lib/ai/exemplos/chat/hooks/use-resizable.ts +98 -0
- package/dist-lib/ai/exemplos/chat/index.ts +1 -0
- package/dist-lib/ai/exemplos/clientes/_table-data.ts +59 -0
- package/dist-lib/ai/exemplos/clientes/clientes-screen.tsx +505 -0
- package/dist-lib/ai/exemplos/clientes/clientes-showcase-mocks.ts +117 -0
- package/dist-lib/ai/exemplos/clientes/clientes-showcase.styles.ts +13 -0
- package/dist-lib/ai/exemplos/clientes/clientes-showcase.types.ts +16 -0
- package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.styles.ts +28 -0
- package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.tsx +303 -0
- package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.types.ts +14 -0
- package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/index.ts +2 -0
- package/dist-lib/ai/exemplos/clientes/components/NovoClienteDrawer/index.ts +2 -0
- package/dist-lib/ai/exemplos/clientes/components/NovoClienteDrawer/novo-cliente-drawer.tsx +207 -0
- package/dist-lib/ai/exemplos/clientes/index.ts +1 -0
- package/dist-lib/ai/exemplos/edit-page/components/StepNav.tsx +81 -0
- package/dist-lib/ai/exemplos/edit-page/components/section-card.tsx +85 -0
- package/dist-lib/ai/exemplos/edit-page/edit-page-screen.tsx +234 -0
- package/dist-lib/ai/exemplos/edit-page/index.ts +1 -0
- package/dist-lib/ai/exemplos/finance/_table-data.ts +57 -0
- package/dist-lib/ai/exemplos/finance/clientes-financeiro-mocks.ts +227 -0
- package/dist-lib/ai/exemplos/finance/clientes-financeiro.types.ts +75 -0
- package/dist-lib/ai/exemplos/finance/clientes-showcase-mocks.ts +117 -0
- package/dist-lib/ai/exemplos/finance/clientes-showcase.styles.ts +13 -0
- package/dist-lib/ai/exemplos/finance/clientes-showcase.types.ts +16 -0
- package/dist-lib/ai/exemplos/finance/components/EditarFinanceDrawer/editar-finance-drawer.tsx +241 -0
- package/dist-lib/ai/exemplos/finance/components/EditarFinanceDrawer/index.ts +5 -0
- package/dist-lib/ai/exemplos/finance/components/ExtratoExpansion/extrato-expansion.tsx +172 -0
- package/dist-lib/ai/exemplos/finance/components/ExtratoExpansion/index.ts +1 -0
- package/dist-lib/ai/exemplos/finance/components/FinanceDetailPanel/finance-detail-panel.tsx +241 -0
- package/dist-lib/ai/exemplos/finance/components/FinanceDetailPanel/index.ts +2 -0
- package/dist-lib/ai/exemplos/finance/components/NovoClienteDrawer/index.ts +2 -0
- package/dist-lib/ai/exemplos/finance/components/NovoClienteDrawer/novo-cliente-drawer.tsx +207 -0
- package/dist-lib/ai/exemplos/finance/components/SacarDialog/index.ts +2 -0
- package/dist-lib/ai/exemplos/finance/components/SacarDialog/sacar-dialog.tsx +346 -0
- package/dist-lib/ai/exemplos/finance/finance-screen.tsx +821 -0
- package/dist-lib/ai/exemplos/finance/index.ts +1 -0
- package/dist-lib/ai/exemplos/gantt/_gantt-data.tsx +402 -0
- package/dist-lib/ai/exemplos/gantt/gantt-screen.tsx +512 -0
- package/dist-lib/ai/exemplos/gantt/index.ts +1 -0
- package/dist-lib/ai/exemplos/login/index.ts +1 -0
- package/dist-lib/ai/exemplos/login/login-screen.tsx +246 -0
- package/dist-lib/ai/exemplos/mapa-rede/components/ConsultorDetailPanel/consultor-detail-panel.tsx +158 -0
- package/dist-lib/ai/exemplos/mapa-rede/components/ConsultorDetailPanel/index.ts +2 -0
- package/dist-lib/ai/exemplos/mapa-rede/index.ts +1 -0
- package/dist-lib/ai/exemplos/mapa-rede/mapa-de-rede-mocks.ts +655 -0
- package/dist-lib/ai/exemplos/mapa-rede/mapa-de-rede.types.ts +53 -0
- package/dist-lib/ai/exemplos/mapa-rede/mapa-rede-screen.tsx +227 -0
- package/dist-lib/ai/exemplos/order-detail/components/ActivityTab.tsx +77 -0
- package/dist-lib/ai/exemplos/order-detail/components/AttachmentsTab.tsx +45 -0
- package/dist-lib/ai/exemplos/order-detail/components/CommentsTab.tsx +111 -0
- package/dist-lib/ai/exemplos/order-detail/components/DetailsTab.tsx +189 -0
- package/dist-lib/ai/exemplos/order-detail/components/OverviewTab.tsx +200 -0
- package/dist-lib/ai/exemplos/order-detail/components/section-card.tsx +85 -0
- package/dist-lib/ai/exemplos/order-detail/index.ts +1 -0
- package/dist-lib/ai/exemplos/order-detail/order-detail-screen.tsx +119 -0
- package/dist-lib/ai/exemplos/order-detail/order-mocks.ts +186 -0
- package/dist-lib/ai/exemplos/order-detail/order.types.ts +118 -0
- package/dist-lib/ai/global/componentes.md +7 -0
- package/dist-lib/ai/indice.json +30 -15
- package/dist-lib/ai/manifest.json +32 -6
- package/dist-lib/ai/roteiros/app-builder/roteiro.md +155 -0
- package/dist-lib/ai/roteiros/auth-builder/roteiro.md +42 -0
- package/dist-lib/ai/roteiros/cards/roteiro.md +34 -0
- package/dist-lib/ai/roteiros/charts/roteiro.md +32 -0
- package/dist-lib/ai/roteiros/chat/roteiro.md +31 -0
- package/dist-lib/ai/roteiros/crud-builder/blueprint.md +63 -0
- package/dist-lib/ai/roteiros/crud-builder/entrevista.md +146 -0
- package/dist-lib/ai/roteiros/crud-builder/geracao.md +113 -0
- package/dist-lib/ai/roteiros/crud-builder/roteiro.md +89 -0
- package/dist-lib/ai/roteiros/drawers/roteiro.md +41 -0
- package/dist-lib/ai/roteiros/list-builder/blueprint.md +94 -0
- package/dist-lib/ai/roteiros/list-builder/entrevista.md +193 -0
- package/dist-lib/ai/roteiros/list-builder/geracao.md +63 -0
- package/dist-lib/ai/roteiros/list-builder/roteiro.md +91 -0
- package/dist-lib/ai/roteiros/module-replicator/roteiro.md +56 -0
- package/dist-lib/ai/roteiros/page-detail/roteiro.md +32 -0
- package/dist-lib/ai/roteiros/page-edit/roteiro.md +31 -0
- package/dist-lib/ai/roteiros/screen-composer/roteiro.md +95 -0
- package/dist-lib/chunks/{panel-B-liPlxr.mjs → panel-CN0ChoHM.mjs} +5 -2
- package/dist-lib/chunks/{panel-B-liPlxr.mjs.map → panel-CN0ChoHM.mjs.map} +1 -1
- package/dist-lib/chunks/{panel-DoNbjeqX.cjs → panel-Cpq1xHck.cjs} +5 -2
- package/dist-lib/chunks/{panel-DoNbjeqX.cjs.map → panel-Cpq1xHck.cjs.map} +1 -1
- package/dist-lib/index.cjs +1 -1
- package/dist-lib/index.mjs +2 -2
- package/dist-lib/preview/clientes.cjs +1 -1
- package/dist-lib/preview/clientes.mjs +1 -1
- package/dist-lib/src/components/ui/DataTable/data-table.d.ts.map +1 -1
- package/dist-lib/src/components/ui/DataTable/data-table.types.d.ts +20 -2
- package/dist-lib/src/components/ui/DataTable/data-table.types.d.ts.map +1 -1
- package/package.json +1 -1
- /package/dist-lib/ai/roteiros/{dashboard → dashboard-builder}/blueprint.md +0 -0
- /package/dist-lib/ai/roteiros/{dashboard → dashboard-builder}/entrevista.md +0 -0
- /package/dist-lib/ai/roteiros/{dashboard → dashboard-builder}/geracao.md +0 -0
- /package/dist-lib/ai/roteiros/{dashboard → dashboard-builder}/roteiro.md +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { useMemo, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Clock,
|
|
4
|
+
Coins,
|
|
5
|
+
Eye,
|
|
6
|
+
MapPin,
|
|
7
|
+
MoreHorizontal,
|
|
8
|
+
Network,
|
|
9
|
+
Pencil,
|
|
10
|
+
RefreshCw,
|
|
11
|
+
UserMinus,
|
|
12
|
+
UserPlus,
|
|
13
|
+
Users,
|
|
14
|
+
} from "lucide-react";
|
|
15
|
+
import { DataList } from "@/components/ui/DataList";
|
|
16
|
+
import type { ListItemData, ListRenderState } from "@/components/ui/List";
|
|
17
|
+
import { Button } from "@/components/ui/Button/button";
|
|
18
|
+
import { Chip } from "@/components/ui/Chip";
|
|
19
|
+
import { PageHeader } from "@/components/ui/PageHeader";
|
|
20
|
+
import { AlertModal } from "@/components/ui/AlertModal";
|
|
21
|
+
import {
|
|
22
|
+
FILTER_FIELDS,
|
|
23
|
+
GRADUACAO,
|
|
24
|
+
NETWORK,
|
|
25
|
+
REGIAO,
|
|
26
|
+
VIEWS,
|
|
27
|
+
countNetwork,
|
|
28
|
+
findConsultor,
|
|
29
|
+
formatNum,
|
|
30
|
+
subtreeLabel,
|
|
31
|
+
} from "./mapa-de-rede-mocks";
|
|
32
|
+
import type { Consultor } from "./mapa-de-rede.types";
|
|
33
|
+
import { ConsultorDetailPanel } from "./components/ConsultorDetailPanel";
|
|
34
|
+
|
|
35
|
+
/* ── Card rico do consultor (renderItem do DataList hierárquico) ──── */
|
|
36
|
+
|
|
37
|
+
function MetaInline({
|
|
38
|
+
icon,
|
|
39
|
+
children,
|
|
40
|
+
}: {
|
|
41
|
+
icon: React.ReactNode;
|
|
42
|
+
children: React.ReactNode;
|
|
43
|
+
}) {
|
|
44
|
+
return (
|
|
45
|
+
<span className="inline-flex items-center gap-gp-xs text-body-sm text-fg-muted [&>svg]:size-icon-sm [&>svg]:text-fg-subtle">
|
|
46
|
+
{icon}
|
|
47
|
+
{children}
|
|
48
|
+
</span>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function renderConsultorCard(item: ListItemData, state: ListRenderState) {
|
|
53
|
+
const c = item.data as Consultor;
|
|
54
|
+
const grad = GRADUACAO[c.graduacao];
|
|
55
|
+
return (
|
|
56
|
+
<div className="flex w-full items-center gap-gp-xl">
|
|
57
|
+
{/* nível por profundidade (N1 = líder), mesma cor — quadrado arredondado
|
|
58
|
+
na altura de título + subtítulo (centralizado). */}
|
|
59
|
+
<span className="grid size-form-md shrink-0 place-items-center rounded-radius-md bg-bg-muted text-caption-sm font-semibold text-fg-muted [font-variant-numeric:tabular-nums]">
|
|
60
|
+
N{state.depth + 1}
|
|
61
|
+
</span>
|
|
62
|
+
|
|
63
|
+
<div className="flex min-w-0 flex-1 flex-col gap-gp-sm">
|
|
64
|
+
<div className="flex flex-wrap items-center gap-gp-sm">
|
|
65
|
+
<span className="truncate text-body-md font-semibold text-fg-default">
|
|
66
|
+
{c.name}
|
|
67
|
+
</span>
|
|
68
|
+
<Chip color={grad.color} variant="soft" size="sm" shape="pill">
|
|
69
|
+
{grad.label}
|
|
70
|
+
</Chip>
|
|
71
|
+
{c.pro && (
|
|
72
|
+
<Chip color="success" variant="soft" size="sm" shape="pill">
|
|
73
|
+
PRO
|
|
74
|
+
</Chip>
|
|
75
|
+
)}
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div className="flex flex-wrap items-center gap-x-gp-2xl gap-y-gp-2xs">
|
|
79
|
+
<MetaInline icon={<Coins />}>
|
|
80
|
+
<strong className="font-semibold text-fg-default [font-variant-numeric:tabular-nums]">
|
|
81
|
+
{formatNum(c.gpProprio)}
|
|
82
|
+
</strong>{" "}
|
|
83
|
+
GP
|
|
84
|
+
</MetaInline>
|
|
85
|
+
<MetaInline icon={<Users />}>
|
|
86
|
+
<strong className="font-semibold text-fg-default [font-variant-numeric:tabular-nums]">
|
|
87
|
+
{formatNum(c.clientes)}
|
|
88
|
+
</strong>{" "}
|
|
89
|
+
clientes
|
|
90
|
+
</MetaInline>
|
|
91
|
+
<MetaInline icon={<Network />}>{subtreeLabel(c)}</MetaInline>
|
|
92
|
+
<MetaInline icon={<MapPin />}>{REGIAO[c.regiao]}</MetaInline>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<span className="hidden shrink-0 items-center gap-gp-xs whitespace-nowrap text-caption-sm text-fg-subtle md:inline-flex [&>svg]:size-icon-xs">
|
|
97
|
+
<Clock />
|
|
98
|
+
{c.lastActive}
|
|
99
|
+
</span>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function toItem(c: Consultor): ListItemData {
|
|
105
|
+
return {
|
|
106
|
+
id: c.id,
|
|
107
|
+
title: c.name,
|
|
108
|
+
data: c,
|
|
109
|
+
children: c.children?.map(toItem),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Mapa de Rede — exemplo de tela (conteúdo, sem AppShell). DataList hierárquico
|
|
115
|
+
* (árvore de consultores por níveis, conectores) + card rico + filtros/abas/
|
|
116
|
+
* refresh + painel de detalhe + modal de remoção. Renderize `<MapaDeRedeScreen />`.
|
|
117
|
+
*/
|
|
118
|
+
export function MapaDeRedeScreen() {
|
|
119
|
+
const [detailId, setDetailId] = useState<string | null>(null);
|
|
120
|
+
const [removing, setRemoving] = useState<Consultor | null>(null);
|
|
121
|
+
|
|
122
|
+
const items = useMemo(() => NETWORK.map(toItem), []);
|
|
123
|
+
const total = useMemo(() => countNetwork(NETWORK), []);
|
|
124
|
+
const detail = detailId ? findConsultor(NETWORK, detailId) : null;
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<div className="flex h-full min-h-0 flex-col gap-gp-2xl">
|
|
128
|
+
<PageHeader
|
|
129
|
+
title="Mapa de Rede"
|
|
130
|
+
description="Navegue a rede de consultores por níveis (líderes → N1 → N2…). Cada card mostra graduação, GP próprio, clientes e o agregado da subárvore. Use as abas, a busca e os filtros pra recortar; clique no card pra ver o detalhe."
|
|
131
|
+
badge={
|
|
132
|
+
<Chip color="primary" variant="soft" size="sm" shape="rounded">
|
|
133
|
+
{total.toLocaleString("pt-BR")} consultores
|
|
134
|
+
</Chip>
|
|
135
|
+
}
|
|
136
|
+
actions={
|
|
137
|
+
<>
|
|
138
|
+
<Button
|
|
139
|
+
variant="outline"
|
|
140
|
+
color="secondary"
|
|
141
|
+
size="icon-md"
|
|
142
|
+
aria-label="Mais ações"
|
|
143
|
+
>
|
|
144
|
+
<MoreHorizontal />
|
|
145
|
+
</Button>
|
|
146
|
+
<Button
|
|
147
|
+
variant="filled"
|
|
148
|
+
color="primary"
|
|
149
|
+
size="md"
|
|
150
|
+
iconLeft={<UserPlus />}
|
|
151
|
+
>
|
|
152
|
+
Convidar consultor
|
|
153
|
+
</Button>
|
|
154
|
+
</>
|
|
155
|
+
}
|
|
156
|
+
/>
|
|
157
|
+
|
|
158
|
+
<DataList
|
|
159
|
+
fillHeight
|
|
160
|
+
className="flex-1 min-h-0"
|
|
161
|
+
title="Consultores"
|
|
162
|
+
items={items}
|
|
163
|
+
layout="hierarchical"
|
|
164
|
+
branchHighlight="none"
|
|
165
|
+
renderItem={renderConsultorCard}
|
|
166
|
+
searchable
|
|
167
|
+
searchPlaceholder="Buscar consultor..."
|
|
168
|
+
filterFields={FILTER_FIELDS}
|
|
169
|
+
views={VIEWS}
|
|
170
|
+
onRefresh={() => {}}
|
|
171
|
+
persistKey="mapa-de-rede"
|
|
172
|
+
onItemClick={(id) => setDetailId(id)}
|
|
173
|
+
getMenuItems={(item) => {
|
|
174
|
+
const c = item.data as Consultor;
|
|
175
|
+
return [
|
|
176
|
+
{
|
|
177
|
+
label: "Ver detalhe",
|
|
178
|
+
icon: <Eye />,
|
|
179
|
+
onClick: () => setDetailId(c.id),
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
label: "Editar",
|
|
183
|
+
icon: <Pencil />,
|
|
184
|
+
onClick: () => setDetailId(c.id),
|
|
185
|
+
},
|
|
186
|
+
{ separator: true },
|
|
187
|
+
{
|
|
188
|
+
label: "Remover da rede",
|
|
189
|
+
icon: <UserMinus />,
|
|
190
|
+
destructive: true,
|
|
191
|
+
onClick: () => setRemoving(c),
|
|
192
|
+
},
|
|
193
|
+
];
|
|
194
|
+
}}
|
|
195
|
+
moreActions={[
|
|
196
|
+
{ label: "Atualizar rede", icon: <RefreshCw />, onClick: () => {} },
|
|
197
|
+
{ label: "Exportar CSV", onClick: () => {} },
|
|
198
|
+
]}
|
|
199
|
+
/>
|
|
200
|
+
|
|
201
|
+
<ConsultorDetailPanel
|
|
202
|
+
consultor={detail}
|
|
203
|
+
onClose={() => setDetailId(null)}
|
|
204
|
+
onEdit={() => {}}
|
|
205
|
+
onRemove={(c) => {
|
|
206
|
+
setDetailId(null);
|
|
207
|
+
setRemoving(c);
|
|
208
|
+
}}
|
|
209
|
+
/>
|
|
210
|
+
|
|
211
|
+
<AlertModal
|
|
212
|
+
open={removing !== null}
|
|
213
|
+
onOpenChange={(open) => {
|
|
214
|
+
if (!open) setRemoving(null);
|
|
215
|
+
}}
|
|
216
|
+
tone="danger"
|
|
217
|
+
title={`Remover ${removing?.name ?? "consultor"} da rede?`}
|
|
218
|
+
description="O consultor e toda a sua subárvore deixam de aparecer no mapa. Esta ação é apenas demonstrativa neste exemplo."
|
|
219
|
+
confirmLabel="Remover"
|
|
220
|
+
cancelLabel="Cancelar"
|
|
221
|
+
onConfirm={() => setRemoving(null)}
|
|
222
|
+
/>
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export default MapaDeRedeScreen;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Fragment } from "react";
|
|
2
|
+
import { Chip } from "@/components/ui/Chip";
|
|
3
|
+
import { SectionCard } from "./section-card";
|
|
4
|
+
import type { Order, OrderActivity } from "../order.types";
|
|
5
|
+
|
|
6
|
+
export function ActivityTab({ order }: { order: Order }) {
|
|
7
|
+
// Agrupa eventos por dateGroup preservando a ordem do mock.
|
|
8
|
+
const groups: { date: string; items: OrderActivity[] }[] = [];
|
|
9
|
+
for (const ev of order.activity) {
|
|
10
|
+
const last = groups[groups.length - 1];
|
|
11
|
+
if (last && last.date === ev.dateGroup) last.items.push(ev);
|
|
12
|
+
else groups.push({ date: ev.dateGroup, items: [ev] });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<SectionCard
|
|
17
|
+
title="Atividade"
|
|
18
|
+
action={
|
|
19
|
+
<Chip color="info" variant="soft" size="sm" shape="pill">
|
|
20
|
+
Enviado
|
|
21
|
+
</Chip>
|
|
22
|
+
}
|
|
23
|
+
>
|
|
24
|
+
<div className="flex flex-col gap-gp-5xl">
|
|
25
|
+
{groups.map((g) => (
|
|
26
|
+
<div key={g.date}>
|
|
27
|
+
<p className="mb-gp-lg text-caption-sm font-semibold uppercase tracking-wider text-fg-muted">
|
|
28
|
+
{g.date}
|
|
29
|
+
</p>
|
|
30
|
+
<ol className="relative flex flex-col">
|
|
31
|
+
{g.items.map((ev, i) => (
|
|
32
|
+
<Fragment key={ev.id}>
|
|
33
|
+
<li className="relative flex gap-gp-lg pb-pad-3xl last:pb-0">
|
|
34
|
+
{/* trilha + dot */}
|
|
35
|
+
<div className="relative flex w-3 shrink-0 justify-center">
|
|
36
|
+
{i < g.items.length - 1 && (
|
|
37
|
+
<span
|
|
38
|
+
className="absolute top-3 h-full w-px bg-border-default"
|
|
39
|
+
aria-hidden="true"
|
|
40
|
+
/>
|
|
41
|
+
)}
|
|
42
|
+
<span
|
|
43
|
+
className={
|
|
44
|
+
"relative z-10 mt-1 size-2.5 rounded-full " +
|
|
45
|
+
(ev.done ? "bg-bg-success" : "bg-bg-brand")
|
|
46
|
+
}
|
|
47
|
+
aria-hidden="true"
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
<div className="min-w-0 flex-1">
|
|
51
|
+
<p
|
|
52
|
+
className={
|
|
53
|
+
"text-body-sm font-semibold " +
|
|
54
|
+
(ev.done ? "text-fg-success" : "text-fg-default")
|
|
55
|
+
}
|
|
56
|
+
>
|
|
57
|
+
{ev.title}
|
|
58
|
+
</p>
|
|
59
|
+
{ev.description && (
|
|
60
|
+
<p className="text-caption-sm text-fg-muted">
|
|
61
|
+
{ev.description}
|
|
62
|
+
</p>
|
|
63
|
+
)}
|
|
64
|
+
<p className="mt-gp-2xs text-caption-sm text-fg-subtle [font-variant-numeric:tabular-nums]">
|
|
65
|
+
{ev.time}
|
|
66
|
+
</p>
|
|
67
|
+
</div>
|
|
68
|
+
</li>
|
|
69
|
+
</Fragment>
|
|
70
|
+
))}
|
|
71
|
+
</ol>
|
|
72
|
+
</div>
|
|
73
|
+
))}
|
|
74
|
+
</div>
|
|
75
|
+
</SectionCard>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Download, FileText, ImageIcon } from "lucide-react";
|
|
2
|
+
import { Button } from "@/components/ui/Button/button";
|
|
3
|
+
import { SectionCard } from "./section-card";
|
|
4
|
+
import type { Order } from "../order.types";
|
|
5
|
+
|
|
6
|
+
export function AttachmentsTab({ order }: { order: Order }) {
|
|
7
|
+
return (
|
|
8
|
+
<SectionCard title="Anexos">
|
|
9
|
+
<ul className="flex flex-col">
|
|
10
|
+
{order.attachments.map((f, i) => {
|
|
11
|
+
const Icon = f.kind === "image" ? ImageIcon : FileText;
|
|
12
|
+
return (
|
|
13
|
+
<li
|
|
14
|
+
key={f.id}
|
|
15
|
+
className={
|
|
16
|
+
"flex items-center gap-gp-md py-pad-lg" +
|
|
17
|
+
(i > 0 ? " border-t border-border-subtle" : "")
|
|
18
|
+
}
|
|
19
|
+
>
|
|
20
|
+
<span className="grid size-9 shrink-0 place-items-center rounded-radius-md bg-bg-muted text-fg-muted">
|
|
21
|
+
<Icon className="size-icon-sm" />
|
|
22
|
+
</span>
|
|
23
|
+
<div className="min-w-0 flex-1">
|
|
24
|
+
<p className="truncate text-body-sm font-semibold text-fg-default">
|
|
25
|
+
{f.name}
|
|
26
|
+
</p>
|
|
27
|
+
<p className="text-caption-sm uppercase tracking-wider text-fg-muted">
|
|
28
|
+
{f.kind} · {f.size}
|
|
29
|
+
</p>
|
|
30
|
+
</div>
|
|
31
|
+
<Button
|
|
32
|
+
variant="ghost"
|
|
33
|
+
color="secondary"
|
|
34
|
+
size="sm"
|
|
35
|
+
iconLeft={<Download />}
|
|
36
|
+
>
|
|
37
|
+
Baixar
|
|
38
|
+
</Button>
|
|
39
|
+
</li>
|
|
40
|
+
);
|
|
41
|
+
})}
|
|
42
|
+
</ul>
|
|
43
|
+
</SectionCard>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Send } from "lucide-react";
|
|
3
|
+
import { Avatar } from "@/components/ui/avatar-ig";
|
|
4
|
+
import { Button } from "@/components/ui/Button/button";
|
|
5
|
+
import { Textarea } from "@/components/shadcn/textarea";
|
|
6
|
+
import { SectionCard } from "./section-card";
|
|
7
|
+
import type { Order, OrderComment } from "../order.types";
|
|
8
|
+
|
|
9
|
+
export function CommentsTab({ order }: { order: Order }) {
|
|
10
|
+
const [comments, setComments] = useState<OrderComment[]>(order.comments);
|
|
11
|
+
const [draft, setDraft] = useState("");
|
|
12
|
+
|
|
13
|
+
const submit = () => {
|
|
14
|
+
const text = draft.trim();
|
|
15
|
+
if (!text) return;
|
|
16
|
+
setComments((prev) => [
|
|
17
|
+
...prev,
|
|
18
|
+
{
|
|
19
|
+
id: `local-${prev.length + 1}`,
|
|
20
|
+
author: "Suporte iGreen",
|
|
21
|
+
initials: "SI",
|
|
22
|
+
colorHex: "#3B82F6",
|
|
23
|
+
when: "agora",
|
|
24
|
+
text,
|
|
25
|
+
},
|
|
26
|
+
]);
|
|
27
|
+
setDraft("");
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className="flex flex-col gap-gp-xl">
|
|
32
|
+
{/* Card só com a thread */}
|
|
33
|
+
<SectionCard
|
|
34
|
+
title="Comentários"
|
|
35
|
+
action={
|
|
36
|
+
<span className="rounded-radius-full bg-bg-muted px-pad-md py-pad-2xs text-caption-sm font-semibold text-fg-muted">
|
|
37
|
+
{comments.length}
|
|
38
|
+
</span>
|
|
39
|
+
}
|
|
40
|
+
>
|
|
41
|
+
<ul className="flex flex-col gap-gp-4xl">
|
|
42
|
+
{comments.map((cm) => (
|
|
43
|
+
<li key={cm.id} className="flex gap-gp-md">
|
|
44
|
+
<Avatar size="md" colorHex={cm.colorHex} aria-label={cm.author}>
|
|
45
|
+
{cm.initials}
|
|
46
|
+
</Avatar>
|
|
47
|
+
<div className="min-w-0 flex-1">
|
|
48
|
+
<div className="rounded-radius-lg border border-border-subtle bg-bg-canvas px-pad-lg py-pad-md">
|
|
49
|
+
<div className="flex items-center justify-between gap-gp-sm">
|
|
50
|
+
<span className="text-body-sm font-semibold text-fg-default">
|
|
51
|
+
{cm.author}
|
|
52
|
+
</span>
|
|
53
|
+
<span className="text-caption-sm text-fg-subtle">
|
|
54
|
+
{cm.when}
|
|
55
|
+
</span>
|
|
56
|
+
</div>
|
|
57
|
+
<p className="mt-gp-xs text-body-sm leading-relaxed text-fg-muted">
|
|
58
|
+
{cm.text}
|
|
59
|
+
</p>
|
|
60
|
+
</div>
|
|
61
|
+
<div className="mt-gp-2xs flex items-center gap-gp-lg pl-pad-sm">
|
|
62
|
+
<button
|
|
63
|
+
type="button"
|
|
64
|
+
className="text-caption-sm font-medium text-fg-muted transition-colors hover:text-fg-brand"
|
|
65
|
+
>
|
|
66
|
+
Responder
|
|
67
|
+
</button>
|
|
68
|
+
<button
|
|
69
|
+
type="button"
|
|
70
|
+
className="text-caption-sm font-medium text-fg-muted transition-colors hover:text-fg-brand"
|
|
71
|
+
>
|
|
72
|
+
Curtir
|
|
73
|
+
</button>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</li>
|
|
77
|
+
))}
|
|
78
|
+
</ul>
|
|
79
|
+
</SectionCard>
|
|
80
|
+
|
|
81
|
+
{/* Composer — embaixo e fora do card; avatar no footer junto da legenda */}
|
|
82
|
+
<div className="overflow-hidden rounded-radius-lg border border-border-default bg-bg-surface transition-[border-color,box-shadow] focus-within:border-border-brand focus-within:shadow-sh-ring">
|
|
83
|
+
<Textarea
|
|
84
|
+
value={draft}
|
|
85
|
+
onChange={(e) => setDraft(e.target.value)}
|
|
86
|
+
placeholder="Escreva um comentário..."
|
|
87
|
+
rows={3}
|
|
88
|
+
className="resize-none border-0 bg-transparent px-pad-lg py-pad-md shadow-none focus-visible:ring-0"
|
|
89
|
+
/>
|
|
90
|
+
<div className="flex items-center justify-between gap-gp-md border-t border-border-subtle px-pad-lg py-pad-lg">
|
|
91
|
+
<span className="flex items-center gap-gp-sm text-caption-sm text-fg-subtle">
|
|
92
|
+
<Avatar size="xs" colorHex="#3B82F6" aria-label="Você">
|
|
93
|
+
SI
|
|
94
|
+
</Avatar>
|
|
95
|
+
Visível para a equipe e o cliente.
|
|
96
|
+
</span>
|
|
97
|
+
<Button
|
|
98
|
+
variant="filled"
|
|
99
|
+
color="primary"
|
|
100
|
+
size="sm"
|
|
101
|
+
iconLeft={<Send />}
|
|
102
|
+
disabled={!draft.trim()}
|
|
103
|
+
onClick={submit}
|
|
104
|
+
>
|
|
105
|
+
Comentar
|
|
106
|
+
</Button>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CreditCard,
|
|
3
|
+
FileText,
|
|
4
|
+
Percent,
|
|
5
|
+
StickyNote,
|
|
6
|
+
Tag,
|
|
7
|
+
Truck,
|
|
8
|
+
User,
|
|
9
|
+
} from "lucide-react";
|
|
10
|
+
import { Avatar } from "@/components/ui/avatar-ig";
|
|
11
|
+
import { Chip } from "@/components/ui/Chip";
|
|
12
|
+
import { SectionCard, SectionDivider, Field } from "./section-card";
|
|
13
|
+
import { formatBRL } from "../order-mocks";
|
|
14
|
+
import type { Order } from "../order.types";
|
|
15
|
+
|
|
16
|
+
export function DetailsTab({ order }: { order: Order }) {
|
|
17
|
+
const c = order.customer;
|
|
18
|
+
const a = order.shippingAddress;
|
|
19
|
+
const b = order.billing;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="flex flex-col gap-gp-2xl">
|
|
23
|
+
{/* Tags rápidas no topo */}
|
|
24
|
+
<div className="flex flex-wrap items-center gap-gp-sm">
|
|
25
|
+
{order.tags.map((t) => (
|
|
26
|
+
<Chip key={t.label} color={t.color} variant="soft" size="sm" shape="pill">
|
|
27
|
+
{t.label}
|
|
28
|
+
</Chip>
|
|
29
|
+
))}
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div className="grid grid-cols-1 gap-gp-2xl lg:grid-cols-3">
|
|
33
|
+
{/* Coluna principal */}
|
|
34
|
+
<div className="flex flex-col gap-gp-2xl lg:col-span-2">
|
|
35
|
+
<SectionCard
|
|
36
|
+
title="Informações do cliente"
|
|
37
|
+
icon={<User className="size-icon-md" />}
|
|
38
|
+
>
|
|
39
|
+
<div className="flex items-start gap-gp-lg">
|
|
40
|
+
<Avatar size="xl" colorHex={c.colorHex} aria-label={`${c.firstName} ${c.lastName}`}>
|
|
41
|
+
{c.initials}
|
|
42
|
+
</Avatar>
|
|
43
|
+
<div className="grid flex-1 grid-cols-1 gap-x-gp-2xl gap-y-gp-2xl sm:grid-cols-3">
|
|
44
|
+
<Field label="Nome" value={c.firstName} />
|
|
45
|
+
<Field label="Nome do meio" value={c.middleName ?? "—"} />
|
|
46
|
+
<Field label="Sobrenome" value={c.lastName} />
|
|
47
|
+
<Field label="E-mail" value={c.email} accent />
|
|
48
|
+
<Field label="Telefone" value={c.phone} />
|
|
49
|
+
<Field label="Tipo de conta" value={c.accountType} />
|
|
50
|
+
<Field label="ID do cliente" value={c.customerId} />
|
|
51
|
+
<Field label="Cadastro" value={c.registeredAt} />
|
|
52
|
+
<Field
|
|
53
|
+
label="Status"
|
|
54
|
+
value={
|
|
55
|
+
<span className="text-fg-success">
|
|
56
|
+
{c.active ? "Ativo" : "Inativo"}
|
|
57
|
+
</span>
|
|
58
|
+
}
|
|
59
|
+
/>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</SectionCard>
|
|
63
|
+
|
|
64
|
+
<SectionCard
|
|
65
|
+
title="Informações de entrega"
|
|
66
|
+
icon={<Truck className="size-icon-md" />}
|
|
67
|
+
>
|
|
68
|
+
<div className="grid grid-cols-1 gap-x-gp-2xl gap-y-gp-2xl sm:grid-cols-3">
|
|
69
|
+
<Field label="Endereço linha 1" value={a.line1} />
|
|
70
|
+
<Field label="Endereço linha 2" value={a.line2 ?? "—"} />
|
|
71
|
+
<Field label="Cidade" value={a.city} />
|
|
72
|
+
<Field label="Estado" value={a.state} />
|
|
73
|
+
<Field label="CEP" value={a.zip} />
|
|
74
|
+
<Field label="País" value={a.country} />
|
|
75
|
+
</div>
|
|
76
|
+
<SectionDivider className="my-gp-2xl" />
|
|
77
|
+
<div className="grid grid-cols-1 gap-x-gp-2xl gap-y-gp-2xl sm:grid-cols-3">
|
|
78
|
+
<Field label="Transportadora" value={a.carrier} />
|
|
79
|
+
<Field label="Rastreamento" value={a.tracking} accent />
|
|
80
|
+
<Field label="Previsão de entrega" value={a.eta} />
|
|
81
|
+
</div>
|
|
82
|
+
</SectionCard>
|
|
83
|
+
|
|
84
|
+
<SectionCard
|
|
85
|
+
title="Informações de cobrança"
|
|
86
|
+
icon={<CreditCard className="size-icon-md" />}
|
|
87
|
+
>
|
|
88
|
+
<div className="grid grid-cols-1 gap-x-gp-2xl gap-y-gp-2xl sm:grid-cols-3">
|
|
89
|
+
<Field label="Endereço de cobrança" value={b.address} />
|
|
90
|
+
<Field label="Cidade / Estado" value={b.cityState} />
|
|
91
|
+
<Field label="País" value={b.country} />
|
|
92
|
+
</div>
|
|
93
|
+
<SectionDivider className="my-gp-2xl" />
|
|
94
|
+
<div className="grid grid-cols-1 gap-x-gp-2xl gap-y-gp-2xl sm:grid-cols-3">
|
|
95
|
+
<Field label="Forma de pagamento" value={b.method} />
|
|
96
|
+
<Field label="ID da transação" value={b.transactionId} accent />
|
|
97
|
+
<Field label="Data do pagamento" value={b.paymentDate} />
|
|
98
|
+
</div>
|
|
99
|
+
</SectionCard>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
{/* Coluna lateral */}
|
|
103
|
+
<div className="flex flex-col gap-gp-2xl">
|
|
104
|
+
<SectionCard
|
|
105
|
+
title="Resumo do pedido"
|
|
106
|
+
icon={<FileText className="size-icon-md" />}
|
|
107
|
+
>
|
|
108
|
+
<div className="grid grid-cols-2 gap-x-gp-2xl gap-y-gp-2xl">
|
|
109
|
+
<Field label="ID do pedido" value={`#${order.id}`} />
|
|
110
|
+
<Field label="Data" value="8 jan 2024" />
|
|
111
|
+
<Field label="Itens" value={`${order.items.length} produtos`} />
|
|
112
|
+
<Field
|
|
113
|
+
label="Total"
|
|
114
|
+
value={
|
|
115
|
+
<span className="text-fg-success [font-variant-numeric:tabular-nums]">
|
|
116
|
+
{formatBRL(order.total)}
|
|
117
|
+
</span>
|
|
118
|
+
}
|
|
119
|
+
/>
|
|
120
|
+
<Field
|
|
121
|
+
label="Pagamento"
|
|
122
|
+
value={
|
|
123
|
+
<Chip color="success" variant="soft" size="sm" shape="pill">
|
|
124
|
+
Pago
|
|
125
|
+
</Chip>
|
|
126
|
+
}
|
|
127
|
+
/>
|
|
128
|
+
<Field
|
|
129
|
+
label="Fulfillment"
|
|
130
|
+
value={
|
|
131
|
+
<Chip color="info" variant="soft" size="sm" shape="pill">
|
|
132
|
+
Enviado
|
|
133
|
+
</Chip>
|
|
134
|
+
}
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
137
|
+
</SectionCard>
|
|
138
|
+
|
|
139
|
+
<SectionCard
|
|
140
|
+
title="Notas internas"
|
|
141
|
+
icon={<StickyNote className="size-icon-md" />}
|
|
142
|
+
>
|
|
143
|
+
<p className="text-body-sm leading-relaxed text-fg-muted">
|
|
144
|
+
{order.internalNote}
|
|
145
|
+
</p>
|
|
146
|
+
</SectionCard>
|
|
147
|
+
|
|
148
|
+
<SectionCard
|
|
149
|
+
title="Tags e rótulos"
|
|
150
|
+
icon={<Tag className="size-icon-md" />}
|
|
151
|
+
>
|
|
152
|
+
<div className="flex flex-wrap gap-gp-sm">
|
|
153
|
+
{order.tags.map((t) => (
|
|
154
|
+
<Chip
|
|
155
|
+
key={t.label}
|
|
156
|
+
color={t.color}
|
|
157
|
+
variant="soft"
|
|
158
|
+
size="sm"
|
|
159
|
+
shape="pill"
|
|
160
|
+
>
|
|
161
|
+
{t.label}
|
|
162
|
+
</Chip>
|
|
163
|
+
))}
|
|
164
|
+
</div>
|
|
165
|
+
</SectionCard>
|
|
166
|
+
|
|
167
|
+
{order.discountValue != null && (
|
|
168
|
+
<SectionCard
|
|
169
|
+
title="Desconto aplicado"
|
|
170
|
+
icon={<Percent className="size-icon-md" />}
|
|
171
|
+
>
|
|
172
|
+
<div className="grid grid-cols-2 gap-x-gp-2xl gap-y-gp-2xl">
|
|
173
|
+
<Field label="Cupom" value={order.discountCode} />
|
|
174
|
+
<Field
|
|
175
|
+
label="Desconto"
|
|
176
|
+
value={
|
|
177
|
+
<span className="text-fg-success">
|
|
178
|
+
{order.discountLabel} ({formatBRL(order.discountValue)})
|
|
179
|
+
</span>
|
|
180
|
+
}
|
|
181
|
+
/>
|
|
182
|
+
</div>
|
|
183
|
+
</SectionCard>
|
|
184
|
+
)}
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
);
|
|
189
|
+
}
|