@accounter/client 0.0.8-alpha-20251022132023-9f41aae2ce16f8761101edc548f486cc6ce80333 → 0.0.8-alpha-20251022144027-c9c7a4b309de8a72dfd0ec4f11e63713ad12878f
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 +1 -25
- package/dist/assets/index-B2UYAO1O.css +1 -0
- package/dist/assets/index-CZb2m31K.js +1229 -0
- package/dist/assets/{index.es-CL8po8bM.js → index.es-9gVMpHTc.js} +5 -5
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/components/business/admin-business-section.tsx +284 -0
- package/src/components/business/index.tsx +22 -0
- package/src/components/business-transactions/business-extended-info.tsx +15 -13
- package/src/components/business-transactions/business-transactions-single.tsx +3 -3
- package/src/components/business-transactions/index.tsx +1 -12
- package/src/components/business-trips/business-trip.tsx +3 -3
- package/src/components/charges/cells/business-trip.tsx +8 -6
- package/src/components/charges/cells/counterparty.tsx +5 -7
- package/src/components/common/accounter-table.tsx +5 -6
- package/src/components/common/business-trip-report/parts/core-expense-row.tsx +9 -11
- package/src/components/common/business-trip-report/parts/uncategorized-transactions.tsx +13 -11
- package/src/components/common/buttons/button-with-label.tsx +41 -0
- package/src/components/common/buttons/button.tsx +44 -0
- package/src/components/common/buttons/index.ts +2 -0
- package/src/components/common/buttons/logout-button.tsx +6 -7
- package/src/components/common/documents-to-charge-matcher/selection-handler/index.tsx +2 -4
- package/src/components/common/documents-to-charge-matcher/selection-handler/wide-filtered-selection.tsx +7 -5
- package/src/components/common/forms/edit-document.tsx +10 -21
- package/src/components/common/new-documents-list.tsx +8 -10
- package/src/components/documents-table/cells/creditor.tsx +4 -11
- package/src/components/documents-table/cells/debtor.tsx +4 -11
- package/src/components/layout/dashboard-layout.tsx +0 -4
- package/src/components/layout/sidelinks.tsx +27 -28
- package/src/components/ledger-table/counterparty-cell.tsx +13 -19
- package/src/components/login-page.tsx +1 -2
- 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/screens/businesses/business.tsx +9 -21
- package/src/components/screens/charges/charge.tsx +9 -22
- package/src/components/transactions-table/cells/counterparty.tsx +2 -9
- package/src/components/transactions-table/cells-legacy/counterparty.tsx +2 -9
- package/src/gql/gql.ts +15 -3
- package/src/gql/graphql.ts +4913 -1559
- package/src/hooks/use-update-admin-business.ts +71 -0
- package/src/index.tsx +22 -4
- package/src/providers/auth-guard.tsx +23 -14
- package/src/providers/index.tsx +2 -7
- package/src/providers/urql.tsx +12 -7
- package/src/providers/user-provider.tsx +2 -3
- package/dist/assets/Checkbox-DBF5r7jA.js +0 -6
- package/dist/assets/Progress-Cjv9yrat.js +0 -1
- package/dist/assets/Typography-DdVppmQe.js +0 -1
- package/dist/assets/accordion-DcMd-Til.js +0 -1
- package/dist/assets/accountant-approvals-CU_EA64d.js +0 -1
- package/dist/assets/all-charges-DKGmrDeD.js +0 -1
- package/dist/assets/arrow-up-down-BeIaAKWn.js +0 -6
- package/dist/assets/business-EtSaFzKh.js +0 -37
- package/dist/assets/business-transactions-single-B0Up2Bs4.js +0 -1
- package/dist/assets/business-trip-CmMmdjCh.js +0 -1
- package/dist/assets/charges-filters-CqWjSIKI.js +0 -1
- package/dist/assets/charges-ledger-validation-DHfLjNlq.js +0 -1
- package/dist/assets/chart-D1Y6fN3z.js +0 -74
- package/dist/assets/data-table-pagination-BCciANKD.js +0 -11
- package/dist/assets/editable-business-trip-VNJjiYOp.js +0 -16
- package/dist/assets/graphql-document-dedupe-fragments-ByT8-wlV.js +0 -1
- package/dist/assets/index-1L3dEkVN.js +0 -1
- package/dist/assets/index-4ZIYAzBJ.js +0 -1
- package/dist/assets/index-4r9JDo8p.js +0 -1
- package/dist/assets/index-BFHiMLVc.js +0 -1
- package/dist/assets/index-BGmhxff9.js +0 -1
- package/dist/assets/index-BMcyYDGt.js +0 -11
- package/dist/assets/index-BVQkLk5i.js +0 -1
- package/dist/assets/index-Bj3HckU6.js +0 -876
- package/dist/assets/index-BnQW4CTb.js +0 -1
- package/dist/assets/index-C0FVazZA.js +0 -24
- package/dist/assets/index-CUDeasQ_.js +0 -1
- package/dist/assets/index-Cfm6e2hX.js +0 -1
- package/dist/assets/index-Cqz-mRoO.js +0 -17
- package/dist/assets/index-Cs7MeC4J.js +0 -137
- package/dist/assets/index-CzzfC-dD.css +0 -1
- package/dist/assets/index-DABsOOfA.js +0 -1
- package/dist/assets/index-DB4BgQdp.js +0 -2
- package/dist/assets/index-DV-Q6DUw.js +0 -1
- package/dist/assets/index-Dd5ylSyH.js +0 -9
- package/dist/assets/index-Dn7p3J8Q.js +0 -1
- package/dist/assets/index-Dpk6i3ex.js +0 -6
- package/dist/assets/index-EiT4SOpW.js +0 -2
- package/dist/assets/index-rcKvu7eR.js +0 -6
- package/dist/assets/index-sDbz8nnb.js +0 -1
- package/dist/assets/issue-document-CYdlZijI.js +0 -1
- package/dist/assets/login-page-CnMdssTN.js +0 -1
- package/dist/assets/missing-info-charges-CzeKbCyG.js +0 -1
- package/dist/assets/page-not-found-Bsa0DCM4.js +0 -1
- package/dist/assets/pencil-BMWYIsZB.js +0 -6
- package/dist/assets/report-commentary-row-D9AtKtZx.js +0 -1
- package/dist/assets/save-DC_eQcBj.js +0 -11
- package/dist/assets/sequential-CAnleQny.js +0 -1
- package/dist/assets/similar-charges-by-business-modal-Dmb2ZymF.js +0 -1
- package/dist/assets/sub-BIMwxqar.js +0 -1
- package/dist/assets/subMonths-B7SR4Lli.js +0 -1
- package/src/components/error-boundary.tsx +0 -189
- package/src/components/layout/breadcrumbs.tsx +0 -72
- package/src/components/layout/document-title.tsx +0 -26
- package/src/components/layout/navigation-progress.tsx +0 -52
- package/src/components/layout/page-skeleton.tsx +0 -49
- package/src/providers/urql-client.ts +0 -86
- package/src/router/config.tsx +0 -534
- package/src/router/layouts/dashboard-layout.tsx +0 -20
- package/src/router/layouts/root-layout.tsx +0 -69
- package/src/router/loaders/auth-loader.ts +0 -32
- package/src/router/loaders/business-loader.ts +0 -25
- package/src/router/loaders/charge-loader.ts +0 -25
- package/src/router/loaders/index.ts +0 -17
- package/src/router/routes.ts +0 -88
- package/src/router/types.ts +0 -68
package/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="icon" href="/icons/accounter-logo.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>Accounter</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-CZb2m31K.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-B2UYAO1O.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type React from 'react';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
import { Save, Shield } from 'lucide-react';
|
|
6
|
+
import { useForm } from 'react-hook-form';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { Button } from '@/components/ui/button.js';
|
|
9
|
+
import {
|
|
10
|
+
Card,
|
|
11
|
+
CardContent,
|
|
12
|
+
CardDescription,
|
|
13
|
+
CardFooter,
|
|
14
|
+
CardHeader,
|
|
15
|
+
CardTitle,
|
|
16
|
+
} from '@/components/ui/card.js';
|
|
17
|
+
import {
|
|
18
|
+
Form,
|
|
19
|
+
FormControl,
|
|
20
|
+
FormField,
|
|
21
|
+
FormItem,
|
|
22
|
+
FormLabel,
|
|
23
|
+
FormMessage,
|
|
24
|
+
} from '@/components/ui/form.js';
|
|
25
|
+
import { Input } from '@/components/ui/input.js';
|
|
26
|
+
import {
|
|
27
|
+
BusinessAdminSectionFragmentDoc,
|
|
28
|
+
type BusinessAdminSectionFragment,
|
|
29
|
+
type UpdateAdminBusinessInput,
|
|
30
|
+
} from '@/gql/graphql.js';
|
|
31
|
+
import { getFragmentData, type FragmentType } from '@/gql/index.js';
|
|
32
|
+
import {
|
|
33
|
+
dirtyFieldMarker,
|
|
34
|
+
relevantDataPicker,
|
|
35
|
+
type MakeBoolean,
|
|
36
|
+
type TimelessDateString,
|
|
37
|
+
} from '@/helpers/index.js';
|
|
38
|
+
import { useUpdateAdminBusiness } from '@/hooks/use-update-admin-business.js';
|
|
39
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
40
|
+
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
|
|
42
|
+
/* GraphQL */ `
|
|
43
|
+
fragment BusinessAdminSection on Business {
|
|
44
|
+
id
|
|
45
|
+
... on LtdFinancialEntity {
|
|
46
|
+
adminInfo {
|
|
47
|
+
id
|
|
48
|
+
employerWithholdingTaxAccountNumber
|
|
49
|
+
taxPrepaymentId
|
|
50
|
+
nationalInsuranceEmployerId
|
|
51
|
+
advanceTaxRate
|
|
52
|
+
registrationDate
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
const adminBusinessFormSchema = z.object({
|
|
59
|
+
employerWithholdingTaxAccountNumber: z.string().min(1, {
|
|
60
|
+
message: 'Employer Withholding Tax Account Number is required',
|
|
61
|
+
}),
|
|
62
|
+
taxPrepaymentId: z.string().min(1, {
|
|
63
|
+
message: 'Tax Prepayment ID is required',
|
|
64
|
+
}),
|
|
65
|
+
nationalInsuranceEmployerId: z.string().min(1, {
|
|
66
|
+
message: 'National Insurance Employer ID is required',
|
|
67
|
+
}),
|
|
68
|
+
advanceTaxRate: z
|
|
69
|
+
.number()
|
|
70
|
+
.min(0, {
|
|
71
|
+
message: 'Advance Tax Rate must be at least 0',
|
|
72
|
+
})
|
|
73
|
+
.max(100, {
|
|
74
|
+
message: 'Advance Tax Rate must be at most 100',
|
|
75
|
+
}),
|
|
76
|
+
registrationDate: z.string().min(1, {
|
|
77
|
+
message: 'Registration Date is required',
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
type AdminBusinessFormValues = z.infer<typeof adminBusinessFormSchema>;
|
|
82
|
+
|
|
83
|
+
function BusinessAdminSectionFragmentToFormValues(
|
|
84
|
+
admin?: BusinessAdminSectionFragment,
|
|
85
|
+
): AdminBusinessFormValues {
|
|
86
|
+
if (!admin || admin.__typename !== 'LtdFinancialEntity' || !admin.adminInfo) {
|
|
87
|
+
return {} as AdminBusinessFormValues;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
employerWithholdingTaxAccountNumber: admin.adminInfo.employerWithholdingTaxAccountNumber ?? '',
|
|
92
|
+
taxPrepaymentId: admin.adminInfo.taxPrepaymentId ?? '',
|
|
93
|
+
nationalInsuranceEmployerId: admin.adminInfo.nationalInsuranceEmployerId ?? '',
|
|
94
|
+
advanceTaxRate: admin.adminInfo.advanceTaxRate ?? 0,
|
|
95
|
+
registrationDate: admin.adminInfo.registrationDate ?? '',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function convertFormDataToUpdateAdminBusinessInput(
|
|
100
|
+
formData: Partial<AdminBusinessFormValues>,
|
|
101
|
+
): UpdateAdminBusinessInput {
|
|
102
|
+
return {
|
|
103
|
+
employerWithholdingTaxAccountNumber: formData.employerWithholdingTaxAccountNumber,
|
|
104
|
+
taxPrepaymentId: formData.taxPrepaymentId,
|
|
105
|
+
nationalInsuranceEmployerId: formData.nationalInsuranceEmployerId,
|
|
106
|
+
advanceTaxRate: formData.advanceTaxRate,
|
|
107
|
+
registrationDate: formData.registrationDate as TimelessDateString,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
interface Props {
|
|
112
|
+
data?: FragmentType<typeof BusinessAdminSectionFragmentDoc>;
|
|
113
|
+
refetchBusiness?: () => Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function AdminBusinessSection({ data, refetchBusiness }: Props): React.ReactElement {
|
|
117
|
+
const admin = getFragmentData(BusinessAdminSectionFragmentDoc, data);
|
|
118
|
+
const [defaultFormValues, setDefaultFormValues] = useState(
|
|
119
|
+
BusinessAdminSectionFragmentToFormValues(admin),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const { updateAdminBusiness: updateDbAdminBusiness, fetching: isUpdating } =
|
|
123
|
+
useUpdateAdminBusiness();
|
|
124
|
+
|
|
125
|
+
const form = useForm<AdminBusinessFormValues>({
|
|
126
|
+
resolver: zodResolver(adminBusinessFormSchema),
|
|
127
|
+
defaultValues: defaultFormValues,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const onSubmit = async (data: AdminBusinessFormValues) => {
|
|
131
|
+
if (!admin) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const dataToUpdate = relevantDataPicker(
|
|
136
|
+
data,
|
|
137
|
+
form.formState.dirtyFields as MakeBoolean<typeof data>,
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
if (!dataToUpdate) return;
|
|
141
|
+
|
|
142
|
+
const updateBusinessInput = convertFormDataToUpdateAdminBusinessInput(dataToUpdate);
|
|
143
|
+
|
|
144
|
+
await updateDbAdminBusiness({
|
|
145
|
+
adminBusinessId: admin.id,
|
|
146
|
+
fields: updateBusinessInput,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
refetchBusiness?.();
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
if (admin) {
|
|
154
|
+
const formValues = BusinessAdminSectionFragmentToFormValues(admin);
|
|
155
|
+
setDefaultFormValues(formValues);
|
|
156
|
+
form.reset(formValues);
|
|
157
|
+
}
|
|
158
|
+
}, [admin, form]);
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<Card>
|
|
162
|
+
<CardHeader>
|
|
163
|
+
<div className="flex items-center gap-2">
|
|
164
|
+
<Shield className="h-5 w-5 text-primary" />
|
|
165
|
+
<CardTitle>Admin Business Configuration</CardTitle>
|
|
166
|
+
</div>
|
|
167
|
+
<CardDescription>
|
|
168
|
+
Tax and registration information for the business operating this application
|
|
169
|
+
</CardDescription>
|
|
170
|
+
</CardHeader>
|
|
171
|
+
<Form {...form}>
|
|
172
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
|
173
|
+
<CardContent>
|
|
174
|
+
<div className="grid gap-6 md:grid-cols-2">
|
|
175
|
+
{/* Employer Withholding Tax Account Number */}
|
|
176
|
+
<FormField
|
|
177
|
+
control={form.control}
|
|
178
|
+
name="employerWithholdingTaxAccountNumber"
|
|
179
|
+
render={({ field, fieldState }) => (
|
|
180
|
+
<FormItem>
|
|
181
|
+
<FormLabel>Employer Withholding Tax Account Number</FormLabel>
|
|
182
|
+
<FormControl>
|
|
183
|
+
<Input
|
|
184
|
+
placeholder="Enter account number"
|
|
185
|
+
{...field}
|
|
186
|
+
className={dirtyFieldMarker(fieldState)}
|
|
187
|
+
/>
|
|
188
|
+
</FormControl>
|
|
189
|
+
<FormMessage />
|
|
190
|
+
</FormItem>
|
|
191
|
+
)}
|
|
192
|
+
/>
|
|
193
|
+
|
|
194
|
+
{/* Tax Prepayment ID */}
|
|
195
|
+
<FormField
|
|
196
|
+
control={form.control}
|
|
197
|
+
name="taxPrepaymentId"
|
|
198
|
+
render={({ field, fieldState }) => (
|
|
199
|
+
<FormItem>
|
|
200
|
+
<FormLabel>Tax Prepayment ID</FormLabel>
|
|
201
|
+
<FormControl>
|
|
202
|
+
<Input
|
|
203
|
+
placeholder="Enter prepayment ID"
|
|
204
|
+
{...field}
|
|
205
|
+
className={dirtyFieldMarker(fieldState)}
|
|
206
|
+
/>
|
|
207
|
+
</FormControl>
|
|
208
|
+
<FormMessage />
|
|
209
|
+
</FormItem>
|
|
210
|
+
)}
|
|
211
|
+
/>
|
|
212
|
+
|
|
213
|
+
{/* National Insurance Employer ID */}
|
|
214
|
+
<FormField
|
|
215
|
+
control={form.control}
|
|
216
|
+
name="nationalInsuranceEmployerId"
|
|
217
|
+
render={({ field, fieldState }) => (
|
|
218
|
+
<FormItem>
|
|
219
|
+
<FormLabel>National Insurance Employer ID</FormLabel>
|
|
220
|
+
<FormControl>
|
|
221
|
+
<Input
|
|
222
|
+
placeholder="Enter employer ID"
|
|
223
|
+
{...field}
|
|
224
|
+
className={dirtyFieldMarker(fieldState)}
|
|
225
|
+
/>
|
|
226
|
+
</FormControl>
|
|
227
|
+
<FormMessage />
|
|
228
|
+
</FormItem>
|
|
229
|
+
)}
|
|
230
|
+
/>
|
|
231
|
+
|
|
232
|
+
{/* Advance Tax Rate */}
|
|
233
|
+
<FormField
|
|
234
|
+
control={form.control}
|
|
235
|
+
name="advanceTaxRate"
|
|
236
|
+
render={({ field, fieldState }) => (
|
|
237
|
+
<FormItem>
|
|
238
|
+
<FormLabel>Advance Tax Rate (%)</FormLabel>
|
|
239
|
+
<FormControl>
|
|
240
|
+
<Input
|
|
241
|
+
type="number"
|
|
242
|
+
step="0.01"
|
|
243
|
+
min="0"
|
|
244
|
+
max="100"
|
|
245
|
+
placeholder="Enter tax rate"
|
|
246
|
+
{...field}
|
|
247
|
+
className={dirtyFieldMarker(fieldState)}
|
|
248
|
+
/>
|
|
249
|
+
</FormControl>
|
|
250
|
+
<FormMessage />
|
|
251
|
+
</FormItem>
|
|
252
|
+
)}
|
|
253
|
+
/>
|
|
254
|
+
|
|
255
|
+
{/* Registration Date */}
|
|
256
|
+
<FormField
|
|
257
|
+
control={form.control}
|
|
258
|
+
name="registrationDate"
|
|
259
|
+
render={({ field, fieldState }) => (
|
|
260
|
+
<FormItem>
|
|
261
|
+
<FormLabel>Registration Date</FormLabel>
|
|
262
|
+
<FormControl>
|
|
263
|
+
<Input type="date" {...field} className={dirtyFieldMarker(fieldState)} />
|
|
264
|
+
</FormControl>
|
|
265
|
+
<FormMessage />
|
|
266
|
+
</FormItem>
|
|
267
|
+
)}
|
|
268
|
+
/>
|
|
269
|
+
</div>
|
|
270
|
+
</CardContent>
|
|
271
|
+
<CardFooter className="flex justify-end border-t mt-4 pt-6">
|
|
272
|
+
<Button
|
|
273
|
+
type="submit"
|
|
274
|
+
disabled={isUpdating || Object.keys(form.formState.dirtyFields).length === 0}
|
|
275
|
+
>
|
|
276
|
+
<Save className="h-4 w-4" />
|
|
277
|
+
Save Changes
|
|
278
|
+
</Button>
|
|
279
|
+
</CardFooter>
|
|
280
|
+
</form>
|
|
281
|
+
</Form>
|
|
282
|
+
</Card>
|
|
283
|
+
);
|
|
284
|
+
}
|
|
@@ -7,12 +7,14 @@ import {
|
|
|
7
7
|
FileText,
|
|
8
8
|
Plug,
|
|
9
9
|
Settings,
|
|
10
|
+
Shield,
|
|
10
11
|
// TrendingUp,
|
|
11
12
|
} from 'lucide-react';
|
|
12
13
|
import { useSearchParams } from 'react-router-dom';
|
|
13
14
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs.js';
|
|
14
15
|
import { getFragmentData, type FragmentType } from '@/gql/index.js';
|
|
15
16
|
import { BusinessPageFragmentDoc } from '../../gql/graphql.js';
|
|
17
|
+
import { AdminBusinessSection } from './admin-business-section.js';
|
|
16
18
|
import { BusinessHeader } from './business-header.js';
|
|
17
19
|
import { ChargesSection } from './charges-section.jsx';
|
|
18
20
|
import { ChartsSection } from './charts-section.jsx';
|
|
@@ -31,11 +33,15 @@ import { TransactionsSection } from './transactions-section.js';
|
|
|
31
33
|
clientInfo {
|
|
32
34
|
id
|
|
33
35
|
}
|
|
36
|
+
adminInfo {
|
|
37
|
+
id
|
|
38
|
+
}
|
|
34
39
|
}
|
|
35
40
|
...ClientIntegrationsSection
|
|
36
41
|
...BusinessHeader
|
|
37
42
|
...BusinessContactSection
|
|
38
43
|
...BusinessConfigurationSection
|
|
44
|
+
...BusinessAdminSection
|
|
39
45
|
}
|
|
40
46
|
`;
|
|
41
47
|
|
|
@@ -59,6 +65,7 @@ export default function Business({ data, refetchBusiness }: Props): ReactElement
|
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
const isClient = 'clientInfo' in business && !!business.clientInfo;
|
|
68
|
+
const isAdmin = 'adminInfo' in business && !!business.adminInfo;
|
|
62
69
|
|
|
63
70
|
return (
|
|
64
71
|
<div className="min-h-screen bg-background">
|
|
@@ -127,6 +134,15 @@ export default function Business({ data, refetchBusiness }: Props): ReactElement
|
|
|
127
134
|
</TabsTrigger> */}
|
|
128
135
|
</>
|
|
129
136
|
)}
|
|
137
|
+
{isAdmin && (
|
|
138
|
+
<TabsTrigger
|
|
139
|
+
value="admin"
|
|
140
|
+
className="flex items-center gap-2 data-[state=active]:bg-background"
|
|
141
|
+
>
|
|
142
|
+
<Shield className="h-4 w-4" />
|
|
143
|
+
<span className="hidden sm:inline">Admin</span>
|
|
144
|
+
</TabsTrigger>
|
|
145
|
+
)}
|
|
130
146
|
</TabsList>
|
|
131
147
|
|
|
132
148
|
<TabsContent value="contact" className="mt-0">
|
|
@@ -164,6 +180,12 @@ export default function Business({ data, refetchBusiness }: Props): ReactElement
|
|
|
164
180
|
</TabsContent>
|
|
165
181
|
</>
|
|
166
182
|
)}
|
|
183
|
+
|
|
184
|
+
{isAdmin && (
|
|
185
|
+
<TabsContent value="admin" className="mt-0">
|
|
186
|
+
<AdminBusinessSection data={business} />
|
|
187
|
+
</TabsContent>
|
|
188
|
+
)}
|
|
167
189
|
</Tabs>
|
|
168
190
|
</main>
|
|
169
191
|
</div>
|
|
@@ -1,9 +1,8 @@
|
|
|
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';
|
|
5
4
|
import { useQuery } from 'urql';
|
|
6
|
-
import { Mark, Table, Tooltip } from '@mantine/core';
|
|
5
|
+
import { Mark, NavLink, Table, Tooltip } from '@mantine/core';
|
|
7
6
|
import {
|
|
8
7
|
BusinessTransactionsInfoDocument,
|
|
9
8
|
Currency,
|
|
@@ -15,7 +14,6 @@ import { AccounterLoader } from '../common/index.js';
|
|
|
15
14
|
import { getChargeHref } from '../screens/charges/charge.js';
|
|
16
15
|
import { Button } from '../ui/button.js';
|
|
17
16
|
import { DownloadCSV } from './download-csv.js';
|
|
18
|
-
import { getBusinessTransactionsHref } from './index.js';
|
|
19
17
|
|
|
20
18
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
|
|
21
19
|
/* GraphQL */ `
|
|
@@ -235,15 +233,17 @@ export function BusinessExtendedInfo({ businessID, filter }: Props): ReactElemen
|
|
|
235
233
|
}}
|
|
236
234
|
>
|
|
237
235
|
<td>
|
|
238
|
-
<
|
|
239
|
-
|
|
236
|
+
<a
|
|
237
|
+
href={`/businesses/${row.business.id}/transactions`}
|
|
240
238
|
target="_blank"
|
|
241
239
|
rel="noreferrer"
|
|
242
240
|
onClick={event => event.stopPropagation()}
|
|
243
|
-
className="inline-flex items-center font-semibold"
|
|
244
241
|
>
|
|
245
|
-
|
|
246
|
-
|
|
242
|
+
<NavLink
|
|
243
|
+
label={row.business.name}
|
|
244
|
+
className="[&>*>.mantine-NavLink-label]:font-semibold"
|
|
245
|
+
/>
|
|
246
|
+
</a>
|
|
247
247
|
</td>
|
|
248
248
|
<td>{row.invoiceDate ? format(new Date(row.invoiceDate), 'dd/MM/yy') : null}</td>
|
|
249
249
|
<td style={{ whiteSpace: 'nowrap' }}>
|
|
@@ -273,15 +273,17 @@ export function BusinessExtendedInfo({ businessID, filter }: Props): ReactElemen
|
|
|
273
273
|
<td>{row.details}</td>
|
|
274
274
|
<td>
|
|
275
275
|
{row.counterAccount && (
|
|
276
|
-
<
|
|
277
|
-
|
|
276
|
+
<a
|
|
277
|
+
href={`/businesses/${row.counterAccount?.id}/transactions`}
|
|
278
278
|
target="_blank"
|
|
279
279
|
rel="noreferrer"
|
|
280
280
|
onClick={event => event.stopPropagation()}
|
|
281
|
-
className="inline-flex items-center font-semibold"
|
|
282
281
|
>
|
|
283
|
-
|
|
284
|
-
|
|
282
|
+
<NavLink
|
|
283
|
+
label={row.counterAccount?.name}
|
|
284
|
+
className="[&>*>.mantine-NavLink-label]:font-semibold"
|
|
285
|
+
/>
|
|
286
|
+
</a>
|
|
285
287
|
)}
|
|
286
288
|
</td>
|
|
287
289
|
<td />
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useContext, useEffect, useMemo, useState, type ReactElement, type ReactNode } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { useMatch } 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 match = useMatch('businesses/:businessId/transactions');
|
|
23
23
|
const { get } = useUrlQuery();
|
|
24
24
|
const { setFiltersContext } = useContext(FiltersContext);
|
|
25
|
-
const id = businessId ||
|
|
25
|
+
const id = businessId || match?.params.businessId;
|
|
26
26
|
const [filter, setFilter] = useState<BusinessTransactionsFilter>(
|
|
27
27
|
get('transactionsFilters')
|
|
28
28
|
? {
|
|
@@ -72,24 +72,13 @@ import {
|
|
|
72
72
|
export function getBusinessTransactionsHref(filter?: BusinessTransactionsFilter | null): string {
|
|
73
73
|
const params = new URLSearchParams();
|
|
74
74
|
|
|
75
|
-
|
|
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);
|
|
75
|
+
const transactionsFilters = encodeTransactionsFilters(filter);
|
|
84
76
|
if (transactionsFilters) {
|
|
85
77
|
// Add it as a single encoded parameter
|
|
86
78
|
params.append('transactionsFilters', transactionsFilters);
|
|
87
79
|
}
|
|
88
80
|
|
|
89
81
|
const queryParams = params.size > 0 ? `?${params}` : '';
|
|
90
|
-
if (businessId) {
|
|
91
|
-
return `/businesses/${businessId}/transactions${queryParams}`;
|
|
92
|
-
}
|
|
93
82
|
return `/businesses/transactions${queryParams}`;
|
|
94
83
|
}
|
|
95
84
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useContext, useEffect, type ReactElement } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { useMatch } 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 match = useMatch('business-trips/:businessTripId');
|
|
30
30
|
const { setFiltersContext } = useContext(FiltersContext);
|
|
31
|
-
const id = businessTripId ??
|
|
31
|
+
const id = businessTripId ?? match?.params.businessTripId ?? '';
|
|
32
32
|
const [{ data, fetching }] = useQuery({
|
|
33
33
|
query: BusinessTripScreenDocument,
|
|
34
34
|
variables: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ReactElement } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { NavLink } from '@mantine/core';
|
|
3
3
|
import { ChargesTableBusinessTripFieldsFragmentDoc } from '../../../gql/graphql.js';
|
|
4
4
|
import { getFragmentData, type FragmentType } from '../../../gql/index.js';
|
|
5
5
|
|
|
@@ -29,15 +29,17 @@ export const BusinessTrip = ({ data }: Props): ReactElement => {
|
|
|
29
29
|
|
|
30
30
|
return (
|
|
31
31
|
<td>
|
|
32
|
-
<
|
|
33
|
-
|
|
32
|
+
<a
|
|
33
|
+
href={`/business-trips/${charge.businessTrip?.id}`}
|
|
34
34
|
target="_blank"
|
|
35
35
|
rel="noreferrer"
|
|
36
36
|
onClick={event => event.stopPropagation()}
|
|
37
|
-
className="inline-flex items-center font-semibold"
|
|
38
37
|
>
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
<NavLink
|
|
39
|
+
label={charge.businessTrip?.name}
|
|
40
|
+
className="[&>*>.mantine-NavLink-label]:font-semibold"
|
|
41
|
+
/>
|
|
42
|
+
</a>
|
|
41
43
|
</td>
|
|
42
44
|
);
|
|
43
45
|
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useCallback, useMemo, type ReactElement } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import { Indicator } from '@mantine/core';
|
|
2
|
+
import { Indicator, NavLink } from '@mantine/core';
|
|
4
3
|
import {
|
|
5
4
|
ChargesTableEntityFieldsFragmentDoc,
|
|
6
5
|
MissingChargeInfo,
|
|
@@ -82,15 +81,14 @@ export const Counterparty = ({ data }: Props): ReactElement => {
|
|
|
82
81
|
<div className="flex flex-wrap">
|
|
83
82
|
<Indicator inline size={12} disabled={!isError} color="red" zIndex="auto">
|
|
84
83
|
{!isError && id && (
|
|
85
|
-
<
|
|
86
|
-
|
|
84
|
+
<a
|
|
85
|
+
href={getHref(id)}
|
|
87
86
|
target="_blank"
|
|
88
87
|
rel="noreferrer"
|
|
89
88
|
onClick={event => event.stopPropagation()}
|
|
90
|
-
className="inline-flex items-center font-semibold"
|
|
91
89
|
>
|
|
92
|
-
{name}
|
|
93
|
-
</
|
|
90
|
+
<NavLink label={name} className="[&>*>.mantine-NavLink-label]:font-semibold" />
|
|
91
|
+
</a>
|
|
94
92
|
)}
|
|
95
93
|
{isError && name}
|
|
96
94
|
</Indicator>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, type ReactElement, type ReactNode } from 'react';
|
|
2
|
-
import { Button } from '@/components/ui/button.js';
|
|
3
2
|
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,9 +47,7 @@ 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 onClick={(): void => setOpened(!opened)}
|
|
51
|
-
More Info
|
|
52
|
-
</Button>
|
|
50
|
+
<Button title="More Info" onClick={(): void => setOpened(!opened)} />
|
|
53
51
|
)}
|
|
54
52
|
</td>
|
|
55
53
|
)}
|
|
@@ -75,14 +73,15 @@ export function AccounterTable<T, U>(props: AccounterTableProps<T, U>): ReactNod
|
|
|
75
73
|
<div className="flex flex-row justify-end w-full">
|
|
76
74
|
{props.pagination && <Pagination className="flex-auto" {...props.pagination} />}
|
|
77
75
|
{props.showButton === true ? (
|
|
78
|
-
<
|
|
76
|
+
<button
|
|
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"
|
|
79
78
|
type="button"
|
|
80
79
|
onClick={(): void => {
|
|
81
80
|
setIsShowAll(prev => !prev);
|
|
82
81
|
}}
|
|
83
82
|
>
|
|
84
83
|
{isShowAll ? 'Hide All' : 'Show All'}
|
|
85
|
-
</
|
|
84
|
+
</button>
|
|
86
85
|
) : null}
|
|
87
86
|
</div>
|
|
88
87
|
<Table striped={props.striped} highlightOnHover={props.highlightOnHover}>
|
|
@@ -1,10 +1,9 @@
|
|
|
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';
|
|
5
4
|
import { toast } from 'sonner';
|
|
6
5
|
import { useQuery } from 'urql';
|
|
7
|
-
import { Select, Text } from '@mantine/core';
|
|
6
|
+
import { NavLink, Select, Text } from '@mantine/core';
|
|
8
7
|
import { DatePickerInput } from '@mantine/dates';
|
|
9
8
|
import {
|
|
10
9
|
AttendeesByBusinessTripDocument,
|
|
@@ -237,16 +236,15 @@ export const CoreExpenseRow = ({
|
|
|
237
236
|
<td>
|
|
238
237
|
<div className="flex flex-col gap-2">
|
|
239
238
|
{linkedChargeIds.map(id => (
|
|
240
|
-
<
|
|
239
|
+
<NavLink
|
|
241
240
|
key={id}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
</Link>
|
|
241
|
+
label="To Charge"
|
|
242
|
+
className="[&>*>.mantine-NavLink-label]:font-semibold"
|
|
243
|
+
onClick={(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
|
|
244
|
+
event.stopPropagation();
|
|
245
|
+
window.open(getChargeHref(id), '_blank', 'noreferrer');
|
|
246
|
+
}}
|
|
247
|
+
/>
|
|
250
248
|
))}
|
|
251
249
|
</div>
|
|
252
250
|
</td>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { ReactElement } from 'react';
|
|
2
2
|
import { AlertCircle } from 'lucide-react';
|
|
3
|
-
import {
|
|
4
|
-
import { Popover, Table, Text } from '@mantine/core';
|
|
3
|
+
import { NavLink, Popover, Table, Text } from '@mantine/core';
|
|
5
4
|
import { useDisclosure } from '@mantine/hooks';
|
|
6
5
|
import {
|
|
7
6
|
BusinessTripUncategorizedTransactionsFieldsFragmentDoc,
|
|
@@ -93,15 +92,18 @@ export const UncategorizedTransactions = ({ data, onChange }: Props): ReactEleme
|
|
|
93
92
|
<DebitDate data={uncategorizedTransaction.transaction} />
|
|
94
93
|
<Amount data={uncategorizedTransaction} />
|
|
95
94
|
<td>
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
95
|
+
<NavLink
|
|
96
|
+
label="To Charge"
|
|
97
|
+
className="[&>*>.mantine-NavLink-label]:font-semibold"
|
|
98
|
+
onClick={(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
|
|
99
|
+
event.stopPropagation();
|
|
100
|
+
window.open(
|
|
101
|
+
getChargeHref(uncategorizedTransaction.transaction.chargeId),
|
|
102
|
+
'_blank',
|
|
103
|
+
'noreferrer',
|
|
104
|
+
);
|
|
105
|
+
}}
|
|
106
|
+
/>
|
|
105
107
|
</td>
|
|
106
108
|
<Account data={uncategorizedTransaction.transaction} />
|
|
107
109
|
<Description data={uncategorizedTransaction.transaction} />
|