@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,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
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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 {
|
|
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
|
-
<
|
|
240
|
+
<Link
|
|
240
241
|
key={id}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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 {
|
|
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
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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,19 +1,20 @@
|
|
|
1
|
-
import {
|
|
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
|
|
11
|
-
authService.logout();
|
|
12
|
-
navigate(
|
|
13
|
-
}
|
|
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={
|
|
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 {
|
|
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
|
-
<
|
|
239
|
-
|
|
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
|
-
|
|
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.
|
|
12
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../../ui/card.
|
|
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.
|
|
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
|
|
25
|
-
|
|
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
|
|
46
|
-
|
|
45
|
+
interface RecentBusinessDocsProps {
|
|
46
|
+
businessId: string;
|
|
47
47
|
linkedDocumentIds: string[];
|
|
48
|
+
limit?: number;
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
export function
|
|
51
|
+
export function RecentBusinessDocs({
|
|
52
|
+
businessId,
|
|
53
|
+
linkedDocumentIds,
|
|
54
|
+
limit,
|
|
55
|
+
}: RecentBusinessDocsProps) {
|
|
51
56
|
const [{ data, fetching }] = useQuery({
|
|
52
|
-
query:
|
|
57
|
+
query: RecentBusinessIssuedDocumentsDocument,
|
|
53
58
|
variables: {
|
|
54
|
-
|
|
59
|
+
businessId,
|
|
60
|
+
limit,
|
|
55
61
|
},
|
|
56
62
|
});
|
|
57
63
|
|
|
58
64
|
const rows = useMemo(
|
|
59
65
|
(): RowType[] =>
|
|
60
|
-
data?.
|
|
66
|
+
data?.recentDocumentsByBusiness?.map(
|
|
61
67
|
rawDocument => getFragmentData(TableDocumentsRowFieldsFragmentDoc, rawDocument) as RowType,
|
|
62
68
|
) ?? [],
|
|
63
|
-
[data?.
|
|
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
|
|
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,
|
|
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
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
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) },
|
|
@@ -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 {
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
<
|
|
128
|
+
<Button
|
|
118
129
|
type="submit"
|
|
119
|
-
|
|
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
|
-
</
|
|
136
|
+
</Button>
|
|
124
137
|
</div>
|
|
125
138
|
</form>
|
|
126
139
|
</Form>
|