@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.
Files changed (108) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/dist/assets/Checkbox-D7nOeER7.js +6 -0
  3. package/dist/assets/Progress-_KQ8BRrQ.js +1 -0
  4. package/dist/assets/Typography-BYHNiKxL.js +1 -0
  5. package/dist/assets/accordion-aunUrlum.js +1 -0
  6. package/dist/assets/accountant-approvals-1lH0z-Rv.js +1 -0
  7. package/dist/assets/all-charges-Cngljy2T.js +1 -0
  8. package/dist/assets/arrow-up-down-De1dsSxL.js +6 -0
  9. package/dist/assets/business-7l6LGFQt.js +37 -0
  10. package/dist/assets/business-transactions-single-BmOnrzw-.js +1 -0
  11. package/dist/assets/business-trip-BH8ZEeSB.js +1 -0
  12. package/dist/assets/charges-filters-DkzCOY0r.js +1 -0
  13. package/dist/assets/charges-ledger-validation-tNA_f_7H.js +1 -0
  14. package/dist/assets/chart-BJ85ZFS0.js +74 -0
  15. package/dist/assets/data-table-pagination-_iPJtRUP.js +11 -0
  16. package/dist/assets/editable-business-trip-DCtXJ6w4.js +16 -0
  17. package/dist/assets/graphql-document-dedupe-fragments-ByT8-wlV.js +1 -0
  18. package/dist/assets/index-6W3Ndi5M.js +1 -0
  19. package/dist/assets/index-A086__I6.js +1 -0
  20. package/dist/assets/index-AoVRWBXr.js +6 -0
  21. package/dist/assets/index-BGZNb2wc.js +17 -0
  22. package/dist/assets/index-BHUgtFG6.js +1 -0
  23. package/dist/assets/index-BQw927FW.js +11 -0
  24. package/dist/assets/index-C27oA70V.js +1 -0
  25. package/dist/assets/index-CIqGQ8uI.js +1 -0
  26. package/dist/assets/index-CMXNoVEJ.js +24 -0
  27. package/dist/assets/index-CQEXsBvi.js +1 -0
  28. package/dist/assets/index-CRWwjUSx.js +1 -0
  29. package/dist/assets/index-CUPkGo8z.js +1 -0
  30. package/dist/assets/index-Ch3veRcP.js +1 -0
  31. package/dist/assets/index-Cll1w4iD.js +6 -0
  32. package/dist/assets/index-Cnf3x_0g.js +2 -0
  33. package/dist/assets/index-DCiQggcN.js +9 -0
  34. package/dist/assets/index-DFO0fSvK.js +876 -0
  35. package/dist/assets/index-DLrRdx1l.js +1 -0
  36. package/dist/assets/index-DgOX69C5.js +1 -0
  37. package/dist/assets/index-DqPz6G2w.js +2 -0
  38. package/dist/assets/index-DxgUoyCT.js +1 -0
  39. package/dist/assets/index-Dxkz1HG4.js +137 -0
  40. package/dist/assets/index-LwYKcUCw.js +1 -0
  41. package/dist/assets/index-gdTXrWXt.css +1 -0
  42. package/dist/assets/{index.es-CWwhWGxX.js → index.es-g2vV-Mpx.js} +5 -5
  43. package/dist/assets/issue-document-DlwQP2Xx.js +1 -0
  44. package/dist/assets/login-page-C2voZ_Kj.js +1 -0
  45. package/dist/assets/missing-info-charges-eJYTfSDz.js +1 -0
  46. package/dist/assets/page-not-found-t6EEvRUF.js +1 -0
  47. package/dist/assets/pencil-nACAiGf5.js +6 -0
  48. package/dist/assets/report-commentary-row-BeCkGfXh.js +1 -0
  49. package/dist/assets/save-DIy7YBvX.js +11 -0
  50. package/dist/assets/sequential-CAnleQny.js +1 -0
  51. package/dist/assets/similar-charges-by-business-modal-BTts1hyT.js +1 -0
  52. package/dist/assets/sub-CNmFodmJ.js +1 -0
  53. package/dist/assets/subMonths-BkHf0iFx.js +1 -0
  54. package/dist/index.html +2 -2
  55. package/package.json +1 -1
  56. package/src/components/business-transactions/business-extended-info.tsx +13 -15
  57. package/src/components/business-transactions/business-transactions-single.tsx +3 -3
  58. package/src/components/business-transactions/index.tsx +12 -1
  59. package/src/components/business-trips/business-trip.tsx +3 -3
  60. package/src/components/charges/cells/business-trip.tsx +6 -8
  61. package/src/components/charges/cells/counterparty.tsx +7 -5
  62. package/src/components/common/accounter-table.tsx +6 -5
  63. package/src/components/common/business-trip-report/parts/core-expense-row.tsx +11 -9
  64. package/src/components/common/business-trip-report/parts/uncategorized-transactions.tsx +11 -13
  65. package/src/components/common/buttons/index.ts +0 -2
  66. package/src/components/common/buttons/logout-button.tsx +7 -6
  67. package/src/components/common/documents-to-charge-matcher/selection-handler/index.tsx +4 -2
  68. package/src/components/common/documents-to-charge-matcher/selection-handler/wide-filtered-selection.tsx +5 -7
  69. package/src/components/common/forms/edit-document.tsx +23 -10
  70. package/src/components/common/new-documents-list.tsx +10 -8
  71. package/src/components/documents-table/cells/creditor.tsx +11 -4
  72. package/src/components/documents-table/cells/debtor.tsx +11 -4
  73. package/src/components/error-boundary.tsx +189 -0
  74. package/src/components/layout/breadcrumbs.tsx +77 -0
  75. package/src/components/layout/dashboard-layout.tsx +4 -0
  76. package/src/components/layout/document-title.tsx +31 -0
  77. package/src/components/layout/navigation-progress.tsx +52 -0
  78. package/src/components/layout/page-skeleton.tsx +49 -0
  79. package/src/components/layout/sidelinks.tsx +28 -27
  80. package/src/components/ledger-table/counterparty-cell.tsx +19 -13
  81. package/src/components/login-page.tsx +2 -1
  82. package/src/components/reports/corporate-tax-ruling-compliance-report/index.tsx +3 -3
  83. package/src/components/reports/profit-and-loss-report/index.tsx +3 -3
  84. package/src/components/reports/tax-report/index.tsx +3 -3
  85. package/src/components/screens/businesses/business.tsx +21 -9
  86. package/src/components/screens/charges/charge.tsx +22 -9
  87. package/src/components/transactions-table/cells/counterparty.tsx +9 -2
  88. package/src/components/transactions-table/cells-legacy/counterparty.tsx +9 -2
  89. package/src/gql/graphql.ts +1554 -4842
  90. package/src/index.tsx +4 -22
  91. package/src/providers/auth-guard.tsx +14 -23
  92. package/src/providers/index.tsx +7 -2
  93. package/src/providers/urql-client.ts +86 -0
  94. package/src/providers/urql.tsx +7 -12
  95. package/src/providers/user-provider.tsx +3 -2
  96. package/src/router/config.tsx +534 -0
  97. package/src/router/layouts/dashboard-layout.tsx +20 -0
  98. package/src/router/layouts/root-layout.tsx +69 -0
  99. package/src/router/loaders/auth-loader.ts +32 -0
  100. package/src/router/loaders/business-loader.ts +25 -0
  101. package/src/router/loaders/charge-loader.ts +25 -0
  102. package/src/router/loaders/index.ts +17 -0
  103. package/src/router/routes.ts +88 -0
  104. package/src/router/types.ts +62 -0
  105. package/dist/assets/index-B2UYAO1O.css +0 -1
  106. package/dist/assets/index-BexxGuN6.js +0 -1224
  107. package/src/components/common/buttons/button-with-label.tsx +0 -41
  108. package/src/components/common/buttons/button.tsx +0 -44
package/src/index.tsx CHANGED
@@ -1,32 +1,14 @@
1
1
  import { StrictMode } from 'react';
2
2
  import { createRoot } from 'react-dom/client';
3
- import {
4
- createBrowserRouter,
5
- createRoutesFromElements,
6
- Route,
7
- RouterProvider,
8
- } from 'react-router-dom';
9
- import { App } from './app.js';
10
- import { Providers } from './providers/index.js';
3
+ import { createBrowserRouter, RouterProvider } from 'react-router-dom';
4
+ import { routes } from './router/config.js';
11
5
  import './index.css';
12
6
 
13
7
  const rootElement = document.getElementById('root');
14
-
15
8
  const root = createRoot(rootElement!);
16
9
 
17
- const router = createBrowserRouter(
18
- createRoutesFromElements(
19
- <Route
20
- path="*"
21
- element={
22
- <Providers>
23
- <App />
24
- </Providers>
25
- }
26
- errorElement={<p>error</p>} // TODO: implement
27
- />,
28
- ),
29
- );
10
+ // Create router with object-based configuration
11
+ const router = createBrowserRouter(routes);
30
12
 
31
13
  root.render(
32
14
  <StrictMode>
@@ -1,7 +1,4 @@
1
- import { createContext, useEffect, useState, type ReactNode } from 'react';
2
- import { Route, Routes, useNavigate } from 'react-router-dom';
3
- import { NetworkError } from '@/components/screens/network-error.js';
4
- import { LoginPage } from '../components/login-page.js';
1
+ import { createContext, useMemo, useState, type ReactNode } from 'react';
5
2
  import { UserService } from '../services/user-service.js';
6
3
 
7
4
  type ContextType = {
@@ -14,25 +11,19 @@ export const AuthContext = createContext<ContextType>({
14
11
  setAuthService: () => void 0,
15
12
  });
16
13
 
17
- export const AuthGuard = ({ children }: { children?: ReactNode }): ReactNode => {
18
- const [authService, setAuthService] = useState<UserService>(new UserService());
19
- const navigate = useNavigate();
14
+ /**
15
+ * AuthProvider - provides authentication context
16
+ * Auth routing logic is now handled by route loaders
17
+ */
18
+ export const AuthProvider = ({ children }: { children?: ReactNode }): ReactNode => {
19
+ // Use lazy initialization to prevent creating new UserService on every render
20
+ const [authService, setAuthService] = useState<UserService>(() => new UserService());
20
21
 
21
- const loggedIn = authService.isLoggedIn();
22
+ // Memoize the context value to prevent unnecessary re-renders
23
+ const contextValue = useMemo(() => ({ authService, setAuthService }), [authService]);
22
24
 
23
- useEffect(() => {
24
- if (!loggedIn) {
25
- navigate('/login');
26
- }
27
- }, [loggedIn, navigate]);
28
-
29
- return (
30
- <AuthContext.Provider value={{ authService, setAuthService }}>
31
- <Routes>
32
- <Route path="/login" element={<LoginPage />} />
33
- <Route path="/network-error" element={<NetworkError />} />
34
- <Route path="*" element={children} />
35
- </Routes>
36
- </AuthContext.Provider>
37
- );
25
+ return <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>;
38
26
  };
27
+
28
+ // Keep AuthGuard as alias for backwards compatibility
29
+ export const AuthGuard = AuthProvider;
@@ -5,9 +5,14 @@ import { red } from '@mui/material/colors';
5
5
  import { createTheme } from '@mui/material/styles';
6
6
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
7
7
  import { Toaster } from '../components/ui/sonner.js';
8
- import { AuthGuard } from './auth-guard.js';
8
+ import { AuthContext, AuthGuard, AuthProvider } from './auth-guard.js';
9
9
  import { UrqlProvider } from './urql.js';
10
- import { UserProvider } from './user-provider.js';
10
+ import { UserContext, UserProvider, type UserInfo } from './user-provider.js';
11
+
12
+ export { AuthGuard, AuthProvider, AuthContext };
13
+ export { FiltersContext } from './filters-context.js';
14
+ export { UrqlProvider };
15
+ export { UserProvider, UserContext, type UserInfo };
11
16
 
12
17
  const queryClient = new QueryClient();
13
18
 
@@ -0,0 +1,86 @@
1
+ import {
2
+ createClient,
3
+ fetchExchange,
4
+ mapExchange,
5
+ type AnyVariables,
6
+ type Client,
7
+ type Operation,
8
+ } from 'urql';
9
+ import { authExchange } from '@urql/exchange-auth';
10
+ import { UserService } from '../services/user-service.js';
11
+
12
+ /**
13
+ * Singleton URQL client for use in loaders and server-side operations
14
+ * This is separate from the Provider client to avoid React context dependencies
15
+ */
16
+ let globalClient: Client | null = null;
17
+
18
+ export function getUrqlClient(): Client {
19
+ if (globalClient) {
20
+ return globalClient;
21
+ }
22
+
23
+ const authService = new UserService();
24
+
25
+ let url: string;
26
+ switch (import.meta.env.MODE) {
27
+ case 'production': {
28
+ url = 'https://accounter.onrender.com/graphql';
29
+ break;
30
+ }
31
+ case 'staging': {
32
+ url = 'https://accounter-staging.onrender.com/graphql';
33
+ break;
34
+ }
35
+ default: {
36
+ url = 'http://localhost:4000/graphql';
37
+ break;
38
+ }
39
+ }
40
+
41
+ globalClient = createClient({
42
+ url,
43
+ exchanges: [
44
+ mapExchange({
45
+ onResult(result) {
46
+ if (result.error?.networkError) {
47
+ console.error('Network Error:', result.error.networkError);
48
+ }
49
+ },
50
+ }),
51
+ authExchange(async utils => {
52
+ return {
53
+ addAuthToOperation(operation): Operation<void, AnyVariables> {
54
+ const token = authService.authToken();
55
+ if (!token) {
56
+ return operation;
57
+ }
58
+ return utils.appendHeaders(operation, {
59
+ Authorization: token,
60
+ });
61
+ },
62
+ didAuthError(error, _operation): boolean {
63
+ return (
64
+ error?.response?.status === 401 ||
65
+ error?.graphQLErrors?.some(e => e.extensions?.code === 'FORBIDDEN')
66
+ );
67
+ },
68
+ async refreshAuth(): Promise<void> {
69
+ authService.logout();
70
+ // Redirect handled by route loader
71
+ },
72
+ };
73
+ }),
74
+ fetchExchange,
75
+ ],
76
+ });
77
+
78
+ return globalClient;
79
+ }
80
+
81
+ /**
82
+ * Reset the global client (useful for tests or logout)
83
+ */
84
+ export function resetUrqlClient(): void {
85
+ globalClient = null;
86
+ }
@@ -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(() => {