@trustless-work/blocks 0.0.6 → 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 (34) 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 +32 -16
  9. package/templates/escrows/escrows-by-role/table/EscrowsTable.tsx +34 -18
  10. package/templates/escrows/escrows-by-role/useEscrowsByRole.shared.ts +33 -25
  11. package/templates/escrows/escrows-by-signer/cards/EscrowsCards.tsx +22 -16
  12. package/templates/escrows/escrows-by-signer/table/EscrowsTable.tsx +23 -17
  13. package/templates/escrows/escrows-by-signer/useEscrowsBySigner.shared.ts +32 -25
  14. package/templates/escrows/single-release/approve-milestone/button/ApproveMilestone.tsx +1 -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 +1 -1
  18. package/templates/escrows/single-release/change-milestone-status/button/ChangeMilestoneStatus.tsx +1 -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 +1 -1
  22. package/templates/escrows/single-release/dispute-escrow/button/DisputeEscrow.tsx +1 -1
  23. package/templates/escrows/single-release/fund-escrow/button/FundEscrow.tsx +1 -1
  24. package/templates/escrows/single-release/fund-escrow/shared/useFundEscrow.ts +1 -1
  25. package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +1 -1
  26. package/templates/escrows/single-release/release-escrow/button/ReleaseEscrow.tsx +3 -3
  27. package/templates/escrows/single-release/resolve-dispute/button/ResolveDispute.tsx +1 -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 +1 -1
  30. package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +224 -224
  31. package/templates/providers/ReactQueryClientProvider.tsx +3 -1
  32. /package/templates/{escrows/escrow-context → providers}/EscrowAmountProvider.tsx +0 -0
  33. /package/templates/{escrows/escrow-context → providers}/EscrowDialogsProvider.tsx +0 -0
  34. /package/templates/{escrows/escrow-context → providers}/EscrowProvider.tsx +0 -0
package/package.json CHANGED
@@ -1,44 +1,44 @@
1
- {
2
- "name": "@trustless-work/blocks",
3
- "version": "0.0.6",
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
  ) {
@@ -129,7 +140,10 @@ export function EscrowsByRoleCards() {
129
140
  *
130
141
  * Depending of the role, you'll have different actions buttons
131
142
  */
132
- const activeRole: Role[] = role.split(",") as Role[];
143
+ const activeRole: Role[] = React.useMemo(
144
+ () => role.split(",") as Role[],
145
+ [role]
146
+ );
133
147
 
134
148
  return (
135
149
  <>
@@ -150,19 +164,19 @@ export function EscrowsByRoleCards() {
150
164
  setEngagementId={setEngagementId}
151
165
  setIsActive={setIsActive}
152
166
  setValidateOnChain={setValidateOnChain}
153
- setType={(v) => setType(v as typeof type)}
154
- setStatus={(v) => setStatus(v as typeof status)}
167
+ setType={setType}
168
+ setStatus={setStatus}
155
169
  setMinAmount={setMinAmount}
156
170
  setMaxAmount={setMaxAmount}
157
171
  setDateRange={setDateRange}
158
- setRole={(v) => setRole(v as Role)}
172
+ setRole={setRoleStable}
159
173
  onClearFilters={onClearFilters}
160
- onRefresh={() => refetch()}
174
+ onRefresh={handleRefresh}
161
175
  isRefreshing={isFetching}
162
176
  orderBy={orderBy}
163
177
  orderDirection={orderDirection}
164
- setOrderBy={(v) => setOrderBy(v)}
165
- setOrderDirection={(v) => setOrderDirection(v)}
178
+ setOrderBy={setOrderBy}
179
+ setOrderDirection={setOrderDirection}
166
180
  />
167
181
 
168
182
  <div className="w-full py-2 sm:py-4">
@@ -502,14 +516,16 @@ export function EscrowsByRoleCards() {
502
516
  </div>
503
517
 
504
518
  {/* Dialog */}
505
- <EscrowDetailDialog
506
- activeRole={activeRole}
507
- isDialogOpen={dialogStates.second.isOpen}
508
- setIsDialogOpen={dialogStates.second.setIsOpen}
509
- setSelectedEscrow={setSelectedEscrow}
510
- />
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}
511
527
  </>
512
528
  );
513
529
  }
514
530
 
515
- 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({
@@ -212,7 +223,10 @@ export function EscrowsByRoleTable() {
212
223
  *
213
224
  * Depending of the role, you'll have different actions buttons
214
225
  */
215
- const activeRole: Role[] = role.split(",") as Role[];
226
+ const activeRole: Role[] = React.useMemo(
227
+ () => role.split(",") as Role[],
228
+ [role]
229
+ );
216
230
 
217
231
  const escrows = data ?? [];
218
232
 
@@ -235,19 +249,19 @@ export function EscrowsByRoleTable() {
235
249
  setEngagementId={setEngagementId}
236
250
  setIsActive={setIsActive}
237
251
  setValidateOnChain={setValidateOnChain}
238
- setType={(v) => setType(v as typeof type)}
239
- setStatus={(v) => setStatus(v as typeof status)}
252
+ setType={setType}
253
+ setStatus={setStatus}
240
254
  setMinAmount={setMinAmount}
241
255
  setMaxAmount={setMaxAmount}
242
256
  setDateRange={setDateRange}
243
- setRole={(v) => setRole(v as Role)}
257
+ setRole={setRoleStable}
244
258
  onClearFilters={onClearFilters}
245
- onRefresh={() => refetch()}
259
+ onRefresh={handleRefresh}
246
260
  isRefreshing={isFetching}
247
261
  orderBy={orderBy}
248
262
  orderDirection={orderDirection}
249
- setOrderBy={(v) => setOrderBy(v)}
250
- setOrderDirection={(v) => setOrderDirection(v)}
263
+ setOrderBy={setOrderBy}
264
+ setOrderDirection={setOrderDirection}
251
265
  />
252
266
 
253
267
  <Card className="w-full py-2 sm:py-4">
@@ -346,7 +360,7 @@ export function EscrowsByRoleTable() {
346
360
  <Button
347
361
  variant="outline"
348
362
  size="sm"
349
- onClick={() => refetch()}
363
+ onClick={handleRefresh}
350
364
  >
351
365
  <RefreshCw className="h-4 w-4 mr-2" />
352
366
  Retry
@@ -425,14 +439,16 @@ export function EscrowsByRoleTable() {
425
439
  </div>
426
440
 
427
441
  {/* Dialog */}
428
- <EscrowDetailDialog
429
- activeRole={activeRole}
430
- isDialogOpen={dialogStates.second.isOpen}
431
- setIsDialogOpen={dialogStates.second.setIsOpen}
432
- setSelectedEscrow={setSelectedEscrow}
433
- />
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}
434
450
  </>
435
451
  );
436
452
  }
437
453
 
438
- 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";
@@ -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
  };
@@ -126,7 +130,7 @@ export function EscrowsBySignerCards() {
126
130
  *
127
131
  * Depending of the role, you'll have different actions buttons
128
132
  */
129
- const activeRole: Role[] = ["approver"];
133
+ const activeRole: Role[] = React.useMemo(() => ["approver"] as Role[], []);
130
134
 
131
135
  const escrows: Escrow[] = data ?? [];
132
136
 
@@ -148,18 +152,18 @@ export function EscrowsBySignerCards() {
148
152
  setEngagementId={setEngagementId}
149
153
  setIsActive={setIsActive}
150
154
  setValidateOnChain={setValidateOnChain}
151
- setType={(v) => setType(v as typeof type)}
152
- setStatus={(v) => setStatus(v as typeof status)}
155
+ setType={setType}
156
+ setStatus={setStatus}
153
157
  setMinAmount={setMinAmount}
154
158
  setMaxAmount={setMaxAmount}
155
159
  setDateRange={setDateRange}
156
160
  onClearFilters={onClearFilters}
157
- onRefresh={() => refetch()}
161
+ onRefresh={handleRefresh}
158
162
  isRefreshing={isFetching}
159
163
  orderBy={orderBy}
160
164
  orderDirection={orderDirection}
161
- setOrderBy={(v) => setOrderBy(v)}
162
- setOrderDirection={(v) => setOrderDirection(v)}
165
+ setOrderBy={setOrderBy}
166
+ setOrderDirection={setOrderDirection}
163
167
  />
164
168
 
165
169
  <div className="w-full py-2 sm:py-4">
@@ -235,7 +239,7 @@ export function EscrowsBySignerCards() {
235
239
  An error occurred while loading the information. Please try
236
240
  again.
237
241
  </p>
238
- <Button variant="outline" size="sm" onClick={() => refetch()}>
242
+ <Button variant="outline" size="sm" onClick={handleRefresh}>
239
243
  <RefreshCw className="h-4 w-4 mr-2" />
240
244
  Retry
241
245
  </Button>
@@ -502,14 +506,16 @@ export function EscrowsBySignerCards() {
502
506
  </div>
503
507
 
504
508
  {/* Dialog */}
505
- <EscrowDetailDialog
506
- activeRole={activeRole}
507
- isDialogOpen={dialogStates.second.isOpen}
508
- setIsDialogOpen={dialogStates.second.setIsOpen}
509
- setSelectedEscrow={setSelectedEscrow}
510
- />
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}
511
517
  </>
512
518
  );
513
519
  }
514
520
 
515
- export default EscrowsBySignerCards;
521
+ export default React.memo(EscrowsBySignerCards);
@@ -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 { useEscrowsBySigner } from "../useEscrowsBySigner.shared";
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 EscrowDetailDialog from "../../escrows-by-role/details/EscrowDetailDialog";
31
31
  import { formatTimestamp } from "../../../helpers/format.helper";
32
32
 
@@ -73,6 +73,10 @@ export function EscrowsBySignerTable() {
73
73
  const dialogStates = useEscrowDialogs();
74
74
  const { setSelectedEscrow } = useEscrowContext();
75
75
 
76
+ const handleRefresh = React.useCallback(() => {
77
+ void refetch();
78
+ }, [refetch]);
79
+
76
80
  const columns = React.useMemo<ColumnDef<Escrow>[]>(
77
81
  () => [
78
82
  {
@@ -184,7 +188,7 @@ export function EscrowsBySignerTable() {
184
188
  ),
185
189
  },
186
190
  ],
187
- []
191
+ [dialogStates.second.setIsOpen, setSelectedEscrow]
188
192
  );
189
193
 
190
194
  const table = useReactTable({
@@ -209,7 +213,7 @@ export function EscrowsBySignerTable() {
209
213
  *
210
214
  * Depending of the role, you'll have different actions buttons
211
215
  */
212
- const activeRole: Role[] = ["approver"];
216
+ const activeRole: Role[] = React.useMemo(() => ["approver"] as Role[], []);
213
217
 
214
218
  const escrows = data ?? [];
215
219
 
@@ -231,18 +235,18 @@ export function EscrowsBySignerTable() {
231
235
  setEngagementId={setEngagementId}
232
236
  setIsActive={setIsActive}
233
237
  setValidateOnChain={setValidateOnChain}
234
- setType={(v) => setType(v as typeof type)}
235
- setStatus={(v) => setStatus(v as typeof status)}
238
+ setType={setType}
239
+ setStatus={setStatus}
236
240
  setMinAmount={setMinAmount}
237
241
  setMaxAmount={setMaxAmount}
238
242
  setDateRange={setDateRange}
239
243
  onClearFilters={onClearFilters}
240
- onRefresh={() => refetch()}
244
+ onRefresh={handleRefresh}
241
245
  isRefreshing={isFetching}
242
246
  orderBy={orderBy}
243
247
  orderDirection={orderDirection}
244
- setOrderBy={(v) => setOrderBy(v)}
245
- setOrderDirection={(v) => setOrderDirection(v)}
248
+ setOrderBy={setOrderBy}
249
+ setOrderDirection={setOrderDirection}
246
250
  />
247
251
 
248
252
  <Card className="w-full py-2 sm:py-4">
@@ -341,7 +345,7 @@ export function EscrowsBySignerTable() {
341
345
  <Button
342
346
  variant="outline"
343
347
  size="sm"
344
- onClick={() => refetch()}
348
+ onClick={handleRefresh}
345
349
  >
346
350
  <RefreshCw className="h-4 w-4 mr-2" />
347
351
  Retry
@@ -420,14 +424,16 @@ export function EscrowsBySignerTable() {
420
424
  </div>
421
425
 
422
426
  {/* Dialog */}
423
- <EscrowDetailDialog
424
- activeRole={activeRole}
425
- isDialogOpen={dialogStates.second.isOpen}
426
- setIsDialogOpen={dialogStates.second.setIsOpen}
427
- setSelectedEscrow={setSelectedEscrow}
428
- />
427
+ {dialogStates.second.isOpen ? (
428
+ <EscrowDetailDialog
429
+ activeRole={activeRole}
430
+ isDialogOpen={dialogStates.second.isOpen}
431
+ setIsDialogOpen={dialogStates.second.setIsOpen}
432
+ setSelectedEscrow={setSelectedEscrow}
433
+ />
434
+ ) : null}
429
435
  </>
430
436
  );
431
437
  }
432
438
 
433
- export default EscrowsBySignerTable;
439
+ export default React.memo(EscrowsBySignerTable);