@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.
- package/CHANGELOG.md +24 -1
- package/dist/assets/index-CJB5mMpd.js +1224 -0
- package/dist/assets/index-CzRRUK04.css +1 -0
- package/dist/assets/{index.es-CNXdeXse.js → index.es-NOVMdy_X.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +3 -2
- package/src/app.tsx +8 -4
- package/src/components/business/business-header.tsx +65 -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 +835 -0
- package/src/components/business/contact-info-section.tsx +544 -0
- package/src/components/business/contracts-section.tsx +195 -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 +479 -0
- package/src/components/business/transactions-section.tsx +26 -0
- package/src/components/business-transactions/business-extended-info.tsx +9 -13
- 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 +466 -0
- package/src/components/clients/modify-client-dialog.tsx +276 -0
- 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/forms/business-card.tsx +1 -0
- package/src/components/common/forms/modify-business-fields.tsx +15 -33
- package/src/components/common/inputs/combo-box.tsx +1 -1
- package/src/components/common/modals/insert-business.tsx +4 -2
- 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 +50 -0
- package/src/components/screens/documents/issue-documents/edit-issue-document-modal.tsx +4 -4
- package/src/components/ui/progress.tsx +25 -0
- package/src/components/ui/skeleton.tsx +12 -0
- package/src/gql/gql.ts +96 -12
- package/src/gql/graphql.ts +290 -11
- 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-create-contract.ts +62 -0
- package/src/hooks/use-delete-contract.ts +64 -0
- package/src/hooks/use-get-all-contracts.ts +0 -1
- package/src/hooks/use-insert-client.ts +80 -0
- package/src/hooks/use-update-client.ts +75 -0
- package/src/hooks/use-update-contract.ts +69 -0
- package/src/providers/user-provider.tsx +2 -0
- package/dist/assets/index-0eCf1BcD.css +0 -1
- package/dist/assets/index-Dh8zU8Ik.js +0 -1188
|
@@ -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
|
+
console.log('[v0] Updating client:', editingClient.id, values);
|
|
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
|
+
console.log('[v0] Creating new client:', values);
|
|
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
|
+
}
|
|
@@ -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,12 +1,12 @@
|
|
|
1
1
|
import { useEffect, useState, type ReactElement } from 'react';
|
|
2
2
|
import type { UseFormReturn } from 'react-hook-form';
|
|
3
|
+
import { useAllCountries } from '@/hooks/use-get-countries.js';
|
|
3
4
|
import {
|
|
4
5
|
EmailAttachmentType,
|
|
5
6
|
type InsertNewBusinessInput,
|
|
6
|
-
type Pcn874RecordType,
|
|
7
7
|
type UpdateBusinessInput,
|
|
8
8
|
} from '../../../gql/graphql.js';
|
|
9
|
-
import { dirtyFieldMarker } from '../../../helpers/index.js';
|
|
9
|
+
import { dirtyFieldMarker, pcn874RecordEnum } from '../../../helpers/index.js';
|
|
10
10
|
import { useGetSortCodes } from '../../../hooks/use-get-sort-codes.js';
|
|
11
11
|
import { useGetTaxCategories } from '../../../hooks/use-get-tax-categories.js';
|
|
12
12
|
import { FormControl, FormField, FormItem, FormLabel, FormMessage } from '../../ui/form.js';
|
|
@@ -15,22 +15,6 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '.
|
|
|
15
15
|
import { Switch } from '../../ui/switch.js';
|
|
16
16
|
import { ComboBox, MultiSelect, NumberInput, StringArrayInput, TagsInput } from '../index.js';
|
|
17
17
|
|
|
18
|
-
const pcn874RecordType: Record<Pcn874RecordType, string> = {
|
|
19
|
-
C: 'INPUT_SELF_INVOICE',
|
|
20
|
-
H: 'INPUT_SINGLE_DOC_BY_LAW',
|
|
21
|
-
I: 'SALE_PALESTINIAN_CUSTOMER',
|
|
22
|
-
K: 'INPUT_PETTY_CASH',
|
|
23
|
-
L1: 'SALE_UNIDENTIFIED_CUSTOMER',
|
|
24
|
-
L2: 'SALE_UNIDENTIFIED_ZERO_OR_EXEMPT',
|
|
25
|
-
M: 'SALE_SELF_INVOICE',
|
|
26
|
-
P: 'INPUT_PALESTINIAN_SUPPLIER',
|
|
27
|
-
R: 'INPUT_IMPORT',
|
|
28
|
-
S1: 'SALE_REGULAR',
|
|
29
|
-
S2: 'SALE_ZERO_OR_EXEMPT',
|
|
30
|
-
T: 'INPUT_REGULAR',
|
|
31
|
-
Y: 'SALE_EXPORT',
|
|
32
|
-
} as const;
|
|
33
|
-
|
|
34
18
|
type ModalProps<T extends boolean> = {
|
|
35
19
|
isInsert: T;
|
|
36
20
|
formManager: UseFormReturn<
|
|
@@ -60,9 +44,12 @@ export function ModifyBusinessFields({
|
|
|
60
44
|
sortCodes: rawSortCodes,
|
|
61
45
|
} = useGetSortCodes();
|
|
62
46
|
|
|
47
|
+
// Countries handle
|
|
48
|
+
const { countries, fetching: fetchingCountries } = useAllCountries();
|
|
49
|
+
|
|
63
50
|
useEffect(() => {
|
|
64
|
-
setFetching(tagsFetching || fetchingTaxCategories || fetchingSortCodes);
|
|
65
|
-
}, [setFetching, tagsFetching, fetchingTaxCategories, fetchingSortCodes]);
|
|
51
|
+
setFetching(tagsFetching || fetchingTaxCategories || fetchingSortCodes || fetchingCountries);
|
|
52
|
+
}, [setFetching, tagsFetching, fetchingTaxCategories, fetchingSortCodes, fetchingCountries]);
|
|
66
53
|
|
|
67
54
|
// on sort code change, update IRS code
|
|
68
55
|
const sortCode = watch('sortCode');
|
|
@@ -138,21 +125,16 @@ export function ModifyBusinessFields({
|
|
|
138
125
|
<FormLabel>Locality</FormLabel>
|
|
139
126
|
<Select required onValueChange={field.onChange} value={field.value ?? undefined}>
|
|
140
127
|
<FormControl>
|
|
141
|
-
<SelectTrigger
|
|
142
|
-
|
|
128
|
+
<SelectTrigger
|
|
129
|
+
className={(isInsert ? '' : dirtyFieldMarker(fieldState)) + ' w-full truncate'}
|
|
130
|
+
>
|
|
131
|
+
<SelectValue placeholder="Select Country" />
|
|
143
132
|
</SelectTrigger>
|
|
144
133
|
</FormControl>
|
|
145
134
|
<SelectContent onClick={event => event.stopPropagation()}>
|
|
146
|
-
{
|
|
147
|
-
{ value
|
|
148
|
-
|
|
149
|
-
].map(({ value, label }) => (
|
|
150
|
-
<SelectItem
|
|
151
|
-
key={value}
|
|
152
|
-
value={value}
|
|
153
|
-
className={isInsert ? '' : dirtyFieldMarker(fieldState)}
|
|
154
|
-
>
|
|
155
|
-
{label}
|
|
135
|
+
{countries.map(({ code, name }) => (
|
|
136
|
+
<SelectItem key={code} value={code}>
|
|
137
|
+
{name}
|
|
156
138
|
</SelectItem>
|
|
157
139
|
))}
|
|
158
140
|
</SelectContent>
|
|
@@ -328,7 +310,7 @@ export function ModifyBusinessFields({
|
|
|
328
310
|
</SelectTrigger>
|
|
329
311
|
</FormControl>
|
|
330
312
|
<SelectContent onClick={event => event.stopPropagation()}>
|
|
331
|
-
{Object.entries(
|
|
313
|
+
{Object.entries(pcn874RecordEnum).map(([value, label]) => (
|
|
332
314
|
<SelectItem key={value} value={value}>
|
|
333
315
|
{`${label} (${value})`}
|
|
334
316
|
</SelectItem>
|
|
@@ -50,7 +50,7 @@ export function ComboBox({
|
|
|
50
50
|
if (isDesktop) {
|
|
51
51
|
return (
|
|
52
52
|
<Popover modal open={open} onOpenChange={setOpen}>
|
|
53
|
-
<PopoverTrigger asChild>
|
|
53
|
+
<PopoverTrigger asChild className="w-fit min-w-40">
|
|
54
54
|
<Trigger
|
|
55
55
|
placeholder={placeholder}
|
|
56
56
|
selectedDatum={selectedDatum}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { useState, type ReactElement } from 'react';
|
|
1
|
+
import { useContext, useState, type ReactElement } from 'react';
|
|
2
2
|
import { useForm, type SubmitHandler } from 'react-hook-form';
|
|
3
|
+
import { UserContext } from '@/providers/user-provider.js';
|
|
3
4
|
import type { InsertNewBusinessInput } from '../../../gql/graphql.js';
|
|
4
5
|
import { useInsertBusiness } from '../../../hooks/use-insert-business.js';
|
|
5
6
|
import { Button } from '../../ui/button.js';
|
|
@@ -44,10 +45,11 @@ type CreateBusinessFormProps = {
|
|
|
44
45
|
};
|
|
45
46
|
|
|
46
47
|
function CreateBusinessForm({ description, close, onAdd }: CreateBusinessFormProps): ReactElement {
|
|
48
|
+
const { userContext } = useContext(UserContext);
|
|
47
49
|
const formManager = useForm<InsertNewBusinessInput>({
|
|
48
50
|
defaultValues: {
|
|
49
51
|
name: description,
|
|
50
|
-
country: '
|
|
52
|
+
country: userContext?.context.locality || 'ISR',
|
|
51
53
|
suggestions: { phrases: [description] },
|
|
52
54
|
},
|
|
53
55
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReactElement } from 'react';
|
|
2
2
|
import { Text } from '@mantine/core';
|
|
3
3
|
import { Currency } from '../../../gql/graphql.js';
|
|
4
|
-
import {
|
|
4
|
+
import { formatAmountWithCurrency } from '../../../helpers/index.js';
|
|
5
5
|
import { TrialBalanceReportFilters } from './trial-balance-report-filters.js';
|
|
6
6
|
import {
|
|
7
7
|
TrialBalanceReportSortCode,
|
|
@@ -44,16 +44,14 @@ export const TrialBalanceReportGroup = ({
|
|
|
44
44
|
<td colSpan={2}>Group total:</td>
|
|
45
45
|
<td colSpan={1}>{group.replaceAll('0', '*')}</td>
|
|
46
46
|
<td colSpan={1}>
|
|
47
|
-
{!!data.totalDebit &&
|
|
48
|
-
`${currencyCodeToSymbol(Currency.Ils)} ${formatStringifyAmount(data.totalDebit)}`}
|
|
47
|
+
{!!data.totalDebit && formatAmountWithCurrency(data.totalDebit, Currency.Ils)}
|
|
49
48
|
</td>
|
|
50
49
|
<td colSpan={1}>
|
|
51
|
-
{!!data.totalCredit &&
|
|
52
|
-
`${currencyCodeToSymbol(Currency.Ils)} ${formatStringifyAmount(data.totalCredit)}`}
|
|
50
|
+
{!!data.totalCredit && formatAmountWithCurrency(data.totalCredit, Currency.Ils)}
|
|
53
51
|
</td>
|
|
54
52
|
<td colSpan={1}>
|
|
55
53
|
<Text fw={700} c={data.sum > 0 ? 'green' : data.sum < 0 ? 'red' : undefined}>
|
|
56
|
-
{
|
|
54
|
+
{formatAmountWithCurrency(data.sum, Currency.Ils)}
|
|
57
55
|
</Text>
|
|
58
56
|
</td>
|
|
59
57
|
</tr>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReactElement } from 'react';
|
|
2
2
|
import { Text } from '@mantine/core';
|
|
3
3
|
import { Currency } from '../../../gql/graphql.js';
|
|
4
|
-
import {
|
|
4
|
+
import { formatAmountWithCurrency } from '../../../helpers/index.js';
|
|
5
5
|
import {
|
|
6
6
|
TrialBalanceReportBusiness,
|
|
7
7
|
type ExtendedBusiness,
|
|
@@ -55,29 +55,26 @@ export const TrialBalanceReportSortCode = ({
|
|
|
55
55
|
<td colSpan={2}>Group total:</td>
|
|
56
56
|
<td colSpan={1}>{sortCode.key}</td>
|
|
57
57
|
<td colSpan={1}>
|
|
58
|
-
{!!sortCode.totalDebit &&
|
|
59
|
-
`${currencyCodeToSymbol(Currency.Ils)} ${formatStringifyAmount(sortCode.totalDebit)}`}
|
|
58
|
+
{!!sortCode.totalDebit && formatAmountWithCurrency(sortCode.totalDebit, Currency.Ils)}
|
|
60
59
|
</td>
|
|
61
60
|
<td colSpan={1}>
|
|
62
61
|
{!!sortCode.totalCredit &&
|
|
63
|
-
|
|
62
|
+
formatAmountWithCurrency(sortCode.totalCredit, Currency.Ils)}
|
|
64
63
|
</td>
|
|
65
64
|
<td colSpan={1}>
|
|
66
|
-
<Text
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
<Text c={sortCode.sum > 0 ? 'green' : sortCode.sum < 0 ? 'red' : undefined}>
|
|
66
|
+
{formatAmountWithCurrency(sortCode.sum, Currency.Ils)}
|
|
67
|
+
</Text>
|
|
69
68
|
{!!sortCode.debit && (
|
|
70
69
|
<>
|
|
71
70
|
<br />
|
|
72
|
-
Total Debit Balances:{
|
|
73
|
-
{`${currencyCodeToSymbol(Currency.Ils)} ${formatStringifyAmount(sortCode.debit)}`}
|
|
71
|
+
Total Debit Balances: {formatAmountWithCurrency(sortCode.debit, Currency.Ils)}
|
|
74
72
|
</>
|
|
75
73
|
)}
|
|
76
74
|
{!!sortCode.credit && (
|
|
77
75
|
<>
|
|
78
76
|
<br />
|
|
79
|
-
Total Credit Balances:{
|
|
80
|
-
{`${currencyCodeToSymbol(Currency.Ils)} ${formatStringifyAmount(sortCode.credit)}`}
|
|
77
|
+
Total Credit Balances: {formatAmountWithCurrency(sortCode.credit, Currency.Ils)}
|
|
81
78
|
</>
|
|
82
79
|
)}
|
|
83
80
|
</td>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useEffect, type ReactElement } from 'react';
|
|
2
|
+
import { useMatch } from 'react-router-dom';
|
|
3
|
+
import { useQuery } from 'urql';
|
|
4
|
+
import Business from '@/components/business/index.js';
|
|
5
|
+
import { BusinessScreenDocument } from '../../../gql/graphql.js';
|
|
6
|
+
import { AccounterLoader } from '../../common/index.js';
|
|
7
|
+
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
|
|
9
|
+
/* GraphQL */ `
|
|
10
|
+
query BusinessScreen($businessId: UUID!) {
|
|
11
|
+
business(id: $businessId) {
|
|
12
|
+
id
|
|
13
|
+
...BusinessPage
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
export function getBusinessHref(businessId: string): string {
|
|
19
|
+
return `/businesses/${businessId}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const BusinessScreen = (): ReactElement => {
|
|
23
|
+
const match = useMatch('/businesses/:businessId');
|
|
24
|
+
|
|
25
|
+
const businessId = match?.params.businessId;
|
|
26
|
+
|
|
27
|
+
const [{ data, fetching }, fetchBusiness] = useQuery({
|
|
28
|
+
query: BusinessScreenDocument,
|
|
29
|
+
pause: !businessId,
|
|
30
|
+
variables: {
|
|
31
|
+
businessId: businessId ?? '',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (businessId) {
|
|
37
|
+
fetchBusiness();
|
|
38
|
+
}
|
|
39
|
+
}, [businessId, fetchBusiness]);
|
|
40
|
+
|
|
41
|
+
if (fetching && !data?.business) {
|
|
42
|
+
return <AccounterLoader />;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!businessId || !data?.business) {
|
|
46
|
+
return <div>Business not found</div>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return <Business data={data.business} refetchBusiness={async () => fetchBusiness()} />;
|
|
50
|
+
};
|
|
@@ -3,7 +3,7 @@ import { Check, Edit, Eye, FileText, Loader2 } from 'lucide-react';
|
|
|
3
3
|
import type { NewDocumentInfoFragment } from '../../../../gql/graphql.js';
|
|
4
4
|
import { usePreviewDocument } from '../../../../hooks/use-preview-document.js';
|
|
5
5
|
import { PdfViewer } from '../../../common/documents/issue-document/pdf-viewer.js';
|
|
6
|
-
import {
|
|
6
|
+
import { RecentBusinessDocs } from '../../../common/documents/issue-document/recent-business-docs.js';
|
|
7
7
|
import { RecentDocsOfSameType } from '../../../common/documents/issue-document/recent-docs-of-same-type.js';
|
|
8
8
|
import {
|
|
9
9
|
convertNewDocumentInfoFragmentIntoPreviewDocumentInput,
|
|
@@ -201,10 +201,10 @@ export function EditIssueDocumentModal({ onApprove, draft }: Props): ReactElemen
|
|
|
201
201
|
</CardContent>
|
|
202
202
|
</Card>
|
|
203
203
|
|
|
204
|
-
{/* Previous
|
|
204
|
+
{/* Previous business documents */}
|
|
205
205
|
{document.client?.id && (
|
|
206
|
-
<
|
|
207
|
-
|
|
206
|
+
<RecentBusinessDocs
|
|
207
|
+
businessId={document.client?.id}
|
|
208
208
|
linkedDocumentIds={document.linkedDocumentIds ?? []}
|
|
209
209
|
/>
|
|
210
210
|
)}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { forwardRef, type ComponentPropsWithoutRef, type ComponentRef } from 'react';
|
|
2
|
+
import { cn } from '@/lib/utils.js';
|
|
3
|
+
import * as ProgressPrimitive from '@radix-ui/react-progress';
|
|
4
|
+
|
|
5
|
+
const Progress = forwardRef<
|
|
6
|
+
ComponentRef<typeof ProgressPrimitive.Root>,
|
|
7
|
+
ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
|
|
8
|
+
>(({ className, value, ...props }, ref) => (
|
|
9
|
+
<ProgressPrimitive.Root
|
|
10
|
+
ref={ref}
|
|
11
|
+
className={cn(
|
|
12
|
+
'relative h-2 w-full overflow-hidden rounded-full bg-gray-900/20 dark:bg-gray-50/20',
|
|
13
|
+
className,
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
>
|
|
17
|
+
<ProgressPrimitive.Indicator
|
|
18
|
+
className="h-full w-full flex-1 bg-gray-900 transition-all dark:bg-gray-50"
|
|
19
|
+
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
|
20
|
+
/>
|
|
21
|
+
</ProgressPrimitive.Root>
|
|
22
|
+
));
|
|
23
|
+
Progress.displayName = ProgressPrimitive.Root.displayName;
|
|
24
|
+
|
|
25
|
+
export { Progress };
|