@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.
- package/README.md +96 -0
- package/bin/index.js +1123 -0
- package/package.json +44 -0
- package/templates/deps.json +29 -0
- package/templates/escrows/details/Actions.tsx +149 -0
- package/templates/escrows/details/Entities.tsx +48 -0
- package/templates/escrows/details/EntityCard.tsx +98 -0
- package/templates/escrows/details/EscrowDetailDialog.tsx +154 -0
- package/templates/escrows/details/GeneralInformation.tsx +329 -0
- package/templates/escrows/details/MilestoneCard.tsx +254 -0
- package/templates/escrows/details/MilestoneDetailDialog.tsx +276 -0
- package/templates/escrows/details/Milestones.tsx +87 -0
- package/templates/escrows/details/ProgressEscrow.tsx +191 -0
- package/templates/escrows/details/StatisticsCard.tsx +79 -0
- package/templates/escrows/details/SuccessReleaseDialog.tsx +101 -0
- package/templates/escrows/details/useDetailsEscrow.ts +126 -0
- package/templates/escrows/escrow-context/EscrowAmountProvider.tsx +86 -0
- package/templates/escrows/escrow-context/EscrowDialogsProvider.tsx +108 -0
- package/templates/escrows/escrow-context/EscrowProvider.tsx +124 -0
- package/templates/escrows/escrows-by-role/cards/EscrowsCards.tsx +503 -0
- package/templates/escrows/escrows-by-role/cards/Filters.tsx +421 -0
- package/templates/escrows/escrows-by-role/table/EscrowsTable.tsx +427 -0
- package/templates/escrows/escrows-by-role/table/Filters.tsx +421 -0
- package/templates/escrows/escrows-by-role/useEscrowsByRole.shared.ts +336 -0
- package/templates/escrows/escrows-by-signer/cards/EscrowsCards.tsx +502 -0
- package/templates/escrows/escrows-by-signer/cards/Filters.tsx +389 -0
- package/templates/escrows/escrows-by-signer/table/EscrowsTable.tsx +422 -0
- package/templates/escrows/escrows-by-signer/table/Filters.tsx +389 -0
- package/templates/escrows/escrows-by-signer/useEscrowsBySigner.shared.ts +320 -0
- package/templates/escrows/single-release/approve-milestone/button/ApproveMilestone.tsx +78 -0
- package/templates/escrows/single-release/approve-milestone/dialog/ApproveMilestone.tsx +102 -0
- package/templates/escrows/single-release/approve-milestone/form/ApproveMilestone.tsx +80 -0
- package/templates/escrows/single-release/approve-milestone/shared/schema.ts +9 -0
- package/templates/escrows/single-release/approve-milestone/shared/useApproveMilestone.ts +67 -0
- package/templates/escrows/single-release/change-milestone-status/button/ChangeMilestoneStatus.tsx +78 -0
- package/templates/escrows/single-release/change-milestone-status/dialog/ChangeMilestoneStatus.tsx +167 -0
- package/templates/escrows/single-release/change-milestone-status/form/ChangeMilestoneStatus.tsx +114 -0
- package/templates/escrows/single-release/change-milestone-status/shared/schema.ts +15 -0
- package/templates/escrows/single-release/change-milestone-status/shared/useChangeMilestoneStatus.ts +77 -0
- package/templates/escrows/single-release/dispute-escrow/button/DisputeEscrow.tsx +68 -0
- package/templates/escrows/single-release/fund-escrow/button/FundEscrow.tsx +84 -0
- package/templates/escrows/single-release/fund-escrow/dialog/FundEscrow.tsx +77 -0
- package/templates/escrows/single-release/fund-escrow/form/FundEscrow.tsx +54 -0
- package/templates/escrows/single-release/fund-escrow/shared/schema.ts +10 -0
- package/templates/escrows/single-release/fund-escrow/shared/useFundEscrow.ts +66 -0
- package/templates/escrows/single-release/initialize-escrow/dialog/InitializeEscrow.tsx +526 -0
- package/templates/escrows/single-release/initialize-escrow/form/InitializeEscrow.tsx +504 -0
- package/templates/escrows/single-release/initialize-escrow/shared/schema.ts +232 -0
- package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +115 -0
- package/templates/escrows/single-release/release-escrow/button/ReleaseEscrow.tsx +80 -0
- package/templates/escrows/single-release/resolve-dispute/button/ResolveDispute.tsx +94 -0
- package/templates/escrows/single-release/resolve-dispute/dialog/ResolveDispute.tsx +123 -0
- package/templates/escrows/single-release/resolve-dispute/form/ResolveDispute.tsx +82 -0
- package/templates/escrows/single-release/resolve-dispute/shared/schema.ts +82 -0
- package/templates/escrows/single-release/resolve-dispute/shared/useResolveDispute.ts +58 -0
- package/templates/escrows/single-release/update-escrow/dialog/UpdateEscrow.tsx +485 -0
- package/templates/escrows/single-release/update-escrow/form/UpdateEscrow.tsx +463 -0
- package/templates/escrows/single-release/update-escrow/shared/schema.ts +139 -0
- package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +211 -0
- package/templates/handle-errors/errors.enum.ts +6 -0
- package/templates/handle-errors/handle.ts +47 -0
- package/templates/helpers/format.helper.ts +27 -0
- package/templates/helpers/useCopy.ts +13 -0
- package/templates/providers/ReactQueryClientProvider.tsx +28 -0
- package/templates/providers/TrustlessWork.tsx +30 -0
- package/templates/tanstak/useEscrowsByRoleQuery.ts +87 -0
- package/templates/tanstak/useEscrowsBySignerQuery.ts +78 -0
- package/templates/tanstak/useEscrowsMutations.ts +411 -0
- package/templates/wallet-kit/WalletButtons.tsx +116 -0
- package/templates/wallet-kit/WalletProvider.tsx +94 -0
- package/templates/wallet-kit/trustlines.ts +40 -0
- package/templates/wallet-kit/useWallet.ts +77 -0
- package/templates/wallet-kit/validators.ts +12 -0
- 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;
|