@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,227 @@
|
|
|
1
|
+
import { CLIENTS_87 } from "./clientes-showcase-mocks";
|
|
2
|
+
import type {
|
|
3
|
+
AccountStatus,
|
|
4
|
+
BankAccount,
|
|
5
|
+
BankId,
|
|
6
|
+
FinanceClientRow,
|
|
7
|
+
FinanceTransaction,
|
|
8
|
+
PaymentMethod,
|
|
9
|
+
} from "./clientes-financeiro.types";
|
|
10
|
+
|
|
11
|
+
/* ── Banks (lookup com nome + cor temática) ─────────────────────── */
|
|
12
|
+
|
|
13
|
+
export const BANKS: Record<BankId, { name: string; color: string; initials: string }> = {
|
|
14
|
+
bb: { name: "Banco do Brasil", color: "#FAE128", initials: "BB" },
|
|
15
|
+
itau: { name: "Itaú", color: "#EC7000", initials: "IT" },
|
|
16
|
+
nubank: { name: "Nubank", color: "#820AD1", initials: "NU" },
|
|
17
|
+
santander: { name: "Santander", color: "#EC0000", initials: "SA" },
|
|
18
|
+
bradesco: { name: "Bradesco", color: "#CC092F", initials: "BR" },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const BANK_IDS: BankId[] = ["bb", "itau", "nubank", "santander", "bradesco"];
|
|
22
|
+
|
|
23
|
+
/** Contas mocadas pra o modal Sacar — opções fixas. */
|
|
24
|
+
export const SACAR_ACCOUNT_OPTIONS: BankAccount[] = [
|
|
25
|
+
{ bank: "bb", bankName: "Banco do Brasil", agency: "0001", account: "12345-6" },
|
|
26
|
+
{ bank: "nubank", bankName: "Nubank", agency: "0001", account: "98765-4" },
|
|
27
|
+
{ bank: "itau", bankName: "Itaú", agency: "1234", account: "55555-1" },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
/* ── Helpers de geração ─────────────────────────────────────────── */
|
|
31
|
+
|
|
32
|
+
/** Gera dado bancário pseudo-determinístico baseado no índice do cliente. */
|
|
33
|
+
function makeAccount(idx: number): BankAccount {
|
|
34
|
+
const bank = BANK_IDS[idx % BANK_IDS.length];
|
|
35
|
+
const agency = String(1000 + (idx * 37) % 9000).padStart(4, "0");
|
|
36
|
+
const accountSeq = String(10000 + (idx * 113) % 89999).padStart(5, "0");
|
|
37
|
+
const dv = (idx * 7) % 10;
|
|
38
|
+
return {
|
|
39
|
+
bank,
|
|
40
|
+
bankName: BANKS[bank].name,
|
|
41
|
+
agency,
|
|
42
|
+
account: `${accountSeq}-${dv}`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Saldo pseudo-determinístico — varia entre R$ 150 e R$ 25.000. */
|
|
47
|
+
function makeBalance(idx: number): number {
|
|
48
|
+
const base = 150 + (idx * 317) % 24850;
|
|
49
|
+
// arredonda pra 2 casas
|
|
50
|
+
return Math.round(base * 100) / 100;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Razão social pseudo-determinística — combina nome do contato + sufixo. */
|
|
54
|
+
const COMPANY_SUFFIXES = [
|
|
55
|
+
"Solar Energy LTDA",
|
|
56
|
+
"Energia Renovável ME",
|
|
57
|
+
"Eco Solutions S.A.",
|
|
58
|
+
"Green Power LTDA",
|
|
59
|
+
"Solar Tech ME",
|
|
60
|
+
"Energy Brasil S.A.",
|
|
61
|
+
];
|
|
62
|
+
function makeCompanyName(idx: number, contactName: string): string {
|
|
63
|
+
// Pega o primeiro nome do contato e combina com sufixo PJ
|
|
64
|
+
const firstName = contactName.split(" ")[0];
|
|
65
|
+
const suffix = COMPANY_SUFFIXES[idx % COMPANY_SUFFIXES.length];
|
|
66
|
+
return `${firstName} ${suffix}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** CNPJ pseudo-determinístico — gera formato XX.XXX.XXX/0001-XX baseado no idx. */
|
|
70
|
+
function makeCNPJ(idx: number): string {
|
|
71
|
+
// Bloco 1: 2 dígitos
|
|
72
|
+
const b1 = String(10 + (idx * 13) % 89).padStart(2, "0");
|
|
73
|
+
// Bloco 2: 3 dígitos
|
|
74
|
+
const b2 = String(100 + (idx * 71) % 899).padStart(3, "0");
|
|
75
|
+
// Bloco 3: 3 dígitos
|
|
76
|
+
const b3 = String(100 + (idx * 127) % 899).padStart(3, "0");
|
|
77
|
+
// DV: 2 dígitos
|
|
78
|
+
const dv = String(10 + (idx * 53) % 89).padStart(2, "0");
|
|
79
|
+
return `${b1}.${b2}.${b3}/0001-${dv}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* ── Situação da conta (badge + eixo do Kanban) ─────────────────── */
|
|
83
|
+
|
|
84
|
+
export const ACCOUNT_STATUS: Record<
|
|
85
|
+
AccountStatus,
|
|
86
|
+
{ label: string; color: "warning" | "success" | "info" | "danger" }
|
|
87
|
+
> = {
|
|
88
|
+
pendente: { label: "Pendente", color: "warning" },
|
|
89
|
+
ativo: { label: "Ativo", color: "success" },
|
|
90
|
+
negociacao: { label: "Em negociação", color: "info" },
|
|
91
|
+
bloqueado: { label: "Bloqueado", color: "danger" },
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const ACCOUNT_STATUS_IDS: AccountStatus[] = [
|
|
95
|
+
"ativo",
|
|
96
|
+
"ativo",
|
|
97
|
+
"pendente",
|
|
98
|
+
"ativo",
|
|
99
|
+
"negociacao",
|
|
100
|
+
"ativo",
|
|
101
|
+
"bloqueado",
|
|
102
|
+
]; // distribuição enviesada p/ "ativo" (realista) via módulo do índice
|
|
103
|
+
|
|
104
|
+
const PAYMENT_METHOD_SETS: PaymentMethod[][] = [
|
|
105
|
+
["pix"],
|
|
106
|
+
["pix", "ted"],
|
|
107
|
+
["pix", "boleto"],
|
|
108
|
+
["pix", "ted", "boleto"],
|
|
109
|
+
["ted", "boleto"],
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
const TRANSACTION_DESCRIPTIONS: Record<"in" | "out", string[]> = {
|
|
113
|
+
in: ["Comissão de venda", "Bônus de meta", "Repasse de royalty", "Estorno"],
|
|
114
|
+
out: ["Saque para conta", "Taxa de serviço", "Antecipação", "Ajuste de saldo"],
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const NOW = new Date("2026-06-10T12:00:00Z").getTime();
|
|
118
|
+
const DAY = 86400000;
|
|
119
|
+
|
|
120
|
+
/** Status pseudo-determinístico baseado no índice. */
|
|
121
|
+
function makeAccountStatus(idx: number): AccountStatus {
|
|
122
|
+
return ACCOUNT_STATUS_IDS[idx % ACCOUNT_STATUS_IDS.length];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Volume mensal pseudo-determinístico — R$ 2k a R$ 80k. */
|
|
126
|
+
function makeMonthlyVolume(idx: number): number {
|
|
127
|
+
return Math.round((2000 + ((idx * 911) % 78000)) * 100) / 100;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Taxa de comissão 4%–14% (1 casa). */
|
|
131
|
+
function makeCommissionRate(idx: number): number {
|
|
132
|
+
return Math.round((4 + ((idx * 37) % 100) / 10) * 10) / 10;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Última movimentação — entre 0 e ~45 dias atrás. */
|
|
136
|
+
function makeLastMovement(idx: number): number {
|
|
137
|
+
return NOW - ((idx * 53) % 45) * DAY - ((idx * 17) % 24) * 3600000;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Extrato pseudo-determinístico — 4 a 6 movimentações recentes. */
|
|
141
|
+
function makeTransactions(idx: number, lastMovement: number): FinanceTransaction[] {
|
|
142
|
+
const count = 4 + (idx % 3);
|
|
143
|
+
return Array.from({ length: count }, (_, i) => {
|
|
144
|
+
const type: "in" | "out" = (idx + i) % 3 === 0 ? "out" : "in";
|
|
145
|
+
const pool = TRANSACTION_DESCRIPTIONS[type];
|
|
146
|
+
return {
|
|
147
|
+
id: `TX-${idx}-${i}`,
|
|
148
|
+
date: lastMovement - i * (2 * DAY + ((idx * 7) % 5) * DAY),
|
|
149
|
+
type,
|
|
150
|
+
amount: Math.round((120 + ((idx * 211 + i * 67) % 9800)) * 100) / 100,
|
|
151
|
+
description: pool[(idx + i) % pool.length],
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* ── Dataset financeiro — 87 clientes com banco + saldo ─────────── */
|
|
157
|
+
|
|
158
|
+
export const FINANCE_CLIENTS: FinanceClientRow[] = CLIENTS_87.map((c, idx) => {
|
|
159
|
+
const lastMovement = makeLastMovement(idx);
|
|
160
|
+
return {
|
|
161
|
+
...c,
|
|
162
|
+
// Force categoria "licenciado" pra tema financeiro (afiliados/parceiros PJ)
|
|
163
|
+
categoryId: "licenciado",
|
|
164
|
+
bankAccount: makeAccount(idx),
|
|
165
|
+
availableBalance: makeBalance(idx),
|
|
166
|
+
monthlyVolume: makeMonthlyVolume(idx),
|
|
167
|
+
commissionRate: makeCommissionRate(idx),
|
|
168
|
+
accountStatus: makeAccountStatus(idx),
|
|
169
|
+
autoWithdraw: idx % 3 !== 0, // ~2/3 com saque automático
|
|
170
|
+
paymentMethods: PAYMENT_METHOD_SETS[idx % PAYMENT_METHOD_SETS.length],
|
|
171
|
+
lastMovement,
|
|
172
|
+
transactions: makeTransactions(idx, lastMovement),
|
|
173
|
+
cnpj: makeCNPJ(idx),
|
|
174
|
+
companyName: makeCompanyName(idx, c.name),
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
/* ── Formatters ─────────────────────────────────────────────────── */
|
|
179
|
+
|
|
180
|
+
export function formatBRL(value: number): string {
|
|
181
|
+
return new Intl.NumberFormat("pt-BR", {
|
|
182
|
+
style: "currency",
|
|
183
|
+
currency: "BRL",
|
|
184
|
+
}).format(value);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function formatBankAccountShort(acc: BankAccount): string {
|
|
188
|
+
// Ex: "BB · Ag 0001 · 12345-6"
|
|
189
|
+
return `${BANKS[acc.bank].initials} · Ag ${acc.agency} · ${acc.account}`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* ── Estatísticas agregadas (KPIs do header da página) ──────────── */
|
|
193
|
+
|
|
194
|
+
export const FINANCE_KPIS = {
|
|
195
|
+
/** Total disponível pra saque em todos os clientes. */
|
|
196
|
+
totalAvailable: FINANCE_CLIENTS.reduce((sum, c) => sum + c.availableBalance, 0),
|
|
197
|
+
/** Quantidade de clientes com saldo > R$ 5.000 (pra destacar high-value). */
|
|
198
|
+
highValueCount: FINANCE_CLIENTS.filter((c) => c.availableBalance > 5000).length,
|
|
199
|
+
/** Saldo médio. */
|
|
200
|
+
averageBalance: 0,
|
|
201
|
+
/** Contas em risco (negociação + bloqueado) — alinha com a preset "Inadimplentes". */
|
|
202
|
+
atRiskCount: FINANCE_CLIENTS.filter(
|
|
203
|
+
(c) => c.accountStatus === "negociacao" || c.accountStatus === "bloqueado",
|
|
204
|
+
).length,
|
|
205
|
+
};
|
|
206
|
+
FINANCE_KPIS.averageBalance =
|
|
207
|
+
FINANCE_KPIS.totalAvailable / FINANCE_CLIENTS.length;
|
|
208
|
+
|
|
209
|
+
/** Label curto pra movimentação relativa ("hoje", "há 3 dias"). */
|
|
210
|
+
export function formatRelativeDays(ts: number): string {
|
|
211
|
+
const days = Math.floor((NOW - ts) / DAY);
|
|
212
|
+
if (days <= 0) return "hoje";
|
|
213
|
+
if (days === 1) return "ontem";
|
|
214
|
+
if (days < 30) return `há ${days} dias`;
|
|
215
|
+
const months = Math.floor(days / 30);
|
|
216
|
+
return months === 1 ? "há 1 mês" : `há ${months} meses`;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Data curta "09 fev" / "09 fev, 14:30" pro extrato. */
|
|
220
|
+
export function formatDateTimeShort(ts: number): string {
|
|
221
|
+
return new Date(ts).toLocaleString("pt-BR", {
|
|
222
|
+
day: "2-digit",
|
|
223
|
+
month: "short",
|
|
224
|
+
hour: "2-digit",
|
|
225
|
+
minute: "2-digit",
|
|
226
|
+
});
|
|
227
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types do ClientesFinanceiroShowcase — extensão financeira do ClientesShowcase.
|
|
3
|
+
*
|
|
4
|
+
* Reaproveita estrutura de cliente mas adiciona dados bancários + saldo
|
|
5
|
+
* disponível pra suportar a action de Saque.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ClientRow } from "./_table-data";
|
|
9
|
+
|
|
10
|
+
/** Banco suportado nos mocks (com logo/cor próprios). */
|
|
11
|
+
export type BankId = "bb" | "itau" | "nubank" | "santander" | "bradesco";
|
|
12
|
+
|
|
13
|
+
export type BankAccount = {
|
|
14
|
+
bank: BankId;
|
|
15
|
+
bankName: string;
|
|
16
|
+
agency: string; // ex: "1234"
|
|
17
|
+
account: string; // ex: "98765-4"
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Situação financeira da conta (pipeline) — vira coluna `badge` (chip colorido)
|
|
22
|
+
* E o eixo do board Kanban. Fluxo: pendente → ativo → negociação → bloqueado.
|
|
23
|
+
*/
|
|
24
|
+
export type AccountStatus = "pendente" | "ativo" | "negociacao" | "bloqueado";
|
|
25
|
+
|
|
26
|
+
/** Método de recebimento aceito pelo licenciado (coluna `tags`). */
|
|
27
|
+
export type PaymentMethod = "pix" | "ted" | "boleto";
|
|
28
|
+
|
|
29
|
+
/** Movimentação financeira — usada no extrato da row expansion. */
|
|
30
|
+
export type FinanceTransaction = {
|
|
31
|
+
id: string;
|
|
32
|
+
/** Timestamp (ms) da movimentação. */
|
|
33
|
+
date: number;
|
|
34
|
+
/** Entrada (crédito) ou saída (saque/débito). */
|
|
35
|
+
type: "in" | "out";
|
|
36
|
+
/** Valor em BRL (sempre positivo; `type` define o sinal). */
|
|
37
|
+
amount: number;
|
|
38
|
+
description: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** Cliente com dados financeiros — estende ClientRow. */
|
|
42
|
+
export type FinanceClientRow = ClientRow & {
|
|
43
|
+
/** Saldo disponível pra saque (BRL). */
|
|
44
|
+
availableBalance: number;
|
|
45
|
+
/** Volume financeiro movimentado no mês (BRL). */
|
|
46
|
+
monthlyVolume: number;
|
|
47
|
+
/** Taxa de comissão do licenciado (0–100, editável inline). */
|
|
48
|
+
commissionRate: number;
|
|
49
|
+
/** Situação da conta — chip + eixo do Kanban. */
|
|
50
|
+
accountStatus: AccountStatus;
|
|
51
|
+
/** Saque automático habilitado (boolean editável inline). */
|
|
52
|
+
autoWithdraw: boolean;
|
|
53
|
+
/** Métodos de recebimento aceitos (tags). */
|
|
54
|
+
paymentMethods: PaymentMethod[];
|
|
55
|
+
/** Timestamp (ms) da última movimentação financeira. */
|
|
56
|
+
lastMovement: number;
|
|
57
|
+
/** Extrato das últimas movimentações (row expansion). */
|
|
58
|
+
transactions: FinanceTransaction[];
|
|
59
|
+
/** Dados bancários do cliente. */
|
|
60
|
+
bankAccount: BankAccount;
|
|
61
|
+
/** CNPJ do cliente (formato XX.XXX.XXX/0001-XX). */
|
|
62
|
+
cnpj: string;
|
|
63
|
+
/** Razão social da empresa (nome PJ). */
|
|
64
|
+
companyName: string;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Form data do modal Sacar — usuário escolhe valor + conta destino.
|
|
69
|
+
* `clientId` é injetado pelo trigger (row da action).
|
|
70
|
+
*/
|
|
71
|
+
export type SacarFormData = {
|
|
72
|
+
clientId: string;
|
|
73
|
+
amount: number;
|
|
74
|
+
selectedAccountBank: BankId;
|
|
75
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLIENTS_MOCK,
|
|
3
|
+
STATUSES,
|
|
4
|
+
CATEGORIES,
|
|
5
|
+
AGENTS,
|
|
6
|
+
type ClientRow,
|
|
7
|
+
} from "./_table-data";
|
|
8
|
+
|
|
9
|
+
/* ── Dataset 87 clientes (bate com badge do print) ─────────────── */
|
|
10
|
+
|
|
11
|
+
export const CLIENTS_87: ClientRow[] = Array.from({ length: 9 }, (_, batch) =>
|
|
12
|
+
CLIENTS_MOCK.map((row, i) => ({
|
|
13
|
+
...row,
|
|
14
|
+
id: `CLI-${2400 + batch * 10 + i}`,
|
|
15
|
+
})),
|
|
16
|
+
)
|
|
17
|
+
.flat()
|
|
18
|
+
.slice(0, 87);
|
|
19
|
+
|
|
20
|
+
/* ── Lookups por categoria de status (column color mapping) ────── */
|
|
21
|
+
|
|
22
|
+
export const STATUS_COLOR_MAP: Record<string, string> = {
|
|
23
|
+
active: "success",
|
|
24
|
+
pending: "warning",
|
|
25
|
+
paused: "info",
|
|
26
|
+
inactive: "neutral",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const STATUS_DOT: Record<string, string> = {
|
|
30
|
+
active: "var(--color-fg-success)",
|
|
31
|
+
pending: "var(--color-fg-warning)",
|
|
32
|
+
paused: "var(--color-fg-info)",
|
|
33
|
+
inactive: "var(--color-fg-muted)",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const CATEGORY_KIND: Record<string, "warning" | "info" | "success"> = {
|
|
37
|
+
royal: "warning",
|
|
38
|
+
licenciado: "info",
|
|
39
|
+
lead: "success",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/* ── Options pra filters/selects ───────────────────────────────── */
|
|
43
|
+
|
|
44
|
+
export const STATUS_OPTIONS = Object.entries(STATUSES).map(([v, m]) => ({
|
|
45
|
+
value: v,
|
|
46
|
+
label: m.label,
|
|
47
|
+
color: STATUS_COLOR_MAP[v] ?? "neutral",
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
export const CATEGORY_OPTIONS = Object.entries(CATEGORIES).map(([v, m]) => ({
|
|
51
|
+
value: v,
|
|
52
|
+
label: m.label,
|
|
53
|
+
color: m.kind === "neutral" ? "neutral" : m.kind,
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
export const AGENT_OPTIONS = Object.entries(AGENTS).map(([v, a]) => ({
|
|
57
|
+
value: v,
|
|
58
|
+
label: a.name,
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
/* ── Avatar lookup pro column type "user" ──────────────────────── */
|
|
62
|
+
|
|
63
|
+
export const AGENTS_LOOKUP = Object.fromEntries(
|
|
64
|
+
Object.entries(AGENTS).map(([id, a]) => [
|
|
65
|
+
id,
|
|
66
|
+
{
|
|
67
|
+
name: a.name,
|
|
68
|
+
initials: a.name
|
|
69
|
+
.split(" ")
|
|
70
|
+
.map((s) => s[0])
|
|
71
|
+
.join("")
|
|
72
|
+
.slice(0, 2),
|
|
73
|
+
color: a.color,
|
|
74
|
+
},
|
|
75
|
+
]),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
/* ── Kanban columns (1 por status) ─────────────────────────────── */
|
|
79
|
+
|
|
80
|
+
export const KANBAN_COLUMNS = Object.entries(STATUSES).map(([id, s]) => ({
|
|
81
|
+
id,
|
|
82
|
+
label: s.label,
|
|
83
|
+
dotColor: STATUS_DOT[id] ?? "var(--color-fg-muted)",
|
|
84
|
+
}));
|
|
85
|
+
|
|
86
|
+
/* ── Cidades pro select do drawer ──────────────────────────────── */
|
|
87
|
+
|
|
88
|
+
export const CITIES = [
|
|
89
|
+
"São Paulo, SP",
|
|
90
|
+
"Rio de Janeiro, RJ",
|
|
91
|
+
"Belo Horizonte, MG",
|
|
92
|
+
"Porto Alegre, RS",
|
|
93
|
+
"Curitiba, PR",
|
|
94
|
+
"Recife, PE",
|
|
95
|
+
"Salvador, BA",
|
|
96
|
+
"Fortaleza, CE",
|
|
97
|
+
"Brasília, DF",
|
|
98
|
+
"Manaus, AM",
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
/* ── Helpers de formatação ─────────────────────────────────────── */
|
|
102
|
+
|
|
103
|
+
export function formatBRL(value: number) {
|
|
104
|
+
return value.toLocaleString("pt-BR", {
|
|
105
|
+
style: "currency",
|
|
106
|
+
currency: "BRL",
|
|
107
|
+
minimumFractionDigits: 2,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function formatShortDate(ms: number) {
|
|
112
|
+
if (!ms) return "—";
|
|
113
|
+
const d = new Date(ms);
|
|
114
|
+
const day = d.getDate().toString().padStart(2, "0");
|
|
115
|
+
const months = ["jan", "fev", "mar", "abr", "mai", "jun", "jul", "ago", "set", "out", "nov", "dez"];
|
|
116
|
+
return `${day} ${months[d.getMonth()]}`;
|
|
117
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { tv } from "@/utils/tv";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Layout em grid de 2 colunas usado dentro do drawer pra pares de campos
|
|
5
|
+
* (Email + WhatsApp, Cidade + Valor).
|
|
6
|
+
* `gap-form-gap` (20px) = token DS pra spacing entre FormField units (L-024).
|
|
7
|
+
*/
|
|
8
|
+
export const drawerFormStyles = tv({
|
|
9
|
+
slots: {
|
|
10
|
+
twoCols: "grid grid-cols-2 gap-form-gap",
|
|
11
|
+
statusDot: "inline-block size-[8px] rounded-radius-full mr-gp-xs shrink-0",
|
|
12
|
+
},
|
|
13
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { STATUSES, CATEGORIES } from "./_table-data";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Payload do formulário do drawer "Novo cliente".
|
|
5
|
+
* Consumido por <NovoClienteDrawer onSubmit={...}> no main page.
|
|
6
|
+
*/
|
|
7
|
+
export type NovoClienteFormData = {
|
|
8
|
+
name: string;
|
|
9
|
+
email: string;
|
|
10
|
+
whatsapp: string;
|
|
11
|
+
statusId: keyof typeof STATUSES;
|
|
12
|
+
categoryId: keyof typeof CATEGORIES;
|
|
13
|
+
city: string;
|
|
14
|
+
value: number;
|
|
15
|
+
notes: string;
|
|
16
|
+
};
|
|
@@ -0,0 +1,241 @@
|
|
|
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
|
+
} from "@/components/ui/FormField";
|
|
9
|
+
import { ChipGroup, ChipGroupItem } from "@/components/ui/Chip";
|
|
10
|
+
import { Switch } from "@/components/shadcn/switch";
|
|
11
|
+
import { AGENTS } from "../../_table-data";
|
|
12
|
+
import { ACCOUNT_STATUS, BANKS } from "../../clientes-financeiro-mocks";
|
|
13
|
+
import type {
|
|
14
|
+
AccountStatus,
|
|
15
|
+
BankId,
|
|
16
|
+
FinanceClientRow,
|
|
17
|
+
PaymentMethod,
|
|
18
|
+
} from "../../clientes-financeiro.types";
|
|
19
|
+
|
|
20
|
+
const PAYMENT_METHODS: { value: PaymentMethod; label: string }[] = [
|
|
21
|
+
{ value: "pix", label: "PIX" },
|
|
22
|
+
{ value: "ted", label: "TED" },
|
|
23
|
+
{ value: "boleto", label: "Boleto" },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const BANK_OPTIONS = (Object.keys(BANKS) as BankId[]).map((id) => ({
|
|
27
|
+
value: id,
|
|
28
|
+
label: BANKS[id].name,
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
const AGENT_OPTIONS = Object.entries(AGENTS).map(([id, a]) => ({
|
|
32
|
+
value: id,
|
|
33
|
+
label: a.name,
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
const STATUS_OPTIONS = (
|
|
37
|
+
Object.entries(ACCOUNT_STATUS) as [AccountStatus, (typeof ACCOUNT_STATUS)[AccountStatus]][]
|
|
38
|
+
).map(([value, m]) => ({ value, label: m.label, color: m.color }));
|
|
39
|
+
|
|
40
|
+
/** Campos editáveis da row financeira. */
|
|
41
|
+
export type FinanceEditPatch = {
|
|
42
|
+
name: string;
|
|
43
|
+
email: string;
|
|
44
|
+
phone: string;
|
|
45
|
+
companyName: string;
|
|
46
|
+
cnpj: string;
|
|
47
|
+
accountStatus: AccountStatus;
|
|
48
|
+
bank: BankId;
|
|
49
|
+
commissionRate: number;
|
|
50
|
+
autoWithdraw: boolean;
|
|
51
|
+
paymentMethods: PaymentMethod[];
|
|
52
|
+
agentId: string;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type EditarFinanceDrawerProps = {
|
|
56
|
+
/** Row em edição — null fecha o drawer. */
|
|
57
|
+
client: FinanceClientRow | null;
|
|
58
|
+
onOpenChange: (open: boolean) => void;
|
|
59
|
+
/** Salva as alterações (mock no showcase). */
|
|
60
|
+
onSave?: (id: string, patch: FinanceEditPatch) => void;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Drawer "Editar licenciado" — campos REAIS da row financeira (não o form
|
|
65
|
+
* genérico de criação). Panel + FormField (Input/Select) + ChipGroup + Switch.
|
|
66
|
+
*/
|
|
67
|
+
export function EditarFinanceDrawer({
|
|
68
|
+
client,
|
|
69
|
+
onOpenChange,
|
|
70
|
+
onSave,
|
|
71
|
+
}: EditarFinanceDrawerProps) {
|
|
72
|
+
const [form, setForm] = useState<FinanceEditPatch | null>(null);
|
|
73
|
+
|
|
74
|
+
// (Re)hidrata o form ao abrir/trocar de cliente.
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (!client) return;
|
|
77
|
+
setForm({
|
|
78
|
+
name: client.name,
|
|
79
|
+
email: client.email,
|
|
80
|
+
phone: client.phone,
|
|
81
|
+
companyName: client.companyName,
|
|
82
|
+
cnpj: client.cnpj,
|
|
83
|
+
accountStatus: client.accountStatus,
|
|
84
|
+
bank: client.bankAccount.bank,
|
|
85
|
+
commissionRate: client.commissionRate,
|
|
86
|
+
autoWithdraw: client.autoWithdraw,
|
|
87
|
+
paymentMethods: client.paymentMethods,
|
|
88
|
+
agentId: client.agentId as string,
|
|
89
|
+
});
|
|
90
|
+
}, [client]);
|
|
91
|
+
|
|
92
|
+
const patch = (p: Partial<FinanceEditPatch>) =>
|
|
93
|
+
setForm((f) => (f ? { ...f, ...p } : f));
|
|
94
|
+
|
|
95
|
+
const canSave = !!form && form.name.trim().length > 0;
|
|
96
|
+
|
|
97
|
+
const handleSave = () => {
|
|
98
|
+
if (!client || !form || !canSave) return;
|
|
99
|
+
onSave?.(client.id, form);
|
|
100
|
+
onOpenChange(false);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<Panel
|
|
105
|
+
open={client !== null}
|
|
106
|
+
onOpenChange={onOpenChange}
|
|
107
|
+
title={client ? `Editar — ${client.name}` : "Editar licenciado"}
|
|
108
|
+
side="right"
|
|
109
|
+
size="md"
|
|
110
|
+
footer={
|
|
111
|
+
<>
|
|
112
|
+
<Button variant="outline" color="secondary" size="md" onClick={() => onOpenChange(false)}>
|
|
113
|
+
Cancelar
|
|
114
|
+
</Button>
|
|
115
|
+
<Button
|
|
116
|
+
variant="filled"
|
|
117
|
+
color="primary"
|
|
118
|
+
size="md"
|
|
119
|
+
disabled={!canSave}
|
|
120
|
+
onClick={handleSave}
|
|
121
|
+
>
|
|
122
|
+
Salvar alterações
|
|
123
|
+
</Button>
|
|
124
|
+
</>
|
|
125
|
+
}
|
|
126
|
+
>
|
|
127
|
+
{form && (
|
|
128
|
+
<>
|
|
129
|
+
<FormFieldInput
|
|
130
|
+
label="Licenciado"
|
|
131
|
+
required
|
|
132
|
+
value={form.name}
|
|
133
|
+
onChange={(e) => patch({ name: e.target.value })}
|
|
134
|
+
/>
|
|
135
|
+
|
|
136
|
+
<div className="grid grid-cols-2 gap-form-gap">
|
|
137
|
+
<FormFieldInput
|
|
138
|
+
label="Email"
|
|
139
|
+
type="email"
|
|
140
|
+
value={form.email}
|
|
141
|
+
onChange={(e) => patch({ email: e.target.value })}
|
|
142
|
+
/>
|
|
143
|
+
<FormFieldInput
|
|
144
|
+
label="Telefone"
|
|
145
|
+
type="tel"
|
|
146
|
+
value={form.phone}
|
|
147
|
+
onChange={(e) => patch({ phone: e.target.value })}
|
|
148
|
+
/>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<FormFieldInput
|
|
152
|
+
label="Razão Social"
|
|
153
|
+
value={form.companyName}
|
|
154
|
+
onChange={(e) => patch({ companyName: e.target.value })}
|
|
155
|
+
/>
|
|
156
|
+
<FormFieldInput
|
|
157
|
+
label="CNPJ"
|
|
158
|
+
value={form.cnpj}
|
|
159
|
+
onChange={(e) => patch({ cnpj: e.target.value })}
|
|
160
|
+
/>
|
|
161
|
+
|
|
162
|
+
<FormField label="Situação">
|
|
163
|
+
{() => (
|
|
164
|
+
<ChipGroup
|
|
165
|
+
type="single"
|
|
166
|
+
value={form.accountStatus}
|
|
167
|
+
onValueChange={(v) => v && patch({ accountStatus: v as AccountStatus })}
|
|
168
|
+
size="md"
|
|
169
|
+
shape="pill"
|
|
170
|
+
ariaLabel="Situação da conta"
|
|
171
|
+
>
|
|
172
|
+
{STATUS_OPTIONS.map((o) => (
|
|
173
|
+
<ChipGroupItem key={o.value} value={o.value}>
|
|
174
|
+
{o.label}
|
|
175
|
+
</ChipGroupItem>
|
|
176
|
+
))}
|
|
177
|
+
</ChipGroup>
|
|
178
|
+
)}
|
|
179
|
+
</FormField>
|
|
180
|
+
|
|
181
|
+
<div className="grid grid-cols-2 gap-form-gap">
|
|
182
|
+
<FormFieldSelect
|
|
183
|
+
label="Banco"
|
|
184
|
+
value={form.bank}
|
|
185
|
+
onValueChange={(v) => patch({ bank: v as BankId })}
|
|
186
|
+
options={BANK_OPTIONS}
|
|
187
|
+
/>
|
|
188
|
+
<FormFieldInput
|
|
189
|
+
label="Comissão (%)"
|
|
190
|
+
type="number"
|
|
191
|
+
value={String(form.commissionRate)}
|
|
192
|
+
onChange={(e) => patch({ commissionRate: Number(e.target.value) })}
|
|
193
|
+
min={0}
|
|
194
|
+
max={100}
|
|
195
|
+
step={0.1}
|
|
196
|
+
/>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<FormFieldSelect
|
|
200
|
+
label="Gestor da conta"
|
|
201
|
+
value={form.agentId}
|
|
202
|
+
onValueChange={(v) => patch({ agentId: v })}
|
|
203
|
+
options={AGENT_OPTIONS}
|
|
204
|
+
/>
|
|
205
|
+
|
|
206
|
+
<FormField label="Métodos de recebimento">
|
|
207
|
+
{() => (
|
|
208
|
+
<ChipGroup
|
|
209
|
+
type="multiple"
|
|
210
|
+
value={form.paymentMethods}
|
|
211
|
+
onValueChange={(v) => patch({ paymentMethods: v as PaymentMethod[] })}
|
|
212
|
+
size="md"
|
|
213
|
+
shape="rounded"
|
|
214
|
+
ariaLabel="Métodos de recebimento"
|
|
215
|
+
>
|
|
216
|
+
{PAYMENT_METHODS.map((m) => (
|
|
217
|
+
<ChipGroupItem key={m.value} value={m.value}>
|
|
218
|
+
{m.label}
|
|
219
|
+
</ChipGroupItem>
|
|
220
|
+
))}
|
|
221
|
+
</ChipGroup>
|
|
222
|
+
)}
|
|
223
|
+
</FormField>
|
|
224
|
+
|
|
225
|
+
<FormField
|
|
226
|
+
label="Saque automático"
|
|
227
|
+
helperText="Quando ligado, saques são processados automaticamente."
|
|
228
|
+
>
|
|
229
|
+
{() => (
|
|
230
|
+
<Switch
|
|
231
|
+
checked={form.autoWithdraw}
|
|
232
|
+
onCheckedChange={(next) => patch({ autoWithdraw: next })}
|
|
233
|
+
aria-label="Saque automático"
|
|
234
|
+
/>
|
|
235
|
+
)}
|
|
236
|
+
</FormField>
|
|
237
|
+
</>
|
|
238
|
+
)}
|
|
239
|
+
</Panel>
|
|
240
|
+
);
|
|
241
|
+
}
|