@snksergio/design-system 0.1.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 (73) hide show
  1. package/README.md +260 -0
  2. package/dist-lib/chunks/app-shell-BNTvOETJ.mjs +2681 -0
  3. package/dist-lib/chunks/app-shell-BNTvOETJ.mjs.map +1 -0
  4. package/dist-lib/chunks/app-shell-CDy42aNI.mjs +2663 -0
  5. package/dist-lib/chunks/app-shell-CDy42aNI.mjs.map +1 -0
  6. package/dist-lib/chunks/app-shell-mocks-kWIVSzLs.mjs +208 -0
  7. package/dist-lib/chunks/app-shell-mocks-kWIVSzLs.mjs.map +1 -0
  8. package/dist-lib/chunks/app-shell-mocks-oO_IV3Gw.mjs +207 -0
  9. package/dist-lib/chunks/app-shell-mocks-oO_IV3Gw.mjs.map +1 -0
  10. package/dist-lib/chunks/badge-B_2Zt52o.mjs +797 -0
  11. package/dist-lib/chunks/badge-B_2Zt52o.mjs.map +1 -0
  12. package/dist-lib/chunks/badge-DKpXURa1.mjs +813 -0
  13. package/dist-lib/chunks/badge-DKpXURa1.mjs.map +1 -0
  14. package/dist-lib/chunks/chat-v2-mocks-BV9Wha_Z.mjs +192 -0
  15. package/dist-lib/chunks/chat-v2-mocks-BV9Wha_Z.mjs.map +1 -0
  16. package/dist-lib/chunks/chat-v2-mocks-DvZRkhmz.mjs +193 -0
  17. package/dist-lib/chunks/chat-v2-mocks-DvZRkhmz.mjs.map +1 -0
  18. package/dist-lib/chunks/chip-group-CsKBBHW2.mjs +796 -0
  19. package/dist-lib/chunks/chip-group-CsKBBHW2.mjs.map +1 -0
  20. package/dist-lib/chunks/chip-group-D3D0i72q.mjs +780 -0
  21. package/dist-lib/chunks/chip-group-D3D0i72q.mjs.map +1 -0
  22. package/dist-lib/chunks/clientes-showcase-mocks-BMfbNNJh.mjs +185 -0
  23. package/dist-lib/chunks/clientes-showcase-mocks-BMfbNNJh.mjs.map +1 -0
  24. package/dist-lib/chunks/clientes-showcase-mocks-Cq9u3nGZ.mjs +169 -0
  25. package/dist-lib/chunks/clientes-showcase-mocks-Cq9u3nGZ.mjs.map +1 -0
  26. package/dist-lib/chunks/input-374MpdBL.mjs +72 -0
  27. package/dist-lib/chunks/input-374MpdBL.mjs.map +1 -0
  28. package/dist-lib/chunks/input-DF4XAWmr.mjs +56 -0
  29. package/dist-lib/chunks/input-DF4XAWmr.mjs.map +1 -0
  30. package/dist-lib/chunks/page-header-Dw-3wqAG.mjs +75 -0
  31. package/dist-lib/chunks/page-header-Dw-3wqAG.mjs.map +1 -0
  32. package/dist-lib/chunks/page-header-mC1gGtOI.mjs +76 -0
  33. package/dist-lib/chunks/page-header-mC1gGtOI.mjs.map +1 -0
  34. package/dist-lib/chunks/panel-Ds15whqp.mjs +9717 -0
  35. package/dist-lib/chunks/panel-Ds15whqp.mjs.map +1 -0
  36. package/dist-lib/chunks/panel-bzNmFlEZ.mjs +9737 -0
  37. package/dist-lib/chunks/panel-bzNmFlEZ.mjs.map +1 -0
  38. package/dist-lib/chunks/useTheme-BUPLZBMs.mjs +66 -0
  39. package/dist-lib/chunks/useTheme-BUPLZBMs.mjs.map +1 -0
  40. package/dist-lib/chunks/useTheme-Dee4mo-A.mjs +65 -0
  41. package/dist-lib/chunks/useTheme-Dee4mo-A.mjs.map +1 -0
  42. package/dist-lib/index.cjs +1207 -0
  43. package/dist-lib/index.cjs.map +1 -0
  44. package/dist-lib/index.d.ts +2 -0
  45. package/dist-lib/index.mjs +1209 -0
  46. package/dist-lib/index.mjs.map +1 -0
  47. package/dist-lib/preview/chat.cjs +1120 -0
  48. package/dist-lib/preview/chat.cjs.map +1 -0
  49. package/dist-lib/preview/chat.d.ts +6 -0
  50. package/dist-lib/preview/chat.mjs +1120 -0
  51. package/dist-lib/preview/chat.mjs.map +1 -0
  52. package/dist-lib/preview/clientes.cjs +897 -0
  53. package/dist-lib/preview/clientes.cjs.map +1 -0
  54. package/dist-lib/preview/clientes.d.ts +6 -0
  55. package/dist-lib/preview/clientes.mjs +897 -0
  56. package/dist-lib/preview/clientes.mjs.map +1 -0
  57. package/dist-lib/preview/dashboard.cjs +733 -0
  58. package/dist-lib/preview/dashboard.cjs.map +1 -0
  59. package/dist-lib/preview/dashboard.d.ts +4 -0
  60. package/dist-lib/preview/dashboard.mjs +733 -0
  61. package/dist-lib/preview/dashboard.mjs.map +1 -0
  62. package/dist-lib/preview/mocks.cjs +15 -0
  63. package/dist-lib/preview/mocks.cjs.map +1 -0
  64. package/dist-lib/preview/mocks.d.ts +2 -0
  65. package/dist-lib/preview/mocks.mjs +15 -0
  66. package/dist-lib/preview/mocks.mjs.map +1 -0
  67. package/dist-lib/theme.css +668 -0
  68. package/dist-lib/tokens.cjs +915 -0
  69. package/dist-lib/tokens.cjs.map +1 -0
  70. package/dist-lib/tokens.d.ts +2 -0
  71. package/dist-lib/tokens.mjs +915 -0
  72. package/dist-lib/tokens.mjs.map +1 -0
  73. package/package.json +142 -0
@@ -0,0 +1,897 @@
1
+ "use strict";
2
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const React = require("react");
5
+ const useTheme = require("../chunks/useTheme-Dee4mo-A.mjs");
6
+ const lucideReact = require("lucide-react");
7
+ const clientesShowcaseMocks = require("../chunks/clientes-showcase-mocks-BMfbNNJh.mjs");
8
+ const panel = require("../chunks/panel-bzNmFlEZ.mjs");
9
+ const chipGroup = require("../chunks/chip-group-CsKBBHW2.mjs");
10
+ const appShell = require("../chunks/app-shell-BNTvOETJ.mjs");
11
+ require("../chunks/input-374MpdBL.mjs");
12
+ require("../chunks/badge-DKpXURa1.mjs");
13
+ const pageHeader = require("../chunks/page-header-Dw-3wqAG.mjs");
14
+ const appShellMocks = require("../chunks/app-shell-mocks-oO_IV3Gw.mjs");
15
+ const drawerFormStyles = chipGroup.tv({
16
+ slots: {
17
+ twoCols: "grid grid-cols-2 gap-gp-xl",
18
+ fieldLabel: "text-[12.5px] font-semibold tracking-[0.01em] leading-none text-fg-default dark:text-fg-muted select-none",
19
+ statusDot: "inline-block size-[8px] rounded-radius-full mr-gp-xs shrink-0"
20
+ }
21
+ });
22
+ const INITIAL_FORM = {
23
+ name: "",
24
+ email: "",
25
+ whatsapp: "",
26
+ statusId: "active",
27
+ categoryId: "lead",
28
+ city: "",
29
+ value: 0,
30
+ notes: ""
31
+ };
32
+ function NovoClienteDrawer({
33
+ open,
34
+ onOpenChange,
35
+ onSubmit
36
+ }) {
37
+ const s = drawerFormStyles();
38
+ const [form, setForm] = React.useState(INITIAL_FORM);
39
+ React.useEffect(() => {
40
+ if (open) setForm(INITIAL_FORM);
41
+ }, [open]);
42
+ const canSubmit = form.name.trim().length > 0;
43
+ const handleSubmit = () => {
44
+ if (!canSubmit) return;
45
+ onSubmit?.(form);
46
+ onOpenChange(false);
47
+ };
48
+ return /* @__PURE__ */ jsxRuntime.jsxs(
49
+ panel.Panel,
50
+ {
51
+ open,
52
+ onOpenChange,
53
+ title: "Novo cliente",
54
+ side: "right",
55
+ size: "md",
56
+ footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
57
+ /* @__PURE__ */ jsxRuntime.jsx(
58
+ chipGroup.Button,
59
+ {
60
+ variant: "outline",
61
+ color: "secondary",
62
+ size: "md",
63
+ onClick: () => onOpenChange(false),
64
+ children: "Cancelar"
65
+ }
66
+ ),
67
+ /* @__PURE__ */ jsxRuntime.jsx(
68
+ chipGroup.Button,
69
+ {
70
+ variant: "filled",
71
+ color: "primary",
72
+ size: "md",
73
+ disabled: !canSubmit,
74
+ onClick: handleSubmit,
75
+ children: "Criar cliente"
76
+ }
77
+ )
78
+ ] }),
79
+ children: [
80
+ /* @__PURE__ */ jsxRuntime.jsx(
81
+ panel.FormFieldInput,
82
+ {
83
+ label: "Nome",
84
+ required: true,
85
+ placeholder: "Maria Silva",
86
+ value: form.name,
87
+ onChange: (e) => setForm((f) => ({ ...f, name: e.target.value }))
88
+ }
89
+ ),
90
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s.twoCols(), children: [
91
+ /* @__PURE__ */ jsxRuntime.jsx(
92
+ panel.FormFieldInput,
93
+ {
94
+ label: "Email",
95
+ type: "email",
96
+ placeholder: "cliente@exemplo.com",
97
+ value: form.email,
98
+ onChange: (e) => setForm((f) => ({ ...f, email: e.target.value }))
99
+ }
100
+ ),
101
+ /* @__PURE__ */ jsxRuntime.jsx(
102
+ panel.FormFieldInput,
103
+ {
104
+ label: "WhatsApp",
105
+ type: "tel",
106
+ placeholder: "55 11 99999-9999",
107
+ value: form.whatsapp,
108
+ onChange: (e) => setForm((f) => ({ ...f, whatsapp: e.target.value }))
109
+ }
110
+ )
111
+ ] }),
112
+ /* @__PURE__ */ jsxRuntime.jsx(panel.FormField, { label: "Status", children: () => /* @__PURE__ */ jsxRuntime.jsx(
113
+ chipGroup.ChipGroup,
114
+ {
115
+ type: "single",
116
+ value: form.statusId,
117
+ onValueChange: (v) => v && setForm((f) => ({
118
+ ...f,
119
+ statusId: v
120
+ })),
121
+ size: "md",
122
+ shape: "pill",
123
+ ariaLabel: "Status do cliente",
124
+ children: Object.entries(clientesShowcaseMocks.STATUSES).map(([id, st]) => /* @__PURE__ */ jsxRuntime.jsxs(chipGroup.ChipGroupItem, { value: id, children: [
125
+ /* @__PURE__ */ jsxRuntime.jsx(
126
+ "span",
127
+ {
128
+ className: s.statusDot(),
129
+ style: { background: clientesShowcaseMocks.STATUS_DOT[id] ?? st.color },
130
+ "aria-hidden": true
131
+ }
132
+ ),
133
+ st.label
134
+ ] }, id))
135
+ }
136
+ ) }),
137
+ /* @__PURE__ */ jsxRuntime.jsx(panel.FormField, { label: "Categoria", children: () => /* @__PURE__ */ jsxRuntime.jsx(
138
+ chipGroup.ChipGroup,
139
+ {
140
+ type: "single",
141
+ value: form.categoryId,
142
+ onValueChange: (v) => v && setForm((f) => ({
143
+ ...f,
144
+ categoryId: v
145
+ })),
146
+ size: "md",
147
+ shape: "pill",
148
+ ariaLabel: "Categoria do cliente",
149
+ children: Object.entries(clientesShowcaseMocks.CATEGORIES).map(([id, cat]) => /* @__PURE__ */ jsxRuntime.jsx(chipGroup.ChipGroupItem, { value: id, children: cat.label }, id))
150
+ }
151
+ ) }),
152
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s.twoCols(), children: [
153
+ /* @__PURE__ */ jsxRuntime.jsx(
154
+ panel.FormFieldSelect,
155
+ {
156
+ label: "Cidade",
157
+ placeholder: "Selecionar...",
158
+ value: form.city || void 0,
159
+ onValueChange: (v) => setForm((f) => ({ ...f, city: v })),
160
+ options: clientesShowcaseMocks.CITIES.map((c) => ({ value: c, label: c }))
161
+ }
162
+ ),
163
+ /* @__PURE__ */ jsxRuntime.jsx(
164
+ panel.FormFieldInput,
165
+ {
166
+ label: "Valor (R$)",
167
+ type: "number",
168
+ startAddon: "R$",
169
+ value: String(form.value),
170
+ onChange: (e) => setForm((f) => ({ ...f, value: Number(e.target.value) })),
171
+ min: 0,
172
+ step: 0.01
173
+ }
174
+ )
175
+ ] }),
176
+ /* @__PURE__ */ jsxRuntime.jsx(
177
+ panel.FormFieldTextarea,
178
+ {
179
+ label: "Notas",
180
+ placeholder: "Observações sobre o cliente...",
181
+ rows: 4,
182
+ value: form.notes,
183
+ onChange: (e) => setForm((f) => ({ ...f, notes: e.target.value }))
184
+ }
185
+ )
186
+ ]
187
+ }
188
+ );
189
+ }
190
+ const detailDrawerStyles = chipGroup.tv({
191
+ slots: {
192
+ section: "border-b border-border-default last:border-b-0",
193
+ sectionHead: [
194
+ "flex items-center justify-between w-full",
195
+ "px-[18px] py-[14px]",
196
+ "bg-transparent border-0 cursor-pointer text-left",
197
+ "text-[13px] font-semibold text-fg-default",
198
+ "transition-colors duration-150",
199
+ "hover:bg-bg-muted",
200
+ "focus-visible:outline-none focus-visible:bg-bg-muted"
201
+ ],
202
+ sectionChev: "size-[14px] text-fg-muted transition-transform duration-200",
203
+ sectionChevOpen: "rotate-180",
204
+ sectionBody: "flex flex-col gap-gp-md px-[18px] pb-pad-2xl",
205
+ field: "flex items-baseline justify-between gap-gp-md text-[13px]",
206
+ fieldLabel: "text-[12px] text-fg-muted shrink-0",
207
+ fieldValue: "text-fg-default text-right break-words min-w-0"
208
+ }
209
+ });
210
+ function formatLongDate(ms) {
211
+ if (!ms) return "—";
212
+ const d = new Date(ms);
213
+ return d.toLocaleDateString("pt-BR", {
214
+ day: "numeric",
215
+ month: "long",
216
+ year: "numeric"
217
+ });
218
+ }
219
+ function DetailField({ label, value, s }) {
220
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s.field(), children: [
221
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s.fieldLabel(), children: label }),
222
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s.fieldValue(), children: value || "—" })
223
+ ] });
224
+ }
225
+ function DetailSection({
226
+ id,
227
+ title,
228
+ open,
229
+ onToggle,
230
+ children,
231
+ s
232
+ }) {
233
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: s.section(), children: [
234
+ /* @__PURE__ */ jsxRuntime.jsxs(
235
+ "button",
236
+ {
237
+ type: "button",
238
+ className: s.sectionHead(),
239
+ onClick: () => onToggle(id),
240
+ "aria-expanded": open,
241
+ children: [
242
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: title }),
243
+ /* @__PURE__ */ jsxRuntime.jsx(
244
+ lucideReact.ChevronDown,
245
+ {
246
+ className: [s.sectionChev(), open ? s.sectionChevOpen() : ""].join(" ")
247
+ }
248
+ )
249
+ ]
250
+ }
251
+ ),
252
+ open && /* @__PURE__ */ jsxRuntime.jsx("div", { className: s.sectionBody(), children })
253
+ ] });
254
+ }
255
+ function DetailDrawer({
256
+ row,
257
+ onClose,
258
+ onEdit,
259
+ onDelete,
260
+ onSave
261
+ }) {
262
+ const s = detailDrawerStyles();
263
+ const [openSections, setOpenSections] = React.useState(
264
+ () => /* @__PURE__ */ new Set(["contact", "meta", "notes"])
265
+ );
266
+ const toggle = (id) => {
267
+ setOpenSections((prev) => {
268
+ const next = new Set(prev);
269
+ if (next.has(id)) next.delete(id);
270
+ else next.add(id);
271
+ return next;
272
+ });
273
+ };
274
+ if (!row) return null;
275
+ const status = clientesShowcaseMocks.STATUSES[row.statusId];
276
+ const cat = clientesShowcaseMocks.CATEGORIES[row.categoryId];
277
+ const agent = clientesShowcaseMocks.AGENTS[row.agentId];
278
+ const agentInitials = agent?.name.split(" ").map((p) => p[0]).join("").slice(0, 2) ?? "?";
279
+ return /* @__PURE__ */ jsxRuntime.jsxs(
280
+ panel.FloatingPanel,
281
+ {
282
+ open: row !== null,
283
+ onOpenChange: (open) => {
284
+ if (!open) onClose();
285
+ },
286
+ side: "right",
287
+ size: "md",
288
+ resizable: true,
289
+ maximizable: true,
290
+ resizableStorageKey: "clientes-showcase.detail-drawer.width",
291
+ titleSlot: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-gp-md min-w-0", children: [
292
+ /* @__PURE__ */ jsxRuntime.jsx(
293
+ appShell.Avatar,
294
+ {
295
+ className: "size-[40px] shrink-0",
296
+ style: { background: row.avatarColor },
297
+ children: /* @__PURE__ */ jsxRuntime.jsx(appShell.AvatarFallback, { className: "bg-transparent text-white text-[14px] font-bold", children: row.initials })
298
+ }
299
+ ),
300
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [
301
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[15px] font-semibold text-fg-default whitespace-nowrap overflow-hidden text-ellipsis", children: row.name }),
302
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-[6px] mt-[2px] text-[12px] text-fg-muted", children: [
303
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: row.id }),
304
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-50", children: "·" }),
305
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-[6px]", children: [
306
+ /* @__PURE__ */ jsxRuntime.jsx(
307
+ "span",
308
+ {
309
+ className: "inline-block size-[8px] rounded-radius-full shrink-0",
310
+ style: { background: clientesShowcaseMocks.STATUS_DOT[row.statusId] ?? status?.color },
311
+ "aria-hidden": true
312
+ }
313
+ ),
314
+ status?.label
315
+ ] })
316
+ ] })
317
+ ] })
318
+ ] }),
319
+ headerActions: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
320
+ /* @__PURE__ */ jsxRuntime.jsx(
321
+ chipGroup.Button,
322
+ {
323
+ variant: "soft",
324
+ color: "secondary",
325
+ size: "icon-sm",
326
+ "aria-label": "Editar",
327
+ onClick: () => onEdit?.(row),
328
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, {})
329
+ }
330
+ ),
331
+ /* @__PURE__ */ jsxRuntime.jsx(
332
+ chipGroup.Button,
333
+ {
334
+ variant: "soft",
335
+ color: "critical",
336
+ size: "icon-sm",
337
+ "aria-label": "Excluir",
338
+ onClick: () => onDelete?.(row),
339
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, {})
340
+ }
341
+ )
342
+ ] }),
343
+ footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
344
+ /* @__PURE__ */ jsxRuntime.jsx(chipGroup.Button, { variant: "outline", color: "secondary", size: "sm", onClick: onClose, children: "Cancelar" }),
345
+ /* @__PURE__ */ jsxRuntime.jsx(
346
+ chipGroup.Button,
347
+ {
348
+ variant: "filled",
349
+ color: "primary",
350
+ size: "sm",
351
+ onClick: () => onSave?.(row),
352
+ children: "Salvar alterações"
353
+ }
354
+ )
355
+ ] }),
356
+ children: [
357
+ /* @__PURE__ */ jsxRuntime.jsxs(
358
+ DetailSection,
359
+ {
360
+ id: "contact",
361
+ title: "Contato",
362
+ open: openSections.has("contact"),
363
+ onToggle: toggle,
364
+ s,
365
+ children: [
366
+ /* @__PURE__ */ jsxRuntime.jsx(
367
+ DetailField,
368
+ {
369
+ label: "Email",
370
+ value: /* @__PURE__ */ jsxRuntime.jsx(
371
+ "a",
372
+ {
373
+ href: `mailto:${row.email}`,
374
+ className: "text-fg-brand hover:underline",
375
+ children: row.email
376
+ }
377
+ ),
378
+ s
379
+ }
380
+ ),
381
+ /* @__PURE__ */ jsxRuntime.jsx(
382
+ DetailField,
383
+ {
384
+ label: "Telefone",
385
+ value: /* @__PURE__ */ jsxRuntime.jsx(
386
+ "a",
387
+ {
388
+ href: `tel:${row.phone.replace(/\D/g, "")}`,
389
+ className: "text-fg-brand hover:underline",
390
+ children: row.phone
391
+ }
392
+ ),
393
+ s
394
+ }
395
+ ),
396
+ /* @__PURE__ */ jsxRuntime.jsx(DetailField, { label: "Localização", value: row.location, s })
397
+ ]
398
+ }
399
+ ),
400
+ /* @__PURE__ */ jsxRuntime.jsxs(
401
+ DetailSection,
402
+ {
403
+ id: "meta",
404
+ title: "Atendimento",
405
+ open: openSections.has("meta"),
406
+ onToggle: toggle,
407
+ s,
408
+ children: [
409
+ /* @__PURE__ */ jsxRuntime.jsx(DetailField, { label: "ID", value: row.id, s }),
410
+ /* @__PURE__ */ jsxRuntime.jsx(
411
+ DetailField,
412
+ {
413
+ label: "Categoria",
414
+ value: cat ? /* @__PURE__ */ jsxRuntime.jsx(
415
+ chipGroup.Chip,
416
+ {
417
+ color: clientesShowcaseMocks.CATEGORY_KIND[row.categoryId] ?? "info",
418
+ variant: "soft",
419
+ size: "sm",
420
+ shape: "pill",
421
+ children: cat.label
422
+ }
423
+ ) : "—",
424
+ s
425
+ }
426
+ ),
427
+ /* @__PURE__ */ jsxRuntime.jsx(
428
+ DetailField,
429
+ {
430
+ label: "Atribuído a",
431
+ value: agent ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-gp-sm", children: [
432
+ /* @__PURE__ */ jsxRuntime.jsx(
433
+ appShell.Avatar,
434
+ {
435
+ className: "size-[22px]",
436
+ style: { background: agent.color },
437
+ children: /* @__PURE__ */ jsxRuntime.jsx(appShell.AvatarFallback, { className: "bg-transparent text-white text-[10px] font-bold", children: agentInitials })
438
+ }
439
+ ),
440
+ agent.name
441
+ ] }) : "—",
442
+ s
443
+ }
444
+ ),
445
+ /* @__PURE__ */ jsxRuntime.jsx(DetailField, { label: "Valor", value: clientesShowcaseMocks.formatBRL(row.value), s }),
446
+ /* @__PURE__ */ jsxRuntime.jsx(DetailField, { label: "Criado em", value: formatLongDate(row.createdAt), s }),
447
+ /* @__PURE__ */ jsxRuntime.jsx(
448
+ DetailField,
449
+ {
450
+ label: "Último contato",
451
+ value: clientesShowcaseMocks.formatShortDate(row.lastContact),
452
+ s
453
+ }
454
+ )
455
+ ]
456
+ }
457
+ ),
458
+ /* @__PURE__ */ jsxRuntime.jsx(
459
+ DetailSection,
460
+ {
461
+ id: "notes",
462
+ title: "Anotações",
463
+ open: openSections.has("notes"),
464
+ onToggle: toggle,
465
+ s,
466
+ children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[12.5px] text-fg-muted", children: "Sem anotações." })
467
+ }
468
+ )
469
+ ]
470
+ }
471
+ );
472
+ }
473
+ function buildColumns(handlers) {
474
+ return [
475
+ { field: "id", headerName: "ID", type: "text", width: 130 },
476
+ {
477
+ field: "name",
478
+ headerName: "Nome",
479
+ type: "user",
480
+ sortable: true,
481
+ width: 220,
482
+ typeOptions: {
483
+ users: Object.fromEntries(
484
+ clientesShowcaseMocks.CLIENTS_87.map((c) => [
485
+ c.name,
486
+ {
487
+ name: c.name,
488
+ initials: c.initials,
489
+ color: c.avatarColor
490
+ }
491
+ ])
492
+ )
493
+ },
494
+ valueGetter: (row) => row.name
495
+ },
496
+ {
497
+ field: "email",
498
+ headerName: "Email",
499
+ type: "email",
500
+ enableColumnFilter: true,
501
+ filterType: "text"
502
+ },
503
+ {
504
+ field: "phone",
505
+ headerName: "Telefone",
506
+ type: "phone"
507
+ },
508
+ {
509
+ field: "statusId",
510
+ headerName: "Status",
511
+ type: "status",
512
+ enableColumnFilter: true,
513
+ filterType: "multiSelect",
514
+ filterOptions: clientesShowcaseMocks.STATUS_OPTIONS
515
+ },
516
+ {
517
+ field: "categoryId",
518
+ headerName: "Categoria",
519
+ type: "badge",
520
+ enableColumnFilter: true,
521
+ filterType: "multiSelect",
522
+ filterOptions: clientesShowcaseMocks.CATEGORY_OPTIONS
523
+ },
524
+ {
525
+ field: "agentId",
526
+ headerName: "Atribuído",
527
+ type: "user",
528
+ enableColumnFilter: true,
529
+ filterType: "select",
530
+ filterOptions: clientesShowcaseMocks.AGENT_OPTIONS,
531
+ typeOptions: { users: clientesShowcaseMocks.AGENTS_LOOKUP }
532
+ },
533
+ {
534
+ field: "value",
535
+ headerName: "Valor",
536
+ type: "currency",
537
+ sortable: true,
538
+ enableColumnFilter: true,
539
+ filterType: "number",
540
+ aggregate: "sum",
541
+ aggregateFormatter: (v) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(v)
542
+ },
543
+ {
544
+ field: "createdAt",
545
+ headerName: "Criado em",
546
+ type: "date",
547
+ sortable: true,
548
+ enableColumnFilter: true,
549
+ filterType: "date",
550
+ width: 130
551
+ },
552
+ {
553
+ field: "lastContact",
554
+ headerName: "Último contato",
555
+ type: "date",
556
+ sortable: true,
557
+ width: 150
558
+ },
559
+ {
560
+ field: "location",
561
+ headerName: "Localização",
562
+ type: "text",
563
+ minWidth: 150
564
+ },
565
+ {
566
+ field: "_actions",
567
+ headerName: "",
568
+ type: "actions",
569
+ width: 64,
570
+ pinned: "right",
571
+ getActions: ({ row }) => [
572
+ {
573
+ id: "edit",
574
+ label: "Editar",
575
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, {}),
576
+ showInMenu: true,
577
+ onClick: () => console.log("Editar", row.id)
578
+ },
579
+ {
580
+ id: "whatsapp",
581
+ label: "Abrir atendimento (WhatsApp)",
582
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, {}),
583
+ showInMenu: true,
584
+ onClick: () => console.log("Atendimento", row.id)
585
+ },
586
+ {
587
+ id: "archive",
588
+ label: "Arquivar",
589
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Archive, {}),
590
+ showInMenu: true,
591
+ onClick: () => console.log("Arquivar", row.id)
592
+ },
593
+ {
594
+ id: "delete",
595
+ label: "Excluir cliente",
596
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, {}),
597
+ showInMenu: true,
598
+ destructive: true,
599
+ onClick: () => handlers.onAskDelete(row)
600
+ }
601
+ ]
602
+ }
603
+ ];
604
+ }
605
+ const DEFAULT_VIEWS = [
606
+ panel.presetView({
607
+ id: "preset:meus",
608
+ name: "Meus",
609
+ filters: [{ field: "agentId", value: "you" }]
610
+ }),
611
+ panel.presetView({
612
+ id: "preset:ativos",
613
+ name: "Ativos",
614
+ filters: [{ field: "statusId", value: "active" }]
615
+ }),
616
+ panel.presetView({
617
+ id: "preset:royals",
618
+ name: "Royals",
619
+ filters: [{ field: "categoryId", value: "royal" }]
620
+ }),
621
+ panel.presetView({
622
+ id: "preset:ultimos-7-dias",
623
+ name: "Últimos 7 dias",
624
+ sort: [{ field: "createdAt", direction: "desc" }]
625
+ })
626
+ ];
627
+ const INITIAL_FILTERS = {
628
+ items: [
629
+ { id: "filter-status-ativo", field: "statusId", operator: "equals", value: "active" },
630
+ { id: "filter-categoria-royal", field: "categoryId", operator: "equals", value: "royal" }
631
+ ],
632
+ logicOperator: "AND"
633
+ };
634
+ const KANBAN_CONFIG = {
635
+ groupByField: "statusId",
636
+ columns: clientesShowcaseMocks.KANBAN_COLUMNS,
637
+ renderCard: ({ row }) => {
638
+ const cat = clientesShowcaseMocks.CATEGORIES[row.categoryId];
639
+ const agent = clientesShowcaseMocks.AGENTS[row.agentId];
640
+ const agentInitials = agent?.name.split(" ").map((s) => s[0]).join("").slice(0, 2) ?? "?";
641
+ return {
642
+ title: row.name,
643
+ subtitle: row.id,
644
+ avatar: /* @__PURE__ */ jsxRuntime.jsx(appShell.Avatar, { className: "size-[28px]", style: { background: row.avatarColor }, children: /* @__PURE__ */ jsxRuntime.jsx(appShell.AvatarFallback, { className: "bg-transparent text-white text-[11px] font-bold", children: row.initials }) }),
645
+ chip: cat ? /* @__PURE__ */ jsxRuntime.jsx(
646
+ chipGroup.Chip,
647
+ {
648
+ color: clientesShowcaseMocks.CATEGORY_KIND[row.categoryId] ?? "info",
649
+ variant: "soft",
650
+ size: "sm",
651
+ shape: "pill",
652
+ children: cat.label
653
+ }
654
+ ) : null,
655
+ value: clientesShowcaseMocks.formatBRL(row.value),
656
+ footerLeft: agent ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-gp-sm min-w-0 flex-1", children: [
657
+ /* @__PURE__ */ jsxRuntime.jsx(appShell.Avatar, { className: "size-[22px]", style: { background: agent.color }, children: /* @__PURE__ */ jsxRuntime.jsx(appShell.AvatarFallback, { className: "bg-transparent text-white text-[10px] font-bold", children: agentInitials }) }),
658
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[12px] text-fg-muted whitespace-nowrap overflow-hidden text-ellipsis", children: agent.name })
659
+ ] }) : null,
660
+ footerRight: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-[4px] text-[11.5px] text-fg-muted [font-variant-numeric:tabular-nums] shrink-0", children: [
661
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { size: 11, strokeWidth: 1.8, "aria-hidden": true }),
662
+ clientesShowcaseMocks.formatShortDate(row.lastContact)
663
+ ] })
664
+ };
665
+ }
666
+ };
667
+ function ClientesShowcase() {
668
+ const { theme, setTheme } = useTheme.useTheme();
669
+ const [rows, setRows] = React.useState(() => clientesShowcaseMocks.CLIENTS_87);
670
+ const [layout, setLayout] = React.useState("fluid");
671
+ const [filterModel, setFilterModel] = React.useState(INITIAL_FILTERS);
672
+ const [viewMode, setViewMode] = React.useState("table");
673
+ const [novoClienteOpen, setNovoClienteOpen] = React.useState(false);
674
+ const [detailRowId, setDetailRowId] = React.useState(null);
675
+ const [confirmDelete, setConfirmDelete] = React.useState(null);
676
+ const detailRow = detailRowId ? rows.find((r) => r.id === detailRowId) ?? null : null;
677
+ const tableRef = React.useRef(null);
678
+ const askDelete = React.useCallback(
679
+ (row) => setConfirmDelete({ ids: [row.id], label: row.name }),
680
+ []
681
+ );
682
+ const askBulkDelete = React.useCallback(
683
+ (ids, onAfter) => setConfirmDelete({
684
+ ids,
685
+ label: `${ids.length} cliente${ids.length === 1 ? "" : "s"}`,
686
+ onAfter
687
+ }),
688
+ []
689
+ );
690
+ const handleConfirmDelete = React.useCallback(() => {
691
+ if (!confirmDelete) return;
692
+ const ids = new Set(confirmDelete.ids);
693
+ setRows((prev) => prev.filter((r) => !ids.has(r.id)));
694
+ if (detailRowId && ids.has(detailRowId)) setDetailRowId(null);
695
+ confirmDelete.onAfter?.();
696
+ setConfirmDelete(null);
697
+ }, [confirmDelete, detailRowId]);
698
+ const columns = React.useMemo(
699
+ () => buildColumns({ onAskDelete: askDelete }),
700
+ [askDelete]
701
+ );
702
+ return /* @__PURE__ */ jsxRuntime.jsxs(
703
+ appShell.AppShell,
704
+ {
705
+ contexts: appShellMocks.APP_SHELL_CONTEXTS,
706
+ defaultActiveContextId: "inbox",
707
+ defaultActiveItemHref: "#atendimentos",
708
+ breadcrumb: [{ label: "Atendimentos" }],
709
+ commandGroups: appShellMocks.APP_SHELL_COMMANDS,
710
+ notifications: {
711
+ items: appShellMocks.APP_SHELL_NOTIFICATIONS,
712
+ onMarkAllRead: () => alert("Marcar todas como lidas"),
713
+ onMoreActions: () => alert("Mais ações"),
714
+ onViewAll: () => alert("Ver todas")
715
+ },
716
+ messages: {
717
+ items: appShellMocks.APP_SHELL_MESSAGES,
718
+ onNewMessage: () => alert("Nova mensagem"),
719
+ onExpand: () => alert("Expandir"),
720
+ onViewAll: () => alert("Ver todas")
721
+ },
722
+ theme,
723
+ onThemeChange: (id) => setTheme(id),
724
+ themeOptions: appShellMocks.APP_SHELL_THEME_OPTIONS,
725
+ user: appShellMocks.APP_SHELL_USER,
726
+ layout,
727
+ onLayoutChange: setLayout,
728
+ layoutOptions: appShellMocks.APP_SHELL_LAYOUT_OPTIONS,
729
+ onSettings: () => alert("Abrir Configurações"),
730
+ onLogout: () => alert("Sair (mock)"),
731
+ children: [
732
+ /* @__PURE__ */ jsxRuntime.jsx(
733
+ pageHeader.PageHeader,
734
+ {
735
+ title: "Clientes",
736
+ description: "Gerencie sua base de clientes, acompanhe status de cadastro e abra atendimentos diretos pelo WhatsApp.",
737
+ badge: /* @__PURE__ */ jsxRuntime.jsxs(chipGroup.Chip, { color: "primary", variant: "soft", size: "sm", shape: "rounded", children: [
738
+ rows.length.toLocaleString("pt-BR"),
739
+ " registros"
740
+ ] }),
741
+ actions: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
742
+ /* @__PURE__ */ jsxRuntime.jsx(
743
+ chipGroup.Button,
744
+ {
745
+ variant: "outline",
746
+ color: "secondary",
747
+ size: "icon-md",
748
+ "aria-label": "Mais ações",
749
+ onClick: () => alert("Mais ações"),
750
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreHorizontal, {})
751
+ }
752
+ ),
753
+ /* @__PURE__ */ jsxRuntime.jsx(
754
+ chipGroup.Button,
755
+ {
756
+ variant: "filled",
757
+ color: "primary",
758
+ size: "md",
759
+ iconLeft: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
760
+ onClick: () => setNovoClienteOpen(true),
761
+ children: "Novo cliente"
762
+ }
763
+ )
764
+ ] })
765
+ }
766
+ ),
767
+ /* @__PURE__ */ jsxRuntime.jsx(
768
+ panel.DataTable,
769
+ {
770
+ ref: tableRef,
771
+ rows,
772
+ columns,
773
+ getRowId: (r) => r.id,
774
+ persistId: "showcase-clientes-crud",
775
+ defaultViews: DEFAULT_VIEWS,
776
+ filterModel,
777
+ onFilterModelChange: setFilterModel,
778
+ viewMode,
779
+ onViewModeChange: setViewMode,
780
+ kanbanConfig: KANBAN_CONFIG,
781
+ toolbar: {
782
+ enableSearch: true,
783
+ enableFilters: true,
784
+ enableColumns: true,
785
+ enableDensity: true,
786
+ enableExport: true,
787
+ moreMenu: {
788
+ items: [
789
+ {
790
+ id: "fullscreen",
791
+ label: "Tela cheia",
792
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2, {}),
793
+ onSelect: () => alert("Tela cheia")
794
+ },
795
+ {
796
+ id: "duplicate",
797
+ label: "Duplicar visualização",
798
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, {}),
799
+ onSelect: () => alert("Duplicar visualização")
800
+ },
801
+ {
802
+ id: "import",
803
+ label: "Importar CSV",
804
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, {}),
805
+ onSelect: () => alert("Importar CSV")
806
+ },
807
+ {
808
+ id: "reset",
809
+ label: "Resetar visualização",
810
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, {}),
811
+ destructive: true,
812
+ onSelect: () => tableRef.current?.resetPersistedState()
813
+ }
814
+ ]
815
+ }
816
+ },
817
+ paginationConfig: {
818
+ enabled: true,
819
+ initialPageSize: 10,
820
+ pageSizeOptions: [10, 25, 50, 100]
821
+ },
822
+ selectionConfig: {
823
+ enabled: true,
824
+ enableGlobal: true,
825
+ actions: (selectedIds, clearSelection) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
826
+ /* @__PURE__ */ jsxRuntime.jsx(
827
+ panel.BulkActionButton,
828
+ {
829
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, {}),
830
+ onClick: () => tableRef.current?.exportCsv("selected"),
831
+ children: "Exportar"
832
+ }
833
+ ),
834
+ /* @__PURE__ */ jsxRuntime.jsx(
835
+ panel.BulkActionButton,
836
+ {
837
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, {}),
838
+ variant: "danger",
839
+ onClick: () => askBulkDelete(selectedIds.map(String), clearSelection),
840
+ children: "Excluir"
841
+ }
842
+ )
843
+ ] })
844
+ },
845
+ onCellEditCommit: async ({ id, field, value }) => {
846
+ await new Promise((res) => setTimeout(res, 400));
847
+ setRows(
848
+ (prev) => prev.map((r) => r.id === id ? { ...r, [field]: value } : r)
849
+ );
850
+ },
851
+ onRowClick: (row) => setDetailRowId(row.id),
852
+ className: "flex-1 min-h-0"
853
+ }
854
+ ),
855
+ /* @__PURE__ */ jsxRuntime.jsx(
856
+ NovoClienteDrawer,
857
+ {
858
+ open: novoClienteOpen,
859
+ onOpenChange: setNovoClienteOpen,
860
+ onSubmit: (data) => {
861
+ console.log("Novo cliente:", data);
862
+ alert(`Cliente "${data.name}" criado (mock)`);
863
+ }
864
+ }
865
+ ),
866
+ /* @__PURE__ */ jsxRuntime.jsx(
867
+ DetailDrawer,
868
+ {
869
+ row: detailRow,
870
+ onClose: () => setDetailRowId(null),
871
+ onEdit: (r) => console.log("Editar", r.id),
872
+ onDelete: (r) => askDelete(r),
873
+ onSave: (r) => alert(`Salvar alterações em ${r.name} (mock)`)
874
+ }
875
+ ),
876
+ /* @__PURE__ */ jsxRuntime.jsx(
877
+ panel.AlertModal,
878
+ {
879
+ open: confirmDelete !== null,
880
+ onOpenChange: (open) => {
881
+ if (!open) setConfirmDelete(null);
882
+ },
883
+ tone: "danger",
884
+ title: confirmDelete && confirmDelete.ids.length > 1 ? `Excluir ${confirmDelete.label}?` : "Excluir cliente?",
885
+ description: confirmDelete ? confirmDelete.ids.length > 1 ? `Esta ação não pode ser desfeita. ${confirmDelete.label} serão removidos permanentemente.` : `Esta ação não pode ser desfeita. "${confirmDelete.label}" será removido permanentemente.` : null,
886
+ confirmLabel: "Excluir",
887
+ cancelLabel: "Cancelar",
888
+ onConfirm: handleConfirmDelete
889
+ }
890
+ )
891
+ ]
892
+ }
893
+ );
894
+ }
895
+ exports.ClientesShowcase = ClientesShowcase;
896
+ exports.default = ClientesShowcase;
897
+ //# sourceMappingURL=clientes.cjs.map