@accounter/client 0.0.8-alpha-20251023121008-8c51a83a479af2fcbc3aa02c876285dc4e21f02b → 0.0.8-alpha-20251023122652-765c7f951395441461726b3b4345eebd020632d7

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.
Files changed (92) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/dist/assets/{Checkbox-Bu7lbyW3.js → Checkbox-CpANMiXr.js} +2 -2
  3. package/dist/assets/{Progress-Cq1kNJW5.js → Progress-BEKiyNRK.js} +1 -1
  4. package/dist/assets/{Typography-qy9vYtPs.js → Typography-C0c5augK.js} +1 -1
  5. package/dist/assets/{accordion-DOoNLp8s.js → accordion-ZFVTCyDy.js} +1 -1
  6. package/dist/assets/accountant-approvals-CpzuSBgK.js +1 -0
  7. package/dist/assets/{all-charges-CkXCeGgB.js → all-charges-BTyaAlp7.js} +1 -1
  8. package/dist/assets/{arrow-up-down-DvV6Dq8U.js → arrow-up-down-PQdrJdei.js} +1 -1
  9. package/dist/assets/business-DiQFqEHI.js +42 -0
  10. package/dist/assets/{business-transactions-single-BSpUxu3D.js → business-transactions-single-DJqvqlMO.js} +1 -1
  11. package/dist/assets/{business-trip-Bv1KXZPV.js → business-trip-BXAjI1mO.js} +1 -1
  12. package/dist/assets/charges-filters-r7FjeZLc.js +1 -0
  13. package/dist/assets/{charges-ledger-validation-BcO2gYIK.js → charges-ledger-validation-CFc_zJFO.js} +1 -1
  14. package/dist/assets/{chart-Cg9PgOtK.js → chart-C5LuxRSv.js} +1 -1
  15. package/dist/assets/{data-table-pagination-aFECnXu9.js → data-table-pagination-g2cF8_uB.js} +1 -1
  16. package/dist/assets/{editable-business-trip-qxr12Yt0.js → editable-business-trip-DyQsUJjl.js} +2 -2
  17. package/dist/assets/index-2g0J28D2.js +1 -0
  18. package/dist/assets/{index-CuIQrA5k.js → index-6-J73JPC.js} +1 -1
  19. package/dist/assets/index-B5j8Fmt8.js +1 -0
  20. package/dist/assets/index-BSBV6pv2.js +9 -0
  21. package/dist/assets/{index-BFERto_M.js → index-BSg8ocop.js} +2 -2
  22. package/dist/assets/{index-B2_BlI07.js → index-Bpef3vuz.js} +2 -2
  23. package/dist/assets/index-BziuNiXZ.js +1 -0
  24. package/dist/assets/{index-BDsbL_Kg.js → index-C0OGGVXE.js} +2 -2
  25. package/dist/assets/index-C7KKktQ5.js +1 -0
  26. package/dist/assets/{index-Dh7go2PN.js → index-C99l-a0m.js} +1 -1
  27. package/dist/assets/index-CE3kLG2a.js +1 -0
  28. package/dist/assets/{index-GwuBtmZO.js → index-CJdrnxxy.js} +2 -2
  29. package/dist/assets/{index-DOrujuWI.js → index-CN2818Wt.js} +223 -233
  30. package/dist/assets/index-CjDRKTTf.js +1 -0
  31. package/dist/assets/index-CzzfC-dD.css +1 -0
  32. package/dist/assets/index-D2xdQJAx.js +1 -0
  33. package/dist/assets/index-DGvh10a7.js +1 -0
  34. package/dist/assets/index-DLI6Z9VU.js +1 -0
  35. package/dist/assets/index-DXL1qPt9.js +1 -0
  36. package/dist/assets/{index-BYyokQ1_.js → index-Is4LJW4y.js} +2 -2
  37. package/dist/assets/{index-aW2k8oov.js → index-S6eSocQH.js} +7 -7
  38. package/dist/assets/index-T-JiUNDA.js +1 -0
  39. package/dist/assets/{index-BNXVR9UW.js → index-gBZ-7Z07.js} +1 -1
  40. package/dist/assets/{index-M3bu66aM.js → index-o_W5PWRq.js} +6 -6
  41. package/dist/assets/{index.es-Bg5AVQ7i.js → index.es-DZgQ4YcV.js} +1 -1
  42. package/dist/assets/{issue-document-B4ZlDg1e.js → issue-document-D-JoFiMZ.js} +1 -1
  43. package/dist/assets/login-page-DsHy5amR.js +1 -0
  44. package/dist/assets/{missing-info-charges-Ck_n6s6i.js → missing-info-charges-CPnjfjfW.js} +1 -1
  45. package/dist/assets/page-not-found-CYmXF0n2.js +1 -0
  46. package/dist/assets/{pencil-CpRI4b2y.js → pencil-BUwX_eHK.js} +1 -1
  47. package/dist/assets/report-commentary-row-Bpvvtwpd.js +1 -0
  48. package/dist/assets/save-Ck68rh6u.js +11 -0
  49. package/dist/assets/similar-charges-by-business-modal-bUtiFfAw.js +1 -0
  50. package/dist/assets/{sub-Daf_8shG.js → sub-C2zifPY5.js} +1 -1
  51. package/dist/assets/subMonths-uUfFviD2.js +1 -0
  52. package/dist/index.html +2 -2
  53. package/package.json +1 -1
  54. package/src/components/business/business-header.tsx +2 -21
  55. package/src/components/businesses/all-businesses-row.tsx +87 -0
  56. package/src/components/businesses/cells/hebrew-name.tsx +31 -0
  57. package/src/components/businesses/cells/index.ts +2 -0
  58. package/src/components/businesses/cells/name.tsx +31 -0
  59. package/src/components/businesses/index.tsx +39 -56
  60. package/src/components/common/forms/business-card.tsx +234 -0
  61. package/src/components/common/forms/index.ts +1 -0
  62. package/src/components/common/modals/insert-business.tsx +51 -659
  63. package/src/gql/gql.ts +30 -6
  64. package/src/gql/graphql.ts +30 -7
  65. package/src/providers/urql-client.ts +2 -3
  66. package/src/providers/urql-error-handler.ts +27 -0
  67. package/src/providers/urql.tsx +5 -0
  68. package/src/router/loaders/business-loader.ts +2 -4
  69. package/src/router/loaders/charge-loader.ts +2 -4
  70. package/dist/assets/accountant-approvals-Be5zF2_c.js +0 -1
  71. package/dist/assets/building-2-B-rX_xIy.js +0 -6
  72. package/dist/assets/business-CNkwr3_Z.js +0 -32
  73. package/dist/assets/business-header-Drp6R3A0.js +0 -1
  74. package/dist/assets/charges-filters-CpNNXLPU.js +0 -1
  75. package/dist/assets/index--C4Az49P.js +0 -1
  76. package/dist/assets/index-A1xHtOLI.js +0 -1
  77. package/dist/assets/index-BrsmDzry.js +0 -1
  78. package/dist/assets/index-C3AdAlvt.js +0 -9
  79. package/dist/assets/index-CD53YLxm.js +0 -1
  80. package/dist/assets/index-CY2CmEvR.js +0 -1
  81. package/dist/assets/index-Crn32InT.js +0 -1
  82. package/dist/assets/index-CyI9xpFz.js +0 -1
  83. package/dist/assets/index-DStQk1jH.js +0 -1
  84. package/dist/assets/index-DtE5Y1ZB.css +0 -1
  85. package/dist/assets/index-DwzG6-LK.js +0 -1
  86. package/dist/assets/index-bPa4yB-5.js +0 -1
  87. package/dist/assets/index-vHwCjYSy.js +0 -1
  88. package/dist/assets/login-page-v4kHU0le.js +0 -1
  89. package/dist/assets/page-not-found-TWjTuISB.js +0 -1
  90. package/dist/assets/report-commentary-row-BNIW-6Qg.js +0 -1
  91. package/dist/assets/save-CjLvssI1.js +0 -6
  92. package/dist/assets/subMonths-DIZWyi5T.js +0 -1
@@ -1,18 +1,18 @@
1
1
  import { useCallback, useContext, useEffect, useState, type ReactElement } from 'react';
2
2
  import { Loader2, PanelTopClose, PanelTopOpen } from 'lucide-react';
3
- import { useNavigate } from 'react-router-dom';
4
3
  import { useQuery } from 'urql';
5
- import { Tooltip } from '@mantine/core';
6
- import { AllBusinessesForScreenDocument } from '../../gql/graphql.js';
4
+ import { Table, Tooltip } from '@mantine/core';
5
+ import {
6
+ AllBusinessesForScreenDocument,
7
+ type AllBusinessesForScreenQuery,
8
+ } from '../../gql/graphql.js';
7
9
  import { useUrlQuery } from '../../hooks/use-url-query.js';
8
10
  import { cn } from '../../lib/utils.js';
9
11
  import { FiltersContext } from '../../providers/filters-context.js';
10
- import { ROUTES } from '../../router/routes.js';
11
- import { BusinessHeader } from '../business/business-header.js';
12
- import { InsertBusiness, MergeBusinessesButton } from '../common/index.js';
12
+ import { MergeBusinessesButton } from '../common/index.js';
13
13
  import { PageLayout } from '../layout/page-layout.js';
14
14
  import { Button } from '../ui/button.js';
15
- import { Checkbox } from '../ui/checkbox.js';
15
+ import { AllBusinessesRow } from './all-businesses-row.js';
16
16
  import { BusinessesFilters } from './businesses-filters.js';
17
17
 
18
18
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
@@ -24,7 +24,7 @@ import { BusinessesFilters } from './businesses-filters.js';
24
24
  id
25
25
  name
26
26
  ... on LtdFinancialEntity {
27
- ...BusinessHeader
27
+ ...AllBusinessesRowFields
28
28
  }
29
29
  }
30
30
  pageInfo {
@@ -43,7 +43,7 @@ export const Businesses = (): ReactElement => {
43
43
  );
44
44
  const { setFiltersContext } = useContext(FiltersContext);
45
45
 
46
- const [{ data, fetching }, refetch] = useQuery({
46
+ const [{ data, fetching }] = useQuery({
47
47
  query: AllBusinessesForScreenDocument,
48
48
  variables: {
49
49
  page: activePage,
@@ -52,7 +52,6 @@ export const Businesses = (): ReactElement => {
52
52
  },
53
53
  });
54
54
 
55
- const navigate = useNavigate();
56
55
  const [mergeSelectedBusinesses, setMergeSelectedBusinesses] = useState<
57
56
  Array<{ id: string; onChange: () => void }>
58
57
  >([]);
@@ -122,57 +121,41 @@ export const Businesses = (): ReactElement => {
122
121
  const selectedIds = new Set(mergeSelectedBusinesses.map(selected => selected.id));
123
122
 
124
123
  return (
125
- <PageLayout
126
- title={`Businesses (${businesses.length})`}
127
- description="All businesses"
128
- headerActions={
129
- <div className="flex items-center py-4 gap-4">
130
- <InsertBusiness description="" onAdd={() => refetch()} />
131
- </div>
132
- }
133
- >
124
+ <PageLayout title={`Businesses (${businesses.length})`} description="All businesses">
134
125
  {fetching ? (
135
126
  <div className="flex flex-row justify-center">
136
127
  <Loader2 className={cn('h-10 w-10 animate-spin mr-2')} />
137
128
  </div>
138
129
  ) : (
139
- <div className="space-y-2">
140
- {businesses.map(business => (
141
- <div
142
- key={business.id}
143
- className="group relative border rounded-lg hover:shadow-md transition-shadow"
144
- >
145
- <div className="flex items-center gap-3">
146
- <div className="pl-4 flex items-center">
147
- <Checkbox
148
- checked={selectedIds.has(business.id)}
149
- onCheckedChange={() => {
150
- toggleMergeBusiness(business.id, () => {});
151
- }}
152
- className="h-4 w-4 rounded border-gray-300 cursor-pointer"
153
- onClick={(e): void => e.stopPropagation()}
154
- />
155
- </div>
156
- <div
157
- role="button"
158
- tabIndex={0}
159
- className="flex-1 cursor-pointer"
160
- onClick={(): void => {
161
- navigate(ROUTES.BUSINESSES.DETAIL(business.id));
162
- }}
163
- onKeyDown={(e): void => {
164
- if (e.key === 'Enter' || e.key === ' ') {
165
- e.preventDefault();
166
- navigate(ROUTES.BUSINESSES.DETAIL(business.id));
167
- }
168
- }}
169
- >
170
- <BusinessHeader data={business} />
171
- </div>
172
- </div>
173
- </div>
174
- ))}
175
- </div>
130
+ <Table striped highlightOnHover>
131
+ <thead className="sticky top-0 z-20">
132
+ <tr className="px-10 py-10 title-font tracking-wider font-medium text-gray-900 text-sm bg-gray-100 rounded-tl rounded-bl">
133
+ <th>Name</th>
134
+ <th>Hebrew Name</th>
135
+ <th>More Info</th>
136
+ </tr>
137
+ </thead>
138
+ <tbody>
139
+ {businesses.map(business => (
140
+ <AllBusinessesRow
141
+ key={business.id}
142
+ data={
143
+ business as Extract<
144
+ NonNullable<AllBusinessesForScreenQuery['allBusinesses']>['nodes'][number],
145
+ { __typename: 'LtdFinancialEntity' }
146
+ >
147
+ }
148
+ isAllOpened={isAllOpened}
149
+ toggleMergeBusiness={
150
+ toggleMergeBusiness
151
+ ? (onChange: () => void): void => toggleMergeBusiness(business.id, onChange)
152
+ : undefined
153
+ }
154
+ isSelectedForMerge={selectedIds.has(business.id) ?? false}
155
+ />
156
+ ))}
157
+ </tbody>
158
+ </Table>
176
159
  )}
177
160
  </PageLayout>
178
161
  );
@@ -0,0 +1,234 @@
1
+ import { useContext, useEffect, useState, type ReactElement } from 'react';
2
+ import { useForm, type SubmitHandler } from 'react-hook-form';
3
+ import { useQuery, type UseQueryExecute } from 'urql';
4
+ import { Loader } from '@mantine/core';
5
+ import {
6
+ FetchBusinessDocument,
7
+ type AllBusinessesRowFieldsFragment,
8
+ type FetchBusinessQuery,
9
+ type UpdateBusinessInput,
10
+ } from '../../../gql/graphql.js';
11
+ import { relevantDataPicker, type MakeBoolean } from '../../../helpers/index.js';
12
+ import { useUpdateBusiness } from '../../../hooks/use-update-business.js';
13
+ import { UserContext } from '../../../providers/user-provider.js';
14
+ import { Form } from '../../ui/form.js';
15
+ import { CopyToClipboardButton, SimilarChargesByBusinessModal } from '../index.js';
16
+ import { ModifyBusinessFields } from './modify-business-fields.js';
17
+
18
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
19
+ /* GraphQL */ `
20
+ query FetchBusiness($id: UUID!) {
21
+ business(id: $id) {
22
+ __typename
23
+ id
24
+ ... on LtdFinancialEntity {
25
+ country
26
+ address
27
+ email
28
+ exemptDealer
29
+ governmentId
30
+ hebrewName
31
+ name
32
+ phoneNumber
33
+ sortCode {
34
+ id
35
+ key
36
+ name
37
+ }
38
+ taxCategory {
39
+ id
40
+ name
41
+ }
42
+ website
43
+ suggestions {
44
+ phrases
45
+ tags {
46
+ id
47
+ name
48
+ }
49
+ description
50
+ emails
51
+ emailListener {
52
+ internalEmailLinks
53
+ emailBody
54
+ attachments
55
+ }
56
+ }
57
+ optionalVAT
58
+ irsCode
59
+ pcn874RecordType
60
+ }
61
+ }
62
+ }
63
+ `;
64
+
65
+ interface Props {
66
+ businessID: string;
67
+ updateBusiness: React.Dispatch<React.SetStateAction<AllBusinessesRowFieldsFragment>>;
68
+ }
69
+
70
+ export function BusinessCard({ businessID, updateBusiness }: Props): ReactElement {
71
+ const [business, setBusiness] = useState<
72
+ Extract<FetchBusinessQuery['business'], { __typename: 'LtdFinancialEntity' }> | undefined
73
+ >(undefined);
74
+
75
+ // Form business data handle
76
+ const [{ data, fetching: businessFetching }, refetchBusiness] = useQuery({
77
+ query: FetchBusinessDocument,
78
+ variables: {
79
+ id: businessID,
80
+ },
81
+ });
82
+
83
+ useEffect(() => {
84
+ if (businessFetching) {
85
+ setBusiness(undefined);
86
+ } else if (data && data.business.__typename === 'LtdFinancialEntity') {
87
+ setBusiness(data.business);
88
+ }
89
+ }, [data, businessFetching]);
90
+
91
+ // on every business successful fetch, update the business in the parent component
92
+ useEffect(() => {
93
+ if (business) {
94
+ updateBusiness(business);
95
+ }
96
+ }, [business, updateBusiness]);
97
+
98
+ return (
99
+ <div className="flex flex-col gap-5">
100
+ {business ? (
101
+ <BusinessCardContent business={business} refetchBusiness={refetchBusiness} />
102
+ ) : businessFetching ? (
103
+ <Loader className="flex self-center my-5" color="dark" size="xl" variant="dots" />
104
+ ) : (
105
+ <p>Error fetching extended information for this business</p>
106
+ )}
107
+ </div>
108
+ );
109
+ }
110
+
111
+ interface ContentProps {
112
+ business: Extract<FetchBusinessQuery['business'], { __typename: 'LtdFinancialEntity' }>;
113
+ refetchBusiness: UseQueryExecute;
114
+ }
115
+
116
+ function BusinessCardContent({ business, refetchBusiness }: ContentProps): ReactElement {
117
+ const [fetching, setFetching] = useState<boolean>(false);
118
+ const [similarChargesOpen, setSimilarChargesOpen] = useState(false);
119
+ const [similarChargesData, setSimilarChargesData] = useState<
120
+ | {
121
+ tagIds?: { id: string }[];
122
+ description?: string;
123
+ }
124
+ | undefined
125
+ >(undefined);
126
+ const { userContext } = useContext(UserContext);
127
+
128
+ const { updateBusiness: updateDbBusiness, fetching: isBusinessLoading } = useUpdateBusiness();
129
+
130
+ // form management
131
+ const formManager = useForm<UpdateBusinessInput>({
132
+ defaultValues: {
133
+ ...business,
134
+ taxCategory: business.taxCategory?.id,
135
+ sortCode: business?.sortCode?.key,
136
+ irsCode: business?.irsCode,
137
+ },
138
+ });
139
+
140
+ const {
141
+ handleSubmit: handleBusinessSubmit,
142
+ formState: { dirtyFields: dirtyBusinessFields },
143
+ } = formManager;
144
+
145
+ const onBusinessSubmit: SubmitHandler<UpdateBusinessInput> = async data => {
146
+ if (!business || !userContext?.context.adminBusinessId) {
147
+ return;
148
+ }
149
+
150
+ const dataToUpdate = relevantDataPicker(data, dirtyBusinessFields as MakeBoolean<typeof data>);
151
+ if (dataToUpdate && Object.keys(dataToUpdate).length > 0) {
152
+ dataToUpdate.sortCode &&= parseInt(dataToUpdate.sortCode.toString());
153
+
154
+ if (dataToUpdate.suggestions?.tags) {
155
+ dataToUpdate.suggestions.tags = dataToUpdate.suggestions.tags.map(tag => ({ id: tag.id }));
156
+ }
157
+ if (dataToUpdate.suggestions?.phrases) {
158
+ dataToUpdate.suggestions.phrases = data.suggestions!.phrases!.map(phrase => phrase);
159
+ }
160
+ if (dataToUpdate.suggestions?.emails) {
161
+ dataToUpdate.suggestions.emails = data.suggestions!.emails!.map(email => email);
162
+ }
163
+ if (dataToUpdate.suggestions?.emailListener) {
164
+ dataToUpdate.suggestions.emailListener.attachments &&=
165
+ data.suggestions!.emailListener!.attachments!.map(type => type);
166
+ dataToUpdate.suggestions.emailListener.internalEmailLinks &&=
167
+ data.suggestions!.emailListener!.internalEmailLinks!.map(link => link);
168
+ }
169
+
170
+ await updateDbBusiness({
171
+ businessId: business.id,
172
+ ownerId: userContext.context.adminBusinessId,
173
+ fields: dataToUpdate,
174
+ });
175
+
176
+ if (dataToUpdate.suggestions?.tags?.length || dataToUpdate.suggestions?.description) {
177
+ setSimilarChargesData({
178
+ tagIds: dataToUpdate.suggestions?.tags?.map(t => ({ id: t.id })),
179
+ description: dataToUpdate.suggestions?.description ?? undefined,
180
+ });
181
+ setSimilarChargesOpen(true);
182
+ } else {
183
+ refetchBusiness();
184
+ }
185
+ }
186
+ };
187
+
188
+ return (
189
+ <>
190
+ {fetching && (
191
+ <Loader className="flex self-center my-5" color="dark" size="xl" variant="dots" />
192
+ )}
193
+ <div className="flex flex-row gap-5">
194
+ <Form {...formManager}>
195
+ <form onSubmit={handleBusinessSubmit(onBusinessSubmit)}>
196
+ <div className="flex flex-row mx-3 pt-3 sm:text-1xl gap-10">
197
+ <h1 className="sm:text-2xl font-small text-gray-900">Edit Business:</h1>
198
+ <div className="flex flex-row gap-2">
199
+ ID: {business.id}
200
+ <CopyToClipboardButton content={business.id} />
201
+ </div>
202
+ </div>
203
+ <div className="flex-row px-10 h-max justify-start block">
204
+ <ModifyBusinessFields
205
+ isInsert={false}
206
+ formManager={formManager}
207
+ setFetching={setFetching}
208
+ />
209
+ </div>
210
+ <div className="mt-10 mb-5 flex justify-center gap-5">
211
+ <button
212
+ type="submit"
213
+ onClick={(): (() => Promise<void>) => handleBusinessSubmit(onBusinessSubmit)}
214
+ className="mt-8 text-white bg-indigo-500 border-0 py-2 px-8 focus:outline-hidden hover:bg-indigo-600 rounded-sm text-lg"
215
+ disabled={isBusinessLoading || Object.keys(dirtyBusinessFields).length === 0}
216
+ >
217
+ Update
218
+ </button>
219
+ </div>
220
+ </form>
221
+ </Form>
222
+ </div>
223
+
224
+ <SimilarChargesByBusinessModal
225
+ businessId={business.id}
226
+ tagIds={similarChargesData?.tagIds}
227
+ description={similarChargesData?.description}
228
+ open={similarChargesOpen}
229
+ onOpenChange={setSimilarChargesOpen}
230
+ onClose={refetchBusiness}
231
+ />
232
+ </>
233
+ );
234
+ }
@@ -1,3 +1,4 @@
1
+ export * from './business-card.js';
1
2
  export * from './edit-charge.js';
2
3
  export * from './edit-document.js';
3
4
  export * from './edit-misc-expense.js';