@snksergio/design-system 0.61.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 (193) 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/AlertModal.md +47 -0
  7. package/dist-lib/ai/componentes/AppShell.md +117 -0
  8. package/dist-lib/ai/componentes/Breadcrumb.md +187 -0
  9. package/dist-lib/ai/componentes/Button.md +116 -0
  10. package/dist-lib/ai/componentes/ButtonGroup.md +162 -0
  11. package/dist-lib/ai/componentes/CardCheckbox.md +99 -0
  12. package/dist-lib/ai/componentes/CardOption.md +133 -0
  13. package/dist-lib/ai/componentes/Chart.md +93 -0
  14. package/dist-lib/ai/componentes/Chip.md +68 -0
  15. package/dist-lib/ai/componentes/ChoroplethMap.md +118 -0
  16. package/dist-lib/ai/componentes/ColorPicker.md +70 -0
  17. package/dist-lib/ai/componentes/Combobox.md +51 -0
  18. package/dist-lib/ai/componentes/ConversationListItem.md +90 -0
  19. package/dist-lib/ai/componentes/DataList.md +111 -0
  20. package/dist-lib/ai/componentes/DataTable.md +867 -0
  21. package/dist-lib/ai/componentes/DatePicker.md +84 -0
  22. package/dist-lib/ai/componentes/DateSeparatorChip.md +59 -0
  23. package/dist-lib/ai/componentes/EmptyState.md +72 -0
  24. package/dist-lib/ai/componentes/FileUploadField.md +95 -0
  25. package/dist-lib/ai/componentes/FloatingPanel.md +118 -0
  26. package/dist-lib/ai/componentes/FooterTable.md +62 -0
  27. package/dist-lib/ai/componentes/FormField.md +110 -0
  28. package/dist-lib/ai/componentes/Gantt.md +552 -0
  29. package/dist-lib/ai/componentes/Header.md +98 -0
  30. package/dist-lib/ai/componentes/Icon.md +65 -0
  31. package/dist-lib/ai/componentes/Kanban.md +343 -0
  32. package/dist-lib/ai/componentes/Kpi.md +103 -0
  33. package/dist-lib/ai/componentes/List.md +61 -0
  34. package/dist-lib/ai/componentes/MarkdownText.md +59 -0
  35. package/dist-lib/ai/componentes/MenuSidebar.md +128 -0
  36. package/dist-lib/ai/componentes/MessageAck.md +53 -0
  37. package/dist-lib/ai/componentes/MessageBubble.md +115 -0
  38. package/dist-lib/ai/componentes/MessageComposer.md +80 -0
  39. package/dist-lib/ai/componentes/MessageVariablesPicker.md +104 -0
  40. package/dist-lib/ai/componentes/Modal.md +88 -0
  41. package/dist-lib/ai/componentes/MonthYearPicker.md +49 -0
  42. package/dist-lib/ai/componentes/PageHeader.md +129 -0
  43. package/dist-lib/ai/componentes/Panel.md +84 -0
  44. package/dist-lib/ai/componentes/Scheduler.md +421 -0
  45. package/dist-lib/ai/componentes/ScreenLoader.md +60 -0
  46. package/dist-lib/ai/componentes/SingleMenuSidebar.md +171 -0
  47. package/dist-lib/ai/componentes/Spinner.md +52 -0
  48. package/dist-lib/ai/componentes/Table.md +192 -0
  49. package/dist-lib/ai/componentes/TableToolbar.md +87 -0
  50. package/dist-lib/ai/componentes/TabsNavigation.md +152 -0
  51. package/dist-lib/ai/componentes/Toast.md +49 -0
  52. package/dist-lib/ai/componentes/_primitivos.md +74 -0
  53. package/dist-lib/ai/componentes/avatar-ig.md +181 -0
  54. package/dist-lib/ai/componentes/indice.json +49 -0
  55. package/dist-lib/ai/exemplos/app-shell/app-shell-example.tsx +140 -0
  56. package/dist-lib/ai/exemplos/app-shell/index.ts +3 -0
  57. package/dist-lib/ai/exemplos/app-shell/nav-data.ts +97 -0
  58. package/dist-lib/ai/exemplos/app-shell/routes.tsx +75 -0
  59. package/dist-lib/ai/exemplos/chat/chat-screen.tsx +152 -0
  60. package/dist-lib/ai/exemplos/chat/chat-v2-mocks.ts +171 -0
  61. package/dist-lib/ai/exemplos/chat/chat-v2.styles.ts +23 -0
  62. package/dist-lib/ai/exemplos/chat/chat-v2.types.ts +81 -0
  63. package/dist-lib/ai/exemplos/chat/components/ChannelDot/channel-dot.tsx +28 -0
  64. package/dist-lib/ai/exemplos/chat/components/ConversationActionsMenu/conversation-actions-menu.tsx +83 -0
  65. package/dist-lib/ai/exemplos/chat/components/ConversationActionsMenu/index.ts +4 -0
  66. package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.styles.ts +22 -0
  67. package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.tsx +203 -0
  68. package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.types.ts +10 -0
  69. package/dist-lib/ai/exemplos/chat/components/ConversationColumn/index.ts +2 -0
  70. package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.styles.ts +55 -0
  71. package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.tsx +52 -0
  72. package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.types.ts +7 -0
  73. package/dist-lib/ai/exemplos/chat/components/ConversationListItem/index.ts +2 -0
  74. package/dist-lib/ai/exemplos/chat/components/DateSeparator/date-separator.tsx +18 -0
  75. package/dist-lib/ai/exemplos/chat/components/DetailField/detail-field.tsx +23 -0
  76. package/dist-lib/ai/exemplos/chat/components/DetailSection/detail-section.tsx +41 -0
  77. package/dist-lib/ai/exemplos/chat/components/DetailSection/index.ts +1 -0
  78. package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.styles.ts +30 -0
  79. package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.tsx +142 -0
  80. package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.types.ts +16 -0
  81. package/dist-lib/ai/exemplos/chat/components/DetailsColumn/index.ts +5 -0
  82. package/dist-lib/ai/exemplos/chat/components/FilterRow/filter-row.styles.ts +37 -0
  83. package/dist-lib/ai/exemplos/chat/components/FilterRow/filter-row.tsx +45 -0
  84. package/dist-lib/ai/exemplos/chat/components/FilterRow/index.ts +1 -0
  85. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.styles.ts +27 -0
  86. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.tsx +66 -0
  87. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.types.ts +13 -0
  88. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-rail.tsx +38 -0
  89. package/dist-lib/ai/exemplos/chat/components/FiltersColumn/index.ts +6 -0
  90. package/dist-lib/ai/exemplos/chat/components/MessageBubble/index.ts +2 -0
  91. package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.styles.ts +37 -0
  92. package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.tsx +29 -0
  93. package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.types.ts +5 -0
  94. package/dist-lib/ai/exemplos/chat/components/PersonAvatar/index.ts +5 -0
  95. package/dist-lib/ai/exemplos/chat/components/PersonAvatar/person-avatar.tsx +31 -0
  96. package/dist-lib/ai/exemplos/chat/components/QueueColumn/index.ts +2 -0
  97. package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.styles.ts +19 -0
  98. package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.tsx +140 -0
  99. package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.types.ts +13 -0
  100. package/dist-lib/ai/exemplos/chat/components/RailItem/index.ts +1 -0
  101. package/dist-lib/ai/exemplos/chat/components/RailItem/rail-item.styles.ts +30 -0
  102. package/dist-lib/ai/exemplos/chat/components/RailItem/rail-item.tsx +42 -0
  103. package/dist-lib/ai/exemplos/chat/hooks/use-resizable.ts +98 -0
  104. package/dist-lib/ai/exemplos/chat/index.ts +1 -0
  105. package/dist-lib/ai/exemplos/clientes/_table-data.ts +59 -0
  106. package/dist-lib/ai/exemplos/clientes/clientes-screen.tsx +505 -0
  107. package/dist-lib/ai/exemplos/clientes/clientes-showcase-mocks.ts +117 -0
  108. package/dist-lib/ai/exemplos/clientes/clientes-showcase.styles.ts +13 -0
  109. package/dist-lib/ai/exemplos/clientes/clientes-showcase.types.ts +16 -0
  110. package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.styles.ts +28 -0
  111. package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.tsx +303 -0
  112. package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.types.ts +14 -0
  113. package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/index.ts +2 -0
  114. package/dist-lib/ai/exemplos/clientes/components/NovoClienteDrawer/index.ts +2 -0
  115. package/dist-lib/ai/exemplos/clientes/components/NovoClienteDrawer/novo-cliente-drawer.tsx +207 -0
  116. package/dist-lib/ai/exemplos/clientes/index.ts +1 -0
  117. package/dist-lib/ai/exemplos/dashboard/dashboard-brazil-map.ts +33 -0
  118. package/dist-lib/ai/exemplos/dashboard/dashboard-screen.tsx +1110 -0
  119. package/dist-lib/ai/exemplos/dashboard/index.ts +1 -0
  120. package/dist-lib/ai/exemplos/edit-page/components/StepNav.tsx +81 -0
  121. package/dist-lib/ai/exemplos/edit-page/components/section-card.tsx +85 -0
  122. package/dist-lib/ai/exemplos/edit-page/edit-page-screen.tsx +234 -0
  123. package/dist-lib/ai/exemplos/edit-page/index.ts +1 -0
  124. package/dist-lib/ai/exemplos/finance/_table-data.ts +57 -0
  125. package/dist-lib/ai/exemplos/finance/clientes-financeiro-mocks.ts +227 -0
  126. package/dist-lib/ai/exemplos/finance/clientes-financeiro.types.ts +75 -0
  127. package/dist-lib/ai/exemplos/finance/clientes-showcase-mocks.ts +117 -0
  128. package/dist-lib/ai/exemplos/finance/clientes-showcase.styles.ts +13 -0
  129. package/dist-lib/ai/exemplos/finance/clientes-showcase.types.ts +16 -0
  130. package/dist-lib/ai/exemplos/finance/components/EditarFinanceDrawer/editar-finance-drawer.tsx +241 -0
  131. package/dist-lib/ai/exemplos/finance/components/EditarFinanceDrawer/index.ts +5 -0
  132. package/dist-lib/ai/exemplos/finance/components/ExtratoExpansion/extrato-expansion.tsx +172 -0
  133. package/dist-lib/ai/exemplos/finance/components/ExtratoExpansion/index.ts +1 -0
  134. package/dist-lib/ai/exemplos/finance/components/FinanceDetailPanel/finance-detail-panel.tsx +241 -0
  135. package/dist-lib/ai/exemplos/finance/components/FinanceDetailPanel/index.ts +2 -0
  136. package/dist-lib/ai/exemplos/finance/components/NovoClienteDrawer/index.ts +2 -0
  137. package/dist-lib/ai/exemplos/finance/components/NovoClienteDrawer/novo-cliente-drawer.tsx +207 -0
  138. package/dist-lib/ai/exemplos/finance/components/SacarDialog/index.ts +2 -0
  139. package/dist-lib/ai/exemplos/finance/components/SacarDialog/sacar-dialog.tsx +346 -0
  140. package/dist-lib/ai/exemplos/finance/finance-screen.tsx +821 -0
  141. package/dist-lib/ai/exemplos/finance/index.ts +1 -0
  142. package/dist-lib/ai/exemplos/gantt/_gantt-data.tsx +402 -0
  143. package/dist-lib/ai/exemplos/gantt/gantt-screen.tsx +512 -0
  144. package/dist-lib/ai/exemplos/gantt/index.ts +1 -0
  145. package/dist-lib/ai/exemplos/login/index.ts +1 -0
  146. package/dist-lib/ai/exemplos/login/login-screen.tsx +246 -0
  147. package/dist-lib/ai/exemplos/mapa-rede/components/ConsultorDetailPanel/consultor-detail-panel.tsx +158 -0
  148. package/dist-lib/ai/exemplos/mapa-rede/components/ConsultorDetailPanel/index.ts +2 -0
  149. package/dist-lib/ai/exemplos/mapa-rede/index.ts +1 -0
  150. package/dist-lib/ai/exemplos/mapa-rede/mapa-de-rede-mocks.ts +655 -0
  151. package/dist-lib/ai/exemplos/mapa-rede/mapa-de-rede.types.ts +53 -0
  152. package/dist-lib/ai/exemplos/mapa-rede/mapa-rede-screen.tsx +227 -0
  153. package/dist-lib/ai/exemplos/order-detail/components/ActivityTab.tsx +77 -0
  154. package/dist-lib/ai/exemplos/order-detail/components/AttachmentsTab.tsx +45 -0
  155. package/dist-lib/ai/exemplos/order-detail/components/CommentsTab.tsx +111 -0
  156. package/dist-lib/ai/exemplos/order-detail/components/DetailsTab.tsx +189 -0
  157. package/dist-lib/ai/exemplos/order-detail/components/OverviewTab.tsx +200 -0
  158. package/dist-lib/ai/exemplos/order-detail/components/section-card.tsx +85 -0
  159. package/dist-lib/ai/exemplos/order-detail/index.ts +1 -0
  160. package/dist-lib/ai/exemplos/order-detail/order-detail-screen.tsx +119 -0
  161. package/dist-lib/ai/exemplos/order-detail/order-mocks.ts +186 -0
  162. package/dist-lib/ai/exemplos/order-detail/order.types.ts +118 -0
  163. package/dist-lib/ai/global/componentes.md +217 -0
  164. package/dist-lib/ai/global/composicao.md +182 -0
  165. package/dist-lib/ai/indice.json +192 -0
  166. package/dist-lib/ai/lint/ds-lint-patterns.mjs +115 -0
  167. package/dist-lib/ai/manifest.json +42 -0
  168. package/dist-lib/ai/regras/design.md +88 -0
  169. package/dist-lib/ai/regras/temas.md +192 -0
  170. package/dist-lib/ai/regras-por-componente.json +103 -0
  171. package/dist-lib/ai/roteiros/app-builder/roteiro.md +155 -0
  172. package/dist-lib/ai/roteiros/auth-builder/roteiro.md +42 -0
  173. package/dist-lib/ai/roteiros/cards/roteiro.md +34 -0
  174. package/dist-lib/ai/roteiros/charts/roteiro.md +32 -0
  175. package/dist-lib/ai/roteiros/chat/roteiro.md +31 -0
  176. package/dist-lib/ai/roteiros/crud-builder/blueprint.md +63 -0
  177. package/dist-lib/ai/roteiros/crud-builder/entrevista.md +139 -0
  178. package/dist-lib/ai/roteiros/crud-builder/geracao.md +113 -0
  179. package/dist-lib/ai/roteiros/crud-builder/roteiro.md +89 -0
  180. package/dist-lib/ai/roteiros/dashboard-builder/blueprint.md +47 -0
  181. package/dist-lib/ai/roteiros/dashboard-builder/entrevista.md +62 -0
  182. package/dist-lib/ai/roteiros/dashboard-builder/geracao.md +88 -0
  183. package/dist-lib/ai/roteiros/dashboard-builder/roteiro.md +88 -0
  184. package/dist-lib/ai/roteiros/drawers/roteiro.md +41 -0
  185. package/dist-lib/ai/roteiros/list-builder/blueprint.md +94 -0
  186. package/dist-lib/ai/roteiros/list-builder/entrevista.md +188 -0
  187. package/dist-lib/ai/roteiros/list-builder/geracao.md +63 -0
  188. package/dist-lib/ai/roteiros/list-builder/roteiro.md +91 -0
  189. package/dist-lib/ai/roteiros/module-replicator/roteiro.md +56 -0
  190. package/dist-lib/ai/roteiros/page-detail/roteiro.md +32 -0
  191. package/dist-lib/ai/roteiros/page-edit/roteiro.md +31 -0
  192. package/dist-lib/ai/roteiros/screen-composer/roteiro.md +95 -0
  193. package/package.json +4 -1
@@ -0,0 +1,303 @@
1
+ import { useState, type ReactNode } from "react";
2
+ import { ChevronDown, Pencil, Trash2 } from "lucide-react";
3
+ import { Avatar, AvatarFallback } from "@/components/shadcn/avatar";
4
+ import { Button } from "@/components/ui/Button/button";
5
+ import { Chip } from "@/components/ui/Chip";
6
+ import { FloatingPanel } from "@/components/ui/FloatingPanel";
7
+ import { STATUSES, CATEGORIES, AGENTS } from "../../_table-data";
8
+ import {
9
+ CATEGORY_KIND,
10
+ STATUS_DOT,
11
+ formatBRL,
12
+ formatShortDate,
13
+ } from "../../clientes-showcase-mocks";
14
+ import { detailDrawerStyles } from "./detail-drawer.styles";
15
+ import type { DetailDrawerProps } from "./detail-drawer.types";
16
+
17
+ /* ── Helpers internos ────────────────────────────────────────────── */
18
+
19
+ function formatLongDate(ms: number): string {
20
+ if (!ms) return "—";
21
+ const d = new Date(ms);
22
+ return d.toLocaleDateString("pt-BR", {
23
+ day: "numeric",
24
+ month: "long",
25
+ year: "numeric",
26
+ });
27
+ }
28
+
29
+ type SectionId = "contact" | "meta" | "notes";
30
+
31
+ /* ── DetailField (label : value) ────────────────────────────────── */
32
+
33
+ type DetailFieldProps = {
34
+ label: string;
35
+ value: ReactNode;
36
+ s: ReturnType<typeof detailDrawerStyles>;
37
+ };
38
+
39
+ function DetailField({ label, value, s }: DetailFieldProps) {
40
+ return (
41
+ <div className={s.field()}>
42
+ <span className={s.fieldLabel()}>{label}</span>
43
+ <span className={s.fieldValue()}>{value || "—"}</span>
44
+ </div>
45
+ );
46
+ }
47
+
48
+ /* ── DetailSection (collapsible) ─────────────────────────────────── */
49
+
50
+ type DetailSectionProps = {
51
+ id: SectionId;
52
+ title: string;
53
+ open: boolean;
54
+ onToggle: (id: SectionId) => void;
55
+ children: ReactNode;
56
+ s: ReturnType<typeof detailDrawerStyles>;
57
+ };
58
+
59
+ function DetailSection({
60
+ id,
61
+ title,
62
+ open,
63
+ onToggle,
64
+ children,
65
+ s,
66
+ }: DetailSectionProps) {
67
+ return (
68
+ <section className={s.section()}>
69
+ <button
70
+ type="button"
71
+ className={s.sectionHead()}
72
+ onClick={() => onToggle(id)}
73
+ aria-expanded={open}
74
+ >
75
+ <span>{title}</span>
76
+ <ChevronDown
77
+ className={[s.sectionChev(), open ? s.sectionChevOpen() : ""].join(" ")}
78
+ />
79
+ </button>
80
+ {open && <div className={s.sectionBody()}>{children}</div>}
81
+ </section>
82
+ );
83
+ }
84
+
85
+ /* ── DetailDrawer ────────────────────────────────────────────────── */
86
+
87
+ /**
88
+ * Drawer "Detalhes do cliente" — usa `<FloatingPanel>` do DS (non-modal,
89
+ * resizable, maximizable, responsive). Replica visual do `TblDetailsPanel`
90
+ * do sandbox /design-and-table-v2.
91
+ */
92
+ export function DetailDrawer({
93
+ row,
94
+ onClose,
95
+ onEdit,
96
+ onDelete,
97
+ onSave,
98
+ }: DetailDrawerProps) {
99
+ const s = detailDrawerStyles();
100
+ const [openSections, setOpenSections] = useState<Set<SectionId>>(
101
+ () => new Set(["contact", "meta", "notes"]),
102
+ );
103
+
104
+ const toggle = (id: SectionId) => {
105
+ setOpenSections((prev) => {
106
+ const next = new Set(prev);
107
+ if (next.has(id)) next.delete(id);
108
+ else next.add(id);
109
+ return next;
110
+ });
111
+ };
112
+
113
+ // Mantém ordem dos hooks estável independente de `row` — depois do toggle.
114
+ if (!row) return null;
115
+
116
+ const status = STATUSES[row.statusId];
117
+ const cat = CATEGORIES[row.categoryId as keyof typeof CATEGORIES];
118
+ const agent = AGENTS[row.agentId as keyof typeof AGENTS];
119
+ const agentInitials =
120
+ agent?.name.split(" ").map((p) => p[0]).join("").slice(0, 2) ?? "?";
121
+
122
+ return (
123
+ <FloatingPanel
124
+ open={row !== null}
125
+ onOpenChange={(open) => {
126
+ if (!open) onClose();
127
+ }}
128
+ side="right"
129
+ size="md"
130
+ resizable
131
+ maximizable
132
+ // Sections internas (DetailSection) gerenciam o próprio padding edge-to-edge.
133
+ bodyPadded={false}
134
+ resizableStorageKey="clientes-showcase.detail-drawer.width"
135
+ titleSlot={
136
+ <div className="flex items-center gap-gp-md min-w-0">
137
+ <Avatar
138
+ className="size-[40px] shrink-0"
139
+ style={{ background: row.avatarColor }}
140
+ >
141
+ <AvatarFallback className="bg-transparent text-white text-body-md font-bold">
142
+ {row.initials}
143
+ </AvatarFallback>
144
+ </Avatar>
145
+ <div className="flex flex-col min-w-0 flex-1">
146
+ <div className="text-body-md font-semibold text-fg-default whitespace-nowrap overflow-hidden text-ellipsis">
147
+ {row.name}
148
+ </div>
149
+ <div className="flex items-center gap-[6px] mt-[2px] text-body-xs font-normal text-fg-muted">
150
+ <span>{row.id}</span>
151
+ <span className="opacity-50">·</span>
152
+ <span className="inline-flex items-center gap-[6px]">
153
+ <span
154
+ className="inline-block size-[8px] rounded-radius-full shrink-0"
155
+ style={{ background: STATUS_DOT[row.statusId] ?? status?.color }}
156
+ aria-hidden
157
+ />
158
+ {status?.label}
159
+ </span>
160
+ </div>
161
+ </div>
162
+ </div>
163
+ }
164
+ headerActions={
165
+ <>
166
+ <Button
167
+ variant="soft"
168
+ color="secondary"
169
+ size="icon-sm"
170
+ aria-label="Editar"
171
+ onClick={() => onEdit?.(row)}
172
+ >
173
+ <Pencil />
174
+ </Button>
175
+ <Button
176
+ variant="soft"
177
+ color="critical"
178
+ size="icon-sm"
179
+ aria-label="Excluir"
180
+ onClick={() => onDelete?.(row)}
181
+ >
182
+ <Trash2 />
183
+ </Button>
184
+ </>
185
+ }
186
+ footer={
187
+ <>
188
+ <Button variant="outline" color="secondary" size="sm" onClick={onClose}>
189
+ Cancelar
190
+ </Button>
191
+ <Button
192
+ variant="filled"
193
+ color="primary"
194
+ size="sm"
195
+ onClick={() => onSave?.(row)}
196
+ >
197
+ Salvar alterações
198
+ </Button>
199
+ </>
200
+ }
201
+ >
202
+ <DetailSection
203
+ id="contact"
204
+ title="Contato"
205
+ open={openSections.has("contact")}
206
+ onToggle={toggle}
207
+ s={s}
208
+ >
209
+ <DetailField
210
+ label="Email"
211
+ value={
212
+ <a
213
+ href={`mailto:${row.email}`}
214
+ className="text-fg-brand hover:underline"
215
+ >
216
+ {row.email}
217
+ </a>
218
+ }
219
+ s={s}
220
+ />
221
+ <DetailField
222
+ label="Telefone"
223
+ value={
224
+ <a
225
+ href={`tel:${row.phone.replace(/\D/g, "")}`}
226
+ className="text-fg-brand hover:underline"
227
+ >
228
+ {row.phone}
229
+ </a>
230
+ }
231
+ s={s}
232
+ />
233
+ <DetailField label="Localização" value={row.location} s={s} />
234
+ </DetailSection>
235
+
236
+ <DetailSection
237
+ id="meta"
238
+ title="Atendimento"
239
+ open={openSections.has("meta")}
240
+ onToggle={toggle}
241
+ s={s}
242
+ >
243
+ <DetailField label="ID" value={row.id} s={s} />
244
+ <DetailField
245
+ label="Categoria"
246
+ value={
247
+ cat ? (
248
+ <Chip
249
+ color={CATEGORY_KIND[row.categoryId] ?? "info"}
250
+ variant="soft"
251
+ size="sm"
252
+ shape="pill"
253
+ >
254
+ {cat.label}
255
+ </Chip>
256
+ ) : (
257
+ "—"
258
+ )
259
+ }
260
+ s={s}
261
+ />
262
+ <DetailField
263
+ label="Atribuído a"
264
+ value={
265
+ agent ? (
266
+ <span className="inline-flex items-center gap-gp-sm">
267
+ <Avatar
268
+ className="size-[22px]"
269
+ style={{ background: agent.color }}
270
+ >
271
+ <AvatarFallback className="bg-transparent text-white text-caption-xs font-bold">
272
+ {agentInitials}
273
+ </AvatarFallback>
274
+ </Avatar>
275
+ {agent.name}
276
+ </span>
277
+ ) : (
278
+ "—"
279
+ )
280
+ }
281
+ s={s}
282
+ />
283
+ <DetailField label="Valor" value={formatBRL(row.value)} s={s} />
284
+ <DetailField label="Criado em" value={formatLongDate(row.createdAt)} s={s} />
285
+ <DetailField
286
+ label="Último contato"
287
+ value={formatShortDate(row.lastContact)}
288
+ s={s}
289
+ />
290
+ </DetailSection>
291
+
292
+ <DetailSection
293
+ id="notes"
294
+ title="Anotações"
295
+ open={openSections.has("notes")}
296
+ onToggle={toggle}
297
+ s={s}
298
+ >
299
+ <p className="text-body-xs font-normal text-fg-muted">Sem anotações.</p>
300
+ </DetailSection>
301
+ </FloatingPanel>
302
+ );
303
+ }
@@ -0,0 +1,14 @@
1
+ import type { ClientRow } from "../../_table-data";
2
+
3
+ export type DetailDrawerProps = {
4
+ /** Cliente em exibição. Quando `null` o drawer fica desmontado. */
5
+ row: ClientRow | null;
6
+ /** Fecha o drawer (chamado por ESC, X do header e Cancelar). */
7
+ onClose: () => void;
8
+ /** Disparado pelo botão Editar (ícone Pencil no header). Opcional. */
9
+ onEdit?: (row: ClientRow) => void;
10
+ /** Disparado pelo botão Excluir (ícone Trash no header). Opcional. */
11
+ onDelete?: (row: ClientRow) => void;
12
+ /** Disparado pelo CTA "Salvar alterações" do footer. Opcional. */
13
+ onSave?: (row: ClientRow) => void;
14
+ };
@@ -0,0 +1,2 @@
1
+ export { DetailDrawer } from "./detail-drawer";
2
+ export type { DetailDrawerProps } from "./detail-drawer.types";
@@ -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 @@
1
+ export { ClientesScreen, default } from "./clientes-screen";