@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.
- package/dist-lib/ai/blocos/chart/budget-breakdown.tsx +174 -0
- package/dist-lib/ai/blocos/indice.md +43 -0
- package/dist-lib/ai/blocos/paneldetail/detalhe-com-tabela.tsx +450 -0
- package/dist-lib/ai/blocos/paneldetail/detalhe-de-tarefa-com-abas.tsx +570 -0
- package/dist-lib/ai/blocos/paneldetail/detalhe-do-registro.tsx +555 -0
- package/dist-lib/ai/componentes/AlertModal.md +47 -0
- package/dist-lib/ai/componentes/AppShell.md +117 -0
- package/dist-lib/ai/componentes/Breadcrumb.md +187 -0
- package/dist-lib/ai/componentes/Button.md +116 -0
- package/dist-lib/ai/componentes/ButtonGroup.md +162 -0
- package/dist-lib/ai/componentes/CardCheckbox.md +99 -0
- package/dist-lib/ai/componentes/CardOption.md +133 -0
- package/dist-lib/ai/componentes/Chart.md +93 -0
- package/dist-lib/ai/componentes/Chip.md +68 -0
- package/dist-lib/ai/componentes/ChoroplethMap.md +118 -0
- package/dist-lib/ai/componentes/ColorPicker.md +70 -0
- package/dist-lib/ai/componentes/Combobox.md +51 -0
- package/dist-lib/ai/componentes/ConversationListItem.md +90 -0
- package/dist-lib/ai/componentes/DataList.md +111 -0
- package/dist-lib/ai/componentes/DataTable.md +867 -0
- package/dist-lib/ai/componentes/DatePicker.md +84 -0
- package/dist-lib/ai/componentes/DateSeparatorChip.md +59 -0
- package/dist-lib/ai/componentes/EmptyState.md +72 -0
- package/dist-lib/ai/componentes/FileUploadField.md +95 -0
- package/dist-lib/ai/componentes/FloatingPanel.md +118 -0
- package/dist-lib/ai/componentes/FooterTable.md +62 -0
- package/dist-lib/ai/componentes/FormField.md +110 -0
- package/dist-lib/ai/componentes/Gantt.md +552 -0
- package/dist-lib/ai/componentes/Header.md +98 -0
- package/dist-lib/ai/componentes/Icon.md +65 -0
- package/dist-lib/ai/componentes/Kanban.md +343 -0
- package/dist-lib/ai/componentes/Kpi.md +103 -0
- package/dist-lib/ai/componentes/List.md +61 -0
- package/dist-lib/ai/componentes/MarkdownText.md +59 -0
- package/dist-lib/ai/componentes/MenuSidebar.md +128 -0
- package/dist-lib/ai/componentes/MessageAck.md +53 -0
- package/dist-lib/ai/componentes/MessageBubble.md +115 -0
- package/dist-lib/ai/componentes/MessageComposer.md +80 -0
- package/dist-lib/ai/componentes/MessageVariablesPicker.md +104 -0
- package/dist-lib/ai/componentes/Modal.md +88 -0
- package/dist-lib/ai/componentes/MonthYearPicker.md +49 -0
- package/dist-lib/ai/componentes/PageHeader.md +129 -0
- package/dist-lib/ai/componentes/Panel.md +84 -0
- package/dist-lib/ai/componentes/Scheduler.md +421 -0
- package/dist-lib/ai/componentes/ScreenLoader.md +60 -0
- package/dist-lib/ai/componentes/SingleMenuSidebar.md +171 -0
- package/dist-lib/ai/componentes/Spinner.md +52 -0
- package/dist-lib/ai/componentes/Table.md +192 -0
- package/dist-lib/ai/componentes/TableToolbar.md +87 -0
- package/dist-lib/ai/componentes/TabsNavigation.md +152 -0
- package/dist-lib/ai/componentes/Toast.md +49 -0
- package/dist-lib/ai/componentes/_primitivos.md +74 -0
- package/dist-lib/ai/componentes/avatar-ig.md +181 -0
- package/dist-lib/ai/componentes/indice.json +49 -0
- package/dist-lib/ai/exemplos/app-shell/app-shell-example.tsx +140 -0
- package/dist-lib/ai/exemplos/app-shell/index.ts +3 -0
- package/dist-lib/ai/exemplos/app-shell/nav-data.ts +97 -0
- package/dist-lib/ai/exemplos/app-shell/routes.tsx +75 -0
- package/dist-lib/ai/exemplos/chat/chat-screen.tsx +152 -0
- package/dist-lib/ai/exemplos/chat/chat-v2-mocks.ts +171 -0
- package/dist-lib/ai/exemplos/chat/chat-v2.styles.ts +23 -0
- package/dist-lib/ai/exemplos/chat/chat-v2.types.ts +81 -0
- package/dist-lib/ai/exemplos/chat/components/ChannelDot/channel-dot.tsx +28 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationActionsMenu/conversation-actions-menu.tsx +83 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationActionsMenu/index.ts +4 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.styles.ts +22 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.tsx +203 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.types.ts +10 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationColumn/index.ts +2 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.styles.ts +55 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.tsx +52 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.types.ts +7 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationListItem/index.ts +2 -0
- package/dist-lib/ai/exemplos/chat/components/DateSeparator/date-separator.tsx +18 -0
- package/dist-lib/ai/exemplos/chat/components/DetailField/detail-field.tsx +23 -0
- package/dist-lib/ai/exemplos/chat/components/DetailSection/detail-section.tsx +41 -0
- package/dist-lib/ai/exemplos/chat/components/DetailSection/index.ts +1 -0
- package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.styles.ts +30 -0
- package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.tsx +142 -0
- package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.types.ts +16 -0
- package/dist-lib/ai/exemplos/chat/components/DetailsColumn/index.ts +5 -0
- package/dist-lib/ai/exemplos/chat/components/FilterRow/filter-row.styles.ts +37 -0
- package/dist-lib/ai/exemplos/chat/components/FilterRow/filter-row.tsx +45 -0
- package/dist-lib/ai/exemplos/chat/components/FilterRow/index.ts +1 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.styles.ts +27 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.tsx +66 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.types.ts +13 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-rail.tsx +38 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/index.ts +6 -0
- package/dist-lib/ai/exemplos/chat/components/MessageBubble/index.ts +2 -0
- package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.styles.ts +37 -0
- package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.tsx +29 -0
- package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.types.ts +5 -0
- package/dist-lib/ai/exemplos/chat/components/PersonAvatar/index.ts +5 -0
- package/dist-lib/ai/exemplos/chat/components/PersonAvatar/person-avatar.tsx +31 -0
- package/dist-lib/ai/exemplos/chat/components/QueueColumn/index.ts +2 -0
- package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.styles.ts +19 -0
- package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.tsx +140 -0
- package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.types.ts +13 -0
- package/dist-lib/ai/exemplos/chat/components/RailItem/index.ts +1 -0
- package/dist-lib/ai/exemplos/chat/components/RailItem/rail-item.styles.ts +30 -0
- package/dist-lib/ai/exemplos/chat/components/RailItem/rail-item.tsx +42 -0
- package/dist-lib/ai/exemplos/chat/hooks/use-resizable.ts +98 -0
- package/dist-lib/ai/exemplos/chat/index.ts +1 -0
- package/dist-lib/ai/exemplos/clientes/_table-data.ts +59 -0
- package/dist-lib/ai/exemplos/clientes/clientes-screen.tsx +505 -0
- package/dist-lib/ai/exemplos/clientes/clientes-showcase-mocks.ts +117 -0
- package/dist-lib/ai/exemplos/clientes/clientes-showcase.styles.ts +13 -0
- package/dist-lib/ai/exemplos/clientes/clientes-showcase.types.ts +16 -0
- package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.styles.ts +28 -0
- package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.tsx +303 -0
- package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.types.ts +14 -0
- package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/index.ts +2 -0
- package/dist-lib/ai/exemplos/clientes/components/NovoClienteDrawer/index.ts +2 -0
- package/dist-lib/ai/exemplos/clientes/components/NovoClienteDrawer/novo-cliente-drawer.tsx +207 -0
- package/dist-lib/ai/exemplos/clientes/index.ts +1 -0
- package/dist-lib/ai/exemplos/dashboard/dashboard-brazil-map.ts +33 -0
- package/dist-lib/ai/exemplos/dashboard/dashboard-screen.tsx +1110 -0
- package/dist-lib/ai/exemplos/dashboard/index.ts +1 -0
- package/dist-lib/ai/exemplos/edit-page/components/StepNav.tsx +81 -0
- package/dist-lib/ai/exemplos/edit-page/components/section-card.tsx +85 -0
- package/dist-lib/ai/exemplos/edit-page/edit-page-screen.tsx +234 -0
- package/dist-lib/ai/exemplos/edit-page/index.ts +1 -0
- package/dist-lib/ai/exemplos/finance/_table-data.ts +57 -0
- package/dist-lib/ai/exemplos/finance/clientes-financeiro-mocks.ts +227 -0
- package/dist-lib/ai/exemplos/finance/clientes-financeiro.types.ts +75 -0
- package/dist-lib/ai/exemplos/finance/clientes-showcase-mocks.ts +117 -0
- package/dist-lib/ai/exemplos/finance/clientes-showcase.styles.ts +13 -0
- package/dist-lib/ai/exemplos/finance/clientes-showcase.types.ts +16 -0
- package/dist-lib/ai/exemplos/finance/components/EditarFinanceDrawer/editar-finance-drawer.tsx +241 -0
- package/dist-lib/ai/exemplos/finance/components/EditarFinanceDrawer/index.ts +5 -0
- package/dist-lib/ai/exemplos/finance/components/ExtratoExpansion/extrato-expansion.tsx +172 -0
- package/dist-lib/ai/exemplos/finance/components/ExtratoExpansion/index.ts +1 -0
- package/dist-lib/ai/exemplos/finance/components/FinanceDetailPanel/finance-detail-panel.tsx +241 -0
- package/dist-lib/ai/exemplos/finance/components/FinanceDetailPanel/index.ts +2 -0
- package/dist-lib/ai/exemplos/finance/components/NovoClienteDrawer/index.ts +2 -0
- package/dist-lib/ai/exemplos/finance/components/NovoClienteDrawer/novo-cliente-drawer.tsx +207 -0
- package/dist-lib/ai/exemplos/finance/components/SacarDialog/index.ts +2 -0
- package/dist-lib/ai/exemplos/finance/components/SacarDialog/sacar-dialog.tsx +346 -0
- package/dist-lib/ai/exemplos/finance/finance-screen.tsx +821 -0
- package/dist-lib/ai/exemplos/finance/index.ts +1 -0
- package/dist-lib/ai/exemplos/gantt/_gantt-data.tsx +402 -0
- package/dist-lib/ai/exemplos/gantt/gantt-screen.tsx +512 -0
- package/dist-lib/ai/exemplos/gantt/index.ts +1 -0
- package/dist-lib/ai/exemplos/login/index.ts +1 -0
- package/dist-lib/ai/exemplos/login/login-screen.tsx +246 -0
- package/dist-lib/ai/exemplos/mapa-rede/components/ConsultorDetailPanel/consultor-detail-panel.tsx +158 -0
- package/dist-lib/ai/exemplos/mapa-rede/components/ConsultorDetailPanel/index.ts +2 -0
- package/dist-lib/ai/exemplos/mapa-rede/index.ts +1 -0
- package/dist-lib/ai/exemplos/mapa-rede/mapa-de-rede-mocks.ts +655 -0
- package/dist-lib/ai/exemplos/mapa-rede/mapa-de-rede.types.ts +53 -0
- package/dist-lib/ai/exemplos/mapa-rede/mapa-rede-screen.tsx +227 -0
- package/dist-lib/ai/exemplos/order-detail/components/ActivityTab.tsx +77 -0
- package/dist-lib/ai/exemplos/order-detail/components/AttachmentsTab.tsx +45 -0
- package/dist-lib/ai/exemplos/order-detail/components/CommentsTab.tsx +111 -0
- package/dist-lib/ai/exemplos/order-detail/components/DetailsTab.tsx +189 -0
- package/dist-lib/ai/exemplos/order-detail/components/OverviewTab.tsx +200 -0
- package/dist-lib/ai/exemplos/order-detail/components/section-card.tsx +85 -0
- package/dist-lib/ai/exemplos/order-detail/index.ts +1 -0
- package/dist-lib/ai/exemplos/order-detail/order-detail-screen.tsx +119 -0
- package/dist-lib/ai/exemplos/order-detail/order-mocks.ts +186 -0
- package/dist-lib/ai/exemplos/order-detail/order.types.ts +118 -0
- package/dist-lib/ai/global/componentes.md +217 -0
- package/dist-lib/ai/global/composicao.md +182 -0
- package/dist-lib/ai/indice.json +192 -0
- package/dist-lib/ai/lint/ds-lint-patterns.mjs +115 -0
- package/dist-lib/ai/manifest.json +42 -0
- package/dist-lib/ai/regras/design.md +88 -0
- package/dist-lib/ai/regras/temas.md +192 -0
- package/dist-lib/ai/regras-por-componente.json +103 -0
- package/dist-lib/ai/roteiros/app-builder/roteiro.md +155 -0
- package/dist-lib/ai/roteiros/auth-builder/roteiro.md +42 -0
- package/dist-lib/ai/roteiros/cards/roteiro.md +34 -0
- package/dist-lib/ai/roteiros/charts/roteiro.md +32 -0
- package/dist-lib/ai/roteiros/chat/roteiro.md +31 -0
- package/dist-lib/ai/roteiros/crud-builder/blueprint.md +63 -0
- package/dist-lib/ai/roteiros/crud-builder/entrevista.md +139 -0
- package/dist-lib/ai/roteiros/crud-builder/geracao.md +113 -0
- package/dist-lib/ai/roteiros/crud-builder/roteiro.md +89 -0
- package/dist-lib/ai/roteiros/dashboard-builder/blueprint.md +47 -0
- package/dist-lib/ai/roteiros/dashboard-builder/entrevista.md +62 -0
- package/dist-lib/ai/roteiros/dashboard-builder/geracao.md +88 -0
- package/dist-lib/ai/roteiros/dashboard-builder/roteiro.md +88 -0
- package/dist-lib/ai/roteiros/drawers/roteiro.md +41 -0
- package/dist-lib/ai/roteiros/list-builder/blueprint.md +94 -0
- package/dist-lib/ai/roteiros/list-builder/entrevista.md +188 -0
- package/dist-lib/ai/roteiros/list-builder/geracao.md +63 -0
- package/dist-lib/ai/roteiros/list-builder/roteiro.md +91 -0
- package/dist-lib/ai/roteiros/module-replicator/roteiro.md +56 -0
- package/dist-lib/ai/roteiros/page-detail/roteiro.md +32 -0
- package/dist-lib/ai/roteiros/page-edit/roteiro.md +31 -0
- package/dist-lib/ai/roteiros/screen-composer/roteiro.md +95 -0
- package/package.json +4 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { ArrowLeft, Plus, X } from "lucide-react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import { Button } from "@/components/ui/Button/button";
|
|
4
|
+
import { Chip } from "@/components/ui/Chip";
|
|
5
|
+
import { CHANNEL_LABEL } from "../../chat-v2-mocks";
|
|
6
|
+
import { DetailField } from "../DetailField/detail-field";
|
|
7
|
+
import { DetailSection } from "../DetailSection";
|
|
8
|
+
import { PersonAvatar } from "../PersonAvatar";
|
|
9
|
+
import { detailsColumnStyles } from "./details-column.styles";
|
|
10
|
+
import type { DetailsColumnProps } from "./details-column.types";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Coluna direita: header com título + close, resize handle no left edge (4px),
|
|
14
|
+
* body scrollável com 3 accordions (Contato, Atendimento, Histórico).
|
|
15
|
+
* Width controlado pelo hook `useResizable` no parent.
|
|
16
|
+
*/
|
|
17
|
+
export function DetailsColumn({
|
|
18
|
+
conversation,
|
|
19
|
+
detail,
|
|
20
|
+
openSections,
|
|
21
|
+
onToggleSection,
|
|
22
|
+
onClose,
|
|
23
|
+
width,
|
|
24
|
+
onResizeStart,
|
|
25
|
+
onBackToChat,
|
|
26
|
+
className,
|
|
27
|
+
}: DetailsColumnProps) {
|
|
28
|
+
const s = detailsColumnStyles();
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<aside
|
|
32
|
+
aria-label="Detalhes do contato"
|
|
33
|
+
style={{ width }}
|
|
34
|
+
className={cn(s.root(), className)}
|
|
35
|
+
>
|
|
36
|
+
<button
|
|
37
|
+
type="button"
|
|
38
|
+
onMouseDown={onResizeStart}
|
|
39
|
+
aria-label="Redimensionar painel de detalhes"
|
|
40
|
+
className={s.handle()}
|
|
41
|
+
/>
|
|
42
|
+
|
|
43
|
+
<header className={s.header()}>
|
|
44
|
+
{onBackToChat && (
|
|
45
|
+
<Button
|
|
46
|
+
color="secondary"
|
|
47
|
+
variant="ghost"
|
|
48
|
+
size="icon-sm"
|
|
49
|
+
aria-label="Voltar pra conversa"
|
|
50
|
+
onClick={onBackToChat}
|
|
51
|
+
className="md:hidden"
|
|
52
|
+
>
|
|
53
|
+
<ArrowLeft />
|
|
54
|
+
</Button>
|
|
55
|
+
)}
|
|
56
|
+
<h3 className={s.title()}>Detalhes</h3>
|
|
57
|
+
<Button
|
|
58
|
+
color="secondary"
|
|
59
|
+
variant="ghost"
|
|
60
|
+
size="icon-sm"
|
|
61
|
+
aria-label="Fechar painel"
|
|
62
|
+
onClick={onClose}
|
|
63
|
+
>
|
|
64
|
+
<X />
|
|
65
|
+
</Button>
|
|
66
|
+
</header>
|
|
67
|
+
|
|
68
|
+
<div className={s.body()}>
|
|
69
|
+
<DetailSection
|
|
70
|
+
title="Contato"
|
|
71
|
+
open={openSections.contact}
|
|
72
|
+
onToggle={() => onToggleSection("contact")}
|
|
73
|
+
>
|
|
74
|
+
<div className={s.contactHead()}>
|
|
75
|
+
<PersonAvatar
|
|
76
|
+
initials={conversation.initials}
|
|
77
|
+
hex={conversation.avatarHex}
|
|
78
|
+
size="lg"
|
|
79
|
+
/>
|
|
80
|
+
<div className={s.contactName()}>{conversation.name}</div>
|
|
81
|
+
</div>
|
|
82
|
+
<DetailField label="Telefone" value={detail.phone} />
|
|
83
|
+
<DetailField label="Email" value={detail.email} />
|
|
84
|
+
<DetailField label="Primeiro contato" value={detail.firstContact} />
|
|
85
|
+
</DetailSection>
|
|
86
|
+
|
|
87
|
+
<DetailSection
|
|
88
|
+
title="Atendimento"
|
|
89
|
+
open={openSections.ticket}
|
|
90
|
+
onToggle={() => onToggleSection("ticket")}
|
|
91
|
+
>
|
|
92
|
+
<DetailField label="ID" value={conversation.id} />
|
|
93
|
+
<DetailField label="Status" value={conversation.statusLabel} />
|
|
94
|
+
<DetailField label="Atribuído a" value={detail.assignedTo} />
|
|
95
|
+
<DetailField label="Canal" value={CHANNEL_LABEL[conversation.channel]} />
|
|
96
|
+
<DetailField label="Criado em" value={detail.createdAt} />
|
|
97
|
+
|
|
98
|
+
<div className={s.tagsRow()}>
|
|
99
|
+
<span className={s.tagsLabel()}>Tags</span>
|
|
100
|
+
<div className={s.tagsChips()}>
|
|
101
|
+
<Chip color={conversation.tagKind} variant="soft" size="sm" shape="pill">
|
|
102
|
+
{conversation.tag}
|
|
103
|
+
</Chip>
|
|
104
|
+
{detail.extraTags.map((t) => (
|
|
105
|
+
<Chip key={t} color="info" variant="soft" size="sm" shape="pill">
|
|
106
|
+
{t}
|
|
107
|
+
</Chip>
|
|
108
|
+
))}
|
|
109
|
+
<button type="button" className={s.tagAddBtn()}>
|
|
110
|
+
<Plus size={11} strokeWidth={2.2} /> tag
|
|
111
|
+
</button>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</DetailSection>
|
|
115
|
+
|
|
116
|
+
<DetailSection
|
|
117
|
+
title="Histórico"
|
|
118
|
+
open={openSections.history}
|
|
119
|
+
onToggle={() => onToggleSection("history")}
|
|
120
|
+
>
|
|
121
|
+
{detail.history.length === 0 ? (
|
|
122
|
+
<div className={s.historyEmpty()}>Nenhum atendimento anterior.</div>
|
|
123
|
+
) : (
|
|
124
|
+
<ul className={s.historyList()}>
|
|
125
|
+
{detail.history.map((h) => (
|
|
126
|
+
<li key={h.id} className={s.historyItem()}>
|
|
127
|
+
<span className={s.historyId()}>{h.id}</span>
|
|
128
|
+
<div className={s.historyBody()}>
|
|
129
|
+
<span className={s.historySubject()}>{h.subject}</span>
|
|
130
|
+
<span className={s.historyMeta()}>
|
|
131
|
+
{h.date} · {h.status}
|
|
132
|
+
</span>
|
|
133
|
+
</div>
|
|
134
|
+
</li>
|
|
135
|
+
))}
|
|
136
|
+
</ul>
|
|
137
|
+
)}
|
|
138
|
+
</DetailSection>
|
|
139
|
+
</div>
|
|
140
|
+
</aside>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Conversation, ConversationDetail } from "../../chat-v2.types";
|
|
2
|
+
|
|
3
|
+
export type SectionsState = Record<"contact" | "ticket" | "history", boolean>;
|
|
4
|
+
|
|
5
|
+
export type DetailsColumnProps = {
|
|
6
|
+
conversation: Conversation;
|
|
7
|
+
detail: ConversationDetail;
|
|
8
|
+
openSections: SectionsState;
|
|
9
|
+
onToggleSection: (id: keyof SectionsState) => void;
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
width: number;
|
|
12
|
+
onResizeStart: (e: React.MouseEvent) => void;
|
|
13
|
+
/** Em mobile: botão de voltar pro chat. Desktop esconde. */
|
|
14
|
+
onBackToChat?: () => void;
|
|
15
|
+
className?: string;
|
|
16
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { tv, type VariantProps } from "@/utils/tv";
|
|
2
|
+
|
|
3
|
+
export const filterRowStyles = tv({
|
|
4
|
+
slots: {
|
|
5
|
+
button: [
|
|
6
|
+
"flex items-center gap-gp-md w-full",
|
|
7
|
+
"px-pad-md py-pad-sm rounded-radius-md text-left",
|
|
8
|
+
"transition-colors duration-150 cursor-pointer",
|
|
9
|
+
].join(" "),
|
|
10
|
+
/** Leading slot (icon ou dot fallback). */
|
|
11
|
+
leading: "shrink-0 inline-flex items-center justify-center size-[18px]",
|
|
12
|
+
/** Label do filtro (ellipsis). */
|
|
13
|
+
label:
|
|
14
|
+
"flex-1 min-w-0 text-body-xs whitespace-nowrap overflow-hidden text-ellipsis",
|
|
15
|
+
/** Dot brand 6px pra "tem novidades". */
|
|
16
|
+
unreadDot: "size-[6px] rounded-radius-full bg-bg-brand shrink-0",
|
|
17
|
+
/** Contador à direita. */
|
|
18
|
+
count: "text-caption-sm text-fg-muted shrink-0 [font-variant-numeric:tabular-nums]",
|
|
19
|
+
/** Dot fallback (quando item não tem icon). */
|
|
20
|
+
fallbackDot: "size-[8px] rounded-radius-full",
|
|
21
|
+
},
|
|
22
|
+
variants: {
|
|
23
|
+
isActive: {
|
|
24
|
+
true: {
|
|
25
|
+
button: "bg-bg-muted dark:bg-bg-emphasis text-fg-default font-semibold",
|
|
26
|
+
},
|
|
27
|
+
false: {
|
|
28
|
+
button: "text-fg-muted hover:bg-bg-muted hover:text-fg-default",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
isActive: false,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export type FilterRowVariants = VariantProps<typeof filterRowStyles>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { FilterItem } from "../../chat-v2.types";
|
|
2
|
+
import { filterRowStyles } from "./filter-row.styles";
|
|
3
|
+
|
|
4
|
+
export type FilterRowProps = {
|
|
5
|
+
item: FilterItem;
|
|
6
|
+
isActive: boolean;
|
|
7
|
+
onClick: () => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Row clicável da `FiltersColumn` expandida. Layout: leading (icon ou dot) +
|
|
12
|
+
* label (ellipsis) + dot unread opcional + count tabular.
|
|
13
|
+
*/
|
|
14
|
+
export function FilterRow({ item, isActive, onClick }: FilterRowProps) {
|
|
15
|
+
const Icon = item.icon;
|
|
16
|
+
const s = filterRowStyles({ isActive });
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<button
|
|
20
|
+
type="button"
|
|
21
|
+
onClick={onClick}
|
|
22
|
+
aria-pressed={isActive}
|
|
23
|
+
className={s.button()}
|
|
24
|
+
>
|
|
25
|
+
<span className={s.leading()}>
|
|
26
|
+
{Icon ? (
|
|
27
|
+
<Icon
|
|
28
|
+
size={15}
|
|
29
|
+
strokeWidth={1.8}
|
|
30
|
+
style={{ color: item.color ?? "var(--color-fg-muted)" }}
|
|
31
|
+
/>
|
|
32
|
+
) : (
|
|
33
|
+
<span
|
|
34
|
+
className={s.fallbackDot()}
|
|
35
|
+
style={{ background: item.color ?? "var(--color-fg-muted)" }}
|
|
36
|
+
aria-hidden
|
|
37
|
+
/>
|
|
38
|
+
)}
|
|
39
|
+
</span>
|
|
40
|
+
<span className={s.label()}>{item.name}</span>
|
|
41
|
+
{item.hasUnread && <span className={s.unreadDot()} aria-label="tem novidades" />}
|
|
42
|
+
<span className={s.count()}>{item.count}</span>
|
|
43
|
+
</button>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FilterRow, type FilterRowProps } from "./filter-row";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { tv } from "@/utils/tv";
|
|
2
|
+
|
|
3
|
+
export const filtersColumnStyles = tv({
|
|
4
|
+
slots: {
|
|
5
|
+
/** Aside raiz da versão expandida (280px). */
|
|
6
|
+
root: "flex flex-col w-[280px] shrink-0 bg-bg-surface border border-border-subtle rounded-radius-xl overflow-hidden",
|
|
7
|
+
/** Aside raiz da versão rail (64px). */
|
|
8
|
+
rail: "flex flex-col w-[64px] shrink-0 bg-bg-surface border border-border-subtle rounded-radius-xl overflow-hidden",
|
|
9
|
+
/** Container scrollável das seções (expandida). */
|
|
10
|
+
scroll: "flex-1 min-h-0 overflow-y-auto px-pad-md py-pad-md flex flex-col gap-gp-lg",
|
|
11
|
+
/** Container scrollável (rail). */
|
|
12
|
+
railScroll:
|
|
13
|
+
"flex-1 min-h-0 overflow-y-auto py-pad-md flex flex-col items-center gap-gp-sm",
|
|
14
|
+
/** Section wrapper (expandida). */
|
|
15
|
+
section: "flex flex-col gap-gp-2xs",
|
|
16
|
+
/** Header de section (título all-caps). Combo: `caption-xs` (10/400)
|
|
17
|
+
+ override `font-bold uppercase tracking-[0.06em]` pra pattern strong. */
|
|
18
|
+
sectionTitle:
|
|
19
|
+
"px-pad-md pt-pad-2xs pb-pad-2xs text-caption-xs font-bold tracking-[0.06em] text-fg-subtle uppercase",
|
|
20
|
+
/** Group wrapper (rail) — divider entre grupos. */
|
|
21
|
+
railGroup: "w-full flex flex-col items-center gap-gp-xs px-pad-md",
|
|
22
|
+
/** Modifier de divisor pros grupos não-primeiros (rail). */
|
|
23
|
+
railGroupDivider: "pt-pad-sm border-t border-border-subtle",
|
|
24
|
+
/** Modifier de divisor pras sections não-primeiras (expandida). */
|
|
25
|
+
sectionDivider: "pt-pad-lg border-t border-border-subtle",
|
|
26
|
+
},
|
|
27
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils";
|
|
2
|
+
import { FILTER_GROUPS } from "../../chat-v2-mocks";
|
|
3
|
+
import type { FilterGroupKey, FilterItem } from "../../chat-v2.types";
|
|
4
|
+
import { FilterRow } from "../FilterRow";
|
|
5
|
+
import { filtersColumnStyles } from "./filters-column.styles";
|
|
6
|
+
import type { FiltersColumnProps, FiltersState } from "./filters-column.types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Coluna esquerda expandida (280px) com as 4 seções de filtros definidas
|
|
10
|
+
* em `FILTER_GROUPS` (mocks).
|
|
11
|
+
*/
|
|
12
|
+
export function FiltersColumn({
|
|
13
|
+
filters,
|
|
14
|
+
onToggleFilter,
|
|
15
|
+
className,
|
|
16
|
+
}: FiltersColumnProps) {
|
|
17
|
+
const s = filtersColumnStyles();
|
|
18
|
+
return (
|
|
19
|
+
<aside aria-label="Filtros" className={cn(s.root(), className)}>
|
|
20
|
+
<div className={s.scroll()}>
|
|
21
|
+
{FILTER_GROUPS.map((sec, i) => (
|
|
22
|
+
<FilterSection
|
|
23
|
+
key={sec.group}
|
|
24
|
+
title={sec.title}
|
|
25
|
+
group={sec.group}
|
|
26
|
+
items={sec.items}
|
|
27
|
+
filters={filters}
|
|
28
|
+
onToggle={onToggleFilter}
|
|
29
|
+
isFirst={i === 0}
|
|
30
|
+
/>
|
|
31
|
+
))}
|
|
32
|
+
</div>
|
|
33
|
+
</aside>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function FilterSection({
|
|
38
|
+
title,
|
|
39
|
+
group,
|
|
40
|
+
items,
|
|
41
|
+
filters,
|
|
42
|
+
onToggle,
|
|
43
|
+
isFirst,
|
|
44
|
+
}: {
|
|
45
|
+
title: string;
|
|
46
|
+
group: FilterGroupKey;
|
|
47
|
+
items: FilterItem[];
|
|
48
|
+
filters: FiltersState;
|
|
49
|
+
onToggle: (group: FilterGroupKey, id: string) => void;
|
|
50
|
+
isFirst?: boolean;
|
|
51
|
+
}) {
|
|
52
|
+
const s = filtersColumnStyles();
|
|
53
|
+
return (
|
|
54
|
+
<section className={cn(s.section(), !isFirst && s.sectionDivider())}>
|
|
55
|
+
<div className={s.sectionTitle()}>{title}</div>
|
|
56
|
+
{items.map((item) => (
|
|
57
|
+
<FilterRow
|
|
58
|
+
key={item.id}
|
|
59
|
+
item={item}
|
|
60
|
+
isActive={filters[group].has(item.id)}
|
|
61
|
+
onClick={() => onToggle(group, item.id)}
|
|
62
|
+
/>
|
|
63
|
+
))}
|
|
64
|
+
</section>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FilterGroupKey } from "../../chat-v2.types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Estado dos filtros — um Set de IDs por grupo. Centralizado no parent
|
|
5
|
+
* (`ChatScreen`) e passado pra baixo.
|
|
6
|
+
*/
|
|
7
|
+
export type FiltersState = Record<FilterGroupKey, Set<string>>;
|
|
8
|
+
|
|
9
|
+
export type FiltersColumnProps = {
|
|
10
|
+
filters: FiltersState;
|
|
11
|
+
onToggleFilter: (group: FilterGroupKey, id: string) => void;
|
|
12
|
+
className?: string;
|
|
13
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils";
|
|
2
|
+
import { FILTER_GROUPS } from "../../chat-v2-mocks";
|
|
3
|
+
import { RailItem } from "../RailItem";
|
|
4
|
+
import { filtersColumnStyles } from "./filters-column.styles";
|
|
5
|
+
import type { FiltersColumnProps } from "./filters-column.types";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Coluna esquerda compacta (64px) — só ícones, divididos por grupo
|
|
9
|
+
* com divider top entre grupos. Consome `FILTER_GROUPS` (mocks).
|
|
10
|
+
*/
|
|
11
|
+
export function FiltersRail({
|
|
12
|
+
filters,
|
|
13
|
+
onToggleFilter,
|
|
14
|
+
className,
|
|
15
|
+
}: FiltersColumnProps) {
|
|
16
|
+
const s = filtersColumnStyles();
|
|
17
|
+
return (
|
|
18
|
+
<aside aria-label="Filtros (compactos)" className={cn(s.rail(), className)}>
|
|
19
|
+
<div className={s.railScroll()}>
|
|
20
|
+
{FILTER_GROUPS.map(({ group, items }, gi) => (
|
|
21
|
+
<div
|
|
22
|
+
key={group}
|
|
23
|
+
className={cn(s.railGroup(), gi > 0 && s.railGroupDivider())}
|
|
24
|
+
>
|
|
25
|
+
{items.map((item) => (
|
|
26
|
+
<RailItem
|
|
27
|
+
key={item.id}
|
|
28
|
+
item={item}
|
|
29
|
+
isActive={filters[group].has(item.id)}
|
|
30
|
+
onClick={() => onToggleFilter(group, item.id)}
|
|
31
|
+
/>
|
|
32
|
+
))}
|
|
33
|
+
</div>
|
|
34
|
+
))}
|
|
35
|
+
</div>
|
|
36
|
+
</aside>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { tv, type VariantProps } from "@/utils/tv";
|
|
2
|
+
|
|
3
|
+
export const messageBubbleStyles = tv({
|
|
4
|
+
slots: {
|
|
5
|
+
/** Wrapper que controla o alinhamento horizontal (esquerda/direita). */
|
|
6
|
+
row: "flex",
|
|
7
|
+
/** Balão em si — variants definem cor + corner pointer. */
|
|
8
|
+
bubble:
|
|
9
|
+
"max-w-[70%] px-pad-xl py-pad-md rounded-radius-xl shadow-sh-sm",
|
|
10
|
+
/** Texto da mensagem. */
|
|
11
|
+
text: "text-body-md leading-[1.45] whitespace-pre-wrap break-words",
|
|
12
|
+
/** Footer com horário + status icon. */
|
|
13
|
+
meta: "flex items-center justify-end gap-[4px] mt-[2px] text-caption-xs",
|
|
14
|
+
/** Span do horário (tabular-nums pra alinhar). */
|
|
15
|
+
time: "[font-variant-numeric:tabular-nums]",
|
|
16
|
+
},
|
|
17
|
+
variants: {
|
|
18
|
+
from: {
|
|
19
|
+
me: {
|
|
20
|
+
row: "justify-end",
|
|
21
|
+
bubble: "bg-bg-brand text-fg-on-brand rounded-br-[4px]",
|
|
22
|
+
meta: "text-fg-on-brand opacity-80",
|
|
23
|
+
},
|
|
24
|
+
them: {
|
|
25
|
+
row: "justify-start",
|
|
26
|
+
bubble:
|
|
27
|
+
"bg-bg-surface text-fg-default border border-border-subtle rounded-bl-[4px]",
|
|
28
|
+
meta: "text-fg-muted",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
from: "them",
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export type MessageBubbleVariants = VariantProps<typeof messageBubbleStyles>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Check, CheckCheck } from "lucide-react";
|
|
2
|
+
import { messageBubbleStyles } from "./message-bubble.styles";
|
|
3
|
+
import type { MessageBubbleProps } from "./message-bubble.types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Balão de mensagem. Variant `from` (me|them) controla cor + alinhamento.
|
|
7
|
+
* Status icons (Check / CheckCheck) renderizam apenas pra `from="me"`.
|
|
8
|
+
*/
|
|
9
|
+
export function MessageBubble({ msg }: MessageBubbleProps) {
|
|
10
|
+
const from = msg.from === "me" ? "me" : "them";
|
|
11
|
+
const { row, bubble, text, meta, time } = messageBubbleStyles({ from });
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className={row()}>
|
|
15
|
+
<div className={bubble()}>
|
|
16
|
+
<div className={text()}>{msg.text}</div>
|
|
17
|
+
<div className={meta()}>
|
|
18
|
+
<span className={time()}>{msg.time}</span>
|
|
19
|
+
{from === "me" &&
|
|
20
|
+
(msg.status === "read" ? (
|
|
21
|
+
<CheckCheck size={12} strokeWidth={2} />
|
|
22
|
+
) : (
|
|
23
|
+
<Check size={12} strokeWidth={2} />
|
|
24
|
+
))}
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Avatar, AvatarFallback } from "@/components/shadcn/avatar";
|
|
2
|
+
|
|
3
|
+
export type PersonAvatarSize = "sm" | "md" | "lg";
|
|
4
|
+
|
|
5
|
+
export type PersonAvatarProps = {
|
|
6
|
+
initials: string;
|
|
7
|
+
/** Cor de fundo do avatar — hex/rgb/CSS color (vem do mock do contato). */
|
|
8
|
+
hex: string;
|
|
9
|
+
/** sm=28px / md=36px / lg=40px. Default: md. */
|
|
10
|
+
size?: PersonAvatarSize;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const SIZE_CLASSES: Record<PersonAvatarSize, string> = {
|
|
14
|
+
sm: "size-[28px] text-caption-sm",
|
|
15
|
+
md: "size-[36px] text-body-sm font-normal",
|
|
16
|
+
lg: "size-[40px] text-body-md",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Avatar circular com `initials` em branco bold sobre `hex` como background.
|
|
21
|
+
* Usado em ConversationListItem, header de ConversationColumn e DetailsColumn.
|
|
22
|
+
*/
|
|
23
|
+
export function PersonAvatar({ initials, hex, size = "md" }: PersonAvatarProps) {
|
|
24
|
+
return (
|
|
25
|
+
<Avatar className={SIZE_CLASSES[size]} style={{ background: hex }}>
|
|
26
|
+
<AvatarFallback className="bg-transparent text-white font-bold">
|
|
27
|
+
{initials}
|
|
28
|
+
</AvatarFallback>
|
|
29
|
+
</Avatar>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { tv } from "@/utils/tv";
|
|
2
|
+
|
|
3
|
+
export const queueColumnStyles = tv({
|
|
4
|
+
slots: {
|
|
5
|
+
root: "flex flex-col w-[340px] shrink-0 bg-bg-surface border border-border-subtle rounded-radius-xl overflow-hidden",
|
|
6
|
+
header: "flex items-center gap-gp-sm px-pad-md pt-pad-md pb-pad-md",
|
|
7
|
+
title: "m-0 text-title-md text-fg-default flex-1 min-w-0",
|
|
8
|
+
searchWrap: "px-pad-md pb-pad-lg",
|
|
9
|
+
searchInner: "relative",
|
|
10
|
+
searchIcon:
|
|
11
|
+
"absolute left-pad-lg top-1/2 -translate-y-1/2 text-fg-muted pointer-events-none",
|
|
12
|
+
searchInput: "pl-[34px] pr-[30px]",
|
|
13
|
+
searchClear:
|
|
14
|
+
"absolute right-pad-md top-1/2 -translate-y-1/2 grid place-items-center size-[20px] rounded-radius-md text-fg-muted hover:bg-bg-muted hover:text-fg-default transition-colors",
|
|
15
|
+
chipsWrap: "px-pad-md pb-pad-2xl",
|
|
16
|
+
list: "flex-1 min-h-0 overflow-y-auto px-pad-md pb-pad-md flex flex-col gap-gp-xs",
|
|
17
|
+
empty: "py-pad-4xl px-pad-xl text-center text-body-xs font-normal text-fg-subtle",
|
|
18
|
+
},
|
|
19
|
+
});
|