@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.
- package/CHANGELOG.md +67 -1
- package/dist/assets/Checkbox-CxedbJAl.js +6 -0
- package/dist/assets/Progress-D5SuJtCd.js +1 -0
- package/dist/assets/Typography-BQFz-z7L.js +1 -0
- package/dist/assets/accordion-COWOBKuq.js +1 -0
- package/dist/assets/accountant-approvals-Bd2y8us_.js +1 -0
- package/dist/assets/all-charges-SWBnaZu7.js +1 -0
- package/dist/assets/arrow-up-down-dZmrBLse.js +6 -0
- package/dist/assets/business--GVVfDEa.js +37 -0
- package/dist/assets/business-transactions-single-BsbkUf_H.js +1 -0
- package/dist/assets/business-trip-ByXPVXdG.js +1 -0
- package/dist/assets/charges-filters-D43UbXob.js +1 -0
- package/dist/assets/charges-ledger-validation-D0uMH_JE.js +1 -0
- package/dist/assets/chart-ClU1KbWe.js +74 -0
- package/dist/assets/data-table-pagination-D9Y0_Tn8.js +11 -0
- package/dist/assets/editable-business-trip-DhqOQBPa.js +16 -0
- package/dist/assets/graphql-document-dedupe-fragments-ByT8-wlV.js +1 -0
- package/dist/assets/index-1U6rQgQe.js +6 -0
- package/dist/assets/index-3-AKn8tg.js +1 -0
- package/dist/assets/index-91A2PLZ6.js +137 -0
- package/dist/assets/index-BBHuCWRn.js +1 -0
- package/dist/assets/index-BPNuFFtx.js +1 -0
- package/dist/assets/index-BXqHnRVY.js +1 -0
- package/dist/assets/index-BciOH8FS.js +1 -0
- package/dist/assets/index-BjHuUHDO.js +1 -0
- package/dist/assets/index-BxKmoNQd.js +1 -0
- package/dist/assets/index-C3bqiFIv.js +2 -0
- package/dist/assets/index-C5MeepK_.js +11 -0
- package/dist/assets/index-CAwm68Mg.js +1 -0
- package/dist/assets/index-CJ8OGXxv.js +1 -0
- package/dist/assets/index-CJyY-qF6.js +1 -0
- package/dist/assets/index-CMYnx46_.js +6 -0
- package/dist/assets/index-CNrwxUZ7.js +1 -0
- package/dist/assets/index-CvV5z5r9.js +876 -0
- package/dist/assets/index-D08H2GXq.js +17 -0
- package/dist/assets/index-GFsPY1p4.js +2 -0
- package/dist/assets/index-KwNwThNu.js +1 -0
- package/dist/assets/index-YA8IBFyB.js +1 -0
- package/dist/assets/index-ZpyI3qxW.js +24 -0
- package/dist/assets/index-gdTXrWXt.css +1 -0
- package/dist/assets/index-ytnIEraq.js +9 -0
- package/dist/assets/{index.es-DHwHzag1.js → index.es-CYeQ4a5s.js} +5 -5
- package/dist/assets/issue-document-CdikNnO2.js +1 -0
- package/dist/assets/login-page-effgZS3V.js +1 -0
- package/dist/assets/missing-info-charges-CnPFTzoZ.js +1 -0
- package/dist/assets/page-not-found-D8YlgDOm.js +1 -0
- package/dist/assets/pencil-mxW0-tGM.js +6 -0
- package/dist/assets/report-commentary-row-DCozKgVE.js +1 -0
- package/dist/assets/save-CHlytUqu.js +11 -0
- package/dist/assets/sequential-CAnleQny.js +1 -0
- package/dist/assets/similar-charges-by-business-modal-Dzbspk_r.js +1 -0
- package/dist/assets/sub-Cp_PhKiD.js +1 -0
- package/dist/assets/subMonths-DCj_iXAn.js +1 -0
- package/dist/index.html +2 -2
- package/package.json +6 -5
- package/src/app.tsx +35 -25
- package/src/components/business/business-header.tsx +68 -0
- package/src/components/business/charges-section.tsx +82 -0
- package/src/components/business/charts-section.tsx +115 -0
- package/src/components/business/configurations-section.tsx +885 -0
- package/src/components/business/contact-info-section.tsx +536 -0
- package/src/components/business/contracts-section.tsx +196 -0
- package/src/components/business/documents-section.tsx +26 -0
- package/src/components/business/index.tsx +171 -0
- package/src/components/business/integrations-section.tsx +477 -0
- package/src/components/business/transactions-section.tsx +26 -0
- package/src/components/business-transactions/business-extended-info.tsx +22 -28
- package/src/components/business-transactions/business-transactions-single.tsx +3 -3
- package/src/components/business-transactions/index.tsx +13 -2
- package/src/components/business-trips/business-trip.tsx +3 -3
- package/src/components/charges/cells/business-trip.tsx +6 -8
- package/src/components/charges/cells/counterparty.tsx +7 -5
- package/src/components/charges/charge-extended-info-menu.tsx +27 -21
- package/src/components/charges/charges-row.tsx +12 -10
- package/src/components/charges/charges-table.tsx +15 -9
- package/src/components/clients/contracts/modify-contract-dialog.tsx +464 -0
- package/src/components/clients/modify-client-dialog.tsx +276 -0
- package/src/components/common/accounter-table.tsx +6 -5
- package/src/components/common/business-trip-report/parts/core-expense-row.tsx +11 -9
- package/src/components/common/business-trip-report/parts/uncategorized-transactions.tsx +11 -13
- package/src/components/common/buttons/index.ts +0 -2
- package/src/components/common/buttons/logout-button.tsx +7 -6
- package/src/components/common/documents/issue-document/index.tsx +3 -3
- package/src/components/common/documents/issue-document/{recent-client-docs.tsx → recent-business-docs.tsx} +19 -13
- package/src/components/common/documents-to-charge-matcher/selection-handler/index.tsx +4 -2
- package/src/components/common/documents-to-charge-matcher/selection-handler/wide-filtered-selection.tsx +5 -7
- package/src/components/common/forms/business-card.tsx +1 -0
- package/src/components/common/forms/edit-document.tsx +23 -10
- package/src/components/common/forms/modify-business-fields.tsx +2 -19
- package/src/components/common/inputs/combo-box.tsx +1 -1
- package/src/components/common/new-documents-list.tsx +10 -8
- package/src/components/documents-table/cells/creditor.tsx +11 -4
- package/src/components/documents-table/cells/debtor.tsx +11 -4
- package/src/components/error-boundary.tsx +189 -0
- package/src/components/layout/breadcrumbs.tsx +77 -0
- package/src/components/layout/dashboard-layout.tsx +4 -0
- package/src/components/layout/document-title.tsx +31 -0
- package/src/components/layout/navigation-progress.tsx +52 -0
- package/src/components/layout/page-skeleton.tsx +49 -0
- package/src/components/layout/sidelinks.tsx +28 -27
- package/src/components/ledger-table/counterparty-cell.tsx +19 -13
- package/src/components/login-page.tsx +2 -1
- package/src/components/reports/corporate-tax-ruling-compliance-report/index.tsx +3 -3
- package/src/components/reports/profit-and-loss-report/index.tsx +3 -3
- package/src/components/reports/tax-report/index.tsx +3 -3
- package/src/components/reports/trial-balance-report/trial-balance-report-group.tsx +4 -6
- package/src/components/reports/trial-balance-report/trial-balance-report-sort-code.tsx +8 -11
- package/src/components/screens/businesses/business.tsx +56 -0
- package/src/components/screens/charges/charge.tsx +22 -9
- package/src/components/screens/documents/issue-documents/edit-issue-document-modal.tsx +4 -4
- package/src/components/transactions-table/cells/counterparty.tsx +9 -2
- package/src/components/transactions-table/cells-legacy/counterparty.tsx +9 -2
- package/src/components/ui/progress.tsx +25 -0
- package/src/components/ui/skeleton.tsx +12 -0
- package/src/gql/gql.ts +93 -9
- package/src/gql/graphql.ts +289 -9
- package/src/helpers/contracts.ts +22 -0
- package/src/helpers/currency.ts +5 -0
- package/src/helpers/index.ts +2 -0
- package/src/helpers/pcn874.ts +17 -0
- package/src/hooks/use-add-sort-code.ts +1 -1
- package/src/hooks/use-add-tag.ts +1 -1
- package/src/hooks/use-create-contract.ts +62 -0
- package/src/hooks/use-delete-contract.ts +64 -0
- package/src/hooks/use-delete-tag.ts +1 -1
- package/src/hooks/use-get-all-contracts.ts +0 -1
- package/src/hooks/use-insert-client.ts +80 -0
- package/src/hooks/use-merge-businesses.ts +1 -1
- package/src/hooks/use-merge-charges.ts +1 -1
- package/src/hooks/use-update-client.ts +75 -0
- package/src/hooks/use-update-contract.ts +69 -0
- package/src/index.tsx +4 -22
- package/src/providers/auth-guard.tsx +14 -23
- package/src/providers/index.tsx +7 -2
- package/src/providers/urql-client.ts +86 -0
- package/src/providers/urql.tsx +7 -12
- package/src/providers/user-provider.tsx +3 -2
- package/src/router/config.tsx +534 -0
- package/src/router/layouts/dashboard-layout.tsx +20 -0
- package/src/router/layouts/root-layout.tsx +69 -0
- package/src/router/loaders/auth-loader.ts +32 -0
- package/src/router/loaders/business-loader.ts +25 -0
- package/src/router/loaders/charge-loader.ts +25 -0
- package/src/router/loaders/index.ts +17 -0
- package/src/router/routes.ts +88 -0
- package/src/router/types.ts +62 -0
- package/dist/assets/index-0eCf1BcD.css +0 -1
- package/dist/assets/index-DHTbHvtz.js +0 -1188
- package/src/components/common/buttons/button-with-label.tsx +0 -41
- package/src/components/common/buttons/button.tsx +0 -44
|
@@ -0,0 +1,477 @@
|
|
|
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
|
+
await updateClient(updatedId!);
|
|
438
|
+
|
|
439
|
+
setIsConfigOpen(false);
|
|
440
|
+
}, [updatedId, updateClient]);
|
|
441
|
+
|
|
442
|
+
return (
|
|
443
|
+
<Dialog open={isConfigOpen} onOpenChange={setIsConfigOpen}>
|
|
444
|
+
<DialogTrigger asChild>
|
|
445
|
+
<Button variant="outline" size="sm" onClick={e => e.stopPropagation()}>
|
|
446
|
+
<Settings className="h-4 w-4 mr-2" />
|
|
447
|
+
Configure
|
|
448
|
+
</Button>
|
|
449
|
+
</DialogTrigger>
|
|
450
|
+
<DialogContent onClick={e => e.stopPropagation()}>
|
|
451
|
+
<DialogHeader>
|
|
452
|
+
<DialogTitle>Configure {provider}</DialogTitle>
|
|
453
|
+
<DialogDescription>Update your {provider} integration settings</DialogDescription>
|
|
454
|
+
</DialogHeader>
|
|
455
|
+
<div className="space-y-4 py-4">
|
|
456
|
+
<div className="space-y-2">
|
|
457
|
+
<Label htmlFor="id">{provider} ID</Label>
|
|
458
|
+
<Input
|
|
459
|
+
id="id"
|
|
460
|
+
value={updatedId}
|
|
461
|
+
onChange={e => setUpdatedId(e.target.value)}
|
|
462
|
+
placeholder={`Enter ${provider} ID`}
|
|
463
|
+
/>
|
|
464
|
+
</div>
|
|
465
|
+
</div>
|
|
466
|
+
<DialogFooter>
|
|
467
|
+
<Button variant="outline" onClick={() => setIsConfigOpen(false)}>
|
|
468
|
+
Cancel
|
|
469
|
+
</Button>
|
|
470
|
+
<Button onClick={handleSaveConfig} disabled={!updatedId}>
|
|
471
|
+
Save Changes
|
|
472
|
+
</Button>
|
|
473
|
+
</DialogFooter>
|
|
474
|
+
</DialogContent>
|
|
475
|
+
</Dialog>
|
|
476
|
+
);
|
|
477
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import { useState, type ReactElement } from 'react';
|
|
2
2
|
import { format } from 'date-fns';
|
|
3
3
|
import { ChevronsLeftRightEllipsis, ChevronsRightLeft } from 'lucide-react';
|
|
4
|
+
import { Link } from 'react-router-dom';
|
|
4
5
|
import { useQuery } from 'urql';
|
|
5
|
-
import { Mark,
|
|
6
|
+
import { Mark, Table, Tooltip } from '@mantine/core';
|
|
6
7
|
import {
|
|
7
8
|
BusinessTransactionsInfoDocument,
|
|
8
9
|
Currency,
|
|
9
10
|
type BusinessTransactionsFilter,
|
|
10
11
|
type BusinessTransactionsInfoQuery,
|
|
11
12
|
} from '../../gql/graphql.js';
|
|
12
|
-
import {
|
|
13
|
-
currencyCodeToSymbol,
|
|
14
|
-
FIAT_CURRENCIES,
|
|
15
|
-
formatStringifyAmount,
|
|
16
|
-
} from '../../helpers/index.js';
|
|
13
|
+
import { FIAT_CURRENCIES, formatAmountWithCurrency } from '../../helpers/index.js';
|
|
17
14
|
import { AccounterLoader } from '../common/index.js';
|
|
18
15
|
import { getChargeHref } from '../screens/charges/charge.js';
|
|
19
16
|
import { Button } from '../ui/button.js';
|
|
20
17
|
import { DownloadCSV } from './download-csv.js';
|
|
18
|
+
import { getBusinessTransactionsHref } from './index.js';
|
|
21
19
|
|
|
22
20
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
|
|
23
21
|
/* GraphQL */ `
|
|
@@ -237,17 +235,15 @@ export function BusinessExtendedInfo({ businessID, filter }: Props): ReactElemen
|
|
|
237
235
|
}}
|
|
238
236
|
>
|
|
239
237
|
<td>
|
|
240
|
-
<
|
|
241
|
-
|
|
238
|
+
<Link
|
|
239
|
+
to={getBusinessTransactionsHref({ businessIDs: [row.business.id] })}
|
|
242
240
|
target="_blank"
|
|
243
241
|
rel="noreferrer"
|
|
244
242
|
onClick={event => event.stopPropagation()}
|
|
243
|
+
className="inline-flex items-center font-semibold"
|
|
245
244
|
>
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
className="[&>*>.mantine-NavLink-label]:font-semibold"
|
|
249
|
-
/>
|
|
250
|
-
</a>
|
|
245
|
+
{row.business.name}
|
|
246
|
+
</Link>
|
|
251
247
|
</td>
|
|
252
248
|
<td>{row.invoiceDate ? format(new Date(row.invoiceDate), 'dd/MM/yy') : null}</td>
|
|
253
249
|
<td style={{ whiteSpace: 'nowrap' }}>
|
|
@@ -257,11 +253,11 @@ export function BusinessExtendedInfo({ businessID, filter }: Props): ReactElemen
|
|
|
257
253
|
</td>
|
|
258
254
|
<td>
|
|
259
255
|
{row.ilsBalance === 0 ? (
|
|
260
|
-
|
|
256
|
+
formatAmountWithCurrency(row.ilsBalance, Currency.Ils)
|
|
261
257
|
) : (
|
|
262
|
-
<Mark
|
|
263
|
-
|
|
264
|
-
|
|
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} />}
|
|
@@ -277,17 +273,15 @@ export function BusinessExtendedInfo({ businessID, filter }: Props): ReactElemen
|
|
|
277
273
|
<td>{row.details}</td>
|
|
278
274
|
<td>
|
|
279
275
|
{row.counterAccount && (
|
|
280
|
-
<
|
|
281
|
-
|
|
276
|
+
<Link
|
|
277
|
+
to={getBusinessTransactionsHref({ businessIDs: [row.counterAccount.id] })}
|
|
282
278
|
target="_blank"
|
|
283
279
|
rel="noreferrer"
|
|
284
280
|
onClick={event => event.stopPropagation()}
|
|
281
|
+
className="inline-flex items-center font-semibold"
|
|
285
282
|
>
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
className="[&>*>.mantine-NavLink-label]:font-semibold"
|
|
289
|
-
/>
|
|
290
|
-
</a>
|
|
283
|
+
{row.counterAccount.name}
|
|
284
|
+
</Link>
|
|
291
285
|
)}
|
|
292
286
|
</td>
|
|
293
287
|
<td />
|
|
@@ -320,11 +314,11 @@ export function CurrencyCells({
|
|
|
320
314
|
<td>
|
|
321
315
|
{(data[key] ?? 0) !== 0 &&
|
|
322
316
|
(data[key] === 0 ? (
|
|
323
|
-
|
|
317
|
+
formatAmountWithCurrency(data[key], currency)
|
|
324
318
|
) : (
|
|
325
|
-
<Mark
|
|
326
|
-
|
|
327
|
-
|
|
319
|
+
<Mark color={data[key] > 0 ? 'green' : 'red'}>
|
|
320
|
+
{formatAmountWithCurrency(data[key], currency)}
|
|
321
|
+
</Mark>
|
|
328
322
|
))}
|
|
329
323
|
</td>
|
|
330
324
|
</>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useContext, useEffect, useMemo, useState, type ReactElement, type ReactNode } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { useParams } from 'react-router-dom';
|
|
3
3
|
import { useQuery } from 'urql';
|
|
4
4
|
import { Mark } from '@mantine/core';
|
|
5
5
|
import {
|
|
@@ -19,10 +19,10 @@ type Props = {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
export const BusinessTransactionsSingle = ({ businessId }: Props): ReactElement => {
|
|
22
|
-
const
|
|
22
|
+
const { businessId: businessIdFromUrl } = useParams<{ businessId: string }>();
|
|
23
23
|
const { get } = useUrlQuery();
|
|
24
24
|
const { setFiltersContext } = useContext(FiltersContext);
|
|
25
|
-
const id = businessId ||
|
|
25
|
+
const id = businessId || businessIdFromUrl;
|
|
26
26
|
const [filter, setFilter] = useState<BusinessTransactionsFilter>(
|
|
27
27
|
get('transactionsFilters')
|
|
28
28
|
? {
|
|
@@ -72,14 +72,25 @@ import {
|
|
|
72
72
|
export function getBusinessTransactionsHref(filter?: BusinessTransactionsFilter | null): string {
|
|
73
73
|
const params = new URLSearchParams();
|
|
74
74
|
|
|
75
|
-
|
|
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);
|
|
76
84
|
if (transactionsFilters) {
|
|
77
85
|
// Add it as a single encoded parameter
|
|
78
86
|
params.append('transactionsFilters', transactionsFilters);
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
const queryParams = params.size > 0 ? `?${params}` : '';
|
|
82
|
-
|
|
90
|
+
if (businessId) {
|
|
91
|
+
return `/businesses/${businessId}/transactions${queryParams}`;
|
|
92
|
+
}
|
|
93
|
+
return `/businesses/transactions${queryParams}`;
|
|
83
94
|
}
|
|
84
95
|
|
|
85
96
|
type BusinessTransactionsSum = Extract<
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useContext, useEffect, type ReactElement } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { useParams } from 'react-router-dom';
|
|
3
3
|
import { useQuery } from 'urql';
|
|
4
4
|
import { Container } from '@mantine/core';
|
|
5
5
|
import { BusinessTripScreenDocument } from '../../gql/graphql.js';
|
|
@@ -26,9 +26,9 @@ type Props = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
export const BusinessTrip = ({ businessTripId }: Props): ReactElement => {
|
|
29
|
-
const
|
|
29
|
+
const { businessTripId: businessTripIdFromUrl } = useParams<{ businessTripId: string }>();
|
|
30
30
|
const { setFiltersContext } = useContext(FiltersContext);
|
|
31
|
-
const id = businessTripId ??
|
|
31
|
+
const id = businessTripId ?? businessTripIdFromUrl ?? '';
|
|
32
32
|
const [{ data, fetching }] = useQuery({
|
|
33
33
|
query: BusinessTripScreenDocument,
|
|
34
34
|
variables: {
|