@trustless-work/blocks 0.0.6 → 0.0.8

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 (72) hide show
  1. package/README.md +39 -13
  2. package/bin/index.js +1596 -1137
  3. package/package.json +44 -44
  4. package/templates/escrows/details/Actions.tsx +144 -149
  5. package/templates/escrows/details/Entities.tsx +1 -1
  6. package/templates/escrows/details/EntityCard.tsx +1 -3
  7. package/templates/escrows/details/EscrowDetailDialog.tsx +18 -18
  8. package/templates/escrows/details/GeneralInformation.tsx +20 -23
  9. package/templates/escrows/details/MilestoneCard.tsx +46 -47
  10. package/templates/escrows/details/MilestoneDetailDialog.tsx +1 -2
  11. package/templates/escrows/details/Milestones.tsx +0 -5
  12. package/templates/escrows/details/SuccessReleaseDialog.tsx +6 -9
  13. package/templates/escrows/details/useDetailsEscrow.ts +2 -2
  14. package/templates/escrows/escrows-by-role/cards/EscrowsCards.tsx +111 -60
  15. package/templates/escrows/escrows-by-role/cards/Filters.tsx +3 -5
  16. package/templates/escrows/escrows-by-role/table/EscrowsTable.tsx +36 -38
  17. package/templates/escrows/escrows-by-role/table/Filters.tsx +3 -5
  18. package/templates/escrows/escrows-by-role/useEscrowsByRole.shared.ts +33 -25
  19. package/templates/escrows/escrows-by-signer/cards/EscrowsCards.tsx +107 -67
  20. package/templates/escrows/escrows-by-signer/cards/Filters.tsx +3 -5
  21. package/templates/escrows/escrows-by-signer/table/EscrowsTable.tsx +28 -38
  22. package/templates/escrows/escrows-by-signer/table/Filters.tsx +3 -5
  23. package/templates/escrows/escrows-by-signer/useEscrowsBySigner.shared.ts +32 -25
  24. package/templates/escrows/multi-release/dispute-milestone/button/DisputeEscrow.tsx +98 -0
  25. package/templates/escrows/multi-release/initialize-escrow/dialog/InitializeEscrow.tsx +528 -0
  26. package/templates/escrows/multi-release/initialize-escrow/form/InitializeEscrow.tsx +506 -0
  27. package/templates/escrows/multi-release/initialize-escrow/shared/schema.ts +179 -0
  28. package/templates/escrows/multi-release/initialize-escrow/shared/useInitializeEscrow.ts +175 -0
  29. package/templates/escrows/multi-release/release-milestone/button/ReleaseEscrow.tsx +116 -0
  30. package/templates/escrows/multi-release/resolve-dispute/button/ResolveDispute.tsx +122 -0
  31. package/templates/escrows/multi-release/resolve-dispute/dialog/ResolveDispute.tsx +178 -0
  32. package/templates/escrows/multi-release/resolve-dispute/form/ResolveDispute.tsx +156 -0
  33. package/templates/escrows/multi-release/resolve-dispute/shared/schema.ts +85 -0
  34. package/templates/escrows/multi-release/resolve-dispute/shared/useResolveDispute.ts +105 -0
  35. package/templates/escrows/multi-release/update-escrow/dialog/UpdateEscrow.tsx +471 -0
  36. package/templates/escrows/multi-release/update-escrow/form/UpdateEscrow.tsx +449 -0
  37. package/templates/escrows/multi-release/update-escrow/shared/schema.ts +152 -0
  38. package/templates/escrows/multi-release/update-escrow/shared/useUpdateEscrow.ts +254 -0
  39. package/templates/escrows/{single-release → single-multi-release}/approve-milestone/button/ApproveMilestone.tsx +21 -8
  40. package/templates/escrows/{single-release → single-multi-release}/approve-milestone/dialog/ApproveMilestone.tsx +4 -4
  41. package/templates/escrows/{single-release → single-multi-release}/approve-milestone/form/ApproveMilestone.tsx +4 -4
  42. package/templates/escrows/{single-release/approve-milestone/shared → single-multi-release/approve-milestone}/useApproveMilestone.ts +17 -17
  43. package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/button/ChangeMilestoneStatus.tsx +5 -5
  44. package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/dialog/ChangeMilestoneStatus.tsx +5 -5
  45. package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/form/ChangeMilestoneStatus.tsx +4 -4
  46. package/templates/escrows/{single-release/change-milestone-status/shared → single-multi-release/change-milestone-status}/useChangeMilestoneStatus.ts +2 -2
  47. package/templates/escrows/{single-release → single-multi-release}/fund-escrow/button/FundEscrow.tsx +4 -4
  48. package/templates/escrows/{single-release → single-multi-release}/fund-escrow/dialog/FundEscrow.tsx +3 -3
  49. package/templates/escrows/{single-release → single-multi-release}/fund-escrow/form/FundEscrow.tsx +3 -3
  50. package/templates/escrows/{single-release/fund-escrow/shared → single-multi-release/fund-escrow}/useFundEscrow.ts +2 -2
  51. package/templates/escrows/single-release/dispute-escrow/button/DisputeEscrow.tsx +3 -3
  52. package/templates/escrows/single-release/initialize-escrow/dialog/InitializeEscrow.tsx +14 -6
  53. package/templates/escrows/single-release/initialize-escrow/form/InitializeEscrow.tsx +14 -6
  54. package/templates/escrows/single-release/initialize-escrow/shared/schema.ts +0 -57
  55. package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +43 -2
  56. package/templates/escrows/single-release/release-escrow/button/ReleaseEscrow.tsx +5 -5
  57. package/templates/escrows/single-release/resolve-dispute/button/ResolveDispute.tsx +4 -4
  58. package/templates/escrows/single-release/resolve-dispute/dialog/ResolveDispute.tsx +4 -7
  59. package/templates/escrows/single-release/resolve-dispute/form/ResolveDispute.tsx +2 -2
  60. package/templates/escrows/single-release/resolve-dispute/shared/useResolveDispute.ts +15 -2
  61. package/templates/escrows/single-release/update-escrow/dialog/UpdateEscrow.tsx +2 -2
  62. package/templates/escrows/single-release/update-escrow/form/UpdateEscrow.tsx +2 -2
  63. package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +229 -224
  64. package/templates/{escrows/escrow-context → providers}/EscrowDialogsProvider.tsx +1 -3
  65. package/templates/{escrows/escrow-context → providers}/EscrowProvider.tsx +27 -4
  66. package/templates/providers/ReactQueryClientProvider.tsx +3 -1
  67. package/templates/providers/TrustlessWork.tsx +1 -1
  68. package/templates/escrows/details/ProgressEscrow.tsx +0 -191
  69. /package/templates/escrows/{single-release/approve-milestone/shared → single-multi-release/approve-milestone}/schema.ts +0 -0
  70. /package/templates/escrows/{single-release/change-milestone-status/shared → single-multi-release/change-milestone-status}/schema.ts +0 -0
  71. /package/templates/escrows/{single-release/fund-escrow/shared → single-multi-release/fund-escrow}/schema.ts +0 -0
  72. /package/templates/{escrows/escrow-context → providers}/EscrowAmountProvider.tsx +0 -0
@@ -6,7 +6,6 @@ import { Button } from "__UI_BASE__/button";
6
6
  import type {
7
7
  GetEscrowsFromIndexerResponse as Escrow,
8
8
  MultiReleaseMilestone,
9
- Role,
10
9
  } from "@trustless-work/escrow/types";
11
10
  import {
12
11
  ColumnDef,
@@ -23,14 +22,14 @@ import {
23
22
  TableRow,
24
23
  } from "__UI_BASE__/table";
25
24
  import { FileX, Loader2, Wallet, RefreshCw, AlertTriangle } from "lucide-react";
26
- import Filters from "./Filters";
25
+ import { Filters } from "./Filters";
27
26
  import { useEscrowsBySigner } from "../useEscrowsBySigner.shared";
28
- import { useEscrowDialogs } from "../../escrow-context/EscrowDialogsProvider";
29
- import { useEscrowContext } from "../../escrow-context/EscrowProvider";
30
- import EscrowDetailDialog from "../../escrows-by-role/details/EscrowDetailDialog";
27
+ import { useEscrowDialogs } from "@/components/tw-blocks/providers/EscrowDialogsProvider";
28
+ import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
29
+ import { EscrowDetailDialog } from "../details/EscrowDetailDialog";
31
30
  import { formatTimestamp } from "../../../helpers/format.helper";
32
31
 
33
- export function EscrowsBySignerTable() {
32
+ export const EscrowsBySignerTable = () => {
34
33
  const {
35
34
  walletAddress,
36
35
  data,
@@ -73,6 +72,10 @@ export function EscrowsBySignerTable() {
73
72
  const dialogStates = useEscrowDialogs();
74
73
  const { setSelectedEscrow } = useEscrowContext();
75
74
 
75
+ const handleRefresh = React.useCallback(() => {
76
+ void refetch();
77
+ }, [refetch]);
78
+
76
79
  const columns = React.useMemo<ColumnDef<Escrow>[]>(
77
80
  () => [
78
81
  {
@@ -184,7 +187,7 @@ export function EscrowsBySignerTable() {
184
187
  ),
185
188
  },
186
189
  ],
187
- []
190
+ [dialogStates.second.setIsOpen, setSelectedEscrow]
188
191
  );
189
192
 
190
193
  const table = useReactTable({
@@ -199,18 +202,6 @@ export function EscrowsBySignerTable() {
199
202
  enableSortingRemoval: true,
200
203
  });
201
204
 
202
- /**
203
- * Based on the provided roles -> https://docs.trustlesswork.com/trustless-work/technology-overview/roles-in-trustless-work
204
- *
205
- * You must pass one or more roles according to requirements
206
- *
207
- * For example:
208
- * - If the user is a freelancer, you must pass the "serviceProvider" and "receiver" role
209
- *
210
- * Depending of the role, you'll have different actions buttons
211
- */
212
- const activeRole: Role[] = ["approver"];
213
-
214
205
  const escrows = data ?? [];
215
206
 
216
207
  return (
@@ -231,18 +222,18 @@ export function EscrowsBySignerTable() {
231
222
  setEngagementId={setEngagementId}
232
223
  setIsActive={setIsActive}
233
224
  setValidateOnChain={setValidateOnChain}
234
- setType={(v) => setType(v as typeof type)}
235
- setStatus={(v) => setStatus(v as typeof status)}
225
+ setType={setType}
226
+ setStatus={setStatus}
236
227
  setMinAmount={setMinAmount}
237
228
  setMaxAmount={setMaxAmount}
238
229
  setDateRange={setDateRange}
239
230
  onClearFilters={onClearFilters}
240
- onRefresh={() => refetch()}
231
+ onRefresh={handleRefresh}
241
232
  isRefreshing={isFetching}
242
233
  orderBy={orderBy}
243
234
  orderDirection={orderDirection}
244
- setOrderBy={(v) => setOrderBy(v)}
245
- setOrderDirection={(v) => setOrderDirection(v)}
235
+ setOrderBy={setOrderBy}
236
+ setOrderDirection={setOrderDirection}
246
237
  />
247
238
 
248
239
  <Card className="w-full py-2 sm:py-4">
@@ -257,8 +248,8 @@ export function EscrowsBySignerTable() {
257
248
  const className =
258
249
  typeof header.column.columnDef.meta === "object" &&
259
250
  header.column.columnDef.meta &&
260
- "className" in (header.column.columnDef.meta as any)
261
- ? (header.column.columnDef.meta as any).className
251
+ "className" in header.column.columnDef.meta
252
+ ? header.column.columnDef.meta.className
262
253
  : "";
263
254
  return (
264
255
  <TableHead
@@ -341,7 +332,7 @@ export function EscrowsBySignerTable() {
341
332
  <Button
342
333
  variant="outline"
343
334
  size="sm"
344
- onClick={() => refetch()}
335
+ onClick={handleRefresh}
345
336
  >
346
337
  <RefreshCw className="h-4 w-4 mr-2" />
347
338
  Retry
@@ -371,8 +362,8 @@ export function EscrowsBySignerTable() {
371
362
  const className =
372
363
  typeof cell.column.columnDef.meta === "object" &&
373
364
  cell.column.columnDef.meta &&
374
- "className" in (cell.column.columnDef.meta as any)
375
- ? (cell.column.columnDef.meta as any).className
365
+ "className" in cell.column.columnDef.meta
366
+ ? (cell.column.columnDef.meta.className as string)
376
367
  : "";
377
368
  return (
378
369
  <TableCell key={cell.id} className={className}>
@@ -420,14 +411,13 @@ export function EscrowsBySignerTable() {
420
411
  </div>
421
412
 
422
413
  {/* Dialog */}
423
- <EscrowDetailDialog
424
- activeRole={activeRole}
425
- isDialogOpen={dialogStates.second.isOpen}
426
- setIsDialogOpen={dialogStates.second.setIsOpen}
427
- setSelectedEscrow={setSelectedEscrow}
428
- />
414
+ {dialogStates.second.isOpen ? (
415
+ <EscrowDetailDialog
416
+ isDialogOpen={dialogStates.second.isOpen}
417
+ setIsDialogOpen={dialogStates.second.setIsOpen}
418
+ setSelectedEscrow={setSelectedEscrow}
419
+ />
420
+ ) : null}
429
421
  </>
430
422
  );
431
- }
432
-
433
- export default EscrowsBySignerTable;
423
+ };
@@ -81,7 +81,7 @@ type FiltersProps = {
81
81
  setOrderDirection: (v: "asc" | "desc") => void;
82
82
  };
83
83
 
84
- function Filters({
84
+ export const Filters = ({
85
85
  title,
86
86
  engagementId,
87
87
  isActive,
@@ -108,7 +108,7 @@ function Filters({
108
108
  onRefresh,
109
109
  setOrderBy,
110
110
  setOrderDirection,
111
- }: FiltersProps) {
111
+ }: FiltersProps) => {
112
112
  return (
113
113
  <div className="w-full bg-card/50 backdrop-blur-sm border border-border/50 rounded-lg p-4 shadow-sm">
114
114
  {/* Header Section */}
@@ -384,6 +384,4 @@ function Filters({
384
384
  </div>
385
385
  </div>
386
386
  );
387
- }
388
-
389
- export default React.memo(Filters);
387
+ };
@@ -101,8 +101,8 @@ export function useEscrowsBySigner() {
101
101
  // eslint-disable-next-line react-hooks/exhaustive-deps
102
102
  }, []);
103
103
 
104
- const debouncedSearchParams = useDebouncedValue(
105
- {
104
+ const stableSearchParams = React.useMemo(
105
+ () => ({
106
106
  page,
107
107
  orderBy,
108
108
  orderDirection,
@@ -118,10 +118,28 @@ export function useEscrowsBySigner() {
118
118
  ? startOfDay(dateRange.from).toISOString()
119
119
  : undefined,
120
120
  endDate: dateRange.to ? endOfDay(dateRange.to).toISOString() : undefined,
121
- },
122
- 200
121
+ }),
122
+ [
123
+ page,
124
+ orderBy,
125
+ orderDirection,
126
+ debouncedTitle,
127
+ debouncedEngagementId,
128
+ isActive,
129
+ validateOnChain,
130
+ type,
131
+ status,
132
+ debouncedMinAmount,
133
+ debouncedMaxAmount,
134
+ dateRange.from,
135
+ dateRange.to,
136
+ ]
123
137
  );
124
138
 
139
+ const debouncedSearchParams = useDebouncedValue(stableSearchParams, 200);
140
+
141
+ const lastQueryStringRef = React.useRef("");
142
+
125
143
  React.useEffect(() => {
126
144
  if (!pathname) return;
127
145
  const qp = new URLSearchParams();
@@ -137,8 +155,10 @@ export function useEscrowsBySigner() {
137
155
  qp.set("engagementId", debouncedSearchParams.engagementId);
138
156
  qp.set("isActive", String(debouncedSearchParams.isActive));
139
157
  qp.set("validateOnChain", String(debouncedSearchParams.validateOnChain));
140
- if (type && type !== "all") qp.set("type", type);
141
- if (status && status !== "all") qp.set("status", status);
158
+ if (debouncedSearchParams.type && debouncedSearchParams.type !== "all")
159
+ qp.set("type", debouncedSearchParams.type);
160
+ if (debouncedSearchParams.status && debouncedSearchParams.status !== "all")
161
+ qp.set("status", debouncedSearchParams.status);
142
162
  if (debouncedSearchParams.minAmount)
143
163
  qp.set("minAmount", String(debouncedSearchParams.minAmount));
144
164
  if (debouncedSearchParams.maxAmount)
@@ -147,25 +167,12 @@ export function useEscrowsBySigner() {
147
167
  qp.set("startDate", String(debouncedSearchParams.startDate));
148
168
  if (debouncedSearchParams.endDate)
149
169
  qp.set("endDate", String(debouncedSearchParams.endDate));
150
-
151
- router.replace(`${pathname}?${qp.toString()}`);
152
- }, [
153
- pathname,
154
- router,
155
- debouncedSearchParams.page,
156
- debouncedSearchParams.orderBy,
157
- debouncedSearchParams.orderDirection,
158
- debouncedSearchParams.title,
159
- debouncedSearchParams.engagementId,
160
- debouncedSearchParams.isActive,
161
- debouncedSearchParams.validateOnChain,
162
- type,
163
- status,
164
- debouncedSearchParams.minAmount,
165
- debouncedSearchParams.maxAmount,
166
- debouncedSearchParams.startDate,
167
- debouncedSearchParams.endDate,
168
- ]);
170
+ const newQs = qp.toString();
171
+ if (lastQueryStringRef.current !== newQs) {
172
+ lastQueryStringRef.current = newQs;
173
+ router.replace(`${pathname}?${newQs}`);
174
+ }
175
+ }, [pathname, router, debouncedSearchParams]);
169
176
 
170
177
  const formattedRangeLabel = React.useMemo(() => {
171
178
  if (!dateRange?.from && !dateRange?.to) return "Date range";
@@ -0,0 +1,98 @@
1
+ import * as React from "react";
2
+ import { Button } from "__UI_BASE__/button";
3
+ import { useEscrowsMutations } from "@/components/tw-blocks/tanstack/useEscrowsMutations";
4
+ import { useWalletContext } from "@/components/tw-blocks/wallet-kit/WalletProvider";
5
+ import {
6
+ MultiReleaseStartDisputePayload,
7
+ MultiReleaseMilestone,
8
+ } from "@trustless-work/escrow/types";
9
+ import { toast } from "sonner";
10
+ import {
11
+ ErrorResponse,
12
+ handleError,
13
+ } from "@/components/tw-blocks/handle-errors/handle";
14
+ import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
15
+ import { Loader2 } from "lucide-react";
16
+
17
+ type DisputeEscrowButtonProps = {
18
+ milestoneIndex: number | string;
19
+ };
20
+
21
+ export const DisputeEscrowButton = ({
22
+ milestoneIndex,
23
+ }: DisputeEscrowButtonProps) => {
24
+ const { startDispute } = useEscrowsMutations();
25
+ const { selectedEscrow, updateEscrow } = useEscrowContext();
26
+ const { walletAddress } = useWalletContext();
27
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
28
+
29
+ async function handleClick() {
30
+ try {
31
+ setIsSubmitting(true);
32
+
33
+ /**
34
+ * Create the payload for the dispute escrow mutation
35
+ *
36
+ * @returns The payload for the dispute escrow mutation
37
+ */
38
+ const payload: MultiReleaseStartDisputePayload = {
39
+ contractId: selectedEscrow?.contractId || "",
40
+ signer: walletAddress || "",
41
+ milestoneIndex: String(milestoneIndex),
42
+ };
43
+
44
+ /**
45
+ * Call the dispute escrow mutation
46
+ *
47
+ * @param payload - The payload for the dispute escrow mutation
48
+ * @param type - The type of the escrow
49
+ * @param address - The address of the escrow
50
+ */
51
+ await startDispute.mutateAsync({
52
+ payload,
53
+ type: "multi-release",
54
+ address: walletAddress || "",
55
+ });
56
+
57
+ toast.success("Escrow disputed successfully");
58
+
59
+ updateEscrow({
60
+ ...selectedEscrow,
61
+ milestones: selectedEscrow?.milestones.map((milestone, index) => {
62
+ if (index === Number(milestoneIndex)) {
63
+ return {
64
+ ...milestone,
65
+ flags: {
66
+ ...(milestone as MultiReleaseMilestone).flags,
67
+ disputed: true,
68
+ },
69
+ };
70
+ }
71
+ return milestone;
72
+ }),
73
+ });
74
+ } catch (error) {
75
+ toast.error(handleError(error as ErrorResponse).message);
76
+ } finally {
77
+ setIsSubmitting(false);
78
+ }
79
+ }
80
+
81
+ return (
82
+ <Button
83
+ type="button"
84
+ disabled={isSubmitting || !selectedEscrow?.balance}
85
+ onClick={handleClick}
86
+ className="cursor-pointer w-full"
87
+ >
88
+ {isSubmitting ? (
89
+ <div className="flex items-center">
90
+ <Loader2 className="h-5 w-5 animate-spin" />
91
+ <span className="ml-2">Disputing...</span>
92
+ </div>
93
+ ) : (
94
+ "Dispute Milestone"
95
+ )}
96
+ </Button>
97
+ );
98
+ };