@trustless-work/blocks 1.1.0 → 1.1.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/README.md +67 -35
- package/bin/index.js +1967 -1967
- package/package.json +1 -1
- package/templates/dashboard/dashboard-01/Dashboard.tsx +315 -315
- package/templates/dashboard/dashboard-01/useDashboard.ts +119 -119
- package/templates/deps.json +1 -1
- package/templates/escrows/details/Actions.tsx +166 -166
- package/templates/escrows/indicators/balance-progress/donut/BalanceProgress.tsx +0 -1
- package/templates/providers/EscrowProvider.tsx +6 -2
- package/templates/wallet-kit/WalletProvider.tsx +4 -0
package/package.json
CHANGED
|
@@ -1,315 +1,315 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import React from "react";
|
|
4
|
-
import {
|
|
5
|
-
Card,
|
|
6
|
-
CardContent,
|
|
7
|
-
CardDescription,
|
|
8
|
-
CardHeader,
|
|
9
|
-
CardTitle,
|
|
10
|
-
} from "__UI_BASE__/card";
|
|
11
|
-
import { Separator } from "__UI_BASE__/separator";
|
|
12
|
-
import { useDashboard } from "./useDashboard";
|
|
13
|
-
import { formatCurrency } from "../../helpers/format.helper";
|
|
14
|
-
import { Activity, Layers3, PiggyBank, CloudOff } from "lucide-react";
|
|
15
|
-
import {
|
|
16
|
-
AreaChart,
|
|
17
|
-
Area,
|
|
18
|
-
XAxis,
|
|
19
|
-
CartesianGrid,
|
|
20
|
-
BarChart,
|
|
21
|
-
Bar,
|
|
22
|
-
PieChart,
|
|
23
|
-
Pie,
|
|
24
|
-
Cell,
|
|
25
|
-
} from "recharts";
|
|
26
|
-
import {
|
|
27
|
-
ChartContainer,
|
|
28
|
-
ChartTooltip,
|
|
29
|
-
ChartTooltipContent,
|
|
30
|
-
type ChartConfig,
|
|
31
|
-
} from "__UI_BASE__/chart";
|
|
32
|
-
import {
|
|
33
|
-
Empty,
|
|
34
|
-
EmptyHeader,
|
|
35
|
-
EmptyMedia,
|
|
36
|
-
EmptyTitle,
|
|
37
|
-
EmptyDescription,
|
|
38
|
-
} from "__UI_BASE__/empty";
|
|
39
|
-
|
|
40
|
-
const chartConfigBar: ChartConfig = {
|
|
41
|
-
desktop: {
|
|
42
|
-
label: "Amount",
|
|
43
|
-
color: "var(--chart-1)",
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const chartConfigDonut: ChartConfig = {
|
|
48
|
-
visitors: { label: "Count" },
|
|
49
|
-
single: { label: "Single", color: "var(--chart-1)" },
|
|
50
|
-
multi: { label: "Multi", color: "var(--chart-2)" },
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const chartConfigArea: ChartConfig = {
|
|
54
|
-
desktop: {
|
|
55
|
-
label: "Created",
|
|
56
|
-
color: "var(--chart-1)",
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export const Dashboard01 = () => {
|
|
61
|
-
const {
|
|
62
|
-
isLoading,
|
|
63
|
-
totalEscrows,
|
|
64
|
-
totalAmount,
|
|
65
|
-
totalBalance,
|
|
66
|
-
typeSlices,
|
|
67
|
-
amountsByDate,
|
|
68
|
-
createdByDate,
|
|
69
|
-
} = useDashboard();
|
|
70
|
-
|
|
71
|
-
const barData = React.useMemo(
|
|
72
|
-
() => amountsByDate.map((d) => ({ month: d.date, desktop: d.amount })),
|
|
73
|
-
[amountsByDate]
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
const donutData = React.useMemo(
|
|
77
|
-
() =>
|
|
78
|
-
typeSlices.map((s) => ({
|
|
79
|
-
browser: s.type === "single" ? "single" : "multi",
|
|
80
|
-
visitors: s.value,
|
|
81
|
-
fill:
|
|
82
|
-
s.type === "single" ? "var(--color-single)" : "var(--color-multi)",
|
|
83
|
-
})),
|
|
84
|
-
[typeSlices]
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
const areaData = React.useMemo(
|
|
88
|
-
() => createdByDate.map((d) => ({ month: d.date, desktop: d.count })),
|
|
89
|
-
[createdByDate]
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<div className="grid gap-6">
|
|
94
|
-
{/* KPI Cards */}
|
|
95
|
-
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
96
|
-
<Card className="gap-3">
|
|
97
|
-
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
98
|
-
<CardTitle className="text-sm font-medium">Escrows</CardTitle>
|
|
99
|
-
<Layers3 className="h-4 w-4 text-muted-foreground" />
|
|
100
|
-
</CardHeader>
|
|
101
|
-
<CardContent>
|
|
102
|
-
<div className="text-2xl font-bold">
|
|
103
|
-
{isLoading ? "-" : totalEscrows}
|
|
104
|
-
</div>
|
|
105
|
-
<p className="text-xs text-muted-foreground">
|
|
106
|
-
Total number of escrows
|
|
107
|
-
</p>
|
|
108
|
-
</CardContent>
|
|
109
|
-
</Card>
|
|
110
|
-
|
|
111
|
-
<Card className="gap-3">
|
|
112
|
-
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
113
|
-
<CardTitle className="text-sm font-medium">Total Amount</CardTitle>
|
|
114
|
-
<Activity className="h-4 w-4 text-muted-foreground" />
|
|
115
|
-
</CardHeader>
|
|
116
|
-
<CardContent>
|
|
117
|
-
<div className="text-2xl font-bold">
|
|
118
|
-
{isLoading ? "-" : formatCurrency(totalAmount, "USDC")}
|
|
119
|
-
</div>
|
|
120
|
-
<p className="text-xs text-muted-foreground">
|
|
121
|
-
Sum of amounts (SR + MR)
|
|
122
|
-
</p>
|
|
123
|
-
</CardContent>
|
|
124
|
-
</Card>
|
|
125
|
-
|
|
126
|
-
<Card className="gap-3">
|
|
127
|
-
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
128
|
-
<CardTitle className="text-sm font-medium">Total Balance</CardTitle>
|
|
129
|
-
<PiggyBank className="h-4 w-4 text-muted-foreground" />
|
|
130
|
-
</CardHeader>
|
|
131
|
-
<CardContent>
|
|
132
|
-
<div className="text-2xl font-bold">
|
|
133
|
-
{isLoading ? "-" : formatCurrency(totalBalance, "USDC")}
|
|
134
|
-
</div>
|
|
135
|
-
<p className="text-xs text-muted-foreground">
|
|
136
|
-
Total balance across all escrows
|
|
137
|
-
</p>
|
|
138
|
-
</CardContent>
|
|
139
|
-
</Card>
|
|
140
|
-
</div>
|
|
141
|
-
|
|
142
|
-
<Separator />
|
|
143
|
-
|
|
144
|
-
{/* Charts */}
|
|
145
|
-
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
146
|
-
{/* Bar chart: Amounts by date (shadcn pattern) */}
|
|
147
|
-
<Card className="gap-3">
|
|
148
|
-
<CardHeader>
|
|
149
|
-
<CardTitle>Escrow Amounts</CardTitle>
|
|
150
|
-
<CardDescription>Amounts by date</CardDescription>
|
|
151
|
-
</CardHeader>
|
|
152
|
-
<CardContent>
|
|
153
|
-
<ChartContainer
|
|
154
|
-
className="w-full h-56 sm:h-64 lg:h-72"
|
|
155
|
-
config={chartConfigBar}
|
|
156
|
-
>
|
|
157
|
-
{barData.length > 0 ? (
|
|
158
|
-
<BarChart accessibilityLayer data={barData}>
|
|
159
|
-
<CartesianGrid vertical={false} />
|
|
160
|
-
<XAxis
|
|
161
|
-
dataKey="month"
|
|
162
|
-
tickLine={false}
|
|
163
|
-
tickMargin={10}
|
|
164
|
-
axisLine={false}
|
|
165
|
-
tickFormatter={(value) =>
|
|
166
|
-
new Date(String(value)).toLocaleDateString("en-US", {
|
|
167
|
-
month: "short",
|
|
168
|
-
day: "numeric",
|
|
169
|
-
})
|
|
170
|
-
}
|
|
171
|
-
/>
|
|
172
|
-
<ChartTooltip
|
|
173
|
-
cursor={false}
|
|
174
|
-
content={<ChartTooltipContent hideLabel />}
|
|
175
|
-
/>
|
|
176
|
-
<Bar
|
|
177
|
-
dataKey="desktop"
|
|
178
|
-
fill="var(--color-desktop)"
|
|
179
|
-
radius={8}
|
|
180
|
-
/>
|
|
181
|
-
</BarChart>
|
|
182
|
-
) : (
|
|
183
|
-
<Empty className="border border-dashed">
|
|
184
|
-
<EmptyHeader>
|
|
185
|
-
<EmptyMedia variant="icon">
|
|
186
|
-
<CloudOff />
|
|
187
|
-
</EmptyMedia>
|
|
188
|
-
<EmptyTitle>No data</EmptyTitle>
|
|
189
|
-
<EmptyDescription>No Data Available</EmptyDescription>
|
|
190
|
-
</EmptyHeader>
|
|
191
|
-
</Empty>
|
|
192
|
-
)}
|
|
193
|
-
</ChartContainer>
|
|
194
|
-
</CardContent>
|
|
195
|
-
</Card>
|
|
196
|
-
|
|
197
|
-
{/* Donut chart: Escrow types (shadcn pattern) */}
|
|
198
|
-
<Card className="flex flex-col">
|
|
199
|
-
<CardHeader className="items-center pb-0">
|
|
200
|
-
<CardTitle>Escrow Types</CardTitle>
|
|
201
|
-
<CardDescription>Escrow types</CardDescription>
|
|
202
|
-
</CardHeader>
|
|
203
|
-
<CardContent className="flex-1 pb-0">
|
|
204
|
-
<ChartContainer
|
|
205
|
-
config={chartConfigDonut}
|
|
206
|
-
className="w-full h-56 sm:h-64 lg:h-72"
|
|
207
|
-
>
|
|
208
|
-
{donutData.some((d) => Number(d.visitors) > 0) ? (
|
|
209
|
-
<PieChart>
|
|
210
|
-
<ChartTooltip
|
|
211
|
-
cursor={false}
|
|
212
|
-
content={<ChartTooltipContent hideLabel />}
|
|
213
|
-
/>
|
|
214
|
-
<Pie
|
|
215
|
-
data={donutData}
|
|
216
|
-
dataKey="visitors"
|
|
217
|
-
nameKey="browser"
|
|
218
|
-
innerRadius={60}
|
|
219
|
-
>
|
|
220
|
-
{donutData.map((slice, idx) => (
|
|
221
|
-
<Cell key={`cell-${idx}`} fill={slice.fill} />
|
|
222
|
-
))}
|
|
223
|
-
</Pie>
|
|
224
|
-
</PieChart>
|
|
225
|
-
) : (
|
|
226
|
-
<Empty className="border border-dashed">
|
|
227
|
-
<EmptyHeader>
|
|
228
|
-
<EmptyMedia variant="icon">
|
|
229
|
-
<CloudOff />
|
|
230
|
-
</EmptyMedia>
|
|
231
|
-
<EmptyTitle>No data</EmptyTitle>
|
|
232
|
-
<EmptyDescription>No Data Available</EmptyDescription>
|
|
233
|
-
</EmptyHeader>
|
|
234
|
-
</Empty>
|
|
235
|
-
)}
|
|
236
|
-
</ChartContainer>
|
|
237
|
-
<div className="mt-4 flex items-center justify-center gap-6">
|
|
238
|
-
<div className="flex items-center gap-2">
|
|
239
|
-
<span
|
|
240
|
-
className="h-2 w-2 rounded-full"
|
|
241
|
-
style={{ background: "var(--chart-1)" }}
|
|
242
|
-
/>
|
|
243
|
-
<span className="text-xs text-muted-foreground">Single</span>
|
|
244
|
-
</div>
|
|
245
|
-
<div className="flex items-center gap-2">
|
|
246
|
-
<span
|
|
247
|
-
className="h-2 w-2 rounded-full"
|
|
248
|
-
style={{ background: "var(--chart-2)" }}
|
|
249
|
-
/>
|
|
250
|
-
<span className="text-xs text-muted-foreground">Multi</span>
|
|
251
|
-
</div>
|
|
252
|
-
</div>
|
|
253
|
-
</CardContent>
|
|
254
|
-
</Card>
|
|
255
|
-
|
|
256
|
-
{/* Area chart: Created escrows (shadcn pattern) */}
|
|
257
|
-
<Card className="lg:col-span-2">
|
|
258
|
-
<CardHeader>
|
|
259
|
-
<CardTitle>Escrow Created</CardTitle>
|
|
260
|
-
<CardDescription>Created escrows by date</CardDescription>
|
|
261
|
-
</CardHeader>
|
|
262
|
-
<CardContent>
|
|
263
|
-
<ChartContainer
|
|
264
|
-
className="w-full h-56 sm:h-64 lg:h-72"
|
|
265
|
-
config={chartConfigArea}
|
|
266
|
-
>
|
|
267
|
-
{areaData.length > 0 ? (
|
|
268
|
-
<AreaChart
|
|
269
|
-
accessibilityLayer
|
|
270
|
-
data={areaData}
|
|
271
|
-
margin={{ left: 12, right: 12 }}
|
|
272
|
-
>
|
|
273
|
-
<CartesianGrid vertical={false} />
|
|
274
|
-
<XAxis
|
|
275
|
-
dataKey="month"
|
|
276
|
-
tickLine={false}
|
|
277
|
-
axisLine={false}
|
|
278
|
-
tickMargin={8}
|
|
279
|
-
tickFormatter={(value) =>
|
|
280
|
-
new Date(String(value)).toLocaleDateString("en-US", {
|
|
281
|
-
month: "short",
|
|
282
|
-
day: "numeric",
|
|
283
|
-
})
|
|
284
|
-
}
|
|
285
|
-
/>
|
|
286
|
-
<ChartTooltip
|
|
287
|
-
cursor={false}
|
|
288
|
-
content={<ChartTooltipContent indicator="line" />}
|
|
289
|
-
/>
|
|
290
|
-
<Area
|
|
291
|
-
dataKey="desktop"
|
|
292
|
-
type="natural"
|
|
293
|
-
fill="var(--color-desktop)"
|
|
294
|
-
fillOpacity={0.4}
|
|
295
|
-
stroke="var(--color-desktop)"
|
|
296
|
-
/>
|
|
297
|
-
</AreaChart>
|
|
298
|
-
) : (
|
|
299
|
-
<Empty className="border border-dashed">
|
|
300
|
-
<EmptyHeader>
|
|
301
|
-
<EmptyMedia variant="icon">
|
|
302
|
-
<CloudOff />
|
|
303
|
-
</EmptyMedia>
|
|
304
|
-
<EmptyTitle>No data</EmptyTitle>
|
|
305
|
-
<EmptyDescription>No Data Available</EmptyDescription>
|
|
306
|
-
</EmptyHeader>
|
|
307
|
-
</Empty>
|
|
308
|
-
)}
|
|
309
|
-
</ChartContainer>
|
|
310
|
-
</CardContent>
|
|
311
|
-
</Card>
|
|
312
|
-
</div>
|
|
313
|
-
</div>
|
|
314
|
-
);
|
|
315
|
-
};
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import {
|
|
5
|
+
Card,
|
|
6
|
+
CardContent,
|
|
7
|
+
CardDescription,
|
|
8
|
+
CardHeader,
|
|
9
|
+
CardTitle,
|
|
10
|
+
} from "__UI_BASE__/card";
|
|
11
|
+
import { Separator } from "__UI_BASE__/separator";
|
|
12
|
+
import { useDashboard } from "./useDashboard";
|
|
13
|
+
import { formatCurrency } from "../../helpers/format.helper";
|
|
14
|
+
import { Activity, Layers3, PiggyBank, CloudOff } from "lucide-react";
|
|
15
|
+
import {
|
|
16
|
+
AreaChart,
|
|
17
|
+
Area,
|
|
18
|
+
XAxis,
|
|
19
|
+
CartesianGrid,
|
|
20
|
+
BarChart,
|
|
21
|
+
Bar,
|
|
22
|
+
PieChart,
|
|
23
|
+
Pie,
|
|
24
|
+
Cell,
|
|
25
|
+
} from "recharts";
|
|
26
|
+
import {
|
|
27
|
+
ChartContainer,
|
|
28
|
+
ChartTooltip,
|
|
29
|
+
ChartTooltipContent,
|
|
30
|
+
type ChartConfig,
|
|
31
|
+
} from "__UI_BASE__/chart";
|
|
32
|
+
import {
|
|
33
|
+
Empty,
|
|
34
|
+
EmptyHeader,
|
|
35
|
+
EmptyMedia,
|
|
36
|
+
EmptyTitle,
|
|
37
|
+
EmptyDescription,
|
|
38
|
+
} from "__UI_BASE__/empty";
|
|
39
|
+
|
|
40
|
+
const chartConfigBar: ChartConfig = {
|
|
41
|
+
desktop: {
|
|
42
|
+
label: "Amount",
|
|
43
|
+
color: "var(--chart-1)",
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const chartConfigDonut: ChartConfig = {
|
|
48
|
+
visitors: { label: "Count" },
|
|
49
|
+
single: { label: "Single", color: "var(--chart-1)" },
|
|
50
|
+
multi: { label: "Multi", color: "var(--chart-2)" },
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const chartConfigArea: ChartConfig = {
|
|
54
|
+
desktop: {
|
|
55
|
+
label: "Created",
|
|
56
|
+
color: "var(--chart-1)",
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const Dashboard01 = () => {
|
|
61
|
+
const {
|
|
62
|
+
isLoading,
|
|
63
|
+
totalEscrows,
|
|
64
|
+
totalAmount,
|
|
65
|
+
totalBalance,
|
|
66
|
+
typeSlices,
|
|
67
|
+
amountsByDate,
|
|
68
|
+
createdByDate,
|
|
69
|
+
} = useDashboard();
|
|
70
|
+
|
|
71
|
+
const barData = React.useMemo(
|
|
72
|
+
() => amountsByDate.map((d) => ({ month: d.date, desktop: d.amount })),
|
|
73
|
+
[amountsByDate]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const donutData = React.useMemo(
|
|
77
|
+
() =>
|
|
78
|
+
typeSlices.map((s) => ({
|
|
79
|
+
browser: s.type === "single" ? "single" : "multi",
|
|
80
|
+
visitors: s.value,
|
|
81
|
+
fill:
|
|
82
|
+
s.type === "single" ? "var(--color-single)" : "var(--color-multi)",
|
|
83
|
+
})),
|
|
84
|
+
[typeSlices]
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const areaData = React.useMemo(
|
|
88
|
+
() => createdByDate.map((d) => ({ month: d.date, desktop: d.count })),
|
|
89
|
+
[createdByDate]
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div className="grid gap-6">
|
|
94
|
+
{/* KPI Cards */}
|
|
95
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
96
|
+
<Card className="gap-3">
|
|
97
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
98
|
+
<CardTitle className="text-sm font-medium">Escrows</CardTitle>
|
|
99
|
+
<Layers3 className="h-4 w-4 text-muted-foreground" />
|
|
100
|
+
</CardHeader>
|
|
101
|
+
<CardContent>
|
|
102
|
+
<div className="text-2xl font-bold">
|
|
103
|
+
{isLoading ? "-" : totalEscrows}
|
|
104
|
+
</div>
|
|
105
|
+
<p className="text-xs text-muted-foreground">
|
|
106
|
+
Total number of escrows
|
|
107
|
+
</p>
|
|
108
|
+
</CardContent>
|
|
109
|
+
</Card>
|
|
110
|
+
|
|
111
|
+
<Card className="gap-3">
|
|
112
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
113
|
+
<CardTitle className="text-sm font-medium">Total Amount</CardTitle>
|
|
114
|
+
<Activity className="h-4 w-4 text-muted-foreground" />
|
|
115
|
+
</CardHeader>
|
|
116
|
+
<CardContent>
|
|
117
|
+
<div className="text-2xl font-bold">
|
|
118
|
+
{isLoading ? "-" : formatCurrency(totalAmount, "USDC")}
|
|
119
|
+
</div>
|
|
120
|
+
<p className="text-xs text-muted-foreground">
|
|
121
|
+
Sum of amounts (SR + MR)
|
|
122
|
+
</p>
|
|
123
|
+
</CardContent>
|
|
124
|
+
</Card>
|
|
125
|
+
|
|
126
|
+
<Card className="gap-3">
|
|
127
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
128
|
+
<CardTitle className="text-sm font-medium">Total Balance</CardTitle>
|
|
129
|
+
<PiggyBank className="h-4 w-4 text-muted-foreground" />
|
|
130
|
+
</CardHeader>
|
|
131
|
+
<CardContent>
|
|
132
|
+
<div className="text-2xl font-bold">
|
|
133
|
+
{isLoading ? "-" : formatCurrency(totalBalance, "USDC")}
|
|
134
|
+
</div>
|
|
135
|
+
<p className="text-xs text-muted-foreground">
|
|
136
|
+
Total balance across all escrows
|
|
137
|
+
</p>
|
|
138
|
+
</CardContent>
|
|
139
|
+
</Card>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<Separator />
|
|
143
|
+
|
|
144
|
+
{/* Charts */}
|
|
145
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
146
|
+
{/* Bar chart: Amounts by date (shadcn pattern) */}
|
|
147
|
+
<Card className="gap-3">
|
|
148
|
+
<CardHeader>
|
|
149
|
+
<CardTitle>Escrow Amounts</CardTitle>
|
|
150
|
+
<CardDescription>Amounts by date</CardDescription>
|
|
151
|
+
</CardHeader>
|
|
152
|
+
<CardContent>
|
|
153
|
+
<ChartContainer
|
|
154
|
+
className="w-full h-56 sm:h-64 lg:h-72"
|
|
155
|
+
config={chartConfigBar}
|
|
156
|
+
>
|
|
157
|
+
{barData.length > 0 ? (
|
|
158
|
+
<BarChart accessibilityLayer data={barData}>
|
|
159
|
+
<CartesianGrid vertical={false} />
|
|
160
|
+
<XAxis
|
|
161
|
+
dataKey="month"
|
|
162
|
+
tickLine={false}
|
|
163
|
+
tickMargin={10}
|
|
164
|
+
axisLine={false}
|
|
165
|
+
tickFormatter={(value) =>
|
|
166
|
+
new Date(String(value)).toLocaleDateString("en-US", {
|
|
167
|
+
month: "short",
|
|
168
|
+
day: "numeric",
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
/>
|
|
172
|
+
<ChartTooltip
|
|
173
|
+
cursor={false}
|
|
174
|
+
content={<ChartTooltipContent hideLabel />}
|
|
175
|
+
/>
|
|
176
|
+
<Bar
|
|
177
|
+
dataKey="desktop"
|
|
178
|
+
fill="var(--color-desktop)"
|
|
179
|
+
radius={8}
|
|
180
|
+
/>
|
|
181
|
+
</BarChart>
|
|
182
|
+
) : (
|
|
183
|
+
<Empty className="border border-dashed">
|
|
184
|
+
<EmptyHeader>
|
|
185
|
+
<EmptyMedia variant="icon">
|
|
186
|
+
<CloudOff />
|
|
187
|
+
</EmptyMedia>
|
|
188
|
+
<EmptyTitle>No data</EmptyTitle>
|
|
189
|
+
<EmptyDescription>No Data Available</EmptyDescription>
|
|
190
|
+
</EmptyHeader>
|
|
191
|
+
</Empty>
|
|
192
|
+
)}
|
|
193
|
+
</ChartContainer>
|
|
194
|
+
</CardContent>
|
|
195
|
+
</Card>
|
|
196
|
+
|
|
197
|
+
{/* Donut chart: Escrow types (shadcn pattern) */}
|
|
198
|
+
<Card className="flex flex-col">
|
|
199
|
+
<CardHeader className="items-center pb-0">
|
|
200
|
+
<CardTitle>Escrow Types</CardTitle>
|
|
201
|
+
<CardDescription>Escrow types</CardDescription>
|
|
202
|
+
</CardHeader>
|
|
203
|
+
<CardContent className="flex-1 pb-0">
|
|
204
|
+
<ChartContainer
|
|
205
|
+
config={chartConfigDonut}
|
|
206
|
+
className="w-full h-56 sm:h-64 lg:h-72"
|
|
207
|
+
>
|
|
208
|
+
{donutData.some((d) => Number(d.visitors) > 0) ? (
|
|
209
|
+
<PieChart>
|
|
210
|
+
<ChartTooltip
|
|
211
|
+
cursor={false}
|
|
212
|
+
content={<ChartTooltipContent hideLabel />}
|
|
213
|
+
/>
|
|
214
|
+
<Pie
|
|
215
|
+
data={donutData}
|
|
216
|
+
dataKey="visitors"
|
|
217
|
+
nameKey="browser"
|
|
218
|
+
innerRadius={60}
|
|
219
|
+
>
|
|
220
|
+
{donutData.map((slice, idx) => (
|
|
221
|
+
<Cell key={`cell-${idx}`} fill={slice.fill} />
|
|
222
|
+
))}
|
|
223
|
+
</Pie>
|
|
224
|
+
</PieChart>
|
|
225
|
+
) : (
|
|
226
|
+
<Empty className="border border-dashed">
|
|
227
|
+
<EmptyHeader>
|
|
228
|
+
<EmptyMedia variant="icon">
|
|
229
|
+
<CloudOff />
|
|
230
|
+
</EmptyMedia>
|
|
231
|
+
<EmptyTitle>No data</EmptyTitle>
|
|
232
|
+
<EmptyDescription>No Data Available</EmptyDescription>
|
|
233
|
+
</EmptyHeader>
|
|
234
|
+
</Empty>
|
|
235
|
+
)}
|
|
236
|
+
</ChartContainer>
|
|
237
|
+
<div className="mt-4 flex items-center justify-center gap-6">
|
|
238
|
+
<div className="flex items-center gap-2">
|
|
239
|
+
<span
|
|
240
|
+
className="h-2 w-2 rounded-full"
|
|
241
|
+
style={{ background: "var(--chart-1)" }}
|
|
242
|
+
/>
|
|
243
|
+
<span className="text-xs text-muted-foreground">Single</span>
|
|
244
|
+
</div>
|
|
245
|
+
<div className="flex items-center gap-2">
|
|
246
|
+
<span
|
|
247
|
+
className="h-2 w-2 rounded-full"
|
|
248
|
+
style={{ background: "var(--chart-2)" }}
|
|
249
|
+
/>
|
|
250
|
+
<span className="text-xs text-muted-foreground">Multi</span>
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
</CardContent>
|
|
254
|
+
</Card>
|
|
255
|
+
|
|
256
|
+
{/* Area chart: Created escrows (shadcn pattern) */}
|
|
257
|
+
<Card className="lg:col-span-2">
|
|
258
|
+
<CardHeader>
|
|
259
|
+
<CardTitle>Escrow Created</CardTitle>
|
|
260
|
+
<CardDescription>Created escrows by date</CardDescription>
|
|
261
|
+
</CardHeader>
|
|
262
|
+
<CardContent>
|
|
263
|
+
<ChartContainer
|
|
264
|
+
className="w-full h-56 sm:h-64 lg:h-72"
|
|
265
|
+
config={chartConfigArea}
|
|
266
|
+
>
|
|
267
|
+
{areaData.length > 0 ? (
|
|
268
|
+
<AreaChart
|
|
269
|
+
accessibilityLayer
|
|
270
|
+
data={areaData}
|
|
271
|
+
margin={{ left: 12, right: 12 }}
|
|
272
|
+
>
|
|
273
|
+
<CartesianGrid vertical={false} />
|
|
274
|
+
<XAxis
|
|
275
|
+
dataKey="month"
|
|
276
|
+
tickLine={false}
|
|
277
|
+
axisLine={false}
|
|
278
|
+
tickMargin={8}
|
|
279
|
+
tickFormatter={(value) =>
|
|
280
|
+
new Date(String(value)).toLocaleDateString("en-US", {
|
|
281
|
+
month: "short",
|
|
282
|
+
day: "numeric",
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
/>
|
|
286
|
+
<ChartTooltip
|
|
287
|
+
cursor={false}
|
|
288
|
+
content={<ChartTooltipContent indicator="line" />}
|
|
289
|
+
/>
|
|
290
|
+
<Area
|
|
291
|
+
dataKey="desktop"
|
|
292
|
+
type="natural"
|
|
293
|
+
fill="var(--color-desktop)"
|
|
294
|
+
fillOpacity={0.4}
|
|
295
|
+
stroke="var(--color-desktop)"
|
|
296
|
+
/>
|
|
297
|
+
</AreaChart>
|
|
298
|
+
) : (
|
|
299
|
+
<Empty className="border border-dashed">
|
|
300
|
+
<EmptyHeader>
|
|
301
|
+
<EmptyMedia variant="icon">
|
|
302
|
+
<CloudOff />
|
|
303
|
+
</EmptyMedia>
|
|
304
|
+
<EmptyTitle>No data</EmptyTitle>
|
|
305
|
+
<EmptyDescription>No Data Available</EmptyDescription>
|
|
306
|
+
</EmptyHeader>
|
|
307
|
+
</Empty>
|
|
308
|
+
)}
|
|
309
|
+
</ChartContainer>
|
|
310
|
+
</CardContent>
|
|
311
|
+
</Card>
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
);
|
|
315
|
+
};
|