@snksergio/design-system 0.62.0 → 0.63.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 (135) 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/exemplos/app-shell/app-shell-example.tsx +140 -0
  7. package/dist-lib/ai/exemplos/app-shell/index.ts +3 -0
  8. package/dist-lib/ai/exemplos/app-shell/nav-data.ts +97 -0
  9. package/dist-lib/ai/exemplos/app-shell/routes.tsx +75 -0
  10. package/dist-lib/ai/exemplos/chat/chat-screen.tsx +152 -0
  11. package/dist-lib/ai/exemplos/chat/chat-v2-mocks.ts +171 -0
  12. package/dist-lib/ai/exemplos/chat/chat-v2.styles.ts +23 -0
  13. package/dist-lib/ai/exemplos/chat/chat-v2.types.ts +81 -0
  14. package/dist-lib/ai/exemplos/chat/components/ChannelDot/channel-dot.tsx +28 -0
  15. package/dist-lib/ai/exemplos/chat/components/ConversationActionsMenu/conversation-actions-menu.tsx +83 -0
  16. package/dist-lib/ai/exemplos/chat/components/ConversationActionsMenu/index.ts +4 -0
  17. package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.styles.ts +22 -0
  18. package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.tsx +203 -0
  19. package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.types.ts +10 -0
  20. package/dist-lib/ai/exemplos/chat/components/ConversationColumn/index.ts +2 -0
  21. package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.styles.ts +55 -0
  22. package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.tsx +52 -0
  23. package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.types.ts +7 -0
  24. package/dist-lib/ai/exemplos/chat/components/ConversationListItem/index.ts +2 -0
  25. package/dist-lib/ai/exemplos/chat/components/DateSeparator/date-separator.tsx +18 -0
  26. package/dist-lib/ai/exemplos/chat/components/DetailField/detail-field.tsx +23 -0
  27. package/dist-lib/ai/exemplos/chat/components/DetailSection/detail-section.tsx +41 -0
  28. package/dist-lib/ai/exemplos/chat/components/DetailSection/index.ts +1 -0
  29. package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.styles.ts +30 -0
  30. package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.tsx +142 -0
  31. package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.types.ts +16 -0
  32. package/dist-lib/ai/exemplos/chat/components/DetailsColumn/index.ts +5 -0
  33. package/dist-lib/ai/exemplos/chat/components/FilterRow/filter-row.styles.ts +37 -0
  34. package/dist-lib/ai/exemplos/chat/components/FilterRow/filter-row.tsx +45 -0
  35. package/dist-lib/ai/exemplos/chat/components/FilterRow/index.ts +1 -0
  36. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.styles.ts +27 -0
  37. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.tsx +66 -0
  38. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.types.ts +13 -0
  39. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-rail.tsx +38 -0
  40. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/index.ts +6 -0
  41. package/dist-lib/ai/exemplos/chat/components/MessageBubble/index.ts +2 -0
  42. package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.styles.ts +37 -0
  43. package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.tsx +29 -0
  44. package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.types.ts +5 -0
  45. package/dist-lib/ai/exemplos/chat/components/PersonAvatar/index.ts +5 -0
  46. package/dist-lib/ai/exemplos/chat/components/PersonAvatar/person-avatar.tsx +31 -0
  47. package/dist-lib/ai/exemplos/chat/components/QueueColumn/index.ts +2 -0
  48. package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.styles.ts +19 -0
  49. package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.tsx +140 -0
  50. package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.types.ts +13 -0
  51. package/dist-lib/ai/exemplos/chat/components/RailItem/index.ts +1 -0
  52. package/dist-lib/ai/exemplos/chat/components/RailItem/rail-item.styles.ts +30 -0
  53. package/dist-lib/ai/exemplos/chat/components/RailItem/rail-item.tsx +42 -0
  54. package/dist-lib/ai/exemplos/chat/hooks/use-resizable.ts +98 -0
  55. package/dist-lib/ai/exemplos/chat/index.ts +1 -0
  56. package/dist-lib/ai/exemplos/clientes/_table-data.ts +59 -0
  57. package/dist-lib/ai/exemplos/clientes/clientes-screen.tsx +505 -0
  58. package/dist-lib/ai/exemplos/clientes/clientes-showcase-mocks.ts +117 -0
  59. package/dist-lib/ai/exemplos/clientes/clientes-showcase.styles.ts +13 -0
  60. package/dist-lib/ai/exemplos/clientes/clientes-showcase.types.ts +16 -0
  61. package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.styles.ts +28 -0
  62. package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.tsx +303 -0
  63. package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.types.ts +14 -0
  64. package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/index.ts +2 -0
  65. package/dist-lib/ai/exemplos/clientes/components/NovoClienteDrawer/index.ts +2 -0
  66. package/dist-lib/ai/exemplos/clientes/components/NovoClienteDrawer/novo-cliente-drawer.tsx +207 -0
  67. package/dist-lib/ai/exemplos/clientes/index.ts +1 -0
  68. package/dist-lib/ai/exemplos/edit-page/components/StepNav.tsx +81 -0
  69. package/dist-lib/ai/exemplos/edit-page/components/section-card.tsx +85 -0
  70. package/dist-lib/ai/exemplos/edit-page/edit-page-screen.tsx +234 -0
  71. package/dist-lib/ai/exemplos/edit-page/index.ts +1 -0
  72. package/dist-lib/ai/exemplos/finance/_table-data.ts +57 -0
  73. package/dist-lib/ai/exemplos/finance/clientes-financeiro-mocks.ts +227 -0
  74. package/dist-lib/ai/exemplos/finance/clientes-financeiro.types.ts +75 -0
  75. package/dist-lib/ai/exemplos/finance/clientes-showcase-mocks.ts +117 -0
  76. package/dist-lib/ai/exemplos/finance/clientes-showcase.styles.ts +13 -0
  77. package/dist-lib/ai/exemplos/finance/clientes-showcase.types.ts +16 -0
  78. package/dist-lib/ai/exemplos/finance/components/EditarFinanceDrawer/editar-finance-drawer.tsx +241 -0
  79. package/dist-lib/ai/exemplos/finance/components/EditarFinanceDrawer/index.ts +5 -0
  80. package/dist-lib/ai/exemplos/finance/components/ExtratoExpansion/extrato-expansion.tsx +172 -0
  81. package/dist-lib/ai/exemplos/finance/components/ExtratoExpansion/index.ts +1 -0
  82. package/dist-lib/ai/exemplos/finance/components/FinanceDetailPanel/finance-detail-panel.tsx +241 -0
  83. package/dist-lib/ai/exemplos/finance/components/FinanceDetailPanel/index.ts +2 -0
  84. package/dist-lib/ai/exemplos/finance/components/NovoClienteDrawer/index.ts +2 -0
  85. package/dist-lib/ai/exemplos/finance/components/NovoClienteDrawer/novo-cliente-drawer.tsx +207 -0
  86. package/dist-lib/ai/exemplos/finance/components/SacarDialog/index.ts +2 -0
  87. package/dist-lib/ai/exemplos/finance/components/SacarDialog/sacar-dialog.tsx +346 -0
  88. package/dist-lib/ai/exemplos/finance/finance-screen.tsx +821 -0
  89. package/dist-lib/ai/exemplos/finance/index.ts +1 -0
  90. package/dist-lib/ai/exemplos/gantt/_gantt-data.tsx +402 -0
  91. package/dist-lib/ai/exemplos/gantt/gantt-screen.tsx +512 -0
  92. package/dist-lib/ai/exemplos/gantt/index.ts +1 -0
  93. package/dist-lib/ai/exemplos/login/index.ts +1 -0
  94. package/dist-lib/ai/exemplos/login/login-screen.tsx +246 -0
  95. package/dist-lib/ai/exemplos/mapa-rede/components/ConsultorDetailPanel/consultor-detail-panel.tsx +158 -0
  96. package/dist-lib/ai/exemplos/mapa-rede/components/ConsultorDetailPanel/index.ts +2 -0
  97. package/dist-lib/ai/exemplos/mapa-rede/index.ts +1 -0
  98. package/dist-lib/ai/exemplos/mapa-rede/mapa-de-rede-mocks.ts +655 -0
  99. package/dist-lib/ai/exemplos/mapa-rede/mapa-de-rede.types.ts +53 -0
  100. package/dist-lib/ai/exemplos/mapa-rede/mapa-rede-screen.tsx +227 -0
  101. package/dist-lib/ai/exemplos/order-detail/components/ActivityTab.tsx +77 -0
  102. package/dist-lib/ai/exemplos/order-detail/components/AttachmentsTab.tsx +45 -0
  103. package/dist-lib/ai/exemplos/order-detail/components/CommentsTab.tsx +111 -0
  104. package/dist-lib/ai/exemplos/order-detail/components/DetailsTab.tsx +189 -0
  105. package/dist-lib/ai/exemplos/order-detail/components/OverviewTab.tsx +200 -0
  106. package/dist-lib/ai/exemplos/order-detail/components/section-card.tsx +85 -0
  107. package/dist-lib/ai/exemplos/order-detail/index.ts +1 -0
  108. package/dist-lib/ai/exemplos/order-detail/order-detail-screen.tsx +119 -0
  109. package/dist-lib/ai/exemplos/order-detail/order-mocks.ts +186 -0
  110. package/dist-lib/ai/exemplos/order-detail/order.types.ts +118 -0
  111. package/dist-lib/ai/indice.json +30 -15
  112. package/dist-lib/ai/manifest.json +31 -5
  113. package/dist-lib/ai/roteiros/app-builder/roteiro.md +155 -0
  114. package/dist-lib/ai/roteiros/auth-builder/roteiro.md +42 -0
  115. package/dist-lib/ai/roteiros/cards/roteiro.md +34 -0
  116. package/dist-lib/ai/roteiros/charts/roteiro.md +32 -0
  117. package/dist-lib/ai/roteiros/chat/roteiro.md +31 -0
  118. package/dist-lib/ai/roteiros/crud-builder/blueprint.md +63 -0
  119. package/dist-lib/ai/roteiros/crud-builder/entrevista.md +139 -0
  120. package/dist-lib/ai/roteiros/crud-builder/geracao.md +113 -0
  121. package/dist-lib/ai/roteiros/crud-builder/roteiro.md +89 -0
  122. package/dist-lib/ai/roteiros/drawers/roteiro.md +41 -0
  123. package/dist-lib/ai/roteiros/list-builder/blueprint.md +94 -0
  124. package/dist-lib/ai/roteiros/list-builder/entrevista.md +188 -0
  125. package/dist-lib/ai/roteiros/list-builder/geracao.md +63 -0
  126. package/dist-lib/ai/roteiros/list-builder/roteiro.md +91 -0
  127. package/dist-lib/ai/roteiros/module-replicator/roteiro.md +56 -0
  128. package/dist-lib/ai/roteiros/page-detail/roteiro.md +32 -0
  129. package/dist-lib/ai/roteiros/page-edit/roteiro.md +31 -0
  130. package/dist-lib/ai/roteiros/screen-composer/roteiro.md +95 -0
  131. package/package.json +1 -1
  132. /package/dist-lib/ai/roteiros/{dashboard → dashboard-builder}/blueprint.md +0 -0
  133. /package/dist-lib/ai/roteiros/{dashboard → dashboard-builder}/entrevista.md +0 -0
  134. /package/dist-lib/ai/roteiros/{dashboard → dashboard-builder}/geracao.md +0 -0
  135. /package/dist-lib/ai/roteiros/{dashboard → dashboard-builder}/roteiro.md +0 -0
@@ -0,0 +1,241 @@
1
+ import { Banknote, Pencil } from "lucide-react";
2
+ import {
3
+ FloatingPanel,
4
+ FloatingPanelSection,
5
+ FloatingPanelField,
6
+ } from "@/components/ui/FloatingPanel";
7
+ import { Avatar } from "@/components/ui/avatar-ig";
8
+ import { Button } from "@/components/ui/Button/button";
9
+ import { Chip } from "@/components/ui/Chip";
10
+ import { AGENTS } from "../../_table-data";
11
+ import {
12
+ ACCOUNT_STATUS,
13
+ BANKS,
14
+ formatBRL,
15
+ formatRelativeDays,
16
+ } from "../../clientes-financeiro-mocks";
17
+ import type {
18
+ FinanceClientRow,
19
+ PaymentMethod,
20
+ } from "../../clientes-financeiro.types";
21
+
22
+ const PAYMENT_METHOD_LABEL: Record<PaymentMethod, string> = {
23
+ pix: "PIX",
24
+ ted: "TED",
25
+ boleto: "Boleto",
26
+ };
27
+
28
+ function formatLongDate(ms: number): string {
29
+ if (!ms) return "—";
30
+ return new Date(ms).toLocaleDateString("pt-BR", {
31
+ day: "numeric",
32
+ month: "long",
33
+ year: "numeric",
34
+ });
35
+ }
36
+
37
+ export type FinanceDetailPanelProps = {
38
+ row: FinanceClientRow | null;
39
+ onClose: () => void;
40
+ onEdit?: (row: FinanceClientRow) => void;
41
+ onSacar?: (row: FinanceClientRow) => void;
42
+ };
43
+
44
+ export function FinanceDetailPanel({
45
+ row,
46
+ onClose,
47
+ onEdit,
48
+ onSacar,
49
+ }: FinanceDetailPanelProps) {
50
+ if (!row) return null;
51
+
52
+ const status = ACCOUNT_STATUS[row.accountStatus];
53
+ const bank = BANKS[row.bankAccount.bank];
54
+ const agent = AGENTS[row.agentId as keyof typeof AGENTS];
55
+ const blocked = row.accountStatus === "bloqueado";
56
+
57
+ return (
58
+ <FloatingPanel
59
+ open={row !== null}
60
+ onOpenChange={(open) => {
61
+ if (!open) onClose();
62
+ }}
63
+ side="right"
64
+ size="md"
65
+ resizable
66
+ maximizable
67
+ // Sections gerenciam o próprio padding edge-to-edge (divisórias full-width).
68
+ bodyPadded={false}
69
+ resizableStorageKey="clientes-financeiro.detail-panel.width"
70
+ titleSlot={
71
+ <div className="flex items-center gap-gp-md min-w-0">
72
+ <Avatar size="lg" colorHex={row.avatarColor} className="shrink-0">
73
+ {row.initials}
74
+ </Avatar>
75
+ <div className="flex flex-col min-w-0 flex-1">
76
+ <div className="text-body-md font-semibold text-fg-default truncate">
77
+ {row.name}
78
+ </div>
79
+ <div className="flex items-center gap-gp-sm mt-[2px] text-body-xs font-normal text-fg-muted">
80
+ <span className="tabular-nums">{row.id}</span>
81
+ <span className="opacity-50">·</span>
82
+ <Chip color={status.color} variant="soft" size="sm" shape="pill">
83
+ {status.label}
84
+ </Chip>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ }
89
+ headerActions={
90
+ <>
91
+ <Button
92
+ variant="soft"
93
+ color="secondary"
94
+ size="icon-sm"
95
+ aria-label="Editar dados"
96
+ onClick={() => onEdit?.(row)}
97
+ >
98
+ <Pencil />
99
+ </Button>
100
+ <Button
101
+ variant="soft"
102
+ color="primary"
103
+ size="icon-sm"
104
+ aria-label="Realizar saque"
105
+ disabled={blocked}
106
+ onClick={() => onSacar?.(row)}
107
+ >
108
+ <Banknote />
109
+ </Button>
110
+ </>
111
+ }
112
+ footer={
113
+ <>
114
+ <Button variant="outline" color="secondary" size="sm" onClick={onClose}>
115
+ Fechar
116
+ </Button>
117
+ <Button
118
+ variant="filled"
119
+ color="primary"
120
+ size="sm"
121
+ disabled={blocked}
122
+ onClick={() => onSacar?.(row)}
123
+ >
124
+ Realizar saque
125
+ </Button>
126
+ </>
127
+ }
128
+ >
129
+ {/* Saldo em destaque — gutter próprio (body não tem padding). */}
130
+ <div className="px-[18px] pt-pad-xl pb-pad-md">
131
+ <div className="flex flex-col gap-gp-2xs p-pad-2xl bg-bg-success-muted rounded-radius-lg">
132
+ <span className="text-caption-sm text-fg-muted">Saldo disponível</span>
133
+ <span className="text-body-2xl font-bold text-fg-success leading-none tabular-nums">
134
+ {formatBRL(row.availableBalance)}
135
+ </span>
136
+ </div>
137
+ </div>
138
+
139
+ <FloatingPanelSection title="Empresa">
140
+ <FloatingPanelField label="Razão Social" value={row.companyName} />
141
+ <FloatingPanelField
142
+ label="CNPJ"
143
+ value={<span className="tabular-nums">{row.cnpj}</span>}
144
+ />
145
+ </FloatingPanelSection>
146
+
147
+ <FloatingPanelSection title="Conta bancária">
148
+ <div className="flex items-center gap-gp-md">
149
+ <Avatar size="lg" colorHex={bank.color} className="text-caption-md font-bold">
150
+ {bank.initials}
151
+ </Avatar>
152
+ <div className="flex flex-col min-w-0">
153
+ <span className="text-body-sm font-medium text-fg-default truncate">
154
+ {row.bankAccount.bankName}
155
+ </span>
156
+ <span className="text-caption-md text-fg-muted tabular-nums">
157
+ Ag {row.bankAccount.agency} · {row.bankAccount.account}
158
+ </span>
159
+ </div>
160
+ </div>
161
+ </FloatingPanelSection>
162
+
163
+ <FloatingPanelSection title="Financeiro">
164
+ <FloatingPanelField
165
+ label="Volume mensal"
166
+ value={<span className="tabular-nums">{formatBRL(row.monthlyVolume)}</span>}
167
+ />
168
+ <FloatingPanelField
169
+ label="Comissão"
170
+ value={
171
+ <span className="tabular-nums">
172
+ {row.commissionRate.toLocaleString("pt-BR", { minimumFractionDigits: 1 })}%
173
+ </span>
174
+ }
175
+ />
176
+ <FloatingPanelField
177
+ label="Saque automático"
178
+ value={row.autoWithdraw ? "Ativado" : "Desativado"}
179
+ />
180
+ <FloatingPanelField
181
+ label="Métodos"
182
+ value={
183
+ <span className="inline-flex flex-wrap gap-gp-2xs justify-end">
184
+ {row.paymentMethods.map((m) => (
185
+ <Chip key={m} color="neutral" variant="soft" size="sm" shape="rounded">
186
+ {PAYMENT_METHOD_LABEL[m]}
187
+ </Chip>
188
+ ))}
189
+ </span>
190
+ }
191
+ />
192
+ </FloatingPanelSection>
193
+
194
+ <FloatingPanelSection title="Contato">
195
+ <FloatingPanelField
196
+ label="Email"
197
+ value={
198
+ <a href={`mailto:${row.email}`} className="text-fg-brand hover:underline">
199
+ {row.email}
200
+ </a>
201
+ }
202
+ />
203
+ <FloatingPanelField
204
+ label="Telefone"
205
+ value={
206
+ <a
207
+ href={`tel:${row.phone.replace(/\D/g, "")}`}
208
+ className="text-fg-brand hover:underline"
209
+ >
210
+ {row.phone}
211
+ </a>
212
+ }
213
+ />
214
+ <FloatingPanelField label="Localização" value={row.location} />
215
+ </FloatingPanelSection>
216
+
217
+ <FloatingPanelSection title="Gestão">
218
+ <FloatingPanelField
219
+ label="Gestor da conta"
220
+ value={
221
+ agent ? (
222
+ <span className="inline-flex items-center gap-gp-sm">
223
+ <Avatar size="xs" colorHex={agent.color}>
224
+ {agent.initials}
225
+ </Avatar>
226
+ {agent.name}
227
+ </span>
228
+ ) : (
229
+ "—"
230
+ )
231
+ }
232
+ />
233
+ <FloatingPanelField label="Cliente desde" value={formatLongDate(row.createdAt)} />
234
+ <FloatingPanelField
235
+ label="Última movimentação"
236
+ value={formatRelativeDays(row.lastMovement)}
237
+ />
238
+ </FloatingPanelSection>
239
+ </FloatingPanel>
240
+ );
241
+ }
@@ -0,0 +1,2 @@
1
+ export { FinanceDetailPanel } from "./finance-detail-panel";
2
+ export type { FinanceDetailPanelProps } from "./finance-detail-panel";
@@ -0,0 +1,2 @@
1
+ export { NovoClienteDrawer } from "./novo-cliente-drawer";
2
+ export type { NovoClienteDrawerProps } from "./novo-cliente-drawer";
@@ -0,0 +1,207 @@
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
+ FormFieldTextarea,
9
+ } from "@/components/ui/FormField";
10
+ import { ChipGroup, ChipGroupItem } from "@/components/ui/Chip";
11
+ import { STATUSES, CATEGORIES } from "../../_table-data";
12
+ import { CITIES, STATUS_DOT } from "../../clientes-showcase-mocks";
13
+ import { drawerFormStyles } from "../../clientes-showcase.styles";
14
+ import type { NovoClienteFormData } from "../../clientes-showcase.types";
15
+
16
+ const INITIAL_FORM: NovoClienteFormData = {
17
+ name: "",
18
+ email: "",
19
+ whatsapp: "",
20
+ statusId: "active",
21
+ categoryId: "lead",
22
+ city: "",
23
+ value: 0,
24
+ notes: "",
25
+ };
26
+
27
+ export type NovoClienteDrawerProps = {
28
+ open: boolean;
29
+ onOpenChange: (open: boolean) => void;
30
+ /** Disparado ao confirmar — drawer fecha automaticamente após o submit. */
31
+ onSubmit?: (data: NovoClienteFormData) => void;
32
+ };
33
+
34
+ /**
35
+ * Drawer "Novo cliente" — replica visual do TblClientFormDrawer
36
+ * (sandbox /design-and-table-v2), recompilado em cima dos componentes do DS:
37
+ * Panel + FormField (Input / Select / Textarea) + ChipGroup + Button.
38
+ *
39
+ * Layout (top → bottom):
40
+ * 1. Nome* — full width
41
+ * 2. Email + WhatsApp — 2 cols
42
+ * 3. Status — ChipGroup com dot colorido
43
+ * 4. Categoria — ChipGroup
44
+ * 5. Cidade + Valor R$ — 2 cols
45
+ * 6. Notas — textarea full width
46
+ */
47
+ export function NovoClienteDrawer({
48
+ open,
49
+ onOpenChange,
50
+ onSubmit,
51
+ }: NovoClienteDrawerProps) {
52
+ const s = drawerFormStyles();
53
+ const [form, setForm] = useState<NovoClienteFormData>(INITIAL_FORM);
54
+
55
+ // Reseta form sempre que abrir
56
+ useEffect(() => {
57
+ if (open) setForm(INITIAL_FORM);
58
+ }, [open]);
59
+
60
+ const canSubmit = form.name.trim().length > 0;
61
+
62
+ const handleSubmit = () => {
63
+ if (!canSubmit) return;
64
+ onSubmit?.(form);
65
+ onOpenChange(false);
66
+ };
67
+
68
+ return (
69
+ <Panel
70
+ open={open}
71
+ onOpenChange={onOpenChange}
72
+ title="Novo cliente"
73
+ side="right"
74
+ size="md"
75
+ footer={
76
+ <>
77
+ <Button
78
+ variant="outline"
79
+ color="secondary"
80
+ size="md"
81
+ onClick={() => onOpenChange(false)}
82
+ >
83
+ Cancelar
84
+ </Button>
85
+ <Button
86
+ variant="filled"
87
+ color="primary"
88
+ size="md"
89
+ disabled={!canSubmit}
90
+ onClick={handleSubmit}
91
+ >
92
+ Criar cliente
93
+ </Button>
94
+ </>
95
+ }
96
+ >
97
+ <FormFieldInput
98
+ label="Nome"
99
+ required
100
+ placeholder="Maria Silva"
101
+ value={form.name}
102
+ onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}
103
+ />
104
+
105
+ <div className={s.twoCols()}>
106
+ <FormFieldInput
107
+ label="Email"
108
+ type="email"
109
+ placeholder="cliente@exemplo.com"
110
+ value={form.email}
111
+ onChange={(e) => setForm((f) => ({ ...f, email: e.target.value }))}
112
+ />
113
+ <FormFieldInput
114
+ label="WhatsApp"
115
+ type="tel"
116
+ placeholder="55 11 99999-9999"
117
+ value={form.whatsapp}
118
+ onChange={(e) => setForm((f) => ({ ...f, whatsapp: e.target.value }))}
119
+ />
120
+ </div>
121
+
122
+ <FormField label="Status">
123
+ {() => (
124
+ <ChipGroup
125
+ type="single"
126
+ value={form.statusId}
127
+ onValueChange={(v) =>
128
+ v &&
129
+ setForm((f) => ({
130
+ ...f,
131
+ statusId: v as keyof typeof STATUSES,
132
+ }))
133
+ }
134
+ size="md"
135
+ shape="pill"
136
+ ariaLabel="Status do cliente"
137
+ >
138
+ {Object.entries(STATUSES).map(([id, st]) => (
139
+ <ChipGroupItem key={id} value={id}>
140
+ <span
141
+ className={s.statusDot()}
142
+ style={{ background: STATUS_DOT[id] ?? st.color }}
143
+ aria-hidden
144
+ />
145
+ {st.label}
146
+ </ChipGroupItem>
147
+ ))}
148
+ </ChipGroup>
149
+ )}
150
+ </FormField>
151
+
152
+ <FormField label="Categoria">
153
+ {() => (
154
+ <ChipGroup
155
+ type="single"
156
+ value={form.categoryId}
157
+ onValueChange={(v) =>
158
+ v &&
159
+ setForm((f) => ({
160
+ ...f,
161
+ categoryId: v as keyof typeof CATEGORIES,
162
+ }))
163
+ }
164
+ size="md"
165
+ shape="pill"
166
+ ariaLabel="Categoria do cliente"
167
+ >
168
+ {Object.entries(CATEGORIES).map(([id, cat]) => (
169
+ <ChipGroupItem key={id} value={id}>
170
+ {cat.label}
171
+ </ChipGroupItem>
172
+ ))}
173
+ </ChipGroup>
174
+ )}
175
+ </FormField>
176
+
177
+ <div className={s.twoCols()}>
178
+ <FormFieldSelect
179
+ label="Cidade"
180
+ placeholder="Selecionar..."
181
+ value={form.city || undefined}
182
+ onValueChange={(v) => setForm((f) => ({ ...f, city: v }))}
183
+ options={CITIES.map((c) => ({ value: c, label: c }))}
184
+ />
185
+ <FormFieldInput
186
+ label="Valor (R$)"
187
+ type="number"
188
+ startAddon="R$"
189
+ value={String(form.value)}
190
+ onChange={(e) =>
191
+ setForm((f) => ({ ...f, value: Number(e.target.value) }))
192
+ }
193
+ min={0}
194
+ step={0.01}
195
+ />
196
+ </div>
197
+
198
+ <FormFieldTextarea
199
+ label="Notas"
200
+ placeholder="Observações sobre o cliente..."
201
+ rows={4}
202
+ value={form.notes}
203
+ onChange={(e) => setForm((f) => ({ ...f, notes: e.target.value }))}
204
+ />
205
+ </Panel>
206
+ );
207
+ }
@@ -0,0 +1,2 @@
1
+ export { SacarDialog } from "./sacar-dialog";
2
+ export type { SacarDialogProps } from "./sacar-dialog";