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