@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
@@ -38,6 +38,7 @@ import {
38
38
  } from '@/helpers/index.js';
39
39
  import { useUpdateAdminBusiness } from '@/hooks/use-update-admin-business.js';
40
40
  import { zodResolver } from '@hookform/resolvers/zod';
41
+ import { Separator } from '../ui/separator';
41
42
 
42
43
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
43
44
  /* GraphQL */ `
@@ -46,10 +47,11 @@ import { zodResolver } from '@hookform/resolvers/zod';
46
47
  ... on LtdFinancialEntity {
47
48
  adminInfo {
48
49
  id
49
- employerWithholdingTaxAccountNumber
50
- taxPrepaymentId
51
- nationalInsuranceEmployerId
52
- advanceTaxRate
50
+ withholdingTaxBookNumber
51
+ withholdingTaxFileNumber
52
+ socialSecurityEmployerId
53
+ taxAdvancesRate
54
+ taxAdvancesId
53
55
  registrationDate
54
56
  }
55
57
  }
@@ -57,16 +59,16 @@ import { zodResolver } from '@hookform/resolvers/zod';
57
59
  `;
58
60
 
59
61
  const adminBusinessFormSchema = z.object({
60
- employerWithholdingTaxAccountNumber: z.string().min(1, {
61
- message: 'Employer Withholding Tax Account Number is required',
62
+ withholdingTaxBookNumber: z.string().min(1, {
63
+ message: 'Withholding Tax Book Number is required',
62
64
  }),
63
- taxPrepaymentId: z.string().min(1, {
64
- message: 'Tax Prepayment ID is required',
65
+ withholdingTaxFileNumber: z.string().min(1, {
66
+ message: 'Withholding Tax File Number is required',
65
67
  }),
66
- nationalInsuranceEmployerId: z.string().min(1, {
68
+ socialSecurityEmployerId: z.string().min(1, {
67
69
  message: 'National Insurance Employer ID is required',
68
70
  }),
69
- advanceTaxRate: z
71
+ taxAdvancesRate: z
70
72
  .number()
71
73
  .min(0, {
72
74
  message: 'Advance Tax Rate must be at least 0',
@@ -74,8 +76,11 @@ const adminBusinessFormSchema = z.object({
74
76
  .max(100, {
75
77
  message: 'Advance Tax Rate must be at most 100',
76
78
  }),
79
+ taxAdvancesId: z.string().min(1, {
80
+ message: 'Tax Advances ID is required',
81
+ }),
77
82
  registrationDate: z.string().min(1, {
78
- message: 'Registration Date is required',
83
+ message: 'Business Registration Start Date is required',
79
84
  }),
80
85
  });
81
86
 
@@ -89,10 +94,11 @@ function BusinessAdminSectionFragmentToFormValues(
89
94
  }
90
95
 
91
96
  return {
92
- employerWithholdingTaxAccountNumber: admin.adminInfo.employerWithholdingTaxAccountNumber ?? '',
93
- taxPrepaymentId: admin.adminInfo.taxPrepaymentId ?? '',
94
- nationalInsuranceEmployerId: admin.adminInfo.nationalInsuranceEmployerId ?? '',
95
- advanceTaxRate: admin.adminInfo.advanceTaxRate ?? 0,
97
+ withholdingTaxBookNumber: admin.adminInfo.withholdingTaxBookNumber ?? '',
98
+ withholdingTaxFileNumber: admin.adminInfo.withholdingTaxFileNumber ?? '',
99
+ socialSecurityEmployerId: admin.adminInfo.socialSecurityEmployerId ?? '',
100
+ taxAdvancesRate: admin.adminInfo.taxAdvancesRate ?? 0,
101
+ taxAdvancesId: admin.adminInfo.taxAdvancesId ?? '',
96
102
  registrationDate: admin.adminInfo.registrationDate ?? '',
97
103
  };
98
104
  }
@@ -101,10 +107,11 @@ function convertFormDataToUpdateAdminBusinessInput(
101
107
  formData: Partial<AdminBusinessFormValues>,
102
108
  ): UpdateAdminBusinessInput {
103
109
  return {
104
- employerWithholdingTaxAccountNumber: formData.employerWithholdingTaxAccountNumber,
105
- taxPrepaymentId: formData.taxPrepaymentId,
106
- nationalInsuranceEmployerId: formData.nationalInsuranceEmployerId,
107
- advanceTaxRate: formData.advanceTaxRate,
110
+ withholdingTaxBookNumber: formData.withholdingTaxBookNumber,
111
+ withholdingTaxFileNumber: formData.withholdingTaxFileNumber,
112
+ socialSecurityEmployerId: formData.socialSecurityEmployerId,
113
+ taxAdvancesRate: formData.taxAdvancesRate,
114
+ taxAdvancesId: formData.taxAdvancesId,
108
115
  registrationDate: formData.registrationDate
109
116
  ? (formatTimelessDateString(new Date(formData.registrationDate)) as TimelessDateString)
110
117
  : undefined,
@@ -174,16 +181,19 @@ export function AdminBusinessSection({ data, refetchBusiness }: Props): React.Re
174
181
  <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
175
182
  <CardContent>
176
183
  <div className="grid gap-6 md:grid-cols-2">
177
- {/* Employer Withholding Tax Account Number */}
184
+ <h3 className="text-sm font-semibold text-foreground md:col-span-2">
185
+ Withholding Tax
186
+ </h3>
187
+
178
188
  <FormField
179
189
  control={form.control}
180
- name="employerWithholdingTaxAccountNumber"
190
+ name="withholdingTaxBookNumber"
181
191
  render={({ field, fieldState }) => (
182
192
  <FormItem>
183
- <FormLabel>Employer Withholding Tax Account Number</FormLabel>
193
+ <FormLabel>Book Number</FormLabel>
184
194
  <FormControl>
185
195
  <Input
186
- placeholder="Enter account number"
196
+ placeholder="Enter book number"
187
197
  {...field}
188
198
  className={dirtyFieldMarker(fieldState)}
189
199
  />
@@ -193,16 +203,15 @@ export function AdminBusinessSection({ data, refetchBusiness }: Props): React.Re
193
203
  )}
194
204
  />
195
205
 
196
- {/* Tax Prepayment ID */}
197
206
  <FormField
198
207
  control={form.control}
199
- name="taxPrepaymentId"
208
+ name="withholdingTaxFileNumber"
200
209
  render={({ field, fieldState }) => (
201
210
  <FormItem>
202
- <FormLabel>Tax Prepayment ID</FormLabel>
211
+ <FormLabel>File Number</FormLabel>
203
212
  <FormControl>
204
213
  <Input
205
- placeholder="Enter prepayment ID"
214
+ placeholder="Enter file number"
206
215
  {...field}
207
216
  className={dirtyFieldMarker(fieldState)}
208
217
  />
@@ -212,16 +221,19 @@ export function AdminBusinessSection({ data, refetchBusiness }: Props): React.Re
212
221
  )}
213
222
  />
214
223
 
215
- {/* National Insurance Employer ID */}
224
+ <Separator className="md:col-span-2" />
225
+
226
+ <h3 className="text-sm font-semibold text-foreground md:col-span-2">Tax Advances</h3>
227
+
216
228
  <FormField
217
229
  control={form.control}
218
- name="nationalInsuranceEmployerId"
230
+ name="taxAdvancesId"
219
231
  render={({ field, fieldState }) => (
220
232
  <FormItem>
221
- <FormLabel>National Insurance Employer ID</FormLabel>
233
+ <FormLabel>Identification number</FormLabel>
222
234
  <FormControl>
223
235
  <Input
224
- placeholder="Enter employer ID"
236
+ placeholder="Enter tax advances ID"
225
237
  {...field}
226
238
  className={dirtyFieldMarker(fieldState)}
227
239
  />
@@ -231,13 +243,12 @@ export function AdminBusinessSection({ data, refetchBusiness }: Props): React.Re
231
243
  )}
232
244
  />
233
245
 
234
- {/* Advance Tax Rate */}
235
246
  <FormField
236
247
  control={form.control}
237
- name="advanceTaxRate"
248
+ name="taxAdvancesRate"
238
249
  render={({ field, fieldState }) => (
239
250
  <FormItem>
240
- <FormLabel>Advance Tax Rate (%)</FormLabel>
251
+ <FormLabel>Tax Rate (%)</FormLabel>
241
252
  <FormControl>
242
253
  <Input
243
254
  type="number"
@@ -254,13 +265,40 @@ export function AdminBusinessSection({ data, refetchBusiness }: Props): React.Re
254
265
  )}
255
266
  />
256
267
 
257
- {/* Registration Date */}
268
+ <Separator className="md:col-span-2" />
269
+
270
+ <h3 className="text-sm font-semibold text-foreground md:col-span-2">
271
+ Social Security
272
+ </h3>
273
+
274
+ {/* Social Security Employer ID */}
275
+ <FormField
276
+ control={form.control}
277
+ name="socialSecurityEmployerId"
278
+ render={({ field, fieldState }) => (
279
+ <FormItem>
280
+ <FormLabel>Employer Identifier</FormLabel>
281
+ <FormControl>
282
+ <Input
283
+ placeholder="Enter employer ID"
284
+ {...field}
285
+ className={dirtyFieldMarker(fieldState)}
286
+ />
287
+ </FormControl>
288
+ <FormMessage />
289
+ </FormItem>
290
+ )}
291
+ />
292
+
293
+ <Separator className="md:col-span-2" />
294
+
295
+ {/* Business Registration Start Date */}
258
296
  <FormField
259
297
  control={form.control}
260
298
  name="registrationDate"
261
299
  render={({ field, fieldState }) => (
262
300
  <FormItem>
263
- <FormLabel>Registration Date</FormLabel>
301
+ <FormLabel>Business Registration Start Date</FormLabel>
264
302
  <FormControl>
265
303
  <Input type="date" {...field} className={dirtyFieldMarker(fieldState)} />
266
304
  </FormControl>
@@ -21,7 +21,7 @@ import {
21
21
  query ClientContractsSection($clientId: UUID!) {
22
22
  contractsByClient(clientId: $clientId) {
23
23
  id
24
- purchaseOrder
24
+ purchaseOrders
25
25
  startDate
26
26
  endDate
27
27
  amount {
@@ -35,6 +35,7 @@ import {
35
35
  remarks
36
36
  plan
37
37
  msCloud
38
+ operationsLimit
38
39
  }
39
40
  }
40
41
  `;
@@ -46,7 +47,7 @@ function convertContractDataToFormValues(
46
47
  id: contract.id,
47
48
  // TODO: activate this field later. requires additional backend support
48
49
  // operationsLimit: 0, // Placeholder, as this field is not in the query
49
- po: contract.purchaseOrder ?? undefined,
50
+ pos: contract.purchaseOrders ?? undefined,
50
51
  startDate: contract.startDate,
51
52
  endDate: contract.endDate,
52
53
  paymentAmount: contract.amount.raw,
@@ -58,6 +59,7 @@ function convertContractDataToFormValues(
58
59
  subscriptionPlan: contract.plan ?? undefined,
59
60
  defaultRemark: contract.remarks ?? undefined,
60
61
  defaultDocumentType: contract.documentType ?? undefined,
62
+ operationsLimit: contract.operationsLimit,
61
63
  };
62
64
  }
63
65
 
@@ -130,8 +132,22 @@ export function ContractsSection({ clientId }: Props) {
130
132
  </div>
131
133
 
132
134
  <div className="space-y-0">
133
- <p className="text-sm text-muted-foreground">PO Number</p>
134
- <p className="text-sm font-medium font-mono">{contract.purchaseOrder}</p>
135
+ <p className="text-sm text-muted-foreground">PO Numbers</p>
136
+ <p className="text-sm font-medium font-mono">
137
+ {contract.purchaseOrders.toReversed()[0] ?? ''}
138
+ </p>
139
+ {contract.purchaseOrders.length > 1 && (
140
+ <div className="space-x-1">
141
+ {contract.purchaseOrders
142
+ .toReversed()
143
+ .slice(1)
144
+ .map(po => (
145
+ <span key={po} className="text-xs font-thin font-mono">
146
+ {po}
147
+ </span>
148
+ ))}
149
+ </div>
150
+ )}
135
151
  </div>
136
152
 
137
153
  {/* TODO: activate this field later. requires additional backend support */}
@@ -161,6 +177,11 @@ export function ContractsSection({ clientId }: Props) {
161
177
  </p>
162
178
  </div>
163
179
 
180
+ <div className="space-y-0">
181
+ <p className="text-sm text-muted-foreground">Operations Limit</p>
182
+ <p className="text-sm font-medium">{contract.operationsLimit.toLocaleString()}</p>
183
+ </div>
184
+
164
185
  <div className="space-y-0">
165
186
  <p className="text-sm text-muted-foreground">Document Type</p>
166
187
  <p className="text-sm font-medium">
@@ -3,7 +3,6 @@ import { format } from 'date-fns';
3
3
  import { ChevronsLeftRightEllipsis, ChevronsRightLeft } from 'lucide-react';
4
4
  import { Link } from 'react-router-dom';
5
5
  import { useQuery } from 'urql';
6
- import { ROUTES } from '@/router/routes.js';
7
6
  import { Mark, Table, Tooltip } from '@mantine/core';
8
7
  import {
9
8
  BusinessTransactionsInfoDocument,
@@ -13,8 +12,10 @@ import {
13
12
  } from '../../gql/graphql.js';
14
13
  import { FIAT_CURRENCIES, formatAmountWithCurrency } from '../../helpers/index.js';
15
14
  import { AccounterLoader } from '../common/index.js';
15
+ import { getChargeHref } from '../screens/charges/charge.js';
16
16
  import { Button } from '../ui/button.js';
17
17
  import { DownloadCSV } from './download-csv.js';
18
+ import { getBusinessTransactionsHref } from './index.js';
18
19
 
19
20
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
20
21
  /* GraphQL */ `
@@ -228,15 +229,14 @@ export function BusinessExtendedInfo({ businessID, filter }: Props): ReactElemen
228
229
  {extendedTransactions.map((row, index) => (
229
230
  <tr
230
231
  key={index}
231
- className="cursor-pointer"
232
232
  onClick={event => {
233
233
  event.stopPropagation();
234
- window.open(ROUTES.CHARGES.DETAIL(row.chargeId), '_blank', 'noreferrer');
234
+ window.open(getChargeHref(row.chargeId), '_blank', 'noreferrer');
235
235
  }}
236
236
  >
237
237
  <td>
238
238
  <Link
239
- to={ROUTES.BUSINESSES.DETAIL(row.business.id)}
239
+ to={getBusinessTransactionsHref({ businessIDs: [row.business.id] })}
240
240
  target="_blank"
241
241
  rel="noreferrer"
242
242
  onClick={event => event.stopPropagation()}
@@ -274,7 +274,7 @@ export function BusinessExtendedInfo({ businessID, filter }: Props): ReactElemen
274
274
  <td>
275
275
  {row.counterAccount && (
276
276
  <Link
277
- to={ROUTES.BUSINESSES.DETAIL(row.counterAccount.id)}
277
+ to={getBusinessTransactionsHref({ businessIDs: [row.counterAccount.id] })}
278
278
  target="_blank"
279
279
  rel="noreferrer"
280
280
  onClick={event => event.stopPropagation()}
@@ -21,7 +21,10 @@ import { AccounterTableRow } from '../common/index.js';
21
21
  import { PageLayout } from '../layout/page-layout.js';
22
22
  import { Button } from '../ui/button.js';
23
23
  import { BusinessExtendedInfo } from './business-extended-info.js';
24
- import { BusinessTransactionsFilters } from './business-transactions-filters.js';
24
+ import {
25
+ BusinessTransactionsFilters,
26
+ encodeTransactionsFilters,
27
+ } from './business-transactions-filters.js';
25
28
 
26
29
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
27
30
  /* GraphQL */ `
@@ -66,6 +69,30 @@ import { BusinessTransactionsFilters } from './business-transactions-filters.js'
66
69
  }
67
70
  `;
68
71
 
72
+ export function getBusinessTransactionsHref(filter?: BusinessTransactionsFilter | null): string {
73
+ const params = new URLSearchParams();
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);
84
+ if (transactionsFilters) {
85
+ // Add it as a single encoded parameter
86
+ params.append('transactionsFilters', transactionsFilters);
87
+ }
88
+
89
+ const queryParams = params.size > 0 ? `?${params}` : '';
90
+ if (businessId) {
91
+ return `/businesses/${businessId}/transactions${queryParams}`;
92
+ }
93
+ return `/businesses/transactions${queryParams}`;
94
+ }
95
+
69
96
  type BusinessTransactionsSum = Extract<
70
97
  BusinessTransactionsSummeryQuery['businessTransactionsSumFromLedgerRecords'],
71
98
  { businessTransactionsSum: unknown }
@@ -159,16 +159,12 @@ export const Businesses = (): ReactElement => {
159
159
  tabIndex={0}
160
160
  className="flex-1 cursor-pointer"
161
161
  onClick={(): void => {
162
- navigate(ROUTES.BUSINESSES.DETAIL(business.id), {
163
- state: { from: ROUTES.BUSINESSES.ALL },
164
- });
162
+ navigate(ROUTES.BUSINESSES.DETAIL(business.id));
165
163
  }}
166
164
  onKeyDown={(e): void => {
167
165
  if (e.key === 'Enter' || e.key === ' ') {
168
166
  e.preventDefault();
169
- navigate(ROUTES.BUSINESSES.DETAIL(business.id), {
170
- state: { from: ROUTES.BUSINESSES.ALL },
171
- });
167
+ navigate(ROUTES.BUSINESSES.DETAIL(business.id));
172
168
  }
173
169
  }}
174
170
  >
@@ -1,6 +1,5 @@
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 { ChargesTableBusinessTripFieldsFragmentDoc } from '../../../gql/graphql.js';
5
4
  import { getFragmentData, type FragmentType } from '../../../gql/index.js';
6
5
 
@@ -31,11 +30,7 @@ export const BusinessTrip = ({ data }: Props): ReactElement => {
31
30
  return (
32
31
  <td>
33
32
  <Link
34
- to={
35
- charge.businessTrip?.id
36
- ? ROUTES.BUSINESS_TRIPS.DETAIL(charge.businessTrip?.id)
37
- : ROUTES.BUSINESS_TRIPS.ROOT
38
- }
33
+ to={`/business-trips/${charge.businessTrip?.id}`}
39
34
  target="_blank"
40
35
  rel="noreferrer"
41
36
  onClick={event => event.stopPropagation()}
@@ -1,9 +1,14 @@
1
- import { useMemo, type ReactElement } from 'react';
1
+ import { useCallback, useMemo, type ReactElement } from 'react';
2
2
  import { Link } from 'react-router-dom';
3
- import { ROUTES } from '@/router/routes.js';
4
3
  import { Indicator } from '@mantine/core';
5
- import { ChargesTableEntityFieldsFragmentDoc, MissingChargeInfo } from '../../../gql/graphql.js';
4
+ import {
5
+ ChargesTableEntityFieldsFragmentDoc,
6
+ MissingChargeInfo,
7
+ type ChargeFilter,
8
+ } from '../../../gql/graphql.js';
6
9
  import { getFragmentData, type FragmentType } from '../../../gql/index.js';
10
+ import { useUrlQuery } from '../../../hooks/use-url-query.js';
11
+ import { getBusinessTransactionsHref } from '../../business-transactions/index.js';
7
12
 
8
13
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
9
14
  /* GraphQL */ `
@@ -27,6 +32,7 @@ type Props = {
27
32
  };
28
33
 
29
34
  export const Counterparty = ({ data }: Props): ReactElement => {
35
+ const { get } = useUrlQuery();
30
36
  const { counterparty, validationData, __typename } = getFragmentData(
31
37
  ChargesTableEntityFieldsFragmentDoc,
32
38
  data,
@@ -53,13 +59,31 @@ export const Counterparty = ({ data }: Props): ReactElement => {
53
59
  );
54
60
  const { name, id } = counterparty ?? { name: 'Missing', id: undefined };
55
61
 
62
+ const encodedFilters = get('chargesFilters');
63
+
64
+ const getHref = useCallback(
65
+ (businessID: string) => {
66
+ const currentFilters = encodedFilters
67
+ ? (JSON.parse(decodeURIComponent(encodedFilters as string)) as ChargeFilter)
68
+ : {};
69
+
70
+ return getBusinessTransactionsHref({
71
+ fromDate: currentFilters.fromDate,
72
+ toDate: currentFilters.toDate,
73
+ ownerIds: currentFilters.byOwners,
74
+ businessIDs: [businessID],
75
+ });
76
+ },
77
+ [encodedFilters],
78
+ );
79
+
56
80
  return (
57
81
  <td>
58
82
  <div className="flex flex-wrap">
59
83
  <Indicator inline size={12} disabled={!isError} color="red" zIndex="auto">
60
84
  {!isError && id && (
61
85
  <Link
62
- to={ROUTES.BUSINESSES.DETAIL(id)}
86
+ to={getHref(id)}
63
87
  target="_blank"
64
88
  rel="noreferrer"
65
89
  onClick={event => event.stopPropagation()}
@@ -0,0 +1,15 @@
1
+ import type { ChargeFilter } from '../../gql/graphql.js';
2
+ import { getBusinessTransactionsHref } from '../business-transactions/index.js';
3
+
4
+ export const getBusinessHref = (businessID: string, encodedFilters: string): string => {
5
+ const currentFilters = encodedFilters
6
+ ? (JSON.parse(decodeURIComponent(encodedFilters)) as ChargeFilter)
7
+ : {};
8
+
9
+ return getBusinessTransactionsHref({
10
+ fromDate: currentFilters.fromDate,
11
+ toDate: currentFilters.toDate,
12
+ ownerIds: currentFilters.byOwners,
13
+ businessIDs: [businessID],
14
+ });
15
+ };
@@ -1,8 +1,9 @@
1
1
  import { useCallback, useEffect, useState } from 'react';
2
2
  import { format } from 'date-fns';
3
- import { Plus } from 'lucide-react';
3
+ import { Plus, X } from 'lucide-react';
4
4
  import { useForm } from 'react-hook-form';
5
5
  import { z } from 'zod';
6
+ import { Badge } from '@/components/ui/badge.js';
6
7
  import { Button } from '@/components/ui/button.js';
7
8
  import {
8
9
  Dialog,
@@ -48,7 +49,7 @@ const contractFormSchema = z.object({
48
49
  // operationsLimit: z.number().optional(),
49
50
  startDate: z.iso.date('Start date is required'),
50
51
  endDate: z.iso.date('End date is required'),
51
- po: z.string().optional(),
52
+ pos: z.array(z.string()),
52
53
  paymentAmount: z.number().min(0, 'Payment amount must be non-negative'),
53
54
  paymentCurrency: z.enum(Object.values(Currency), 'Currency is required'),
54
55
  productType: z.enum(Object.values(Product)).optional(),
@@ -58,6 +59,7 @@ const contractFormSchema = z.object({
58
59
  isActive: z.boolean(),
59
60
  defaultRemark: z.string().optional(),
60
61
  defaultDocumentType: z.enum(Object.values(DocumentType)),
62
+ operationsLimit: z.int().min(0, 'Operations limit must be non-negative'),
61
63
  });
62
64
 
63
65
  export type ContractFormValues = z.infer<typeof contractFormSchema>;
@@ -67,7 +69,7 @@ const newContractDefaultValues: ContractFormValues = {
67
69
  // operationsLimit: 0,
68
70
  startDate: '',
69
71
  endDate: '',
70
- po: undefined,
72
+ pos: [],
71
73
  paymentAmount: 0,
72
74
  paymentCurrency: Currency.Usd,
73
75
  productType: Product.Hive,
@@ -77,6 +79,7 @@ const newContractDefaultValues: ContractFormValues = {
77
79
  isActive: true,
78
80
  defaultRemark: undefined,
79
81
  defaultDocumentType: DocumentType.Proforma,
82
+ operationsLimit: 0,
80
83
  };
81
84
 
82
85
  interface Props {
@@ -88,6 +91,7 @@ interface Props {
88
91
  export function ModifyContractDialog({ clientId, contract, onDone }: Props) {
89
92
  const [isDialogOpen, setIsDialogOpen] = useState(false);
90
93
  const [editingContract, setEditingContract] = useState<ContractFormValues | null>(null);
94
+ const [newPO, setNewPO] = useState('');
91
95
 
92
96
  const { updateContract, updating } = useUpdateContract();
93
97
  const { createContract, creating } = useCreateContract();
@@ -97,6 +101,26 @@ export function ModifyContractDialog({ clientId, contract, onDone }: Props) {
97
101
  defaultValues: contract || newContractDefaultValues,
98
102
  });
99
103
 
104
+ const addPO = () => {
105
+ const trimmedPO = newPO.trim();
106
+ if (trimmedPO) {
107
+ const currentLinks = form.getValues('pos');
108
+ if (!currentLinks.includes(trimmedPO)) {
109
+ form.setValue('pos', [...currentLinks, trimmedPO], { shouldDirty: true });
110
+ }
111
+ setNewPO('');
112
+ }
113
+ };
114
+
115
+ const removePO = (index: number) => {
116
+ const currentLinks = form.getValues('pos');
117
+ form.setValue(
118
+ 'pos',
119
+ currentLinks.filter((_, i) => i !== index),
120
+ { shouldDirty: true },
121
+ );
122
+ };
123
+
100
124
  useEffect(() => {
101
125
  if (contract) {
102
126
  setEditingContract(contract);
@@ -105,7 +129,7 @@ export function ModifyContractDialog({ clientId, contract, onDone }: Props) {
105
129
  // operationsLimit: contract.operationsLimit,
106
130
  startDate: contract.startDate,
107
131
  endDate: contract.endDate,
108
- po: contract.po,
132
+ pos: contract.pos,
109
133
  paymentAmount: contract.paymentAmount,
110
134
  paymentCurrency: contract.paymentCurrency,
111
135
  productType: contract.productType,
@@ -115,6 +139,7 @@ export function ModifyContractDialog({ clientId, contract, onDone }: Props) {
115
139
  isActive: contract.isActive,
116
140
  defaultRemark: contract.defaultRemark,
117
141
  defaultDocumentType: contract.defaultDocumentType,
142
+ operationsLimit: contract.operationsLimit,
118
143
  });
119
144
  setIsDialogOpen(true);
120
145
  }
@@ -141,9 +166,10 @@ export function ModifyContractDialog({ clientId, contract, onDone }: Props) {
141
166
  msCloud: values.msCloudLink,
142
167
  plan: values.subscriptionPlan,
143
168
  product: values.productType,
144
- purchaseOrder: values.po,
169
+ purchaseOrders: values.pos,
145
170
  remarks: values.defaultRemark,
146
171
  startDate: format(new Date(values.startDate), 'yyyy-MM-dd') as TimelessDateString,
172
+ operationsLimit: values.operationsLimit,
147
173
  },
148
174
  });
149
175
  } else {
@@ -163,9 +189,10 @@ export function ModifyContractDialog({ clientId, contract, onDone }: Props) {
163
189
  msCloud: values.msCloudLink,
164
190
  plan: values.subscriptionPlan,
165
191
  product: values.productType,
166
- purchaseOrder: values.po,
192
+ purchaseOrders: values.pos,
167
193
  remarks: values.defaultRemark,
168
194
  startDate: format(new Date(values.startDate), 'yyyy-MM-dd') as TimelessDateString,
195
+ operationsLimit: values.operationsLimit,
169
196
  },
170
197
  });
171
198
  }
@@ -197,8 +224,7 @@ export function ModifyContractDialog({ clientId, contract, onDone }: Props) {
197
224
  <form onSubmit={form.handleSubmit(onSubmit)}>
198
225
  <div className="grid gap-4 py-4">
199
226
  <div className="grid gap-4 md:grid-cols-2">
200
- {/* TODO: activate this field later. requires additional backend support */}
201
- {/* <FormField
227
+ <FormField
202
228
  control={form.control}
203
229
  name="operationsLimit"
204
230
  render={({ field }) => (
@@ -210,16 +236,46 @@ export function ModifyContractDialog({ clientId, contract, onDone }: Props) {
210
236
  <FormMessage />
211
237
  </FormItem>
212
238
  )}
213
- /> */}
239
+ />
214
240
  <FormField
215
241
  control={form.control}
216
- name="po"
242
+ name="pos"
217
243
  render={({ field }) => (
218
244
  <FormItem>
219
- <FormLabel>PO Number</FormLabel>
220
- <FormControl>
221
- <Input placeholder="PO-2024-001" {...field} />
222
- </FormControl>
245
+ <FormLabel>Purchase Orders</FormLabel>
246
+ <div className="flex gap-2">
247
+ <Input
248
+ placeholder="Add PO..."
249
+ value={newPO}
250
+ onChange={e => setNewPO(e.target.value)}
251
+ onKeyDown={e => {
252
+ if (e.key === 'Enter') {
253
+ e.preventDefault();
254
+ addPO();
255
+ }
256
+ }}
257
+ />
258
+ <Button type="button" size="sm" onClick={addPO}>
259
+ <Plus className="h-4 w-4" />
260
+ </Button>
261
+ </div>
262
+ <div className="flex flex-wrap gap-2 mt-2">
263
+ {field.value?.map((link, index) => (
264
+ <Badge key={link} variant="secondary" className="gap-1 max-w-xs truncate">
265
+ {link}
266
+ {index === field.value.length - 1 && (
267
+ <Button
268
+ variant="ghost"
269
+ size="icon"
270
+ className="p-0 size-3"
271
+ onClick={() => removePO(index)}
272
+ >
273
+ <X className="size-3 cursor-pointer flex-shrink-0" />
274
+ </Button>
275
+ )}
276
+ </Badge>
277
+ ))}
278
+ </div>
223
279
  <FormMessage />
224
280
  </FormItem>
225
281
  )}