@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustless-work/blocks",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "author": "Trustless Work",
5
5
  "keywords": [
6
6
  "react",
@@ -4,7 +4,7 @@
4
4
  "react-dom": "^18.2.0",
5
5
  "react-hook-form": "^7.53.0",
6
6
  "zod": "^3.23.8",
7
- "@trustless-work/escrow": "^2.0.9",
7
+ "@trustless-work/escrow": "^3.0.0",
8
8
  "@tanstack/react-query": "^5.75.0",
9
9
  "@tanstack/react-query-devtools": "^5.75.0",
10
10
  "tailwindcss": "^3.3.3",
@@ -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
+ };