@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,504 @@
1
+ import * as React from "react";
2
+ import {
3
+ Form,
4
+ FormField,
5
+ FormItem,
6
+ FormLabel,
7
+ FormControl,
8
+ FormMessage,
9
+ } from "__UI_BASE__/form";
10
+ import { Input } from "__UI_BASE__/input";
11
+ import { Button } from "__UI_BASE__/button";
12
+ import { Card } from "__UI_BASE__/card";
13
+ import {
14
+ Select,
15
+ SelectTrigger,
16
+ SelectValue,
17
+ SelectContent,
18
+ SelectItem,
19
+ } from "__UI_BASE__/select";
20
+ import { Textarea } from "__UI_BASE__/textarea";
21
+ import { useInitializeEscrow } from "./useInitializeEscrow";
22
+ import { Trash2, DollarSign, Percent, Loader2 } from "lucide-react";
23
+ import Link from "next/link";
24
+ import { trustlineOptions } from "@/components/tw-blocks/wallet-kit/trustlines";
25
+
26
+ export function InitializeEscrowForm() {
27
+ const {
28
+ form,
29
+ isSubmitting,
30
+ milestones,
31
+ isAnyMilestoneEmpty,
32
+ handleSubmit,
33
+ handleAddMilestone,
34
+ handleRemoveMilestone,
35
+ } = useInitializeEscrow();
36
+
37
+ const handleAmountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
38
+ let rawValue = e.target.value;
39
+ rawValue = rawValue.replace(/[^0-9.]/g, "");
40
+
41
+ if (rawValue.split(".").length > 2) {
42
+ rawValue = rawValue.slice(0, -1);
43
+ }
44
+
45
+ // Limit to 2 decimal places
46
+ if (rawValue.includes(".")) {
47
+ const parts = rawValue.split(".");
48
+ if (parts[1] && parts[1].length > 2) {
49
+ rawValue = parts[0] + "." + parts[1].slice(0, 2);
50
+ }
51
+ }
52
+
53
+ // Always keep as string to allow partial input like "0." or "0.5"
54
+ form.setValue("amount", rawValue);
55
+ };
56
+
57
+ const handlePlatformFeeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
58
+ let rawValue = e.target.value;
59
+ rawValue = rawValue.replace(/[^0-9.]/g, "");
60
+
61
+ if (rawValue.split(".").length > 2) {
62
+ rawValue = rawValue.slice(0, -1);
63
+ }
64
+
65
+ // Limit to 2 decimal places
66
+ if (rawValue.includes(".")) {
67
+ const parts = rawValue.split(".");
68
+ if (parts[1] && parts[1].length > 2) {
69
+ rawValue = parts[0] + "." + parts[1].slice(0, 2);
70
+ }
71
+ }
72
+
73
+ // Always keep as string to allow partial input like "0." or "0.5"
74
+ form.setValue("platformFee", rawValue);
75
+ };
76
+
77
+ return (
78
+ <Form {...form}>
79
+ <form onSubmit={handleSubmit} className="flex flex-col space-y-6">
80
+ <Card className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 p-4">
81
+ <Link
82
+ className="flex-1"
83
+ href="https://docs.trustlesswork.com/trustless-work/technology-overview/escrow-types"
84
+ target="_blank"
85
+ >
86
+ <div className="flex items-center gap-2">
87
+ <div className="h-2 w-2 rounded-full bg-primary" />
88
+ <h2 className="text-xl font-semibold">Single Release Escrow</h2>
89
+ </div>
90
+ <p className="text-muted-foreground mt-1">
91
+ A single payment will be released upon completion of all
92
+ milestones
93
+ </p>
94
+ </Link>
95
+ </Card>
96
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
97
+ <FormField
98
+ control={form.control}
99
+ name="title"
100
+ render={({ field }) => (
101
+ <FormItem>
102
+ <FormLabel className="flex items-center">
103
+ Title<span className="text-destructive ml-1">*</span>
104
+ </FormLabel>
105
+ <FormControl>
106
+ <Input
107
+ placeholder="Escrow title"
108
+ {...field}
109
+ onChange={(e) => {
110
+ field.onChange(e);
111
+ }}
112
+ />
113
+ </FormControl>
114
+ <FormMessage />
115
+ </FormItem>
116
+ )}
117
+ />
118
+
119
+ <FormField
120
+ control={form.control}
121
+ name="engagementId"
122
+ render={({ field }) => (
123
+ <FormItem>
124
+ <FormLabel className="flex items-center">
125
+ Engagement<span className="text-destructive ml-1">*</span>
126
+ </FormLabel>
127
+ <FormControl>
128
+ <Input
129
+ placeholder="Enter identifier"
130
+ {...field}
131
+ onChange={(e) => {
132
+ field.onChange(e);
133
+ }}
134
+ />
135
+ </FormControl>
136
+ <FormMessage />
137
+ </FormItem>
138
+ )}
139
+ />
140
+
141
+ <FormField
142
+ control={form.control}
143
+ name="trustline.address"
144
+ render={({ field }) => (
145
+ <FormItem>
146
+ <FormLabel className="flex items-center">
147
+ Trustline<span className="text-destructive ml-1">*</span>
148
+ </FormLabel>
149
+ <FormControl>
150
+ <Select
151
+ value={field.value}
152
+ onValueChange={(e) => {
153
+ field.onChange(e);
154
+ }}
155
+ >
156
+ <SelectTrigger className="w-full">
157
+ <SelectValue placeholder="Select trustline" />
158
+ </SelectTrigger>
159
+ <SelectContent>
160
+ {trustlineOptions
161
+ .filter((option) => option.value)
162
+ .map((option, index) => (
163
+ <SelectItem
164
+ key={`${option.value}-${index}`}
165
+ value={option.value}
166
+ >
167
+ {option.label}
168
+ </SelectItem>
169
+ ))}
170
+ </SelectContent>
171
+ </Select>
172
+ </FormControl>
173
+ <FormMessage />
174
+ </FormItem>
175
+ )}
176
+ />
177
+ </div>
178
+
179
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
180
+ <FormField
181
+ control={form.control}
182
+ name="roles.approver"
183
+ render={({ field }) => (
184
+ <FormItem>
185
+ <FormLabel className="flex items-center justify-between">
186
+ <span className="flex items-center">
187
+ Approver<span className="text-destructive ml-1">*</span>
188
+ </span>
189
+ </FormLabel>
190
+
191
+ <FormControl>
192
+ <Input
193
+ placeholder="Enter approver address"
194
+ {...field}
195
+ onChange={(e) => {
196
+ field.onChange(e);
197
+ }}
198
+ />
199
+ </FormControl>
200
+ <FormMessage />
201
+ </FormItem>
202
+ )}
203
+ />
204
+
205
+ <FormField
206
+ control={form.control}
207
+ name="roles.serviceProvider"
208
+ render={({ field }) => (
209
+ <FormItem>
210
+ <FormLabel className="flex items-center justify-between">
211
+ <span className="flex items-center">
212
+ Service Provider
213
+ <span className="text-destructive ml-1">*</span>
214
+ </span>
215
+ </FormLabel>
216
+
217
+ <FormControl>
218
+ <Input
219
+ placeholder="Enter service provider address"
220
+ {...field}
221
+ onChange={(e) => {
222
+ field.onChange(e);
223
+ }}
224
+ />
225
+ </FormControl>
226
+ <FormMessage />
227
+ </FormItem>
228
+ )}
229
+ />
230
+ </div>
231
+
232
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
233
+ <FormField
234
+ control={form.control}
235
+ name="roles.releaseSigner"
236
+ render={({ field }) => (
237
+ <FormItem>
238
+ <FormLabel className="flex items-center justify-between">
239
+ <span className="flex items-center">
240
+ Release Signer
241
+ <span className="text-destructive ml-1">*</span>
242
+ </span>
243
+ </FormLabel>
244
+
245
+ <FormControl>
246
+ <Input
247
+ placeholder="Enter release signer address"
248
+ {...field}
249
+ onChange={(e) => {
250
+ field.onChange(e);
251
+ }}
252
+ />
253
+ </FormControl>
254
+ <FormMessage />
255
+ </FormItem>
256
+ )}
257
+ />
258
+
259
+ <FormField
260
+ control={form.control}
261
+ name="roles.disputeResolver"
262
+ render={({ field }) => (
263
+ <FormItem>
264
+ <FormLabel className="flex items-center justify-between">
265
+ <span className="flex items-center">
266
+ Dispute Resolver
267
+ <span className="text-destructive ml-1">*</span>
268
+ </span>
269
+ </FormLabel>
270
+
271
+ <FormControl>
272
+ <Input
273
+ placeholder="Enter dispute resolver address"
274
+ {...field}
275
+ onChange={(e) => {
276
+ field.onChange(e);
277
+ }}
278
+ />
279
+ </FormControl>
280
+ <FormMessage />
281
+ </FormItem>
282
+ )}
283
+ />
284
+ </div>
285
+
286
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
287
+ <FormField
288
+ control={form.control}
289
+ name="roles.platformAddress"
290
+ render={({ field }) => (
291
+ <FormItem>
292
+ <FormLabel className="flex items-center justify-between">
293
+ <span className="flex items-center">
294
+ Platform Address
295
+ <span className="text-destructive ml-1">*</span>
296
+ </span>
297
+ </FormLabel>
298
+
299
+ <FormControl>
300
+ <Input
301
+ placeholder="Enter platform address"
302
+ {...field}
303
+ onChange={(e) => {
304
+ field.onChange(e);
305
+ }}
306
+ />
307
+ </FormControl>
308
+ <FormMessage />
309
+ </FormItem>
310
+ )}
311
+ />
312
+ <FormField
313
+ control={form.control}
314
+ name="roles.receiver"
315
+ render={({ field }) => (
316
+ <FormItem>
317
+ <FormLabel className="flex items-center justify-between">
318
+ <span className="flex items-center">
319
+ Receiver<span className="text-destructive ml-1">*</span>
320
+ </span>
321
+ </FormLabel>
322
+
323
+ <FormControl>
324
+ <Input
325
+ placeholder="Enter receiver address"
326
+ {...field}
327
+ onChange={(e) => {
328
+ field.onChange(e);
329
+ }}
330
+ />
331
+ </FormControl>
332
+ <FormMessage />
333
+ </FormItem>
334
+ )}
335
+ />
336
+ </div>
337
+
338
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
339
+ <FormField
340
+ control={form.control}
341
+ name="platformFee"
342
+ render={() => (
343
+ <FormItem>
344
+ <FormLabel className="flex items-center">
345
+ Platform Fee<span className="text-destructive ml-1">*</span>
346
+ </FormLabel>
347
+ <FormControl>
348
+ <div className="relative">
349
+ <Percent
350
+ className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500"
351
+ size={18}
352
+ />
353
+ <Input
354
+ placeholder="Enter platform fee"
355
+ className="pl-10"
356
+ value={form.watch("platformFee")?.toString() || ""}
357
+ onChange={handlePlatformFeeChange}
358
+ />
359
+ </div>
360
+ </FormControl>
361
+ <FormMessage />
362
+ </FormItem>
363
+ )}
364
+ />
365
+
366
+ <FormField
367
+ control={form.control}
368
+ name="amount"
369
+ render={() => (
370
+ <FormItem>
371
+ <FormLabel className="flex items-center">
372
+ Amount<span className="text-destructive ml-1">*</span>
373
+ </FormLabel>
374
+ <FormControl>
375
+ <div className="relative">
376
+ <DollarSign
377
+ className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500"
378
+ size={18}
379
+ />
380
+ <Input
381
+ placeholder="Enter amount"
382
+ className="pl-10"
383
+ value={form.watch("amount")?.toString() || ""}
384
+ onChange={handleAmountChange}
385
+ />
386
+ </div>
387
+ </FormControl>
388
+ <FormMessage />
389
+ </FormItem>
390
+ )}
391
+ />
392
+
393
+ <FormField
394
+ control={form.control}
395
+ name="receiverMemo"
396
+ render={({ field }) => (
397
+ <FormItem>
398
+ <FormLabel className="flex items-center">
399
+ Receiver Memo (opcional)
400
+ </FormLabel>
401
+ <FormControl>
402
+ <Input
403
+ type="text"
404
+ placeholder="Enter the escrow receiver Memo"
405
+ {...field}
406
+ onChange={(e) => {
407
+ field.onChange(e);
408
+ }}
409
+ />
410
+ </FormControl>
411
+ <FormMessage />
412
+ </FormItem>
413
+ )}
414
+ />
415
+ </div>
416
+
417
+ <FormField
418
+ control={form.control}
419
+ name="description"
420
+ render={({ field }) => (
421
+ <FormItem>
422
+ <FormLabel className="flex items-center">
423
+ Description<span className="text-destructive ml-1">*</span>
424
+ </FormLabel>
425
+ <FormControl>
426
+ <Textarea
427
+ placeholder="Escrow description"
428
+ {...field}
429
+ onChange={(e) => {
430
+ field.onChange(e);
431
+ }}
432
+ />
433
+ </FormControl>
434
+ <FormMessage />
435
+ </FormItem>
436
+ )}
437
+ />
438
+
439
+ <div className="space-y-4">
440
+ <FormLabel className="flex items-center">
441
+ Milestones<span className="text-destructive ml-1">*</span>
442
+ </FormLabel>
443
+ {milestones.map((milestone, index) => (
444
+ <div key={index}>
445
+ <div className="flex flex-col sm:flex-row items-start sm:items-center space-y-2 sm:space-y-0 sm:space-x-4">
446
+ <Input
447
+ placeholder="Milestone Description"
448
+ value={milestone.description}
449
+ className="w-full sm:flex-1"
450
+ onChange={(e) => {
451
+ const updatedMilestones = [...milestones];
452
+ updatedMilestones[index].description = e.target.value;
453
+ form.setValue("milestones", updatedMilestones);
454
+ }}
455
+ />
456
+
457
+ <Button
458
+ onClick={() => handleRemoveMilestone(index)}
459
+ className="p-2 bg-transparent text-destructive rounded-md border-none shadow-none hover:bg-transparent hover:shadow-none hover:text-destructive focus:ring-0 active:ring-0 self-start sm:self-center cursor-pointer"
460
+ disabled={milestones.length === 1}
461
+ >
462
+ <Trash2 className="h-5 w-5" />
463
+ </Button>
464
+ </div>
465
+
466
+ {index === milestones.length - 1 && (
467
+ <div className="flex justify-end mt-4">
468
+ <Button
469
+ disabled={isAnyMilestoneEmpty}
470
+ className="w-full md:w-1/4 cursor-pointer"
471
+ variant="outline"
472
+ onClick={handleAddMilestone}
473
+ type="button"
474
+ >
475
+ Add Item
476
+ </Button>
477
+ </div>
478
+ )}
479
+ </div>
480
+ ))}
481
+ </div>
482
+
483
+ <div className="flex justify-start">
484
+ <Button
485
+ className="w-full md:w-1/4 cursor-pointer"
486
+ type="submit"
487
+ disabled={isAnyMilestoneEmpty || isSubmitting}
488
+ >
489
+ {isSubmitting ? (
490
+ <div className="flex items-center">
491
+ <Loader2 className="h-5 w-5 animate-spin" />
492
+ <span className="ml-2">Deploying...</span>
493
+ </div>
494
+ ) : (
495
+ "Deploy"
496
+ )}
497
+ </Button>
498
+ </div>
499
+ </form>
500
+ </Form>
501
+ );
502
+ }
503
+
504
+ export default InitializeEscrowForm;