@trustless-work/blocks 1.0.5 → 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.
- package/bin/index.js +1930 -1920
- package/package.json +1 -1
- package/templates/dashboard/dashboard-01/Dashboard.tsx +118 -71
- package/templates/deps.json +1 -1
- package/templates/escrows/details/Actions.tsx +1 -2
- package/templates/escrows/details/Entities.tsx +23 -2
- package/templates/escrows/details/GeneralInformation.tsx +1 -13
- package/templates/escrows/details/MilestoneCard.tsx +1 -1
- package/templates/escrows/details/MilestoneDetailDialog.tsx +38 -19
- package/templates/escrows/details/SuccessReleaseDialog.tsx +84 -28
- package/templates/escrows/details/useDetailsEscrow.ts +15 -2
- package/templates/escrows/multi-release/initialize-escrow/dialog/InitializeEscrow.tsx +76 -101
- package/templates/escrows/multi-release/initialize-escrow/form/InitializeEscrow.tsx +78 -102
- package/templates/escrows/multi-release/initialize-escrow/shared/schema.ts +8 -18
- package/templates/escrows/multi-release/initialize-escrow/shared/useInitializeEscrow.ts +21 -12
- package/templates/escrows/multi-release/release-milestone/button/ReleaseMilestone.tsx +5 -1
- package/templates/escrows/multi-release/update-escrow/dialog/UpdateEscrow.tsx +112 -101
- package/templates/escrows/multi-release/update-escrow/form/UpdateEscrow.tsx +103 -101
- package/templates/escrows/multi-release/update-escrow/shared/schema.ts +8 -16
- package/templates/escrows/multi-release/update-escrow/shared/useUpdateEscrow.ts +33 -14
- package/templates/escrows/multi-release/withdraw-remaining-funds/button/WithdrawRemainingFunds.tsx +0 -1
- package/templates/escrows/multi-release/withdraw-remaining-funds/shared/useWithdrawRemainingFunds.ts +0 -1
- package/templates/escrows/single-release/initialize-escrow/dialog/InitializeEscrow.tsx +2 -25
- package/templates/escrows/single-release/initialize-escrow/form/InitializeEscrow.tsx +3 -26
- package/templates/escrows/single-release/initialize-escrow/shared/schema.ts +0 -10
- package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +0 -4
- package/templates/escrows/single-release/release-escrow/button/ReleaseEscrow.tsx +5 -1
- package/templates/escrows/single-release/update-escrow/dialog/UpdateEscrow.tsx +41 -27
- package/templates/escrows/single-release/update-escrow/form/UpdateEscrow.tsx +38 -3
- package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +28 -14
- package/templates/providers/EscrowAmountProvider.tsx +8 -0
- package/templates/tanstack/useEscrowsMutations.ts +1 -6
- package/templates/wallet-kit/WalletButtons.tsx +2 -2
- package/templates/wallet-kit/WalletProvider.tsx +0 -1
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import { Separator } from "__UI_BASE__/separator";
|
|
12
12
|
import { useDashboard } from "./useDashboard";
|
|
13
13
|
import { formatCurrency } from "../../helpers/format.helper";
|
|
14
|
-
import { Activity, Layers3, PiggyBank } from "lucide-react";
|
|
14
|
+
import { Activity, Layers3, PiggyBank, CloudOff } from "lucide-react";
|
|
15
15
|
import {
|
|
16
16
|
AreaChart,
|
|
17
17
|
Area,
|
|
@@ -29,6 +29,13 @@ import {
|
|
|
29
29
|
ChartTooltipContent,
|
|
30
30
|
type ChartConfig,
|
|
31
31
|
} from "__UI_BASE__/chart";
|
|
32
|
+
import {
|
|
33
|
+
Empty,
|
|
34
|
+
EmptyHeader,
|
|
35
|
+
EmptyMedia,
|
|
36
|
+
EmptyTitle,
|
|
37
|
+
EmptyDescription,
|
|
38
|
+
} from "__UI_BASE__/empty";
|
|
32
39
|
|
|
33
40
|
const chartConfigBar: ChartConfig = {
|
|
34
41
|
desktop: {
|
|
@@ -86,7 +93,7 @@ export const Dashboard01 = () => {
|
|
|
86
93
|
<div className="grid gap-6">
|
|
87
94
|
{/* KPI Cards */}
|
|
88
95
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
89
|
-
<Card>
|
|
96
|
+
<Card className="gap-3">
|
|
90
97
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
91
98
|
<CardTitle className="text-sm font-medium">Escrows</CardTitle>
|
|
92
99
|
<Layers3 className="h-4 w-4 text-muted-foreground" />
|
|
@@ -101,7 +108,7 @@ export const Dashboard01 = () => {
|
|
|
101
108
|
</CardContent>
|
|
102
109
|
</Card>
|
|
103
110
|
|
|
104
|
-
<Card>
|
|
111
|
+
<Card className="gap-3">
|
|
105
112
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
106
113
|
<CardTitle className="text-sm font-medium">Total Amount</CardTitle>
|
|
107
114
|
<Activity className="h-4 w-4 text-muted-foreground" />
|
|
@@ -116,7 +123,7 @@ export const Dashboard01 = () => {
|
|
|
116
123
|
</CardContent>
|
|
117
124
|
</Card>
|
|
118
125
|
|
|
119
|
-
<Card>
|
|
126
|
+
<Card className="gap-3">
|
|
120
127
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
121
128
|
<CardTitle className="text-sm font-medium">Total Balance</CardTitle>
|
|
122
129
|
<PiggyBank className="h-4 w-4 text-muted-foreground" />
|
|
@@ -137,7 +144,7 @@ export const Dashboard01 = () => {
|
|
|
137
144
|
{/* Charts */}
|
|
138
145
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
139
146
|
{/* Bar chart: Amounts by date (shadcn pattern) */}
|
|
140
|
-
<Card>
|
|
147
|
+
<Card className="gap-3">
|
|
141
148
|
<CardHeader>
|
|
142
149
|
<CardTitle>Escrow Amounts</CardTitle>
|
|
143
150
|
<CardDescription>Amounts by date</CardDescription>
|
|
@@ -147,26 +154,42 @@ export const Dashboard01 = () => {
|
|
|
147
154
|
className="w-full h-56 sm:h-64 lg:h-72"
|
|
148
155
|
config={chartConfigBar}
|
|
149
156
|
>
|
|
150
|
-
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
157
|
+
{barData.length > 0 ? (
|
|
158
|
+
<BarChart accessibilityLayer data={barData}>
|
|
159
|
+
<CartesianGrid vertical={false} />
|
|
160
|
+
<XAxis
|
|
161
|
+
dataKey="month"
|
|
162
|
+
tickLine={false}
|
|
163
|
+
tickMargin={10}
|
|
164
|
+
axisLine={false}
|
|
165
|
+
tickFormatter={(value) =>
|
|
166
|
+
new Date(String(value)).toLocaleDateString("en-US", {
|
|
167
|
+
month: "short",
|
|
168
|
+
day: "numeric",
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
/>
|
|
172
|
+
<ChartTooltip
|
|
173
|
+
cursor={false}
|
|
174
|
+
content={<ChartTooltipContent hideLabel />}
|
|
175
|
+
/>
|
|
176
|
+
<Bar
|
|
177
|
+
dataKey="desktop"
|
|
178
|
+
fill="var(--color-desktop)"
|
|
179
|
+
radius={8}
|
|
180
|
+
/>
|
|
181
|
+
</BarChart>
|
|
182
|
+
) : (
|
|
183
|
+
<Empty className="border border-dashed">
|
|
184
|
+
<EmptyHeader>
|
|
185
|
+
<EmptyMedia variant="icon">
|
|
186
|
+
<CloudOff />
|
|
187
|
+
</EmptyMedia>
|
|
188
|
+
<EmptyTitle>No data</EmptyTitle>
|
|
189
|
+
<EmptyDescription>No Data Available</EmptyDescription>
|
|
190
|
+
</EmptyHeader>
|
|
191
|
+
</Empty>
|
|
192
|
+
)}
|
|
170
193
|
</ChartContainer>
|
|
171
194
|
</CardContent>
|
|
172
195
|
</Card>
|
|
@@ -182,22 +205,34 @@ export const Dashboard01 = () => {
|
|
|
182
205
|
config={chartConfigDonut}
|
|
183
206
|
className="w-full h-56 sm:h-64 lg:h-72"
|
|
184
207
|
>
|
|
185
|
-
|
|
186
|
-
<
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
208
|
+
{donutData.some((d) => Number(d.visitors) > 0) ? (
|
|
209
|
+
<PieChart>
|
|
210
|
+
<ChartTooltip
|
|
211
|
+
cursor={false}
|
|
212
|
+
content={<ChartTooltipContent hideLabel />}
|
|
213
|
+
/>
|
|
214
|
+
<Pie
|
|
215
|
+
data={donutData}
|
|
216
|
+
dataKey="visitors"
|
|
217
|
+
nameKey="browser"
|
|
218
|
+
innerRadius={60}
|
|
219
|
+
>
|
|
220
|
+
{donutData.map((slice, idx) => (
|
|
221
|
+
<Cell key={`cell-${idx}`} fill={slice.fill} />
|
|
222
|
+
))}
|
|
223
|
+
</Pie>
|
|
224
|
+
</PieChart>
|
|
225
|
+
) : (
|
|
226
|
+
<Empty className="border border-dashed">
|
|
227
|
+
<EmptyHeader>
|
|
228
|
+
<EmptyMedia variant="icon">
|
|
229
|
+
<CloudOff />
|
|
230
|
+
</EmptyMedia>
|
|
231
|
+
<EmptyTitle>No data</EmptyTitle>
|
|
232
|
+
<EmptyDescription>No Data Available</EmptyDescription>
|
|
233
|
+
</EmptyHeader>
|
|
234
|
+
</Empty>
|
|
235
|
+
)}
|
|
201
236
|
</ChartContainer>
|
|
202
237
|
<div className="mt-4 flex items-center justify-center gap-6">
|
|
203
238
|
<div className="flex items-center gap-2">
|
|
@@ -229,36 +264,48 @@ export const Dashboard01 = () => {
|
|
|
229
264
|
className="w-full h-56 sm:h-64 lg:h-72"
|
|
230
265
|
config={chartConfigArea}
|
|
231
266
|
>
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
267
|
+
{areaData.length > 0 ? (
|
|
268
|
+
<AreaChart
|
|
269
|
+
accessibilityLayer
|
|
270
|
+
data={areaData}
|
|
271
|
+
margin={{ left: 12, right: 12 }}
|
|
272
|
+
>
|
|
273
|
+
<CartesianGrid vertical={false} />
|
|
274
|
+
<XAxis
|
|
275
|
+
dataKey="month"
|
|
276
|
+
tickLine={false}
|
|
277
|
+
axisLine={false}
|
|
278
|
+
tickMargin={8}
|
|
279
|
+
tickFormatter={(value) =>
|
|
280
|
+
new Date(String(value)).toLocaleDateString("en-US", {
|
|
281
|
+
month: "short",
|
|
282
|
+
day: "numeric",
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
/>
|
|
286
|
+
<ChartTooltip
|
|
287
|
+
cursor={false}
|
|
288
|
+
content={<ChartTooltipContent indicator="line" />}
|
|
289
|
+
/>
|
|
290
|
+
<Area
|
|
291
|
+
dataKey="desktop"
|
|
292
|
+
type="natural"
|
|
293
|
+
fill="var(--color-desktop)"
|
|
294
|
+
fillOpacity={0.4}
|
|
295
|
+
stroke="var(--color-desktop)"
|
|
296
|
+
/>
|
|
297
|
+
</AreaChart>
|
|
298
|
+
) : (
|
|
299
|
+
<Empty className="border border-dashed">
|
|
300
|
+
<EmptyHeader>
|
|
301
|
+
<EmptyMedia variant="icon">
|
|
302
|
+
<CloudOff />
|
|
303
|
+
</EmptyMedia>
|
|
304
|
+
<EmptyTitle>No data</EmptyTitle>
|
|
305
|
+
<EmptyDescription>No Data Available</EmptyDescription>
|
|
306
|
+
</EmptyHeader>
|
|
307
|
+
</Empty>
|
|
308
|
+
)}
|
|
262
309
|
</ChartContainer>
|
|
263
310
|
</CardContent>
|
|
264
311
|
</Card>
|
package/templates/deps.json
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
27
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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={
|
|
89
|
-
|
|
90
|
-
|
|
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 {
|
|
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
|
-
{
|
|
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
|