@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.
Files changed (149) hide show
  1. package/dist-lib/ai/blocos/chart/budget-breakdown.tsx +174 -0
  2. package/dist-lib/ai/blocos/indice.md +43 -0
  3. package/dist-lib/ai/blocos/paneldetail/detalhe-com-tabela.tsx +450 -0
  4. package/dist-lib/ai/blocos/paneldetail/detalhe-de-tarefa-com-abas.tsx +570 -0
  5. package/dist-lib/ai/blocos/paneldetail/detalhe-do-registro.tsx +555 -0
  6. package/dist-lib/ai/componentes/DataTable.md +2 -1
  7. package/dist-lib/ai/componentes/TableToolbar.md +1 -1
  8. package/dist-lib/ai/exemplos/app-shell/app-shell-example.tsx +140 -0
  9. package/dist-lib/ai/exemplos/app-shell/index.ts +3 -0
  10. package/dist-lib/ai/exemplos/app-shell/nav-data.ts +97 -0
  11. package/dist-lib/ai/exemplos/app-shell/routes.tsx +75 -0
  12. package/dist-lib/ai/exemplos/chat/chat-screen.tsx +152 -0
  13. package/dist-lib/ai/exemplos/chat/chat-v2-mocks.ts +171 -0
  14. package/dist-lib/ai/exemplos/chat/chat-v2.styles.ts +23 -0
  15. package/dist-lib/ai/exemplos/chat/chat-v2.types.ts +81 -0
  16. package/dist-lib/ai/exemplos/chat/components/ChannelDot/channel-dot.tsx +28 -0
  17. package/dist-lib/ai/exemplos/chat/components/ConversationActionsMenu/conversation-actions-menu.tsx +83 -0
  18. package/dist-lib/ai/exemplos/chat/components/ConversationActionsMenu/index.ts +4 -0
  19. package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.styles.ts +22 -0
  20. package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.tsx +203 -0
  21. package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.types.ts +10 -0
  22. package/dist-lib/ai/exemplos/chat/components/ConversationColumn/index.ts +2 -0
  23. package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.styles.ts +55 -0
  24. package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.tsx +52 -0
  25. package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.types.ts +7 -0
  26. package/dist-lib/ai/exemplos/chat/components/ConversationListItem/index.ts +2 -0
  27. package/dist-lib/ai/exemplos/chat/components/DateSeparator/date-separator.tsx +18 -0
  28. package/dist-lib/ai/exemplos/chat/components/DetailField/detail-field.tsx +23 -0
  29. package/dist-lib/ai/exemplos/chat/components/DetailSection/detail-section.tsx +41 -0
  30. package/dist-lib/ai/exemplos/chat/components/DetailSection/index.ts +1 -0
  31. package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.styles.ts +30 -0
  32. package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.tsx +142 -0
  33. package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.types.ts +16 -0
  34. package/dist-lib/ai/exemplos/chat/components/DetailsColumn/index.ts +5 -0
  35. package/dist-lib/ai/exemplos/chat/components/FilterRow/filter-row.styles.ts +37 -0
  36. package/dist-lib/ai/exemplos/chat/components/FilterRow/filter-row.tsx +45 -0
  37. package/dist-lib/ai/exemplos/chat/components/FilterRow/index.ts +1 -0
  38. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.styles.ts +27 -0
  39. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.tsx +66 -0
  40. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.types.ts +13 -0
  41. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-rail.tsx +38 -0
  42. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/index.ts +6 -0
  43. package/dist-lib/ai/exemplos/chat/components/MessageBubble/index.ts +2 -0
  44. package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.styles.ts +37 -0
  45. package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.tsx +29 -0
  46. package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.types.ts +5 -0
  47. package/dist-lib/ai/exemplos/chat/components/PersonAvatar/index.ts +5 -0
  48. package/dist-lib/ai/exemplos/chat/components/PersonAvatar/person-avatar.tsx +31 -0
  49. package/dist-lib/ai/exemplos/chat/components/QueueColumn/index.ts +2 -0
  50. package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.styles.ts +19 -0
  51. package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.tsx +140 -0
  52. package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.types.ts +13 -0
  53. package/dist-lib/ai/exemplos/chat/components/RailItem/index.ts +1 -0
  54. package/dist-lib/ai/exemplos/chat/components/RailItem/rail-item.styles.ts +30 -0
  55. package/dist-lib/ai/exemplos/chat/components/RailItem/rail-item.tsx +42 -0
  56. package/dist-lib/ai/exemplos/chat/hooks/use-resizable.ts +98 -0
  57. package/dist-lib/ai/exemplos/chat/index.ts +1 -0
  58. package/dist-lib/ai/exemplos/clientes/_table-data.ts +59 -0
  59. package/dist-lib/ai/exemplos/clientes/clientes-screen.tsx +505 -0
  60. package/dist-lib/ai/exemplos/clientes/clientes-showcase-mocks.ts +117 -0
  61. package/dist-lib/ai/exemplos/clientes/clientes-showcase.styles.ts +13 -0
  62. package/dist-lib/ai/exemplos/clientes/clientes-showcase.types.ts +16 -0
  63. package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.styles.ts +28 -0
  64. package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.tsx +303 -0
  65. package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.types.ts +14 -0
  66. package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/index.ts +2 -0
  67. package/dist-lib/ai/exemplos/clientes/components/NovoClienteDrawer/index.ts +2 -0
  68. package/dist-lib/ai/exemplos/clientes/components/NovoClienteDrawer/novo-cliente-drawer.tsx +207 -0
  69. package/dist-lib/ai/exemplos/clientes/index.ts +1 -0
  70. package/dist-lib/ai/exemplos/edit-page/components/StepNav.tsx +81 -0
  71. package/dist-lib/ai/exemplos/edit-page/components/section-card.tsx +85 -0
  72. package/dist-lib/ai/exemplos/edit-page/edit-page-screen.tsx +234 -0
  73. package/dist-lib/ai/exemplos/edit-page/index.ts +1 -0
  74. package/dist-lib/ai/exemplos/finance/_table-data.ts +57 -0
  75. package/dist-lib/ai/exemplos/finance/clientes-financeiro-mocks.ts +227 -0
  76. package/dist-lib/ai/exemplos/finance/clientes-financeiro.types.ts +75 -0
  77. package/dist-lib/ai/exemplos/finance/clientes-showcase-mocks.ts +117 -0
  78. package/dist-lib/ai/exemplos/finance/clientes-showcase.styles.ts +13 -0
  79. package/dist-lib/ai/exemplos/finance/clientes-showcase.types.ts +16 -0
  80. package/dist-lib/ai/exemplos/finance/components/EditarFinanceDrawer/editar-finance-drawer.tsx +241 -0
  81. package/dist-lib/ai/exemplos/finance/components/EditarFinanceDrawer/index.ts +5 -0
  82. package/dist-lib/ai/exemplos/finance/components/ExtratoExpansion/extrato-expansion.tsx +172 -0
  83. package/dist-lib/ai/exemplos/finance/components/ExtratoExpansion/index.ts +1 -0
  84. package/dist-lib/ai/exemplos/finance/components/FinanceDetailPanel/finance-detail-panel.tsx +241 -0
  85. package/dist-lib/ai/exemplos/finance/components/FinanceDetailPanel/index.ts +2 -0
  86. package/dist-lib/ai/exemplos/finance/components/NovoClienteDrawer/index.ts +2 -0
  87. package/dist-lib/ai/exemplos/finance/components/NovoClienteDrawer/novo-cliente-drawer.tsx +207 -0
  88. package/dist-lib/ai/exemplos/finance/components/SacarDialog/index.ts +2 -0
  89. package/dist-lib/ai/exemplos/finance/components/SacarDialog/sacar-dialog.tsx +346 -0
  90. package/dist-lib/ai/exemplos/finance/finance-screen.tsx +821 -0
  91. package/dist-lib/ai/exemplos/finance/index.ts +1 -0
  92. package/dist-lib/ai/exemplos/gantt/_gantt-data.tsx +402 -0
  93. package/dist-lib/ai/exemplos/gantt/gantt-screen.tsx +512 -0
  94. package/dist-lib/ai/exemplos/gantt/index.ts +1 -0
  95. package/dist-lib/ai/exemplos/login/index.ts +1 -0
  96. package/dist-lib/ai/exemplos/login/login-screen.tsx +246 -0
  97. package/dist-lib/ai/exemplos/mapa-rede/components/ConsultorDetailPanel/consultor-detail-panel.tsx +158 -0
  98. package/dist-lib/ai/exemplos/mapa-rede/components/ConsultorDetailPanel/index.ts +2 -0
  99. package/dist-lib/ai/exemplos/mapa-rede/index.ts +1 -0
  100. package/dist-lib/ai/exemplos/mapa-rede/mapa-de-rede-mocks.ts +655 -0
  101. package/dist-lib/ai/exemplos/mapa-rede/mapa-de-rede.types.ts +53 -0
  102. package/dist-lib/ai/exemplos/mapa-rede/mapa-rede-screen.tsx +227 -0
  103. package/dist-lib/ai/exemplos/order-detail/components/ActivityTab.tsx +77 -0
  104. package/dist-lib/ai/exemplos/order-detail/components/AttachmentsTab.tsx +45 -0
  105. package/dist-lib/ai/exemplos/order-detail/components/CommentsTab.tsx +111 -0
  106. package/dist-lib/ai/exemplos/order-detail/components/DetailsTab.tsx +189 -0
  107. package/dist-lib/ai/exemplos/order-detail/components/OverviewTab.tsx +200 -0
  108. package/dist-lib/ai/exemplos/order-detail/components/section-card.tsx +85 -0
  109. package/dist-lib/ai/exemplos/order-detail/index.ts +1 -0
  110. package/dist-lib/ai/exemplos/order-detail/order-detail-screen.tsx +119 -0
  111. package/dist-lib/ai/exemplos/order-detail/order-mocks.ts +186 -0
  112. package/dist-lib/ai/exemplos/order-detail/order.types.ts +118 -0
  113. package/dist-lib/ai/global/componentes.md +7 -0
  114. package/dist-lib/ai/indice.json +30 -15
  115. package/dist-lib/ai/manifest.json +32 -6
  116. package/dist-lib/ai/roteiros/app-builder/roteiro.md +155 -0
  117. package/dist-lib/ai/roteiros/auth-builder/roteiro.md +42 -0
  118. package/dist-lib/ai/roteiros/cards/roteiro.md +34 -0
  119. package/dist-lib/ai/roteiros/charts/roteiro.md +32 -0
  120. package/dist-lib/ai/roteiros/chat/roteiro.md +31 -0
  121. package/dist-lib/ai/roteiros/crud-builder/blueprint.md +63 -0
  122. package/dist-lib/ai/roteiros/crud-builder/entrevista.md +146 -0
  123. package/dist-lib/ai/roteiros/crud-builder/geracao.md +113 -0
  124. package/dist-lib/ai/roteiros/crud-builder/roteiro.md +89 -0
  125. package/dist-lib/ai/roteiros/drawers/roteiro.md +41 -0
  126. package/dist-lib/ai/roteiros/list-builder/blueprint.md +94 -0
  127. package/dist-lib/ai/roteiros/list-builder/entrevista.md +193 -0
  128. package/dist-lib/ai/roteiros/list-builder/geracao.md +63 -0
  129. package/dist-lib/ai/roteiros/list-builder/roteiro.md +91 -0
  130. package/dist-lib/ai/roteiros/module-replicator/roteiro.md +56 -0
  131. package/dist-lib/ai/roteiros/page-detail/roteiro.md +32 -0
  132. package/dist-lib/ai/roteiros/page-edit/roteiro.md +31 -0
  133. package/dist-lib/ai/roteiros/screen-composer/roteiro.md +95 -0
  134. package/dist-lib/chunks/{panel-B-liPlxr.mjs → panel-CN0ChoHM.mjs} +5 -2
  135. package/dist-lib/chunks/{panel-B-liPlxr.mjs.map → panel-CN0ChoHM.mjs.map} +1 -1
  136. package/dist-lib/chunks/{panel-DoNbjeqX.cjs → panel-Cpq1xHck.cjs} +5 -2
  137. package/dist-lib/chunks/{panel-DoNbjeqX.cjs.map → panel-Cpq1xHck.cjs.map} +1 -1
  138. package/dist-lib/index.cjs +1 -1
  139. package/dist-lib/index.mjs +2 -2
  140. package/dist-lib/preview/clientes.cjs +1 -1
  141. package/dist-lib/preview/clientes.mjs +1 -1
  142. package/dist-lib/src/components/ui/DataTable/data-table.d.ts.map +1 -1
  143. package/dist-lib/src/components/ui/DataTable/data-table.types.d.ts +20 -2
  144. package/dist-lib/src/components/ui/DataTable/data-table.types.d.ts.map +1 -1
  145. package/package.json +1 -1
  146. /package/dist-lib/ai/roteiros/{dashboard → dashboard-builder}/blueprint.md +0 -0
  147. /package/dist-lib/ai/roteiros/{dashboard → dashboard-builder}/entrevista.md +0 -0
  148. /package/dist-lib/ai/roteiros/{dashboard → dashboard-builder}/geracao.md +0 -0
  149. /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
+ }