@trustless-work/blocks 0.0.9 → 1.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/bin/index.js +87 -23
- package/package.json +1 -1
- package/templates/deps.json +1 -1
- package/templates/escrows/details/Actions.tsx +24 -1
- package/templates/escrows/details/MilestoneCard.tsx +2 -2
- package/templates/escrows/escrows-by-role/cards/EscrowsCards.tsx +0 -2
- package/templates/escrows/multi-release/dispute-milestone/button/{DisputeEscrow.tsx → DisputeMilestone.tsx} +4 -4
- package/templates/escrows/multi-release/initialize-escrow/dialog/InitializeEscrow.tsx +1 -0
- package/templates/escrows/multi-release/initialize-escrow/form/InitializeEscrow.tsx +1 -0
- package/templates/escrows/multi-release/initialize-escrow/shared/schema.ts +0 -1
- package/templates/escrows/multi-release/initialize-escrow/shared/useInitializeEscrow.ts +0 -2
- package/templates/escrows/multi-release/release-milestone/button/{ReleaseEscrow.tsx → ReleaseMilestone.tsx} +4 -4
- package/templates/escrows/multi-release/resolve-dispute/button/ResolveDispute.tsx +10 -20
- package/templates/escrows/multi-release/resolve-dispute/dialog/ResolveDispute.tsx +117 -60
- package/templates/escrows/multi-release/resolve-dispute/form/ResolveDispute.tsx +111 -55
- package/templates/escrows/multi-release/resolve-dispute/shared/schema.ts +68 -71
- package/templates/escrows/multi-release/resolve-dispute/shared/useResolveDispute.ts +107 -21
- package/templates/escrows/multi-release/update-escrow/shared/schema.ts +0 -1
- package/templates/escrows/multi-release/update-escrow/shared/useUpdateEscrow.ts +0 -4
- package/templates/escrows/multi-release/withdraw-remaining-funds/button/WithdrawRemainingFunds.tsx +85 -0
- package/templates/escrows/multi-release/withdraw-remaining-funds/dialog/WithdrawRemainingFunds.tsx +176 -0
- package/templates/escrows/multi-release/withdraw-remaining-funds/form/WithdrawRemainingFunds.tsx +153 -0
- package/templates/escrows/multi-release/withdraw-remaining-funds/shared/schema.ts +81 -0
- package/templates/escrows/multi-release/withdraw-remaining-funds/shared/useWithdrawRemainingFunds.ts +160 -0
- package/templates/escrows/single-multi-release/approve-milestone/button/ApproveMilestone.tsx +0 -1
- package/templates/escrows/single-multi-release/approve-milestone/shared/useApproveMilestone.ts +0 -1
- package/templates/escrows/single-release/initialize-escrow/shared/schema.ts +0 -1
- package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +0 -2
- package/templates/escrows/single-release/resolve-dispute/button/ResolveDispute.tsx +15 -31
- package/templates/escrows/single-release/resolve-dispute/dialog/ResolveDispute.tsx +116 -60
- package/templates/escrows/single-release/resolve-dispute/form/ResolveDispute.tsx +98 -43
- package/templates/escrows/single-release/resolve-dispute/shared/schema.ts +65 -68
- package/templates/escrows/single-release/resolve-dispute/shared/useResolveDispute.ts +100 -22
- package/templates/escrows/single-release/update-escrow/shared/schema.ts +0 -1
- package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +0 -4
- package/templates/tanstack/useEscrowsMutations.ts +53 -0
- package/templates/wallet-kit/trustlines.ts +0 -4
package/bin/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
4
|
AUTHOR: @trustless-work / Joel Vargas
|
|
@@ -814,6 +814,49 @@ function copyTemplate(name, { uiBase, shouldInstall = false } = {}) {
|
|
|
814
814
|
);
|
|
815
815
|
}
|
|
816
816
|
|
|
817
|
+
// Post-copy: materialize shared files for multi-release withdraw-remaining-funds
|
|
818
|
+
try {
|
|
819
|
+
const isMultiWithdrawRoot =
|
|
820
|
+
name === "escrows/multi-release/withdraw-remaining-funds";
|
|
821
|
+
const isMultiWithdrawDialog =
|
|
822
|
+
name === "escrows/multi-release/withdraw-remaining-funds/dialog";
|
|
823
|
+
const isMultiWithdrawForm =
|
|
824
|
+
name === "escrows/multi-release/withdraw-remaining-funds/form";
|
|
825
|
+
|
|
826
|
+
const srcSharedDir = path.join(
|
|
827
|
+
TEMPLATES_DIR,
|
|
828
|
+
"escrows",
|
|
829
|
+
"multi-release",
|
|
830
|
+
"withdraw-remaining-funds",
|
|
831
|
+
"shared"
|
|
832
|
+
);
|
|
833
|
+
|
|
834
|
+
function copyMultiWithdrawSharedInto(targetDir) {
|
|
835
|
+
if (!fs.existsSync(srcSharedDir)) return;
|
|
836
|
+
const entries = fs.readdirSync(srcSharedDir, { withFileTypes: true });
|
|
837
|
+
for (const entry of entries) {
|
|
838
|
+
if (!/\.(tsx?|jsx?)$/i.test(entry.name)) continue;
|
|
839
|
+
const entrySrc = path.join(srcSharedDir, entry.name);
|
|
840
|
+
const entryDest = path.join(targetDir, entry.name);
|
|
841
|
+
writeTransformed(entrySrc, entryDest);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
if (isMultiWithdrawRoot) {
|
|
846
|
+
copyMultiWithdrawSharedInto(path.join(destDir, "dialog"));
|
|
847
|
+
copyMultiWithdrawSharedInto(path.join(destDir, "form"));
|
|
848
|
+
} else if (isMultiWithdrawDialog) {
|
|
849
|
+
copyMultiWithdrawSharedInto(destDir);
|
|
850
|
+
} else if (isMultiWithdrawForm) {
|
|
851
|
+
copyMultiWithdrawSharedInto(destDir);
|
|
852
|
+
}
|
|
853
|
+
} catch (e) {
|
|
854
|
+
console.warn(
|
|
855
|
+
"⚠️ Failed to materialize shared multi-release withdraw-remaining-funds files:",
|
|
856
|
+
e?.message || e
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
|
|
817
860
|
try {
|
|
818
861
|
const isMultiUpdateRoot = name === "escrows/multi-release/update-escrow";
|
|
819
862
|
const isMultiUpdateDialog =
|
|
@@ -1034,6 +1077,7 @@ function copyTemplate(name, { uiBase, shouldInstall = false } = {}) {
|
|
|
1034
1077
|
"initialize-escrow",
|
|
1035
1078
|
"resolve-dispute",
|
|
1036
1079
|
"update-escrow",
|
|
1080
|
+
"withdraw-remaining-funds",
|
|
1037
1081
|
];
|
|
1038
1082
|
|
|
1039
1083
|
for (const mod of modules) {
|
|
@@ -1164,6 +1208,7 @@ function copyTemplate(name, { uiBase, shouldInstall = false } = {}) {
|
|
|
1164
1208
|
"initialize-escrow",
|
|
1165
1209
|
"resolve-dispute",
|
|
1166
1210
|
"update-escrow",
|
|
1211
|
+
"withdraw-remaining-funds",
|
|
1167
1212
|
];
|
|
1168
1213
|
|
|
1169
1214
|
const baseTarget = path.join(destDir, "multi-release");
|
|
@@ -1757,9 +1802,6 @@ if (args[0] === "init") {
|
|
|
1757
1802
|
trustless-work add escrows/escrows-by-signer/table
|
|
1758
1803
|
trustless-work add escrows/escrows-by-signer/cards
|
|
1759
1804
|
|
|
1760
|
-
--- Escrow details (optional standalone) ---
|
|
1761
|
-
trustless-work add escrows/details
|
|
1762
|
-
|
|
1763
1805
|
----------------------
|
|
1764
1806
|
--- SINGLE-RELEASE ---
|
|
1765
1807
|
trustless-work add escrows/single-release
|
|
@@ -1769,24 +1811,6 @@ if (args[0] === "init") {
|
|
|
1769
1811
|
- trustless-work add escrows/single-release/initialize-escrow/form
|
|
1770
1812
|
- trustless-work add escrows/single-release/initialize-escrow/dialog
|
|
1771
1813
|
|
|
1772
|
-
--- Approve milestone ---
|
|
1773
|
-
- trustless-work add escrows/single-release/approve-milestone
|
|
1774
|
-
- trustless-work add escrows/single-release/approve-milestone/form
|
|
1775
|
-
- trustless-work add escrows/single-release/approve-milestone/button
|
|
1776
|
-
- trustless-work add escrows/single-release/approve-milestone/dialog
|
|
1777
|
-
|
|
1778
|
-
--- Change milestone status ---
|
|
1779
|
-
- trustless-work add escrows/single-release/change-milestone-status
|
|
1780
|
-
- trustless-work add escrows/single-release/change-milestone-status/form
|
|
1781
|
-
- trustless-work add escrows/single-release/change-milestone-status/button
|
|
1782
|
-
- trustless-work add escrows/single-release/change-milestone-status/dialog
|
|
1783
|
-
|
|
1784
|
-
--- Fund escrow ---
|
|
1785
|
-
- trustless-work add escrows/single-release/fund-escrow
|
|
1786
|
-
- trustless-work add escrows/single-release/fund-escrow/form
|
|
1787
|
-
- trustless-work add escrows/single-release/fund-escrow/button
|
|
1788
|
-
- trustless-work add escrows/single-release/fund-escrow/dialog
|
|
1789
|
-
|
|
1790
1814
|
--- Resolve dispute ---
|
|
1791
1815
|
- trustless-work add escrows/single-release/resolve-dispute
|
|
1792
1816
|
- trustless-work add escrows/single-release/resolve-dispute/form
|
|
@@ -1805,9 +1829,49 @@ if (args[0] === "init") {
|
|
|
1805
1829
|
--- Dispute escrow ---
|
|
1806
1830
|
- trustless-work add escrows/single-release/dispute-escrow
|
|
1807
1831
|
- trustless-work add escrows/single-release/dispute-escrow/button
|
|
1832
|
+
|
|
1833
|
+
----------------------
|
|
1834
|
+
--- MULTI-RELEASE ---
|
|
1835
|
+
trustless-work add escrows/multi-release
|
|
1836
|
+
|
|
1837
|
+
--- Initialize escrow ---
|
|
1838
|
+
- trustless-work add escrows/multi-release/initialize-escrow
|
|
1839
|
+
- trustless-work add escrows/multi-release/initialize-escrow/form
|
|
1840
|
+
- trustless-work add escrows/multi-release/initialize-escrow/dialog
|
|
1841
|
+
|
|
1842
|
+
--- Resolve dispute ---
|
|
1843
|
+
- trustless-work add escrows/multi-release/resolve-dispute
|
|
1844
|
+
- trustless-work add escrows/multi-release/resolve-dispute/form
|
|
1845
|
+
- trustless-work add escrows/multi-release/resolve-dispute/button
|
|
1846
|
+
- trustless-work add escrows/multi-release/resolve-dispute/dialog
|
|
1847
|
+
|
|
1848
|
+
--- Update escrow ---
|
|
1849
|
+
- trustless-work add escrows/multi-release/update-escrow
|
|
1850
|
+
- trustless-work add escrows/multi-release/update-escrow/form
|
|
1851
|
+
- trustless-work add escrows/multi-release/update-escrow/dialog
|
|
1852
|
+
|
|
1853
|
+
--- Withdraw remaining funds ---
|
|
1854
|
+
- trustless-work add escrows/multi-release/withdraw-remaining-funds
|
|
1855
|
+
- trustless-work add escrows/multi-release/withdraw-remaining-funds/form
|
|
1856
|
+
- trustless-work add escrows/multi-release/withdraw-remaining-funds/button
|
|
1857
|
+
- trustless-work add escrows/multi-release/withdraw-remaining-funds/dialog
|
|
1858
|
+
|
|
1859
|
+
--- Release escrow ---
|
|
1860
|
+
- trustless-work add escrows/multi-release/release-milestone
|
|
1861
|
+
- trustless-work add escrows/multi-release/release-milestone/button
|
|
1862
|
+
|
|
1863
|
+
--- Dispute escrow ---
|
|
1864
|
+
- trustless-work add escrows/multi-release/dispute-milestone
|
|
1865
|
+
- trustless-work add escrows/multi-release/dispute-milestone/button
|
|
1866
|
+
|
|
1867
|
+
--- Withdraw remaining funds ---
|
|
1868
|
+
- trustless-work add escrows/multi-release/withdraw-remaining-funds
|
|
1869
|
+
- trustless-work add escrows/multi-release/withdraw-remaining-funds/form
|
|
1870
|
+
- trustless-work add escrows/multi-release/withdraw-remaining-funds/button
|
|
1871
|
+
- trustless-work add escrows/multi-release/withdraw-remaining-funds/dialog
|
|
1808
1872
|
|
|
1809
1873
|
----------------------
|
|
1810
|
-
--- SINGLE-MULTI-RELEASE ---
|
|
1874
|
+
--- SINGLE-MULTI-RELEASE -> Works with both types of escrows ---
|
|
1811
1875
|
trustless-work add escrows/single-multi-release
|
|
1812
1876
|
|
|
1813
1877
|
--- Approve milestone ---
|
package/package.json
CHANGED
package/templates/deps.json
CHANGED
|
@@ -113,11 +113,28 @@ export const Actions = ({
|
|
|
113
113
|
userRolesInEscrow.includes("releaseSigner") &&
|
|
114
114
|
!selectedEscrow.flags?.released;
|
|
115
115
|
|
|
116
|
+
const shouldShowWithdrawRemaining = (() => {
|
|
117
|
+
if (selectedEscrow.type !== "multi-release") return false;
|
|
118
|
+
if (!userRolesInEscrow.includes("disputeResolver")) return false;
|
|
119
|
+
if ((selectedEscrow.balance ?? 0) === 0) return false;
|
|
120
|
+
const milestones = (selectedEscrow.milestones || []) as Array<{
|
|
121
|
+
flags?: { resolved?: boolean; released?: boolean; disputed?: boolean };
|
|
122
|
+
}>;
|
|
123
|
+
return (
|
|
124
|
+
milestones.length > 0 &&
|
|
125
|
+
milestones.every((m) => {
|
|
126
|
+
const f = m.flags || {};
|
|
127
|
+
return !!(f.resolved || f.released || f.disputed);
|
|
128
|
+
})
|
|
129
|
+
);
|
|
130
|
+
})();
|
|
131
|
+
|
|
116
132
|
const hasConditionalButtons =
|
|
117
133
|
shouldShowEditButton ||
|
|
118
134
|
shouldShowDisputeButton ||
|
|
119
135
|
shouldShowResolveButton ||
|
|
120
|
-
shouldShowReleaseFundsButton
|
|
136
|
+
shouldShowReleaseFundsButton ||
|
|
137
|
+
shouldShowWithdrawRemaining;
|
|
121
138
|
|
|
122
139
|
return (
|
|
123
140
|
<div className="flex items-start justify-start flex-col gap-2 w-full">
|
|
@@ -128,13 +145,19 @@ export const Actions = ({
|
|
|
128
145
|
{/* {shouldShowEditButton && <UpdateEscrowDialog />} */}
|
|
129
146
|
|
|
130
147
|
{/* Works only with single-release escrows */}
|
|
148
|
+
{/* Only appears if the escrow has balance */}
|
|
131
149
|
{/* {shouldShowDisputeButton && <DisputeEscrowButton />} */}
|
|
132
150
|
|
|
133
151
|
{/* Works only with single-release escrows */}
|
|
152
|
+
{/* Only appears if the escrow is disputed */}
|
|
134
153
|
{/* {shouldShowResolveButton && <ResolveDisputeDialog />} */}
|
|
135
154
|
|
|
136
155
|
{/* Works only with single-release escrows */}
|
|
156
|
+
{/* Only appears if all the milestones are approved */}
|
|
137
157
|
{/* {shouldShowReleaseFundsButton && <ReleaseEscrowButton />} */}
|
|
158
|
+
|
|
159
|
+
{/* Multi-release: Withdraw Remaining Funds */}
|
|
160
|
+
{/* {shouldShowWithdrawRemaining && <WithdrawRemainingFundsDialog />} */}
|
|
138
161
|
</div>
|
|
139
162
|
)}
|
|
140
163
|
|
|
@@ -141,7 +141,7 @@ const MilestoneCardComponent = ({
|
|
|
141
141
|
buttons
|
|
142
142
|
.push
|
|
143
143
|
// You can add the button here, using the button from the blocks. This button is conditional based on the milestone status and the user roles. Works only with multi-release escrows.
|
|
144
|
-
// <
|
|
144
|
+
// <ReleaseMilestoneButton
|
|
145
145
|
// key={`release-${milestoneIndex}`}
|
|
146
146
|
// milestoneIndex={milestoneIndex}
|
|
147
147
|
// />
|
|
@@ -159,7 +159,7 @@ const MilestoneCardComponent = ({
|
|
|
159
159
|
buttons
|
|
160
160
|
.push
|
|
161
161
|
// You can add the button here, using the button from the blocks. This button is conditional based on the milestone status and the user roles. Works only with multi-release escrows.
|
|
162
|
-
// <
|
|
162
|
+
// <DisputeMilestoneButton
|
|
163
163
|
// key={`dispute-${milestoneIndex}`}
|
|
164
164
|
// milestoneIndex={milestoneIndex}
|
|
165
165
|
// />
|
|
@@ -14,13 +14,13 @@ import {
|
|
|
14
14
|
import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
|
|
15
15
|
import { Loader2 } from "lucide-react";
|
|
16
16
|
|
|
17
|
-
type
|
|
17
|
+
type DisputeMilestoneButtonProps = {
|
|
18
18
|
milestoneIndex: number | string;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
export const
|
|
21
|
+
export const DisputeMilestoneButton = ({
|
|
22
22
|
milestoneIndex,
|
|
23
|
-
}:
|
|
23
|
+
}: DisputeMilestoneButtonProps) => {
|
|
24
24
|
const { startDispute } = useEscrowsMutations();
|
|
25
25
|
const { selectedEscrow, updateEscrow } = useEscrowContext();
|
|
26
26
|
const { walletAddress } = useWalletContext();
|
|
@@ -54,7 +54,7 @@ export const DisputeEscrowButton = ({
|
|
|
54
54
|
address: walletAddress || "",
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
-
toast.success("
|
|
57
|
+
toast.success("Milestone disputed successfully");
|
|
58
58
|
|
|
59
59
|
updateEscrow({
|
|
60
60
|
...selectedEscrow,
|
|
@@ -482,6 +482,7 @@ export const InitializeEscrowDialog = () => {
|
|
|
482
482
|
onClick={() => handleRemoveMilestone(index)}
|
|
483
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
484
|
disabled={milestones.length === 1}
|
|
485
|
+
type="button"
|
|
485
486
|
>
|
|
486
487
|
<Trash2 className="h-5 w-5" />
|
|
487
488
|
</Button>
|
|
@@ -462,6 +462,7 @@ export const InitializeEscrowForm = () => {
|
|
|
462
462
|
onClick={() => handleRemoveMilestone(index)}
|
|
463
463
|
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"
|
|
464
464
|
disabled={milestones.length === 1}
|
|
465
|
+
type="button"
|
|
465
466
|
>
|
|
466
467
|
<Trash2 className="h-5 w-5" />
|
|
467
468
|
</Button>
|
|
@@ -37,7 +37,6 @@ export function useInitializeEscrow() {
|
|
|
37
37
|
receiverMemo: "",
|
|
38
38
|
trustline: {
|
|
39
39
|
address: "",
|
|
40
|
-
decimals: 10000000,
|
|
41
40
|
},
|
|
42
41
|
roles: {
|
|
43
42
|
approver: "",
|
|
@@ -83,7 +82,6 @@ export function useInitializeEscrow() {
|
|
|
83
82
|
receiverMemo: "123",
|
|
84
83
|
trustline: {
|
|
85
84
|
address: usdc?.value || "",
|
|
86
|
-
decimals: 10000000,
|
|
87
85
|
},
|
|
88
86
|
roles: {
|
|
89
87
|
approver: walletAddress || "",
|
|
@@ -16,13 +16,13 @@ import { useEscrowDialogs } from "@/components/tw-blocks/providers/EscrowDialogs
|
|
|
16
16
|
import { useEscrowAmountContext } from "@/components/tw-blocks/providers/EscrowAmountProvider";
|
|
17
17
|
import { Loader2 } from "lucide-react";
|
|
18
18
|
|
|
19
|
-
type
|
|
19
|
+
type ReleaseMilestoneButtonProps = {
|
|
20
20
|
milestoneIndex: number | string;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
export const
|
|
23
|
+
export const ReleaseMilestoneButton = ({
|
|
24
24
|
milestoneIndex,
|
|
25
|
-
}:
|
|
25
|
+
}: ReleaseMilestoneButtonProps) => {
|
|
26
26
|
const { releaseFunds } = useEscrowsMutations();
|
|
27
27
|
const { selectedEscrow, updateEscrow } = useEscrowContext();
|
|
28
28
|
const dialogStates = useEscrowDialogs();
|
|
@@ -58,7 +58,7 @@ export const ReleaseEscrowButton = ({
|
|
|
58
58
|
address: walletAddress || "",
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
toast.success("
|
|
61
|
+
toast.success("Milestone released successfully");
|
|
62
62
|
|
|
63
63
|
// Ensure amounts are up to date for the success dialog
|
|
64
64
|
if (selectedEscrow) {
|
|
@@ -15,14 +15,12 @@ import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvide
|
|
|
15
15
|
import { Loader2 } from "lucide-react";
|
|
16
16
|
|
|
17
17
|
type ResolveDisputeButtonProps = {
|
|
18
|
-
|
|
19
|
-
receiverFunds: number;
|
|
18
|
+
distributions: { address: string; amount: number }[];
|
|
20
19
|
milestoneIndex: number | string;
|
|
21
20
|
};
|
|
22
21
|
|
|
23
22
|
export const ResolveDisputeButton = ({
|
|
24
|
-
|
|
25
|
-
receiverFunds,
|
|
23
|
+
distributions,
|
|
26
24
|
milestoneIndex,
|
|
27
25
|
}: ResolveDisputeButtonProps) => {
|
|
28
26
|
const { resolveDispute } = useEscrowsMutations();
|
|
@@ -32,18 +30,11 @@ export const ResolveDisputeButton = ({
|
|
|
32
30
|
|
|
33
31
|
async function handleClick() {
|
|
34
32
|
try {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
) {
|
|
41
|
-
toast.error("Both amounts are required");
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (approverFunds < 0 || receiverFunds < 0) {
|
|
46
|
-
toast.error("Amounts must be >= 0");
|
|
33
|
+
const hasInvalid = distributions.some(
|
|
34
|
+
(d) => !d.address || Number.isNaN(d.amount) || d.amount < 0
|
|
35
|
+
);
|
|
36
|
+
if (hasInvalid) {
|
|
37
|
+
toast.error("Invalid distributions");
|
|
47
38
|
return;
|
|
48
39
|
}
|
|
49
40
|
|
|
@@ -57,8 +48,7 @@ export const ResolveDisputeButton = ({
|
|
|
57
48
|
const payload: MultiReleaseResolveDisputePayload = {
|
|
58
49
|
contractId: selectedEscrow?.contractId || "",
|
|
59
50
|
disputeResolver: walletAddress || "",
|
|
60
|
-
|
|
61
|
-
receiverFunds: Number(receiverFunds),
|
|
51
|
+
distributions: distributions as [{ address: string; amount: number }],
|
|
62
52
|
milestoneIndex: String(milestoneIndex),
|
|
63
53
|
};
|
|
64
54
|
|
|
@@ -92,8 +82,8 @@ export const ResolveDisputeButton = ({
|
|
|
92
82
|
return milestone;
|
|
93
83
|
}),
|
|
94
84
|
balance:
|
|
95
|
-
selectedEscrow?.balance ||
|
|
96
|
-
|
|
85
|
+
(selectedEscrow?.balance || 0) -
|
|
86
|
+
distributions.reduce((acc, d) => acc + Number(d.amount || 0), 0),
|
|
97
87
|
});
|
|
98
88
|
} catch (error) {
|
|
99
89
|
toast.error(handleError(error as ErrorResponse).message);
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
DialogTitle,
|
|
17
17
|
DialogTrigger,
|
|
18
18
|
} from "__UI_BASE__/dialog";
|
|
19
|
-
import { Loader2 } from "lucide-react";
|
|
19
|
+
import { Loader2, Trash2 } from "lucide-react";
|
|
20
20
|
import { useResolveDispute } from "./useResolveDispute";
|
|
21
21
|
import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
|
|
22
22
|
import { formatCurrency } from "../../../../helpers/format.helper";
|
|
@@ -35,7 +35,22 @@ export const ResolveDisputeDialog = ({
|
|
|
35
35
|
showSelectMilestone?: boolean;
|
|
36
36
|
milestoneIndex?: number | string;
|
|
37
37
|
}) => {
|
|
38
|
-
const {
|
|
38
|
+
const {
|
|
39
|
+
form,
|
|
40
|
+
handleSubmit,
|
|
41
|
+
isSubmitting,
|
|
42
|
+
totalAmount,
|
|
43
|
+
distributions,
|
|
44
|
+
handleAddDistribution,
|
|
45
|
+
handleRemoveDistribution,
|
|
46
|
+
handleDistributionAddressChange,
|
|
47
|
+
handleDistributionAmountChange,
|
|
48
|
+
isAnyDistributionEmpty,
|
|
49
|
+
allowedAmount,
|
|
50
|
+
distributedSum,
|
|
51
|
+
isExactMatch,
|
|
52
|
+
difference,
|
|
53
|
+
} = useResolveDispute();
|
|
39
54
|
const { selectedEscrow } = useEscrowContext();
|
|
40
55
|
|
|
41
56
|
React.useEffect(() => {
|
|
@@ -55,7 +70,7 @@ export const ResolveDisputeDialog = ({
|
|
|
55
70
|
Resolve Dispute
|
|
56
71
|
</Button>
|
|
57
72
|
</DialogTrigger>
|
|
58
|
-
<DialogContent>
|
|
73
|
+
<DialogContent className="!w-full sm:!max-w-3xl max-h-[95vh] overflow-y-auto">
|
|
59
74
|
<DialogHeader>
|
|
60
75
|
<DialogTitle>Resolve Dispute</DialogTitle>
|
|
61
76
|
</DialogHeader>
|
|
@@ -96,52 +111,110 @@ export const ResolveDisputeDialog = ({
|
|
|
96
111
|
/>
|
|
97
112
|
)}
|
|
98
113
|
|
|
99
|
-
<
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
name="approverFunds"
|
|
103
|
-
render={({ field }) => (
|
|
104
|
-
<FormItem>
|
|
105
|
-
<FormLabel>Approver Funds</FormLabel>
|
|
106
|
-
<FormControl>
|
|
107
|
-
<Input
|
|
108
|
-
type="text"
|
|
109
|
-
inputMode="decimal"
|
|
110
|
-
placeholder="Enter approver funds"
|
|
111
|
-
value={field.value as unknown as string}
|
|
112
|
-
onChange={(e) => field.onChange(e.target.value)}
|
|
113
|
-
/>
|
|
114
|
-
</FormControl>
|
|
115
|
-
<FormMessage />
|
|
116
|
-
</FormItem>
|
|
117
|
-
)}
|
|
118
|
-
/>
|
|
114
|
+
<FormLabel className="flex items-center my-4">
|
|
115
|
+
Distributions<span className="text-destructive ml-1">*</span>
|
|
116
|
+
</FormLabel>
|
|
119
117
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
118
|
+
{distributions.map((d, idx) => (
|
|
119
|
+
<div
|
|
120
|
+
key={`dist-${idx}`}
|
|
121
|
+
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-[1fr_minmax(140px,220px)_auto] gap-3 sm:gap-4 items-end mb-2"
|
|
122
|
+
>
|
|
123
|
+
<FormField
|
|
124
|
+
control={form.control}
|
|
125
|
+
name={`distributions.${idx}.address` as const}
|
|
126
|
+
render={() => (
|
|
127
|
+
<FormItem className="sm:col-span-2 lg:col-span-1">
|
|
128
|
+
<FormLabel>Address</FormLabel>
|
|
129
|
+
<FormControl>
|
|
130
|
+
<Input
|
|
131
|
+
type="text"
|
|
132
|
+
placeholder="Receiver address"
|
|
133
|
+
value={d.address}
|
|
134
|
+
onChange={(e) =>
|
|
135
|
+
handleDistributionAddressChange(idx, e.target.value)
|
|
136
|
+
}
|
|
137
|
+
/>
|
|
138
|
+
</FormControl>
|
|
139
|
+
<FormMessage />
|
|
140
|
+
</FormItem>
|
|
141
|
+
)}
|
|
142
|
+
/>
|
|
143
|
+
|
|
144
|
+
<FormField
|
|
145
|
+
control={form.control}
|
|
146
|
+
name={`distributions.${idx}.amount` as const}
|
|
147
|
+
render={() => (
|
|
148
|
+
<FormItem>
|
|
149
|
+
<FormLabel>Amount</FormLabel>
|
|
150
|
+
<FormControl>
|
|
151
|
+
<Input
|
|
152
|
+
type="text"
|
|
153
|
+
inputMode="decimal"
|
|
154
|
+
placeholder="0.00"
|
|
155
|
+
value={(d.amount as string) ?? ""}
|
|
156
|
+
onChange={(e) =>
|
|
157
|
+
handleDistributionAmountChange(idx, e)
|
|
158
|
+
}
|
|
159
|
+
/>
|
|
160
|
+
</FormControl>
|
|
161
|
+
<FormMessage />
|
|
162
|
+
</FormItem>
|
|
163
|
+
)}
|
|
164
|
+
/>
|
|
165
|
+
|
|
166
|
+
<Button
|
|
167
|
+
type="button"
|
|
168
|
+
onClick={() => handleRemoveDistribution(idx)}
|
|
169
|
+
className="justify-self-end self-end 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"
|
|
170
|
+
disabled={distributions.length <= 2}
|
|
171
|
+
>
|
|
172
|
+
<Trash2 className="h-5 w-5" />
|
|
173
|
+
</Button>
|
|
174
|
+
</div>
|
|
175
|
+
))}
|
|
176
|
+
|
|
177
|
+
<div className="flex justify-between items-center mt-4">
|
|
178
|
+
<Button
|
|
179
|
+
type="button"
|
|
180
|
+
variant="outline"
|
|
181
|
+
onClick={handleAddDistribution}
|
|
182
|
+
disabled={isAnyDistributionEmpty}
|
|
183
|
+
className="cursor-pointer"
|
|
184
|
+
>
|
|
185
|
+
Add Item
|
|
186
|
+
</Button>
|
|
187
|
+
|
|
188
|
+
<div className="flex items-center gap-4">
|
|
189
|
+
<div className="text-xs text-muted-foreground">
|
|
190
|
+
<p>
|
|
191
|
+
<span className="font-bold">Total Amount: </span>
|
|
192
|
+
{distributedSum.toFixed(2)} / {allowedAmount.toFixed(2)}
|
|
193
|
+
</p>
|
|
194
|
+
{!isExactMatch && (
|
|
195
|
+
<p className="text-destructive">
|
|
196
|
+
<span className="font-bold">Difference: </span>
|
|
197
|
+
{difference.toFixed(2)}
|
|
198
|
+
</p>
|
|
199
|
+
)}
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<p className="text-xs text-muted-foreground">
|
|
203
|
+
<span className="font-bold">Total Balance: </span>
|
|
204
|
+
{formatCurrency(
|
|
205
|
+
selectedEscrow?.balance || 0,
|
|
206
|
+
selectedEscrow?.trustline.name || ""
|
|
207
|
+
)}
|
|
208
|
+
</p>
|
|
209
|
+
</div>
|
|
139
210
|
</div>
|
|
140
211
|
|
|
141
|
-
<div className="mt-4 flex justify-
|
|
212
|
+
<div className="mt-4 flex justify-start items-center">
|
|
142
213
|
<Button
|
|
143
214
|
type="submit"
|
|
144
|
-
disabled={
|
|
215
|
+
disabled={
|
|
216
|
+
isSubmitting || isAnyDistributionEmpty || !isExactMatch
|
|
217
|
+
}
|
|
145
218
|
className="cursor-pointer"
|
|
146
219
|
>
|
|
147
220
|
{isSubmitting ? (
|
|
@@ -153,22 +226,6 @@ export const ResolveDisputeDialog = ({
|
|
|
153
226
|
"Resolve"
|
|
154
227
|
)}
|
|
155
228
|
</Button>
|
|
156
|
-
|
|
157
|
-
<p className="text-xs text-muted-foreground">
|
|
158
|
-
<span className="font-bold">Total Amount: </span>
|
|
159
|
-
{formatCurrency(
|
|
160
|
-
totalAmount,
|
|
161
|
-
selectedEscrow?.trustline.name || ""
|
|
162
|
-
)}
|
|
163
|
-
</p>
|
|
164
|
-
|
|
165
|
-
<p className="text-xs text-muted-foreground">
|
|
166
|
-
<span className="font-bold">Total Balance: </span>
|
|
167
|
-
{formatCurrency(
|
|
168
|
-
selectedEscrow?.balance || 0,
|
|
169
|
-
selectedEscrow?.trustline.name || ""
|
|
170
|
-
)}
|
|
171
|
-
</p>
|
|
172
229
|
</div>
|
|
173
230
|
</form>
|
|
174
231
|
</Form>
|