@trustless-work/blocks 1.0.6 → 1.0.7

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 (33) hide show
  1. package/bin/index.js +1930 -1921
  2. package/package.json +1 -1
  3. package/templates/deps.json +1 -1
  4. package/templates/escrows/details/Actions.tsx +1 -2
  5. package/templates/escrows/details/Entities.tsx +23 -2
  6. package/templates/escrows/details/GeneralInformation.tsx +1 -13
  7. package/templates/escrows/details/MilestoneCard.tsx +1 -1
  8. package/templates/escrows/details/MilestoneDetailDialog.tsx +38 -19
  9. package/templates/escrows/details/SuccessReleaseDialog.tsx +84 -28
  10. package/templates/escrows/details/useDetailsEscrow.ts +15 -2
  11. package/templates/escrows/multi-release/initialize-escrow/dialog/InitializeEscrow.tsx +76 -101
  12. package/templates/escrows/multi-release/initialize-escrow/form/InitializeEscrow.tsx +78 -102
  13. package/templates/escrows/multi-release/initialize-escrow/shared/schema.ts +8 -18
  14. package/templates/escrows/multi-release/initialize-escrow/shared/useInitializeEscrow.ts +21 -12
  15. package/templates/escrows/multi-release/release-milestone/button/ReleaseMilestone.tsx +5 -1
  16. package/templates/escrows/multi-release/update-escrow/dialog/UpdateEscrow.tsx +112 -101
  17. package/templates/escrows/multi-release/update-escrow/form/UpdateEscrow.tsx +103 -101
  18. package/templates/escrows/multi-release/update-escrow/shared/schema.ts +8 -16
  19. package/templates/escrows/multi-release/update-escrow/shared/useUpdateEscrow.ts +33 -14
  20. package/templates/escrows/multi-release/withdraw-remaining-funds/button/WithdrawRemainingFunds.tsx +0 -1
  21. package/templates/escrows/multi-release/withdraw-remaining-funds/shared/useWithdrawRemainingFunds.ts +0 -1
  22. package/templates/escrows/single-release/initialize-escrow/dialog/InitializeEscrow.tsx +2 -25
  23. package/templates/escrows/single-release/initialize-escrow/form/InitializeEscrow.tsx +3 -26
  24. package/templates/escrows/single-release/initialize-escrow/shared/schema.ts +0 -10
  25. package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +0 -4
  26. package/templates/escrows/single-release/release-escrow/button/ReleaseEscrow.tsx +5 -1
  27. package/templates/escrows/single-release/update-escrow/dialog/UpdateEscrow.tsx +41 -27
  28. package/templates/escrows/single-release/update-escrow/form/UpdateEscrow.tsx +38 -3
  29. package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +28 -14
  30. package/templates/providers/EscrowAmountProvider.tsx +8 -0
  31. package/templates/tanstack/useEscrowsMutations.ts +1 -6
  32. package/templates/wallet-kit/WalletButtons.tsx +2 -2
  33. package/templates/wallet-kit/WalletProvider.tsx +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustless-work/blocks",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "author": "Trustless Work",
5
5
  "keywords": [
6
6
  "react",
@@ -4,7 +4,7 @@
4
4
  "react-dom": "^18.2.0",
5
5
  "react-hook-form": "^7.53.0",
6
6
  "zod": "^3.23.8",
7
- "@trustless-work/escrow": "^3.0.0",
7
+ "@trustless-work/escrow": "^3.0.2",
8
8
  "@tanstack/react-query": "^5.75.0",
9
9
  "@tanstack/react-query-devtools": "^5.75.0",
10
10
  "tailwindcss": "^3.3.3",
@@ -91,8 +91,7 @@ export const Actions = ({
91
91
  userRolesInEscrow.includes("platformAddress") &&
92
92
  !selectedEscrow?.flags?.disputed &&
93
93
  !selectedEscrow?.flags?.resolved &&
94
- !selectedEscrow?.flags?.released &&
95
- selectedEscrow?.balance === 0;
94
+ !selectedEscrow?.flags?.released;
96
95
 
97
96
  const shouldShowDisputeButton =
98
97
  selectedEscrow.type === "single-release" &&
@@ -1,13 +1,26 @@
1
1
  "use client";
2
2
 
3
- import { GetEscrowsFromIndexerResponse as Escrow } from "@trustless-work/escrow/types";
3
+ import {
4
+ GetEscrowsFromIndexerResponse as Escrow,
5
+ MultiReleaseMilestone,
6
+ } from "@trustless-work/escrow/types";
4
7
  import { EntityCard } from "./EntityCard";
8
+ import { Separator } from "__UI_BASE__/separator";
5
9
 
6
10
  interface EntitiesProps {
7
11
  selectedEscrow: Escrow;
8
12
  }
9
13
 
10
14
  export const Entities = ({ selectedEscrow }: EntitiesProps) => {
15
+ const receivers =
16
+ selectedEscrow.type === "single-release"
17
+ ? (selectedEscrow.roles as { receiver?: string })?.receiver
18
+ ? [(selectedEscrow.roles as { receiver?: string }).receiver as string]
19
+ : []
20
+ : (selectedEscrow.milestones || [])
21
+ .map((m) => (m as MultiReleaseMilestone | undefined)?.receiver)
22
+ .filter((r): r is string => Boolean(r));
23
+
11
24
  return (
12
25
  <>
13
26
  <div className="flex justify-between items-center mb-6">
@@ -41,7 +54,15 @@ export const Entities = ({ selectedEscrow }: EntitiesProps) => {
41
54
  type="releaseSigner"
42
55
  entity={selectedEscrow.roles?.releaseSigner}
43
56
  />
44
- <EntityCard type="receiver" entity={selectedEscrow.roles?.receiver} />
57
+ </div>
58
+
59
+ <Separator className="my-4" />
60
+
61
+ <h2 className="text-lg font-semibold">Receivers</h2>
62
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mt-4">
63
+ {receivers.map((r, idx) => (
64
+ <EntityCard key={`receiver-${idx}-${r}`} type="receiver" entity={r} />
65
+ ))}
45
66
  </div>
46
67
  </>
47
68
  );
@@ -159,7 +159,7 @@ export const GeneralInformation = ({
159
159
  </div>
160
160
  </div>
161
161
 
162
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
162
+ <div className="grid grid-cols-1 gap-4">
163
163
  <div className="p-4 bg-muted/50 rounded-lg border">
164
164
  <div className="flex items-center gap-3 mb-3">
165
165
  <Users className="h-5 w-5 text-primary flex-shrink-0" />
@@ -185,18 +185,6 @@ export const GeneralInformation = ({
185
185
  })}
186
186
  </div>
187
187
  </div>
188
-
189
- <div className="p-4 bg-muted/50 rounded-lg border">
190
- <div className="flex items-center gap-3 mb-2">
191
- <Info className="h-5 w-5 text-primary flex-shrink-0" />
192
- <span className="text-sm font-medium text-muted-foreground">
193
- Memo
194
- </span>
195
- </div>
196
- <span className="font-medium text-foreground">
197
- {selectedEscrow?.receiverMemo || "No Memo"}
198
- </span>
199
- </div>
200
188
  </div>
201
189
 
202
190
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
@@ -221,7 +221,7 @@ const MilestoneCardComponent = ({
221
221
  <Button
222
222
  size="sm"
223
223
  variant="outline"
224
- className="w-full border-border text-muted-foreground"
224
+ className="w-full border-border text-muted-foreground cursor-pointer"
225
225
  onClick={(e) => {
226
226
  e.stopPropagation();
227
227
  onViewDetails(milestone, milestoneIndex);
@@ -10,7 +10,6 @@ import {
10
10
  import { Card, CardContent, CardHeader, CardTitle } from "__UI_BASE__/card";
11
11
  import { Badge } from "__UI_BASE__/badge";
12
12
  import {
13
- DollarSign,
14
13
  FileCheck2,
15
14
  User,
16
15
  Calendar,
@@ -28,6 +27,9 @@ import {
28
27
  SingleReleaseMilestone,
29
28
  } from "@trustless-work/escrow";
30
29
  import Link from "next/link";
30
+ import { formatCurrency } from "@/components/tw-blocks/helpers/format.helper";
31
+ import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
32
+ import { EntityCard } from "./EntityCard";
31
33
 
32
34
  interface MilestoneDetailDialogProps {
33
35
  isOpen: boolean;
@@ -49,6 +51,8 @@ export const MilestoneDetailDialog = ({
49
51
  onClose,
50
52
  selectedMilestone,
51
53
  }: MilestoneDetailDialogProps) => {
54
+ const { selectedEscrow } = useEscrowContext();
55
+
52
56
  const getMilestoneStatusBadge = (
53
57
  milestone: SingleReleaseMilestone | MultiReleaseMilestone
54
58
  ) => {
@@ -115,11 +119,15 @@ export const MilestoneDetailDialog = ({
115
119
  }
116
120
  };
117
121
 
122
+ const currentReceiver =
123
+ selectedEscrow?.type === "multi-release" &&
124
+ ((selectedMilestone?.milestone as MultiReleaseMilestone)?.receiver ?? "");
125
+
118
126
  if (!selectedMilestone) return null;
119
127
 
120
128
  return (
121
129
  <Dialog open={isOpen} onOpenChange={onClose}>
122
- <DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
130
+ <DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
123
131
  <DialogHeader className="pb-4 border-b border-border">
124
132
  <div className="flex items-center gap-3">
125
133
  <div className="flex items-center justify-center w-10 h-10 bg-primary/10 rounded-full">
@@ -154,6 +162,28 @@ export const MilestoneDetailDialog = ({
154
162
  {getMilestoneStatusBadge(selectedMilestone.milestone)}
155
163
  </div>
156
164
 
165
+ {"amount" in selectedMilestone.milestone && (
166
+ <div className="flex items-center justify-between p-4 rounded-lg border border-border">
167
+ <div className="flex items-center gap-3">
168
+ <div className="flex items-center justify-center w-8 h-8 bg-background rounded-full shadow-sm border border-border">
169
+ <Calendar className="w-4 h-4 text-primary" />
170
+ </div>
171
+ <div>
172
+ <p className="text-sm font-medium text-foreground">Amount</p>
173
+ <p className="text-xs text-muted-foreground">
174
+ The amount of the milestone
175
+ </p>
176
+ </div>
177
+ </div>
178
+ <span className="font-bold text-foreground">
179
+ {formatCurrency(
180
+ selectedMilestone.milestone.amount,
181
+ selectedEscrow?.trustline?.name ?? "USDC"
182
+ )}
183
+ </span>
184
+ </div>
185
+ )}
186
+
157
187
  <Card className="border border-border shadow-sm">
158
188
  <CardHeader className="pb-3">
159
189
  <CardTitle className="text-lg flex items-center gap-2">
@@ -171,21 +201,6 @@ export const MilestoneDetailDialog = ({
171
201
  {selectedMilestone.milestone.description}
172
202
  </p>
173
203
  </div>
174
-
175
- {"amount" in selectedMilestone.milestone && (
176
- <div className="space-y-2">
177
- <label className="text-sm font-medium text-foreground flex items-center gap-2">
178
- <DollarSign className="w-4 h-4 text-green-600 dark:text-green-400" />
179
- Amount
180
- </label>
181
- <div className="flex items-center gap-2 bg-green-500/10 dark:bg-green-400/10 p-3 rounded-md border-l-4 border-green-500/20 dark:border-green-400/20">
182
- <DollarSign className="w-5 h-5 text-green-600 dark:text-green-400" />
183
- <span className="text-lg font-bold text-green-700 dark:text-green-300">
184
- {selectedMilestone.milestone.amount}
185
- </span>
186
- </div>
187
- </div>
188
- )}
189
204
  </CardContent>
190
205
  </Card>
191
206
 
@@ -193,7 +208,7 @@ export const MilestoneDetailDialog = ({
193
208
  <Card className="border border-border shadow-sm">
194
209
  <CardHeader className="pb-3">
195
210
  <CardTitle className="text-lg flex items-center gap-2">
196
- <div className="w-1 h-6 bg-green-600 dark:bg-green-400 rounded-full"></div>
211
+ <div className="w-1 h-6 rounded-full"></div>
197
212
  Evidence
198
213
  </CardTitle>
199
214
  </CardHeader>
@@ -203,7 +218,7 @@ export const MilestoneDetailDialog = ({
203
218
  <FileCheck2 className="w-4 h-4 text-green-600 dark:text-green-400" />
204
219
  Evidence URL
205
220
  </label>
206
- <div className="bg-muted/50 p-3 rounded-md border-l-4 border-green-500/20 dark:border-green-400/20">
221
+ <div className="bg-muted/50 p-3 rounded-md border-l-4">
207
222
  {(() => {
208
223
  const result = isValidUrl(
209
224
  selectedMilestone.milestone.evidence
@@ -269,6 +284,10 @@ export const MilestoneDetailDialog = ({
269
284
  </Card>
270
285
  )}
271
286
  </div>
287
+
288
+ {currentReceiver && (
289
+ <EntityCard type="receiver" entity={currentReceiver} />
290
+ )}
272
291
  </DialogContent>
273
292
  </Dialog>
274
293
  );
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import React, { useMemo } from "react";
3
+ import { useMemo } from "react";
4
4
  import {
5
5
  Dialog,
6
6
  DialogContent,
@@ -12,6 +12,7 @@ import { EntityCard } from "./EntityCard";
12
12
  import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
13
13
  import { useEscrowAmountContext } from "@/components/tw-blocks/providers/EscrowAmountProvider";
14
14
  import { CircleCheckBig } from "lucide-react";
15
+ import { MultiReleaseMilestone } from "@trustless-work/escrow";
15
16
 
16
17
  interface SuccessReleaseDialogProps {
17
18
  isOpen: boolean;
@@ -23,8 +24,12 @@ export const SuccessReleaseDialog = ({
23
24
  onOpenChange,
24
25
  }: SuccessReleaseDialogProps) => {
25
26
  const { selectedEscrow } = useEscrowContext();
26
- const { receiverAmount, platformFeeAmount, trustlessWorkAmount } =
27
- useEscrowAmountContext();
27
+ const {
28
+ receiverAmount,
29
+ platformFeeAmount,
30
+ trustlessWorkAmount,
31
+ lastReleasedMilestoneIndex,
32
+ } = useEscrowAmountContext();
28
33
 
29
34
  const platformFee = Number(selectedEscrow?.platformFee || 0);
30
35
  const trustlessPercentage = 0.3;
@@ -32,8 +37,15 @@ export const SuccessReleaseDialog = ({
32
37
 
33
38
  const currency = selectedEscrow?.trustline?.name ?? "";
34
39
 
35
- const cards = useMemo(
36
- () => [
40
+ const cards = useMemo<
41
+ Array<{
42
+ type: string;
43
+ entity?: string;
44
+ percentage?: number;
45
+ amount?: number;
46
+ }>
47
+ >(() => {
48
+ const baseCards = [
37
49
  {
38
50
  type: "Platform",
39
51
  entity: selectedEscrow?.roles?.platformAddress,
@@ -46,24 +58,62 @@ export const SuccessReleaseDialog = ({
46
58
  percentage: trustlessPercentage,
47
59
  amount: trustlessWorkAmount,
48
60
  },
49
- {
50
- type: "Receiver",
51
- entity: selectedEscrow?.roles?.receiver,
52
- percentage: receiverPercentage,
53
- amount: receiverAmount,
54
- },
55
- ],
56
- [
57
- platformFee,
58
- receiverPercentage,
59
- trustlessPercentage,
60
- platformFeeAmount,
61
- trustlessWorkAmount,
62
- receiverAmount,
63
- selectedEscrow?.roles?.platformAddress,
64
- selectedEscrow?.roles?.receiver,
65
- ]
66
- );
61
+ ];
62
+
63
+ if (selectedEscrow?.type === "single-release") {
64
+ return [
65
+ ...baseCards,
66
+ {
67
+ type: "Receiver",
68
+ entity: (selectedEscrow?.roles as { receiver?: string })?.receiver,
69
+ percentage: receiverPercentage,
70
+ amount: receiverAmount,
71
+ },
72
+ ];
73
+ }
74
+
75
+ // Multi-release: show only the receiver for the just-released milestone
76
+ const idx =
77
+ typeof lastReleasedMilestoneIndex === "number"
78
+ ? lastReleasedMilestoneIndex
79
+ : -1;
80
+ const milestone = selectedEscrow?.milestones?.[idx] as
81
+ | MultiReleaseMilestone
82
+ | undefined;
83
+ const receiverForReleased = milestone?.receiver;
84
+
85
+ if (receiverForReleased) {
86
+ return [
87
+ ...baseCards,
88
+ {
89
+ type: "Receiver",
90
+ entity: receiverForReleased,
91
+ percentage: receiverPercentage,
92
+ amount: receiverAmount,
93
+ },
94
+ ];
95
+ }
96
+
97
+ // Fallback: if no index available, list all receivers (legacy behavior)
98
+ const receiverCards = (selectedEscrow?.milestones || [])
99
+ .map((m) => (m as MultiReleaseMilestone | undefined)?.receiver)
100
+ .filter((r): r is string => Boolean(r))
101
+ .map((r) => ({ type: "Receiver", entity: r }));
102
+
103
+ return [...baseCards, ...receiverCards];
104
+ }, [
105
+ platformFee,
106
+ receiverPercentage,
107
+ trustlessPercentage,
108
+ platformFeeAmount,
109
+ trustlessWorkAmount,
110
+ receiverAmount,
111
+ selectedEscrow?.roles?.platformAddress,
112
+ selectedEscrow?.type,
113
+ selectedEscrow?.milestones,
114
+ selectedEscrow?.roles,
115
+ lastReleasedMilestoneIndex,
116
+ ]);
67
117
 
68
118
  return (
69
119
  <Dialog open={isOpen} onOpenChange={onOpenChange}>
@@ -81,13 +131,19 @@ export const SuccessReleaseDialog = ({
81
131
  <div className="flex flex-col gap-3">
82
132
  {cards.map((c) => (
83
133
  <EntityCard
84
- key={c.type}
134
+ key={`${c.type}-${c.entity ?? "unknown"}`}
85
135
  type={c.type}
86
136
  entity={c.entity}
87
- hasPercentage
88
- percentage={Number(c.percentage.toFixed(2))}
89
- hasAmount
90
- amount={Number(c.amount.toFixed(2))}
137
+ hasPercentage={c.percentage !== undefined}
138
+ percentage={
139
+ c.percentage !== undefined
140
+ ? Number(c.percentage.toFixed(2))
141
+ : undefined
142
+ }
143
+ hasAmount={c.amount !== undefined}
144
+ amount={
145
+ c.amount !== undefined ? Number(c.amount.toFixed(2)) : undefined
146
+ }
91
147
  currency={currency}
92
148
  />
93
149
  ))}
@@ -1,5 +1,8 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
- import { GetEscrowsFromIndexerResponse as Escrow } from "@trustless-work/escrow/types";
2
+ import {
3
+ GetEscrowsFromIndexerResponse as Escrow,
4
+ MultiReleaseMilestone,
5
+ } from "@trustless-work/escrow/types";
3
6
  import { useWalletContext } from "@/components/tw-blocks/wallet-kit/WalletProvider";
4
7
  import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
5
8
  import { useEscrowAmountContext } from "@/components/tw-blocks/providers/EscrowAmountProvider";
@@ -60,7 +63,17 @@ const useEscrowDetailDialog = ({
60
63
  name: "disputeResolver",
61
64
  address: selectedEscrow.roles.disputeResolver,
62
65
  },
63
- { name: "receiver", address: selectedEscrow.roles.receiver },
66
+ {
67
+ name: "receiver",
68
+ address:
69
+ selectedEscrow.type === "single-release"
70
+ ? (selectedEscrow.roles as { receiver?: string })?.receiver
71
+ : (
72
+ selectedEscrow.milestones?.[0] as
73
+ | MultiReleaseMilestone
74
+ | undefined
75
+ )?.receiver,
76
+ },
64
77
  ];
65
78
 
66
79
  const userRoles = roleMappings
@@ -29,6 +29,7 @@ import {
29
29
  DialogTitle,
30
30
  DialogTrigger,
31
31
  } from "__UI_BASE__/dialog";
32
+ import { Separator } from "__UI_BASE__/separator";
32
33
 
33
34
  export const InitializeEscrowDialog = () => {
34
35
  const {
@@ -130,7 +131,7 @@ export const InitializeEscrowDialog = () => {
130
131
  </Button>
131
132
  )}
132
133
  </Card>
133
- <div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
134
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
134
135
  <FormField
135
136
  control={form.control}
136
137
  name="title"
@@ -174,7 +175,9 @@ export const InitializeEscrowDialog = () => {
174
175
  </FormItem>
175
176
  )}
176
177
  />
178
+ </div>
177
179
 
180
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
178
181
  <FormField
179
182
  control={form.control}
180
183
  name="trustline.address"
@@ -211,6 +214,34 @@ export const InitializeEscrowDialog = () => {
211
214
  </FormItem>
212
215
  )}
213
216
  />
217
+
218
+ <FormField
219
+ control={form.control}
220
+ name="platformFee"
221
+ render={() => (
222
+ <FormItem>
223
+ <FormLabel className="flex items-center">
224
+ Platform Fee
225
+ <span className="text-destructive ml-1">*</span>
226
+ </FormLabel>
227
+ <FormControl>
228
+ <div className="relative">
229
+ <Percent
230
+ className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500"
231
+ size={18}
232
+ />
233
+ <Input
234
+ placeholder="Enter platform fee"
235
+ className="pl-10"
236
+ value={form.watch("platformFee")?.toString() || ""}
237
+ onChange={handlePlatformFeeChange}
238
+ />
239
+ </div>
240
+ </FormControl>
241
+ <FormMessage />
242
+ </FormItem>
243
+ )}
244
+ />
214
245
  </div>
215
246
 
216
247
  <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
@@ -320,7 +351,7 @@ export const InitializeEscrowDialog = () => {
320
351
  />
321
352
  </div>
322
353
 
323
- <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
354
+ <div className="grid grid-cols-1 gap-4">
324
355
  <FormField
325
356
  control={form.control}
326
357
  name="roles.platformAddress"
@@ -328,7 +359,7 @@ export const InitializeEscrowDialog = () => {
328
359
  <FormItem>
329
360
  <FormLabel className="flex items-center justify-between">
330
361
  <span className="flex items-center">
331
- Platform Address
362
+ Platform
332
363
  <span className="text-destructive ml-1">*</span>
333
364
  </span>
334
365
  </FormLabel>
@@ -346,83 +377,6 @@ export const InitializeEscrowDialog = () => {
346
377
  </FormItem>
347
378
  )}
348
379
  />
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
380
  </div>
427
381
 
428
382
  <FormField
@@ -453,19 +407,32 @@ export const InitializeEscrowDialog = () => {
453
407
  </FormLabel>
454
408
  {milestones.map((milestone, index) => (
455
409
  <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
- />
410
+ <div className="grid grid-cols-1 md:grid-cols-12 gap-4 items-center">
411
+ <div className="md:col-span-4">
412
+ <Input
413
+ placeholder="Enter receiver address"
414
+ value={milestone.receiver}
415
+ onChange={(e) => {
416
+ const updatedMilestones = [...milestones];
417
+ updatedMilestones[index].receiver = e.target.value;
418
+ form.setValue("milestones", updatedMilestones);
419
+ }}
420
+ />
421
+ </div>
422
+
423
+ <div className="md:col-span-4">
424
+ <Input
425
+ placeholder="Milestone description"
426
+ value={milestone.description}
427
+ onChange={(e) => {
428
+ const updatedMilestones = [...milestones];
429
+ updatedMilestones[index].description = e.target.value;
430
+ form.setValue("milestones", updatedMilestones);
431
+ }}
432
+ />
433
+ </div>
467
434
 
468
- <div className="relative w-full sm:w-2/5">
435
+ <div className="md:col-span-3 relative">
469
436
  <DollarSign
470
437
  className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500"
471
438
  size={18}
@@ -478,21 +445,29 @@ export const InitializeEscrowDialog = () => {
478
445
  />
479
446
  </div>
480
447
 
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
- type="button"
486
- >
487
- <Trash2 className="h-5 w-5" />
488
- </Button>
448
+ <div className="md:col-span-1 flex justify-end">
449
+ <Button
450
+ onClick={() => handleRemoveMilestone(index)}
451
+ className="p-2 bg-transparent text-red-500 hover:text-red-600"
452
+ disabled={milestones.length === 1}
453
+ type="button"
454
+ >
455
+ <Trash2 className="h-5 w-5" />
456
+ </Button>
457
+ </div>
489
458
  </div>
490
459
 
460
+ {/* Separator */}
461
+ {index < milestones.length - 1 && (
462
+ <Separator className="w-full" />
463
+ )}
464
+
465
+ {/* Add button */}
491
466
  {index === milestones.length - 1 && (
492
467
  <div className="flex justify-end mt-4">
493
468
  <Button
494
469
  disabled={isAnyMilestoneEmpty}
495
- className="w-full md:w-1/4"
470
+ className="w-full md:w-fit md:min-w-40 cursor-pointer"
496
471
  variant="outline"
497
472
  onClick={handleAddMilestone}
498
473
  type="button"