@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,83 @@
1
+ import {
2
+ Archive,
3
+ CheckCircle2,
4
+ MoreVertical,
5
+ Phone,
6
+ Tag,
7
+ UserPlus,
8
+ Video,
9
+ } from "lucide-react";
10
+ import { Button } from "@/components/ui/Button/button";
11
+ import {
12
+ DropdownMenu,
13
+ DropdownMenuContent,
14
+ DropdownMenuItem,
15
+ DropdownMenuSeparator,
16
+ DropdownMenuTrigger,
17
+ } from "@/components/shadcn/dropdown-menu";
18
+
19
+ export type ConversationActionsMenuProps = {
20
+ /** Quando true, adiciona "Resolver" no topo do dropdown (mobile compact). */
21
+ showResolveInMenu?: boolean;
22
+ onResolve?: () => void;
23
+ onCall?: () => void;
24
+ onVideo?: () => void;
25
+ onAddTag?: () => void;
26
+ onAssign?: () => void;
27
+ onArchive?: () => void;
28
+ };
29
+
30
+ /**
31
+ * Dropdown "..." do header da conversa com 5–6 ações (Ligar, Vídeo, Tag,
32
+ * Atribuir, Arquivar; opcionalmente Resolver no topo pra mobile).
33
+ */
34
+ export function ConversationActionsMenu({
35
+ showResolveInMenu = false,
36
+ onResolve,
37
+ onCall,
38
+ onVideo,
39
+ onAddTag,
40
+ onAssign,
41
+ onArchive,
42
+ }: ConversationActionsMenuProps) {
43
+ return (
44
+ <DropdownMenu>
45
+ <DropdownMenuTrigger asChild>
46
+ <Button
47
+ color="secondary"
48
+ variant="outline"
49
+ size="icon-sm"
50
+ aria-label="Mais opções"
51
+ >
52
+ <MoreVertical />
53
+ </Button>
54
+ </DropdownMenuTrigger>
55
+ <DropdownMenuContent align="end" sideOffset={6} className="min-w-[200px]">
56
+ {showResolveInMenu && (
57
+ <>
58
+ <DropdownMenuItem onSelect={onResolve}>
59
+ <CheckCircle2 /> Resolver
60
+ </DropdownMenuItem>
61
+ <DropdownMenuSeparator />
62
+ </>
63
+ )}
64
+ <DropdownMenuItem onSelect={onCall}>
65
+ <Phone /> Ligar
66
+ </DropdownMenuItem>
67
+ <DropdownMenuItem onSelect={onVideo}>
68
+ <Video /> Vídeo chamada
69
+ </DropdownMenuItem>
70
+ <DropdownMenuItem onSelect={onAddTag}>
71
+ <Tag /> Adicionar tag
72
+ </DropdownMenuItem>
73
+ <DropdownMenuItem onSelect={onAssign}>
74
+ <UserPlus /> Atribuir
75
+ </DropdownMenuItem>
76
+ <DropdownMenuSeparator />
77
+ <DropdownMenuItem variant="destructive" onSelect={onArchive}>
78
+ <Archive /> Arquivar
79
+ </DropdownMenuItem>
80
+ </DropdownMenuContent>
81
+ </DropdownMenu>
82
+ );
83
+ }
@@ -0,0 +1,4 @@
1
+ export {
2
+ ConversationActionsMenu,
3
+ type ConversationActionsMenuProps,
4
+ } from "./conversation-actions-menu";
@@ -0,0 +1,22 @@
1
+ import { tv } from "@/utils/tv";
2
+
3
+ export const conversationColumnStyles = tv({
4
+ slots: {
5
+ root: "flex flex-col flex-1 min-w-0 bg-bg-surface border border-border-subtle rounded-radius-xl overflow-hidden",
6
+ header:
7
+ "flex items-center gap-gp-lg px-pad-2xl py-pad-md border-b border-border-subtle shrink-0",
8
+ headerInfo: "flex-1 min-w-0 flex flex-col gap-[2px]",
9
+ headerTitleRow: "flex items-center gap-gp-sm",
10
+ headerName:
11
+ "text-title-md text-fg-default whitespace-nowrap overflow-hidden text-ellipsis",
12
+ headerMetaRow:
13
+ "flex flex-wrap items-center gap-x-gp-sm gap-y-[2px] text-caption-sm text-fg-muted min-w-0",
14
+ headerActions: "flex items-center gap-gp-sm shrink-0",
15
+ statusInner: "inline-flex items-center gap-[4px]",
16
+ statusDot: "size-[6px] rounded-radius-full shrink-0",
17
+ thread:
18
+ "flex-1 min-h-0 overflow-y-auto bg-bg-muted dark:bg-bg-canvas px-pad-3xl py-pad-2xl flex flex-col gap-gp-md",
19
+ composer:
20
+ "flex items-center gap-gp-sm px-pad-2xl py-pad-md border-t border-border-subtle shrink-0 bg-bg-surface",
21
+ },
22
+ });
@@ -0,0 +1,203 @@
1
+ import { useState } from "react";
2
+ import {
3
+ ArrowLeft,
4
+ CheckCircle2,
5
+ Mic,
6
+ PanelRightClose,
7
+ PanelRightOpen,
8
+ Paperclip,
9
+ Send,
10
+ Smile,
11
+ } from "lucide-react";
12
+ import { cn } from "@/lib/utils";
13
+ import { Button } from "@/components/ui/Button/button";
14
+ import { Chip } from "@/components/ui/Chip";
15
+ import { Input } from "@/components/shadcn/input";
16
+ import { SAMPLE_MESSAGES, STATUS_DOT } from "../../chat-v2-mocks";
17
+ import type { Message } from "../../chat-v2.types";
18
+ import { ChannelDot } from "../ChannelDot/channel-dot";
19
+ import { ConversationActionsMenu } from "../ConversationActionsMenu";
20
+ import { DateSeparator } from "../DateSeparator/date-separator";
21
+ import { MessageBubble } from "../MessageBubble";
22
+ import { PersonAvatar } from "../PersonAvatar";
23
+ import { conversationColumnStyles } from "./conversation-column.styles";
24
+ import type { ConversationColumnProps } from "./conversation-column.types";
25
+
26
+ /**
27
+ * Coluna central. Header com avatar + nome + meta (id/canal/status) + ações,
28
+ * thread scrollável com `DateSeparator` + `MessageBubble`, composer com Input
29
+ * + botões. Estado local: texto digitado + lista de mensagens.
30
+ */
31
+ export function ConversationColumn({
32
+ conversation,
33
+ detailsOpen,
34
+ onToggleDetails,
35
+ onBackToList,
36
+ className,
37
+ }: ConversationColumnProps) {
38
+ const s = conversationColumnStyles();
39
+ const [text, setText] = useState("");
40
+ const [msgs, setMsgs] = useState<Message[]>(SAMPLE_MESSAGES);
41
+
42
+ const send = (e?: React.FormEvent) => {
43
+ e?.preventDefault();
44
+ if (!text.trim()) return;
45
+ setMsgs((m) => [
46
+ ...m,
47
+ {
48
+ id: Date.now(),
49
+ from: "me",
50
+ text: text.trim(),
51
+ time: new Date().toLocaleTimeString("pt-BR", {
52
+ hour: "2-digit",
53
+ minute: "2-digit",
54
+ }),
55
+ status: "sent",
56
+ },
57
+ ]);
58
+ setText("");
59
+ };
60
+
61
+ return (
62
+ <section aria-label="Conversa" className={cn(s.root(), className)}>
63
+ {/* Header da conversa */}
64
+ <header className={s.header()}>
65
+ {onBackToList && (
66
+ <Button
67
+ color="secondary"
68
+ variant="ghost"
69
+ size="icon-sm"
70
+ aria-label="Voltar pra lista"
71
+ onClick={onBackToList}
72
+ className="md:hidden"
73
+ >
74
+ <ArrowLeft />
75
+ </Button>
76
+ )}
77
+ <div className="max-md:hidden">
78
+ <PersonAvatar
79
+ initials={conversation.initials}
80
+ hex={conversation.avatarHex}
81
+ size="md"
82
+ />
83
+ </div>
84
+
85
+ <div className={s.headerInfo()}>
86
+ <div className={s.headerTitleRow()}>
87
+ <span className={s.headerName()}>{conversation.name}</span>
88
+ <Chip color={conversation.tagKind} variant="soft" size="sm" shape="pill">
89
+ {conversation.tag}
90
+ </Chip>
91
+ </div>
92
+
93
+ <div className={s.headerMetaRow()}>
94
+ <span className="[font-variant-numeric:tabular-nums]">
95
+ {conversation.id}
96
+ </span>
97
+ <span aria-hidden>·</span>
98
+ <ChannelDot channel={conversation.channel} />
99
+ <span aria-hidden>·</span>
100
+ <span className={s.statusInner()}>
101
+ <span
102
+ className={s.statusDot()}
103
+ style={{ background: STATUS_DOT[conversation.status] }}
104
+ aria-hidden
105
+ />
106
+ {conversation.statusLabel}
107
+ </span>
108
+ </div>
109
+ </div>
110
+
111
+ <div className={s.headerActions()}>
112
+ <Button
113
+ color="primary"
114
+ variant="filled"
115
+ size="sm"
116
+ iconLeft={<CheckCircle2 />}
117
+ className="max-md:hidden"
118
+ >
119
+ Resolver
120
+ </Button>
121
+ <ConversationActionsMenu
122
+ showResolveInMenu
123
+ onResolve={() => console.log("resolver")}
124
+ onCall={() => console.log("ligar")}
125
+ onVideo={() => console.log("video")}
126
+ onAddTag={() => console.log("tag")}
127
+ onAssign={() => console.log("atribuir")}
128
+ onArchive={() => console.log("arquivar")}
129
+ />
130
+ <Button
131
+ color="secondary"
132
+ variant="outline"
133
+ size="icon-sm"
134
+ aria-label={detailsOpen ? "Ocultar detalhes" : "Mostrar detalhes"}
135
+ aria-pressed={detailsOpen}
136
+ onClick={onToggleDetails}
137
+ >
138
+ {detailsOpen ? <PanelRightClose /> : <PanelRightOpen />}
139
+ </Button>
140
+ </div>
141
+ </header>
142
+
143
+ {/* Thread */}
144
+ <div className={s.thread()} aria-live="polite">
145
+ <DateSeparator label="Hoje" />
146
+ {msgs.map((m) => (
147
+ <MessageBubble key={m.id} msg={m} />
148
+ ))}
149
+ </div>
150
+
151
+ {/* Composer */}
152
+ <form onSubmit={send} className={s.composer()}>
153
+ <Button
154
+ color="secondary"
155
+ variant="ghost"
156
+ size="icon-sm"
157
+ aria-label="Anexar"
158
+ type="button"
159
+ >
160
+ <Paperclip />
161
+ </Button>
162
+ <Input
163
+ type="text"
164
+ value={text}
165
+ onChange={(e) => setText(e.target.value)}
166
+ placeholder="Digite uma mensagem..."
167
+ aria-label="Mensagem"
168
+ className="flex-1"
169
+ />
170
+ <Button
171
+ color="secondary"
172
+ variant="ghost"
173
+ size="icon-sm"
174
+ aria-label="Emoji"
175
+ type="button"
176
+ >
177
+ <Smile />
178
+ </Button>
179
+ {text.trim() ? (
180
+ <Button
181
+ color="primary"
182
+ variant="filled"
183
+ size="icon-sm"
184
+ aria-label="Enviar"
185
+ type="submit"
186
+ >
187
+ <Send strokeWidth={2.2} />
188
+ </Button>
189
+ ) : (
190
+ <Button
191
+ color="primary"
192
+ variant="filled"
193
+ size="icon-sm"
194
+ aria-label="Gravar áudio"
195
+ type="button"
196
+ >
197
+ <Mic strokeWidth={2.2} />
198
+ </Button>
199
+ )}
200
+ </form>
201
+ </section>
202
+ );
203
+ }
@@ -0,0 +1,10 @@
1
+ import type { Conversation } from "../../chat-v2.types";
2
+
3
+ export type ConversationColumnProps = {
4
+ conversation: Conversation;
5
+ detailsOpen: boolean;
6
+ onToggleDetails: () => void;
7
+ /** Visível só em mobile (max-md:flex). Desktop esconde. */
8
+ onBackToList?: () => void;
9
+ className?: string;
10
+ };
@@ -0,0 +1,2 @@
1
+ export { ConversationColumn } from "./conversation-column";
2
+ export type { ConversationColumnProps } from "./conversation-column.types";
@@ -0,0 +1,55 @@
1
+ import { tv, type VariantProps } from "@/utils/tv";
2
+
3
+ export const conversationListItemStyles = tv({
4
+ slots: {
5
+ /** Card raiz (button) — flex horizontal com avatar + conteúdo. */
6
+ root: [
7
+ "group/conv text-left relative",
8
+ "flex gap-gp-md p-pad-md rounded-radius-md",
9
+ "cursor-pointer transition-colors duration-150",
10
+ ].join(" "),
11
+ /** Coluna direita (3 rows). */
12
+ content: "flex-1 min-w-0 flex flex-col gap-[3px]",
13
+ /** Row 1 — nome + horário. */
14
+ rowTop: "flex items-center gap-gp-sm",
15
+ /** Nome (ellipsis). */
16
+ name: "text-body-sm font-semibold text-fg-default whitespace-nowrap overflow-hidden text-ellipsis flex-1 min-w-0",
17
+ /** Horário. */
18
+ time: "text-caption-sm text-fg-muted shrink-0 [font-variant-numeric:tabular-nums]",
19
+ /** Row 2 — preview + badge unread. */
20
+ rowMid: "flex items-center gap-gp-sm",
21
+ /** Preview da última mensagem (ellipsis). */
22
+ preview:
23
+ "text-body-xs font-normal text-fg-muted whitespace-nowrap overflow-hidden text-ellipsis flex-1 min-w-0",
24
+ /** Badge contador de não lidas — `caption-xs` (10/400) + override
25
+ `font-bold leading-none` pra centralizar o número dentro do círculo.
26
+ Tabular-nums alinha dígitos verticalmente em badges com counters dinâmicos. */
27
+ unread:
28
+ "inline-flex items-center justify-center min-w-[18px] h-[18px] px-[5px] rounded-radius-full bg-bg-brand text-fg-on-brand text-caption-xs font-bold leading-none shrink-0 [font-variant-numeric:tabular-nums]",
29
+ /** Row 3 — canal + tag + ID. */
30
+ rowBot: "flex items-center gap-gp-sm",
31
+ /** ID da conversa (canto inferior direito). */
32
+ id: "ml-auto text-caption-xs text-fg-subtle shrink-0 [font-variant-numeric:tabular-nums]",
33
+ },
34
+ variants: {
35
+ isActive: {
36
+ true: {
37
+ root: [
38
+ "bg-bg-muted dark:bg-bg-emphasis",
39
+ "before:content-[''] before:absolute before:left-0 before:top-[10px] before:bottom-[10px]",
40
+ "before:w-[3px] before:bg-bg-brand before:rounded-r-[2px] before:pointer-events-none",
41
+ ].join(" "),
42
+ },
43
+ false: {
44
+ root: "hover:bg-bg-muted",
45
+ },
46
+ },
47
+ },
48
+ defaultVariants: {
49
+ isActive: false,
50
+ },
51
+ });
52
+
53
+ export type ConversationListItemVariants = VariantProps<
54
+ typeof conversationListItemStyles
55
+ >;
@@ -0,0 +1,52 @@
1
+ import { Chip } from "@/components/ui/Chip";
2
+ import { ChannelDot } from "../ChannelDot/channel-dot";
3
+ import { PersonAvatar } from "../PersonAvatar";
4
+ import { conversationListItemStyles } from "./conversation-list-item.styles";
5
+ import type { ConversationListItemProps } from "./conversation-list-item.types";
6
+
7
+ /**
8
+ * Item da fila de conversas. Avatar (36px) à esquerda + 3 rows de info à direita.
9
+ * Variant `isActive` adiciona strip lateral brand de 3px + bg muted.
10
+ */
11
+ export function ConversationListItem({
12
+ conv,
13
+ isActive,
14
+ onSelect,
15
+ }: ConversationListItemProps) {
16
+ const s = conversationListItemStyles({ isActive });
17
+
18
+ return (
19
+ <button
20
+ type="button"
21
+ onClick={() => onSelect(conv.id)}
22
+ className={s.root()}
23
+ aria-pressed={isActive}
24
+ >
25
+ <PersonAvatar initials={conv.initials} hex={conv.avatarHex} size="md" />
26
+
27
+ <div className={s.content()}>
28
+ <div className={s.rowTop()}>
29
+ <span className={s.name()}>{conv.name}</span>
30
+ <span className={s.time()}>{conv.time}</span>
31
+ </div>
32
+
33
+ <div className={s.rowMid()}>
34
+ <span className={s.preview()}>{conv.last}</span>
35
+ {conv.unread > 0 && (
36
+ <span className={s.unread()} aria-label={`${conv.unread} não lidas`}>
37
+ {conv.unread}
38
+ </span>
39
+ )}
40
+ </div>
41
+
42
+ <div className={s.rowBot()}>
43
+ <ChannelDot channel={conv.channel} />
44
+ <Chip color={conv.tagKind} variant="soft" size="sm" shape="pill">
45
+ {conv.tag}
46
+ </Chip>
47
+ <span className={s.id()}>{conv.id}</span>
48
+ </div>
49
+ </div>
50
+ </button>
51
+ );
52
+ }
@@ -0,0 +1,7 @@
1
+ import type { Conversation } from "../../chat-v2.types";
2
+
3
+ export type ConversationListItemProps = {
4
+ conv: Conversation;
5
+ isActive: boolean;
6
+ onSelect: (id: string) => void;
7
+ };
@@ -0,0 +1,2 @@
1
+ export { ConversationListItem } from "./conversation-list-item";
2
+ export type { ConversationListItemProps } from "./conversation-list-item.types";
@@ -0,0 +1,18 @@
1
+ export type DateSeparatorProps = {
2
+ /** Texto exibido — ex: "Hoje", "Ontem", "12 mar 2024". */
3
+ label: string;
4
+ };
5
+
6
+ /**
7
+ * Pílula centralizada com label de data entre grupos de mensagens.
8
+ * Visual: chip arredondado com `bg-bg-muted`, tipografia tabular.
9
+ */
10
+ export function DateSeparator({ label }: DateSeparatorProps) {
11
+ return (
12
+ <div className="flex items-center justify-center my-pad-md">
13
+ <span className="px-pad-lg py-pad-2xs rounded-radius-full bg-bg-muted text-caption-sm text-fg-muted [font-variant-numeric:tabular-nums]">
14
+ {label}
15
+ </span>
16
+ </div>
17
+ );
18
+ }
@@ -0,0 +1,23 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ export type DetailFieldProps = {
4
+ /** Label à esquerda (text-fg-muted). */
5
+ label: string;
6
+ /** Valor à direita — string, número ou ReactNode (Chip, link, etc). */
7
+ value: ReactNode;
8
+ };
9
+
10
+ /**
11
+ * Row label : value usada nas seções de detalhes (Contato, Atendimento).
12
+ * Layout flex baseline justify-between, quebra de palavra no valor.
13
+ */
14
+ export function DetailField({ label, value }: DetailFieldProps) {
15
+ return (
16
+ <div className="flex items-baseline justify-between gap-gp-md py-pad-2xs">
17
+ <span className="text-body-xs font-normal text-fg-muted shrink-0">{label}</span>
18
+ <span className="text-body-xs font-normal text-fg-default text-right break-words min-w-0">
19
+ {value || "—"}
20
+ </span>
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,41 @@
1
+ import type { ReactNode } from "react";
2
+ import { ChevronDown } from "lucide-react";
3
+
4
+ export type DetailSectionProps = {
5
+ title: string;
6
+ open: boolean;
7
+ onToggle: () => void;
8
+ children: ReactNode;
9
+ };
10
+
11
+ /**
12
+ * Seção colapsável da `DetailsColumn` (Contato / Atendimento / Histórico).
13
+ * Header clicável com chevron rotativo + corpo só monta quando `open`.
14
+ */
15
+ export function DetailSection({
16
+ title,
17
+ open,
18
+ onToggle,
19
+ children,
20
+ }: DetailSectionProps) {
21
+ return (
22
+ <section className="flex flex-col border-b border-border-subtle pb-pad-xl last:border-b-0 last:pb-0">
23
+ <button
24
+ type="button"
25
+ onClick={onToggle}
26
+ aria-expanded={open}
27
+ className="flex items-center justify-between w-full px-pad-sm py-pad-sm -mx-pad-sm rounded-radius-md cursor-pointer text-left transition-colors duration-150 hover:bg-bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring-brand"
28
+ >
29
+ <span className="text-body-sm font-semibold text-fg-default">{title}</span>
30
+ <ChevronDown
31
+ size={14}
32
+ className={[
33
+ "text-fg-muted transition-transform duration-150",
34
+ open ? "" : "-rotate-90",
35
+ ].join(" ")}
36
+ />
37
+ </button>
38
+ {open && <div className="flex flex-col gap-gp-sm pt-pad-xs">{children}</div>}
39
+ </section>
40
+ );
41
+ }
@@ -0,0 +1 @@
1
+ export { DetailSection, type DetailSectionProps } from "./detail-section";
@@ -0,0 +1,30 @@
1
+ import { tv } from "@/utils/tv";
2
+
3
+ export const detailsColumnStyles = tv({
4
+ slots: {
5
+ root: "relative flex flex-col shrink-0 bg-bg-surface border border-border-subtle rounded-radius-xl overflow-hidden",
6
+ /** Resize handle no left edge — 4px. */
7
+ handle:
8
+ "absolute left-0 top-0 bottom-0 w-[4px] z-[1] bg-transparent cursor-col-resize hover:bg-bg-brand active:bg-bg-brand transition-colors duration-150 focus-visible:outline-none focus-visible:bg-bg-brand",
9
+ header:
10
+ "flex items-center gap-gp-md px-pad-2xl py-pad-lg border-b border-border-subtle shrink-0",
11
+ title: "m-0 text-title-md text-fg-default flex-1",
12
+ body: "flex-1 min-h-0 overflow-y-auto px-pad-2xl py-pad-md flex flex-col gap-gp-2xl",
13
+ contactHead: "flex flex-col items-center gap-gp-md pb-pad-md",
14
+ contactName: "text-body-md font-semibold text-fg-default text-center",
15
+ tagsRow: "flex flex-col gap-gp-sm pt-pad-xs",
16
+ tagsLabel: "text-body-xs font-normal text-fg-muted",
17
+ tagsChips: "flex items-center gap-gp-xs flex-wrap",
18
+ tagAddBtn:
19
+ "inline-flex items-center gap-[3px] px-pad-md h-[22px] rounded-radius-full border border-dashed border-border-input text-caption-sm font-medium text-fg-muted hover:bg-bg-muted hover:text-fg-default transition-colors",
20
+ historyEmpty: "text-body-xs font-normal text-fg-subtle text-center py-pad-md",
21
+ historyList: "m-0 p-0 list-none flex flex-col gap-gp-sm",
22
+ historyItem:
23
+ "flex gap-gp-md p-pad-md rounded-radius-md bg-bg-muted border border-border-subtle",
24
+ historyId:
25
+ "text-caption-sm text-fg-muted shrink-0 [font-variant-numeric:tabular-nums] pt-[1px]",
26
+ historyBody: "flex-1 min-w-0 flex flex-col gap-[1px]",
27
+ historySubject: "text-body-xs text-fg-default",
28
+ historyMeta: "text-caption-sm text-fg-muted",
29
+ },
30
+ });