@accounter/client 0.0.8-alpha-20251021150553-5fe662174014d6173ebcea8412cf0fa294abbed2 → 0.0.8-alpha-20251021163440-2ab1a9ffaec95fd99fac5495c3a392b97429ce77
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 +36 -1
- package/dist/assets/index-B2UYAO1O.css +1 -0
- package/dist/assets/index-BexxGuN6.js +1224 -0
- package/dist/assets/{index.es-BaJmdn-u.js → index.es-CWwhWGxX.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +2 -1
- 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 +11 -15
- package/src/components/business-transactions/business-transactions-single.tsx +1 -1
- package/src/components/business-transactions/index.tsx +1 -1
- 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/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 +2 -19
- package/src/components/common/inputs/combo-box.tsx +1 -1
- package/src/components/layout/sidelinks.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 +44 -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 +93 -9
- package/src/gql/graphql.ts +300 -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/dist/assets/index-CqaS5jWM.css +0 -1
- package/dist/assets/index-IlfO2QvT.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
|
+
// 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
|
+
}
|
|
@@ -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>({
|
|
@@ -4,10 +4,9 @@ import { useAllCountries } from '@/hooks/use-get-countries.js';
|
|
|
4
4
|
import {
|
|
5
5
|
EmailAttachmentType,
|
|
6
6
|
type InsertNewBusinessInput,
|
|
7
|
-
type Pcn874RecordType,
|
|
8
7
|
type UpdateBusinessInput,
|
|
9
8
|
} from '../../../gql/graphql.js';
|
|
10
|
-
import { dirtyFieldMarker } from '../../../helpers/index.js';
|
|
9
|
+
import { dirtyFieldMarker, pcn874RecordEnum } from '../../../helpers/index.js';
|
|
11
10
|
import { useGetSortCodes } from '../../../hooks/use-get-sort-codes.js';
|
|
12
11
|
import { useGetTaxCategories } from '../../../hooks/use-get-tax-categories.js';
|
|
13
12
|
import { FormControl, FormField, FormItem, FormLabel, FormMessage } from '../../ui/form.js';
|
|
@@ -16,22 +15,6 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '.
|
|
|
16
15
|
import { Switch } from '../../ui/switch.js';
|
|
17
16
|
import { ComboBox, MultiSelect, NumberInput, StringArrayInput, TagsInput } from '../index.js';
|
|
18
17
|
|
|
19
|
-
const pcn874RecordType: Record<Pcn874RecordType, string> = {
|
|
20
|
-
C: 'INPUT_SELF_INVOICE',
|
|
21
|
-
H: 'INPUT_SINGLE_DOC_BY_LAW',
|
|
22
|
-
I: 'SALE_PALESTINIAN_CUSTOMER',
|
|
23
|
-
K: 'INPUT_PETTY_CASH',
|
|
24
|
-
L1: 'SALE_UNIDENTIFIED_CUSTOMER',
|
|
25
|
-
L2: 'SALE_UNIDENTIFIED_ZERO_OR_EXEMPT',
|
|
26
|
-
M: 'SALE_SELF_INVOICE',
|
|
27
|
-
P: 'INPUT_PALESTINIAN_SUPPLIER',
|
|
28
|
-
R: 'INPUT_IMPORT',
|
|
29
|
-
S1: 'SALE_REGULAR',
|
|
30
|
-
S2: 'SALE_ZERO_OR_EXEMPT',
|
|
31
|
-
T: 'INPUT_REGULAR',
|
|
32
|
-
Y: 'SALE_EXPORT',
|
|
33
|
-
} as const;
|
|
34
|
-
|
|
35
18
|
type ModalProps<T extends boolean> = {
|
|
36
19
|
isInsert: T;
|
|
37
20
|
formManager: UseFormReturn<
|
|
@@ -327,7 +310,7 @@ export function ModifyBusinessFields({
|
|
|
327
310
|
</SelectTrigger>
|
|
328
311
|
</FormControl>
|
|
329
312
|
<SelectContent onClick={event => event.stopPropagation()}>
|
|
330
|
-
{Object.entries(
|
|
313
|
+
{Object.entries(pcn874RecordEnum).map(([value, label]) => (
|
|
331
314
|
<SelectItem key={value} value={value}>
|
|
332
315
|
{`${label} (${value})`}
|
|
333
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}
|
|
@@ -55,13 +55,13 @@ export const sidelinks: SideLink[] = [
|
|
|
55
55
|
{
|
|
56
56
|
title: 'Missing Info Charges',
|
|
57
57
|
label: '',
|
|
58
|
-
href: '/missing-info
|
|
58
|
+
href: '/charges/missing-info',
|
|
59
59
|
icon: <RectangleEllipsis size={18} />,
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
title: 'Ledger Validation',
|
|
63
63
|
label: '',
|
|
64
|
-
href: '/charges
|
|
64
|
+
href: '/charges/ledger-validation',
|
|
65
65
|
icon: <BookOpenCheck size={18} />,
|
|
66
66
|
},
|
|
67
67
|
],
|
|
@@ -166,7 +166,7 @@ export const sidelinks: SideLink[] = [
|
|
|
166
166
|
{
|
|
167
167
|
title: 'Business Transactions',
|
|
168
168
|
label: '',
|
|
169
|
-
href: '/
|
|
169
|
+
href: '/businesses/transactions',
|
|
170
170
|
icon: <ArrowLeftRight size={18} />,
|
|
171
171
|
},
|
|
172
172
|
{
|
|
@@ -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,44 @@
|
|
|
1
|
+
import { 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
|
+
if (fetching && !data?.business) {
|
|
36
|
+
return <AccounterLoader />;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!businessId || !data?.business) {
|
|
40
|
+
return <div>Business not found</div>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return <Business data={data.business} refetchBusiness={async () => fetchBusiness()} />;
|
|
44
|
+
};
|
|
@@ -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 };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { cn } from '@/lib/utils.js';
|
|
2
|
+
|
|
3
|
+
function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
4
|
+
return (
|
|
5
|
+
<div
|
|
6
|
+
className={cn('animate-pulse rounded-md bg-gray-900/10 dark:bg-gray-50/10', className)}
|
|
7
|
+
{...props}
|
|
8
|
+
/>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { Skeleton };
|