@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,422 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { Card } from "__UI_BASE__/card";
5
+ import { Button } from "__UI_BASE__/button";
6
+ import type {
7
+ GetEscrowsFromIndexerResponse as Escrow,
8
+ MultiReleaseMilestone,
9
+ Role,
10
+ } from "@trustless-work/escrow/types";
11
+ import {
12
+ ColumnDef,
13
+ flexRender,
14
+ getCoreRowModel,
15
+ useReactTable,
16
+ } from "@tanstack/react-table";
17
+ import {
18
+ Table,
19
+ TableBody,
20
+ TableCell,
21
+ TableHead,
22
+ TableHeader,
23
+ TableRow,
24
+ } from "__UI_BASE__/table";
25
+ import { FileX, Loader2, Wallet, RefreshCw, AlertTriangle } from "lucide-react";
26
+ import Filters from "./Filters";
27
+ 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";
31
+ import { formatTimestamp } from "../../../helpers/format.helper";
32
+
33
+ export function EscrowsBySignerTable() {
34
+ const {
35
+ walletAddress,
36
+ data,
37
+ isLoading,
38
+ isError,
39
+ refetch,
40
+ isFetching,
41
+ nextData,
42
+ isFetchingNext,
43
+ page,
44
+ setPage,
45
+ orderBy,
46
+ setOrderBy,
47
+ orderDirection,
48
+ setOrderDirection,
49
+ sorting,
50
+ title,
51
+ setTitle,
52
+ engagementId,
53
+ setEngagementId,
54
+ isActive,
55
+ setIsActive,
56
+ validateOnChain,
57
+ setValidateOnChain,
58
+ type,
59
+ setType,
60
+ status,
61
+ setStatus,
62
+ minAmount,
63
+ setMinAmount,
64
+ maxAmount,
65
+ setMaxAmount,
66
+ dateRange,
67
+ setDateRange,
68
+ formattedRangeLabel,
69
+ onClearFilters,
70
+ handleSortingChange,
71
+ } = useEscrowsBySigner();
72
+
73
+ const dialogStates = useEscrowDialogs();
74
+ const { setSelectedEscrow } = useEscrowContext();
75
+
76
+ const columns = React.useMemo<ColumnDef<Escrow>[]>(
77
+ () => [
78
+ {
79
+ header: "Title",
80
+ accessorKey: "title",
81
+ enableSorting: false,
82
+ cell: ({ row }) => (
83
+ <span
84
+ className="max-w-[220px] truncate block"
85
+ title={row.original.title}
86
+ >
87
+ {row.original.title}
88
+ </span>
89
+ ),
90
+ },
91
+ {
92
+ header: "Engagement ID",
93
+ accessorKey: "engagementId",
94
+ enableSorting: false,
95
+ meta: { className: "hidden sm:table-cell" },
96
+ cell: ({ row }) => (
97
+ <span
98
+ className="max-w-[180px] truncate block"
99
+ title={row.original.engagementId}
100
+ >
101
+ {row.original.engagementId}
102
+ </span>
103
+ ),
104
+ },
105
+ {
106
+ header: "Amount",
107
+ accessorKey: "amount",
108
+ enableSorting: true,
109
+ cell: ({ row }) => {
110
+ // single release
111
+ if (row.original.type === "single-release") {
112
+ return row.original.amount;
113
+ }
114
+ // multi release
115
+ return row.original.milestones.reduce(
116
+ (acc, milestone) =>
117
+ acc + (milestone as MultiReleaseMilestone).amount,
118
+ 0
119
+ );
120
+ },
121
+ },
122
+ {
123
+ header: "Balance",
124
+ accessorKey: "balance",
125
+ enableSorting: false,
126
+ meta: { className: "hidden md:table-cell" },
127
+ cell: ({ row }) => row.original.balance ?? 0,
128
+ },
129
+ {
130
+ header: "Type",
131
+ accessorKey: "type",
132
+ enableSorting: false,
133
+ meta: { className: "hidden lg:table-cell" },
134
+ },
135
+ {
136
+ header: "Active",
137
+ accessorKey: "isActive",
138
+ enableSorting: false,
139
+ meta: { className: "hidden md:table-cell" },
140
+ cell: ({ row }) => (row.original.isActive ? "Yes" : "No"),
141
+ },
142
+ {
143
+ header: "Trustline",
144
+ id: "trustline",
145
+ enableSorting: false,
146
+ meta: { className: "hidden lg:table-cell" },
147
+ cell: ({ row }) => (
148
+ <span
149
+ className="max-w-[220px] truncate block"
150
+ title={`${row.original.trustline.name} (${row.original.trustline.address})`}
151
+ >
152
+ {row.original.trustline.name}
153
+ </span>
154
+ ),
155
+ },
156
+ {
157
+ header: "Created",
158
+ accessorKey: "createdAt",
159
+ enableSorting: true,
160
+ meta: { className: "hidden sm:table-cell" },
161
+ cell: ({ row }) => formatTimestamp(row.original.createdAt),
162
+ },
163
+ {
164
+ header: "Updated",
165
+ accessorKey: "updatedAt",
166
+ enableSorting: true,
167
+ meta: { className: "hidden xl:table-cell" },
168
+ cell: ({ row }) => formatTimestamp(row.original.updatedAt),
169
+ },
170
+ {
171
+ header: "Actions",
172
+ id: "actions",
173
+ enableSorting: false,
174
+ cell: ({ row }) => (
175
+ <Button
176
+ variant="outline"
177
+ onClick={() => {
178
+ setSelectedEscrow(row.original);
179
+ dialogStates.second.setIsOpen(true);
180
+ }}
181
+ >
182
+ Details
183
+ </Button>
184
+ ),
185
+ },
186
+ ],
187
+ []
188
+ );
189
+
190
+ const table = useReactTable({
191
+ data: React.useMemo(() => data ?? [], [data]),
192
+ columns,
193
+ getCoreRowModel: getCoreRowModel(),
194
+ state: {
195
+ sorting,
196
+ },
197
+ onSortingChange: handleSortingChange,
198
+ manualSorting: true,
199
+ enableSortingRemoval: true,
200
+ });
201
+
202
+ const activeRole: Role[] = ["approver"];
203
+ const escrows = data ?? [];
204
+
205
+ return (
206
+ <>
207
+ <div className="w-full flex flex-col gap-4">
208
+ <Filters
209
+ title={title}
210
+ engagementId={engagementId}
211
+ isActive={isActive}
212
+ validateOnChain={validateOnChain}
213
+ type={type}
214
+ status={status}
215
+ minAmount={minAmount}
216
+ maxAmount={maxAmount}
217
+ dateRange={dateRange}
218
+ formattedRangeLabel={formattedRangeLabel}
219
+ setTitle={setTitle}
220
+ setEngagementId={setEngagementId}
221
+ setIsActive={setIsActive}
222
+ setValidateOnChain={setValidateOnChain}
223
+ setType={(v) => setType(v as typeof type)}
224
+ setStatus={(v) => setStatus(v as typeof status)}
225
+ setMinAmount={setMinAmount}
226
+ setMaxAmount={setMaxAmount}
227
+ setDateRange={setDateRange}
228
+ onClearFilters={onClearFilters}
229
+ onRefresh={() => refetch()}
230
+ isRefreshing={isFetching}
231
+ orderBy={orderBy}
232
+ orderDirection={orderDirection}
233
+ setOrderBy={(v) => setOrderBy(v)}
234
+ setOrderDirection={(v) => setOrderDirection(v)}
235
+ />
236
+
237
+ <Card className="w-full p-2 sm:p-4">
238
+ <div className="mt-2 sm:mt-4 overflow-x-auto">
239
+ <Table>
240
+ <TableHeader>
241
+ {table.getHeaderGroups().map((headerGroup) => (
242
+ <TableRow key={headerGroup.id} className="text-xs sm:text-sm">
243
+ {headerGroup.headers.map((header) => {
244
+ const isSortable = header.column.getCanSort();
245
+ const sorted = header.column.getIsSorted();
246
+ const className =
247
+ typeof header.column.columnDef.meta === "object" &&
248
+ header.column.columnDef.meta &&
249
+ "className" in (header.column.columnDef.meta as any)
250
+ ? (header.column.columnDef.meta as any).className
251
+ : "";
252
+ return (
253
+ <TableHead
254
+ key={header.id}
255
+ className={`select-none ${className}`}
256
+ onClick={
257
+ isSortable
258
+ ? header.column.getToggleSortingHandler()
259
+ : undefined
260
+ }
261
+ role={isSortable ? "button" : undefined}
262
+ aria-sort={
263
+ sorted
264
+ ? sorted === "desc"
265
+ ? "descending"
266
+ : "ascending"
267
+ : "none"
268
+ }
269
+ >
270
+ <div className="flex items-center gap-1">
271
+ {flexRender(
272
+ header.column.columnDef.header,
273
+ header.getContext()
274
+ )}
275
+ {isSortable && (
276
+ <span className="text-muted-foreground">
277
+ {sorted === "asc"
278
+ ? "▲"
279
+ : sorted === "desc"
280
+ ? "▼"
281
+ : ""}
282
+ </span>
283
+ )}
284
+ </div>
285
+ </TableHead>
286
+ );
287
+ })}
288
+ </TableRow>
289
+ ))}
290
+ </TableHeader>
291
+ <TableBody>
292
+ {!walletAddress ? (
293
+ <TableRow>
294
+ <TableCell colSpan={table.getAllLeafColumns().length}>
295
+ <div className="p-6 md:p-8 flex flex-col items-center justify-center text-center">
296
+ <Wallet className="h-8 w-8 md:h-12 md:w-12 text-primary mb-3" />
297
+ <h3 className="font-medium text-foreground mb-2">
298
+ Connect your wallet
299
+ </h3>
300
+ <p className="text-sm text-muted-foreground max-w-sm">
301
+ To continue, connect your wallet and authorize the
302
+ application.
303
+ </p>
304
+ </div>
305
+ </TableCell>
306
+ </TableRow>
307
+ ) : isLoading ? (
308
+ <TableRow>
309
+ <TableCell colSpan={table.getAllLeafColumns().length}>
310
+ <div className="p-6 md:p-8 flex flex-col items-center justify-center text-center">
311
+ <Loader2 className="h-6 w-6 md:h-8 md:w-8 animate-spin text-primary mb-3" />
312
+ <p className="text-sm text-muted-foreground">
313
+ Loading escrows…
314
+ </p>
315
+ </div>
316
+ </TableCell>
317
+ </TableRow>
318
+ ) : isError ? (
319
+ <TableRow>
320
+ <TableCell colSpan={table.getAllLeafColumns().length}>
321
+ <div className="p-6 md:p-8 flex flex-col items-center justify-center text-center">
322
+ <AlertTriangle className="h-8 w-8 md:h-10 md:w-10 text-destructive mb-3" />
323
+ <h3 className="font-medium text-foreground mb-2">
324
+ Error loading data
325
+ </h3>
326
+ <p className="text-sm text-muted-foreground max-w-sm mb-4">
327
+ An error occurred while loading the information.
328
+ Please try again.
329
+ </p>
330
+ <Button
331
+ variant="outline"
332
+ size="sm"
333
+ onClick={() => refetch()}
334
+ >
335
+ <RefreshCw className="h-4 w-4 mr-2" />
336
+ Retry
337
+ </Button>
338
+ </div>
339
+ </TableCell>
340
+ </TableRow>
341
+ ) : escrows.length === 0 ? (
342
+ <TableRow>
343
+ <TableCell colSpan={table.getAllLeafColumns().length}>
344
+ <div className="p-6 md:p-8 flex flex-col items-center justify-center text-center">
345
+ <FileX className="h-8 w-8 md:h-10 md:w-10 text-muted-foreground/60 mb-3" />
346
+ <h3 className="font-medium text-foreground mb-2">
347
+ No data available
348
+ </h3>
349
+ <p className="text-sm text-muted-foreground">
350
+ No escrows found for the selected filters.
351
+ </p>
352
+ </div>
353
+ </TableCell>
354
+ </TableRow>
355
+ ) : (
356
+ <>
357
+ {table.getRowModel().rows.map((row) => (
358
+ <TableRow key={row.id} className="text-xs sm:text-sm">
359
+ {row.getVisibleCells().map((cell) => {
360
+ const className =
361
+ typeof cell.column.columnDef.meta === "object" &&
362
+ cell.column.columnDef.meta &&
363
+ "className" in (cell.column.columnDef.meta as any)
364
+ ? (cell.column.columnDef.meta as any).className
365
+ : "";
366
+ return (
367
+ <TableCell key={cell.id} className={className}>
368
+ {flexRender(
369
+ cell.column.columnDef.cell,
370
+ cell.getContext()
371
+ )}
372
+ </TableCell>
373
+ );
374
+ })}
375
+ </TableRow>
376
+ ))}
377
+ </>
378
+ )}
379
+ </TableBody>
380
+ </Table>
381
+ </div>
382
+
383
+ <div className="mt-3 sm:mt-4 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
384
+ <div className="text-xs sm:text-sm text-muted-foreground">
385
+ Page {page}
386
+ </div>
387
+ <div className="flex items-center gap-2 self-end sm:self-auto">
388
+ <Button
389
+ variant="outline"
390
+ onClick={() => setPage((p) => Math.max(1, p - 1))}
391
+ disabled={page === 1 || isFetching}
392
+ >
393
+ Previous
394
+ </Button>
395
+ <Button
396
+ variant="outline"
397
+ onClick={() => setPage((p) => p + 1)}
398
+ disabled={
399
+ isFetching ||
400
+ !walletAddress ||
401
+ ((nextData?.length ?? 0) === 0 && !isFetchingNext)
402
+ }
403
+ >
404
+ Next
405
+ </Button>
406
+ </div>
407
+ </div>
408
+ </Card>
409
+ </div>
410
+
411
+ {/* Dialog */}
412
+ <EscrowDetailDialog
413
+ activeRole={activeRole}
414
+ isDialogOpen={dialogStates.second.isOpen}
415
+ setIsDialogOpen={dialogStates.second.setIsOpen}
416
+ setSelectedEscrow={setSelectedEscrow}
417
+ />
418
+ </>
419
+ );
420
+ }
421
+
422
+ export default EscrowsBySignerTable;