@fayz-ai/plugin-financial 0.1.1

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 (98) hide show
  1. package/dist/FinancialContext.d.ts +44 -0
  2. package/dist/FinancialContext.d.ts.map +1 -0
  3. package/dist/FinancialPage.d.ts +18 -0
  4. package/dist/FinancialPage.d.ts.map +1 -0
  5. package/dist/components/FinancialGeneralSettings.d.ts +3 -0
  6. package/dist/components/FinancialGeneralSettings.d.ts.map +1 -0
  7. package/dist/components/FinancialOnboarding.d.ts +5 -0
  8. package/dist/components/FinancialOnboarding.d.ts.map +1 -0
  9. package/dist/components/FinancialSettings.d.ts +8 -0
  10. package/dist/components/FinancialSettings.d.ts.map +1 -0
  11. package/dist/components/PaymentModal.d.ts +8 -0
  12. package/dist/components/PaymentModal.d.ts.map +1 -0
  13. package/dist/data/index.d.ts +3 -0
  14. package/dist/data/index.d.ts.map +1 -0
  15. package/dist/data/mock.d.ts +9 -0
  16. package/dist/data/mock.d.ts.map +1 -0
  17. package/dist/data/supabase.d.ts +3 -0
  18. package/dist/data/supabase.d.ts.map +1 -0
  19. package/dist/data/types.d.ts +27 -0
  20. package/dist/data/types.d.ts.map +1 -0
  21. package/dist/index.cjs +4937 -0
  22. package/dist/index.cjs.map +1 -0
  23. package/dist/index.d.ts +91 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +4930 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/lib/tenant.d.ts +3 -0
  28. package/dist/lib/tenant.d.ts.map +1 -0
  29. package/dist/locales/en.d.ts +2 -0
  30. package/dist/locales/en.d.ts.map +1 -0
  31. package/dist/locales/index.d.ts +2 -0
  32. package/dist/locales/index.d.ts.map +1 -0
  33. package/dist/locales/pt-BR.d.ts +2 -0
  34. package/dist/locales/pt-BR.d.ts.map +1 -0
  35. package/dist/registries.d.ts +3 -0
  36. package/dist/registries.d.ts.map +1 -0
  37. package/dist/store.d.ts +38 -0
  38. package/dist/store.d.ts.map +1 -0
  39. package/dist/types.d.ts +302 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/views/CardsView.d.ts +3 -0
  42. package/dist/views/CardsView.d.ts.map +1 -0
  43. package/dist/views/CashRegistersView.d.ts +3 -0
  44. package/dist/views/CashRegistersView.d.ts.map +1 -0
  45. package/dist/views/CommissionsView.d.ts +3 -0
  46. package/dist/views/CommissionsView.d.ts.map +1 -0
  47. package/dist/views/InvoiceDetailView.d.ts +9 -0
  48. package/dist/views/InvoiceDetailView.d.ts.map +1 -0
  49. package/dist/views/InvoiceFormView.d.ts +13 -0
  50. package/dist/views/InvoiceFormView.d.ts.map +1 -0
  51. package/dist/views/InvoiceListView.d.ts +8 -0
  52. package/dist/views/InvoiceListView.d.ts.map +1 -0
  53. package/dist/views/PayablesView.d.ts +7 -0
  54. package/dist/views/PayablesView.d.ts.map +1 -0
  55. package/dist/views/ReceivablesView.d.ts +7 -0
  56. package/dist/views/ReceivablesView.d.ts.map +1 -0
  57. package/dist/views/StatementsView.d.ts +3 -0
  58. package/dist/views/StatementsView.d.ts.map +1 -0
  59. package/dist/views/SummaryView.d.ts +9 -0
  60. package/dist/views/SummaryView.d.ts.map +1 -0
  61. package/dist/views/dashboardWidgets.d.ts +11 -0
  62. package/dist/views/dashboardWidgets.d.ts.map +1 -0
  63. package/package.json +55 -0
  64. package/src/FinancialContext.tsx +47 -0
  65. package/src/FinancialPage.tsx +261 -0
  66. package/src/README.md +155 -0
  67. package/src/components/FinancialGeneralSettings.tsx +26 -0
  68. package/src/components/FinancialOnboarding.tsx +87 -0
  69. package/src/components/FinancialSettings.tsx +31 -0
  70. package/src/components/PaymentModal.tsx +322 -0
  71. package/src/data/index.ts +2 -0
  72. package/src/data/mock.ts +520 -0
  73. package/src/data/supabase.ts +509 -0
  74. package/src/data/types.ts +53 -0
  75. package/src/index.ts +307 -0
  76. package/src/lib/tenant.ts +3 -0
  77. package/src/locales/en.ts +331 -0
  78. package/src/locales/index.ts +7 -0
  79. package/src/locales/pt-BR.ts +331 -0
  80. package/src/migrations/001_financial_base.sql +109 -0
  81. package/src/migrations/002_chart_of_accounts.sql +28 -0
  82. package/src/migrations/003_card_brands.sql +12 -0
  83. package/src/migrations/004_order_to_cash.sql +164 -0
  84. package/src/migrations/005_seed_defaults.sql +38 -0
  85. package/src/registries.ts +305 -0
  86. package/src/store.ts +221 -0
  87. package/src/types.ts +359 -0
  88. package/src/views/CardsView.tsx +74 -0
  89. package/src/views/CashRegistersView.tsx +597 -0
  90. package/src/views/CommissionsView.tsx +18 -0
  91. package/src/views/InvoiceDetailView.tsx +595 -0
  92. package/src/views/InvoiceFormView.tsx +900 -0
  93. package/src/views/InvoiceListView.tsx +177 -0
  94. package/src/views/PayablesView.tsx +37 -0
  95. package/src/views/ReceivablesView.tsx +37 -0
  96. package/src/views/StatementsView.tsx +123 -0
  97. package/src/views/SummaryView.tsx +12 -0
  98. package/src/views/dashboardWidgets.tsx +191 -0
@@ -0,0 +1,261 @@
1
+ import React, { useState, useCallback, useMemo } from 'react'
2
+ import type { StoreApi } from 'zustand/vanilla'
3
+ import { useTranslation } from '@fayz-ai/core'
4
+ import { ModulePage, PageTransition, type ModuleNavItem } from '@fayz-ai/ui'
5
+ import { FinancialContextProvider, type ResolvedFinancialConfig } from './FinancialContext'
6
+ import type { FinancialDataProvider } from './data/types'
7
+ import type { FinancialUIState } from './store'
8
+ import type { PluginRegistryDef, PluginQuickAction } from '@fayz-ai/core'
9
+ import { useModuleNavigation, ModuleActionBar, parseViewId } from '@fayz-ai/saas'
10
+ import { SummaryView } from './views/SummaryView'
11
+ import { PayablesView } from './views/PayablesView'
12
+ import { ReceivablesView } from './views/ReceivablesView'
13
+ import { CashRegistersView } from './views/CashRegistersView'
14
+ import { StatementsView } from './views/StatementsView'
15
+ import { CommissionsView } from './views/CommissionsView'
16
+ import { CardsView } from './views/CardsView'
17
+ import { FinancialGeneralSettings } from './components/FinancialGeneralSettings'
18
+ import { FinancialOnboarding } from './components/FinancialOnboarding'
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // View intent — what subpage + mode to show
22
+ // ---------------------------------------------------------------------------
23
+
24
+ export interface ViewIntent {
25
+ view: string
26
+ mode?: 'list' | 'new' | 'edit' | 'detail'
27
+ editId?: string
28
+ }
29
+
30
+ function parseIntent(activeView: string): ViewIntent {
31
+ // `base` collapses 'payables-detail' / 'payables-edit' etc; `id` is the canonical :id param.
32
+ const { base, id } = parseViewId(activeView)
33
+ const m = base.match(/^(payables|receivables)-(new|list|detail|edit)$/)
34
+ if (m) return { view: m[1], mode: m[2] as ViewIntent['mode'], editId: id }
35
+ return { view: activeView }
36
+ }
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // Nav builder
40
+ // ---------------------------------------------------------------------------
41
+
42
+ function buildNav(
43
+ config: ResolvedFinancialConfig,
44
+ activeView: string,
45
+ navigate: (view: string, hash?: string) => void,
46
+ t: (key: string, params?: Record<string, string | number>) => string,
47
+ ): ModuleNavItem[] {
48
+ const items: ModuleNavItem[] = [
49
+ {
50
+ id: 'summary',
51
+ label: t('financial.nav.summary'),
52
+ icon: 'BarChart3',
53
+ active: activeView === 'summary',
54
+ onClick: () => navigate('summary'),
55
+ },
56
+ ]
57
+
58
+ if (config.modules.payables) {
59
+ items.push({
60
+ id: 'payables',
61
+ label: t('financial.nav.payables'),
62
+ icon: 'ArrowUpCircle',
63
+ active: activeView.startsWith('payables'),
64
+ children: [
65
+ { id: 'payables-new', label: t('financial.nav.new'), active: activeView === 'payables-new', onClick: () => navigate('payables-new') },
66
+ { id: 'payables-list', label: t('financial.nav.list'), active: activeView === 'payables-list', onClick: () => navigate('payables-list') },
67
+ { id: 'payables-recurring', label: t('financial.nav.recurringExpenses'), active: activeView === 'payables-recurring', onClick: () => navigate('payables-recurring') },
68
+ ],
69
+ })
70
+ }
71
+
72
+ if (config.modules.receivables) {
73
+ items.push({
74
+ id: 'receivables',
75
+ label: t('financial.nav.receivables'),
76
+ icon: 'ArrowDownCircle',
77
+ active: activeView.startsWith('receivables'),
78
+ children: [
79
+ { id: 'receivables-new', label: t('financial.nav.new'), active: activeView === 'receivables-new', onClick: () => navigate('receivables-new') },
80
+ { id: 'receivables-list', label: t('financial.nav.list'), active: activeView === 'receivables-list', onClick: () => navigate('receivables-list') },
81
+ ],
82
+ })
83
+ }
84
+
85
+ if (config.modules.cashRegisters) {
86
+ items.push({
87
+ id: 'cash-registers',
88
+ label: t('financial.nav.cashRegisters'),
89
+ icon: 'Landmark',
90
+ active: activeView === 'cash-registers',
91
+ onClick: () => navigate('cash-registers'),
92
+ })
93
+ }
94
+
95
+ if (config.modules.statements) {
96
+ items.push({
97
+ id: 'statements',
98
+ label: t('financial.nav.statements'),
99
+ icon: 'Receipt',
100
+ active: activeView === 'statements',
101
+ onClick: () => navigate('statements'),
102
+ })
103
+ }
104
+
105
+ if (config.modules.commissions) {
106
+ items.push({
107
+ id: 'commissions',
108
+ label: t('financial.nav.commissions'),
109
+ icon: 'Users',
110
+ children: [
111
+ { id: 'commissions-overview', label: t('financial.nav.overview'), active: activeView === 'commissions', onClick: () => navigate('commissions') },
112
+ { id: 'commissions-rules', label: t('financial.nav.rules'), active: activeView === 'commissions', onClick: () => navigate('commissions') },
113
+ ],
114
+ })
115
+ }
116
+
117
+ if (config.modules.cards) {
118
+ items.push({
119
+ id: 'cards',
120
+ label: t('financial.nav.cards'),
121
+ icon: 'CreditCard',
122
+ children: [
123
+ { id: 'cards-overview', label: t('financial.nav.overview'), active: activeView === 'cards', onClick: () => navigate('cards') },
124
+ { id: 'cards-reconciliation', label: t('financial.nav.reconciliation'), active: activeView === 'cards', onClick: () => navigate('cards') },
125
+ ],
126
+ })
127
+ }
128
+
129
+ return items
130
+ }
131
+
132
+ // ---------------------------------------------------------------------------
133
+ // Main page component
134
+ // ---------------------------------------------------------------------------
135
+
136
+ export function FinancialPage({ config, provider, store, registries }: {
137
+ config: ResolvedFinancialConfig
138
+ provider: FinancialDataProvider
139
+ store: StoreApi<FinancialUIState>
140
+ registries?: PluginRegistryDef[]
141
+ }) {
142
+ const { view, direction, navigate } = useModuleNavigation('/financial', {
143
+ summary: 0,
144
+ 'payables-list': 0, 'payables-new': 1, 'receivables-list': 0, 'receivables-new': 1,
145
+ 'cash-registers': 0, statements: 0, commissions: 0, cards: 0,
146
+ settings: 1,
147
+ })
148
+
149
+ const [onboardingComplete, setOnboardingComplete] = useState(() => {
150
+ try { return localStorage.getItem('saas-core:financial-onboarded') === 'true' } catch { return false }
151
+ })
152
+
153
+ const t = useTranslation()
154
+ const intent = parseIntent(view)
155
+ const isSettings = view === 'settings'
156
+ const nav = buildNav(config, view, navigate, t)
157
+
158
+ const quickActions = useMemo<PluginQuickAction[]>(() => {
159
+ const actions: PluginQuickAction[] = []
160
+ if (config.modules.payables) {
161
+ actions.push({
162
+ id: 'new-payable',
163
+ label: t('financial.quickActions.newPayable'),
164
+ icon: 'ArrowUpCircle',
165
+ description: t('financial.quickActions.newPayableDesc'),
166
+ action: () => navigate('payables-new'),
167
+ })
168
+ }
169
+ if (config.modules.receivables) {
170
+ actions.push({
171
+ id: 'new-receivable',
172
+ label: t('financial.quickActions.newReceivable'),
173
+ icon: 'ArrowDownCircle',
174
+ description: t('financial.quickActions.newReceivableDesc'),
175
+ action: () => navigate('receivables-new'),
176
+ })
177
+ }
178
+ if (config.modules.cashRegisters) {
179
+ actions.push({
180
+ id: 'open-cash',
181
+ label: t('financial.quickActions.openCashRegister'),
182
+ icon: 'Landmark',
183
+ description: t('financial.quickActions.openCashRegisterDesc'),
184
+ action: () => navigate('cash-registers'),
185
+ })
186
+ }
187
+ return actions
188
+ }, [config.modules])
189
+
190
+ function handleOnboardingComplete() {
191
+ setOnboardingComplete(true)
192
+ try { localStorage.setItem('saas-core:financial-onboarded', 'true') } catch {}
193
+ }
194
+
195
+ // Show onboarding on first visit
196
+ if (!onboardingComplete) {
197
+ return (
198
+ <FinancialContextProvider config={config} provider={provider} store={store}>
199
+ <FinancialOnboarding onComplete={handleOnboardingComplete} />
200
+ </FinancialContextProvider>
201
+ )
202
+ }
203
+
204
+ // Settings view
205
+ if (isSettings && registries && registries.length > 0) {
206
+ return (
207
+ <FinancialContextProvider config={config} provider={provider} store={store}>
208
+ <PageTransition transitionKey="settings" direction={direction}>
209
+ <div style={{ padding: '24px' }}>
210
+ <div style={{ marginBottom: '16px' }}>
211
+ <h1 style={{ fontSize: '20px', fontWeight: 600, margin: 0 }}>{t('financial.settingsPage.title')}</h1>
212
+ <p style={{ color: 'var(--muted-foreground, #6b7280)', margin: '4px 0 0', fontSize: '14px' }}>{t('financial.settingsPage.subtitle')}</p>
213
+ </div>
214
+ <FinancialGeneralSettings />
215
+ </div>
216
+ </PageTransition>
217
+ </FinancialContextProvider>
218
+ )
219
+ }
220
+
221
+ function renderView() {
222
+ switch (intent.view) {
223
+ case 'payables':
224
+ return <PayablesView intent={intent} onNavigate={navigate} />
225
+ case 'receivables':
226
+ return <ReceivablesView intent={intent} onNavigate={navigate} />
227
+ case 'cash-registers':
228
+ return <CashRegistersView />
229
+ case 'statements':
230
+ return <StatementsView />
231
+ case 'commissions':
232
+ return <CommissionsView />
233
+ case 'cards':
234
+ return <CardsView />
235
+ default:
236
+ return <SummaryView />
237
+ }
238
+ }
239
+
240
+ return (
241
+ <FinancialContextProvider config={config} provider={provider} store={store}>
242
+ <ModulePage
243
+ title={config.labels.pageTitle}
244
+ subtitle={config.labels.pageSubtitle}
245
+ nav={nav}
246
+ showHeader={intent.view === 'summary' || view === 'summary'}
247
+ viewKey={view}
248
+ direction={direction}
249
+ headerAction={
250
+ <ModuleActionBar
251
+ quickActions={quickActions}
252
+ settingsPath={registries && registries.length > 0 ? '/settings/financial' : undefined}
253
+ settingsLabel={t('financial.settingsPage.title')}
254
+ />
255
+ }
256
+ >
257
+ {renderView()}
258
+ </ModulePage>
259
+ </FinancialContextProvider>
260
+ )
261
+ }
package/src/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # Financial Plugin
2
+
3
+ Accounts payable/receivable, cash registers, bank statements, and financial reporting for any SaaS vertical.
4
+
5
+ ## Quick Start
6
+
7
+ ```typescript
8
+ import { createFinancialPlugin } from '@fayz-ai/saas-core/plugins/financial'
9
+
10
+ // In your createSaasApp config:
11
+ plugins: [
12
+ createFinancialPlugin({
13
+ currency: { code: 'BRL', locale: 'pt-BR', symbol: 'R$' },
14
+ }),
15
+ ]
16
+ ```
17
+
18
+ ## Configuration
19
+
20
+ | Option | Type | Default | Description |
21
+ |--------|------|---------|-------------|
22
+ | `modules.payables` | boolean | true | Accounts payable |
23
+ | `modules.receivables` | boolean | true | Accounts receivable |
24
+ | `modules.cashRegisters` | boolean | true | Cash register sessions |
25
+ | `modules.statements` | boolean | true | Bank statements |
26
+ | `modules.commissions` | boolean | true | Staff commissions |
27
+ | `modules.cards` | boolean | true | Card transactions |
28
+ | `currency` | object | BRL/pt-BR | Currency code, locale, symbol |
29
+ | `itemTypes` | array | service, product, other | Invoice line item types |
30
+ | `enableServiceExecution` | boolean | false | Track service execution on items |
31
+ | `contactEntity` | object | client | Person archetype kind for contacts |
32
+ | `locations` | array | [] | Business units (shows unit picker when 1+) |
33
+ | `dataProvider` | FinancialDataProvider | mock | Custom data provider |
34
+ | `navPosition` | number | 6 | Navigation order |
35
+
36
+ ## Architecture
37
+
38
+ ```
39
+ src/plugins/financial/
40
+ index.ts # createFinancialPlugin() factory
41
+ types.ts # Pure TS domain types (zero deps)
42
+ registries.ts # CRUD entities: payment methods, chart of accounts, etc.
43
+ store.ts # Zustand UI state + provider actions
44
+ FinancialContext.tsx # React contexts: config, provider, store
45
+ FinancialPage.tsx # Main page with ModulePage layout
46
+ data/
47
+ types.ts # FinancialDataProvider interface
48
+ mock.ts # In-memory mock provider
49
+ views/ # All sub-views (Summary, Invoices, Cash, etc.)
50
+ components/ # Settings, onboarding
51
+ migrations/ # SQL migration files
52
+ ```
53
+
54
+ ### Data Flow
55
+
56
+ ```
57
+ createFinancialPlugin(options)
58
+ → resolves config (labels, currency, modules, itemTypes)
59
+ → creates provider (mock or custom)
60
+ → creates zustand store (bound to provider)
61
+ → returns PluginManifest with route, navigation, registries, settings tab
62
+
63
+ FinancialPage (rendered by plugin route)
64
+ → wraps children in FinancialContextProvider (config + provider + store)
65
+ → useModuleNavigation handles view switching + hash routing + animations
66
+ → each view calls useFinancialStore(selector) to read/write data
67
+ ```
68
+
69
+ ## Type System
70
+
71
+ Core entities (in `types.ts`):
72
+
73
+ | Entity | Purpose |
74
+ |--------|---------|
75
+ | `Invoice` | Header: direction (debit/credit), date, contact, total, status |
76
+ | `InvoiceItem` | Line item: kind, description, qty, price, discount |
77
+ | `FinancialMovement` | Installments & payments: amount, due date, status |
78
+ | `BankAccount` | Bank, cash register, credit card, digital wallet |
79
+ | `CashSession` | Open/close sessions with balance reconciliation |
80
+ | `PaymentMethod` | Configured methods with discount/interest rules |
81
+ | `ChartOfAccountsNode` | Hierarchical ledger accounts |
82
+ | `CardTransaction` | Credit card installment tracking |
83
+
84
+ Status enums: `InvoiceStatus`, `MovementStatus`, `TransactionDirection`, `MovementKind`
85
+
86
+ ## Registries (Plugin Settings)
87
+
88
+ Accessible via gear icon in Financial module or in global Settings > Financial tab:
89
+
90
+ | Registry | Editable | Seed Data |
91
+ |----------|----------|-----------|
92
+ | Payment Method | Yes | — |
93
+ | Ledger Account | Yes | Default chart of accounts |
94
+ | Cost Center | Yes | "General" |
95
+ | Bank Account | Yes | — |
96
+ | Payment Type | Read-only | Cash, PIX, Credit Card, Debit Card, Transfer, Check |
97
+ | Card Brand | Read-only | Visa, Mastercard, Amex, Elo, etc. |
98
+
99
+ ## Database Migrations
100
+
101
+ Run in order:
102
+
103
+ 1. `001_financial_base.sql` — Core tables: payment_method_types, payment_methods, bank_accounts, cash_register_sessions, invoices, invoice_items, financial_movements
104
+ 2. `002_chart_of_accounts.sql` — chart_of_accounts (self-referencing tree), cost_centers
105
+ 3. `003_card_brands.sql` — card_brands
106
+
107
+ All tables use `tenant_id` referencing `saas_core.tenants(id)` for multi-tenant isolation.
108
+
109
+ ## Views
110
+
111
+ | View | Route suffix | Description |
112
+ |------|-------------|-------------|
113
+ | Summary | `/financial` | Dashboard with KPIs, cash flow, overdue alerts |
114
+ | Payables List | `/financial/payables/list` | Filterable invoice list (debit) |
115
+ | Payables New | `/financial/payables/new` | Create payable invoice |
116
+ | Receivables List | `/financial/receivables/list` | Filterable invoice list (credit) |
117
+ | Cash Registers | `/financial/cash/registers` | Open/close sessions |
118
+ | Statements | `/financial/statements` | Bank account statement viewer |
119
+ | Cards | `/financial/cards` | Card transaction reconciliation |
120
+ | Settings | `/financial/settings/*` | Registry CRUD management |
121
+
122
+ ## Extending
123
+
124
+ ### Custom Data Provider
125
+
126
+ ```typescript
127
+ import type { FinancialDataProvider } from '@fayz-ai/saas-core/plugins/financial'
128
+
129
+ const supabaseProvider: FinancialDataProvider = {
130
+ async getInvoices(query) { /* Supabase query */ },
131
+ async createInvoice(input) { /* Supabase insert */ },
132
+ // ... implement all methods
133
+ }
134
+
135
+ createFinancialPlugin({ dataProvider: supabaseProvider })
136
+ ```
137
+
138
+ ### Vertical Customization
139
+
140
+ ```typescript
141
+ // Restaurant — no commissions, custom item types
142
+ createFinancialPlugin({
143
+ modules: { commissions: false },
144
+ itemTypes: [
145
+ { value: 'menu_item', label: 'Menu Item' },
146
+ { value: 'beverage', label: 'Beverage' },
147
+ ],
148
+ })
149
+
150
+ // Beauty salon — with service execution tracking
151
+ createFinancialPlugin({
152
+ enableServiceExecution: true,
153
+ contactEntity: { archetypeKind: 'client', label: 'Client' },
154
+ })
155
+ ```
@@ -0,0 +1,26 @@
1
+ import React from 'react'
2
+ import { SettingsGroup, ToggleRow } from '@fayz-ai/saas'
3
+ import { useTranslation } from '@fayz-ai/core'
4
+
5
+ export function FinancialGeneralSettings() {
6
+ const t = useTranslation()
7
+ return (
8
+ <div className="space-y-4">
9
+ <SettingsGroup title={t('financial.settings.invoices')} description={t('financial.settings.invoicesDesc')}>
10
+ <ToggleRow label={t('financial.settings.autoInstallments')} description={t('financial.settings.autoInstallmentsDesc')} checked={true} onChange={() => {}} />
11
+ <ToggleRow label={t('financial.settings.requireDoc')} description={t('financial.settings.requireDocDesc')} checked={false} onChange={() => {}} />
12
+ <ToggleRow label={t('financial.settings.allowPartial')} description={t('financial.settings.allowPartialDesc')} checked={true} onChange={() => {}} />
13
+ </SettingsGroup>
14
+
15
+ <SettingsGroup title={t('financial.settings.cashRegister')} description={t('financial.settings.cashRegisterDesc')}>
16
+ <ToggleRow label={t('financial.settings.requireOpening')} description={t('financial.settings.requireOpeningDesc')} checked={true} onChange={() => {}} />
17
+ <ToggleRow label={t('financial.settings.autoClose')} description={t('financial.settings.autoCloseDesc')} checked={false} onChange={() => {}} />
18
+ </SettingsGroup>
19
+
20
+ <SettingsGroup title={t('financial.settings.notifications')} description={t('financial.settings.notificationsDesc')}>
21
+ <ToggleRow label={t('financial.settings.overdueAlerts')} description={t('financial.settings.overdueAlertsDesc')} checked={true} onChange={() => {}} />
22
+ <ToggleRow label={t('financial.settings.dailySummary')} description={t('financial.settings.dailySummaryDesc')} checked={false} onChange={() => {}} />
23
+ </SettingsGroup>
24
+ </div>
25
+ )
26
+ }
@@ -0,0 +1,87 @@
1
+ import React, { useState } from 'react'
2
+ import { DollarSign, Landmark, CreditCard, BookOpen, ChevronRight, Check } from 'lucide-react'
3
+ import { useTranslation } from '@fayz-ai/core'
4
+ import { Button } from '@fayz-ai/ui'
5
+
6
+ const STEPS = [
7
+ { id: 'welcome', icon: DollarSign, titleKey: 'financial.onboarding.welcome', descKey: 'financial.onboarding.description' },
8
+ { id: 'accounts', icon: Landmark, titleKey: 'financial.onboarding.accounts.title', descKey: 'financial.onboarding.accounts.description' },
9
+ { id: 'payments', icon: CreditCard, titleKey: 'financial.onboarding.payments.title', descKey: 'financial.onboarding.payments.description' },
10
+ { id: 'chart', icon: BookOpen, titleKey: 'financial.onboarding.chart.title', descKey: 'financial.onboarding.chart.description' },
11
+ ]
12
+
13
+ export function FinancialOnboarding({ onComplete }: { onComplete: () => void }) {
14
+ const t = useTranslation()
15
+ const [step, setStep] = useState(0)
16
+ const current = STEPS[step]
17
+ const isLast = step === STEPS.length - 1
18
+ const Icon = current.icon
19
+
20
+ return (
21
+ <div className="flex items-center justify-center py-12">
22
+ <div className="w-full max-w-lg">
23
+ {/* Progress */}
24
+ <div className="flex items-center gap-1 mb-8">
25
+ {STEPS.map((s, i) => (
26
+ <div
27
+ key={s.id}
28
+ className={`h-1 flex-1 rounded-full transition-colors ${
29
+ i <= step ? 'bg-primary' : 'bg-muted'
30
+ }`}
31
+ />
32
+ ))}
33
+ </div>
34
+
35
+ {/* Card */}
36
+ <div className="rounded-card border bg-card p-8 text-center space-y-6 shadow-sm">
37
+ <div className="flex justify-center">
38
+ <div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-primary/10">
39
+ <Icon className="h-8 w-8 text-primary" />
40
+ </div>
41
+ </div>
42
+
43
+ <div>
44
+ <h2 className="text-xl font-bold">{t(current.titleKey)}</h2>
45
+ <p className="text-sm text-muted-foreground mt-2 max-w-sm mx-auto">{t(current.descKey)}</p>
46
+ </div>
47
+
48
+ {/* Step-specific hint */}
49
+ {step > 0 && step < STEPS.length - 1 && (
50
+ <div className="rounded-lg bg-muted/30 px-4 py-3 text-xs text-muted-foreground">
51
+ {t('financial.onboarding.configureHint')}
52
+ </div>
53
+ )}
54
+
55
+ {/* Actions */}
56
+ <div className="flex items-center justify-center gap-3 pt-2">
57
+ {step > 0 && (
58
+ <Button variant="outline" size="lg" onClick={() => setStep(step - 1)}>
59
+ {t('financial.onboarding.back')}
60
+ </Button>
61
+ )}
62
+ {isLast ? (
63
+ <Button variant="default" size="lg" onClick={onComplete}>
64
+ <Check className="h-4 w-4" /> {t('financial.onboarding.startUsing')}
65
+ </Button>
66
+ ) : (
67
+ <Button variant="default" size="lg" onClick={() => setStep(step + 1)}>
68
+ {step === 0 ? t('financial.onboarding.getStarted') : t('financial.onboarding.next')}
69
+ <ChevronRight className="h-4 w-4" />
70
+ </Button>
71
+ )}
72
+ </div>
73
+
74
+ {/* Skip */}
75
+ {!isLast && step === 0 && (
76
+ <button
77
+ onClick={onComplete}
78
+ className="text-xs text-muted-foreground hover:text-foreground transition-colors"
79
+ >
80
+ {t('financial.onboarding.skip')}
81
+ </button>
82
+ )}
83
+ </div>
84
+ </div>
85
+ </div>
86
+ )
87
+ }
@@ -0,0 +1,31 @@
1
+ import React from 'react'
2
+ import { ChevronLeft } from 'lucide-react'
3
+ import { PluginRegistryManager } from '@fayz-ai/saas'
4
+ import type { PluginRegistryDef } from '@fayz-ai/core'
5
+ import { useTranslation } from '@fayz-ai/core'
6
+
7
+ export function FinancialSettings({ registries, routeBase, onClose }: {
8
+ registries: PluginRegistryDef[]
9
+ routeBase: string
10
+ onClose: () => void
11
+ }) {
12
+ const t = useTranslation()
13
+ return (
14
+ <div className="space-y-4">
15
+ <div className="flex items-center gap-3">
16
+ <button
17
+ onClick={onClose}
18
+ className="flex h-8 w-8 items-center justify-center rounded-lg border hover:bg-muted bg-card shadow-button active:shadow-button-inset transition-colors"
19
+ >
20
+ <ChevronLeft className="h-4 w-4" />
21
+ </button>
22
+ <div>
23
+ <h2 className="text-lg font-bold">{t('financial.settingsPage.title')}</h2>
24
+ <p className="text-xs text-muted-foreground">{t('financial.settingsPage.subtitle')}</p>
25
+ </div>
26
+ </div>
27
+
28
+ <PluginRegistryManager registries={registries} routeBase={routeBase} />
29
+ </div>
30
+ )
31
+ }