@trustless-work/blocks 1.0.1 → 1.0.2
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 +21 -1
- package/package.json +1 -1
- package/templates/deps.json +1 -1
- package/templates/escrows/indicators/balance-progress/bar/BalanceProgress.tsx +55 -0
- package/templates/escrows/indicators/balance-progress/donut/BalanceProgress.tsx +99 -0
- package/templates/tanstack/useGetMultipleEscrowBalances.ts +41 -0
package/bin/index.js
CHANGED
|
@@ -1580,7 +1580,7 @@ if (args[0] === "init") {
|
|
|
1580
1580
|
}
|
|
1581
1581
|
|
|
1582
1582
|
const addShadcn = await promptYesNo(
|
|
1583
|
-
"Add shadcn components (button, input, form, card, sonner, checkbox, dialog, textarea, sonner, select, table, calendar, popover, separator, calendar-05, badge, sheet, tabs, avatar, tooltip)?",
|
|
1583
|
+
"Add shadcn components (button, input, form, card, sonner, checkbox, dialog, textarea, sonner, select, table, calendar, popover, separator, calendar-05, badge, sheet, tabs, avatar, tooltip, progress)?",
|
|
1584
1584
|
true
|
|
1585
1585
|
);
|
|
1586
1586
|
if (addShadcn) {
|
|
@@ -1608,6 +1608,7 @@ if (args[0] === "init") {
|
|
|
1608
1608
|
"tabs",
|
|
1609
1609
|
"avatar",
|
|
1610
1610
|
"tooltip",
|
|
1611
|
+
"progress",
|
|
1611
1612
|
]);
|
|
1612
1613
|
});
|
|
1613
1614
|
} else {
|
|
@@ -1699,6 +1700,20 @@ if (args[0] === "init") {
|
|
|
1699
1700
|
console.log("- " + oscHyperlink("X", "https://x.com/TrustlessWork"));
|
|
1700
1701
|
} else if (args[0] === "add" && args[1]) {
|
|
1701
1702
|
const flags = parseFlags(args.slice(2));
|
|
1703
|
+
// Normalize common aliases (singular/plural, shorthand)
|
|
1704
|
+
const normalizeTemplateName = (name) => {
|
|
1705
|
+
let n = String(name).trim();
|
|
1706
|
+
// singular to plural base
|
|
1707
|
+
n = n.replace(/^escrow\b/, "escrows");
|
|
1708
|
+
n = n.replace(/^indicator\b/, "indicators");
|
|
1709
|
+
// allow nested segments singulars
|
|
1710
|
+
n = n.replace(/(^|\/)escrow(\/|$)/g, "$1escrows$2");
|
|
1711
|
+
n = n.replace(/(^|\/)indicator(\/|$)/g, "$1indicators$2");
|
|
1712
|
+
// friendly shape variants
|
|
1713
|
+
n = n.replace(/(^|\/)circle(\/|$)/g, "$1circular$2");
|
|
1714
|
+
return n;
|
|
1715
|
+
};
|
|
1716
|
+
args[1] = normalizeTemplateName(args[1]);
|
|
1702
1717
|
const cfgPath = path.join(PROJECT_ROOT, ".twblocks.json");
|
|
1703
1718
|
if (!fs.existsSync(cfgPath)) {
|
|
1704
1719
|
console.error(
|
|
@@ -1791,6 +1806,11 @@ if (args[0] === "init") {
|
|
|
1791
1806
|
|
|
1792
1807
|
--- Escrows ---
|
|
1793
1808
|
trustless-work add escrows
|
|
1809
|
+
|
|
1810
|
+
--- Indicators ---
|
|
1811
|
+
trustless-work add escrows/indicators/balance-progress
|
|
1812
|
+
trustless-work add escrows/indicators/balance-progress/bar
|
|
1813
|
+
trustless-work add escrows/indicators/balance-progress/donut
|
|
1794
1814
|
|
|
1795
1815
|
--- Escrows by role ---
|
|
1796
1816
|
trustless-work add escrows/escrows-by-role
|
package/package.json
CHANGED
package/templates/deps.json
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Progress } from "__UI_BASE__/progress";
|
|
3
|
+
import { useGetMultipleEscrowBalancesQuery } from "@/components/tw-blocks/tanstack/useGetMultipleEscrowBalances";
|
|
4
|
+
import { formatCurrency } from "@/components/tw-blocks/helpers/format.helper";
|
|
5
|
+
|
|
6
|
+
type BalanceProgressBarProps = {
|
|
7
|
+
contractId: string;
|
|
8
|
+
target: number;
|
|
9
|
+
currency: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const BalanceProgressBar = ({
|
|
13
|
+
contractId,
|
|
14
|
+
target,
|
|
15
|
+
currency,
|
|
16
|
+
}: BalanceProgressBarProps) => {
|
|
17
|
+
const isContractProvided = Boolean(
|
|
18
|
+
contractId && contractId.trim().length > 0
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const { data, isLoading, isError } = useGetMultipleEscrowBalancesQuery({
|
|
22
|
+
addresses: isContractProvided ? [contractId] : [],
|
|
23
|
+
enabled: isContractProvided,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const currentBalanceRaw = Number(data?.[0]?.balance ?? 0);
|
|
27
|
+
const safeTarget = Number.isFinite(target) && target > 0 ? target : 0;
|
|
28
|
+
const progressValue =
|
|
29
|
+
safeTarget > 0
|
|
30
|
+
? Math.min(100, Math.max(0, (currentBalanceRaw / safeTarget) * 100))
|
|
31
|
+
: 0;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className="w-full">
|
|
35
|
+
<div className="flex items-center justify-between text-sm text-muted-foreground mb-2">
|
|
36
|
+
<p>
|
|
37
|
+
<span className="font-bold mr-1">Balance:</span>
|
|
38
|
+
{isLoading
|
|
39
|
+
? "Loading…"
|
|
40
|
+
: isError
|
|
41
|
+
? "-"
|
|
42
|
+
: formatCurrency(currentBalanceRaw, currency)}
|
|
43
|
+
</p>
|
|
44
|
+
<p>
|
|
45
|
+
<span className="font-bold mr-1">Target:</span>{" "}
|
|
46
|
+
{formatCurrency(safeTarget, currency)}
|
|
47
|
+
</p>
|
|
48
|
+
</div>
|
|
49
|
+
<Progress
|
|
50
|
+
value={isLoading || isError ? 0 : progressValue}
|
|
51
|
+
className="w-full"
|
|
52
|
+
/>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { useGetMultipleEscrowBalancesQuery } from "@/components/tw-blocks/tanstack/useGetMultipleEscrowBalances";
|
|
4
|
+
import { formatCurrency } from "@/components/tw-blocks/helpers/format.helper";
|
|
5
|
+
|
|
6
|
+
type BalanceProgressDonutProps = {
|
|
7
|
+
contractId: string;
|
|
8
|
+
target: number;
|
|
9
|
+
currency: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const BalanceProgressDonut = ({
|
|
13
|
+
contractId,
|
|
14
|
+
target,
|
|
15
|
+
currency,
|
|
16
|
+
}: BalanceProgressDonutProps) => {
|
|
17
|
+
const isContractProvided = Boolean(
|
|
18
|
+
contractId && contractId.trim().length > 0
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const { data, isLoading, isError } = useGetMultipleEscrowBalancesQuery({
|
|
22
|
+
addresses: isContractProvided ? [contractId] : [],
|
|
23
|
+
enabled: isContractProvided,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const currentBalanceRaw = Number(data?.[0]?.balance ?? 0);
|
|
27
|
+
const safeTarget = Number.isFinite(target) && target > 0 ? target : 0;
|
|
28
|
+
const progressValue =
|
|
29
|
+
safeTarget > 0
|
|
30
|
+
? Math.min(100, Math.max(0, (currentBalanceRaw / safeTarget) * 100))
|
|
31
|
+
: 0;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className="w-full">
|
|
35
|
+
<div className="flex items-center justify-between text-sm text-muted-foreground mb-2">
|
|
36
|
+
<p>
|
|
37
|
+
<span className="font-bold mr-1">Balance:</span>
|
|
38
|
+
{isLoading
|
|
39
|
+
? "Loading…"
|
|
40
|
+
: isError
|
|
41
|
+
? "-"
|
|
42
|
+
: formatCurrency(currentBalanceRaw, currency)}
|
|
43
|
+
</p>
|
|
44
|
+
<p>
|
|
45
|
+
<span className="font-bold mr-1">Target:</span>{" "}
|
|
46
|
+
{formatCurrency(safeTarget, currency)}
|
|
47
|
+
</p>
|
|
48
|
+
</div>
|
|
49
|
+
{(() => {
|
|
50
|
+
const size = 160; // px
|
|
51
|
+
const stroke = 12; // px
|
|
52
|
+
const radius = (size - stroke) / 2;
|
|
53
|
+
const circumference = 2 * Math.PI * radius;
|
|
54
|
+
const pct = isLoading || isError ? 0 : progressValue;
|
|
55
|
+
const dashOffset = circumference * (1 - pct / 100);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div className="flex justify-center">
|
|
59
|
+
<div className="relative" style={{ width: size, height: size }}>
|
|
60
|
+
<svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
|
|
61
|
+
{/* Track */}
|
|
62
|
+
<circle
|
|
63
|
+
cx={size / 2}
|
|
64
|
+
cy={size / 2}
|
|
65
|
+
r={radius}
|
|
66
|
+
strokeWidth={stroke}
|
|
67
|
+
stroke="currentColor"
|
|
68
|
+
className="text-muted-foreground/20"
|
|
69
|
+
fill="none"
|
|
70
|
+
strokeLinecap="round"
|
|
71
|
+
/>
|
|
72
|
+
{/* Progress */}
|
|
73
|
+
<g transform={`rotate(-90 ${size / 2} ${size / 2})`}>
|
|
74
|
+
<circle
|
|
75
|
+
cx={size / 2}
|
|
76
|
+
cy={size / 2}
|
|
77
|
+
r={radius}
|
|
78
|
+
strokeWidth={stroke}
|
|
79
|
+
stroke="currentColor"
|
|
80
|
+
className="text-primary"
|
|
81
|
+
fill="none"
|
|
82
|
+
strokeDasharray={`${circumference} ${circumference}`}
|
|
83
|
+
strokeDashoffset={dashOffset}
|
|
84
|
+
strokeLinecap="round"
|
|
85
|
+
/>
|
|
86
|
+
</g>
|
|
87
|
+
</svg>
|
|
88
|
+
|
|
89
|
+
<div className="absolute inset-0 flex flex-col items-center justify-center">
|
|
90
|
+
<span className="text-2xl font-bold">{Math.round(pct)}%</span>
|
|
91
|
+
<span className="text-muted-foreground text-sm">Progress</span>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
})()}
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
import {
|
|
3
|
+
GetEscrowBalancesResponse,
|
|
4
|
+
GetBalanceParams,
|
|
5
|
+
} from "@trustless-work/escrow/types";
|
|
6
|
+
import { useGetMultipleEscrowBalances } from "@trustless-work/escrow/hooks";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Use the query to get the escrows balances
|
|
10
|
+
*
|
|
11
|
+
* @param params - The parameters for the query
|
|
12
|
+
* @returns The query result
|
|
13
|
+
*/
|
|
14
|
+
export const useGetMultipleEscrowBalancesQuery = ({
|
|
15
|
+
addresses,
|
|
16
|
+
enabled = true,
|
|
17
|
+
}: GetBalanceParams & { enabled?: boolean }) => {
|
|
18
|
+
const { getMultipleBalances } = useGetMultipleEscrowBalances();
|
|
19
|
+
|
|
20
|
+
// Get the escrows by signer
|
|
21
|
+
return useQuery({
|
|
22
|
+
queryKey: ["escrows", addresses],
|
|
23
|
+
queryFn: async (): Promise<GetEscrowBalancesResponse[]> => {
|
|
24
|
+
/**
|
|
25
|
+
* Call the query to get the escrows from the Trustless Work Indexer
|
|
26
|
+
*
|
|
27
|
+
* @param params - The parameters for the query
|
|
28
|
+
* @returns The query result
|
|
29
|
+
*/
|
|
30
|
+
const balances = await getMultipleBalances({ addresses });
|
|
31
|
+
|
|
32
|
+
if (!balances) {
|
|
33
|
+
throw new Error("Escrows not found");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return balances;
|
|
37
|
+
},
|
|
38
|
+
enabled: enabled,
|
|
39
|
+
staleTime: 1000 * 60 * 5, // 5 min
|
|
40
|
+
});
|
|
41
|
+
};
|