@accounter/client 0.0.8-alpha-20251022132023-9f41aae2ce16f8761101edc548f486cc6ce80333 → 0.0.8-alpha-20251022144027-c9c7a4b309de8a72dfd0ec4f11e63713ad12878f

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 (112) hide show
  1. package/CHANGELOG.md +1 -25
  2. package/dist/assets/index-B2UYAO1O.css +1 -0
  3. package/dist/assets/index-CZb2m31K.js +1229 -0
  4. package/dist/assets/{index.es-CL8po8bM.js → index.es-9gVMpHTc.js} +5 -5
  5. package/dist/index.html +2 -2
  6. package/package.json +1 -1
  7. package/src/components/business/admin-business-section.tsx +284 -0
  8. package/src/components/business/index.tsx +22 -0
  9. package/src/components/business-transactions/business-extended-info.tsx +15 -13
  10. package/src/components/business-transactions/business-transactions-single.tsx +3 -3
  11. package/src/components/business-transactions/index.tsx +1 -12
  12. package/src/components/business-trips/business-trip.tsx +3 -3
  13. package/src/components/charges/cells/business-trip.tsx +8 -6
  14. package/src/components/charges/cells/counterparty.tsx +5 -7
  15. package/src/components/common/accounter-table.tsx +5 -6
  16. package/src/components/common/business-trip-report/parts/core-expense-row.tsx +9 -11
  17. package/src/components/common/business-trip-report/parts/uncategorized-transactions.tsx +13 -11
  18. package/src/components/common/buttons/button-with-label.tsx +41 -0
  19. package/src/components/common/buttons/button.tsx +44 -0
  20. package/src/components/common/buttons/index.ts +2 -0
  21. package/src/components/common/buttons/logout-button.tsx +6 -7
  22. package/src/components/common/documents-to-charge-matcher/selection-handler/index.tsx +2 -4
  23. package/src/components/common/documents-to-charge-matcher/selection-handler/wide-filtered-selection.tsx +7 -5
  24. package/src/components/common/forms/edit-document.tsx +10 -21
  25. package/src/components/common/new-documents-list.tsx +8 -10
  26. package/src/components/documents-table/cells/creditor.tsx +4 -11
  27. package/src/components/documents-table/cells/debtor.tsx +4 -11
  28. package/src/components/layout/dashboard-layout.tsx +0 -4
  29. package/src/components/layout/sidelinks.tsx +27 -28
  30. package/src/components/ledger-table/counterparty-cell.tsx +13 -19
  31. package/src/components/login-page.tsx +1 -2
  32. package/src/components/reports/corporate-tax-ruling-compliance-report/index.tsx +3 -3
  33. package/src/components/reports/profit-and-loss-report/index.tsx +3 -3
  34. package/src/components/reports/tax-report/index.tsx +3 -3
  35. package/src/components/screens/businesses/business.tsx +9 -21
  36. package/src/components/screens/charges/charge.tsx +9 -22
  37. package/src/components/transactions-table/cells/counterparty.tsx +2 -9
  38. package/src/components/transactions-table/cells-legacy/counterparty.tsx +2 -9
  39. package/src/gql/gql.ts +15 -3
  40. package/src/gql/graphql.ts +4913 -1559
  41. package/src/hooks/use-update-admin-business.ts +71 -0
  42. package/src/index.tsx +22 -4
  43. package/src/providers/auth-guard.tsx +23 -14
  44. package/src/providers/index.tsx +2 -7
  45. package/src/providers/urql.tsx +12 -7
  46. package/src/providers/user-provider.tsx +2 -3
  47. package/dist/assets/Checkbox-DBF5r7jA.js +0 -6
  48. package/dist/assets/Progress-Cjv9yrat.js +0 -1
  49. package/dist/assets/Typography-DdVppmQe.js +0 -1
  50. package/dist/assets/accordion-DcMd-Til.js +0 -1
  51. package/dist/assets/accountant-approvals-CU_EA64d.js +0 -1
  52. package/dist/assets/all-charges-DKGmrDeD.js +0 -1
  53. package/dist/assets/arrow-up-down-BeIaAKWn.js +0 -6
  54. package/dist/assets/business-EtSaFzKh.js +0 -37
  55. package/dist/assets/business-transactions-single-B0Up2Bs4.js +0 -1
  56. package/dist/assets/business-trip-CmMmdjCh.js +0 -1
  57. package/dist/assets/charges-filters-CqWjSIKI.js +0 -1
  58. package/dist/assets/charges-ledger-validation-DHfLjNlq.js +0 -1
  59. package/dist/assets/chart-D1Y6fN3z.js +0 -74
  60. package/dist/assets/data-table-pagination-BCciANKD.js +0 -11
  61. package/dist/assets/editable-business-trip-VNJjiYOp.js +0 -16
  62. package/dist/assets/graphql-document-dedupe-fragments-ByT8-wlV.js +0 -1
  63. package/dist/assets/index-1L3dEkVN.js +0 -1
  64. package/dist/assets/index-4ZIYAzBJ.js +0 -1
  65. package/dist/assets/index-4r9JDo8p.js +0 -1
  66. package/dist/assets/index-BFHiMLVc.js +0 -1
  67. package/dist/assets/index-BGmhxff9.js +0 -1
  68. package/dist/assets/index-BMcyYDGt.js +0 -11
  69. package/dist/assets/index-BVQkLk5i.js +0 -1
  70. package/dist/assets/index-Bj3HckU6.js +0 -876
  71. package/dist/assets/index-BnQW4CTb.js +0 -1
  72. package/dist/assets/index-C0FVazZA.js +0 -24
  73. package/dist/assets/index-CUDeasQ_.js +0 -1
  74. package/dist/assets/index-Cfm6e2hX.js +0 -1
  75. package/dist/assets/index-Cqz-mRoO.js +0 -17
  76. package/dist/assets/index-Cs7MeC4J.js +0 -137
  77. package/dist/assets/index-CzzfC-dD.css +0 -1
  78. package/dist/assets/index-DABsOOfA.js +0 -1
  79. package/dist/assets/index-DB4BgQdp.js +0 -2
  80. package/dist/assets/index-DV-Q6DUw.js +0 -1
  81. package/dist/assets/index-Dd5ylSyH.js +0 -9
  82. package/dist/assets/index-Dn7p3J8Q.js +0 -1
  83. package/dist/assets/index-Dpk6i3ex.js +0 -6
  84. package/dist/assets/index-EiT4SOpW.js +0 -2
  85. package/dist/assets/index-rcKvu7eR.js +0 -6
  86. package/dist/assets/index-sDbz8nnb.js +0 -1
  87. package/dist/assets/issue-document-CYdlZijI.js +0 -1
  88. package/dist/assets/login-page-CnMdssTN.js +0 -1
  89. package/dist/assets/missing-info-charges-CzeKbCyG.js +0 -1
  90. package/dist/assets/page-not-found-Bsa0DCM4.js +0 -1
  91. package/dist/assets/pencil-BMWYIsZB.js +0 -6
  92. package/dist/assets/report-commentary-row-D9AtKtZx.js +0 -1
  93. package/dist/assets/save-DC_eQcBj.js +0 -11
  94. package/dist/assets/sequential-CAnleQny.js +0 -1
  95. package/dist/assets/similar-charges-by-business-modal-Dmb2ZymF.js +0 -1
  96. package/dist/assets/sub-BIMwxqar.js +0 -1
  97. package/dist/assets/subMonths-B7SR4Lli.js +0 -1
  98. package/src/components/error-boundary.tsx +0 -189
  99. package/src/components/layout/breadcrumbs.tsx +0 -72
  100. package/src/components/layout/document-title.tsx +0 -26
  101. package/src/components/layout/navigation-progress.tsx +0 -52
  102. package/src/components/layout/page-skeleton.tsx +0 -49
  103. package/src/providers/urql-client.ts +0 -86
  104. package/src/router/config.tsx +0 -534
  105. package/src/router/layouts/dashboard-layout.tsx +0 -20
  106. package/src/router/layouts/root-layout.tsx +0 -69
  107. package/src/router/loaders/auth-loader.ts +0 -32
  108. package/src/router/loaders/business-loader.ts +0 -25
  109. package/src/router/loaders/charge-loader.ts +0 -25
  110. package/src/router/loaders/index.ts +0 -17
  111. package/src/router/routes.ts +0 -88
  112. package/src/router/types.ts +0 -68
package/dist/index.html CHANGED
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" href="/icons/accounter-logo.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Accounter</title>
8
- <script type="module" crossorigin src="/assets/index-Bj3HckU6.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-CzzfC-dD.css">
8
+ <script type="module" crossorigin src="/assets/index-CZb2m31K.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-B2UYAO1O.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@accounter/client",
3
- "version": "0.0.8-alpha-20251022132023-9f41aae2ce16f8761101edc548f486cc6ce80333",
3
+ "version": "0.0.8-alpha-20251022144027-c9c7a4b309de8a72dfd0ec4f11e63713ad12878f",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "scripts": {
@@ -0,0 +1,284 @@
1
+ 'use client';
2
+
3
+ import type React from 'react';
4
+ import { useEffect, useState } from 'react';
5
+ import { Save, Shield } from 'lucide-react';
6
+ import { useForm } from 'react-hook-form';
7
+ import { z } from 'zod';
8
+ import { Button } from '@/components/ui/button.js';
9
+ import {
10
+ Card,
11
+ CardContent,
12
+ CardDescription,
13
+ CardFooter,
14
+ CardHeader,
15
+ CardTitle,
16
+ } from '@/components/ui/card.js';
17
+ import {
18
+ Form,
19
+ FormControl,
20
+ FormField,
21
+ FormItem,
22
+ FormLabel,
23
+ FormMessage,
24
+ } from '@/components/ui/form.js';
25
+ import { Input } from '@/components/ui/input.js';
26
+ import {
27
+ BusinessAdminSectionFragmentDoc,
28
+ type BusinessAdminSectionFragment,
29
+ type UpdateAdminBusinessInput,
30
+ } from '@/gql/graphql.js';
31
+ import { getFragmentData, type FragmentType } from '@/gql/index.js';
32
+ import {
33
+ dirtyFieldMarker,
34
+ relevantDataPicker,
35
+ type MakeBoolean,
36
+ type TimelessDateString,
37
+ } from '@/helpers/index.js';
38
+ import { useUpdateAdminBusiness } from '@/hooks/use-update-admin-business.js';
39
+ import { zodResolver } from '@hookform/resolvers/zod';
40
+
41
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
42
+ /* GraphQL */ `
43
+ fragment BusinessAdminSection on Business {
44
+ id
45
+ ... on LtdFinancialEntity {
46
+ adminInfo {
47
+ id
48
+ employerWithholdingTaxAccountNumber
49
+ taxPrepaymentId
50
+ nationalInsuranceEmployerId
51
+ advanceTaxRate
52
+ registrationDate
53
+ }
54
+ }
55
+ }
56
+ `;
57
+
58
+ const adminBusinessFormSchema = z.object({
59
+ employerWithholdingTaxAccountNumber: z.string().min(1, {
60
+ message: 'Employer Withholding Tax Account Number is required',
61
+ }),
62
+ taxPrepaymentId: z.string().min(1, {
63
+ message: 'Tax Prepayment ID is required',
64
+ }),
65
+ nationalInsuranceEmployerId: z.string().min(1, {
66
+ message: 'National Insurance Employer ID is required',
67
+ }),
68
+ advanceTaxRate: z
69
+ .number()
70
+ .min(0, {
71
+ message: 'Advance Tax Rate must be at least 0',
72
+ })
73
+ .max(100, {
74
+ message: 'Advance Tax Rate must be at most 100',
75
+ }),
76
+ registrationDate: z.string().min(1, {
77
+ message: 'Registration Date is required',
78
+ }),
79
+ });
80
+
81
+ type AdminBusinessFormValues = z.infer<typeof adminBusinessFormSchema>;
82
+
83
+ function BusinessAdminSectionFragmentToFormValues(
84
+ admin?: BusinessAdminSectionFragment,
85
+ ): AdminBusinessFormValues {
86
+ if (!admin || admin.__typename !== 'LtdFinancialEntity' || !admin.adminInfo) {
87
+ return {} as AdminBusinessFormValues;
88
+ }
89
+
90
+ return {
91
+ employerWithholdingTaxAccountNumber: admin.adminInfo.employerWithholdingTaxAccountNumber ?? '',
92
+ taxPrepaymentId: admin.adminInfo.taxPrepaymentId ?? '',
93
+ nationalInsuranceEmployerId: admin.adminInfo.nationalInsuranceEmployerId ?? '',
94
+ advanceTaxRate: admin.adminInfo.advanceTaxRate ?? 0,
95
+ registrationDate: admin.adminInfo.registrationDate ?? '',
96
+ };
97
+ }
98
+
99
+ function convertFormDataToUpdateAdminBusinessInput(
100
+ formData: Partial<AdminBusinessFormValues>,
101
+ ): UpdateAdminBusinessInput {
102
+ return {
103
+ employerWithholdingTaxAccountNumber: formData.employerWithholdingTaxAccountNumber,
104
+ taxPrepaymentId: formData.taxPrepaymentId,
105
+ nationalInsuranceEmployerId: formData.nationalInsuranceEmployerId,
106
+ advanceTaxRate: formData.advanceTaxRate,
107
+ registrationDate: formData.registrationDate as TimelessDateString,
108
+ };
109
+ }
110
+
111
+ interface Props {
112
+ data?: FragmentType<typeof BusinessAdminSectionFragmentDoc>;
113
+ refetchBusiness?: () => Promise<void>;
114
+ }
115
+
116
+ export function AdminBusinessSection({ data, refetchBusiness }: Props): React.ReactElement {
117
+ const admin = getFragmentData(BusinessAdminSectionFragmentDoc, data);
118
+ const [defaultFormValues, setDefaultFormValues] = useState(
119
+ BusinessAdminSectionFragmentToFormValues(admin),
120
+ );
121
+
122
+ const { updateAdminBusiness: updateDbAdminBusiness, fetching: isUpdating } =
123
+ useUpdateAdminBusiness();
124
+
125
+ const form = useForm<AdminBusinessFormValues>({
126
+ resolver: zodResolver(adminBusinessFormSchema),
127
+ defaultValues: defaultFormValues,
128
+ });
129
+
130
+ const onSubmit = async (data: AdminBusinessFormValues) => {
131
+ if (!admin) {
132
+ return;
133
+ }
134
+
135
+ const dataToUpdate = relevantDataPicker(
136
+ data,
137
+ form.formState.dirtyFields as MakeBoolean<typeof data>,
138
+ );
139
+
140
+ if (!dataToUpdate) return;
141
+
142
+ const updateBusinessInput = convertFormDataToUpdateAdminBusinessInput(dataToUpdate);
143
+
144
+ await updateDbAdminBusiness({
145
+ adminBusinessId: admin.id,
146
+ fields: updateBusinessInput,
147
+ });
148
+
149
+ refetchBusiness?.();
150
+ };
151
+
152
+ useEffect(() => {
153
+ if (admin) {
154
+ const formValues = BusinessAdminSectionFragmentToFormValues(admin);
155
+ setDefaultFormValues(formValues);
156
+ form.reset(formValues);
157
+ }
158
+ }, [admin, form]);
159
+
160
+ return (
161
+ <Card>
162
+ <CardHeader>
163
+ <div className="flex items-center gap-2">
164
+ <Shield className="h-5 w-5 text-primary" />
165
+ <CardTitle>Admin Business Configuration</CardTitle>
166
+ </div>
167
+ <CardDescription>
168
+ Tax and registration information for the business operating this application
169
+ </CardDescription>
170
+ </CardHeader>
171
+ <Form {...form}>
172
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
173
+ <CardContent>
174
+ <div className="grid gap-6 md:grid-cols-2">
175
+ {/* Employer Withholding Tax Account Number */}
176
+ <FormField
177
+ control={form.control}
178
+ name="employerWithholdingTaxAccountNumber"
179
+ render={({ field, fieldState }) => (
180
+ <FormItem>
181
+ <FormLabel>Employer Withholding Tax Account Number</FormLabel>
182
+ <FormControl>
183
+ <Input
184
+ placeholder="Enter account number"
185
+ {...field}
186
+ className={dirtyFieldMarker(fieldState)}
187
+ />
188
+ </FormControl>
189
+ <FormMessage />
190
+ </FormItem>
191
+ )}
192
+ />
193
+
194
+ {/* Tax Prepayment ID */}
195
+ <FormField
196
+ control={form.control}
197
+ name="taxPrepaymentId"
198
+ render={({ field, fieldState }) => (
199
+ <FormItem>
200
+ <FormLabel>Tax Prepayment ID</FormLabel>
201
+ <FormControl>
202
+ <Input
203
+ placeholder="Enter prepayment ID"
204
+ {...field}
205
+ className={dirtyFieldMarker(fieldState)}
206
+ />
207
+ </FormControl>
208
+ <FormMessage />
209
+ </FormItem>
210
+ )}
211
+ />
212
+
213
+ {/* National Insurance Employer ID */}
214
+ <FormField
215
+ control={form.control}
216
+ name="nationalInsuranceEmployerId"
217
+ render={({ field, fieldState }) => (
218
+ <FormItem>
219
+ <FormLabel>National Insurance Employer ID</FormLabel>
220
+ <FormControl>
221
+ <Input
222
+ placeholder="Enter employer ID"
223
+ {...field}
224
+ className={dirtyFieldMarker(fieldState)}
225
+ />
226
+ </FormControl>
227
+ <FormMessage />
228
+ </FormItem>
229
+ )}
230
+ />
231
+
232
+ {/* Advance Tax Rate */}
233
+ <FormField
234
+ control={form.control}
235
+ name="advanceTaxRate"
236
+ render={({ field, fieldState }) => (
237
+ <FormItem>
238
+ <FormLabel>Advance Tax Rate (%)</FormLabel>
239
+ <FormControl>
240
+ <Input
241
+ type="number"
242
+ step="0.01"
243
+ min="0"
244
+ max="100"
245
+ placeholder="Enter tax rate"
246
+ {...field}
247
+ className={dirtyFieldMarker(fieldState)}
248
+ />
249
+ </FormControl>
250
+ <FormMessage />
251
+ </FormItem>
252
+ )}
253
+ />
254
+
255
+ {/* Registration Date */}
256
+ <FormField
257
+ control={form.control}
258
+ name="registrationDate"
259
+ render={({ field, fieldState }) => (
260
+ <FormItem>
261
+ <FormLabel>Registration Date</FormLabel>
262
+ <FormControl>
263
+ <Input type="date" {...field} className={dirtyFieldMarker(fieldState)} />
264
+ </FormControl>
265
+ <FormMessage />
266
+ </FormItem>
267
+ )}
268
+ />
269
+ </div>
270
+ </CardContent>
271
+ <CardFooter className="flex justify-end border-t mt-4 pt-6">
272
+ <Button
273
+ type="submit"
274
+ disabled={isUpdating || Object.keys(form.formState.dirtyFields).length === 0}
275
+ >
276
+ <Save className="h-4 w-4" />
277
+ Save Changes
278
+ </Button>
279
+ </CardFooter>
280
+ </form>
281
+ </Form>
282
+ </Card>
283
+ );
284
+ }
@@ -7,12 +7,14 @@ import {
7
7
  FileText,
8
8
  Plug,
9
9
  Settings,
10
+ Shield,
10
11
  // TrendingUp,
11
12
  } from 'lucide-react';
12
13
  import { useSearchParams } from 'react-router-dom';
13
14
  import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs.js';
14
15
  import { getFragmentData, type FragmentType } from '@/gql/index.js';
15
16
  import { BusinessPageFragmentDoc } from '../../gql/graphql.js';
17
+ import { AdminBusinessSection } from './admin-business-section.js';
16
18
  import { BusinessHeader } from './business-header.js';
17
19
  import { ChargesSection } from './charges-section.jsx';
18
20
  import { ChartsSection } from './charts-section.jsx';
@@ -31,11 +33,15 @@ import { TransactionsSection } from './transactions-section.js';
31
33
  clientInfo {
32
34
  id
33
35
  }
36
+ adminInfo {
37
+ id
38
+ }
34
39
  }
35
40
  ...ClientIntegrationsSection
36
41
  ...BusinessHeader
37
42
  ...BusinessContactSection
38
43
  ...BusinessConfigurationSection
44
+ ...BusinessAdminSection
39
45
  }
40
46
  `;
41
47
 
@@ -59,6 +65,7 @@ export default function Business({ data, refetchBusiness }: Props): ReactElement
59
65
  }
60
66
 
61
67
  const isClient = 'clientInfo' in business && !!business.clientInfo;
68
+ const isAdmin = 'adminInfo' in business && !!business.adminInfo;
62
69
 
63
70
  return (
64
71
  <div className="min-h-screen bg-background">
@@ -127,6 +134,15 @@ export default function Business({ data, refetchBusiness }: Props): ReactElement
127
134
  </TabsTrigger> */}
128
135
  </>
129
136
  )}
137
+ {isAdmin && (
138
+ <TabsTrigger
139
+ value="admin"
140
+ className="flex items-center gap-2 data-[state=active]:bg-background"
141
+ >
142
+ <Shield className="h-4 w-4" />
143
+ <span className="hidden sm:inline">Admin</span>
144
+ </TabsTrigger>
145
+ )}
130
146
  </TabsList>
131
147
 
132
148
  <TabsContent value="contact" className="mt-0">
@@ -164,6 +180,12 @@ export default function Business({ data, refetchBusiness }: Props): ReactElement
164
180
  </TabsContent>
165
181
  </>
166
182
  )}
183
+
184
+ {isAdmin && (
185
+ <TabsContent value="admin" className="mt-0">
186
+ <AdminBusinessSection data={business} />
187
+ </TabsContent>
188
+ )}
167
189
  </Tabs>
168
190
  </main>
169
191
  </div>
@@ -1,9 +1,8 @@
1
1
  import { useState, type ReactElement } from 'react';
2
2
  import { format } from 'date-fns';
3
3
  import { ChevronsLeftRightEllipsis, ChevronsRightLeft } from 'lucide-react';
4
- import { Link } from 'react-router-dom';
5
4
  import { useQuery } from 'urql';
6
- import { Mark, Table, Tooltip } from '@mantine/core';
5
+ import { Mark, NavLink, Table, Tooltip } from '@mantine/core';
7
6
  import {
8
7
  BusinessTransactionsInfoDocument,
9
8
  Currency,
@@ -15,7 +14,6 @@ import { AccounterLoader } from '../common/index.js';
15
14
  import { getChargeHref } from '../screens/charges/charge.js';
16
15
  import { Button } from '../ui/button.js';
17
16
  import { DownloadCSV } from './download-csv.js';
18
- import { getBusinessTransactionsHref } from './index.js';
19
17
 
20
18
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
21
19
  /* GraphQL */ `
@@ -235,15 +233,17 @@ export function BusinessExtendedInfo({ businessID, filter }: Props): ReactElemen
235
233
  }}
236
234
  >
237
235
  <td>
238
- <Link
239
- to={getBusinessTransactionsHref({ businessIDs: [row.business.id] })}
236
+ <a
237
+ href={`/businesses/${row.business.id}/transactions`}
240
238
  target="_blank"
241
239
  rel="noreferrer"
242
240
  onClick={event => event.stopPropagation()}
243
- className="inline-flex items-center font-semibold"
244
241
  >
245
- {row.business.name}
246
- </Link>
242
+ <NavLink
243
+ label={row.business.name}
244
+ className="[&>*>.mantine-NavLink-label]:font-semibold"
245
+ />
246
+ </a>
247
247
  </td>
248
248
  <td>{row.invoiceDate ? format(new Date(row.invoiceDate), 'dd/MM/yy') : null}</td>
249
249
  <td style={{ whiteSpace: 'nowrap' }}>
@@ -273,15 +273,17 @@ export function BusinessExtendedInfo({ businessID, filter }: Props): ReactElemen
273
273
  <td>{row.details}</td>
274
274
  <td>
275
275
  {row.counterAccount && (
276
- <Link
277
- to={getBusinessTransactionsHref({ businessIDs: [row.counterAccount.id] })}
276
+ <a
277
+ href={`/businesses/${row.counterAccount?.id}/transactions`}
278
278
  target="_blank"
279
279
  rel="noreferrer"
280
280
  onClick={event => event.stopPropagation()}
281
- className="inline-flex items-center font-semibold"
282
281
  >
283
- {row.counterAccount.name}
284
- </Link>
282
+ <NavLink
283
+ label={row.counterAccount?.name}
284
+ className="[&>*>.mantine-NavLink-label]:font-semibold"
285
+ />
286
+ </a>
285
287
  )}
286
288
  </td>
287
289
  <td />
@@ -1,5 +1,5 @@
1
1
  import { useContext, useEffect, useMemo, useState, type ReactElement, type ReactNode } from 'react';
2
- import { useParams } from 'react-router-dom';
2
+ import { useMatch } from 'react-router-dom';
3
3
  import { useQuery } from 'urql';
4
4
  import { Mark } from '@mantine/core';
5
5
  import {
@@ -19,10 +19,10 @@ type Props = {
19
19
  };
20
20
 
21
21
  export const BusinessTransactionsSingle = ({ businessId }: Props): ReactElement => {
22
- const { businessId: businessIdFromUrl } = useParams<{ businessId: string }>();
22
+ const match = useMatch('businesses/:businessId/transactions');
23
23
  const { get } = useUrlQuery();
24
24
  const { setFiltersContext } = useContext(FiltersContext);
25
- const id = businessId || businessIdFromUrl;
25
+ const id = businessId || match?.params.businessId;
26
26
  const [filter, setFilter] = useState<BusinessTransactionsFilter>(
27
27
  get('transactionsFilters')
28
28
  ? {
@@ -72,24 +72,13 @@ import {
72
72
  export function getBusinessTransactionsHref(filter?: BusinessTransactionsFilter | null): string {
73
73
  const params = new URLSearchParams();
74
74
 
75
- let businessId: string | undefined = undefined;
76
- let adjustedFilter = filter;
77
- if (filter?.businessIDs && filter.businessIDs.length === 1) {
78
- const { businessIDs, ...rest } = filter;
79
- businessId = businessIDs[0];
80
- adjustedFilter = rest;
81
- }
82
-
83
- const transactionsFilters = encodeTransactionsFilters(adjustedFilter);
75
+ const transactionsFilters = encodeTransactionsFilters(filter);
84
76
  if (transactionsFilters) {
85
77
  // Add it as a single encoded parameter
86
78
  params.append('transactionsFilters', transactionsFilters);
87
79
  }
88
80
 
89
81
  const queryParams = params.size > 0 ? `?${params}` : '';
90
- if (businessId) {
91
- return `/businesses/${businessId}/transactions${queryParams}`;
92
- }
93
82
  return `/businesses/transactions${queryParams}`;
94
83
  }
95
84
 
@@ -1,5 +1,5 @@
1
1
  import { useContext, useEffect, type ReactElement } from 'react';
2
- import { useParams } from 'react-router-dom';
2
+ import { useMatch } from 'react-router-dom';
3
3
  import { useQuery } from 'urql';
4
4
  import { Container } from '@mantine/core';
5
5
  import { BusinessTripScreenDocument } from '../../gql/graphql.js';
@@ -26,9 +26,9 @@ type Props = {
26
26
  };
27
27
 
28
28
  export const BusinessTrip = ({ businessTripId }: Props): ReactElement => {
29
- const { businessTripId: businessTripIdFromUrl } = useParams<{ businessTripId: string }>();
29
+ const match = useMatch('business-trips/:businessTripId');
30
30
  const { setFiltersContext } = useContext(FiltersContext);
31
- const id = businessTripId ?? businessTripIdFromUrl ?? '';
31
+ const id = businessTripId ?? match?.params.businessTripId ?? '';
32
32
  const [{ data, fetching }] = useQuery({
33
33
  query: BusinessTripScreenDocument,
34
34
  variables: {
@@ -1,5 +1,5 @@
1
1
  import { type ReactElement } from 'react';
2
- import { Link } from 'react-router-dom';
2
+ import { NavLink } from '@mantine/core';
3
3
  import { ChargesTableBusinessTripFieldsFragmentDoc } from '../../../gql/graphql.js';
4
4
  import { getFragmentData, type FragmentType } from '../../../gql/index.js';
5
5
 
@@ -29,15 +29,17 @@ export const BusinessTrip = ({ data }: Props): ReactElement => {
29
29
 
30
30
  return (
31
31
  <td>
32
- <Link
33
- to={`/business-trips/${charge.businessTrip?.id}`}
32
+ <a
33
+ href={`/business-trips/${charge.businessTrip?.id}`}
34
34
  target="_blank"
35
35
  rel="noreferrer"
36
36
  onClick={event => event.stopPropagation()}
37
- className="inline-flex items-center font-semibold"
38
37
  >
39
- {charge.businessTrip?.name}
40
- </Link>
38
+ <NavLink
39
+ label={charge.businessTrip?.name}
40
+ className="[&>*>.mantine-NavLink-label]:font-semibold"
41
+ />
42
+ </a>
41
43
  </td>
42
44
  );
43
45
  };
@@ -1,6 +1,5 @@
1
1
  import { useCallback, useMemo, type ReactElement } from 'react';
2
- import { Link } from 'react-router-dom';
3
- import { Indicator } from '@mantine/core';
2
+ import { Indicator, NavLink } from '@mantine/core';
4
3
  import {
5
4
  ChargesTableEntityFieldsFragmentDoc,
6
5
  MissingChargeInfo,
@@ -82,15 +81,14 @@ export const Counterparty = ({ data }: Props): ReactElement => {
82
81
  <div className="flex flex-wrap">
83
82
  <Indicator inline size={12} disabled={!isError} color="red" zIndex="auto">
84
83
  {!isError && id && (
85
- <Link
86
- to={getHref(id)}
84
+ <a
85
+ href={getHref(id)}
87
86
  target="_blank"
88
87
  rel="noreferrer"
89
88
  onClick={event => event.stopPropagation()}
90
- className="inline-flex items-center font-semibold"
91
89
  >
92
- {name}
93
- </Link>
90
+ <NavLink label={name} className="[&>*>.mantine-NavLink-label]:font-semibold" />
91
+ </a>
94
92
  )}
95
93
  {isError && name}
96
94
  </Indicator>
@@ -1,6 +1,6 @@
1
1
  import { useState, type ReactElement, type ReactNode } from 'react';
2
- import { Button } from '@/components/ui/button.js';
3
2
  import { Pagination, Paper, Table, type PaginationProps } from '@mantine/core';
3
+ import { Button } from './index.js';
4
4
 
5
5
  export interface AccounterTableProps<T, U> {
6
6
  highlightOnHover?: boolean;
@@ -47,9 +47,7 @@ export function AccounterTableRow<T, U>(props: AccountTableRow<T, U>): ReactElem
47
47
  {moreInfoValue === null ? (
48
48
  <p>No Data Related</p>
49
49
  ) : (
50
- <Button onClick={(): void => setOpened(!opened)} className="ml-auto">
51
- More Info
52
- </Button>
50
+ <Button title="More Info" onClick={(): void => setOpened(!opened)} />
53
51
  )}
54
52
  </td>
55
53
  )}
@@ -75,14 +73,15 @@ export function AccounterTable<T, U>(props: AccounterTableProps<T, U>): ReactNod
75
73
  <div className="flex flex-row justify-end w-full">
76
74
  {props.pagination && <Pagination className="flex-auto" {...props.pagination} />}
77
75
  {props.showButton === true ? (
78
- <Button
76
+ <button
77
+ className="inline-flex text-white bg-indigo-500 border-0 py-1.5 px-3 focus:outline-hidden hover:bg-indigo-600 rounded-sm text-sm"
79
78
  type="button"
80
79
  onClick={(): void => {
81
80
  setIsShowAll(prev => !prev);
82
81
  }}
83
82
  >
84
83
  {isShowAll ? 'Hide All' : 'Show All'}
85
- </Button>
84
+ </button>
86
85
  ) : null}
87
86
  </div>
88
87
  <Table striped={props.striped} highlightOnHover={props.highlightOnHover}>
@@ -1,10 +1,9 @@
1
1
  import { useEffect, useState, type ReactElement } from 'react';
2
2
  import { format } from 'date-fns';
3
3
  import { Controller, type Control } from 'react-hook-form';
4
- import { Link } from 'react-router-dom';
5
4
  import { toast } from 'sonner';
6
5
  import { useQuery } from 'urql';
7
- import { Select, Text } from '@mantine/core';
6
+ import { NavLink, Select, Text } from '@mantine/core';
8
7
  import { DatePickerInput } from '@mantine/dates';
9
8
  import {
10
9
  AttendeesByBusinessTripDocument,
@@ -237,16 +236,15 @@ export const CoreExpenseRow = ({
237
236
  <td>
238
237
  <div className="flex flex-col gap-2">
239
238
  {linkedChargeIds.map(id => (
240
- <Link
239
+ <NavLink
241
240
  key={id}
242
- to={getChargeHref(id)}
243
- target="_blank"
244
- rel="noreferrer"
245
- onClick={event => event.stopPropagation()}
246
- className="inline-flex items-center font-semibold"
247
- >
248
- To Charge
249
- </Link>
241
+ label="To Charge"
242
+ className="[&>*>.mantine-NavLink-label]:font-semibold"
243
+ onClick={(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
244
+ event.stopPropagation();
245
+ window.open(getChargeHref(id), '_blank', 'noreferrer');
246
+ }}
247
+ />
250
248
  ))}
251
249
  </div>
252
250
  </td>
@@ -1,7 +1,6 @@
1
1
  import type { ReactElement } from 'react';
2
2
  import { AlertCircle } from 'lucide-react';
3
- import { Link } from 'react-router-dom';
4
- import { Popover, Table, Text } from '@mantine/core';
3
+ import { NavLink, Popover, Table, Text } from '@mantine/core';
5
4
  import { useDisclosure } from '@mantine/hooks';
6
5
  import {
7
6
  BusinessTripUncategorizedTransactionsFieldsFragmentDoc,
@@ -93,15 +92,18 @@ export const UncategorizedTransactions = ({ data, onChange }: Props): ReactEleme
93
92
  <DebitDate data={uncategorizedTransaction.transaction} />
94
93
  <Amount data={uncategorizedTransaction} />
95
94
  <td>
96
- <Link
97
- to={getChargeHref(uncategorizedTransaction.transaction.chargeId)}
98
- target="_blank"
99
- rel="noreferrer"
100
- onClick={event => event.stopPropagation()}
101
- className="inline-flex items-center font-semibold"
102
- >
103
- To Charge
104
- </Link>
95
+ <NavLink
96
+ label="To Charge"
97
+ className="[&>*>.mantine-NavLink-label]:font-semibold"
98
+ onClick={(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
99
+ event.stopPropagation();
100
+ window.open(
101
+ getChargeHref(uncategorizedTransaction.transaction.chargeId),
102
+ '_blank',
103
+ 'noreferrer',
104
+ );
105
+ }}
106
+ />
105
107
  </td>
106
108
  <Account data={uncategorizedTransaction.transaction} />
107
109
  <Description data={uncategorizedTransaction.transaction} />