@accounter/client 0.0.8-alpha-20251021150615-800574fc6d416cd319de216c97b431643d8958a2 → 0.0.8-alpha-20251021225827-178e480c997a9811913e16f85cb94329041b096e

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 (150) hide show
  1. package/CHANGELOG.md +67 -1
  2. package/dist/assets/Checkbox-CxedbJAl.js +6 -0
  3. package/dist/assets/Progress-D5SuJtCd.js +1 -0
  4. package/dist/assets/Typography-BQFz-z7L.js +1 -0
  5. package/dist/assets/accordion-COWOBKuq.js +1 -0
  6. package/dist/assets/accountant-approvals-Bd2y8us_.js +1 -0
  7. package/dist/assets/all-charges-SWBnaZu7.js +1 -0
  8. package/dist/assets/arrow-up-down-dZmrBLse.js +6 -0
  9. package/dist/assets/business--GVVfDEa.js +37 -0
  10. package/dist/assets/business-transactions-single-BsbkUf_H.js +1 -0
  11. package/dist/assets/business-trip-ByXPVXdG.js +1 -0
  12. package/dist/assets/charges-filters-D43UbXob.js +1 -0
  13. package/dist/assets/charges-ledger-validation-D0uMH_JE.js +1 -0
  14. package/dist/assets/chart-ClU1KbWe.js +74 -0
  15. package/dist/assets/data-table-pagination-D9Y0_Tn8.js +11 -0
  16. package/dist/assets/editable-business-trip-DhqOQBPa.js +16 -0
  17. package/dist/assets/graphql-document-dedupe-fragments-ByT8-wlV.js +1 -0
  18. package/dist/assets/index-1U6rQgQe.js +6 -0
  19. package/dist/assets/index-3-AKn8tg.js +1 -0
  20. package/dist/assets/index-91A2PLZ6.js +137 -0
  21. package/dist/assets/index-BBHuCWRn.js +1 -0
  22. package/dist/assets/index-BPNuFFtx.js +1 -0
  23. package/dist/assets/index-BXqHnRVY.js +1 -0
  24. package/dist/assets/index-BciOH8FS.js +1 -0
  25. package/dist/assets/index-BjHuUHDO.js +1 -0
  26. package/dist/assets/index-BxKmoNQd.js +1 -0
  27. package/dist/assets/index-C3bqiFIv.js +2 -0
  28. package/dist/assets/index-C5MeepK_.js +11 -0
  29. package/dist/assets/index-CAwm68Mg.js +1 -0
  30. package/dist/assets/index-CJ8OGXxv.js +1 -0
  31. package/dist/assets/index-CJyY-qF6.js +1 -0
  32. package/dist/assets/index-CMYnx46_.js +6 -0
  33. package/dist/assets/index-CNrwxUZ7.js +1 -0
  34. package/dist/assets/index-CvV5z5r9.js +876 -0
  35. package/dist/assets/index-D08H2GXq.js +17 -0
  36. package/dist/assets/index-GFsPY1p4.js +2 -0
  37. package/dist/assets/index-KwNwThNu.js +1 -0
  38. package/dist/assets/index-YA8IBFyB.js +1 -0
  39. package/dist/assets/index-ZpyI3qxW.js +24 -0
  40. package/dist/assets/index-gdTXrWXt.css +1 -0
  41. package/dist/assets/index-ytnIEraq.js +9 -0
  42. package/dist/assets/{index.es-DHwHzag1.js → index.es-CYeQ4a5s.js} +5 -5
  43. package/dist/assets/issue-document-CdikNnO2.js +1 -0
  44. package/dist/assets/login-page-effgZS3V.js +1 -0
  45. package/dist/assets/missing-info-charges-CnPFTzoZ.js +1 -0
  46. package/dist/assets/page-not-found-D8YlgDOm.js +1 -0
  47. package/dist/assets/pencil-mxW0-tGM.js +6 -0
  48. package/dist/assets/report-commentary-row-DCozKgVE.js +1 -0
  49. package/dist/assets/save-CHlytUqu.js +11 -0
  50. package/dist/assets/sequential-CAnleQny.js +1 -0
  51. package/dist/assets/similar-charges-by-business-modal-Dzbspk_r.js +1 -0
  52. package/dist/assets/sub-Cp_PhKiD.js +1 -0
  53. package/dist/assets/subMonths-DCj_iXAn.js +1 -0
  54. package/dist/index.html +2 -2
  55. package/package.json +6 -5
  56. package/src/app.tsx +35 -25
  57. package/src/components/business/business-header.tsx +68 -0
  58. package/src/components/business/charges-section.tsx +82 -0
  59. package/src/components/business/charts-section.tsx +115 -0
  60. package/src/components/business/configurations-section.tsx +885 -0
  61. package/src/components/business/contact-info-section.tsx +536 -0
  62. package/src/components/business/contracts-section.tsx +196 -0
  63. package/src/components/business/documents-section.tsx +26 -0
  64. package/src/components/business/index.tsx +171 -0
  65. package/src/components/business/integrations-section.tsx +477 -0
  66. package/src/components/business/transactions-section.tsx +26 -0
  67. package/src/components/business-transactions/business-extended-info.tsx +22 -28
  68. package/src/components/business-transactions/business-transactions-single.tsx +3 -3
  69. package/src/components/business-transactions/index.tsx +13 -2
  70. package/src/components/business-trips/business-trip.tsx +3 -3
  71. package/src/components/charges/cells/business-trip.tsx +6 -8
  72. package/src/components/charges/cells/counterparty.tsx +7 -5
  73. package/src/components/charges/charge-extended-info-menu.tsx +27 -21
  74. package/src/components/charges/charges-row.tsx +12 -10
  75. package/src/components/charges/charges-table.tsx +15 -9
  76. package/src/components/clients/contracts/modify-contract-dialog.tsx +464 -0
  77. package/src/components/clients/modify-client-dialog.tsx +276 -0
  78. package/src/components/common/accounter-table.tsx +6 -5
  79. package/src/components/common/business-trip-report/parts/core-expense-row.tsx +11 -9
  80. package/src/components/common/business-trip-report/parts/uncategorized-transactions.tsx +11 -13
  81. package/src/components/common/buttons/index.ts +0 -2
  82. package/src/components/common/buttons/logout-button.tsx +7 -6
  83. package/src/components/common/documents/issue-document/index.tsx +3 -3
  84. package/src/components/common/documents/issue-document/{recent-client-docs.tsx → recent-business-docs.tsx} +19 -13
  85. package/src/components/common/documents-to-charge-matcher/selection-handler/index.tsx +4 -2
  86. package/src/components/common/documents-to-charge-matcher/selection-handler/wide-filtered-selection.tsx +5 -7
  87. package/src/components/common/forms/business-card.tsx +1 -0
  88. package/src/components/common/forms/edit-document.tsx +23 -10
  89. package/src/components/common/forms/modify-business-fields.tsx +2 -19
  90. package/src/components/common/inputs/combo-box.tsx +1 -1
  91. package/src/components/common/new-documents-list.tsx +10 -8
  92. package/src/components/documents-table/cells/creditor.tsx +11 -4
  93. package/src/components/documents-table/cells/debtor.tsx +11 -4
  94. package/src/components/error-boundary.tsx +189 -0
  95. package/src/components/layout/breadcrumbs.tsx +77 -0
  96. package/src/components/layout/dashboard-layout.tsx +4 -0
  97. package/src/components/layout/document-title.tsx +31 -0
  98. package/src/components/layout/navigation-progress.tsx +52 -0
  99. package/src/components/layout/page-skeleton.tsx +49 -0
  100. package/src/components/layout/sidelinks.tsx +28 -27
  101. package/src/components/ledger-table/counterparty-cell.tsx +19 -13
  102. package/src/components/login-page.tsx +2 -1
  103. package/src/components/reports/corporate-tax-ruling-compliance-report/index.tsx +3 -3
  104. package/src/components/reports/profit-and-loss-report/index.tsx +3 -3
  105. package/src/components/reports/tax-report/index.tsx +3 -3
  106. package/src/components/reports/trial-balance-report/trial-balance-report-group.tsx +4 -6
  107. package/src/components/reports/trial-balance-report/trial-balance-report-sort-code.tsx +8 -11
  108. package/src/components/screens/businesses/business.tsx +56 -0
  109. package/src/components/screens/charges/charge.tsx +22 -9
  110. package/src/components/screens/documents/issue-documents/edit-issue-document-modal.tsx +4 -4
  111. package/src/components/transactions-table/cells/counterparty.tsx +9 -2
  112. package/src/components/transactions-table/cells-legacy/counterparty.tsx +9 -2
  113. package/src/components/ui/progress.tsx +25 -0
  114. package/src/components/ui/skeleton.tsx +12 -0
  115. package/src/gql/gql.ts +93 -9
  116. package/src/gql/graphql.ts +289 -9
  117. package/src/helpers/contracts.ts +22 -0
  118. package/src/helpers/currency.ts +5 -0
  119. package/src/helpers/index.ts +2 -0
  120. package/src/helpers/pcn874.ts +17 -0
  121. package/src/hooks/use-add-sort-code.ts +1 -1
  122. package/src/hooks/use-add-tag.ts +1 -1
  123. package/src/hooks/use-create-contract.ts +62 -0
  124. package/src/hooks/use-delete-contract.ts +64 -0
  125. package/src/hooks/use-delete-tag.ts +1 -1
  126. package/src/hooks/use-get-all-contracts.ts +0 -1
  127. package/src/hooks/use-insert-client.ts +80 -0
  128. package/src/hooks/use-merge-businesses.ts +1 -1
  129. package/src/hooks/use-merge-charges.ts +1 -1
  130. package/src/hooks/use-update-client.ts +75 -0
  131. package/src/hooks/use-update-contract.ts +69 -0
  132. package/src/index.tsx +4 -22
  133. package/src/providers/auth-guard.tsx +14 -23
  134. package/src/providers/index.tsx +7 -2
  135. package/src/providers/urql-client.ts +86 -0
  136. package/src/providers/urql.tsx +7 -12
  137. package/src/providers/user-provider.tsx +3 -2
  138. package/src/router/config.tsx +534 -0
  139. package/src/router/layouts/dashboard-layout.tsx +20 -0
  140. package/src/router/layouts/root-layout.tsx +69 -0
  141. package/src/router/loaders/auth-loader.ts +32 -0
  142. package/src/router/loaders/business-loader.ts +25 -0
  143. package/src/router/loaders/charge-loader.ts +25 -0
  144. package/src/router/loaders/index.ts +17 -0
  145. package/src/router/routes.ts +88 -0
  146. package/src/router/types.ts +62 -0
  147. package/dist/assets/index-0eCf1BcD.css +0 -1
  148. package/dist/assets/index-DHTbHvtz.js +0 -1188
  149. package/src/components/common/buttons/button-with-label.tsx +0 -41
  150. package/src/components/common/buttons/button.tsx +0 -44
@@ -0,0 +1,276 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ import { Plus, X } from 'lucide-react';
3
+ import { useForm } from 'react-hook-form';
4
+ import { z } from 'zod';
5
+ import { Button } from '@/components/ui/button.js';
6
+ import {
7
+ Dialog,
8
+ DialogContent,
9
+ DialogDescription,
10
+ DialogFooter,
11
+ DialogHeader,
12
+ DialogTitle,
13
+ DialogTrigger,
14
+ } from '@/components/ui/dialog.js';
15
+ import {
16
+ Form,
17
+ FormControl,
18
+ FormField,
19
+ FormItem,
20
+ FormLabel,
21
+ FormMessage,
22
+ } from '@/components/ui/form.js';
23
+ import { Input } from '@/components/ui/input.js';
24
+ import {
25
+ Select,
26
+ SelectContent,
27
+ SelectItem,
28
+ SelectTrigger,
29
+ SelectValue,
30
+ } from '@/components/ui/select.js';
31
+ import { DocumentType } from '@/gql/graphql.js';
32
+ import { getDocumentNameFromType } from '@/helpers/index.js';
33
+ import { useInsertClient } from '@/hooks/use-insert-client.js';
34
+ import { useUpdateClient } from '@/hooks/use-update-client.js';
35
+ import { zodResolver } from '@hookform/resolvers/zod';
36
+ import { Badge } from '../ui/badge';
37
+
38
+ const clientFormSchema = z.object({
39
+ id: z.uuid().optional(),
40
+ emails: z.array(z.email()).optional(),
41
+ generatedDocumentType: z.enum(Object.values(DocumentType)),
42
+ greenInvoiceId: z.uuid().optional(),
43
+ hiveId: z.string().optional(),
44
+ });
45
+
46
+ export type ClientFormValues = z.infer<typeof clientFormSchema>;
47
+
48
+ interface Props {
49
+ businessId: string;
50
+ client?: ClientFormValues | null;
51
+ onDone?: () => void;
52
+ }
53
+
54
+ export function ModifyClientDialog({ client, businessId, onDone }: Props) {
55
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
56
+ const [editingClient, setEditingClient] = useState<ClientFormValues | null>(null);
57
+
58
+ const { updateClient } = useUpdateClient();
59
+ const { insertClient } = useInsertClient();
60
+
61
+ const [newEmail, setNewEmail] = useState('');
62
+
63
+ const newClientDefaultValues: ClientFormValues = {
64
+ id: businessId,
65
+ generatedDocumentType: DocumentType.Proforma,
66
+ emails: [],
67
+ };
68
+
69
+ const form = useForm<ClientFormValues>({
70
+ resolver: zodResolver(clientFormSchema),
71
+ defaultValues: client || newClientDefaultValues,
72
+ });
73
+
74
+ useEffect(() => {
75
+ if (client) {
76
+ setEditingClient(client);
77
+ form.reset({
78
+ emails: client.emails,
79
+ greenInvoiceId: client.greenInvoiceId,
80
+ hiveId: client.hiveId,
81
+ generatedDocumentType: client.generatedDocumentType,
82
+ id: client.id,
83
+ });
84
+ setIsDialogOpen(true);
85
+ }
86
+ }, [client, form]);
87
+
88
+ const handleNew = () => {
89
+ setEditingClient(null);
90
+ form.reset(newClientDefaultValues);
91
+ setIsDialogOpen(true);
92
+ };
93
+
94
+ const addEmail = () => {
95
+ if (newEmail.trim()) {
96
+ const currentEmails = form.getValues('emails');
97
+ form.setValue('emails', [...(currentEmails ?? []), newEmail.trim()], { shouldDirty: true });
98
+ setNewEmail('');
99
+ }
100
+ };
101
+
102
+ const removeEmail = (index: number) => {
103
+ const currentEmails = form.getValues('emails');
104
+ form.setValue(
105
+ 'emails',
106
+ (currentEmails ?? []).filter((_, i) => i !== index),
107
+ { shouldDirty: true },
108
+ );
109
+ };
110
+
111
+ const onSubmit = useCallback(
112
+ async (values: ClientFormValues) => {
113
+ if (editingClient) {
114
+ // Handle client update
115
+ updateClient({
116
+ businessId,
117
+ fields: values,
118
+ });
119
+ } else {
120
+ if (!values.greenInvoiceId) {
121
+ form.setError('greenInvoiceId', { message: 'Green Invoice ID is required' });
122
+ return;
123
+ }
124
+ // Handle new client creation
125
+ insertClient({
126
+ fields: {
127
+ ...values,
128
+ greenInvoiceId: values.greenInvoiceId!,
129
+ businessId,
130
+ },
131
+ });
132
+ }
133
+ setIsDialogOpen(false);
134
+ setEditingClient(null);
135
+ onDone?.();
136
+ },
137
+ [editingClient, onDone, updateClient, businessId, insertClient, form],
138
+ );
139
+
140
+ 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>
148
+ <DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
149
+ <DialogHeader>
150
+ <DialogTitle>{editingClient ? 'Edit Client' : 'Create New Client'}</DialogTitle>
151
+ <DialogDescription>
152
+ {editingClient ? 'Update client details' : 'Add a new client with all required details'}
153
+ </DialogDescription>
154
+ </DialogHeader>
155
+ <Form {...form}>
156
+ <form onSubmit={form.handleSubmit(onSubmit)}>
157
+ <div className="grid gap-4 py-4">
158
+ <div className="grid gap-4 md:grid-cols-2">
159
+ <FormField
160
+ control={form.control}
161
+ name="emails"
162
+ render={({ field }) => (
163
+ <FormItem>
164
+ <FormLabel>Emails</FormLabel>
165
+ <div className="flex gap-2">
166
+ <Input
167
+ placeholder="Add email..."
168
+ value={newEmail}
169
+ onChange={e => setNewEmail(e.target.value)}
170
+ onKeyDown={e => {
171
+ if (e.key === 'Enter') {
172
+ e.preventDefault();
173
+ addEmail();
174
+ }
175
+ }}
176
+ />
177
+ <Button type="button" size="sm" onClick={addEmail}>
178
+ <Plus className="h-4 w-4" />
179
+ </Button>
180
+ </div>
181
+ <div className="flex flex-wrap gap-2">
182
+ {field.value?.map((email, index) => (
183
+ <Badge key={index} variant="secondary" className="gap-1">
184
+ {email}
185
+ <Button
186
+ variant="ghost"
187
+ size="icon"
188
+ className="p-0 size-3"
189
+ onClick={() => removeEmail(index)}
190
+ >
191
+ <X className="size-3 cursor-pointer" />
192
+ </Button>
193
+ </Badge>
194
+ ))}
195
+ </div>
196
+ <FormMessage />
197
+ </FormItem>
198
+ )}
199
+ />
200
+
201
+ <FormField
202
+ control={form.control}
203
+ name="generatedDocumentType"
204
+ render={({ field }) => (
205
+ <FormItem>
206
+ <FormLabel>Default Document Type</FormLabel>
207
+ <Select onValueChange={field.onChange} defaultValue={field.value}>
208
+ <FormControl>
209
+ <SelectTrigger>
210
+ <SelectValue />
211
+ </SelectTrigger>
212
+ </FormControl>
213
+ <SelectContent>
214
+ {Object.values(DocumentType).map(type => (
215
+ <SelectItem key={type} value={type}>
216
+ {getDocumentNameFromType(type)}
217
+ </SelectItem>
218
+ ))}
219
+ </SelectContent>
220
+ </Select>
221
+ <FormMessage />
222
+ </FormItem>
223
+ )}
224
+ />
225
+
226
+ <FormField
227
+ control={form.control}
228
+ name="greenInvoiceId"
229
+ render={({ field }) => (
230
+ <FormItem>
231
+ <FormLabel>Green Invoice ID</FormLabel>
232
+ <FormControl>
233
+ <Input
234
+ type="text"
235
+ placeholder="Enter Green Invoice ID"
236
+ {...field}
237
+ required={!editingClient}
238
+ />
239
+ </FormControl>
240
+ <FormMessage />
241
+ </FormItem>
242
+ )}
243
+ />
244
+
245
+ <FormField
246
+ control={form.control}
247
+ name="hiveId"
248
+ render={({ field }) => (
249
+ <FormItem>
250
+ <FormLabel>Hive ID</FormLabel>
251
+ <FormControl>
252
+ <Input
253
+ type="text"
254
+ placeholder="Enter Hive ID"
255
+ {...field}
256
+ required={!editingClient}
257
+ />
258
+ </FormControl>
259
+ <FormMessage />
260
+ </FormItem>
261
+ )}
262
+ />
263
+ </div>
264
+ </div>
265
+ <DialogFooter>
266
+ <Button type="button" variant="outline" onClick={() => setIsDialogOpen(false)}>
267
+ Cancel
268
+ </Button>
269
+ <Button type="submit">{editingClient ? 'Update Client' : 'Create Client'}</Button>
270
+ </DialogFooter>
271
+ </form>
272
+ </Form>
273
+ </DialogContent>
274
+ </Dialog>
275
+ );
276
+ }
@@ -1,6 +1,6 @@
1
1
  import { useState, type ReactElement, type ReactNode } from 'react';
2
+ import { Button } from '@/components/ui/button.js';
2
3
  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,7 +47,9 @@ 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 title="More Info" onClick={(): void => setOpened(!opened)} />
50
+ <Button onClick={(): void => setOpened(!opened)} className="ml-auto">
51
+ More Info
52
+ </Button>
51
53
  )}
52
54
  </td>
53
55
  )}
@@ -73,15 +75,14 @@ export function AccounterTable<T, U>(props: AccounterTableProps<T, U>): ReactNod
73
75
  <div className="flex flex-row justify-end w-full">
74
76
  {props.pagination && <Pagination className="flex-auto" {...props.pagination} />}
75
77
  {props.showButton === true ? (
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"
78
+ <Button
78
79
  type="button"
79
80
  onClick={(): void => {
80
81
  setIsShowAll(prev => !prev);
81
82
  }}
82
83
  >
83
84
  {isShowAll ? 'Hide All' : 'Show All'}
84
- </button>
85
+ </Button>
85
86
  ) : null}
86
87
  </div>
87
88
  <Table striped={props.striped} highlightOnHover={props.highlightOnHover}>
@@ -1,9 +1,10 @@
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';
4
5
  import { toast } from 'sonner';
5
6
  import { useQuery } from 'urql';
6
- import { NavLink, Select, Text } from '@mantine/core';
7
+ import { Select, Text } from '@mantine/core';
7
8
  import { DatePickerInput } from '@mantine/dates';
8
9
  import {
9
10
  AttendeesByBusinessTripDocument,
@@ -236,15 +237,16 @@ export const CoreExpenseRow = ({
236
237
  <td>
237
238
  <div className="flex flex-col gap-2">
238
239
  {linkedChargeIds.map(id => (
239
- <NavLink
240
+ <Link
240
241
  key={id}
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
- />
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>
248
250
  ))}
249
251
  </div>
250
252
  </td>
@@ -1,6 +1,7 @@
1
1
  import type { ReactElement } from 'react';
2
2
  import { AlertCircle } from 'lucide-react';
3
- import { NavLink, Popover, Table, Text } from '@mantine/core';
3
+ import { Link } from 'react-router-dom';
4
+ import { Popover, Table, Text } from '@mantine/core';
4
5
  import { useDisclosure } from '@mantine/hooks';
5
6
  import {
6
7
  BusinessTripUncategorizedTransactionsFieldsFragmentDoc,
@@ -92,18 +93,15 @@ export const UncategorizedTransactions = ({ data, onChange }: Props): ReactEleme
92
93
  <DebitDate data={uncategorizedTransaction.transaction} />
93
94
  <Amount data={uncategorizedTransaction} />
94
95
  <td>
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
- />
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>
107
105
  </td>
108
106
  <Account data={uncategorizedTransaction.transaction} />
109
107
  <Description data={uncategorizedTransaction.transaction} />
@@ -1,5 +1,3 @@
1
- export * from './button.js';
2
- export * from './button-with-label.js';
3
1
  export * from './charge-navigate-button.js';
4
2
  export * from './close-document-button.js';
5
3
  export * from './confirm-mini-button.js';
@@ -1,19 +1,20 @@
1
- import { useCallback, useContext, type ReactElement } from 'react';
1
+ import { useContext, type ReactElement } from 'react';
2
2
  import { useNavigate } from 'react-router-dom';
3
3
  import { AuthContext } from '../../../providers/auth-guard.js';
4
+ import { ROUTES } from '../../../router/routes.js';
4
5
  import { Button } from '../../ui/button.js';
5
6
 
6
7
  export function LogoutButton(): ReactElement {
7
8
  const navigate = useNavigate();
8
9
  const { authService } = useContext(AuthContext);
9
10
 
10
- const onLogout = useCallback(() => {
11
- authService.logout();
12
- navigate('/login');
13
- }, [navigate, authService]);
11
+ const handleLogout = async (): Promise<void> => {
12
+ await authService.logout();
13
+ navigate(ROUTES.LOGIN);
14
+ };
14
15
 
15
16
  return (
16
- <Button variant="ghost" onClick={onLogout}>
17
+ <Button variant="ghost" onClick={handleLogout}>
17
18
  Log out
18
19
  </Button>
19
20
  );
@@ -20,7 +20,7 @@ import {
20
20
  } from '../../forms/index.js';
21
21
  import { IssueDocumentModal, type IssueDocumentData } from './issue-document-modal.js';
22
22
  import { PdfViewer } from './pdf-viewer.js';
23
- import { RecentClientDocs } from './recent-client-docs.js';
23
+ import { RecentBusinessDocs } from './recent-business-docs.js';
24
24
  import { RecentDocsOfSameType } from './recent-docs-of-same-type.js';
25
25
 
26
26
  interface GenerateDocumentProps {
@@ -235,8 +235,8 @@ export function GenerateDocument({
235
235
 
236
236
  {/* Previous client documents */}
237
237
  {formData.client?.id && (
238
- <RecentClientDocs
239
- clientId={formData.client?.id}
238
+ <RecentBusinessDocs
239
+ businessId={formData.client?.id}
240
240
  linkedDocumentIds={formData.linkedDocumentIds ?? []}
241
241
  />
242
242
  )}
@@ -4,12 +4,12 @@ import { useMemo } from 'react';
4
4
  import { useQuery } from 'urql';
5
5
  import { flexRender, getCoreRowModel, useReactTable, type ColumnDef } from '@tanstack/react-table';
6
6
  import {
7
- RecentClientIssuedDocumentsDocument,
7
+ RecentBusinessIssuedDocumentsDocument,
8
8
  TableDocumentsRowFieldsFragmentDoc,
9
9
  } from '../../../../gql/graphql.js';
10
10
  import { getFragmentData } from '../../../../gql/index.js';
11
- import { columns, type DocumentsTableRowType } from '../../../documents-table/columns.js';
12
- import { Card, CardContent, CardHeader, CardTitle } from '../../../ui/card.js';
11
+ import { columns, type DocumentsTableRowType } from '../../../documents-table/columns.jsx';
12
+ import { Card, CardContent, CardHeader, CardTitle } from '../../../ui/card.jsx';
13
13
  import {
14
14
  Table,
15
15
  TableBody,
@@ -17,12 +17,12 @@ import {
17
17
  TableHead,
18
18
  TableHeader,
19
19
  TableRow,
20
- } from '../../../ui/table.js';
20
+ } from '../../../ui/table.jsx';
21
21
 
22
22
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
23
23
  /* GraphQL */ `
24
- query RecentClientIssuedDocuments($clientId: UUID!) {
25
- recentDocumentsByClient(clientId: $clientId) {
24
+ query RecentBusinessIssuedDocuments($businessId: UUID!, $limit: Int) {
25
+ recentDocumentsByBusiness(businessId: $businessId, limit: $limit) {
26
26
  id
27
27
  ... on FinancialDocument {
28
28
  issuedDocumentInfo {
@@ -42,25 +42,31 @@ type RowType = DocumentsTableRowType & {
42
42
  };
43
43
  };
44
44
 
45
- interface RecentClientDocsProps {
46
- clientId: string;
45
+ interface RecentBusinessDocsProps {
46
+ businessId: string;
47
47
  linkedDocumentIds: string[];
48
+ limit?: number;
48
49
  }
49
50
 
50
- export function RecentClientDocs({ clientId, linkedDocumentIds }: RecentClientDocsProps) {
51
+ export function RecentBusinessDocs({
52
+ businessId,
53
+ linkedDocumentIds,
54
+ limit,
55
+ }: RecentBusinessDocsProps) {
51
56
  const [{ data, fetching }] = useQuery({
52
- query: RecentClientIssuedDocumentsDocument,
57
+ query: RecentBusinessIssuedDocumentsDocument,
53
58
  variables: {
54
- clientId,
59
+ businessId,
60
+ limit,
55
61
  },
56
62
  });
57
63
 
58
64
  const rows = useMemo(
59
65
  (): RowType[] =>
60
- data?.recentDocumentsByClient?.map(
66
+ data?.recentDocumentsByBusiness?.map(
61
67
  rawDocument => getFragmentData(TableDocumentsRowFieldsFragmentDoc, rawDocument) as RowType,
62
68
  ) ?? [],
63
- [data?.recentDocumentsByClient],
69
+ [data?.recentDocumentsByBusiness],
64
70
  );
65
71
  const limitedColumns = ['date', 'amount', 'vat', 'type', 'serial', 'description', 'file'];
66
72
  const table = useReactTable<RowType>({
@@ -1,4 +1,5 @@
1
1
  import { useMemo, useState, type ReactElement } from 'react';
2
+ import { Button } from '@/components/ui/button.js';
2
3
  import {
3
4
  ChargeToMatchDocumentsFieldsFragmentDoc,
4
5
  DocumentsToMatchFieldsFragmentDoc,
@@ -9,7 +10,6 @@ import { getFragmentData, type FragmentType } from '../../../../gql/index.js';
9
10
  import { useUpdateDocument } from '../../../../hooks/use-update-document.js';
10
11
  import { FormLabel } from '../../../ui/form.js';
11
12
  import { Switch } from '../../../ui/switch.js';
12
- import { Button } from '../../index.js';
13
13
  import { StrictFilteredSelection } from './strict-filtered-selection.js';
14
14
  import { WideFilteredSelection } from './wide-filtered-selection.js';
15
15
 
@@ -165,7 +165,9 @@ export function SelectionHandler({ chargeProps, documentsProps, onDone }: Props)
165
165
  <Switch checked={filterSuggestions} onCheckedChange={setFilterSuggestions} />
166
166
  </div>
167
167
  )}
168
- <Button title="Accept" disabled={selectedDocuments.length === 0} onClick={onExecuteMatch} />
168
+ <Button disabled={selectedDocuments.length === 0} onClick={onExecuteMatch}>
169
+ Accept
170
+ </Button>
169
171
  </div>
170
172
  <div>
171
173
  {filterSuggestions ? (
@@ -1,10 +1,11 @@
1
1
  import { useState, type ReactElement } from 'react';
2
+ import { Button } from '@/components/ui/button.js';
2
3
  import { Image } from '@mantine/core';
3
4
  import {
4
5
  type ChargeToMatchDocumentsFieldsFragment,
5
6
  type DocumentsToMatchFieldsFragment,
6
7
  } from '../../../../gql/graphql.js';
7
- import { AccounterTable, Button, PopUpModal } from '../../index.js';
8
+ import { AccounterTable, PopUpModal } from '../../index.js';
8
9
 
9
10
  interface Props {
10
11
  documents: Exclude<
@@ -53,12 +54,9 @@ export function WideFilteredSelection({
53
54
  title: 'File',
54
55
  value: doc =>
55
56
  doc.file && (
56
- <Button
57
- target="_blank"
58
- rel="noreferrer"
59
- herf={doc.file?.toString()}
60
- title="Open Link"
61
- />
57
+ <a href={doc.file?.toString()} target="_blank" rel="noreferrer">
58
+ <Button>Open Link</Button>
59
+ </a>
62
60
  ),
63
61
  },
64
62
  { title: 'Date', value: doc => ('date' in doc ? doc.date : null) },
@@ -56,6 +56,7 @@ import { ModifyBusinessFields } from './modify-business-fields.js';
56
56
  }
57
57
  optionalVAT
58
58
  irsCode
59
+ pcn874RecordType
59
60
  }
60
61
  }
61
62
  }
@@ -1,12 +1,15 @@
1
1
  import { useEffect, useState, type ReactElement } from 'react';
2
2
  import { useForm, type SubmitHandler } from 'react-hook-form';
3
+ import { Link } from 'react-router-dom';
3
4
  import { useQuery } from 'urql';
5
+ import { Button } from '@/components/ui/button.js';
6
+ import { Label } from '@/components/ui/label.js';
4
7
  import { Drawer, Image, Loader } from '@mantine/core';
5
8
  import { EditDocumentDocument, type UpdateDocumentFieldsInput } from '../../../gql/graphql.js';
6
9
  import { relevantDataPicker, type MakeBoolean } from '../../../helpers/form.js';
7
10
  import { useUpdateDocument } from '../../../hooks/use-update-document.js';
8
11
  import { Form } from '../../ui/form.js';
9
- import { ButtonWithLabel, ImageMagnifier, SimpleGrid } from '../index.js';
12
+ import { ImageMagnifier, SimpleGrid } from '../index.js';
10
13
  import { ModifyDocumentFields } from './modify-document-fields.js';
11
14
 
12
15
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
@@ -106,21 +109,31 @@ export const EditDocument = ({ documentId, onDone, onChange }: Props): ReactElem
106
109
  <form onSubmit={handleSubmit(onSubmit)}>
107
110
  <SimpleGrid cols={4}>
108
111
  <ModifyDocumentFields document={document} formManager={formManager} />
109
- <ButtonWithLabel
110
- target="_blank"
111
- textLabel="File"
112
- url={document?.file?.toString()}
113
- title="Open Link"
114
- />
112
+
113
+ <div className="space-y-2">
114
+ <Label htmlFor="file">File</Label>
115
+ <Link
116
+ to={document?.file?.toString() || ''}
117
+ target="_blank"
118
+ rel="noreferrer"
119
+ className="flex flex-col items-center justify-center mt-5"
120
+ >
121
+ <Button variant="outline" className="w-full mb-2">
122
+ Open File
123
+ </Button>
124
+ </Link>
125
+ </div>
115
126
  </SimpleGrid>
116
127
  <div className="flex justify-center mt-5">
117
- <button
128
+ <Button
118
129
  type="submit"
119
- className="inline-flex cursor-pointer justify-center py-2 px-4 w-2/12 border border-transparent shadow-xs text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
130
+ variant="default"
131
+ // className="inline-flex cursor-pointer justify-center py-2 px-4 w-2/12 mr-5 border border-transparent shadow-xs text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
132
+ onClick={onDone}
120
133
  disabled={fetching || Object.keys(dirtyFields).length === 0}
121
134
  >
122
135
  Save
123
- </button>
136
+ </Button>
124
137
  </div>
125
138
  </form>
126
139
  </Form>