@accounter/client 0.0.8-alpha-20251021150615-800574fc6d416cd319de216c97b431643d8958a2 → 0.0.8-alpha-20251021225827-178e480c997a9811913e16f85cb94329041b096e

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 (150) hide show
  1. package/CHANGELOG.md +67 -1
  2. package/dist/assets/Checkbox-CxedbJAl.js +6 -0
  3. package/dist/assets/Progress-D5SuJtCd.js +1 -0
  4. package/dist/assets/Typography-BQFz-z7L.js +1 -0
  5. package/dist/assets/accordion-COWOBKuq.js +1 -0
  6. package/dist/assets/accountant-approvals-Bd2y8us_.js +1 -0
  7. package/dist/assets/all-charges-SWBnaZu7.js +1 -0
  8. package/dist/assets/arrow-up-down-dZmrBLse.js +6 -0
  9. package/dist/assets/business--GVVfDEa.js +37 -0
  10. package/dist/assets/business-transactions-single-BsbkUf_H.js +1 -0
  11. package/dist/assets/business-trip-ByXPVXdG.js +1 -0
  12. package/dist/assets/charges-filters-D43UbXob.js +1 -0
  13. package/dist/assets/charges-ledger-validation-D0uMH_JE.js +1 -0
  14. package/dist/assets/chart-ClU1KbWe.js +74 -0
  15. package/dist/assets/data-table-pagination-D9Y0_Tn8.js +11 -0
  16. package/dist/assets/editable-business-trip-DhqOQBPa.js +16 -0
  17. package/dist/assets/graphql-document-dedupe-fragments-ByT8-wlV.js +1 -0
  18. package/dist/assets/index-1U6rQgQe.js +6 -0
  19. package/dist/assets/index-3-AKn8tg.js +1 -0
  20. package/dist/assets/index-91A2PLZ6.js +137 -0
  21. package/dist/assets/index-BBHuCWRn.js +1 -0
  22. package/dist/assets/index-BPNuFFtx.js +1 -0
  23. package/dist/assets/index-BXqHnRVY.js +1 -0
  24. package/dist/assets/index-BciOH8FS.js +1 -0
  25. package/dist/assets/index-BjHuUHDO.js +1 -0
  26. package/dist/assets/index-BxKmoNQd.js +1 -0
  27. package/dist/assets/index-C3bqiFIv.js +2 -0
  28. package/dist/assets/index-C5MeepK_.js +11 -0
  29. package/dist/assets/index-CAwm68Mg.js +1 -0
  30. package/dist/assets/index-CJ8OGXxv.js +1 -0
  31. package/dist/assets/index-CJyY-qF6.js +1 -0
  32. package/dist/assets/index-CMYnx46_.js +6 -0
  33. package/dist/assets/index-CNrwxUZ7.js +1 -0
  34. package/dist/assets/index-CvV5z5r9.js +876 -0
  35. package/dist/assets/index-D08H2GXq.js +17 -0
  36. package/dist/assets/index-GFsPY1p4.js +2 -0
  37. package/dist/assets/index-KwNwThNu.js +1 -0
  38. package/dist/assets/index-YA8IBFyB.js +1 -0
  39. package/dist/assets/index-ZpyI3qxW.js +24 -0
  40. package/dist/assets/index-gdTXrWXt.css +1 -0
  41. package/dist/assets/index-ytnIEraq.js +9 -0
  42. package/dist/assets/{index.es-DHwHzag1.js → index.es-CYeQ4a5s.js} +5 -5
  43. package/dist/assets/issue-document-CdikNnO2.js +1 -0
  44. package/dist/assets/login-page-effgZS3V.js +1 -0
  45. package/dist/assets/missing-info-charges-CnPFTzoZ.js +1 -0
  46. package/dist/assets/page-not-found-D8YlgDOm.js +1 -0
  47. package/dist/assets/pencil-mxW0-tGM.js +6 -0
  48. package/dist/assets/report-commentary-row-DCozKgVE.js +1 -0
  49. package/dist/assets/save-CHlytUqu.js +11 -0
  50. package/dist/assets/sequential-CAnleQny.js +1 -0
  51. package/dist/assets/similar-charges-by-business-modal-Dzbspk_r.js +1 -0
  52. package/dist/assets/sub-Cp_PhKiD.js +1 -0
  53. package/dist/assets/subMonths-DCj_iXAn.js +1 -0
  54. package/dist/index.html +2 -2
  55. package/package.json +6 -5
  56. package/src/app.tsx +35 -25
  57. package/src/components/business/business-header.tsx +68 -0
  58. package/src/components/business/charges-section.tsx +82 -0
  59. package/src/components/business/charts-section.tsx +115 -0
  60. package/src/components/business/configurations-section.tsx +885 -0
  61. package/src/components/business/contact-info-section.tsx +536 -0
  62. package/src/components/business/contracts-section.tsx +196 -0
  63. package/src/components/business/documents-section.tsx +26 -0
  64. package/src/components/business/index.tsx +171 -0
  65. package/src/components/business/integrations-section.tsx +477 -0
  66. package/src/components/business/transactions-section.tsx +26 -0
  67. package/src/components/business-transactions/business-extended-info.tsx +22 -28
  68. package/src/components/business-transactions/business-transactions-single.tsx +3 -3
  69. package/src/components/business-transactions/index.tsx +13 -2
  70. package/src/components/business-trips/business-trip.tsx +3 -3
  71. package/src/components/charges/cells/business-trip.tsx +6 -8
  72. package/src/components/charges/cells/counterparty.tsx +7 -5
  73. package/src/components/charges/charge-extended-info-menu.tsx +27 -21
  74. package/src/components/charges/charges-row.tsx +12 -10
  75. package/src/components/charges/charges-table.tsx +15 -9
  76. package/src/components/clients/contracts/modify-contract-dialog.tsx +464 -0
  77. package/src/components/clients/modify-client-dialog.tsx +276 -0
  78. package/src/components/common/accounter-table.tsx +6 -5
  79. package/src/components/common/business-trip-report/parts/core-expense-row.tsx +11 -9
  80. package/src/components/common/business-trip-report/parts/uncategorized-transactions.tsx +11 -13
  81. package/src/components/common/buttons/index.ts +0 -2
  82. package/src/components/common/buttons/logout-button.tsx +7 -6
  83. package/src/components/common/documents/issue-document/index.tsx +3 -3
  84. package/src/components/common/documents/issue-document/{recent-client-docs.tsx → recent-business-docs.tsx} +19 -13
  85. package/src/components/common/documents-to-charge-matcher/selection-handler/index.tsx +4 -2
  86. package/src/components/common/documents-to-charge-matcher/selection-handler/wide-filtered-selection.tsx +5 -7
  87. package/src/components/common/forms/business-card.tsx +1 -0
  88. package/src/components/common/forms/edit-document.tsx +23 -10
  89. package/src/components/common/forms/modify-business-fields.tsx +2 -19
  90. package/src/components/common/inputs/combo-box.tsx +1 -1
  91. package/src/components/common/new-documents-list.tsx +10 -8
  92. package/src/components/documents-table/cells/creditor.tsx +11 -4
  93. package/src/components/documents-table/cells/debtor.tsx +11 -4
  94. package/src/components/error-boundary.tsx +189 -0
  95. package/src/components/layout/breadcrumbs.tsx +77 -0
  96. package/src/components/layout/dashboard-layout.tsx +4 -0
  97. package/src/components/layout/document-title.tsx +31 -0
  98. package/src/components/layout/navigation-progress.tsx +52 -0
  99. package/src/components/layout/page-skeleton.tsx +49 -0
  100. package/src/components/layout/sidelinks.tsx +28 -27
  101. package/src/components/ledger-table/counterparty-cell.tsx +19 -13
  102. package/src/components/login-page.tsx +2 -1
  103. package/src/components/reports/corporate-tax-ruling-compliance-report/index.tsx +3 -3
  104. package/src/components/reports/profit-and-loss-report/index.tsx +3 -3
  105. package/src/components/reports/tax-report/index.tsx +3 -3
  106. package/src/components/reports/trial-balance-report/trial-balance-report-group.tsx +4 -6
  107. package/src/components/reports/trial-balance-report/trial-balance-report-sort-code.tsx +8 -11
  108. package/src/components/screens/businesses/business.tsx +56 -0
  109. package/src/components/screens/charges/charge.tsx +22 -9
  110. package/src/components/screens/documents/issue-documents/edit-issue-document-modal.tsx +4 -4
  111. package/src/components/transactions-table/cells/counterparty.tsx +9 -2
  112. package/src/components/transactions-table/cells-legacy/counterparty.tsx +9 -2
  113. package/src/components/ui/progress.tsx +25 -0
  114. package/src/components/ui/skeleton.tsx +12 -0
  115. package/src/gql/gql.ts +93 -9
  116. package/src/gql/graphql.ts +289 -9
  117. package/src/helpers/contracts.ts +22 -0
  118. package/src/helpers/currency.ts +5 -0
  119. package/src/helpers/index.ts +2 -0
  120. package/src/helpers/pcn874.ts +17 -0
  121. package/src/hooks/use-add-sort-code.ts +1 -1
  122. package/src/hooks/use-add-tag.ts +1 -1
  123. package/src/hooks/use-create-contract.ts +62 -0
  124. package/src/hooks/use-delete-contract.ts +64 -0
  125. package/src/hooks/use-delete-tag.ts +1 -1
  126. package/src/hooks/use-get-all-contracts.ts +0 -1
  127. package/src/hooks/use-insert-client.ts +80 -0
  128. package/src/hooks/use-merge-businesses.ts +1 -1
  129. package/src/hooks/use-merge-charges.ts +1 -1
  130. package/src/hooks/use-update-client.ts +75 -0
  131. package/src/hooks/use-update-contract.ts +69 -0
  132. package/src/index.tsx +4 -22
  133. package/src/providers/auth-guard.tsx +14 -23
  134. package/src/providers/index.tsx +7 -2
  135. package/src/providers/urql-client.ts +86 -0
  136. package/src/providers/urql.tsx +7 -12
  137. package/src/providers/user-provider.tsx +3 -2
  138. package/src/router/config.tsx +534 -0
  139. package/src/router/layouts/dashboard-layout.tsx +20 -0
  140. package/src/router/layouts/root-layout.tsx +69 -0
  141. package/src/router/loaders/auth-loader.ts +32 -0
  142. package/src/router/loaders/business-loader.ts +25 -0
  143. package/src/router/loaders/charge-loader.ts +25 -0
  144. package/src/router/loaders/index.ts +17 -0
  145. package/src/router/routes.ts +88 -0
  146. package/src/router/types.ts +62 -0
  147. package/dist/assets/index-0eCf1BcD.css +0 -1
  148. package/dist/assets/index-DHTbHvtz.js +0 -1188
  149. package/src/components/common/buttons/button-with-label.tsx +0 -41
  150. package/src/components/common/buttons/button.tsx +0 -44
@@ -9,23 +9,23 @@ import {
9
9
  type Operation,
10
10
  } from 'urql';
11
11
  import { authExchange } from '@urql/exchange-auth';
12
+ import { ROUTES } from '../router/routes.js';
12
13
  import { AuthContext } from './auth-guard.js';
13
14
 
14
15
  export function UrqlProvider({ children }: { children?: ReactNode }): ReactNode {
15
16
  const { authService } = useContext(AuthContext);
16
17
  const navigate = useNavigate();
18
+
19
+ // Track login state to trigger token updates
17
20
  const loggedIn = authService.isLoggedIn();
18
21
 
19
22
  const token = useMemo(() => {
20
23
  const token = authService.authToken();
21
24
  return token;
22
- }, [authService]);
25
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- loggedIn is needed to trigger token refresh on login
26
+ }, [authService, loggedIn]);
23
27
 
24
28
  const client = useMemo(() => {
25
- if (!loggedIn) {
26
- return null;
27
- }
28
-
29
29
  let url: string;
30
30
  switch (import.meta.env.MODE) {
31
31
  case 'production': {
@@ -47,16 +47,11 @@ export function UrqlProvider({ children }: { children?: ReactNode }): ReactNode
47
47
  exchanges: [
48
48
  mapExchange({
49
49
  onResult(result) {
50
- if (result.error?.networkError) {
51
- console.error('Network Error:', result.error.networkError);
52
- navigate('/network-error');
53
- return;
54
- }
55
50
  const isAuthError =
56
51
  result?.error?.graphQLErrors.some(e => e.extensions?.code === 'FORBIDDEN') ||
57
52
  result?.error?.response?.status === 401;
58
53
  if (isAuthError) {
59
- navigate('/login', {
54
+ navigate(ROUTES.LOGIN, {
60
55
  state: { message: 'You are not authorized to access this page' },
61
56
  });
62
57
  }
@@ -86,7 +81,7 @@ export function UrqlProvider({ children }: { children?: ReactNode }): ReactNode
86
81
  fetchExchange,
87
82
  ],
88
83
  });
89
- }, [loggedIn, navigate, token, authService]);
84
+ }, [navigate, token, authService]);
90
85
 
91
86
  useEffect(() => {
92
87
  if (!client) {
@@ -54,13 +54,14 @@ export function UserProvider({ children }: { children?: ReactNode }): ReactNode
54
54
  });
55
55
 
56
56
  // update active user
57
- const currentUser = authService.currentUser();
58
57
  useEffect(() => {
58
+ const currentUser = authService.currentUser();
59
59
  if (!equal(currentUser, user)) {
60
60
  setUser(currentUser);
61
61
  fetchUserContext();
62
62
  }
63
- }, [currentUser, user, fetchUserContext]);
63
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- authService and fetchUserContext are stable
64
+ }, [user]);
64
65
 
65
66
  // on user defaults fetched, update user defaults
66
67
  useEffect(() => {
@@ -0,0 +1,534 @@
1
+ import { lazy, Suspense, type ReactElement } from 'react';
2
+ import type { RouteObject } from 'react-router-dom';
3
+ import { ErrorBoundary } from '../components/error-boundary.js';
4
+ import { PageSkeleton, ReportSkeleton, TableSkeleton } from '../components/layout/page-skeleton.js';
5
+ import { DashboardLayoutRoute } from './layouts/dashboard-layout.js';
6
+ import { RootLayout } from './layouts/root-layout.js';
7
+ import { businessLoader, chargeLoader, publicOnly, requireAuth } from './loaders/index.js';
8
+ import { ROUTES } from './routes.js';
9
+
10
+ /**
11
+ * Lazy load page components for code splitting
12
+ * Components use named exports, so we need to extract them
13
+ */
14
+
15
+ // Charges
16
+ const AllCharges = lazy(() =>
17
+ import('../components/screens/charges/all-charges.js').then(m => ({ default: m.AllCharges })),
18
+ );
19
+ const MissingInfoCharges = lazy(() =>
20
+ import('../components/screens/charges/missing-info-charges.js').then(m => ({
21
+ default: m.MissingInfoCharges,
22
+ })),
23
+ );
24
+ const Charge = lazy(() =>
25
+ import('../components/screens/charges/charge.js').then(m => ({ default: m.Charge })),
26
+ );
27
+ const ChargesLedgerValidation = lazy(() =>
28
+ import('../components/charges-ledger-validation.js').then(m => ({
29
+ default: m.ChargesLedgerValidation,
30
+ })),
31
+ );
32
+
33
+ // Businesses
34
+ const Businesses = lazy(() =>
35
+ import('../components/businesses/index.js').then(m => ({ default: m.Businesses })),
36
+ );
37
+ const BusinessScreen = lazy(() =>
38
+ import('../components/screens/businesses/business.js').then(m => ({ default: m.BusinessScreen })),
39
+ );
40
+ const BusinessTransactionsSummery = lazy(() =>
41
+ import('../components/business-transactions/index.js').then(m => ({
42
+ default: m.BusinessTransactionsSummery,
43
+ })),
44
+ );
45
+ const BusinessTransactionsSingle = lazy(() =>
46
+ import('../components/business-transactions/business-transactions-single.js').then(m => ({
47
+ default: m.BusinessTransactionsSingle,
48
+ })),
49
+ );
50
+
51
+ // Business Trips
52
+ const BusinessTrips = lazy(() =>
53
+ import('../components/business-trips/index.js').then(m => ({ default: m.BusinessTrips })),
54
+ );
55
+ const BusinessTrip = lazy(() =>
56
+ import('../components/business-trips/business-trip.js').then(m => ({ default: m.BusinessTrip })),
57
+ );
58
+
59
+ // Charts
60
+ const ChartPage = lazy(() =>
61
+ import('../components/charts/index.js').then(m => ({ default: m.ChartPage })),
62
+ );
63
+ const MonthlyIncomeExpenseChart = lazy(() =>
64
+ import('../components/charts/monthly-income-expense/index.js').then(m => ({
65
+ default: m.MonthlyIncomeExpenseChart,
66
+ })),
67
+ );
68
+
69
+ // Documents
70
+ const DocumentsReport = lazy(() =>
71
+ import('../components/screens/documents/all-documents/index.jsx').then(m => ({
72
+ default: m.DocumentsReport,
73
+ })),
74
+ );
75
+ const IssueDocumentScreen = lazy(() =>
76
+ import('../components/screens/documents/issue-document.js').then(m => ({
77
+ default: m.IssueDocumentScreen,
78
+ })),
79
+ );
80
+ const IssueDocuments = lazy(() =>
81
+ import('../components/screens/documents/issue-documents/index.js').then(m => ({
82
+ default: m.IssueDocuments,
83
+ })),
84
+ );
85
+
86
+ // Reports
87
+ const TrialBalanceReport = lazy(() =>
88
+ import('../components/reports/trial-balance-report/index.js').then(m => ({
89
+ default: m.TrialBalanceReport,
90
+ })),
91
+ );
92
+ const ContoReport = lazy(() =>
93
+ import('../components/reports/conto/index.js').then(m => ({ default: m.ContoReport })),
94
+ );
95
+ const VatMonthlyReport = lazy(() =>
96
+ import('../components/reports/vat-monthly-report/index.js').then(m => ({
97
+ default: m.VatMonthlyReport,
98
+ })),
99
+ );
100
+ const ProfitAndLossReport = lazy(() =>
101
+ import('../components/reports/profit-and-loss-report/index.js').then(m => ({
102
+ default: m.ProfitAndLossReport,
103
+ })),
104
+ );
105
+ const TaxReport = lazy(() =>
106
+ import('../components/reports/tax-report/index.js').then(m => ({ default: m.TaxReport })),
107
+ );
108
+ const DepreciationReport = lazy(() =>
109
+ import('../components/screens/reports/depreciation-report/index.js').then(m => ({
110
+ default: m.DepreciationReport,
111
+ })),
112
+ );
113
+ const Shaam6111Report = lazy(() =>
114
+ import('../components/screens/reports/shaam6111-report/index.js').then(m => ({
115
+ default: m.Shaam6111Report,
116
+ })),
117
+ );
118
+ const YearlyLedgerReport = lazy(() =>
119
+ import('../components/reports/yearly-ledger/index.js').then(m => ({
120
+ default: m.YearlyLedgerReport,
121
+ })),
122
+ );
123
+ const CorporateTaxRulingComplianceReport = lazy(() =>
124
+ import('../components/reports/corporate-tax-ruling-compliance-report/index.js').then(m => ({
125
+ default: m.CorporateTaxRulingComplianceReport,
126
+ })),
127
+ );
128
+ const BalanceReport = lazy(() =>
129
+ import('../components/screens/reports/balance-report/index.js').then(m => ({
130
+ default: m.BalanceReport,
131
+ })),
132
+ );
133
+ const ValidateReportsScreen = lazy(() =>
134
+ import('../components/reports/validations/index.js').then(m => ({
135
+ default: m.ValidateReportsScreen,
136
+ })),
137
+ );
138
+ const AccountantApprovals = lazy(() =>
139
+ import('../components/reports/accountant-approvals.js').then(m => ({
140
+ default: m.AccountantApprovals,
141
+ })),
142
+ );
143
+
144
+ // Other
145
+ const Salaries = lazy(() =>
146
+ import('../components/salaries/index.js').then(m => ({ default: m.Salaries })),
147
+ );
148
+ const TagsManager = lazy(() =>
149
+ import('../components/tags/index.js').then(m => ({ default: m.TagsManager })),
150
+ );
151
+ const TaxCategories = lazy(() =>
152
+ import('../components/tax-categories/index.js').then(m => ({ default: m.TaxCategories })),
153
+ );
154
+ const SortCodes = lazy(() =>
155
+ import('../components/screens/sort-codes/index.js').then(m => ({ default: m.SortCodes })),
156
+ );
157
+ const PageNotFound = lazy(() =>
158
+ import('../components/screens/page-not-found.js').then(m => ({ default: m.PageNotFound })),
159
+ );
160
+
161
+ // Auth
162
+ const LoginPage = lazy(() =>
163
+ import('../components/login-page.js').then(m => ({ default: m.LoginPage })),
164
+ );
165
+
166
+ /**
167
+ * Helper to wrap components with Suspense
168
+ */
169
+ function withSuspense(
170
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
171
+ Component: React.LazyExoticComponent<React.ComponentType<any>>,
172
+ fallback?: ReactElement,
173
+ ) {
174
+ return (
175
+ <Suspense fallback={fallback || <PageSkeleton />}>
176
+ <Component />
177
+ </Suspense>
178
+ );
179
+ }
180
+
181
+ /**
182
+ * Application route configuration
183
+ * Using object-based routes for better type safety and features
184
+ */
185
+ export const routes: RouteObject[] = [
186
+ {
187
+ path: '/',
188
+ element: <RootLayout />,
189
+ errorElement: <ErrorBoundary />,
190
+ children: [
191
+ // Public routes (login, error pages)
192
+ {
193
+ path: ROUTES.LOGIN,
194
+ loader: publicOnly,
195
+ element: withSuspense(LoginPage),
196
+ handle: {
197
+ title: 'Login',
198
+ },
199
+ },
200
+
201
+ // Protected routes (require authentication)
202
+ {
203
+ path: '/',
204
+ loader: requireAuth,
205
+ element: <DashboardLayoutRoute />,
206
+ errorElement: <ErrorBoundary />,
207
+ children: [
208
+ // Home / Charges (default)
209
+ {
210
+ index: true,
211
+ element: withSuspense(AllCharges, <TableSkeleton />),
212
+ handle: {
213
+ title: 'All Charges',
214
+ breadcrumb: 'Charges',
215
+ },
216
+ },
217
+
218
+ // Charges section
219
+ {
220
+ path: 'charges',
221
+ handle: { breadcrumb: 'Charges' },
222
+ children: [
223
+ {
224
+ index: true,
225
+ element: withSuspense(AllCharges, <TableSkeleton />),
226
+ handle: { title: 'All Charges' },
227
+ },
228
+ {
229
+ path: 'missing-info',
230
+ element: withSuspense(MissingInfoCharges, <TableSkeleton />),
231
+ handle: {
232
+ title: 'Missing Info Charges',
233
+ breadcrumb: 'Missing Info',
234
+ },
235
+ },
236
+ {
237
+ path: 'ledger-validation',
238
+ element: withSuspense(ChargesLedgerValidation, <TableSkeleton />),
239
+ handle: {
240
+ title: 'Ledger Validation',
241
+ breadcrumb: 'Ledger Validation',
242
+ },
243
+ },
244
+ {
245
+ path: ':chargeId',
246
+ loader: chargeLoader,
247
+ element: withSuspense(Charge),
248
+ handle: {
249
+ title: 'Charge Details',
250
+ breadcrumb: 'Details',
251
+ },
252
+ },
253
+ ],
254
+ },
255
+
256
+ // Businesses section
257
+ {
258
+ path: 'businesses',
259
+ handle: { breadcrumb: 'Businesses' },
260
+ children: [
261
+ {
262
+ index: true,
263
+ element: withSuspense(Businesses, <TableSkeleton />),
264
+ handle: { title: 'All Businesses' },
265
+ },
266
+ {
267
+ path: 'transactions',
268
+ element: withSuspense(BusinessTransactionsSummery, <TableSkeleton />),
269
+ handle: {
270
+ title: 'Business Transactions Summary',
271
+ breadcrumb: 'Transactions',
272
+ },
273
+ },
274
+ {
275
+ path: ':businessId',
276
+ loader: businessLoader,
277
+ element: withSuspense(BusinessScreen),
278
+ handle: {
279
+ title: 'Business Details',
280
+ breadcrumb: 'Details',
281
+ },
282
+ },
283
+ {
284
+ path: ':businessId/transactions',
285
+ element: withSuspense(BusinessTransactionsSingle, <TableSkeleton />),
286
+ handle: {
287
+ title: 'Business Transactions',
288
+ breadcrumb: 'Transactions',
289
+ },
290
+ },
291
+ ],
292
+ },
293
+
294
+ // Business Trips section
295
+ {
296
+ path: 'business-trips',
297
+ handle: { breadcrumb: 'Business Trips' },
298
+ children: [
299
+ {
300
+ index: true,
301
+ element: withSuspense(BusinessTrips, <TableSkeleton />),
302
+ handle: { title: 'Business Trips' },
303
+ },
304
+ {
305
+ path: ':businessTripId',
306
+ element: withSuspense(BusinessTrip),
307
+ handle: {
308
+ title: 'Business Trip Details',
309
+ breadcrumb: 'Details',
310
+ },
311
+ },
312
+ ],
313
+ },
314
+
315
+ // Charts section
316
+ {
317
+ path: 'charts',
318
+ handle: { breadcrumb: 'Charts' },
319
+ children: [
320
+ {
321
+ index: true,
322
+ element: withSuspense(ChartPage),
323
+ handle: { title: 'Charts' },
324
+ },
325
+ {
326
+ path: 'monthly-income-expense',
327
+ element: withSuspense(MonthlyIncomeExpenseChart),
328
+ handle: {
329
+ title: 'Monthly Income/Expense',
330
+ breadcrumb: 'Income/Expense',
331
+ },
332
+ },
333
+ ],
334
+ },
335
+
336
+ // Documents section
337
+ {
338
+ path: 'documents',
339
+ handle: { breadcrumb: 'Documents' },
340
+ children: [
341
+ {
342
+ index: true,
343
+ element: withSuspense(DocumentsReport, <TableSkeleton />),
344
+ handle: { title: 'All Documents' },
345
+ },
346
+ {
347
+ path: 'issue-document',
348
+ element: withSuspense(IssueDocumentScreen),
349
+ handle: {
350
+ title: 'Issue Document',
351
+ breadcrumb: 'Issue Document',
352
+ },
353
+ },
354
+ {
355
+ path: 'issue-documents',
356
+ element: withSuspense(IssueDocuments),
357
+ handle: {
358
+ title: 'Issue Documents',
359
+ breadcrumb: 'Issue Documents',
360
+ },
361
+ },
362
+ ],
363
+ },
364
+
365
+ // Reports section
366
+ {
367
+ path: 'reports',
368
+ handle: { breadcrumb: 'Reports' },
369
+ children: [
370
+ {
371
+ path: 'trial-balance',
372
+ element: withSuspense(TrialBalanceReport, <ReportSkeleton />),
373
+ handle: {
374
+ title: 'Trial Balance Report',
375
+ breadcrumb: 'Trial Balance',
376
+ },
377
+ },
378
+ {
379
+ path: 'conto',
380
+ element: withSuspense(ContoReport, <ReportSkeleton />),
381
+ handle: {
382
+ title: 'Conto Report',
383
+ breadcrumb: 'Conto',
384
+ },
385
+ },
386
+ {
387
+ path: 'vat-monthly',
388
+ element: withSuspense(VatMonthlyReport, <ReportSkeleton />),
389
+ handle: {
390
+ title: 'VAT Monthly Report',
391
+ breadcrumb: 'VAT Monthly',
392
+ },
393
+ },
394
+ {
395
+ path: 'profit-and-loss',
396
+ handle: { breadcrumb: 'Profit & Loss' },
397
+ children: [
398
+ {
399
+ index: true,
400
+ element: withSuspense(ProfitAndLossReport, <ReportSkeleton />),
401
+ handle: { title: 'Profit & Loss Report' },
402
+ },
403
+ {
404
+ path: ':year',
405
+ element: withSuspense(ProfitAndLossReport, <ReportSkeleton />),
406
+ handle: {
407
+ title: 'Profit & Loss Report',
408
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- loader data type varies by route
409
+ breadcrumb: (data: any) => data?.year || 'Year',
410
+ },
411
+ },
412
+ ],
413
+ },
414
+ {
415
+ path: 'tax',
416
+ handle: { breadcrumb: 'Tax' },
417
+ children: [
418
+ {
419
+ index: true,
420
+ element: withSuspense(TaxReport, <ReportSkeleton />),
421
+ handle: { title: 'Tax Report' },
422
+ },
423
+ {
424
+ path: ':year',
425
+ element: withSuspense(TaxReport, <ReportSkeleton />),
426
+ handle: {
427
+ title: 'Tax Report',
428
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- loader data type varies by route
429
+ breadcrumb: (data: any) => data?.year || 'Year',
430
+ },
431
+ },
432
+ ],
433
+ },
434
+ {
435
+ path: 'depreciation',
436
+ element: withSuspense(DepreciationReport, <ReportSkeleton />),
437
+ handle: {
438
+ title: 'Depreciation Report',
439
+ breadcrumb: 'Depreciation',
440
+ },
441
+ },
442
+ {
443
+ path: 'shaam-6111',
444
+ element: withSuspense(Shaam6111Report, <ReportSkeleton />),
445
+ handle: {
446
+ title: 'Shaam 6111 Report',
447
+ breadcrumb: 'Shaam 6111',
448
+ },
449
+ },
450
+ {
451
+ path: 'yearly-ledger',
452
+ element: withSuspense(YearlyLedgerReport, <ReportSkeleton />),
453
+ handle: {
454
+ title: 'Yearly Ledger Report',
455
+ breadcrumb: 'Yearly Ledger',
456
+ },
457
+ },
458
+ {
459
+ path: 'corporate-tax-ruling-compliance',
460
+ handle: { breadcrumb: 'Tax Ruling' },
461
+ children: [
462
+ {
463
+ index: true,
464
+ element: withSuspense(CorporateTaxRulingComplianceReport, <ReportSkeleton />),
465
+ handle: { title: 'Corporate Tax Ruling Compliance Report' },
466
+ },
467
+ {
468
+ path: ':year',
469
+ element: withSuspense(CorporateTaxRulingComplianceReport, <ReportSkeleton />),
470
+ handle: {
471
+ title: 'Corporate Tax Ruling Compliance Report',
472
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- loader data type varies by route
473
+ breadcrumb: (data: any) => data?.year || 'Year',
474
+ },
475
+ },
476
+ ],
477
+ },
478
+ {
479
+ path: 'balance',
480
+ element: withSuspense(BalanceReport, <ReportSkeleton />),
481
+ handle: {
482
+ title: 'Balance Report',
483
+ breadcrumb: 'Balance',
484
+ },
485
+ },
486
+ {
487
+ path: 'validate-reports',
488
+ element: withSuspense(ValidateReportsScreen),
489
+ handle: {
490
+ title: 'Validate Reports',
491
+ breadcrumb: 'Validate',
492
+ },
493
+ },
494
+ ],
495
+ },
496
+
497
+ // Standalone routes
498
+ {
499
+ path: 'accountant-approvals',
500
+ element: withSuspense(AccountantApprovals, <TableSkeleton />),
501
+ handle: { title: 'Accountant Approvals', breadcrumb: 'Accountant Approvals' },
502
+ },
503
+ {
504
+ path: 'salaries',
505
+ element: withSuspense(Salaries, <TableSkeleton />),
506
+ handle: { title: 'Salaries', breadcrumb: 'Salaries' },
507
+ },
508
+ {
509
+ path: 'tags',
510
+ element: withSuspense(TagsManager),
511
+ handle: { title: 'Tags', breadcrumb: 'Tags' },
512
+ },
513
+ {
514
+ path: 'tax-categories',
515
+ element: withSuspense(TaxCategories),
516
+ handle: { title: 'Tax Categories', breadcrumb: 'Tax Categories' },
517
+ },
518
+ {
519
+ path: 'sort-codes',
520
+ element: withSuspense(SortCodes),
521
+ handle: { title: 'Sort Codes', breadcrumb: 'Sort Codes' },
522
+ },
523
+
524
+ // 404 catch-all
525
+ {
526
+ path: '*',
527
+ element: withSuspense(PageNotFound),
528
+ handle: { title: 'Page Not Found' },
529
+ },
530
+ ],
531
+ },
532
+ ],
533
+ },
534
+ ];
@@ -0,0 +1,20 @@
1
+ import { useState, type ReactElement } from 'react';
2
+ import { Outlet } from 'react-router-dom';
3
+ import { DashboardLayout as DashboardUI } from '../../components/layout/dashboard-layout.js';
4
+ import { FiltersContext } from '../../providers/filters-context.js';
5
+
6
+ /**
7
+ * Dashboard layout route wrapper
8
+ * Provides the dashboard UI with sidebar, header, footer
9
+ */
10
+ export function DashboardLayoutRoute(): ReactElement {
11
+ const [filtersContext, setFiltersContext] = useState<ReactElement | null>(null);
12
+
13
+ return (
14
+ <FiltersContext.Provider value={{ filtersContext, setFiltersContext }}>
15
+ <DashboardUI filtersContext={filtersContext}>
16
+ <Outlet />
17
+ </DashboardUI>
18
+ </FiltersContext.Provider>
19
+ );
20
+ }
@@ -0,0 +1,69 @@
1
+ import type { ReactElement } from 'react';
2
+ import { Outlet } from 'react-router-dom';
3
+ import { MantineProvider } from '@mantine/core';
4
+ import { CssBaseline, ThemeProvider } from '@mui/material';
5
+ import { red } from '@mui/material/colors';
6
+ import { createTheme } from '@mui/material/styles';
7
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
8
+ import { DocumentTitle } from '../../components/layout/document-title.js';
9
+ import { NavigationProgress } from '../../components/layout/navigation-progress.js';
10
+ import { Toaster } from '../../components/ui/sonner.js';
11
+ import { AuthProvider } from '../../providers/auth-guard.js';
12
+ import { UrqlProvider } from '../../providers/urql.js';
13
+ import { UserProvider } from '../../providers/user-provider.js';
14
+
15
+ // Create these outside the component to prevent recreation on every render
16
+ const queryClient = new QueryClient({
17
+ defaultOptions: {
18
+ queries: {
19
+ refetchOnWindowFocus: false,
20
+ retry: 1,
21
+ },
22
+ },
23
+ });
24
+
25
+ const theme = createTheme({
26
+ palette: {
27
+ primary: {
28
+ main: '#556cd6',
29
+ },
30
+ secondary: {
31
+ main: '#19857b',
32
+ },
33
+ error: {
34
+ main: red.A400,
35
+ },
36
+ },
37
+ });
38
+
39
+ /**
40
+ * Root layout - wraps all routes
41
+ * Provides all app-level providers and navigation progress indicator
42
+ */
43
+ export function RootLayout(): ReactElement {
44
+ return (
45
+ <MantineProvider
46
+ withGlobalStyles
47
+ theme={{
48
+ fontFamily: 'Roboto, sans-serif',
49
+ fontSizes: { md: '14' },
50
+ }}
51
+ >
52
+ <ThemeProvider theme={theme}>
53
+ <CssBaseline />
54
+ <AuthProvider>
55
+ <Toaster />
56
+ <UrqlProvider>
57
+ <QueryClientProvider client={queryClient}>
58
+ <UserProvider>
59
+ <DocumentTitle />
60
+ <NavigationProgress />
61
+ <Outlet />
62
+ </UserProvider>
63
+ </QueryClientProvider>
64
+ </UrqlProvider>
65
+ </AuthProvider>
66
+ </ThemeProvider>
67
+ </MantineProvider>
68
+ );
69
+ }