@accounter/client 0.0.8-alpha-20251028124234-328844c99a42b68aa9e2f32fd86da80b4832f631 → 0.0.8-alpha-20251028124537-8b0946b10a6e597c997017c33fb80fbf674e2feb

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 (153) hide show
  1. package/CHANGELOG.md +84 -13
  2. package/dist/assets/{Checkbox-BR6kGuhg.js → Checkbox-DBiOK2wr.js} +3 -3
  3. package/dist/assets/{Progress-7xtiFwK3.js → Progress-3b9RPwUr.js} +1 -1
  4. package/dist/assets/Typography-BjOvYDtJ.js +1 -0
  5. package/dist/assets/accordion-gDGB9JKa.js +1 -0
  6. package/dist/assets/accountant-approvals-aQMSAP78.js +1 -0
  7. package/dist/assets/all-charges-X9R8PfNk.js +1 -0
  8. package/dist/assets/{arrow-up-down-DPG1l9S1.js → arrow-up-down-B3nz1j1G.js} +2 -2
  9. package/dist/assets/{building-2-BgvPBt2Z.js → building-2-DyCA1u9z.js} +2 -2
  10. package/dist/assets/business-BOAeYn9N.js +27 -0
  11. package/dist/assets/{business-header-u2sfE_d8.js → business-header-Ck0v04Iw.js} +1 -1
  12. package/dist/assets/business-transactions-single-_OIHJEfY.js +1 -0
  13. package/dist/assets/business-trip-CYZP6qrZ.js +1 -0
  14. package/dist/assets/charges-filters-Dc9JkkaM.js +1 -0
  15. package/dist/assets/charges-ledger-validation-BiBZ2pEf.js +1 -0
  16. package/dist/assets/{chart-ByngRufj.js → chart-xV-krckX.js} +6 -6
  17. package/dist/assets/{data-table-pagination-D4OEFmX_.js → data-table-pagination-BtKmZaSS.js} +3 -3
  18. package/dist/assets/editable-business-trip-BIRxgXHu.js +16 -0
  19. package/dist/assets/index-2x_26B9B.js +1 -0
  20. package/dist/assets/index-4yVbIKWS.js +1 -0
  21. package/dist/assets/index-B-uojS_H.js +1 -0
  22. package/dist/assets/index-BFRBCjVk.css +1 -0
  23. package/dist/assets/index-BVffhJ_X.js +2 -0
  24. package/dist/assets/index-BZFydPbo.js +1 -0
  25. package/dist/assets/index-BazMRdI3.js +6 -0
  26. package/dist/assets/index-BsTpXlvi.js +891 -0
  27. package/dist/assets/{index-CLUhAJ3t.js → index-C3BnUF_U.js} +3 -3
  28. package/dist/assets/index-CD_kl3oZ.js +1 -0
  29. package/dist/assets/{index-UMFH47hN.js → index-CWM7AEkA.js} +4 -4
  30. package/dist/assets/index-CaXjK2fb.js +2 -0
  31. package/dist/assets/index-CbJd62Hp.js +1 -0
  32. package/dist/assets/index-CdKoH7Cz.js +9 -0
  33. package/dist/assets/index-Crc98w5p.js +1 -0
  34. package/dist/assets/index-Cs-QBGd0.js +1 -0
  35. package/dist/assets/index-D2ada9K2.js +1 -0
  36. package/dist/assets/index-DAkukoD_.js +1 -0
  37. package/dist/assets/index-DML4EmHc.js +1 -0
  38. package/dist/assets/{index-Bsl7tJp_.js → index-DuBbCD-l.js} +22 -22
  39. package/dist/assets/{index-BetG0HVN.js → index-DypFylYK.js} +3 -3
  40. package/dist/assets/{index-vChLJwSt.js → index-KFd7K_WR.js} +3 -3
  41. package/dist/assets/index-rwlm4BcW.js +1 -0
  42. package/dist/assets/index-xA-DiOzv.js +1 -0
  43. package/dist/assets/{index.es-NphQjU3m.js → index.es-C6nk1zNp.js} +1 -1
  44. package/dist/assets/issue-document-BezTjH4r.js +1 -0
  45. package/dist/assets/login-page-I-7tCrbB.js +1 -0
  46. package/dist/assets/missing-info-charges-BDgEJ4mj.js +1 -0
  47. package/dist/assets/page-not-found-CIT5vwb-.js +1 -0
  48. package/dist/assets/{pencil-tV0FsM6V.js → pencil-BHXyXIP9.js} +2 -2
  49. package/dist/assets/report-commentary-row-DHfnYhMN.js +1 -0
  50. package/dist/assets/{save-Czmh3tvC.js → save-NUZuyPEH.js} +2 -2
  51. package/dist/assets/sub-3UcXQDsZ.js +1 -0
  52. package/dist/assets/subMonths-BbuiFsxa.js +1 -0
  53. package/dist/index.html +2 -2
  54. package/package.json +5 -5
  55. package/src/components/business/admin-business-section.tsx +74 -36
  56. package/src/components/business/contracts-section.tsx +25 -4
  57. package/src/components/business-transactions/business-extended-info.tsx +5 -5
  58. package/src/components/business-transactions/index.tsx +28 -1
  59. package/src/components/businesses/index.tsx +2 -6
  60. package/src/components/charges/cells/business-trip.tsx +1 -6
  61. package/src/components/charges/cells/counterparty.tsx +28 -4
  62. package/src/components/charges/helpers.ts +15 -0
  63. package/src/components/clients/contracts/modify-contract-dialog.tsx +70 -14
  64. package/src/components/clients/modify-client-dialog.tsx +27 -9
  65. package/src/components/common/business-trip-report/parts/core-expense-row.tsx +2 -2
  66. package/src/components/common/business-trip-report/parts/report-header.tsx +1 -2
  67. package/src/components/common/business-trip-report/parts/uncategorized-transactions.tsx +2 -2
  68. package/src/components/common/buttons/charge-navigate-button.tsx +11 -12
  69. package/src/components/common/modals/edit-charge-modal.tsx +2 -2
  70. package/src/components/common/modals/insert-business.tsx +88 -36
  71. package/src/components/common/modals/insert-document-modal.tsx +2 -2
  72. package/src/components/common/modals/match-document-modal.tsx +2 -2
  73. package/src/components/common/new-documents-list.tsx +2 -2
  74. package/src/components/documents-table/cells/creditor.tsx +11 -2
  75. package/src/components/documents-table/cells/debtor.tsx +11 -2
  76. package/src/components/layout/user-nav.tsx +22 -3
  77. package/src/components/ledger-table/counterparty-cell.tsx +33 -4
  78. package/src/components/reports/conto/index.tsx +18 -1
  79. package/src/components/reports/trial-balance-report/index.tsx +17 -1
  80. package/src/components/screens/businesses/business.tsx +4 -0
  81. package/src/components/screens/charges/all-charges.tsx +17 -1
  82. package/src/components/screens/charges/charge.tsx +4 -0
  83. package/src/components/screens/reports/balance-report/index.tsx +14 -0
  84. package/src/components/screens/reports/depreciation-report/index.tsx +14 -0
  85. package/src/components/transactions-table/cells/counterparty.tsx +23 -2
  86. package/src/components/transactions-table/cells-legacy/counterparty.tsx +26 -3
  87. package/src/gql/gql.ts +9 -9
  88. package/src/gql/graphql.ts +29 -23
  89. package/src/hooks/use-get-all-contracts.ts +1 -1
  90. package/src/providers/urql.tsx +2 -2
  91. package/dist/assets/MultiSelect-BqcJrWnD.js +0 -1
  92. package/dist/assets/Pagination-6Sv5LXxl.js +0 -1
  93. package/dist/assets/Table-C5zxD95i.js +0 -1
  94. package/dist/assets/Typography-CMzSQZKD.js +0 -1
  95. package/dist/assets/YearPickerInput-CllwSjcn.js +0 -1
  96. package/dist/assets/accordion-CMR2fANH.js +0 -1
  97. package/dist/assets/accountant-approvals-BHFXpHHc.js +0 -1
  98. package/dist/assets/accounter-table-CpeQD4PG.js +0 -1
  99. package/dist/assets/addDays-DvwuJbUs.js +0 -1
  100. package/dist/assets/all-charges-At7WJXuW.js +0 -1
  101. package/dist/assets/business-DKvbSnga.js +0 -32
  102. package/dist/assets/business-extended-info-7CM8px2e.js +0 -13
  103. package/dist/assets/business-transactions-filters-DmpO9Bwo.js +0 -1
  104. package/dist/assets/business-transactions-single-rF6JbAL7.js +0 -1
  105. package/dist/assets/business-trip-BGrp09N7.js +0 -1
  106. package/dist/assets/charge-C-EaA1Xc.js +0 -1
  107. package/dist/assets/charges-filters-Dq-8QHH-.js +0 -1
  108. package/dist/assets/charges-ledger-validation-DlkzVow7.js +0 -1
  109. package/dist/assets/charges-table-Cncs1HLi.js +0 -62
  110. package/dist/assets/create-event-handler-BiA-1lGu.js +0 -1
  111. package/dist/assets/download-csv-button-Df5rDeDc.js +0 -1
  112. package/dist/assets/editable-business-trip-Bav24z0u.js +0 -16
  113. package/dist/assets/funnel-BJ71hzoq.js +0 -6
  114. package/dist/assets/index-BRv0JZw4.js +0 -1
  115. package/dist/assets/index-BXlS8oD_.js +0 -1
  116. package/dist/assets/index-BplSRMAq.css +0 -1
  117. package/dist/assets/index-Cix5qjA-.js +0 -2
  118. package/dist/assets/index-CizLtiES.js +0 -1
  119. package/dist/assets/index-DKBzv0gc.js +0 -1
  120. package/dist/assets/index-DR_5jpS4.js +0 -6
  121. package/dist/assets/index-DTmtILfq.js +0 -1
  122. package/dist/assets/index-DU0G7FZm.js +0 -1
  123. package/dist/assets/index-DUWRgLfu.js +0 -9
  124. package/dist/assets/index-DYZPWG4k.js +0 -2
  125. package/dist/assets/index-DdUE2_44.js +0 -1
  126. package/dist/assets/index-De5dvO0S.js +0 -1
  127. package/dist/assets/index-Des3eR4f.js +0 -1
  128. package/dist/assets/index-DpYwi5kf.js +0 -6
  129. package/dist/assets/index-GzyQZkbY.js +0 -1
  130. package/dist/assets/index-HzLXi-qB.js +0 -1
  131. package/dist/assets/index-Ji28AZwq.js +0 -1
  132. package/dist/assets/index-fuT9duEO.js +0 -1
  133. package/dist/assets/index-wck8Xuay.js +0 -1
  134. package/dist/assets/index-wyU9I4Kz.js +0 -768
  135. package/dist/assets/insert-business-trip-modal-vS1QrUJz.js +0 -6
  136. package/dist/assets/issue-document-pIswc_8i.js +0 -1
  137. package/dist/assets/list-plus-Dy3vzJVy.js +0 -6
  138. package/dist/assets/login-page-YXRfklK1.js +0 -1
  139. package/dist/assets/match-document-modal-BPG_hljb.js +0 -16
  140. package/dist/assets/missing-info-charges-D8dUfChs.js +0 -1
  141. package/dist/assets/modal-D47umnak.js +0 -1
  142. package/dist/assets/page-layout-BG0XvWfD.js +0 -1
  143. package/dist/assets/page-not-found-Cy6oIIb1.js +0 -1
  144. package/dist/assets/panel-top-open-B1rhJYjQ.js +0 -11
  145. package/dist/assets/report-commentary-row-BL2RkIHq.js +0 -1
  146. package/dist/assets/similar-transactions-modal-MQeC2gtu.js +0 -1
  147. package/dist/assets/sub-B4_VmmF9.js +0 -1
  148. package/dist/assets/subMonths-DG7w6bCY.js +0 -1
  149. package/dist/assets/summary-DvHtnArZ.js +0 -1
  150. package/dist/assets/switch-GSloW_Ey.js +0 -1
  151. package/dist/assets/toggle-expansion-button-lwJYTklN.js +0 -1
  152. package/dist/assets/tooltip-BbKSwLCX.js +0 -1
  153. package/dist/assets/use-url-query-CUVSXgm0.js +0 -1
@@ -49,9 +49,10 @@ interface Props {
49
49
  businessId: string;
50
50
  client?: ClientFormValues | null;
51
51
  onDone?: () => void;
52
+ showTrigger?: boolean;
52
53
  }
53
54
 
54
- export function ModifyClientDialog({ client, businessId, onDone }: Props) {
55
+ export function ModifyClientDialog({ client, businessId, onDone, showTrigger = true }: Props) {
55
56
  const [isDialogOpen, setIsDialogOpen] = useState(false);
56
57
  const [editingClient, setEditingClient] = useState<ClientFormValues | null>(null);
57
58
 
@@ -138,13 +139,23 @@ export function ModifyClientDialog({ client, businessId, onDone }: Props) {
138
139
  );
139
140
 
140
141
  return (
141
- <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
142
- <DialogTrigger asChild>
143
- <Button size="sm" onClick={handleNew}>
144
- <Plus className="h-4 w-4 mr-2" />
145
- New Client
146
- </Button>
147
- </DialogTrigger>
142
+ <Dialog
143
+ open={isDialogOpen}
144
+ onOpenChange={status => {
145
+ setIsDialogOpen(status);
146
+ if (!status) {
147
+ onDone?.();
148
+ }
149
+ }}
150
+ >
151
+ {showTrigger && (
152
+ <DialogTrigger asChild>
153
+ <Button size="sm" onClick={handleNew}>
154
+ <Plus className="h-4 w-4 mr-2" />
155
+ New Client
156
+ </Button>
157
+ </DialogTrigger>
158
+ )}
148
159
  <DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
149
160
  <DialogHeader>
150
161
  <DialogTitle>{editingClient ? 'Edit Client' : 'Create New Client'}</DialogTitle>
@@ -263,7 +274,14 @@ export function ModifyClientDialog({ client, businessId, onDone }: Props) {
263
274
  </div>
264
275
  </div>
265
276
  <DialogFooter>
266
- <Button type="button" variant="outline" onClick={() => setIsDialogOpen(false)}>
277
+ <Button
278
+ type="button"
279
+ variant="outline"
280
+ onClick={() => {
281
+ setIsDialogOpen(false);
282
+ onDone?.();
283
+ }}
284
+ >
267
285
  Cancel
268
286
  </Button>
269
287
  <Button type="submit">{editingClient ? 'Update Client' : 'Create Client'}</Button>
@@ -4,7 +4,6 @@ import { Controller, type Control } from 'react-hook-form';
4
4
  import { Link } from 'react-router-dom';
5
5
  import { toast } from 'sonner';
6
6
  import { useQuery } from 'urql';
7
- import { ROUTES } from '@/router/routes.js';
8
7
  import { Select, Text } from '@mantine/core';
9
8
  import { DatePickerInput } from '@mantine/dates';
10
9
  import {
@@ -15,6 +14,7 @@ import {
15
14
  } from '../../../../gql/graphql.js';
16
15
  import { getFragmentData, type FragmentType } from '../../../../gql/index.js';
17
16
  import { TIMELESS_DATE_REGEX } from '../../../../helpers/consts.js';
17
+ import { getChargeHref } from '../../../screens/charges/charge.js';
18
18
  import { CurrencyInput } from '../../index.js';
19
19
 
20
20
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
@@ -239,7 +239,7 @@ export const CoreExpenseRow = ({
239
239
  {linkedChargeIds.map(id => (
240
240
  <Link
241
241
  key={id}
242
- to={ROUTES.CHARGES.DETAIL(id)}
242
+ to={getChargeHref(id)}
243
243
  target="_blank"
244
244
  rel="noreferrer"
245
245
  onClick={event => event.stopPropagation()}
@@ -1,6 +1,5 @@
1
1
  import type { ReactElement } from 'react';
2
2
  import { differenceInDays, format, setHours } from 'date-fns';
3
- import { ROUTES } from '@/router/routes.js';
4
3
  import { Grid, Text } from '@mantine/core';
5
4
  import { BusinessTripReportHeaderFieldsFragmentDoc } from '../../../../gql/graphql.js';
6
5
  import { getFragmentData, type FragmentType } from '../../../../gql/index.js';
@@ -44,7 +43,7 @@ export const ReportHeader = ({ data, onChange }: Props): ReactElement => {
44
43
  <AccountantApproval data={businessTrip} onChange={onChange} />
45
44
  <CopyToClipboardButton
46
45
  isLink
47
- content={`${window.location.origin}${ROUTES.BUSINESS_TRIPS.DETAIL(businessTrip.id)}`}
46
+ content={`${window.location.origin}/business-trips/${businessTrip.id}`}
48
47
  />
49
48
  <BusinessTripToggleMenu businessTripId={businessTrip.id} />
50
49
  </div>
@@ -1,7 +1,6 @@
1
1
  import type { ReactElement } from 'react';
2
2
  import { AlertCircle } from 'lucide-react';
3
3
  import { Link } from 'react-router-dom';
4
- import { ROUTES } from '@/router/routes.js';
5
4
  import { Popover, Table, Text } from '@mantine/core';
6
5
  import { useDisclosure } from '@mantine/hooks';
7
6
  import {
@@ -11,6 +10,7 @@ import {
11
10
  } from '../../../../gql/graphql.js';
12
11
  import { getFragmentData, type FragmentType } from '../../../../gql/index.js';
13
12
  import { formatStringifyAmount } from '../../../../helpers/index.js';
13
+ import { getChargeHref } from '../../../screens/charges/charge.js';
14
14
  import {
15
15
  Account,
16
16
  Counterparty,
@@ -94,7 +94,7 @@ export const UncategorizedTransactions = ({ data, onChange }: Props): ReactEleme
94
94
  <Amount data={uncategorizedTransaction} />
95
95
  <td>
96
96
  <Link
97
- to={ROUTES.CHARGES.DETAIL(uncategorizedTransaction.transaction.chargeId)}
97
+ to={getChargeHref(uncategorizedTransaction.transaction.chargeId)}
98
98
  target="_blank"
99
99
  rel="noreferrer"
100
100
  onClick={event => event.stopPropagation()}
@@ -1,9 +1,8 @@
1
1
  import type { ComponentProps, ReactElement } from 'react';
2
2
  import { ExternalLink } from 'lucide-react';
3
- import { Link } from 'react-router-dom';
4
3
  import { Tooltip } from '@mantine/core';
5
4
  import { cn } from '../../../lib/utils.js';
6
- import { ROUTES } from '../../../router/routes.js';
5
+ import { getChargeHref } from '../../screens/charges/charge.js';
7
6
  import { Button } from '../../ui/button.js';
8
7
 
9
8
  export function ChargeNavigateButton(
@@ -11,17 +10,17 @@ export function ChargeNavigateButton(
11
10
  ): ReactElement {
12
11
  return (
13
12
  <Tooltip label="To Charge">
14
- <Link
15
- to={ROUTES.CHARGES.DETAIL(props.chargeId)}
16
- target="_blank"
17
- rel="noreferrer"
18
- onClick={event => event.stopPropagation()}
19
- className="inline-block"
13
+ <Button
14
+ onClick={event => {
15
+ event.stopPropagation();
16
+ window.open(getChargeHref(props.chargeId), '_blank', 'noreferrer');
17
+ }}
18
+ {...props}
19
+ className={cn('size-7.5', props.className)}
20
+ variant="ghost"
20
21
  >
21
- <Button {...props} className={cn('size-7.5', props.className)} variant="ghost">
22
- <ExternalLink className="size-5" />
23
- </Button>
24
- </Link>
22
+ <ExternalLink className="size-5" />
23
+ </Button>
25
24
  </Tooltip>
26
25
  );
27
26
  }
@@ -1,8 +1,8 @@
1
1
  import type { ReactElement } from 'react';
2
2
  import { useQuery } from 'urql';
3
- import { ROUTES } from '@/router/routes.js';
4
3
  import { Loader } from '@mantine/core';
5
4
  import { EditChargeDocument } from '../../../gql/graphql.js';
5
+ import { getChargeHref } from '../../screens/charges/charge.js';
6
6
  import { CopyToClipboardButton, EditCharge, PopUpDrawer } from '../index.js';
7
7
 
8
8
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
@@ -89,7 +89,7 @@ export const EditChargeModalContent = ({
89
89
  ID: {chargeId}
90
90
  <CopyToClipboardButton
91
91
  isLink
92
- content={`${window.location.origin}${ROUTES.CHARGES.DETAIL(chargeId)}`}
92
+ content={`${window.location.origin}${getChargeHref(chargeId)}`}
93
93
  />
94
94
  </div>
95
95
  </div>
@@ -2,6 +2,10 @@ import { useCallback, useContext, useState } from 'react';
2
2
  import { Globe, Mail, MapPin, Phone, Plus, X } from 'lucide-react';
3
3
  import { useForm } from 'react-hook-form';
4
4
  import { z } from 'zod';
5
+ import {
6
+ ModifyClientDialog,
7
+ type ClientFormValues,
8
+ } from '@/components/clients/modify-client-dialog.js';
5
9
  import { Badge } from '@/components/ui/badge.js';
6
10
  import { Button } from '@/components/ui/button.js';
7
11
  import {
@@ -15,6 +19,7 @@ import {
15
19
  import {
16
20
  Form,
17
21
  FormControl,
22
+ FormDescription,
18
23
  FormField,
19
24
  FormItem,
20
25
  FormLabel,
@@ -28,7 +33,9 @@ import {
28
33
  SelectTrigger,
29
34
  SelectValue,
30
35
  } from '@/components/ui/select.js';
36
+ import { Switch } from '@/components/ui/switch.js';
31
37
  import { Textarea } from '@/components/ui/textarea.js';
38
+ import { DocumentType } from '@/gql/graphql.js';
32
39
  import { pcn874RecordEnum } from '@/helpers/index.js';
33
40
  import { useAllCountries } from '@/hooks/use-get-countries.js';
34
41
  import { useGetSortCodes } from '@/hooks/use-get-sort-codes.js';
@@ -48,8 +55,8 @@ const businessFormSchema = z
48
55
  localName: z.string().optional(),
49
56
  govId: z.string().optional(),
50
57
  address: z.string().optional(),
51
- generalContacts: z.array(z.string().email()),
52
- website: z.string().url().optional().or(z.literal('')),
58
+ generalContacts: z.array(z.email()),
59
+ website: z.url().optional().or(z.literal('')),
53
60
  phone: z.string().optional(),
54
61
  taxCategory: z.string().optional(),
55
62
  sortCode: z.string().optional(),
@@ -57,8 +64,9 @@ const businessFormSchema = z
57
64
  irsCode: z.int().optional(),
58
65
  defaultDescription: z.string().optional(),
59
66
  defaultTags: z.array(z.string()),
67
+ isClient: z.boolean().default(false).optional(),
60
68
  transactionPhrases: z.array(z.string()),
61
- emailAddresses: z.array(z.string().email()),
69
+ emailAddresses: z.array(z.email()),
62
70
  })
63
71
  .refine(
64
72
  data => {
@@ -117,6 +125,7 @@ export function InsertBusiness({
117
125
  onAdd?: (businessId: string) => void;
118
126
  }) {
119
127
  const [isNewBusinessOpen, setIsNewBusinessOpen] = useState(false);
128
+ const [client, setClient] = useState<ClientFormValues | null>(null);
120
129
 
121
130
  const { insertBusiness, fetching: addingInProcess } = useInsertBusiness();
122
131
 
@@ -128,6 +137,7 @@ export function InsertBusiness({
128
137
  defaultTags: [],
129
138
  transactionPhrases: description ? [description] : [],
130
139
  emailAddresses: [],
140
+ locality: 'GAB', // TODO: set default based on DB
131
141
  },
132
142
  });
133
143
 
@@ -135,44 +145,69 @@ export function InsertBusiness({
135
145
  const newBusiness = await insertBusiness({
136
146
  fields: convertFormDataToInsertNewBusinessInput(data),
137
147
  });
148
+ if (data.isClient && newBusiness?.id) {
149
+ setClient({
150
+ businessId: newBusiness.id,
151
+ generatedDocumentType: DocumentType.Proforma,
152
+ emails: [],
153
+ });
154
+ } else {
155
+ onComplete(newBusiness?.id);
156
+ }
157
+ };
158
+
159
+ const onComplete = (businessId: string = '') => {
160
+ setClient(null);
138
161
  setIsNewBusinessOpen(false);
139
162
  form.reset();
140
- onAdd?.(newBusiness?.id ?? '');
163
+ onAdd?.(businessId);
141
164
  };
142
165
 
143
166
  return (
144
- <Dialog open={isNewBusinessOpen} onOpenChange={setIsNewBusinessOpen}>
145
- <DialogTrigger asChild>
146
- <Button>
147
- <Plus className="size-4 mr-2" />
148
- New Business
149
- </Button>
150
- </DialogTrigger>
151
- <DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
152
- <DialogHeader>
153
- <DialogTitle>Create New Business</DialogTitle>
154
- <DialogDescription>Add a new business to Accounter.</DialogDescription>
155
- </DialogHeader>
156
- <Form {...form}>
157
- <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
158
- <ContactInformationSection form={form} />
159
-
160
- <DefaultsSection form={form} />
161
-
162
- <AutoMatchingSection form={form} />
163
-
164
- <div className="flex justify-end gap-3 pt-4 border-t">
165
- <Button type="button" variant="outline" onClick={() => setIsNewBusinessOpen(false)}>
166
- Cancel
167
- </Button>
168
- <Button type="submit" disabled={addingInProcess}>
169
- Create Business
170
- </Button>
171
- </div>
172
- </form>
173
- </Form>
174
- </DialogContent>
175
- </Dialog>
167
+ <>
168
+ <Dialog open={isNewBusinessOpen} onOpenChange={setIsNewBusinessOpen}>
169
+ <DialogTrigger asChild>
170
+ <Button>
171
+ <Plus className="size-4 mr-2" />
172
+ New Business
173
+ </Button>
174
+ </DialogTrigger>
175
+ <DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
176
+ <DialogHeader>
177
+ <DialogTitle>Create New Business</DialogTitle>
178
+ <DialogDescription>Add a new business to Accounter.</DialogDescription>
179
+ </DialogHeader>
180
+ <Form {...form}>
181
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
182
+ <ContactInformationSection form={form} />
183
+
184
+ <DefaultsSection form={form} />
185
+
186
+ <AutoMatchingSection form={form} />
187
+
188
+ <div className="flex justify-end gap-3 pt-4 border-t">
189
+ <Button type="button" variant="outline" onClick={() => setIsNewBusinessOpen(false)}>
190
+ Cancel
191
+ </Button>
192
+ <Button type="submit" disabled={addingInProcess}>
193
+ Create Business
194
+ </Button>
195
+ </div>
196
+ </form>
197
+ </Form>
198
+ </DialogContent>
199
+ </Dialog>
200
+ {client?.businessId && (
201
+ <ModifyClientDialog
202
+ businessId={client.businessId}
203
+ client={client}
204
+ showTrigger={false}
205
+ onDone={() => {
206
+ onComplete(client.businessId);
207
+ }}
208
+ />
209
+ )}
210
+ </>
176
211
  );
177
212
  }
178
213
 
@@ -538,12 +573,29 @@ function DefaultsSection({ form }: SectionProps) {
538
573
  placeholder="Select Default Tags"
539
574
  variant="default"
540
575
  disabled={fetchingTags}
576
+ asChild
541
577
  />
542
578
  </FormControl>
543
579
  <FormMessage />
544
580
  </FormItem>
545
581
  )}
546
582
  />
583
+
584
+ <FormField
585
+ control={form.control}
586
+ name="isClient"
587
+ render={({ field }) => (
588
+ <FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
589
+ <div className="space-y-0.5">
590
+ <FormLabel>Client</FormLabel>
591
+ <FormDescription>Business is a client</FormDescription>
592
+ </div>
593
+ <FormControl>
594
+ <Switch checked={field.value} onCheckedChange={field.onChange} />
595
+ </FormControl>
596
+ </FormItem>
597
+ )}
598
+ />
547
599
  </div>
548
600
  </div>
549
601
  );
@@ -1,5 +1,5 @@
1
1
  import type { ReactElement } from 'react';
2
- import { ROUTES } from '@/router/routes.js';
2
+ import { getChargeHref } from '../../screens/charges/charge.js';
3
3
  import { CopyToClipboardButton, InsertDocument, PopUpDrawer } from '../index.js';
4
4
 
5
5
  interface Props {
@@ -24,7 +24,7 @@ export const InsertDocumentModal = ({
24
24
  Charge ID: {chargeId}
25
25
  <CopyToClipboardButton
26
26
  isLink
27
- content={`${window.location.origin}${ROUTES.CHARGES.DETAIL(chargeId)}`}
27
+ content={`${window.location.origin}${getChargeHref(chargeId)}`}
28
28
  />
29
29
  </div>
30
30
  </div>
@@ -1,5 +1,5 @@
1
1
  import type { ReactElement } from 'react';
2
- import { ROUTES } from '@/router/routes.js';
2
+ import { getChargeHref } from '../../screens/charges/charge.js';
3
3
  import { DocumentsToChargeMatcher } from '../documents-to-charge-matcher/index.js';
4
4
  import { CopyToClipboardButton, PopUpDrawer } from '../index.js';
5
5
 
@@ -25,7 +25,7 @@ export const MatchDocumentModal = ({
25
25
  Charge ID: {chargeId}
26
26
  <CopyToClipboardButton
27
27
  isLink
28
- content={`${window.location.origin}${ROUTES.CHARGES.DETAIL(chargeId)}`}
28
+ content={`${window.location.origin}${getChargeHref(chargeId)}`}
29
29
  />
30
30
  </div>
31
31
  </div>
@@ -1,8 +1,8 @@
1
1
  import type { ReactElement } from 'react';
2
2
  import { Link } from 'react-router-dom';
3
- import { ROUTES } from '@/router/routes.js';
4
3
  import { NewFetchedDocumentFieldsFragmentDoc } from '../../gql/graphql.js';
5
4
  import { getFragmentData, type FragmentType } from '../../gql/index.js';
5
+ import { getChargeHref } from '../screens/charges/charge.js';
6
6
 
7
7
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
8
8
  /* GraphQL */ `
@@ -34,7 +34,7 @@ export const NewDocumentsList = ({ data }: Props): ReactElement => {
34
34
  .map(doc => (
35
35
  <Link
36
36
  key={doc.id}
37
- to={ROUTES.CHARGES.DETAIL(doc.charge!.id)}
37
+ to={getChargeHref(doc.charge!.id)}
38
38
  target="_blank"
39
39
  rel="noreferrer"
40
40
  onClick={event => event.stopPropagation()}
@@ -3,8 +3,9 @@ import { Link } from 'react-router-dom';
3
3
  import { DocumentType } from '@/gql/graphql.js';
4
4
  import { useGetBusinesses } from '@/hooks/use-get-businesses.js';
5
5
  import { useUpdateDocument } from '@/hooks/use-update-document.js';
6
- import { ROUTES } from '@/router/routes.js';
6
+ import { useUrlQuery } from '@/hooks/use-url-query.js';
7
7
  import { Indicator } from '@mantine/core';
8
+ import { getBusinessHref } from '../../charges/helpers.js';
8
9
  import { ConfirmMiniButton, InsertBusiness, SelectWithSearch } from '../../common/index.js';
9
10
  import type { DocumentsTableRowType } from '../columns.js';
10
11
 
@@ -19,6 +20,7 @@ type Props = {
19
20
  };
20
21
 
21
22
  export const Creditor = ({ document, onChange }: Props): ReactElement => {
23
+ const { get } = useUrlQuery();
22
24
  const dbCreditor = 'creditor' in document ? document.creditor : undefined;
23
25
 
24
26
  const shouldHaveCreditor =
@@ -28,6 +30,13 @@ export const Creditor = ({ document, onChange }: Props): ReactElement => {
28
30
 
29
31
  const { selectableBusinesses, refresh: refreshBusinesses } = useGetBusinesses();
30
32
 
33
+ const encodedFilters = get('chargesFilters');
34
+
35
+ const getHref = useCallback(
36
+ (businessId: string) => getBusinessHref(businessId, encodedFilters as string),
37
+ [encodedFilters],
38
+ );
39
+
31
40
  const suggestedCreditor = useMemo(() => {
32
41
  if (dbCreditor || !('missingInfoSuggestions' in document) || !document.missingInfoSuggestions) {
33
42
  // case when creditor is already set or no suggestions
@@ -97,7 +106,7 @@ export const Creditor = ({ document, onChange }: Props): ReactElement => {
97
106
  {shouldHaveCreditor &&
98
107
  (id ? (
99
108
  <Link
100
- to={ROUTES.BUSINESSES.DETAIL(id)}
109
+ to={getHref(id)}
101
110
  target="_blank"
102
111
  rel="noreferrer"
103
112
  onClick={event => event.stopPropagation()}
@@ -1,10 +1,11 @@
1
1
  import { useCallback, useMemo, useState, type ReactElement } from 'react';
2
2
  import { Link } from 'react-router-dom';
3
3
  import { useGetBusinesses } from '@/hooks/use-get-businesses.js';
4
- import { ROUTES } from '@/router/routes.js';
5
4
  import { Indicator } from '@mantine/core';
6
5
  import { DocumentType } from '../../../gql/graphql.js';
7
6
  import { useUpdateDocument } from '../../../hooks/use-update-document.js';
7
+ import { useUrlQuery } from '../../../hooks/use-url-query.js';
8
+ import { getBusinessHref } from '../../charges/helpers.js';
8
9
  import { ConfirmMiniButton, InsertBusiness, SelectWithSearch } from '../../common/index.js';
9
10
  import type { DocumentsTableRowType } from '../columns.js';
10
11
  import { COUNTERPARTIES_LESS_DOCUMENT_TYPES } from './index.js';
@@ -15,6 +16,7 @@ type Props = {
15
16
  };
16
17
 
17
18
  export const Debtor = ({ document, onChange }: Props): ReactElement => {
19
+ const { get } = useUrlQuery();
18
20
  const dbDebtor = 'debtor' in document ? document.debtor : undefined;
19
21
 
20
22
  const shouldHaveDebtor =
@@ -24,6 +26,13 @@ export const Debtor = ({ document, onChange }: Props): ReactElement => {
24
26
 
25
27
  const { selectableBusinesses, refresh: refreshBusinesses } = useGetBusinesses();
26
28
 
29
+ const encodedFilters = get('chargesFilters');
30
+
31
+ const getHref = useCallback(
32
+ (businessId: string) => getBusinessHref(businessId, encodedFilters as string),
33
+ [encodedFilters],
34
+ );
35
+
27
36
  const suggestedDebtor = useMemo(() => {
28
37
  if (dbDebtor || !('missingInfoSuggestions' in document) || !document.missingInfoSuggestions) {
29
38
  // case when creditor is already set or no suggestions
@@ -93,7 +102,7 @@ export const Debtor = ({ document, onChange }: Props): ReactElement => {
93
102
  {shouldHaveDebtor &&
94
103
  (id ? (
95
104
  <Link
96
- to={ROUTES.BUSINESSES.DETAIL(id)}
105
+ to={getHref(id)}
97
106
  target="_blank"
98
107
  rel="noreferrer"
99
108
  onClick={event => event.stopPropagation()}
@@ -1,5 +1,8 @@
1
- import { useState, type JSX } from 'react';
2
- import { CircleCheckBig, FileDown, User2Icon } from 'lucide-react';
1
+ import { useContext, useState, type JSX } from 'react';
2
+ import { CircleCheckBig, FileDown, Shield, User2Icon } from 'lucide-react';
3
+ import { Link } from 'react-router-dom';
4
+ import { UserContext } from '@/providers/index.js';
5
+ import { ROUTES } from '@/router/routes.js';
3
6
  import { Tooltip } from '@mantine/core';
4
7
  import { useDisclosure } from '@mantine/hooks';
5
8
  import { useCornJobs } from '../../hooks/use-corn-jobs.js';
@@ -17,11 +20,14 @@ import {
17
20
  } from '../ui/dropdown-menu.js';
18
21
 
19
22
  export function UserNav(): JSX.Element {
23
+ const { userContext } = useContext(UserContext);
20
24
  const [pullDocumentsOpened, { close: closePullDocuments, open: openPullDocuments }] =
21
25
  useDisclosure(false);
22
26
  const [balanceChargeModalOpen, setBalanceChargeModalOpen] = useState(false);
23
27
  const { executeJobs } = useCornJobs();
24
28
 
29
+ const userName = userContext?.username || 'User Name';
30
+
25
31
  return (
26
32
  <>
27
33
  <DropdownMenu>
@@ -35,11 +41,24 @@ export function UserNav(): JSX.Element {
35
41
  <DropdownMenuContent className="w-56" align="end" forceMount>
36
42
  <DropdownMenuLabel className="font-normal">
37
43
  <div className="flex flex-col space-y-1">
38
- <p className="text-sm font-medium leading-none">User Name</p>
44
+ <p className="text-sm font-medium leading-none">{userName}</p>
39
45
  <p className="text-xs leading-none text-muted-foreground">Email</p>
40
46
  </div>
41
47
  </DropdownMenuLabel>
42
48
  <DropdownMenuSeparator />
49
+ {userContext?.context.adminBusinessId && (
50
+ <DropdownMenuItem asChild>
51
+ <Link
52
+ to={{
53
+ pathname: ROUTES.BUSINESSES.DETAIL(userContext.context.adminBusinessId),
54
+ search: '?tab=admin',
55
+ }}
56
+ >
57
+ <Shield className="size-4" />
58
+ <span className="hidden sm:inline">Admin Configurations</span>
59
+ </Link>
60
+ </DropdownMenuItem>
61
+ )}
43
62
  <DropdownMenuItem>
44
63
  <Button variant="ghost" onClick={() => setBalanceChargeModalOpen(true)}>
45
64
  Add Balance Charge
@@ -1,6 +1,8 @@
1
- import { type ReactElement } from 'react';
1
+ import { useCallback, type ReactElement } from 'react';
2
2
  import { Link } from 'react-router-dom';
3
- import { ROUTES } from '@/router/routes.js';
3
+ import type { ChargeFilter } from '../../gql/graphql.js';
4
+ import { useUrlQuery } from '../../hooks/use-url-query.js';
5
+ import { getBusinessTransactionsHref } from '../business-transactions/index.js';
4
6
 
5
7
  type Props = {
6
8
  account?: {
@@ -14,6 +16,33 @@ type Props = {
14
16
  };
15
17
 
16
18
  export const CounterpartyCell = ({ account, diffAccount }: Props): ReactElement => {
19
+ const { get } = useUrlQuery();
20
+
21
+ const encodedFilters = get('chargesFilters');
22
+
23
+ const getHref = useCallback(
24
+ (businessID: string) => {
25
+ let currentFilters: ChargeFilter = {};
26
+ if (encodedFilters) {
27
+ try {
28
+ const decoded = decodeURIComponent(encodedFilters);
29
+ const parsed = JSON.parse(decoded);
30
+ currentFilters = parsed as ChargeFilter;
31
+ } catch (error) {
32
+ console.error('Failed to parse filters from URL:', error);
33
+ }
34
+ }
35
+
36
+ return getBusinessTransactionsHref({
37
+ ownerIds: currentFilters.byOwners || [],
38
+ businessIDs: [businessID],
39
+ ...(currentFilters.fromDate && { fromDate: currentFilters.fromDate }),
40
+ ...(currentFilters.toDate && { toDate: currentFilters.toDate }),
41
+ });
42
+ },
43
+ [encodedFilters],
44
+ );
45
+
17
46
  const isAccountDiff = diffAccount && diffAccount?.id !== account?.id;
18
47
 
19
48
  return (
@@ -22,7 +51,7 @@ export const CounterpartyCell = ({ account, diffAccount }: Props): ReactElement
22
51
  <>
23
52
  {account && (
24
53
  <Link
25
- to={ROUTES.BUSINESSES.DETAIL(account.id)}
54
+ to={getHref(account.id)}
26
55
  target="_blank"
27
56
  rel="noreferrer"
28
57
  onClick={event => event.stopPropagation()}
@@ -34,7 +63,7 @@ export const CounterpartyCell = ({ account, diffAccount }: Props): ReactElement
34
63
  {isAccountDiff && diffAccount && (
35
64
  <div className="border-2 border-yellow-500 rounded-md">
36
65
  <Link
37
- to={ROUTES.BUSINESSES.DETAIL(diffAccount.id)}
66
+ to={getHref(diffAccount.id)}
38
67
  target="_blank"
39
68
  rel="noreferrer"
40
69
  onClick={event => event.stopPropagation()}