@trustless-work/blocks 0.0.6 → 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.
- package/README.md +39 -13
- package/bin/index.js +1596 -1137
- package/package.json +44 -44
- package/templates/escrows/details/Actions.tsx +144 -149
- package/templates/escrows/details/Entities.tsx +1 -1
- package/templates/escrows/details/EntityCard.tsx +1 -3
- package/templates/escrows/details/EscrowDetailDialog.tsx +18 -18
- package/templates/escrows/details/GeneralInformation.tsx +20 -23
- package/templates/escrows/details/MilestoneCard.tsx +46 -47
- package/templates/escrows/details/MilestoneDetailDialog.tsx +1 -2
- package/templates/escrows/details/Milestones.tsx +0 -5
- package/templates/escrows/details/SuccessReleaseDialog.tsx +6 -9
- package/templates/escrows/details/useDetailsEscrow.ts +2 -2
- package/templates/escrows/escrows-by-role/cards/EscrowsCards.tsx +111 -60
- package/templates/escrows/escrows-by-role/cards/Filters.tsx +3 -5
- package/templates/escrows/escrows-by-role/table/EscrowsTable.tsx +36 -38
- package/templates/escrows/escrows-by-role/table/Filters.tsx +3 -5
- package/templates/escrows/escrows-by-role/useEscrowsByRole.shared.ts +33 -25
- package/templates/escrows/escrows-by-signer/cards/EscrowsCards.tsx +107 -67
- package/templates/escrows/escrows-by-signer/cards/Filters.tsx +3 -5
- package/templates/escrows/escrows-by-signer/table/EscrowsTable.tsx +28 -38
- package/templates/escrows/escrows-by-signer/table/Filters.tsx +3 -5
- package/templates/escrows/escrows-by-signer/useEscrowsBySigner.shared.ts +32 -25
- package/templates/escrows/multi-release/dispute-milestone/button/DisputeEscrow.tsx +98 -0
- package/templates/escrows/multi-release/initialize-escrow/dialog/InitializeEscrow.tsx +528 -0
- package/templates/escrows/multi-release/initialize-escrow/form/InitializeEscrow.tsx +506 -0
- package/templates/escrows/multi-release/initialize-escrow/shared/schema.ts +179 -0
- package/templates/escrows/multi-release/initialize-escrow/shared/useInitializeEscrow.ts +175 -0
- package/templates/escrows/multi-release/release-milestone/button/ReleaseEscrow.tsx +116 -0
- package/templates/escrows/multi-release/resolve-dispute/button/ResolveDispute.tsx +122 -0
- package/templates/escrows/multi-release/resolve-dispute/dialog/ResolveDispute.tsx +178 -0
- package/templates/escrows/multi-release/resolve-dispute/form/ResolveDispute.tsx +156 -0
- package/templates/escrows/multi-release/resolve-dispute/shared/schema.ts +85 -0
- package/templates/escrows/multi-release/resolve-dispute/shared/useResolveDispute.ts +105 -0
- package/templates/escrows/multi-release/update-escrow/dialog/UpdateEscrow.tsx +471 -0
- package/templates/escrows/multi-release/update-escrow/form/UpdateEscrow.tsx +449 -0
- package/templates/escrows/multi-release/update-escrow/shared/schema.ts +152 -0
- package/templates/escrows/multi-release/update-escrow/shared/useUpdateEscrow.ts +254 -0
- package/templates/escrows/{single-release → single-multi-release}/approve-milestone/button/ApproveMilestone.tsx +21 -8
- package/templates/escrows/{single-release → single-multi-release}/approve-milestone/dialog/ApproveMilestone.tsx +4 -4
- package/templates/escrows/{single-release → single-multi-release}/approve-milestone/form/ApproveMilestone.tsx +4 -4
- package/templates/escrows/{single-release/approve-milestone/shared → single-multi-release/approve-milestone}/useApproveMilestone.ts +17 -17
- package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/button/ChangeMilestoneStatus.tsx +5 -5
- package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/dialog/ChangeMilestoneStatus.tsx +5 -5
- package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/form/ChangeMilestoneStatus.tsx +4 -4
- package/templates/escrows/{single-release/change-milestone-status/shared → single-multi-release/change-milestone-status}/useChangeMilestoneStatus.ts +2 -2
- package/templates/escrows/{single-release → single-multi-release}/fund-escrow/button/FundEscrow.tsx +4 -4
- package/templates/escrows/{single-release → single-multi-release}/fund-escrow/dialog/FundEscrow.tsx +3 -3
- package/templates/escrows/{single-release → single-multi-release}/fund-escrow/form/FundEscrow.tsx +3 -3
- package/templates/escrows/{single-release/fund-escrow/shared → single-multi-release/fund-escrow}/useFundEscrow.ts +2 -2
- package/templates/escrows/single-release/dispute-escrow/button/DisputeEscrow.tsx +3 -3
- package/templates/escrows/single-release/initialize-escrow/dialog/InitializeEscrow.tsx +14 -6
- package/templates/escrows/single-release/initialize-escrow/form/InitializeEscrow.tsx +14 -6
- package/templates/escrows/single-release/initialize-escrow/shared/schema.ts +0 -57
- package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +43 -2
- package/templates/escrows/single-release/release-escrow/button/ReleaseEscrow.tsx +5 -5
- package/templates/escrows/single-release/resolve-dispute/button/ResolveDispute.tsx +4 -4
- package/templates/escrows/single-release/resolve-dispute/dialog/ResolveDispute.tsx +4 -7
- package/templates/escrows/single-release/resolve-dispute/form/ResolveDispute.tsx +2 -2
- package/templates/escrows/single-release/resolve-dispute/shared/useResolveDispute.ts +15 -2
- package/templates/escrows/single-release/update-escrow/dialog/UpdateEscrow.tsx +2 -2
- package/templates/escrows/single-release/update-escrow/form/UpdateEscrow.tsx +2 -2
- package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +229 -224
- package/templates/{escrows/escrow-context → providers}/EscrowDialogsProvider.tsx +1 -3
- package/templates/{escrows/escrow-context → providers}/EscrowProvider.tsx +27 -4
- package/templates/providers/ReactQueryClientProvider.tsx +3 -1
- package/templates/providers/TrustlessWork.tsx +1 -1
- package/templates/escrows/details/ProgressEscrow.tsx +0 -191
- /package/templates/escrows/{single-release/approve-milestone/shared → single-multi-release/approve-milestone}/schema.ts +0 -0
- /package/templates/escrows/{single-release/change-milestone-status/shared → single-multi-release/change-milestone-status}/schema.ts +0 -0
- /package/templates/escrows/{single-release/fund-escrow/shared → single-multi-release/fund-escrow}/schema.ts +0 -0
- /package/templates/{escrows/escrow-context → providers}/EscrowAmountProvider.tsx +0 -0
|
@@ -6,7 +6,6 @@ import { Button } from "__UI_BASE__/button";
|
|
|
6
6
|
import type {
|
|
7
7
|
GetEscrowsFromIndexerResponse as Escrow,
|
|
8
8
|
MultiReleaseMilestone,
|
|
9
|
-
Role,
|
|
10
9
|
} from "@trustless-work/escrow/types";
|
|
11
10
|
import {
|
|
12
11
|
ColumnDef,
|
|
@@ -23,14 +22,14 @@ import {
|
|
|
23
22
|
TableRow,
|
|
24
23
|
} from "__UI_BASE__/table";
|
|
25
24
|
import { FileX, Loader2, Wallet, RefreshCw, AlertTriangle } from "lucide-react";
|
|
26
|
-
import Filters from "./Filters";
|
|
25
|
+
import { Filters } from "./Filters";
|
|
27
26
|
import { useEscrowsBySigner } from "../useEscrowsBySigner.shared";
|
|
28
|
-
import { useEscrowDialogs } from "
|
|
29
|
-
import { useEscrowContext } from "
|
|
30
|
-
import EscrowDetailDialog from "
|
|
27
|
+
import { useEscrowDialogs } from "@/components/tw-blocks/providers/EscrowDialogsProvider";
|
|
28
|
+
import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
|
|
29
|
+
import { EscrowDetailDialog } from "../details/EscrowDetailDialog";
|
|
31
30
|
import { formatTimestamp } from "../../../helpers/format.helper";
|
|
32
31
|
|
|
33
|
-
export
|
|
32
|
+
export const EscrowsBySignerTable = () => {
|
|
34
33
|
const {
|
|
35
34
|
walletAddress,
|
|
36
35
|
data,
|
|
@@ -73,6 +72,10 @@ export function EscrowsBySignerTable() {
|
|
|
73
72
|
const dialogStates = useEscrowDialogs();
|
|
74
73
|
const { setSelectedEscrow } = useEscrowContext();
|
|
75
74
|
|
|
75
|
+
const handleRefresh = React.useCallback(() => {
|
|
76
|
+
void refetch();
|
|
77
|
+
}, [refetch]);
|
|
78
|
+
|
|
76
79
|
const columns = React.useMemo<ColumnDef<Escrow>[]>(
|
|
77
80
|
() => [
|
|
78
81
|
{
|
|
@@ -184,7 +187,7 @@ export function EscrowsBySignerTable() {
|
|
|
184
187
|
),
|
|
185
188
|
},
|
|
186
189
|
],
|
|
187
|
-
[]
|
|
190
|
+
[dialogStates.second.setIsOpen, setSelectedEscrow]
|
|
188
191
|
);
|
|
189
192
|
|
|
190
193
|
const table = useReactTable({
|
|
@@ -199,18 +202,6 @@ export function EscrowsBySignerTable() {
|
|
|
199
202
|
enableSortingRemoval: true,
|
|
200
203
|
});
|
|
201
204
|
|
|
202
|
-
/**
|
|
203
|
-
* Based on the provided roles -> https://docs.trustlesswork.com/trustless-work/technology-overview/roles-in-trustless-work
|
|
204
|
-
*
|
|
205
|
-
* You must pass one or more roles according to requirements
|
|
206
|
-
*
|
|
207
|
-
* For example:
|
|
208
|
-
* - If the user is a freelancer, you must pass the "serviceProvider" and "receiver" role
|
|
209
|
-
*
|
|
210
|
-
* Depending of the role, you'll have different actions buttons
|
|
211
|
-
*/
|
|
212
|
-
const activeRole: Role[] = ["approver"];
|
|
213
|
-
|
|
214
205
|
const escrows = data ?? [];
|
|
215
206
|
|
|
216
207
|
return (
|
|
@@ -231,18 +222,18 @@ export function EscrowsBySignerTable() {
|
|
|
231
222
|
setEngagementId={setEngagementId}
|
|
232
223
|
setIsActive={setIsActive}
|
|
233
224
|
setValidateOnChain={setValidateOnChain}
|
|
234
|
-
setType={
|
|
235
|
-
setStatus={
|
|
225
|
+
setType={setType}
|
|
226
|
+
setStatus={setStatus}
|
|
236
227
|
setMinAmount={setMinAmount}
|
|
237
228
|
setMaxAmount={setMaxAmount}
|
|
238
229
|
setDateRange={setDateRange}
|
|
239
230
|
onClearFilters={onClearFilters}
|
|
240
|
-
onRefresh={
|
|
231
|
+
onRefresh={handleRefresh}
|
|
241
232
|
isRefreshing={isFetching}
|
|
242
233
|
orderBy={orderBy}
|
|
243
234
|
orderDirection={orderDirection}
|
|
244
|
-
setOrderBy={
|
|
245
|
-
setOrderDirection={
|
|
235
|
+
setOrderBy={setOrderBy}
|
|
236
|
+
setOrderDirection={setOrderDirection}
|
|
246
237
|
/>
|
|
247
238
|
|
|
248
239
|
<Card className="w-full py-2 sm:py-4">
|
|
@@ -257,8 +248,8 @@ export function EscrowsBySignerTable() {
|
|
|
257
248
|
const className =
|
|
258
249
|
typeof header.column.columnDef.meta === "object" &&
|
|
259
250
|
header.column.columnDef.meta &&
|
|
260
|
-
"className" in
|
|
261
|
-
?
|
|
251
|
+
"className" in header.column.columnDef.meta
|
|
252
|
+
? header.column.columnDef.meta.className
|
|
262
253
|
: "";
|
|
263
254
|
return (
|
|
264
255
|
<TableHead
|
|
@@ -341,7 +332,7 @@ export function EscrowsBySignerTable() {
|
|
|
341
332
|
<Button
|
|
342
333
|
variant="outline"
|
|
343
334
|
size="sm"
|
|
344
|
-
onClick={
|
|
335
|
+
onClick={handleRefresh}
|
|
345
336
|
>
|
|
346
337
|
<RefreshCw className="h-4 w-4 mr-2" />
|
|
347
338
|
Retry
|
|
@@ -371,8 +362,8 @@ export function EscrowsBySignerTable() {
|
|
|
371
362
|
const className =
|
|
372
363
|
typeof cell.column.columnDef.meta === "object" &&
|
|
373
364
|
cell.column.columnDef.meta &&
|
|
374
|
-
"className" in
|
|
375
|
-
? (cell.column.columnDef.meta as
|
|
365
|
+
"className" in cell.column.columnDef.meta
|
|
366
|
+
? (cell.column.columnDef.meta.className as string)
|
|
376
367
|
: "";
|
|
377
368
|
return (
|
|
378
369
|
<TableCell key={cell.id} className={className}>
|
|
@@ -420,14 +411,13 @@ export function EscrowsBySignerTable() {
|
|
|
420
411
|
</div>
|
|
421
412
|
|
|
422
413
|
{/* Dialog */}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
414
|
+
{dialogStates.second.isOpen ? (
|
|
415
|
+
<EscrowDetailDialog
|
|
416
|
+
isDialogOpen={dialogStates.second.isOpen}
|
|
417
|
+
setIsDialogOpen={dialogStates.second.setIsOpen}
|
|
418
|
+
setSelectedEscrow={setSelectedEscrow}
|
|
419
|
+
/>
|
|
420
|
+
) : null}
|
|
429
421
|
</>
|
|
430
422
|
);
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
export default EscrowsBySignerTable;
|
|
423
|
+
};
|
|
@@ -81,7 +81,7 @@ type FiltersProps = {
|
|
|
81
81
|
setOrderDirection: (v: "asc" | "desc") => void;
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
export const Filters = ({
|
|
85
85
|
title,
|
|
86
86
|
engagementId,
|
|
87
87
|
isActive,
|
|
@@ -108,7 +108,7 @@ function Filters({
|
|
|
108
108
|
onRefresh,
|
|
109
109
|
setOrderBy,
|
|
110
110
|
setOrderDirection,
|
|
111
|
-
}: FiltersProps) {
|
|
111
|
+
}: FiltersProps) => {
|
|
112
112
|
return (
|
|
113
113
|
<div className="w-full bg-card/50 backdrop-blur-sm border border-border/50 rounded-lg p-4 shadow-sm">
|
|
114
114
|
{/* Header Section */}
|
|
@@ -384,6 +384,4 @@ function Filters({
|
|
|
384
384
|
</div>
|
|
385
385
|
</div>
|
|
386
386
|
);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
export default React.memo(Filters);
|
|
387
|
+
};
|
|
@@ -101,8 +101,8 @@ export function useEscrowsBySigner() {
|
|
|
101
101
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
102
102
|
}, []);
|
|
103
103
|
|
|
104
|
-
const
|
|
105
|
-
{
|
|
104
|
+
const stableSearchParams = React.useMemo(
|
|
105
|
+
() => ({
|
|
106
106
|
page,
|
|
107
107
|
orderBy,
|
|
108
108
|
orderDirection,
|
|
@@ -118,10 +118,28 @@ export function useEscrowsBySigner() {
|
|
|
118
118
|
? startOfDay(dateRange.from).toISOString()
|
|
119
119
|
: undefined,
|
|
120
120
|
endDate: dateRange.to ? endOfDay(dateRange.to).toISOString() : undefined,
|
|
121
|
-
},
|
|
122
|
-
|
|
121
|
+
}),
|
|
122
|
+
[
|
|
123
|
+
page,
|
|
124
|
+
orderBy,
|
|
125
|
+
orderDirection,
|
|
126
|
+
debouncedTitle,
|
|
127
|
+
debouncedEngagementId,
|
|
128
|
+
isActive,
|
|
129
|
+
validateOnChain,
|
|
130
|
+
type,
|
|
131
|
+
status,
|
|
132
|
+
debouncedMinAmount,
|
|
133
|
+
debouncedMaxAmount,
|
|
134
|
+
dateRange.from,
|
|
135
|
+
dateRange.to,
|
|
136
|
+
]
|
|
123
137
|
);
|
|
124
138
|
|
|
139
|
+
const debouncedSearchParams = useDebouncedValue(stableSearchParams, 200);
|
|
140
|
+
|
|
141
|
+
const lastQueryStringRef = React.useRef("");
|
|
142
|
+
|
|
125
143
|
React.useEffect(() => {
|
|
126
144
|
if (!pathname) return;
|
|
127
145
|
const qp = new URLSearchParams();
|
|
@@ -137,8 +155,10 @@ export function useEscrowsBySigner() {
|
|
|
137
155
|
qp.set("engagementId", debouncedSearchParams.engagementId);
|
|
138
156
|
qp.set("isActive", String(debouncedSearchParams.isActive));
|
|
139
157
|
qp.set("validateOnChain", String(debouncedSearchParams.validateOnChain));
|
|
140
|
-
if (type && type !== "all")
|
|
141
|
-
|
|
158
|
+
if (debouncedSearchParams.type && debouncedSearchParams.type !== "all")
|
|
159
|
+
qp.set("type", debouncedSearchParams.type);
|
|
160
|
+
if (debouncedSearchParams.status && debouncedSearchParams.status !== "all")
|
|
161
|
+
qp.set("status", debouncedSearchParams.status);
|
|
142
162
|
if (debouncedSearchParams.minAmount)
|
|
143
163
|
qp.set("minAmount", String(debouncedSearchParams.minAmount));
|
|
144
164
|
if (debouncedSearchParams.maxAmount)
|
|
@@ -147,25 +167,12 @@ export function useEscrowsBySigner() {
|
|
|
147
167
|
qp.set("startDate", String(debouncedSearchParams.startDate));
|
|
148
168
|
if (debouncedSearchParams.endDate)
|
|
149
169
|
qp.set("endDate", String(debouncedSearchParams.endDate));
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
debouncedSearchParams.orderBy,
|
|
157
|
-
debouncedSearchParams.orderDirection,
|
|
158
|
-
debouncedSearchParams.title,
|
|
159
|
-
debouncedSearchParams.engagementId,
|
|
160
|
-
debouncedSearchParams.isActive,
|
|
161
|
-
debouncedSearchParams.validateOnChain,
|
|
162
|
-
type,
|
|
163
|
-
status,
|
|
164
|
-
debouncedSearchParams.minAmount,
|
|
165
|
-
debouncedSearchParams.maxAmount,
|
|
166
|
-
debouncedSearchParams.startDate,
|
|
167
|
-
debouncedSearchParams.endDate,
|
|
168
|
-
]);
|
|
170
|
+
const newQs = qp.toString();
|
|
171
|
+
if (lastQueryStringRef.current !== newQs) {
|
|
172
|
+
lastQueryStringRef.current = newQs;
|
|
173
|
+
router.replace(`${pathname}?${newQs}`);
|
|
174
|
+
}
|
|
175
|
+
}, [pathname, router, debouncedSearchParams]);
|
|
169
176
|
|
|
170
177
|
const formattedRangeLabel = React.useMemo(() => {
|
|
171
178
|
if (!dateRange?.from && !dateRange?.to) return "Date range";
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Button } from "__UI_BASE__/button";
|
|
3
|
+
import { useEscrowsMutations } from "@/components/tw-blocks/tanstack/useEscrowsMutations";
|
|
4
|
+
import { useWalletContext } from "@/components/tw-blocks/wallet-kit/WalletProvider";
|
|
5
|
+
import {
|
|
6
|
+
MultiReleaseStartDisputePayload,
|
|
7
|
+
MultiReleaseMilestone,
|
|
8
|
+
} from "@trustless-work/escrow/types";
|
|
9
|
+
import { toast } from "sonner";
|
|
10
|
+
import {
|
|
11
|
+
ErrorResponse,
|
|
12
|
+
handleError,
|
|
13
|
+
} from "@/components/tw-blocks/handle-errors/handle";
|
|
14
|
+
import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
|
|
15
|
+
import { Loader2 } from "lucide-react";
|
|
16
|
+
|
|
17
|
+
type DisputeEscrowButtonProps = {
|
|
18
|
+
milestoneIndex: number | string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const DisputeEscrowButton = ({
|
|
22
|
+
milestoneIndex,
|
|
23
|
+
}: DisputeEscrowButtonProps) => {
|
|
24
|
+
const { startDispute } = useEscrowsMutations();
|
|
25
|
+
const { selectedEscrow, updateEscrow } = useEscrowContext();
|
|
26
|
+
const { walletAddress } = useWalletContext();
|
|
27
|
+
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
28
|
+
|
|
29
|
+
async function handleClick() {
|
|
30
|
+
try {
|
|
31
|
+
setIsSubmitting(true);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create the payload for the dispute escrow mutation
|
|
35
|
+
*
|
|
36
|
+
* @returns The payload for the dispute escrow mutation
|
|
37
|
+
*/
|
|
38
|
+
const payload: MultiReleaseStartDisputePayload = {
|
|
39
|
+
contractId: selectedEscrow?.contractId || "",
|
|
40
|
+
signer: walletAddress || "",
|
|
41
|
+
milestoneIndex: String(milestoneIndex),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Call the dispute escrow mutation
|
|
46
|
+
*
|
|
47
|
+
* @param payload - The payload for the dispute escrow mutation
|
|
48
|
+
* @param type - The type of the escrow
|
|
49
|
+
* @param address - The address of the escrow
|
|
50
|
+
*/
|
|
51
|
+
await startDispute.mutateAsync({
|
|
52
|
+
payload,
|
|
53
|
+
type: "multi-release",
|
|
54
|
+
address: walletAddress || "",
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
toast.success("Escrow disputed successfully");
|
|
58
|
+
|
|
59
|
+
updateEscrow({
|
|
60
|
+
...selectedEscrow,
|
|
61
|
+
milestones: selectedEscrow?.milestones.map((milestone, index) => {
|
|
62
|
+
if (index === Number(milestoneIndex)) {
|
|
63
|
+
return {
|
|
64
|
+
...milestone,
|
|
65
|
+
flags: {
|
|
66
|
+
...(milestone as MultiReleaseMilestone).flags,
|
|
67
|
+
disputed: true,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return milestone;
|
|
72
|
+
}),
|
|
73
|
+
});
|
|
74
|
+
} catch (error) {
|
|
75
|
+
toast.error(handleError(error as ErrorResponse).message);
|
|
76
|
+
} finally {
|
|
77
|
+
setIsSubmitting(false);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<Button
|
|
83
|
+
type="button"
|
|
84
|
+
disabled={isSubmitting || !selectedEscrow?.balance}
|
|
85
|
+
onClick={handleClick}
|
|
86
|
+
className="cursor-pointer w-full"
|
|
87
|
+
>
|
|
88
|
+
{isSubmitting ? (
|
|
89
|
+
<div className="flex items-center">
|
|
90
|
+
<Loader2 className="h-5 w-5 animate-spin" />
|
|
91
|
+
<span className="ml-2">Disputing...</span>
|
|
92
|
+
</div>
|
|
93
|
+
) : (
|
|
94
|
+
"Dispute Milestone"
|
|
95
|
+
)}
|
|
96
|
+
</Button>
|
|
97
|
+
);
|
|
98
|
+
};
|