@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.
Files changed (66) hide show
  1. package/bin/index.js +485 -17
  2. package/package.json +1 -1
  3. package/templates/escrows/details/Actions.tsx +144 -149
  4. package/templates/escrows/details/Entities.tsx +1 -1
  5. package/templates/escrows/details/EntityCard.tsx +1 -3
  6. package/templates/escrows/details/EscrowDetailDialog.tsx +16 -16
  7. package/templates/escrows/details/GeneralInformation.tsx +19 -22
  8. package/templates/escrows/details/MilestoneCard.tsx +46 -47
  9. package/templates/escrows/details/MilestoneDetailDialog.tsx +1 -2
  10. package/templates/escrows/details/Milestones.tsx +0 -5
  11. package/templates/escrows/details/SuccessReleaseDialog.tsx +4 -6
  12. package/templates/escrows/escrows-by-role/cards/EscrowsCards.tsx +84 -49
  13. package/templates/escrows/escrows-by-role/cards/Filters.tsx +3 -5
  14. package/templates/escrows/escrows-by-role/table/EscrowsTable.tsx +8 -26
  15. package/templates/escrows/escrows-by-role/table/Filters.tsx +3 -5
  16. package/templates/escrows/escrows-by-signer/cards/EscrowsCards.tsx +89 -55
  17. package/templates/escrows/escrows-by-signer/cards/Filters.tsx +3 -5
  18. package/templates/escrows/escrows-by-signer/table/EscrowsTable.tsx +8 -24
  19. package/templates/escrows/escrows-by-signer/table/Filters.tsx +3 -5
  20. package/templates/escrows/multi-release/dispute-milestone/button/DisputeEscrow.tsx +98 -0
  21. package/templates/escrows/multi-release/initialize-escrow/dialog/InitializeEscrow.tsx +528 -0
  22. package/templates/escrows/multi-release/initialize-escrow/form/InitializeEscrow.tsx +506 -0
  23. package/templates/escrows/multi-release/initialize-escrow/shared/schema.ts +179 -0
  24. package/templates/escrows/multi-release/initialize-escrow/shared/useInitializeEscrow.ts +175 -0
  25. package/templates/escrows/multi-release/release-milestone/button/ReleaseEscrow.tsx +116 -0
  26. package/templates/escrows/multi-release/resolve-dispute/button/ResolveDispute.tsx +122 -0
  27. package/templates/escrows/multi-release/resolve-dispute/dialog/ResolveDispute.tsx +178 -0
  28. package/templates/escrows/multi-release/resolve-dispute/form/ResolveDispute.tsx +156 -0
  29. package/templates/escrows/multi-release/resolve-dispute/shared/schema.ts +85 -0
  30. package/templates/escrows/multi-release/resolve-dispute/shared/useResolveDispute.ts +105 -0
  31. package/templates/escrows/multi-release/update-escrow/dialog/UpdateEscrow.tsx +471 -0
  32. package/templates/escrows/multi-release/update-escrow/form/UpdateEscrow.tsx +449 -0
  33. package/templates/escrows/multi-release/update-escrow/shared/schema.ts +152 -0
  34. package/templates/escrows/multi-release/update-escrow/shared/useUpdateEscrow.ts +254 -0
  35. package/templates/escrows/{single-release → single-multi-release}/approve-milestone/button/ApproveMilestone.tsx +20 -7
  36. package/templates/escrows/{single-release → single-multi-release}/approve-milestone/dialog/ApproveMilestone.tsx +3 -3
  37. package/templates/escrows/{single-release → single-multi-release}/approve-milestone/form/ApproveMilestone.tsx +3 -3
  38. package/templates/escrows/{single-release/approve-milestone/shared → single-multi-release/approve-milestone}/useApproveMilestone.ts +16 -16
  39. package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/button/ChangeMilestoneStatus.tsx +4 -4
  40. package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/dialog/ChangeMilestoneStatus.tsx +4 -4
  41. package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/form/ChangeMilestoneStatus.tsx +3 -3
  42. package/templates/escrows/{single-release/change-milestone-status/shared → single-multi-release/change-milestone-status}/useChangeMilestoneStatus.ts +1 -1
  43. package/templates/escrows/{single-release → single-multi-release}/fund-escrow/button/FundEscrow.tsx +3 -3
  44. package/templates/escrows/{single-release → single-multi-release}/fund-escrow/dialog/FundEscrow.tsx +3 -3
  45. package/templates/escrows/{single-release → single-multi-release}/fund-escrow/form/FundEscrow.tsx +3 -3
  46. package/templates/escrows/{single-release/fund-escrow/shared → single-multi-release/fund-escrow}/useFundEscrow.ts +1 -1
  47. package/templates/escrows/single-release/dispute-escrow/button/DisputeEscrow.tsx +2 -2
  48. package/templates/escrows/single-release/initialize-escrow/dialog/InitializeEscrow.tsx +14 -6
  49. package/templates/escrows/single-release/initialize-escrow/form/InitializeEscrow.tsx +14 -6
  50. package/templates/escrows/single-release/initialize-escrow/shared/schema.ts +0 -57
  51. package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +42 -1
  52. package/templates/escrows/single-release/release-escrow/button/ReleaseEscrow.tsx +2 -2
  53. package/templates/escrows/single-release/resolve-dispute/button/ResolveDispute.tsx +3 -3
  54. package/templates/escrows/single-release/resolve-dispute/dialog/ResolveDispute.tsx +3 -6
  55. package/templates/escrows/single-release/resolve-dispute/form/ResolveDispute.tsx +2 -2
  56. package/templates/escrows/single-release/resolve-dispute/shared/useResolveDispute.ts +14 -1
  57. package/templates/escrows/single-release/update-escrow/dialog/UpdateEscrow.tsx +2 -2
  58. package/templates/escrows/single-release/update-escrow/form/UpdateEscrow.tsx +2 -2
  59. package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +12 -7
  60. package/templates/providers/EscrowDialogsProvider.tsx +1 -3
  61. package/templates/providers/EscrowProvider.tsx +27 -4
  62. package/templates/providers/TrustlessWork.tsx +1 -1
  63. package/templates/escrows/details/ProgressEscrow.tsx +0 -191
  64. /package/templates/escrows/{single-release/approve-milestone/shared → single-multi-release/approve-milestone}/schema.ts +0 -0
  65. /package/templates/escrows/{single-release/change-milestone-status/shared → single-multi-release/change-milestone-status}/schema.ts +0 -0
  66. /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
+ }