@accounter/client 0.0.7-alpha-20251020201144-e623a2bb538d1219a5f541df2364646012ebe9ea → 0.0.7-alpha-20251021062721-ab43b5f013b9852ff5216bc141852da9dcfe0df7

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 (50) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/dist/assets/index-CJB5mMpd.js +1224 -0
  3. package/dist/assets/index-CzRRUK04.css +1 -0
  4. package/dist/assets/{index.es-CNXdeXse.js → index.es-NOVMdy_X.js} +1 -1
  5. package/dist/index.html +2 -2
  6. package/package.json +3 -2
  7. package/src/app.tsx +8 -4
  8. package/src/components/business/business-header.tsx +65 -0
  9. package/src/components/business/charges-section.tsx +82 -0
  10. package/src/components/business/charts-section.tsx +115 -0
  11. package/src/components/business/configurations-section.tsx +835 -0
  12. package/src/components/business/contact-info-section.tsx +544 -0
  13. package/src/components/business/contracts-section.tsx +195 -0
  14. package/src/components/business/documents-section.tsx +26 -0
  15. package/src/components/business/index.tsx +171 -0
  16. package/src/components/business/integrations-section.tsx +479 -0
  17. package/src/components/business/transactions-section.tsx +26 -0
  18. package/src/components/business-transactions/business-extended-info.tsx +9 -13
  19. package/src/components/charges/charge-extended-info-menu.tsx +27 -21
  20. package/src/components/charges/charges-row.tsx +12 -10
  21. package/src/components/charges/charges-table.tsx +15 -9
  22. package/src/components/clients/contracts/modify-contract-dialog.tsx +466 -0
  23. package/src/components/clients/modify-client-dialog.tsx +276 -0
  24. package/src/components/common/documents/issue-document/index.tsx +3 -3
  25. package/src/components/common/documents/issue-document/{recent-client-docs.tsx → recent-business-docs.tsx} +19 -13
  26. package/src/components/common/forms/business-card.tsx +1 -0
  27. package/src/components/common/forms/modify-business-fields.tsx +15 -33
  28. package/src/components/common/inputs/combo-box.tsx +1 -1
  29. package/src/components/common/modals/insert-business.tsx +4 -2
  30. package/src/components/reports/trial-balance-report/trial-balance-report-group.tsx +4 -6
  31. package/src/components/reports/trial-balance-report/trial-balance-report-sort-code.tsx +8 -11
  32. package/src/components/screens/businesses/business.tsx +50 -0
  33. package/src/components/screens/documents/issue-documents/edit-issue-document-modal.tsx +4 -4
  34. package/src/components/ui/progress.tsx +25 -0
  35. package/src/components/ui/skeleton.tsx +12 -0
  36. package/src/gql/gql.ts +96 -12
  37. package/src/gql/graphql.ts +290 -11
  38. package/src/helpers/contracts.ts +22 -0
  39. package/src/helpers/currency.ts +5 -0
  40. package/src/helpers/index.ts +2 -0
  41. package/src/helpers/pcn874.ts +17 -0
  42. package/src/hooks/use-create-contract.ts +62 -0
  43. package/src/hooks/use-delete-contract.ts +64 -0
  44. package/src/hooks/use-get-all-contracts.ts +0 -1
  45. package/src/hooks/use-insert-client.ts +80 -0
  46. package/src/hooks/use-update-client.ts +75 -0
  47. package/src/hooks/use-update-contract.ts +69 -0
  48. package/src/providers/user-provider.tsx +2 -0
  49. package/dist/assets/index-0eCf1BcD.css +0 -1
  50. package/dist/assets/index-Dh8zU8Ik.js +0 -1188
@@ -0,0 +1,479 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ import { CheckCircle2, Mail, MapPin, Phone, Plus, Settings, XCircle } from 'lucide-react';
3
+ import { useQuery } from 'urql';
4
+ import { Button } from '@/components/ui/button.js';
5
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card.js';
6
+ import { Skeleton } from '@/components/ui/skeleton.js';
7
+ import {
8
+ ClientIntegrationsSectionFragmentDoc,
9
+ ClientIntegrationsSectionGreenInvoiceDocument,
10
+ type ClientUpdateInput,
11
+ } from '@/gql/graphql.js';
12
+ import { getFragmentData, type FragmentType } from '@/gql/index.js';
13
+ import { useUpdateClient } from '@/hooks/use-update-client.js';
14
+ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '../ui/accordion';
15
+ import {
16
+ Dialog,
17
+ DialogContent,
18
+ DialogDescription,
19
+ DialogFooter,
20
+ DialogHeader,
21
+ DialogTitle,
22
+ DialogTrigger,
23
+ } from '../ui/dialog';
24
+ import { Input } from '../ui/input';
25
+ import { Label } from '../ui/label';
26
+
27
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
28
+ /* GraphQL */ `
29
+ fragment ClientIntegrationsSection on LtdFinancialEntity {
30
+ id
31
+ clientInfo {
32
+ id
33
+ greenInvoiceId
34
+ hiveId
35
+ }
36
+ }
37
+ `;
38
+
39
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
40
+ /* GraphQL */ `
41
+ query ClientIntegrationsSectionGreenInvoice($clientId: UUID!) {
42
+ greenInvoiceClient(clientId: $clientId) {
43
+ id
44
+ country
45
+ emails
46
+ name
47
+ phone
48
+ taxId
49
+ address
50
+ city
51
+ zip
52
+ fax
53
+ mobile
54
+ }
55
+ }
56
+ `;
57
+
58
+ const generalIntegrations = [
59
+ {
60
+ id: 'hive',
61
+ name: 'Hive',
62
+ description:
63
+ 'Schema registry, analytics, metrics and gateway for GraphQL federation and other GraphQL APIs',
64
+ status: 'disconnected',
65
+ lastSync: 'Never',
66
+ },
67
+ {
68
+ id: 'slack',
69
+ name: 'Slack',
70
+ description: 'Team communication and notifications',
71
+ status: 'disconnected',
72
+ lastSync: 'Never',
73
+ },
74
+ {
75
+ id: 'retool',
76
+ name: 'Retool',
77
+ description: 'Internal tools and dashboards',
78
+ status: 'disconnected',
79
+ lastSync: 'Never',
80
+ },
81
+ {
82
+ id: 'linear',
83
+ name: 'Linear',
84
+ description: 'Issue tracking and project management',
85
+ status: 'disconnected',
86
+ lastSync: 'Never',
87
+ },
88
+ {
89
+ id: 'workflowy',
90
+ name: 'Workflowy',
91
+ description: 'Task management and note taking',
92
+ status: 'disconnected',
93
+ lastSync: 'Never',
94
+ },
95
+ ];
96
+
97
+ interface Props {
98
+ data?: FragmentType<typeof ClientIntegrationsSectionFragmentDoc>;
99
+ }
100
+
101
+ export function IntegrationsSection({ data }: Props) {
102
+ const business = getFragmentData(ClientIntegrationsSectionFragmentDoc, data);
103
+
104
+ const [{ data: greenInvoiceData, fetching: fetchingGreenInvoice }, fetchGreenInvoice] = useQuery({
105
+ query: ClientIntegrationsSectionGreenInvoiceDocument,
106
+ variables: {
107
+ clientId: business?.id ?? '',
108
+ },
109
+ pause: !business?.clientInfo?.greenInvoiceId || !business?.id,
110
+ });
111
+
112
+ const { updateClient } = useUpdateClient();
113
+
114
+ const updateIdByAttribute = useCallback(
115
+ (id: string, attribute: keyof Pick<ClientUpdateInput, 'greenInvoiceId' | 'hiveId'>) => {
116
+ if (!business?.id) return;
117
+
118
+ const fields: ClientUpdateInput = {};
119
+ fields[attribute] = id;
120
+ updateClient({
121
+ businessId: business?.id,
122
+ fields,
123
+ });
124
+ },
125
+ [business?.id, updateClient],
126
+ );
127
+
128
+ useEffect(() => {
129
+ if (business?.clientInfo?.greenInvoiceId && business?.id) {
130
+ fetchGreenInvoice();
131
+ }
132
+ }, [business?.clientInfo?.greenInvoiceId, business?.id, fetchGreenInvoice]);
133
+
134
+ const greenInvoiceClient = greenInvoiceData?.greenInvoiceClient;
135
+
136
+ return (
137
+ <Card>
138
+ <CardHeader>
139
+ <div className="flex items-center justify-between">
140
+ <div>
141
+ <CardTitle>Integrations</CardTitle>
142
+ <CardDescription>Connected external services and providers</CardDescription>
143
+ </div>
144
+ <Button size="sm">
145
+ <Plus className="h-4 w-4 mr-2" />
146
+ Add Integration
147
+ </Button>
148
+ </div>
149
+ </CardHeader>
150
+ <CardContent className="space-y-6">
151
+ <Accordion type="multiple" defaultValue={['green-invoice']} className="space-y-4">
152
+ {/* Green Invoice Integration */}
153
+ {fetchingGreenInvoice ? (
154
+ <AccordionItem
155
+ value="green-invoice"
156
+ className="rounded-lg border-2 border-green-600/20 bg-green-600/5"
157
+ >
158
+ <div className="px-4 py-3 bg-green-600/10 rounded-t-lg">
159
+ <div className="flex items-center justify-between">
160
+ <div className="flex items-center gap-3 flex-1">
161
+ <Skeleton className="h-10 w-10 rounded-lg" />
162
+ <div className="flex-1 space-y-2">
163
+ <div className="flex items-center gap-2">
164
+ <Skeleton className="h-6 w-32" />
165
+ <Skeleton className="h-5 w-5 rounded-full" />
166
+ </div>
167
+ <Skeleton className="h-4 w-64" />
168
+ </div>
169
+ </div>
170
+ <div className="flex items-center gap-2">
171
+ <Skeleton className="h-8 w-24" />
172
+ <Skeleton className="h-8 w-8" />
173
+ </div>
174
+ </div>
175
+ </div>
176
+
177
+ <AccordionContent>
178
+ <div className="p-6 space-y-6">
179
+ {/* Business Information Skeleton */}
180
+ <div className="space-y-4">
181
+ <Skeleton className="h-4 w-40" />
182
+ <div className="grid gap-4 md:grid-cols-2">
183
+ <div className="space-y-2">
184
+ <Skeleton className="h-3 w-24" />
185
+ <Skeleton className="h-4 w-full" />
186
+ </div>
187
+ <div className="space-y-2">
188
+ <Skeleton className="h-3 w-24" />
189
+ <Skeleton className="h-4 w-full" />
190
+ </div>
191
+ <div className="space-y-2">
192
+ <Skeleton className="h-3 w-24" />
193
+ <Skeleton className="h-4 w-full" />
194
+ </div>
195
+ <div className="space-y-2">
196
+ <Skeleton className="h-3 w-24" />
197
+ <Skeleton className="h-4 w-full" />
198
+ </div>
199
+ </div>
200
+ </div>
201
+
202
+ {/* Contact Information Skeleton */}
203
+ <div className="space-y-4">
204
+ <Skeleton className="h-4 w-40" />
205
+ <div className="grid gap-4 md:grid-cols-2">
206
+ <div className="space-y-2">
207
+ <Skeleton className="h-3 w-32" />
208
+ <Skeleton className="h-6 w-full" />
209
+ </div>
210
+ <div className="space-y-2">
211
+ <Skeleton className="h-3 w-24" />
212
+ <Skeleton className="h-4 w-full" />
213
+ </div>
214
+ <div className="space-y-2">
215
+ <Skeleton className="h-3 w-24" />
216
+ <Skeleton className="h-4 w-full" />
217
+ </div>
218
+ <div className="space-y-2">
219
+ <Skeleton className="h-3 w-24" />
220
+ <Skeleton className="h-4 w-full" />
221
+ </div>
222
+ </div>
223
+ </div>
224
+
225
+ {/* Address Information Skeleton */}
226
+ <div className="space-y-4">
227
+ <Skeleton className="h-4 w-40" />
228
+ <div className="grid gap-4 md:grid-cols-3">
229
+ <div className="space-y-2 md:col-span-2">
230
+ <Skeleton className="h-3 w-24" />
231
+ <Skeleton className="h-4 w-full" />
232
+ </div>
233
+ <div className="space-y-2">
234
+ <Skeleton className="h-3 w-24" />
235
+ <Skeleton className="h-4 w-full" />
236
+ </div>
237
+ <div className="space-y-2">
238
+ <Skeleton className="h-3 w-24" />
239
+ <Skeleton className="h-4 w-full" />
240
+ </div>
241
+ </div>
242
+ </div>
243
+ </div>
244
+ </AccordionContent>
245
+ </AccordionItem>
246
+ ) : (
247
+ <AccordionItem
248
+ value="green-invoice"
249
+ className="rounded-lg border-2 border-green-600/20 bg-green-600/5"
250
+ >
251
+ <div className="px-4 py-3 bg-green-600/10 rounded-t-lg">
252
+ <div className="flex items-center justify-between">
253
+ <div className="flex items-center gap-3 flex-1">
254
+ <div className="h-10 w-10 rounded-lg bg-green-600 flex items-center justify-center text-white font-bold">
255
+ GI
256
+ </div>
257
+ <div className="flex-1">
258
+ <div className="flex items-center gap-2">
259
+ <h3 className="font-semibold text-lg">Green Invoice</h3>
260
+ <CheckCircle2 className="h-5 w-5 text-green-600" />
261
+ </div>
262
+ <p className="text-sm text-muted-foreground">
263
+ Israeli invoicing system integration
264
+ </p>
265
+ </div>
266
+ </div>
267
+ <div className="flex items-center gap-2">
268
+ <UpdateIntegrationConfigDialog
269
+ id={business?.clientInfo?.greenInvoiceId}
270
+ provider="Green Invoice"
271
+ updateClient={async newId => updateIdByAttribute(newId, 'greenInvoiceId')}
272
+ />
273
+ <AccordionTrigger className="hover:no-underline p-2" />
274
+ </div>
275
+ </div>
276
+ </div>
277
+
278
+ <AccordionContent>
279
+ {greenInvoiceClient && (
280
+ <div className="p-6 space-y-6">
281
+ {/* Business Information */}
282
+ <div className="space-y-4">
283
+ <h4 className="font-semibold text-sm text-muted-foreground uppercase tracking-wide">
284
+ Business Information
285
+ </h4>
286
+ <div className="grid gap-4 md:grid-cols-2">
287
+ <div className="space-y-1">
288
+ <span className="text-sm font-medium text-muted-foreground">
289
+ Integration ID
290
+ </span>
291
+ <p className="text-sm font-mono">
292
+ {business?.clientInfo?.greenInvoiceId}
293
+ </p>
294
+ </div>
295
+ <div className="space-y-1">
296
+ <span className="text-sm font-medium text-muted-foreground">
297
+ Business Name
298
+ </span>
299
+ <p className="text-sm">{greenInvoiceClient.name}</p>
300
+ </div>
301
+ <div className="space-y-1">
302
+ <span className="text-sm font-medium text-muted-foreground">Country</span>
303
+ <p className="text-sm">{greenInvoiceClient.country}</p>
304
+ </div>
305
+ <div className="space-y-1">
306
+ <span className="text-sm font-medium text-muted-foreground">Tax ID</span>
307
+ <p className="text-sm font-mono">{greenInvoiceClient.taxId}</p>
308
+ </div>
309
+ </div>
310
+ </div>
311
+
312
+ {/* Contact Information */}
313
+ <div className="space-y-4">
314
+ <h4 className="font-semibold text-sm text-muted-foreground uppercase tracking-wide">
315
+ Contact Information
316
+ </h4>
317
+ <div className="grid gap-4 md:grid-cols-2">
318
+ <div className="space-y-1">
319
+ <span className="text-sm font-medium text-muted-foreground flex items-center gap-2">
320
+ <Mail className="h-3.5 w-3.5" />
321
+ Email Addresses
322
+ </span>
323
+ <div className="flex flex-wrap gap-2">
324
+ {greenInvoiceClient.emails?.map((email, index) => (
325
+ <span key={index} className="text-sm px-2 py-1 bg-muted rounded">
326
+ {email}
327
+ </span>
328
+ ))}
329
+ </div>
330
+ </div>
331
+ <div className="space-y-1">
332
+ <span className="text-sm font-medium text-muted-foreground flex items-center gap-2">
333
+ <Phone className="h-3.5 w-3.5" />
334
+ Phone
335
+ </span>
336
+ <p className="text-sm font-mono">{greenInvoiceClient.phone}</p>
337
+ </div>
338
+ <div className="space-y-1">
339
+ <span className="text-sm font-medium text-muted-foreground">Mobile</span>
340
+ <p className="text-sm font-mono">{greenInvoiceClient.mobile}</p>
341
+ </div>
342
+ <div className="space-y-1">
343
+ <span className="text-sm font-medium text-muted-foreground">Fax</span>
344
+ <p className="text-sm font-mono">{greenInvoiceClient.fax}</p>
345
+ </div>
346
+ </div>
347
+ </div>
348
+
349
+ {/* Address Information */}
350
+ <div className="space-y-4">
351
+ <h4 className="font-semibold text-sm text-muted-foreground uppercase tracking-wide flex items-center gap-2">
352
+ <MapPin className="h-3.5 w-3.5" />
353
+ Address
354
+ </h4>
355
+ <div className="grid gap-4 md:grid-cols-3">
356
+ <div className="space-y-1 md:col-span-2">
357
+ <span className="text-sm font-medium text-muted-foreground">
358
+ Street Address
359
+ </span>
360
+ <p className="text-sm">{greenInvoiceClient.address}</p>
361
+ </div>
362
+ <div className="space-y-1">
363
+ <span className="text-sm font-medium text-muted-foreground">City</span>
364
+ <p className="text-sm">{greenInvoiceClient.city}</p>
365
+ </div>
366
+ <div className="space-y-1">
367
+ <span className="text-sm font-medium text-muted-foreground">
368
+ Zip Code
369
+ </span>
370
+ <p className="text-sm font-mono">{greenInvoiceClient.zip}</p>
371
+ </div>
372
+ </div>
373
+ </div>
374
+ </div>
375
+ )}
376
+ </AccordionContent>
377
+ </AccordionItem>
378
+ )}
379
+
380
+ {generalIntegrations.map(integration => (
381
+ <AccordionItem
382
+ key={integration.id}
383
+ value={integration.id}
384
+ className="rounded-lg border"
385
+ >
386
+ <div className="px-4 py-3">
387
+ <div className="flex items-center justify-between">
388
+ <div className="flex items-center gap-3 flex-1">
389
+ <div className="space-y-1">
390
+ <div className="flex items-center gap-2">
391
+ <h4 className="font-semibold">{integration.name}</h4>
392
+ {integration.status === 'connected' ? (
393
+ <CheckCircle2 className="h-4 w-4 text-green-600" />
394
+ ) : (
395
+ <XCircle className="h-4 w-4 text-muted-foreground" />
396
+ )}
397
+ </div>
398
+ <p className="text-sm text-muted-foreground">{integration.description}</p>
399
+ </div>
400
+ </div>
401
+ <div className="flex items-center gap-2">
402
+ <Button variant="ghost" size="sm" onClick={e => e.stopPropagation()}>
403
+ <Settings className="h-4 w-4" />
404
+ </Button>
405
+ <AccordionTrigger className="hover:no-underline p-2" />
406
+ </div>
407
+ </div>
408
+ </div>
409
+
410
+ <AccordionContent>
411
+ <div className="px-4 pb-4 pt-2 border-t">
412
+ <div className="text-sm">
413
+ <span className="text-muted-foreground">Last sync: </span>
414
+ <span className="font-medium">{integration.lastSync}</span>
415
+ </div>
416
+ </div>
417
+ </AccordionContent>
418
+ </AccordionItem>
419
+ ))}
420
+ </Accordion>
421
+ </CardContent>
422
+ </Card>
423
+ );
424
+ }
425
+
426
+ interface updateIntegrationProps {
427
+ id?: string;
428
+ provider: string;
429
+ updateClient: (newId: string) => Promise<void>;
430
+ }
431
+
432
+ function UpdateIntegrationConfigDialog({ id, provider, updateClient }: updateIntegrationProps) {
433
+ const [isConfigOpen, setIsConfigOpen] = useState(false);
434
+ const [updatedId, setUpdatedId] = useState(id);
435
+
436
+ const handleSaveConfig = useCallback(async () => {
437
+ console.log(`Saving ${provider} ID: ${updatedId}`);
438
+
439
+ await updateClient(updatedId!);
440
+
441
+ setIsConfigOpen(false);
442
+ }, [provider, updatedId, updateClient]);
443
+
444
+ return (
445
+ <Dialog open={isConfigOpen} onOpenChange={setIsConfigOpen}>
446
+ <DialogTrigger asChild>
447
+ <Button variant="outline" size="sm" onClick={e => e.stopPropagation()}>
448
+ <Settings className="h-4 w-4 mr-2" />
449
+ Configure
450
+ </Button>
451
+ </DialogTrigger>
452
+ <DialogContent onClick={e => e.stopPropagation()}>
453
+ <DialogHeader>
454
+ <DialogTitle>Configure {provider}</DialogTitle>
455
+ <DialogDescription>Update your {provider} integration settings</DialogDescription>
456
+ </DialogHeader>
457
+ <div className="space-y-4 py-4">
458
+ <div className="space-y-2">
459
+ <Label htmlFor="id">{provider} ID</Label>
460
+ <Input
461
+ id="id"
462
+ value={updatedId}
463
+ onChange={e => setUpdatedId(e.target.value)}
464
+ placeholder={`Enter ${provider} ID`}
465
+ />
466
+ </div>
467
+ </div>
468
+ <DialogFooter>
469
+ <Button variant="outline" onClick={() => setIsConfigOpen(false)}>
470
+ Cancel
471
+ </Button>
472
+ <Button onClick={handleSaveConfig} disabled={!updatedId}>
473
+ Save Changes
474
+ </Button>
475
+ </DialogFooter>
476
+ </DialogContent>
477
+ </Dialog>
478
+ );
479
+ }
@@ -0,0 +1,26 @@
1
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card.js';
2
+ import { BusinessExtendedInfo } from '../business-transactions/business-extended-info';
3
+
4
+ interface Props {
5
+ businessId: string;
6
+ }
7
+
8
+ export function TransactionsSection({ businessId }: Props) {
9
+ return (
10
+ <Card>
11
+ <CardHeader className="flex w-full justify-between items-center">
12
+ <div className="flex items-center justify-between">
13
+ <div>
14
+ <CardTitle>Transactions</CardTitle>
15
+ <CardDescription>Complete transaction history for this business</CardDescription>
16
+ </div>
17
+ </div>
18
+ </CardHeader>
19
+ <CardContent>
20
+ <div className="rounded-md border">
21
+ <BusinessExtendedInfo businessID={businessId} filter={{}} />
22
+ </div>
23
+ </CardContent>
24
+ </Card>
25
+ );
26
+ }
@@ -9,11 +9,7 @@ import {
9
9
  type BusinessTransactionsFilter,
10
10
  type BusinessTransactionsInfoQuery,
11
11
  } from '../../gql/graphql.js';
12
- import {
13
- currencyCodeToSymbol,
14
- FIAT_CURRENCIES,
15
- formatStringifyAmount,
16
- } from '../../helpers/index.js';
12
+ import { FIAT_CURRENCIES, formatAmountWithCurrency } from '../../helpers/index.js';
17
13
  import { AccounterLoader } from '../common/index.js';
18
14
  import { getChargeHref } from '../screens/charges/charge.js';
19
15
  import { Button } from '../ui/button.js';
@@ -257,11 +253,11 @@ export function BusinessExtendedInfo({ businessID, filter }: Props): ReactElemen
257
253
  </td>
258
254
  <td>
259
255
  {row.ilsBalance === 0 ? (
260
- `${currencyCodeToSymbol(Currency.Ils)} ${formatStringifyAmount(row.ilsBalance)}`
256
+ formatAmountWithCurrency(row.ilsBalance, Currency.Ils)
261
257
  ) : (
262
- <Mark
263
- color={row.ilsBalance > 0 ? 'green' : 'red'}
264
- >{`${currencyCodeToSymbol(Currency.Ils)} ${formatStringifyAmount(row.ilsBalance)}`}</Mark>
258
+ <Mark color={row.ilsBalance > 0 ? 'green' : 'red'}>
259
+ {formatAmountWithCurrency(row.ilsBalance, Currency.Ils)}
260
+ </Mark>
265
261
  )}
266
262
  </td>
267
263
  {isEur && <CurrencyCells data={row} currency={Currency.Eur} />}
@@ -320,11 +316,11 @@ export function CurrencyCells({
320
316
  <td>
321
317
  {(data[key] ?? 0) !== 0 &&
322
318
  (data[key] === 0 ? (
323
- `${currencyCodeToSymbol(currency)} ${formatStringifyAmount(data[key])}`
319
+ formatAmountWithCurrency(data[key], currency)
324
320
  ) : (
325
- <Mark
326
- color={data[key] > 0 ? 'green' : 'red'}
327
- >{`${currencyCodeToSymbol(currency)} ${formatStringifyAmount(data[key])}`}</Mark>
321
+ <Mark color={data[key] > 0 ? 'green' : 'red'}>
322
+ {formatAmountWithCurrency(data[key], currency)}
323
+ </Mark>
328
324
  ))}
329
325
  </td>
330
326
  </>
@@ -17,8 +17,8 @@ import { Dialog, DialogContent } from '../ui/dialog.js';
17
17
  interface ChargeExtendedInfoMenuProps {
18
18
  chargeId: string;
19
19
  chargeType: ChargesTableRowFieldsFragment['__typename'];
20
- setInsertDocument: () => void;
21
- setMatchDocuments: () => void;
20
+ setInsertDocument?: () => void;
21
+ setMatchDocuments?: () => void;
22
22
  onChange?: () => void;
23
23
  isIncome: boolean;
24
24
  }
@@ -78,16 +78,19 @@ export function ChargeExtendedInfoMenu({
78
78
  </ConfirmationModal>
79
79
  <Menu.Divider />
80
80
  <Menu.Label>Documents</Menu.Label>
81
- <Menu.Item
82
- icon={<ListPlus size={14} />}
83
- onClick={(event: ClickEvent): void => {
84
- event.stopPropagation();
85
- setInsertDocument();
86
- closeMenu();
87
- }}
88
- >
89
- Insert Document
90
- </Menu.Item>
81
+ {setInsertDocument && (
82
+ <Menu.Item
83
+ icon={<ListPlus size={14} />}
84
+ onClick={(event: ClickEvent): void => {
85
+ event.stopPropagation();
86
+ setInsertDocument();
87
+ closeMenu();
88
+ }}
89
+ >
90
+ Insert Document
91
+ </Menu.Item>
92
+ )}
93
+ (
91
94
  <Menu.Item
92
95
  icon={<FilePlus2 size={14} />}
93
96
  onClick={(event: ClickEvent): void => {
@@ -98,15 +101,18 @@ export function ChargeExtendedInfoMenu({
98
101
  >
99
102
  Upload Documents
100
103
  </Menu.Item>
101
- <Menu.Item
102
- icon={<Search size={14} />}
103
- onClick={(): void => {
104
- setMatchDocuments();
105
- closeMenu();
106
- }}
107
- >
108
- Match Document
109
- </Menu.Item>
104
+ )
105
+ {setMatchDocuments && (
106
+ <Menu.Item
107
+ icon={<Search size={14} />}
108
+ onClick={(): void => {
109
+ setMatchDocuments();
110
+ closeMenu();
111
+ }}
112
+ >
113
+ Match Document
114
+ </Menu.Item>
115
+ )}
110
116
  {isIncome && (
111
117
  <Menu.Item
112
118
  icon={<ListPlus size={14} />}
@@ -70,9 +70,9 @@ import { ChargeExtendedInfo } from './charge-extended-info.js';
70
70
  `;
71
71
 
72
72
  interface Props {
73
- setEditCharge: (onChange: () => void) => void;
74
- setInsertDocument: (onChange: () => void) => void;
75
- setMatchDocuments: () => void;
73
+ setEditCharge?: (onChange: () => void) => void;
74
+ setInsertDocument?: (onChange: () => void) => void;
75
+ setMatchDocuments?: () => void;
76
76
  toggleMergeCharge?: (onChange: () => void) => void;
77
77
  isSelectedForMerge: boolean;
78
78
  data: ChargesTableFieldsFragment;
@@ -198,12 +198,14 @@ export const ChargesTableRow = ({
198
198
 
199
199
  <td>
200
200
  <div className="flex flex-col gap-2">
201
- <EditMiniButton
202
- onClick={event => {
203
- event.stopPropagation();
204
- setEditCharge(onChange);
205
- }}
206
- />
201
+ {setEditCharge && (
202
+ <EditMiniButton
203
+ onClick={event => {
204
+ event.stopPropagation();
205
+ setEditCharge(onChange);
206
+ }}
207
+ />
208
+ )}
207
209
  {toggleMergeCharge && (
208
210
  <ToggleMergeSelected
209
211
  toggleMergeSelected={(): void => toggleMergeCharge(onChange)}
@@ -217,7 +219,7 @@ export const ChargesTableRow = ({
217
219
  <ChargeExtendedInfoMenu
218
220
  chargeId={charge.id}
219
221
  chargeType={charge.__typename}
220
- setInsertDocument={() => setInsertDocument(onChange)}
222
+ setInsertDocument={setInsertDocument ? () => setInsertDocument(onChange) : undefined}
221
223
  setMatchDocuments={setMatchDocuments}
222
224
  onChange={onChange}
223
225
  isIncome={isIncomeCharge}