@accounter/client 0.0.8-alpha-20251022130946-0923a77d2ee3f22a60a7f5b1e0623bd3bee88868 → 0.0.8-alpha-20251022131500-0d2446e83760934b05cf2c65a8b065989f7724c8
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/CHANGELOG.md +25 -1
- package/dist/assets/Checkbox-D7nOeER7.js +6 -0
- package/dist/assets/Progress-_KQ8BRrQ.js +1 -0
- package/dist/assets/Typography-BYHNiKxL.js +1 -0
- package/dist/assets/accordion-aunUrlum.js +1 -0
- package/dist/assets/accountant-approvals-1lH0z-Rv.js +1 -0
- package/dist/assets/all-charges-Cngljy2T.js +1 -0
- package/dist/assets/arrow-up-down-De1dsSxL.js +6 -0
- package/dist/assets/business-7l6LGFQt.js +37 -0
- package/dist/assets/business-transactions-single-BmOnrzw-.js +1 -0
- package/dist/assets/business-trip-BH8ZEeSB.js +1 -0
- package/dist/assets/charges-filters-DkzCOY0r.js +1 -0
- package/dist/assets/charges-ledger-validation-tNA_f_7H.js +1 -0
- package/dist/assets/chart-BJ85ZFS0.js +74 -0
- package/dist/assets/data-table-pagination-_iPJtRUP.js +11 -0
- package/dist/assets/editable-business-trip-DCtXJ6w4.js +16 -0
- package/dist/assets/graphql-document-dedupe-fragments-ByT8-wlV.js +1 -0
- package/dist/assets/index-6W3Ndi5M.js +1 -0
- package/dist/assets/index-A086__I6.js +1 -0
- package/dist/assets/index-AoVRWBXr.js +6 -0
- package/dist/assets/index-BGZNb2wc.js +17 -0
- package/dist/assets/index-BHUgtFG6.js +1 -0
- package/dist/assets/index-BQw927FW.js +11 -0
- package/dist/assets/index-C27oA70V.js +1 -0
- package/dist/assets/index-CIqGQ8uI.js +1 -0
- package/dist/assets/index-CMXNoVEJ.js +24 -0
- package/dist/assets/index-CQEXsBvi.js +1 -0
- package/dist/assets/index-CRWwjUSx.js +1 -0
- package/dist/assets/index-CUPkGo8z.js +1 -0
- package/dist/assets/index-Ch3veRcP.js +1 -0
- package/dist/assets/index-Cll1w4iD.js +6 -0
- package/dist/assets/index-Cnf3x_0g.js +2 -0
- package/dist/assets/index-DCiQggcN.js +9 -0
- package/dist/assets/index-DFO0fSvK.js +876 -0
- package/dist/assets/index-DLrRdx1l.js +1 -0
- package/dist/assets/index-DgOX69C5.js +1 -0
- package/dist/assets/index-DqPz6G2w.js +2 -0
- package/dist/assets/index-DxgUoyCT.js +1 -0
- package/dist/assets/index-Dxkz1HG4.js +137 -0
- package/dist/assets/index-LwYKcUCw.js +1 -0
- package/dist/assets/index-gdTXrWXt.css +1 -0
- package/dist/assets/{index.es-CWwhWGxX.js → index.es-g2vV-Mpx.js} +5 -5
- package/dist/assets/issue-document-DlwQP2Xx.js +1 -0
- package/dist/assets/login-page-C2voZ_Kj.js +1 -0
- package/dist/assets/missing-info-charges-eJYTfSDz.js +1 -0
- package/dist/assets/page-not-found-t6EEvRUF.js +1 -0
- package/dist/assets/pencil-nACAiGf5.js +6 -0
- package/dist/assets/report-commentary-row-BeCkGfXh.js +1 -0
- package/dist/assets/save-DIy7YBvX.js +11 -0
- package/dist/assets/sequential-CAnleQny.js +1 -0
- package/dist/assets/similar-charges-by-business-modal-BTts1hyT.js +1 -0
- package/dist/assets/sub-CNmFodmJ.js +1 -0
- package/dist/assets/subMonths-BkHf0iFx.js +1 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/components/business-transactions/business-extended-info.tsx +13 -15
- package/src/components/business-transactions/business-transactions-single.tsx +3 -3
- package/src/components/business-transactions/index.tsx +12 -1
- package/src/components/business-trips/business-trip.tsx +3 -3
- package/src/components/charges/cells/business-trip.tsx +6 -8
- package/src/components/charges/cells/counterparty.tsx +7 -5
- package/src/components/common/accounter-table.tsx +6 -5
- package/src/components/common/business-trip-report/parts/core-expense-row.tsx +11 -9
- package/src/components/common/business-trip-report/parts/uncategorized-transactions.tsx +11 -13
- package/src/components/common/buttons/index.ts +0 -2
- package/src/components/common/buttons/logout-button.tsx +7 -6
- package/src/components/common/documents-to-charge-matcher/selection-handler/index.tsx +4 -2
- package/src/components/common/documents-to-charge-matcher/selection-handler/wide-filtered-selection.tsx +5 -7
- package/src/components/common/forms/edit-document.tsx +23 -10
- package/src/components/common/new-documents-list.tsx +10 -8
- package/src/components/documents-table/cells/creditor.tsx +11 -4
- package/src/components/documents-table/cells/debtor.tsx +11 -4
- package/src/components/error-boundary.tsx +189 -0
- package/src/components/layout/breadcrumbs.tsx +77 -0
- package/src/components/layout/dashboard-layout.tsx +4 -0
- package/src/components/layout/document-title.tsx +31 -0
- package/src/components/layout/navigation-progress.tsx +52 -0
- package/src/components/layout/page-skeleton.tsx +49 -0
- package/src/components/layout/sidelinks.tsx +28 -27
- package/src/components/ledger-table/counterparty-cell.tsx +19 -13
- package/src/components/login-page.tsx +2 -1
- package/src/components/reports/corporate-tax-ruling-compliance-report/index.tsx +3 -3
- package/src/components/reports/profit-and-loss-report/index.tsx +3 -3
- package/src/components/reports/tax-report/index.tsx +3 -3
- package/src/components/screens/businesses/business.tsx +21 -9
- package/src/components/screens/charges/charge.tsx +22 -9
- package/src/components/transactions-table/cells/counterparty.tsx +9 -2
- package/src/components/transactions-table/cells-legacy/counterparty.tsx +9 -2
- package/src/gql/graphql.ts +1554 -4842
- package/src/index.tsx +4 -22
- package/src/providers/auth-guard.tsx +14 -23
- package/src/providers/index.tsx +7 -2
- package/src/providers/urql-client.ts +86 -0
- package/src/providers/urql.tsx +7 -12
- package/src/providers/user-provider.tsx +3 -2
- package/src/router/config.tsx +534 -0
- package/src/router/layouts/dashboard-layout.tsx +20 -0
- package/src/router/layouts/root-layout.tsx +69 -0
- package/src/router/loaders/auth-loader.ts +32 -0
- package/src/router/loaders/business-loader.ts +25 -0
- package/src/router/loaders/charge-loader.ts +25 -0
- package/src/router/loaders/index.ts +17 -0
- package/src/router/routes.ts +88 -0
- package/src/router/types.ts +62 -0
- package/dist/assets/index-B2UYAO1O.css +0 -1
- package/dist/assets/index-BexxGuN6.js +0 -1224
- package/src/components/common/buttons/button-with-label.tsx +0 -41
- package/src/components/common/buttons/button.tsx +0 -44
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { redirect } from 'react-router-dom';
|
|
2
|
+
import { UserService } from '../../services/user-service.js';
|
|
3
|
+
import { ROUTES } from '../routes.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Auth loader - protects routes that require authentication
|
|
7
|
+
* Redirects to login if user is not authenticated
|
|
8
|
+
*/
|
|
9
|
+
export function requireAuth() {
|
|
10
|
+
const authService = new UserService();
|
|
11
|
+
|
|
12
|
+
if (!authService.isLoggedIn()) {
|
|
13
|
+
// Redirect to login page
|
|
14
|
+
throw redirect(ROUTES.LOGIN);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Public only loader - redirects authenticated users away from login page
|
|
22
|
+
*/
|
|
23
|
+
export function publicOnly() {
|
|
24
|
+
const authService = new UserService();
|
|
25
|
+
|
|
26
|
+
if (authService.isLoggedIn()) {
|
|
27
|
+
// Redirect to home page if already logged in
|
|
28
|
+
throw redirect(ROUTES.HOME);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { LoaderFunctionArgs } from 'react-router-dom';
|
|
2
|
+
import { BusinessScreenDocument } from '../../gql/graphql.js';
|
|
3
|
+
import { getUrqlClient } from '../../providers/urql-client.js';
|
|
4
|
+
import { validateBusinessParams } from '../types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Business loader - prefetches business data before component renders
|
|
8
|
+
* Used by: /businesses/:businessId route
|
|
9
|
+
*/
|
|
10
|
+
export async function businessLoader({ params }: LoaderFunctionArgs) {
|
|
11
|
+
const { businessId } = validateBusinessParams(params);
|
|
12
|
+
|
|
13
|
+
const client = getUrqlClient();
|
|
14
|
+
const result = await client.query(BusinessScreenDocument, { businessId }).toPromise();
|
|
15
|
+
|
|
16
|
+
if (result.error) {
|
|
17
|
+
throw new Response('Failed to load business', { status: 500 });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!result.data?.business) {
|
|
21
|
+
throw new Response('Business not found', { status: 404 });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return result.data;
|
|
25
|
+
}
|