@trustless-work/blocks 0.0.5 → 0.0.7

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 (44) hide show
  1. package/README.md +39 -13
  2. package/bin/index.js +1128 -1137
  3. package/package.json +44 -44
  4. package/templates/escrows/details/EscrowDetailDialog.tsx +3 -3
  5. package/templates/escrows/details/GeneralInformation.tsx +2 -2
  6. package/templates/escrows/details/SuccessReleaseDialog.tsx +2 -3
  7. package/templates/escrows/details/useDetailsEscrow.ts +2 -2
  8. package/templates/escrows/escrows-by-role/cards/EscrowsCards.tsx +42 -16
  9. package/templates/escrows/escrows-by-role/table/EscrowsTable.tsx +45 -18
  10. package/templates/escrows/escrows-by-role/useEscrowsByRole.shared.ts +39 -25
  11. package/templates/escrows/escrows-by-signer/cards/EscrowsCards.tsx +33 -16
  12. package/templates/escrows/escrows-by-signer/table/EscrowsTable.tsx +34 -17
  13. package/templates/escrows/escrows-by-signer/useEscrowsBySigner.shared.ts +38 -25
  14. package/templates/escrows/single-release/approve-milestone/button/ApproveMilestone.tsx +14 -1
  15. package/templates/escrows/single-release/approve-milestone/dialog/ApproveMilestone.tsx +1 -1
  16. package/templates/escrows/single-release/approve-milestone/form/ApproveMilestone.tsx +1 -1
  17. package/templates/escrows/single-release/approve-milestone/shared/useApproveMilestone.ts +14 -1
  18. package/templates/escrows/single-release/change-milestone-status/button/ChangeMilestoneStatus.tsx +16 -1
  19. package/templates/escrows/single-release/change-milestone-status/dialog/ChangeMilestoneStatus.tsx +1 -1
  20. package/templates/escrows/single-release/change-milestone-status/form/ChangeMilestoneStatus.tsx +1 -1
  21. package/templates/escrows/single-release/change-milestone-status/shared/useChangeMilestoneStatus.ts +14 -1
  22. package/templates/escrows/single-release/dispute-escrow/button/DisputeEscrow.tsx +13 -1
  23. package/templates/escrows/single-release/fund-escrow/button/FundEscrow.tsx +13 -1
  24. package/templates/escrows/single-release/fund-escrow/shared/useFundEscrow.ts +14 -1
  25. package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +17 -1
  26. package/templates/escrows/single-release/release-escrow/button/ReleaseEscrow.tsx +15 -3
  27. package/templates/escrows/single-release/resolve-dispute/button/ResolveDispute.tsx +13 -1
  28. package/templates/escrows/single-release/resolve-dispute/dialog/ResolveDispute.tsx +1 -1
  29. package/templates/escrows/single-release/resolve-dispute/shared/useResolveDispute.ts +14 -1
  30. package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +224 -211
  31. package/templates/handle-errors/handle.ts +16 -0
  32. package/templates/helpers/format.helper.ts +31 -0
  33. package/templates/helpers/useCopy.ts +5 -0
  34. package/templates/{escrows/escrow-context → providers}/EscrowAmountProvider.tsx +3 -0
  35. package/templates/providers/EscrowDialogsProvider.tsx +61 -0
  36. package/templates/{escrows/escrow-context → providers}/EscrowProvider.tsx +30 -0
  37. package/templates/providers/ReactQueryClientProvider.tsx +17 -1
  38. package/templates/tanstack/useEscrowsByRoleQuery.ts +14 -0
  39. package/templates/tanstack/useEscrowsBySignerQuery.ts +13 -0
  40. package/templates/tanstack/useEscrowsMutations.ts +36 -0
  41. package/templates/wallet-kit/trustlines.ts +7 -0
  42. package/templates/wallet-kit/validators.ts +6 -0
  43. package/templates/wallet-kit/wallet-kit.ts +13 -0
  44. package/templates/escrows/escrow-context/EscrowDialogsProvider.tsx +0 -108
package/package.json CHANGED
@@ -1,44 +1,44 @@
1
- {
2
- "name": "@trustless-work/blocks",
3
- "version": "0.0.5",
4
- "author": "Trustless Work",
5
- "keywords": [
6
- "react",
7
- "hooks",
8
- "trustless work",
9
- "escrow",
10
- "api",
11
- "blocks",
12
- "ui",
13
- "components",
14
- "helpers",
15
- "templates",
16
- "blockchain"
17
- ],
18
- "bin": {
19
- "trustless-work": "bin/index.js"
20
- },
21
- "scripts": {
22
- "test": "echo \"Error: no test specified\" && exit 1",
23
- "build": "node -e \"console.log('Skipping build: no src to bundle')\"",
24
- "prepublishOnly": "npm run build"
25
- },
26
- "description": "",
27
- "type": "module",
28
- "publishConfig": {
29
- "access": "public"
30
- },
31
- "repository": {
32
- "type": "git",
33
- "url": "https://github.com/Trustless-Work/react-library-trustless-work-blocks.git"
34
- },
35
- "files": [
36
- "bin",
37
- "templates",
38
- "README.md"
39
- ],
40
- "license": "MIT",
41
- "engines": {
42
- "node": ">=18.17"
43
- }
44
- }
1
+ {
2
+ "name": "@trustless-work/blocks",
3
+ "version": "0.0.7",
4
+ "author": "Trustless Work",
5
+ "keywords": [
6
+ "react",
7
+ "hooks",
8
+ "trustless work",
9
+ "escrow",
10
+ "api",
11
+ "blocks",
12
+ "ui",
13
+ "components",
14
+ "helpers",
15
+ "templates",
16
+ "blockchain"
17
+ ],
18
+ "bin": {
19
+ "trustless-work": "bin/index.js"
20
+ },
21
+ "scripts": {
22
+ "test": "echo \"Error: no test specified\" && exit 1",
23
+ "build": "node -e \"console.log('Skipping build: no src to bundle')\"",
24
+ "prepublishOnly": "npm run build"
25
+ },
26
+ "description": "",
27
+ "type": "module",
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/Trustless-Work/react-library-trustless-work-blocks.git"
34
+ },
35
+ "files": [
36
+ "bin",
37
+ "templates",
38
+ "README.md"
39
+ ],
40
+ "license": "MIT",
41
+ "engines": {
42
+ "node": ">=18.17"
43
+ }
44
+ }
@@ -12,7 +12,7 @@ import useEscrowDetailDialog from "./useDetailsEscrow";
12
12
  import Link from "next/link";
13
13
  import { Card } from "__UI_BASE__/card";
14
14
  import { Info, Users, ListChecks } from "lucide-react";
15
- import { useEscrowDialogs } from "../../escrow-context/EscrowDialogsProvider";
15
+ import { useEscrowDialogs } from "@/components/tw-blocks/providers/EscrowDialogsProvider";
16
16
  import type {
17
17
  GetEscrowsFromIndexerResponse as Escrow,
18
18
  Role,
@@ -21,7 +21,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "__UI_BASE__/tabs";
21
21
  import { Milestones } from "./Milestones";
22
22
  import { Entities } from "./Entities";
23
23
  import { GeneralInformation } from "./GeneralInformation";
24
- import { useEscrowContext } from "../../escrow-context/EscrowProvider";
24
+ import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
25
25
  import SuccessReleaseDialog from "./SuccessReleaseDialog";
26
26
 
27
27
  interface EscrowDetailDialogProps {
@@ -151,4 +151,4 @@ const EscrowDetailDialog = ({
151
151
  );
152
152
  };
153
153
 
154
- export default EscrowDetailDialog;
154
+ export default React.memo(EscrowDetailDialog);
@@ -19,12 +19,12 @@ import { Actions, roleActions } from "./Actions";
19
19
  import type {
20
20
  DialogStates,
21
21
  StatusStates,
22
- } from "../../escrow-context/EscrowDialogsProvider";
22
+ } from "@/components/tw-blocks/providers/EscrowDialogsProvider";
23
23
  import {
24
24
  GetEscrowsFromIndexerResponse,
25
25
  Role,
26
26
  } from "@trustless-work/escrow/types";
27
- import { useEscrowAmountContext } from "../../escrow-context/EscrowAmountProvider";
27
+ import { useEscrowAmountContext } from "@/components/tw-blocks/providers/EscrowAmountProvider";
28
28
  import { StatisticsCard } from "./StatisticsCard";
29
29
  import {
30
30
  formatAddress,
@@ -8,10 +8,9 @@ import {
8
8
  DialogHeader,
9
9
  DialogTitle,
10
10
  } from "__UI_BASE__/dialog";
11
- import { Card } from "__UI_BASE__/card";
12
11
  import EntityCard from "./EntityCard";
13
- import { useEscrowContext } from "../../escrow-context/EscrowProvider";
14
- import { useEscrowAmountContext } from "../../escrow-context/EscrowAmountProvider";
12
+ import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
13
+ import { useEscrowAmountContext } from "@/components/tw-blocks/providers/EscrowAmountProvider";
15
14
  import { CircleCheckBig } from "lucide-react";
16
15
 
17
16
  interface SuccessReleaseDialogProps {
@@ -1,8 +1,8 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { GetEscrowsFromIndexerResponse as Escrow } from "@trustless-work/escrow/types";
3
3
  import { useWalletContext } from "@/components/tw-blocks/wallet-kit/WalletProvider";
4
- import { useEscrowContext } from "../../escrow-context/EscrowProvider";
5
- import { useEscrowAmountContext } from "../../escrow-context/EscrowAmountProvider";
4
+ import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
5
+ import { useEscrowAmountContext } from "@/components/tw-blocks/providers/EscrowAmountProvider";
6
6
 
7
7
  interface EscrowDetailDialogProps {
8
8
  setIsDialogOpen: (value: boolean) => void;
@@ -22,8 +22,8 @@ import {
22
22
  import { useEscrowsByRole } from "../useEscrowsByRole.shared";
23
23
  import Filters from "./Filters";
24
24
  import EscrowDetailDialog from "../details/EscrowDetailDialog";
25
- import { useEscrowContext } from "../../escrow-context/EscrowProvider";
26
- import { useEscrowDialogs } from "../../escrow-context/EscrowDialogsProvider";
25
+ import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
26
+ import { useEscrowDialogs } from "@/components/tw-blocks/providers/EscrowDialogsProvider";
27
27
  import {
28
28
  formatCurrency,
29
29
  formatTimestamp,
@@ -75,6 +75,17 @@ export function EscrowsByRoleCards() {
75
75
 
76
76
  const dialogStates = useEscrowDialogs();
77
77
 
78
+ const handleRefresh = React.useCallback(() => {
79
+ void refetch();
80
+ }, [refetch]);
81
+
82
+ const setRoleStable = React.useCallback(
83
+ (v: Role | undefined) => {
84
+ if (v) setRole(v);
85
+ },
86
+ [setRole]
87
+ );
88
+
78
89
  function allMilestonesReleasedOrResolved(
79
90
  milestones: MultiReleaseMilestone[]
80
91
  ) {
@@ -119,7 +130,20 @@ export function EscrowsByRoleCards() {
119
130
  dialogStates.second.setIsOpen(true);
120
131
  };
121
132
 
122
- const activeRole: Role[] = role.split(",") as Role[];
133
+ /**
134
+ * Based on the provided roles -> https://docs.trustlesswork.com/trustless-work/technology-overview/roles-in-trustless-work
135
+ *
136
+ * You must pass one or more roles according to requirements. Acctually it's coming from the select filter.
137
+ *
138
+ * For example:
139
+ * - If the user is a freelancer, you must pass the "serviceProvider" and "receiver" role
140
+ *
141
+ * Depending of the role, you'll have different actions buttons
142
+ */
143
+ const activeRole: Role[] = React.useMemo(
144
+ () => role.split(",") as Role[],
145
+ [role]
146
+ );
123
147
 
124
148
  return (
125
149
  <>
@@ -140,19 +164,19 @@ export function EscrowsByRoleCards() {
140
164
  setEngagementId={setEngagementId}
141
165
  setIsActive={setIsActive}
142
166
  setValidateOnChain={setValidateOnChain}
143
- setType={(v) => setType(v as typeof type)}
144
- setStatus={(v) => setStatus(v as typeof status)}
167
+ setType={setType}
168
+ setStatus={setStatus}
145
169
  setMinAmount={setMinAmount}
146
170
  setMaxAmount={setMaxAmount}
147
171
  setDateRange={setDateRange}
148
- setRole={(v) => setRole(v as Role)}
172
+ setRole={setRoleStable}
149
173
  onClearFilters={onClearFilters}
150
- onRefresh={() => refetch()}
174
+ onRefresh={handleRefresh}
151
175
  isRefreshing={isFetching}
152
176
  orderBy={orderBy}
153
177
  orderDirection={orderDirection}
154
- setOrderBy={(v) => setOrderBy(v)}
155
- setOrderDirection={(v) => setOrderDirection(v)}
178
+ setOrderBy={setOrderBy}
179
+ setOrderDirection={setOrderDirection}
156
180
  />
157
181
 
158
182
  <div className="w-full py-2 sm:py-4">
@@ -492,14 +516,16 @@ export function EscrowsByRoleCards() {
492
516
  </div>
493
517
 
494
518
  {/* Dialog */}
495
- <EscrowDetailDialog
496
- activeRole={activeRole}
497
- isDialogOpen={dialogStates.second.isOpen}
498
- setIsDialogOpen={dialogStates.second.setIsOpen}
499
- setSelectedEscrow={setSelectedEscrow}
500
- />
519
+ {dialogStates.second.isOpen ? (
520
+ <EscrowDetailDialog
521
+ activeRole={activeRole}
522
+ isDialogOpen={dialogStates.second.isOpen}
523
+ setIsDialogOpen={dialogStates.second.setIsOpen}
524
+ setSelectedEscrow={setSelectedEscrow}
525
+ />
526
+ ) : null}
501
527
  </>
502
528
  );
503
529
  }
504
530
 
505
- export default EscrowsByRoleCards;
531
+ export default React.memo(EscrowsByRoleCards);
@@ -25,8 +25,8 @@ import {
25
25
  import { FileX, Loader2, Wallet, RefreshCw, AlertTriangle } from "lucide-react";
26
26
  import Filters from "./Filters";
27
27
  import EscrowDetailDialog from "../details/EscrowDetailDialog";
28
- import { useEscrowDialogs } from "../../escrow-context/EscrowDialogsProvider";
29
- import { useEscrowContext } from "../../escrow-context/EscrowProvider";
28
+ import { useEscrowDialogs } from "@/components/tw-blocks/providers/EscrowDialogsProvider";
29
+ import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
30
30
  import { useEscrowsByRole } from "../useEscrowsByRole.shared";
31
31
  import { formatTimestamp } from "../../../helpers/format.helper";
32
32
 
@@ -75,6 +75,17 @@ export function EscrowsByRoleTable() {
75
75
  const dialogStates = useEscrowDialogs();
76
76
  const { setSelectedEscrow } = useEscrowContext();
77
77
 
78
+ const handleRefresh = React.useCallback(() => {
79
+ void refetch();
80
+ }, [refetch]);
81
+
82
+ const setRoleStable = React.useCallback(
83
+ (v: Role | undefined) => {
84
+ if (v) setRole(v);
85
+ },
86
+ [setRole]
87
+ );
88
+
78
89
  const columns = React.useMemo<ColumnDef<Escrow>[]>(
79
90
  () => [
80
91
  {
@@ -187,7 +198,7 @@ export function EscrowsByRoleTable() {
187
198
  ),
188
199
  },
189
200
  ],
190
- []
201
+ [dialogStates.second.setIsOpen, setSelectedEscrow]
191
202
  );
192
203
 
193
204
  const table = useReactTable({
@@ -202,7 +213,21 @@ export function EscrowsByRoleTable() {
202
213
  enableSortingRemoval: true,
203
214
  });
204
215
 
205
- const activeRole: Role[] = role.split(",") as Role[];
216
+ /**
217
+ * Based on the provided roles -> https://docs.trustlesswork.com/trustless-work/technology-overview/roles-in-trustless-work
218
+ *
219
+ * You must pass one or more roles according to requirements. Acctually it's coming from the select filter.
220
+ *
221
+ * For example:
222
+ * - If the user is a freelancer, you must pass the "serviceProvider" and "receiver" role
223
+ *
224
+ * Depending of the role, you'll have different actions buttons
225
+ */
226
+ const activeRole: Role[] = React.useMemo(
227
+ () => role.split(",") as Role[],
228
+ [role]
229
+ );
230
+
206
231
  const escrows = data ?? [];
207
232
 
208
233
  return (
@@ -224,19 +249,19 @@ export function EscrowsByRoleTable() {
224
249
  setEngagementId={setEngagementId}
225
250
  setIsActive={setIsActive}
226
251
  setValidateOnChain={setValidateOnChain}
227
- setType={(v) => setType(v as typeof type)}
228
- setStatus={(v) => setStatus(v as typeof status)}
252
+ setType={setType}
253
+ setStatus={setStatus}
229
254
  setMinAmount={setMinAmount}
230
255
  setMaxAmount={setMaxAmount}
231
256
  setDateRange={setDateRange}
232
- setRole={(v) => setRole(v as Role)}
257
+ setRole={setRoleStable}
233
258
  onClearFilters={onClearFilters}
234
- onRefresh={() => refetch()}
259
+ onRefresh={handleRefresh}
235
260
  isRefreshing={isFetching}
236
261
  orderBy={orderBy}
237
262
  orderDirection={orderDirection}
238
- setOrderBy={(v) => setOrderBy(v)}
239
- setOrderDirection={(v) => setOrderDirection(v)}
263
+ setOrderBy={setOrderBy}
264
+ setOrderDirection={setOrderDirection}
240
265
  />
241
266
 
242
267
  <Card className="w-full py-2 sm:py-4">
@@ -335,7 +360,7 @@ export function EscrowsByRoleTable() {
335
360
  <Button
336
361
  variant="outline"
337
362
  size="sm"
338
- onClick={() => refetch()}
363
+ onClick={handleRefresh}
339
364
  >
340
365
  <RefreshCw className="h-4 w-4 mr-2" />
341
366
  Retry
@@ -414,14 +439,16 @@ export function EscrowsByRoleTable() {
414
439
  </div>
415
440
 
416
441
  {/* Dialog */}
417
- <EscrowDetailDialog
418
- activeRole={activeRole}
419
- isDialogOpen={dialogStates.second.isOpen}
420
- setIsDialogOpen={dialogStates.second.setIsOpen}
421
- setSelectedEscrow={setSelectedEscrow}
422
- />
442
+ {dialogStates.second.isOpen ? (
443
+ <EscrowDetailDialog
444
+ activeRole={activeRole}
445
+ isDialogOpen={dialogStates.second.isOpen}
446
+ setIsDialogOpen={dialogStates.second.setIsOpen}
447
+ setSelectedEscrow={setSelectedEscrow}
448
+ />
449
+ ) : null}
423
450
  </>
424
451
  );
425
452
  }
426
453
 
427
- export default EscrowsByRoleTable;
454
+ export default React.memo(EscrowsByRoleTable);
@@ -108,8 +108,8 @@ export function useEscrowsByRole() {
108
108
  // eslint-disable-next-line react-hooks/exhaustive-deps
109
109
  }, []);
110
110
 
111
- const debouncedSearchParams = useDebouncedValue(
112
- {
111
+ const stableSearchParams = React.useMemo(
112
+ () => ({
113
113
  page,
114
114
  orderBy,
115
115
  orderDirection,
@@ -126,10 +126,29 @@ export function useEscrowsByRole() {
126
126
  : undefined,
127
127
  endDate: dateRange.to ? endOfDay(dateRange.to).toISOString() : undefined,
128
128
  role,
129
- },
130
- 200
129
+ }),
130
+ [
131
+ page,
132
+ orderBy,
133
+ orderDirection,
134
+ debouncedTitle,
135
+ debouncedEngagementId,
136
+ isActive,
137
+ validateOnChain,
138
+ type,
139
+ status,
140
+ debouncedMinAmount,
141
+ debouncedMaxAmount,
142
+ dateRange.from,
143
+ dateRange.to,
144
+ role,
145
+ ]
131
146
  );
132
147
 
148
+ const debouncedSearchParams = useDebouncedValue(stableSearchParams, 200);
149
+
150
+ const lastQueryStringRef = React.useRef("");
151
+
133
152
  React.useEffect(() => {
134
153
  if (!pathname) return;
135
154
  const qp = new URLSearchParams();
@@ -145,8 +164,10 @@ export function useEscrowsByRole() {
145
164
  qp.set("engagementId", debouncedSearchParams.engagementId);
146
165
  qp.set("isActive", String(debouncedSearchParams.isActive));
147
166
  qp.set("validateOnChain", String(debouncedSearchParams.validateOnChain));
148
- if (type && type !== "all") qp.set("type", type);
149
- if (status && status !== "all") qp.set("status", status);
167
+ if (debouncedSearchParams.type && debouncedSearchParams.type !== "all")
168
+ qp.set("type", debouncedSearchParams.type);
169
+ if (debouncedSearchParams.status && debouncedSearchParams.status !== "all")
170
+ qp.set("status", debouncedSearchParams.status);
150
171
  if (debouncedSearchParams.minAmount)
151
172
  qp.set("minAmount", String(debouncedSearchParams.minAmount));
152
173
  if (debouncedSearchParams.maxAmount)
@@ -158,25 +179,12 @@ export function useEscrowsByRole() {
158
179
  if (debouncedSearchParams.role)
159
180
  qp.set("role", String(debouncedSearchParams.role));
160
181
 
161
- router.replace(`${pathname}?${qp.toString()}`);
162
- }, [
163
- pathname,
164
- router,
165
- debouncedSearchParams.page,
166
- debouncedSearchParams.orderBy,
167
- debouncedSearchParams.orderDirection,
168
- debouncedSearchParams.title,
169
- debouncedSearchParams.engagementId,
170
- debouncedSearchParams.isActive,
171
- debouncedSearchParams.validateOnChain,
172
- type,
173
- status,
174
- debouncedSearchParams.minAmount,
175
- debouncedSearchParams.maxAmount,
176
- debouncedSearchParams.startDate,
177
- debouncedSearchParams.endDate,
178
- debouncedSearchParams.role,
179
- ]);
182
+ const newQs = qp.toString();
183
+ if (lastQueryStringRef.current !== newQs) {
184
+ lastQueryStringRef.current = newQs;
185
+ router.replace(`${pathname}?${newQs}`);
186
+ }
187
+ }, [pathname, router, debouncedSearchParams]);
180
188
 
181
189
  const formattedRangeLabel = React.useMemo(() => {
182
190
  if (!dateRange?.from && !dateRange?.to) return "Date range";
@@ -234,6 +242,12 @@ export function useEscrowsByRole() {
234
242
  dateRange,
235
243
  ]);
236
244
 
245
+ /**
246
+ * Call the query to get the escrows from the Trustless Work Indexer
247
+ *
248
+ * @param params - The parameters for the query
249
+ * @returns The query result
250
+ */
237
251
  const query = useEscrowsByRoleQuery(params);
238
252
  const nextPageQuery = useEscrowsByRoleQuery({ ...params, page: page + 1 });
239
253
 
@@ -21,8 +21,8 @@ import {
21
21
  RefreshCw,
22
22
  FileX,
23
23
  } from "lucide-react";
24
- import { useEscrowContext } from "../../escrow-context/EscrowProvider";
25
- import { useEscrowDialogs } from "../../escrow-context/EscrowDialogsProvider";
24
+ import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
25
+ import { useEscrowDialogs } from "@/components/tw-blocks/providers/EscrowDialogsProvider";
26
26
  import EscrowDetailDialog from "../../escrows-by-role/details/EscrowDetailDialog";
27
27
  import { formatTimestamp } from "../../../helpers/format.helper";
28
28
 
@@ -70,6 +70,10 @@ export function EscrowsBySignerCards() {
70
70
 
71
71
  const dialogStates = useEscrowDialogs();
72
72
 
73
+ const handleRefresh = React.useCallback(() => {
74
+ void refetch();
75
+ }, [refetch]);
76
+
73
77
  const formatCurrency = (value: number, currency: string) => {
74
78
  return `${currency} ${value.toFixed(2)}`;
75
79
  };
@@ -116,7 +120,18 @@ export function EscrowsBySignerCards() {
116
120
  dialogStates.second.setIsOpen(true);
117
121
  };
118
122
 
119
- const activeRole: Role[] = ["approver"];
123
+ /**
124
+ * Based on the provided roles -> https://docs.trustlesswork.com/trustless-work/technology-overview/roles-in-trustless-work
125
+ *
126
+ * You must pass one or more roles according to requirements
127
+ *
128
+ * For example:
129
+ * - If the user is a freelancer, you must pass the "serviceProvider" and "receiver" role
130
+ *
131
+ * Depending of the role, you'll have different actions buttons
132
+ */
133
+ const activeRole: Role[] = React.useMemo(() => ["approver"] as Role[], []);
134
+
120
135
  const escrows: Escrow[] = data ?? [];
121
136
 
122
137
  return (
@@ -137,18 +152,18 @@ export function EscrowsBySignerCards() {
137
152
  setEngagementId={setEngagementId}
138
153
  setIsActive={setIsActive}
139
154
  setValidateOnChain={setValidateOnChain}
140
- setType={(v) => setType(v as typeof type)}
141
- setStatus={(v) => setStatus(v as typeof status)}
155
+ setType={setType}
156
+ setStatus={setStatus}
142
157
  setMinAmount={setMinAmount}
143
158
  setMaxAmount={setMaxAmount}
144
159
  setDateRange={setDateRange}
145
160
  onClearFilters={onClearFilters}
146
- onRefresh={() => refetch()}
161
+ onRefresh={handleRefresh}
147
162
  isRefreshing={isFetching}
148
163
  orderBy={orderBy}
149
164
  orderDirection={orderDirection}
150
- setOrderBy={(v) => setOrderBy(v)}
151
- setOrderDirection={(v) => setOrderDirection(v)}
165
+ setOrderBy={setOrderBy}
166
+ setOrderDirection={setOrderDirection}
152
167
  />
153
168
 
154
169
  <div className="w-full py-2 sm:py-4">
@@ -224,7 +239,7 @@ export function EscrowsBySignerCards() {
224
239
  An error occurred while loading the information. Please try
225
240
  again.
226
241
  </p>
227
- <Button variant="outline" size="sm" onClick={() => refetch()}>
242
+ <Button variant="outline" size="sm" onClick={handleRefresh}>
228
243
  <RefreshCw className="h-4 w-4 mr-2" />
229
244
  Retry
230
245
  </Button>
@@ -491,14 +506,16 @@ export function EscrowsBySignerCards() {
491
506
  </div>
492
507
 
493
508
  {/* Dialog */}
494
- <EscrowDetailDialog
495
- activeRole={activeRole}
496
- isDialogOpen={dialogStates.second.isOpen}
497
- setIsDialogOpen={dialogStates.second.setIsOpen}
498
- setSelectedEscrow={setSelectedEscrow}
499
- />
509
+ {dialogStates.second.isOpen ? (
510
+ <EscrowDetailDialog
511
+ activeRole={activeRole}
512
+ isDialogOpen={dialogStates.second.isOpen}
513
+ setIsDialogOpen={dialogStates.second.setIsOpen}
514
+ setSelectedEscrow={setSelectedEscrow}
515
+ />
516
+ ) : null}
500
517
  </>
501
518
  );
502
519
  }
503
520
 
504
- export default EscrowsBySignerCards;
521
+ export default React.memo(EscrowsBySignerCards);