@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,117 @@
1
+ import {
2
+ CLIENTS_MOCK,
3
+ STATUSES,
4
+ CATEGORIES,
5
+ AGENTS,
6
+ type ClientRow,
7
+ } from "./_table-data";
8
+
9
+ /* ── Dataset 87 clientes (bate com badge do print) ─────────────── */
10
+
11
+ export const CLIENTS_87: ClientRow[] = Array.from({ length: 9 }, (_, batch) =>
12
+ CLIENTS_MOCK.map((row, i) => ({
13
+ ...row,
14
+ id: `CLI-${2400 + batch * 10 + i}`,
15
+ })),
16
+ )
17
+ .flat()
18
+ .slice(0, 87);
19
+
20
+ /* ── Lookups por categoria de status (column color mapping) ────── */
21
+
22
+ export const STATUS_COLOR_MAP: Record<string, string> = {
23
+ active: "success",
24
+ pending: "warning",
25
+ paused: "info",
26
+ inactive: "neutral",
27
+ };
28
+
29
+ export const STATUS_DOT: Record<string, string> = {
30
+ active: "var(--color-fg-success)",
31
+ pending: "var(--color-fg-warning)",
32
+ paused: "var(--color-fg-info)",
33
+ inactive: "var(--color-fg-muted)",
34
+ };
35
+
36
+ export const CATEGORY_KIND: Record<string, "warning" | "info" | "success"> = {
37
+ royal: "warning",
38
+ licenciado: "info",
39
+ lead: "success",
40
+ };
41
+
42
+ /* ── Options pra filters/selects ───────────────────────────────── */
43
+
44
+ export const STATUS_OPTIONS = Object.entries(STATUSES).map(([v, m]) => ({
45
+ value: v,
46
+ label: m.label,
47
+ color: STATUS_COLOR_MAP[v] ?? "neutral",
48
+ }));
49
+
50
+ export const CATEGORY_OPTIONS = Object.entries(CATEGORIES).map(([v, m]) => ({
51
+ value: v,
52
+ label: m.label,
53
+ color: m.kind === "neutral" ? "neutral" : m.kind,
54
+ }));
55
+
56
+ export const AGENT_OPTIONS = Object.entries(AGENTS).map(([v, a]) => ({
57
+ value: v,
58
+ label: a.name,
59
+ }));
60
+
61
+ /* ── Avatar lookup pro column type "user" ──────────────────────── */
62
+
63
+ export const AGENTS_LOOKUP = Object.fromEntries(
64
+ Object.entries(AGENTS).map(([id, a]) => [
65
+ id,
66
+ {
67
+ name: a.name,
68
+ initials: a.name
69
+ .split(" ")
70
+ .map((s) => s[0])
71
+ .join("")
72
+ .slice(0, 2),
73
+ color: a.color,
74
+ },
75
+ ]),
76
+ );
77
+
78
+ /* ── Kanban columns (1 por status) ─────────────────────────────── */
79
+
80
+ export const KANBAN_COLUMNS = Object.entries(STATUSES).map(([id, s]) => ({
81
+ id,
82
+ label: s.label,
83
+ dotColor: STATUS_DOT[id] ?? "var(--color-fg-muted)",
84
+ }));
85
+
86
+ /* ── Cidades pro select do drawer ──────────────────────────────── */
87
+
88
+ export const CITIES = [
89
+ "São Paulo, SP",
90
+ "Rio de Janeiro, RJ",
91
+ "Belo Horizonte, MG",
92
+ "Porto Alegre, RS",
93
+ "Curitiba, PR",
94
+ "Recife, PE",
95
+ "Salvador, BA",
96
+ "Fortaleza, CE",
97
+ "Brasília, DF",
98
+ "Manaus, AM",
99
+ ];
100
+
101
+ /* ── Helpers de formatação ─────────────────────────────────────── */
102
+
103
+ export function formatBRL(value: number) {
104
+ return value.toLocaleString("pt-BR", {
105
+ style: "currency",
106
+ currency: "BRL",
107
+ minimumFractionDigits: 2,
108
+ });
109
+ }
110
+
111
+ export function formatShortDate(ms: number) {
112
+ if (!ms) return "—";
113
+ const d = new Date(ms);
114
+ const day = d.getDate().toString().padStart(2, "0");
115
+ const months = ["jan", "fev", "mar", "abr", "mai", "jun", "jul", "ago", "set", "out", "nov", "dez"];
116
+ return `${day} ${months[d.getMonth()]}`;
117
+ }
@@ -0,0 +1,13 @@
1
+ import { tv } from "@/utils/tv";
2
+
3
+ /**
4
+ * Layout em grid de 2 colunas usado dentro do drawer pra pares de campos
5
+ * (Email + WhatsApp, Cidade + Valor).
6
+ * `gap-form-gap` (20px) = token DS pra spacing entre FormField units (L-024).
7
+ */
8
+ export const drawerFormStyles = tv({
9
+ slots: {
10
+ twoCols: "grid grid-cols-2 gap-form-gap",
11
+ statusDot: "inline-block size-[8px] rounded-radius-full mr-gp-xs shrink-0",
12
+ },
13
+ });
@@ -0,0 +1,16 @@
1
+ import type { STATUSES, CATEGORIES } from "./_table-data";
2
+
3
+ /**
4
+ * Payload do formulário do drawer "Novo cliente".
5
+ * Consumido por <NovoClienteDrawer onSubmit={...}> no main page.
6
+ */
7
+ export type NovoClienteFormData = {
8
+ name: string;
9
+ email: string;
10
+ whatsapp: string;
11
+ statusId: keyof typeof STATUSES;
12
+ categoryId: keyof typeof CATEGORIES;
13
+ city: string;
14
+ value: number;
15
+ notes: string;
16
+ };
@@ -0,0 +1,241 @@
1
+ import { useEffect, useState } from "react";
2
+ import { Panel } from "@/components/ui/Panel";
3
+ import { Button } from "@/components/ui/Button/button";
4
+ import {
5
+ FormField,
6
+ FormFieldInput,
7
+ FormFieldSelect,
8
+ } from "@/components/ui/FormField";
9
+ import { ChipGroup, ChipGroupItem } from "@/components/ui/Chip";
10
+ import { Switch } from "@/components/shadcn/switch";
11
+ import { AGENTS } from "../../_table-data";
12
+ import { ACCOUNT_STATUS, BANKS } from "../../clientes-financeiro-mocks";
13
+ import type {
14
+ AccountStatus,
15
+ BankId,
16
+ FinanceClientRow,
17
+ PaymentMethod,
18
+ } from "../../clientes-financeiro.types";
19
+
20
+ const PAYMENT_METHODS: { value: PaymentMethod; label: string }[] = [
21
+ { value: "pix", label: "PIX" },
22
+ { value: "ted", label: "TED" },
23
+ { value: "boleto", label: "Boleto" },
24
+ ];
25
+
26
+ const BANK_OPTIONS = (Object.keys(BANKS) as BankId[]).map((id) => ({
27
+ value: id,
28
+ label: BANKS[id].name,
29
+ }));
30
+
31
+ const AGENT_OPTIONS = Object.entries(AGENTS).map(([id, a]) => ({
32
+ value: id,
33
+ label: a.name,
34
+ }));
35
+
36
+ const STATUS_OPTIONS = (
37
+ Object.entries(ACCOUNT_STATUS) as [AccountStatus, (typeof ACCOUNT_STATUS)[AccountStatus]][]
38
+ ).map(([value, m]) => ({ value, label: m.label, color: m.color }));
39
+
40
+ /** Campos editáveis da row financeira. */
41
+ export type FinanceEditPatch = {
42
+ name: string;
43
+ email: string;
44
+ phone: string;
45
+ companyName: string;
46
+ cnpj: string;
47
+ accountStatus: AccountStatus;
48
+ bank: BankId;
49
+ commissionRate: number;
50
+ autoWithdraw: boolean;
51
+ paymentMethods: PaymentMethod[];
52
+ agentId: string;
53
+ };
54
+
55
+ export type EditarFinanceDrawerProps = {
56
+ /** Row em edição — null fecha o drawer. */
57
+ client: FinanceClientRow | null;
58
+ onOpenChange: (open: boolean) => void;
59
+ /** Salva as alterações (mock no showcase). */
60
+ onSave?: (id: string, patch: FinanceEditPatch) => void;
61
+ };
62
+
63
+ /**
64
+ * Drawer "Editar licenciado" — campos REAIS da row financeira (não o form
65
+ * genérico de criação). Panel + FormField (Input/Select) + ChipGroup + Switch.
66
+ */
67
+ export function EditarFinanceDrawer({
68
+ client,
69
+ onOpenChange,
70
+ onSave,
71
+ }: EditarFinanceDrawerProps) {
72
+ const [form, setForm] = useState<FinanceEditPatch | null>(null);
73
+
74
+ // (Re)hidrata o form ao abrir/trocar de cliente.
75
+ useEffect(() => {
76
+ if (!client) return;
77
+ setForm({
78
+ name: client.name,
79
+ email: client.email,
80
+ phone: client.phone,
81
+ companyName: client.companyName,
82
+ cnpj: client.cnpj,
83
+ accountStatus: client.accountStatus,
84
+ bank: client.bankAccount.bank,
85
+ commissionRate: client.commissionRate,
86
+ autoWithdraw: client.autoWithdraw,
87
+ paymentMethods: client.paymentMethods,
88
+ agentId: client.agentId as string,
89
+ });
90
+ }, [client]);
91
+
92
+ const patch = (p: Partial<FinanceEditPatch>) =>
93
+ setForm((f) => (f ? { ...f, ...p } : f));
94
+
95
+ const canSave = !!form && form.name.trim().length > 0;
96
+
97
+ const handleSave = () => {
98
+ if (!client || !form || !canSave) return;
99
+ onSave?.(client.id, form);
100
+ onOpenChange(false);
101
+ };
102
+
103
+ return (
104
+ <Panel
105
+ open={client !== null}
106
+ onOpenChange={onOpenChange}
107
+ title={client ? `Editar — ${client.name}` : "Editar licenciado"}
108
+ side="right"
109
+ size="md"
110
+ footer={
111
+ <>
112
+ <Button variant="outline" color="secondary" size="md" onClick={() => onOpenChange(false)}>
113
+ Cancelar
114
+ </Button>
115
+ <Button
116
+ variant="filled"
117
+ color="primary"
118
+ size="md"
119
+ disabled={!canSave}
120
+ onClick={handleSave}
121
+ >
122
+ Salvar alterações
123
+ </Button>
124
+ </>
125
+ }
126
+ >
127
+ {form && (
128
+ <>
129
+ <FormFieldInput
130
+ label="Licenciado"
131
+ required
132
+ value={form.name}
133
+ onChange={(e) => patch({ name: e.target.value })}
134
+ />
135
+
136
+ <div className="grid grid-cols-2 gap-form-gap">
137
+ <FormFieldInput
138
+ label="Email"
139
+ type="email"
140
+ value={form.email}
141
+ onChange={(e) => patch({ email: e.target.value })}
142
+ />
143
+ <FormFieldInput
144
+ label="Telefone"
145
+ type="tel"
146
+ value={form.phone}
147
+ onChange={(e) => patch({ phone: e.target.value })}
148
+ />
149
+ </div>
150
+
151
+ <FormFieldInput
152
+ label="Razão Social"
153
+ value={form.companyName}
154
+ onChange={(e) => patch({ companyName: e.target.value })}
155
+ />
156
+ <FormFieldInput
157
+ label="CNPJ"
158
+ value={form.cnpj}
159
+ onChange={(e) => patch({ cnpj: e.target.value })}
160
+ />
161
+
162
+ <FormField label="Situação">
163
+ {() => (
164
+ <ChipGroup
165
+ type="single"
166
+ value={form.accountStatus}
167
+ onValueChange={(v) => v && patch({ accountStatus: v as AccountStatus })}
168
+ size="md"
169
+ shape="pill"
170
+ ariaLabel="Situação da conta"
171
+ >
172
+ {STATUS_OPTIONS.map((o) => (
173
+ <ChipGroupItem key={o.value} value={o.value}>
174
+ {o.label}
175
+ </ChipGroupItem>
176
+ ))}
177
+ </ChipGroup>
178
+ )}
179
+ </FormField>
180
+
181
+ <div className="grid grid-cols-2 gap-form-gap">
182
+ <FormFieldSelect
183
+ label="Banco"
184
+ value={form.bank}
185
+ onValueChange={(v) => patch({ bank: v as BankId })}
186
+ options={BANK_OPTIONS}
187
+ />
188
+ <FormFieldInput
189
+ label="Comissão (%)"
190
+ type="number"
191
+ value={String(form.commissionRate)}
192
+ onChange={(e) => patch({ commissionRate: Number(e.target.value) })}
193
+ min={0}
194
+ max={100}
195
+ step={0.1}
196
+ />
197
+ </div>
198
+
199
+ <FormFieldSelect
200
+ label="Gestor da conta"
201
+ value={form.agentId}
202
+ onValueChange={(v) => patch({ agentId: v })}
203
+ options={AGENT_OPTIONS}
204
+ />
205
+
206
+ <FormField label="Métodos de recebimento">
207
+ {() => (
208
+ <ChipGroup
209
+ type="multiple"
210
+ value={form.paymentMethods}
211
+ onValueChange={(v) => patch({ paymentMethods: v as PaymentMethod[] })}
212
+ size="md"
213
+ shape="rounded"
214
+ ariaLabel="Métodos de recebimento"
215
+ >
216
+ {PAYMENT_METHODS.map((m) => (
217
+ <ChipGroupItem key={m.value} value={m.value}>
218
+ {m.label}
219
+ </ChipGroupItem>
220
+ ))}
221
+ </ChipGroup>
222
+ )}
223
+ </FormField>
224
+
225
+ <FormField
226
+ label="Saque automático"
227
+ helperText="Quando ligado, saques são processados automaticamente."
228
+ >
229
+ {() => (
230
+ <Switch
231
+ checked={form.autoWithdraw}
232
+ onCheckedChange={(next) => patch({ autoWithdraw: next })}
233
+ aria-label="Saque automático"
234
+ />
235
+ )}
236
+ </FormField>
237
+ </>
238
+ )}
239
+ </Panel>
240
+ );
241
+ }
@@ -0,0 +1,5 @@
1
+ export { EditarFinanceDrawer } from "./editar-finance-drawer";
2
+ export type {
3
+ EditarFinanceDrawerProps,
4
+ FinanceEditPatch,
5
+ } from "./editar-finance-drawer";
@@ -0,0 +1,172 @@
1
+ import {
2
+ ArrowDownLeft,
3
+ ArrowUpRight,
4
+ Mail,
5
+ MapPin,
6
+ Phone,
7
+ Wallet,
8
+ } from "lucide-react";
9
+ import { Avatar } from "@/components/ui/avatar-ig";
10
+ import { Chip } from "@/components/ui/Chip";
11
+ import {
12
+ ACCOUNT_STATUS,
13
+ BANKS,
14
+ formatBRL,
15
+ formatDateTimeShort,
16
+ } from "../../clientes-financeiro-mocks";
17
+ import type { FinanceClientRow } from "../../clientes-financeiro.types";
18
+
19
+ /* ── Linha do extrato ───────────────────────────────────────────── */
20
+
21
+ function TransactionRow({
22
+ date,
23
+ type,
24
+ amount,
25
+ description,
26
+ }: FinanceClientRow["transactions"][number]) {
27
+ const isIn = type === "in";
28
+ return (
29
+ <li className="flex items-center gap-gp-md py-pad-sm">
30
+ <span
31
+ className={`grid place-items-center size-form-md shrink-0 rounded-radius-md ${
32
+ isIn
33
+ ? "bg-bg-success-muted text-fg-success"
34
+ : "bg-bg-danger-muted text-fg-danger"
35
+ }`}
36
+ aria-hidden="true"
37
+ >
38
+ {isIn ? (
39
+ <ArrowDownLeft className="size-icon-sm" strokeWidth={2} />
40
+ ) : (
41
+ <ArrowUpRight className="size-icon-sm" strokeWidth={2} />
42
+ )}
43
+ </span>
44
+ <div className="flex flex-col min-w-0 flex-1">
45
+ <span className="text-body-sm font-medium text-fg-default truncate">
46
+ {description}
47
+ </span>
48
+ <span className="text-caption-md text-fg-muted tabular-nums">
49
+ {formatDateTimeShort(date)}
50
+ </span>
51
+ </div>
52
+ <span
53
+ className={`text-body-sm font-semibold tabular-nums shrink-0 ${
54
+ isIn ? "text-fg-success" : "text-fg-danger"
55
+ }`}
56
+ >
57
+ {isIn ? "+" : "−"} {formatBRL(amount)}
58
+ </span>
59
+ </li>
60
+ );
61
+ }
62
+
63
+ /* ── Item label/valor das colunas laterais ─────────────────────── */
64
+
65
+ function InfoRow({
66
+ icon: Icon,
67
+ label,
68
+ children,
69
+ }: {
70
+ icon: React.ComponentType<{ className?: string; strokeWidth?: number }>;
71
+ label: string;
72
+ children: React.ReactNode;
73
+ }) {
74
+ return (
75
+ <div className="flex items-start gap-gp-sm">
76
+ <Icon className="size-icon-sm text-fg-muted mt-[2px] shrink-0" strokeWidth={1.8} />
77
+ <div className="flex flex-col gap-gp-2xs min-w-0">
78
+ <span className="text-body-sm text-fg-strong break-words">{children}</span>
79
+ <span className="text-body-xs font-normal text-fg-muted">{label}</span>
80
+ </div>
81
+ </div>
82
+ );
83
+ }
84
+
85
+ /* ── Painel de expansão da row ──────────────────────────────────── */
86
+
87
+ export function ExtratoExpansion({ row }: { row: FinanceClientRow }) {
88
+ const status = ACCOUNT_STATUS[row.accountStatus];
89
+ const bank = BANKS[row.bankAccount.bank];
90
+ // Extrato exibe as 5 movimentações mais recentes (transactions já vem ordenado desc).
91
+ const recent = row.transactions.slice(0, 5);
92
+
93
+ return (
94
+ <div className="w-full p-pad-4xl bg-bg-subtle">
95
+ <div className="grid grid-cols-1 lg:grid-cols-[1.5fr_1fr] gap-gp-4xl lg:gap-gp-6xl max-w-[1100px]">
96
+ {/* Extrato */}
97
+ <section className="flex flex-col gap-gp-lg">
98
+ <header className="flex items-center justify-between gap-gp-md">
99
+ <h4 className="m-0 text-body-md font-semibold text-fg-muted uppercase tracking-wider">
100
+ Extrato recente
101
+ </h4>
102
+ <Chip color={status.color} variant="soft" size="sm" shape="pill">
103
+ {status.label}
104
+ </Chip>
105
+ </header>
106
+ <ul className="flex flex-col divide-y divide-border-subtle m-0 p-0 list-none">
107
+ {recent.map((tx) => (
108
+ <TransactionRow key={tx.id} {...tx} />
109
+ ))}
110
+ </ul>
111
+ </section>
112
+
113
+ {/* Dados bancários + contato + resumo — separados do extrato por
114
+ divisória sutil + respiro (evita poluição visual de "grudado"). */}
115
+ <div className="flex flex-col gap-gp-3xl lg:pl-gp-6xl lg:border-l lg:border-border-subtle">
116
+ <section className="flex flex-col gap-gp-md">
117
+ <h4 className="m-0 text-body-md font-semibold text-fg-muted uppercase tracking-wider">
118
+ Conta bancária
119
+ </h4>
120
+ <div className="flex items-center gap-gp-md">
121
+ <Avatar size="lg" colorHex={bank.color} className="text-caption-md font-bold">
122
+ {bank.initials}
123
+ </Avatar>
124
+ <div className="flex flex-col min-w-0">
125
+ <span className="text-body-sm font-medium text-fg-default truncate">
126
+ {row.bankAccount.bankName}
127
+ </span>
128
+ <span className="text-caption-md text-fg-muted tabular-nums">
129
+ Ag {row.bankAccount.agency} · {row.bankAccount.account}
130
+ </span>
131
+ </div>
132
+ </div>
133
+ </section>
134
+
135
+ <section className="flex flex-col gap-gp-md">
136
+ <h4 className="m-0 text-body-md font-semibold text-fg-muted uppercase tracking-wider">
137
+ Contato
138
+ </h4>
139
+ <InfoRow icon={Mail} label="Email">{row.email}</InfoRow>
140
+ <InfoRow icon={Phone} label="Telefone">{row.phone}</InfoRow>
141
+ <InfoRow icon={MapPin} label="Localização">{row.location}</InfoRow>
142
+ </section>
143
+
144
+ <section className="flex flex-col gap-gp-md">
145
+ <h4 className="m-0 text-body-md font-semibold text-fg-muted uppercase tracking-wider">
146
+ Resumo financeiro
147
+ </h4>
148
+ <InfoRow icon={Wallet} label="Saldo disponível">
149
+ <span className="font-semibold tabular-nums text-fg-success">
150
+ {formatBRL(row.availableBalance)}
151
+ </span>
152
+ </InfoRow>
153
+ <div className="flex gap-gp-2xl">
154
+ <div className="flex flex-col gap-gp-2xs">
155
+ <span className="text-body-sm font-medium text-fg-default tabular-nums">
156
+ {formatBRL(row.monthlyVolume)}
157
+ </span>
158
+ <span className="text-body-xs font-normal text-fg-muted">Volume mensal</span>
159
+ </div>
160
+ <div className="flex flex-col gap-gp-2xs">
161
+ <span className="text-body-sm font-medium text-fg-default tabular-nums">
162
+ {row.commissionRate.toLocaleString("pt-BR", { minimumFractionDigits: 1 })}%
163
+ </span>
164
+ <span className="text-body-xs font-normal text-fg-muted">Comissão</span>
165
+ </div>
166
+ </div>
167
+ </section>
168
+ </div>
169
+ </div>
170
+ </div>
171
+ );
172
+ }
@@ -0,0 +1 @@
1
+ export { ExtratoExpansion } from "./extrato-expansion";