@trustless-work/blocks 0.0.1

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 (74) hide show
  1. package/README.md +96 -0
  2. package/bin/index.js +1123 -0
  3. package/package.json +44 -0
  4. package/templates/deps.json +29 -0
  5. package/templates/escrows/details/Actions.tsx +149 -0
  6. package/templates/escrows/details/Entities.tsx +48 -0
  7. package/templates/escrows/details/EntityCard.tsx +98 -0
  8. package/templates/escrows/details/EscrowDetailDialog.tsx +154 -0
  9. package/templates/escrows/details/GeneralInformation.tsx +329 -0
  10. package/templates/escrows/details/MilestoneCard.tsx +254 -0
  11. package/templates/escrows/details/MilestoneDetailDialog.tsx +276 -0
  12. package/templates/escrows/details/Milestones.tsx +87 -0
  13. package/templates/escrows/details/ProgressEscrow.tsx +191 -0
  14. package/templates/escrows/details/StatisticsCard.tsx +79 -0
  15. package/templates/escrows/details/SuccessReleaseDialog.tsx +101 -0
  16. package/templates/escrows/details/useDetailsEscrow.ts +126 -0
  17. package/templates/escrows/escrow-context/EscrowAmountProvider.tsx +86 -0
  18. package/templates/escrows/escrow-context/EscrowDialogsProvider.tsx +108 -0
  19. package/templates/escrows/escrow-context/EscrowProvider.tsx +124 -0
  20. package/templates/escrows/escrows-by-role/cards/EscrowsCards.tsx +503 -0
  21. package/templates/escrows/escrows-by-role/cards/Filters.tsx +421 -0
  22. package/templates/escrows/escrows-by-role/table/EscrowsTable.tsx +427 -0
  23. package/templates/escrows/escrows-by-role/table/Filters.tsx +421 -0
  24. package/templates/escrows/escrows-by-role/useEscrowsByRole.shared.ts +336 -0
  25. package/templates/escrows/escrows-by-signer/cards/EscrowsCards.tsx +502 -0
  26. package/templates/escrows/escrows-by-signer/cards/Filters.tsx +389 -0
  27. package/templates/escrows/escrows-by-signer/table/EscrowsTable.tsx +422 -0
  28. package/templates/escrows/escrows-by-signer/table/Filters.tsx +389 -0
  29. package/templates/escrows/escrows-by-signer/useEscrowsBySigner.shared.ts +320 -0
  30. package/templates/escrows/single-release/approve-milestone/button/ApproveMilestone.tsx +78 -0
  31. package/templates/escrows/single-release/approve-milestone/dialog/ApproveMilestone.tsx +102 -0
  32. package/templates/escrows/single-release/approve-milestone/form/ApproveMilestone.tsx +80 -0
  33. package/templates/escrows/single-release/approve-milestone/shared/schema.ts +9 -0
  34. package/templates/escrows/single-release/approve-milestone/shared/useApproveMilestone.ts +67 -0
  35. package/templates/escrows/single-release/change-milestone-status/button/ChangeMilestoneStatus.tsx +78 -0
  36. package/templates/escrows/single-release/change-milestone-status/dialog/ChangeMilestoneStatus.tsx +167 -0
  37. package/templates/escrows/single-release/change-milestone-status/form/ChangeMilestoneStatus.tsx +114 -0
  38. package/templates/escrows/single-release/change-milestone-status/shared/schema.ts +15 -0
  39. package/templates/escrows/single-release/change-milestone-status/shared/useChangeMilestoneStatus.ts +77 -0
  40. package/templates/escrows/single-release/dispute-escrow/button/DisputeEscrow.tsx +68 -0
  41. package/templates/escrows/single-release/fund-escrow/button/FundEscrow.tsx +84 -0
  42. package/templates/escrows/single-release/fund-escrow/dialog/FundEscrow.tsx +77 -0
  43. package/templates/escrows/single-release/fund-escrow/form/FundEscrow.tsx +54 -0
  44. package/templates/escrows/single-release/fund-escrow/shared/schema.ts +10 -0
  45. package/templates/escrows/single-release/fund-escrow/shared/useFundEscrow.ts +66 -0
  46. package/templates/escrows/single-release/initialize-escrow/dialog/InitializeEscrow.tsx +526 -0
  47. package/templates/escrows/single-release/initialize-escrow/form/InitializeEscrow.tsx +504 -0
  48. package/templates/escrows/single-release/initialize-escrow/shared/schema.ts +232 -0
  49. package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +115 -0
  50. package/templates/escrows/single-release/release-escrow/button/ReleaseEscrow.tsx +80 -0
  51. package/templates/escrows/single-release/resolve-dispute/button/ResolveDispute.tsx +94 -0
  52. package/templates/escrows/single-release/resolve-dispute/dialog/ResolveDispute.tsx +123 -0
  53. package/templates/escrows/single-release/resolve-dispute/form/ResolveDispute.tsx +82 -0
  54. package/templates/escrows/single-release/resolve-dispute/shared/schema.ts +82 -0
  55. package/templates/escrows/single-release/resolve-dispute/shared/useResolveDispute.ts +58 -0
  56. package/templates/escrows/single-release/update-escrow/dialog/UpdateEscrow.tsx +485 -0
  57. package/templates/escrows/single-release/update-escrow/form/UpdateEscrow.tsx +463 -0
  58. package/templates/escrows/single-release/update-escrow/shared/schema.ts +139 -0
  59. package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +211 -0
  60. package/templates/handle-errors/errors.enum.ts +6 -0
  61. package/templates/handle-errors/handle.ts +47 -0
  62. package/templates/helpers/format.helper.ts +27 -0
  63. package/templates/helpers/useCopy.ts +13 -0
  64. package/templates/providers/ReactQueryClientProvider.tsx +28 -0
  65. package/templates/providers/TrustlessWork.tsx +30 -0
  66. package/templates/tanstak/useEscrowsByRoleQuery.ts +87 -0
  67. package/templates/tanstak/useEscrowsBySignerQuery.ts +78 -0
  68. package/templates/tanstak/useEscrowsMutations.ts +411 -0
  69. package/templates/wallet-kit/WalletButtons.tsx +116 -0
  70. package/templates/wallet-kit/WalletProvider.tsx +94 -0
  71. package/templates/wallet-kit/trustlines.ts +40 -0
  72. package/templates/wallet-kit/useWallet.ts +77 -0
  73. package/templates/wallet-kit/validators.ts +12 -0
  74. package/templates/wallet-kit/wallet-kit.ts +30 -0
@@ -0,0 +1,336 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { startOfDay, endOfDay, format } from "date-fns";
5
+ import type { DateRange as DayPickerDateRange } from "react-day-picker";
6
+ import { usePathname, useRouter, useSearchParams } from "next/navigation";
7
+ import type { SortingState } from "@tanstack/react-table";
8
+ import { useWalletContext } from "../../wallet-kit/WalletProvider";
9
+ import { useEscrowsByRoleQuery } from "../../tanstak/useEscrowsByRoleQuery";
10
+ import type { GetEscrowsFromIndexerByRoleParams } from "@trustless-work/escrow";
11
+ import { GetEscrowsFromIndexerResponse as Escrow } from "@trustless-work/escrow/types";
12
+
13
+ export type EscrowOrderBy = "createdAt" | "updatedAt" | "amount";
14
+ export type EscrowOrderDirection = "asc" | "desc";
15
+ export type EscrowType = "single-release" | "multi-release" | "all";
16
+ export type EscrowStatus =
17
+ | "working"
18
+ | "pendingRelease"
19
+ | "released"
20
+ | "resolved"
21
+ | "inDispute"
22
+ | "all";
23
+ export type DateRange = DayPickerDateRange;
24
+
25
+ export function useEscrowsByRole() {
26
+ const { walletAddress } = useWalletContext();
27
+ const router = useRouter();
28
+ const pathname = usePathname();
29
+ const searchParams = useSearchParams();
30
+
31
+ const [page, setPage] = React.useState<number>(1);
32
+ const [orderBy, setOrderBy] = React.useState<EscrowOrderBy>("createdAt");
33
+ const [orderDirection, setOrderDirection] =
34
+ React.useState<EscrowOrderDirection>("desc");
35
+ const [sorting, setSorting] = React.useState<SortingState>([]);
36
+ const [title, setTitle] = React.useState<string>("");
37
+ const [engagementId, setEngagementId] = React.useState<string>("");
38
+ const [isActive, setIsActive] = React.useState<boolean>(true);
39
+ const [validateOnChain, setValidateOnChain] = React.useState<boolean>(true);
40
+ const [type, setType] = React.useState<EscrowType>("all");
41
+ const [status, setStatus] = React.useState<EscrowStatus>("all");
42
+ const [minAmount, setMinAmount] = React.useState<string>("");
43
+ const [maxAmount, setMaxAmount] = React.useState<string>("");
44
+ const [dateRange, setDateRange] = React.useState<DateRange>({
45
+ from: undefined,
46
+ to: undefined,
47
+ });
48
+ const [role, setRole] =
49
+ React.useState<GetEscrowsFromIndexerByRoleParams["role"]>("approver");
50
+
51
+ function useDebouncedValue<T>(value: T, delayMs: number) {
52
+ const [debounced, setDebounced] = React.useState<T>(value);
53
+ React.useEffect(() => {
54
+ const id = setTimeout(() => setDebounced(value), delayMs);
55
+ return () => clearTimeout(id);
56
+ }, [value, delayMs]);
57
+ return debounced;
58
+ }
59
+
60
+ const debouncedTitle = useDebouncedValue(title, 400);
61
+ const debouncedEngagementId = useDebouncedValue(engagementId, 400);
62
+ const debouncedMinAmount = useDebouncedValue(minAmount, 400);
63
+ const debouncedMaxAmount = useDebouncedValue(maxAmount, 400);
64
+
65
+ React.useEffect(() => {
66
+ if (!searchParams) return;
67
+ const qp = new URLSearchParams(searchParams.toString());
68
+ const qpPage = Number(qp.get("page") || 1);
69
+ const qpOrderBy = (qp.get("orderBy") as EscrowOrderBy) || "createdAt";
70
+ const qpOrderDir =
71
+ (qp.get("orderDirection") as EscrowOrderDirection) || "desc";
72
+ const qpTitle = qp.get("title") || "";
73
+ const qpEng = qp.get("engagementId") || "";
74
+ const qpActive = qp.get("isActive");
75
+ const qpValidateOnChain = qp.get("validateOnChain");
76
+ const qpType = (qp.get("type") as EscrowType) || "all";
77
+ const qpStatus = (qp.get("status") as EscrowStatus) || "all";
78
+ const qpMin = qp.get("minAmount") || "";
79
+ const qpMax = qp.get("maxAmount") || "";
80
+ const qpStart = qp.get("startDate");
81
+ const qpEnd = qp.get("endDate");
82
+ const qpRole = qp.get("role");
83
+
84
+ setPage(Number.isFinite(qpPage) && qpPage > 0 ? qpPage : 1);
85
+ setOrderBy(
86
+ ["createdAt", "updatedAt", "amount"].includes(qpOrderBy)
87
+ ? qpOrderBy
88
+ : "createdAt"
89
+ );
90
+ setOrderDirection(qpOrderDir === "asc" ? "asc" : "desc");
91
+ setTitle(qpTitle);
92
+ setEngagementId(qpEng);
93
+ setIsActive(qpActive === null ? true : qpActive === "true");
94
+ setValidateOnChain(
95
+ qpValidateOnChain === null ? true : qpValidateOnChain === "true"
96
+ );
97
+ setType(qpType);
98
+ setStatus(qpStatus);
99
+ setMinAmount(qpMin);
100
+ setMaxAmount(qpMax);
101
+ setDateRange({
102
+ from: qpStart ? new Date(qpStart) : undefined,
103
+ to: qpEnd ? new Date(qpEnd) : undefined,
104
+ });
105
+ setRole(
106
+ (qpRole as GetEscrowsFromIndexerByRoleParams["role"]) || "approver"
107
+ );
108
+ // eslint-disable-next-line react-hooks/exhaustive-deps
109
+ }, []);
110
+
111
+ const debouncedSearchParams = useDebouncedValue(
112
+ {
113
+ page,
114
+ orderBy,
115
+ orderDirection,
116
+ title: debouncedTitle,
117
+ engagementId: debouncedEngagementId,
118
+ isActive,
119
+ validateOnChain,
120
+ type,
121
+ status,
122
+ minAmount: debouncedMinAmount,
123
+ maxAmount: debouncedMaxAmount,
124
+ startDate: dateRange.from
125
+ ? startOfDay(dateRange.from).toISOString()
126
+ : undefined,
127
+ endDate: dateRange.to ? endOfDay(dateRange.to).toISOString() : undefined,
128
+ role,
129
+ },
130
+ 200
131
+ );
132
+
133
+ React.useEffect(() => {
134
+ if (!pathname) return;
135
+ const qp = new URLSearchParams();
136
+ qp.set("page", String(debouncedSearchParams.page ?? 1));
137
+ qp.set("orderBy", String(debouncedSearchParams.orderBy ?? "createdAt"));
138
+ qp.set(
139
+ "orderDirection",
140
+ String(debouncedSearchParams.orderDirection ?? "desc")
141
+ );
142
+ if (debouncedSearchParams.title)
143
+ qp.set("title", debouncedSearchParams.title);
144
+ if (debouncedSearchParams.engagementId)
145
+ qp.set("engagementId", debouncedSearchParams.engagementId);
146
+ qp.set("isActive", String(debouncedSearchParams.isActive));
147
+ qp.set("validateOnChain", String(debouncedSearchParams.validateOnChain));
148
+ if (type && type !== "all") qp.set("type", type);
149
+ if (status && status !== "all") qp.set("status", status);
150
+ if (debouncedSearchParams.minAmount)
151
+ qp.set("minAmount", String(debouncedSearchParams.minAmount));
152
+ if (debouncedSearchParams.maxAmount)
153
+ qp.set("maxAmount", String(debouncedSearchParams.maxAmount));
154
+ if (debouncedSearchParams.startDate)
155
+ qp.set("startDate", String(debouncedSearchParams.startDate));
156
+ if (debouncedSearchParams.endDate)
157
+ qp.set("endDate", String(debouncedSearchParams.endDate));
158
+ if (debouncedSearchParams.role)
159
+ qp.set("role", String(debouncedSearchParams.role));
160
+
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
+ ]);
180
+
181
+ const formattedRangeLabel = React.useMemo(() => {
182
+ if (!dateRange?.from && !dateRange?.to) return "Date range";
183
+ const fromStr = dateRange.from
184
+ ? format(dateRange.from, "LLL dd, yyyy")
185
+ : "";
186
+ const toStr = dateRange.to ? format(dateRange.to, "LLL dd, yyyy") : "";
187
+ return [fromStr, toStr].filter(Boolean).join(" – ") || "Date range";
188
+ }, [dateRange]);
189
+
190
+ const params = React.useMemo(() => {
191
+ return {
192
+ roleAddress: walletAddress ?? "",
193
+ role,
194
+ page,
195
+ orderBy,
196
+ orderDirection,
197
+ title: debouncedTitle || undefined,
198
+ engagementId: debouncedEngagementId || undefined,
199
+ isActive,
200
+ validateOnChain,
201
+ type: (type === "all" ? undefined : type) as
202
+ | undefined
203
+ | "single-release"
204
+ | "multi-release",
205
+ status: (status === "all" ? undefined : status) as
206
+ | undefined
207
+ | "working"
208
+ | "pendingRelease"
209
+ | "released"
210
+ | "resolved"
211
+ | "inDispute",
212
+ minAmount: debouncedMinAmount ? Number(debouncedMinAmount) : undefined,
213
+ maxAmount: debouncedMaxAmount ? Number(debouncedMaxAmount) : undefined,
214
+ startDate: dateRange.from
215
+ ? startOfDay(dateRange.from).toISOString()
216
+ : undefined,
217
+ endDate: dateRange.to ? endOfDay(dateRange.to).toISOString() : undefined,
218
+ enabled: Boolean(walletAddress && role),
219
+ };
220
+ }, [
221
+ walletAddress,
222
+ role,
223
+ page,
224
+ orderBy,
225
+ orderDirection,
226
+ debouncedTitle,
227
+ debouncedEngagementId,
228
+ isActive,
229
+ validateOnChain,
230
+ type,
231
+ status,
232
+ debouncedMinAmount,
233
+ debouncedMaxAmount,
234
+ dateRange,
235
+ ]);
236
+
237
+ const query = useEscrowsByRoleQuery(params);
238
+ const nextPageQuery = useEscrowsByRoleQuery({ ...params, page: page + 1 });
239
+
240
+ const didMountValidateRef = React.useRef(false);
241
+ React.useEffect(() => {
242
+ if (!didMountValidateRef.current) {
243
+ didMountValidateRef.current = true;
244
+ return;
245
+ }
246
+ query.refetch();
247
+ nextPageQuery.refetch();
248
+ // eslint-disable-next-line react-hooks/exhaustive-deps
249
+ }, [validateOnChain]);
250
+
251
+ const onClearFilters = React.useCallback(() => {
252
+ setTitle("");
253
+ setEngagementId("");
254
+ setIsActive(true);
255
+ setValidateOnChain(true);
256
+ setType("all");
257
+ setStatus("all");
258
+ setMinAmount("");
259
+ setMaxAmount("");
260
+ setDateRange({ from: undefined, to: undefined });
261
+ setPage(1);
262
+ setOrderBy("createdAt");
263
+ setOrderDirection("desc");
264
+ setSorting([]);
265
+ setRole("approver");
266
+ }, []);
267
+
268
+ const handleSortingChange = React.useCallback(
269
+ (updater: SortingState | ((old: SortingState) => SortingState)) => {
270
+ setSorting((prev) => {
271
+ const next =
272
+ typeof updater === "function"
273
+ ? (updater as (old: SortingState) => SortingState)(prev)
274
+ : updater;
275
+ const first = next[0];
276
+ if (first) {
277
+ if (
278
+ first.id === "amount" ||
279
+ first.id === "createdAt" ||
280
+ first.id === "updatedAt"
281
+ ) {
282
+ setOrderBy(first.id as EscrowOrderBy);
283
+ setOrderDirection(first.desc ? "desc" : "asc");
284
+ }
285
+ } else {
286
+ setOrderBy("createdAt");
287
+ setOrderDirection("desc");
288
+ }
289
+ return next;
290
+ });
291
+ },
292
+ []
293
+ );
294
+
295
+ return {
296
+ walletAddress,
297
+ data: query.data ?? ([] as Escrow[]),
298
+ isLoading: query.isLoading,
299
+ isError: query.isError,
300
+ isFetching: query.isFetching,
301
+ refetch: query.refetch,
302
+ nextData: nextPageQuery.data ?? [],
303
+ isFetchingNext: nextPageQuery.isFetching,
304
+ page,
305
+ setPage,
306
+ orderBy,
307
+ setOrderBy,
308
+ orderDirection,
309
+ setOrderDirection,
310
+ sorting,
311
+ setSorting,
312
+ title,
313
+ setTitle,
314
+ engagementId,
315
+ setEngagementId,
316
+ isActive,
317
+ setIsActive,
318
+ validateOnChain,
319
+ setValidateOnChain,
320
+ type,
321
+ setType,
322
+ status,
323
+ setStatus,
324
+ minAmount,
325
+ setMinAmount,
326
+ maxAmount,
327
+ setMaxAmount,
328
+ dateRange,
329
+ setDateRange,
330
+ formattedRangeLabel,
331
+ role,
332
+ setRole,
333
+ onClearFilters,
334
+ handleSortingChange,
335
+ } as const;
336
+ }