@trustless-work/blocks 0.0.7 → 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/bin/index.js +485 -17
- package/package.json +1 -1
- 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 +16 -16
- package/templates/escrows/details/GeneralInformation.tsx +19 -22
- 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 +4 -6
- package/templates/escrows/escrows-by-role/cards/EscrowsCards.tsx +84 -49
- package/templates/escrows/escrows-by-role/cards/Filters.tsx +3 -5
- package/templates/escrows/escrows-by-role/table/EscrowsTable.tsx +8 -26
- package/templates/escrows/escrows-by-role/table/Filters.tsx +3 -5
- package/templates/escrows/escrows-by-signer/cards/EscrowsCards.tsx +89 -55
- package/templates/escrows/escrows-by-signer/cards/Filters.tsx +3 -5
- package/templates/escrows/escrows-by-signer/table/EscrowsTable.tsx +8 -24
- package/templates/escrows/escrows-by-signer/table/Filters.tsx +3 -5
- 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 +20 -7
- package/templates/escrows/{single-release → single-multi-release}/approve-milestone/dialog/ApproveMilestone.tsx +3 -3
- package/templates/escrows/{single-release → single-multi-release}/approve-milestone/form/ApproveMilestone.tsx +3 -3
- package/templates/escrows/{single-release/approve-milestone/shared → single-multi-release/approve-milestone}/useApproveMilestone.ts +16 -16
- package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/button/ChangeMilestoneStatus.tsx +4 -4
- package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/dialog/ChangeMilestoneStatus.tsx +4 -4
- package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/form/ChangeMilestoneStatus.tsx +3 -3
- package/templates/escrows/{single-release/change-milestone-status/shared → single-multi-release/change-milestone-status}/useChangeMilestoneStatus.ts +1 -1
- package/templates/escrows/{single-release → single-multi-release}/fund-escrow/button/FundEscrow.tsx +3 -3
- 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 +1 -1
- package/templates/escrows/single-release/dispute-escrow/button/DisputeEscrow.tsx +2 -2
- 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 +42 -1
- package/templates/escrows/single-release/release-escrow/button/ReleaseEscrow.tsx +2 -2
- package/templates/escrows/single-release/resolve-dispute/button/ResolveDispute.tsx +3 -3
- package/templates/escrows/single-release/resolve-dispute/dialog/ResolveDispute.tsx +3 -6
- package/templates/escrows/single-release/resolve-dispute/form/ResolveDispute.tsx +2 -2
- package/templates/escrows/single-release/resolve-dispute/shared/useResolveDispute.ts +14 -1
- 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 +12 -7
- package/templates/providers/EscrowDialogsProvider.tsx +1 -3
- package/templates/providers/EscrowProvider.tsx +27 -4
- 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
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import {
|
|
3
|
+
Form,
|
|
4
|
+
FormField,
|
|
5
|
+
FormItem,
|
|
6
|
+
FormLabel,
|
|
7
|
+
FormControl,
|
|
8
|
+
FormMessage,
|
|
9
|
+
} from "__UI_BASE__/form";
|
|
10
|
+
import { Input } from "__UI_BASE__/input";
|
|
11
|
+
import { Button } from "__UI_BASE__/button";
|
|
12
|
+
import { useResolveDispute } from "./useResolveDispute";
|
|
13
|
+
import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
|
|
14
|
+
import { Loader2 } from "lucide-react";
|
|
15
|
+
import {
|
|
16
|
+
Select,
|
|
17
|
+
SelectContent,
|
|
18
|
+
SelectItem,
|
|
19
|
+
SelectTrigger,
|
|
20
|
+
SelectValue,
|
|
21
|
+
} from "__UI_BASE__/select";
|
|
22
|
+
import { formatCurrency } from "../../../../helpers/format.helper";
|
|
23
|
+
|
|
24
|
+
export const ResolveDisputeForm = ({
|
|
25
|
+
showSelectMilestone = false,
|
|
26
|
+
milestoneIndex,
|
|
27
|
+
}: {
|
|
28
|
+
showSelectMilestone?: boolean;
|
|
29
|
+
milestoneIndex?: number | string;
|
|
30
|
+
}) => {
|
|
31
|
+
const { form, handleSubmit, isSubmitting, totalAmount } = useResolveDispute();
|
|
32
|
+
const { selectedEscrow } = useEscrowContext();
|
|
33
|
+
|
|
34
|
+
React.useEffect(() => {
|
|
35
|
+
if (
|
|
36
|
+
!showSelectMilestone &&
|
|
37
|
+
milestoneIndex !== undefined &&
|
|
38
|
+
milestoneIndex !== null
|
|
39
|
+
) {
|
|
40
|
+
form.setValue("milestoneIndex", String(milestoneIndex));
|
|
41
|
+
}
|
|
42
|
+
}, [showSelectMilestone, milestoneIndex, form]);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<Form {...form}>
|
|
46
|
+
<form onSubmit={handleSubmit} className="flex flex-col space-y-6 w-full">
|
|
47
|
+
{showSelectMilestone && (
|
|
48
|
+
<FormField
|
|
49
|
+
control={form.control}
|
|
50
|
+
name="milestoneIndex"
|
|
51
|
+
render={({ field }) => (
|
|
52
|
+
<FormItem>
|
|
53
|
+
<FormLabel className="flex items-center">
|
|
54
|
+
Milestone
|
|
55
|
+
<span className="text-destructive ml-1">*</span>
|
|
56
|
+
</FormLabel>
|
|
57
|
+
<FormControl>
|
|
58
|
+
<Select
|
|
59
|
+
value={field.value}
|
|
60
|
+
onValueChange={(e) => {
|
|
61
|
+
field.onChange(e);
|
|
62
|
+
}}
|
|
63
|
+
>
|
|
64
|
+
<SelectTrigger className="w-full">
|
|
65
|
+
<SelectValue placeholder="Select milestone" />
|
|
66
|
+
</SelectTrigger>
|
|
67
|
+
<SelectContent>
|
|
68
|
+
{(selectedEscrow?.milestones || []).map((m, idx) => (
|
|
69
|
+
<SelectItem key={`ms-${idx}`} value={String(idx)}>
|
|
70
|
+
{m?.description || `Milestone ${idx + 1}`}
|
|
71
|
+
</SelectItem>
|
|
72
|
+
))}
|
|
73
|
+
</SelectContent>
|
|
74
|
+
</Select>
|
|
75
|
+
</FormControl>
|
|
76
|
+
<FormMessage />
|
|
77
|
+
</FormItem>
|
|
78
|
+
)}
|
|
79
|
+
/>
|
|
80
|
+
)}
|
|
81
|
+
|
|
82
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
83
|
+
<FormField
|
|
84
|
+
control={form.control}
|
|
85
|
+
name="approverFunds"
|
|
86
|
+
render={({ field }) => (
|
|
87
|
+
<FormItem>
|
|
88
|
+
<FormLabel>Approver Funds</FormLabel>
|
|
89
|
+
<FormControl>
|
|
90
|
+
<Input
|
|
91
|
+
type="text"
|
|
92
|
+
inputMode="decimal"
|
|
93
|
+
placeholder="Enter approver funds"
|
|
94
|
+
value={field.value as unknown as string}
|
|
95
|
+
onChange={(e) => field.onChange(e.target.value)}
|
|
96
|
+
/>
|
|
97
|
+
</FormControl>
|
|
98
|
+
<FormMessage />
|
|
99
|
+
</FormItem>
|
|
100
|
+
)}
|
|
101
|
+
/>
|
|
102
|
+
|
|
103
|
+
<FormField
|
|
104
|
+
control={form.control}
|
|
105
|
+
name="receiverFunds"
|
|
106
|
+
render={({ field }) => (
|
|
107
|
+
<FormItem>
|
|
108
|
+
<FormLabel>Receiver Funds</FormLabel>
|
|
109
|
+
<FormControl>
|
|
110
|
+
<Input
|
|
111
|
+
type="text"
|
|
112
|
+
inputMode="decimal"
|
|
113
|
+
placeholder="Enter receiver funds"
|
|
114
|
+
value={field.value as unknown as string}
|
|
115
|
+
onChange={(e) => field.onChange(e.target.value)}
|
|
116
|
+
/>
|
|
117
|
+
</FormControl>
|
|
118
|
+
<FormMessage />
|
|
119
|
+
</FormItem>
|
|
120
|
+
)}
|
|
121
|
+
/>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<div className="mt-4">
|
|
125
|
+
<Button
|
|
126
|
+
type="submit"
|
|
127
|
+
disabled={isSubmitting}
|
|
128
|
+
className="cursor-pointer"
|
|
129
|
+
>
|
|
130
|
+
{isSubmitting ? (
|
|
131
|
+
<div className="flex items-center">
|
|
132
|
+
<Loader2 className="h-5 w-5 animate-spin" />
|
|
133
|
+
<span className="ml-2">Resolving...</span>
|
|
134
|
+
</div>
|
|
135
|
+
) : (
|
|
136
|
+
"Resolve"
|
|
137
|
+
)}
|
|
138
|
+
</Button>
|
|
139
|
+
|
|
140
|
+
<p className="text-xs text-muted-foreground">
|
|
141
|
+
<span className="font-bold">Total Amount: </span>
|
|
142
|
+
{formatCurrency(totalAmount, selectedEscrow?.trustline.name || "")}
|
|
143
|
+
</p>
|
|
144
|
+
|
|
145
|
+
<p className="text-xs text-muted-foreground">
|
|
146
|
+
<span className="font-bold">Total Balance: </span>
|
|
147
|
+
{formatCurrency(
|
|
148
|
+
selectedEscrow?.balance || 0,
|
|
149
|
+
selectedEscrow?.trustline.name || ""
|
|
150
|
+
)}
|
|
151
|
+
</p>
|
|
152
|
+
</div>
|
|
153
|
+
</form>
|
|
154
|
+
</Form>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const getFormSchema = () => {
|
|
4
|
+
return z.object({
|
|
5
|
+
approverFunds: z
|
|
6
|
+
.union([z.string(), z.number()])
|
|
7
|
+
.refine(
|
|
8
|
+
(val) => {
|
|
9
|
+
if (typeof val === "string") {
|
|
10
|
+
if (val === "" || val === "." || val.endsWith(".")) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
const numVal = Number(val);
|
|
14
|
+
return !isNaN(numVal) && numVal >= 0;
|
|
15
|
+
}
|
|
16
|
+
return val >= 0;
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
message: "Approver funds must be 0 or greater.",
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
.refine(
|
|
23
|
+
(val) => {
|
|
24
|
+
if (typeof val === "string") {
|
|
25
|
+
if (val === "" || val === "." || val.endsWith(".")) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
const numVal = Number(val);
|
|
29
|
+
if (isNaN(numVal)) return false;
|
|
30
|
+
const decimalPlaces = (numVal.toString().split(".")[1] || "")
|
|
31
|
+
.length;
|
|
32
|
+
return decimalPlaces <= 2;
|
|
33
|
+
}
|
|
34
|
+
const decimalPlaces = (val.toString().split(".")[1] || "").length;
|
|
35
|
+
return decimalPlaces <= 2;
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
message: "Approver funds can have a maximum of 2 decimal places.",
|
|
39
|
+
}
|
|
40
|
+
),
|
|
41
|
+
receiverFunds: z
|
|
42
|
+
.union([z.string(), z.number()])
|
|
43
|
+
.refine(
|
|
44
|
+
(val) => {
|
|
45
|
+
if (typeof val === "string") {
|
|
46
|
+
if (val === "" || val === "." || val.endsWith(".")) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
const numVal = Number(val);
|
|
50
|
+
return !isNaN(numVal) && numVal >= 0;
|
|
51
|
+
}
|
|
52
|
+
return val >= 0;
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
message: "Receiver funds must be 0 or greater.",
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
.refine(
|
|
59
|
+
(val) => {
|
|
60
|
+
if (typeof val === "string") {
|
|
61
|
+
if (val === "" || val === "." || val.endsWith(".")) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
const numVal = Number(val);
|
|
65
|
+
if (isNaN(numVal)) return false;
|
|
66
|
+
const decimalPlaces = (numVal.toString().split(".")[1] || "")
|
|
67
|
+
.length;
|
|
68
|
+
return decimalPlaces <= 2;
|
|
69
|
+
}
|
|
70
|
+
const decimalPlaces = (val.toString().split(".")[1] || "").length;
|
|
71
|
+
return decimalPlaces <= 2;
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
message: "Receiver funds can have a maximum of 2 decimal places.",
|
|
75
|
+
}
|
|
76
|
+
),
|
|
77
|
+
milestoneIndex: z
|
|
78
|
+
.string({ required_error: "Milestone is required" })
|
|
79
|
+
.min(1, { message: "Milestone is required" }),
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const resolveDisputeSchema = getFormSchema();
|
|
84
|
+
|
|
85
|
+
export type ResolveDisputeValues = z.infer<typeof resolveDisputeSchema>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { useForm } from "react-hook-form";
|
|
3
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
4
|
+
import { resolveDisputeSchema, type ResolveDisputeValues } from "./schema";
|
|
5
|
+
import { toast } from "sonner";
|
|
6
|
+
import {
|
|
7
|
+
MultiReleaseResolveDisputePayload,
|
|
8
|
+
MultiReleaseMilestone,
|
|
9
|
+
} from "@trustless-work/escrow";
|
|
10
|
+
import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
|
|
11
|
+
import { useEscrowsMutations } from "@/components/tw-blocks/tanstack/useEscrowsMutations";
|
|
12
|
+
import {
|
|
13
|
+
ErrorResponse,
|
|
14
|
+
handleError,
|
|
15
|
+
} from "@/components/tw-blocks/handle-errors/handle";
|
|
16
|
+
import { useWalletContext } from "@/components/tw-blocks/wallet-kit/WalletProvider";
|
|
17
|
+
|
|
18
|
+
export function useResolveDispute() {
|
|
19
|
+
const { resolveDispute } = useEscrowsMutations();
|
|
20
|
+
const { selectedEscrow, updateEscrow } = useEscrowContext();
|
|
21
|
+
const { walletAddress } = useWalletContext();
|
|
22
|
+
|
|
23
|
+
const form = useForm<ResolveDisputeValues>({
|
|
24
|
+
resolver: zodResolver(resolveDisputeSchema),
|
|
25
|
+
defaultValues: {
|
|
26
|
+
approverFunds: 0,
|
|
27
|
+
receiverFunds: 0,
|
|
28
|
+
milestoneIndex: "0",
|
|
29
|
+
},
|
|
30
|
+
mode: "onChange",
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const totalAmount = React.useMemo(() => {
|
|
34
|
+
if (selectedEscrow?.type !== "multi-release") return 0;
|
|
35
|
+
const milestones = selectedEscrow.milestones as MultiReleaseMilestone[];
|
|
36
|
+
return milestones.reduce(
|
|
37
|
+
(acc, milestone) => acc + Number(milestone.amount),
|
|
38
|
+
0
|
|
39
|
+
);
|
|
40
|
+
}, [selectedEscrow]);
|
|
41
|
+
|
|
42
|
+
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
43
|
+
|
|
44
|
+
const handleSubmit = form.handleSubmit(async (payload) => {
|
|
45
|
+
try {
|
|
46
|
+
setIsSubmitting(true);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create the final payload for the resolve dispute mutation
|
|
50
|
+
*
|
|
51
|
+
* @param payload - The payload from the form
|
|
52
|
+
* @returns The final payload for the resolve dispute mutation
|
|
53
|
+
*/
|
|
54
|
+
const finalPayload: MultiReleaseResolveDisputePayload = {
|
|
55
|
+
contractId: selectedEscrow?.contractId || "",
|
|
56
|
+
disputeResolver: walletAddress || "",
|
|
57
|
+
approverFunds: Number(payload.approverFunds),
|
|
58
|
+
receiverFunds: Number(payload.receiverFunds),
|
|
59
|
+
milestoneIndex: String(payload.milestoneIndex),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Call the resolve dispute mutation
|
|
64
|
+
*
|
|
65
|
+
* @param payload - The final payload for the resolve dispute mutation
|
|
66
|
+
* @param type - The type of the escrow
|
|
67
|
+
* @param address - The address of the escrow
|
|
68
|
+
*/
|
|
69
|
+
await resolveDispute.mutateAsync({
|
|
70
|
+
payload: finalPayload,
|
|
71
|
+
type: "multi-release",
|
|
72
|
+
address: walletAddress || "",
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
toast.success("Dispute resolved successfully");
|
|
76
|
+
|
|
77
|
+
updateEscrow({
|
|
78
|
+
...selectedEscrow,
|
|
79
|
+
milestones: selectedEscrow?.milestones.map((milestone, index) => {
|
|
80
|
+
if (index === Number(payload.milestoneIndex)) {
|
|
81
|
+
return {
|
|
82
|
+
...milestone,
|
|
83
|
+
flags: {
|
|
84
|
+
...(milestone as MultiReleaseMilestone).flags,
|
|
85
|
+
disputed: false,
|
|
86
|
+
resolved: true,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return milestone;
|
|
91
|
+
}),
|
|
92
|
+
balance:
|
|
93
|
+
selectedEscrow?.balance ||
|
|
94
|
+
Number(payload.approverFunds) + Number(payload.receiverFunds),
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
toast.error(handleError(error as ErrorResponse).message);
|
|
98
|
+
} finally {
|
|
99
|
+
setIsSubmitting(false);
|
|
100
|
+
form.reset();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return { form, handleSubmit, isSubmitting, totalAmount };
|
|
105
|
+
}
|