astryx-dracula 0.4.0 → 0.5.0
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/package.json +6 -2
- package/shared/auth-chrome.tsx +156 -6
- package/shared/chaptered-doc.tsx +3 -4
- package/shared/chart-hues.ts +0 -5
- package/shared/chart-legend.tsx +2 -3
- package/shared/data-bar.tsx +5 -5
- package/shared/format-money.ts +3 -0
- package/shared/metric-card.tsx +127 -0
- package/shared/scene-hues.ts +0 -8
- package/shared/scene-tile.tsx +3 -11
- package/shared/settings-data.ts +28 -0
- package/shared/settings-rows.tsx +1 -6
- package/shared/stars.tsx +28 -0
- package/templates/ai-chat-landing.tsx +88 -58
- package/templates/ai-chat.tsx +65 -78
- package/templates/centered-hero.tsx +2 -2
- package/templates/dashboard-portfolio.tsx +77 -155
- package/templates/dashboard.tsx +17 -134
- package/templates/detail-page.tsx +14 -18
- package/templates/documentation-design.tsx +66 -519
- package/templates/documentation-technical.tsx +2 -22
- package/templates/editor.tsx +168 -185
- package/templates/file-explorer.tsx +10 -24
- package/templates/ide.tsx +12 -18
- package/templates/incident-console.tsx +48 -46
- package/templates/kanban-board.tsx +36 -85
- package/templates/library.tsx +42 -66
- package/templates/login-card.tsx +33 -126
- package/templates/login-split.tsx +91 -123
- package/templates/login-sso.tsx +12 -33
- package/templates/login.tsx +21 -121
- package/templates/messaging-shell.tsx +162 -168
- package/templates/payment-form.tsx +24 -26
- package/templates/product-detail.tsx +7 -40
- package/templates/product-tour.tsx +38 -60
- package/templates/settings-dialog.tsx +10 -32
- package/templates/settings-sidebar.tsx +44 -77
- package/templates/settings.tsx +208 -121
- package/templates/shell-nav.tsx +20 -75
- package/templates/shell-side-nav.tsx +16 -90
- package/templates/shell-top-nav.tsx +20 -53
- package/templates/side-gallery.tsx +19 -19
- package/templates/table-grouped.tsx +17 -164
- package/templates/table-page-chart.tsx +12 -11
- package/templates/table-page-heatmap-status.tsx +0 -17
- package/templates/table-page-shoe-store-heatmap.tsx +12 -12
- package/templates/table-page.tsx +4 -31
- package/templates/table.tsx +7 -10
- package/templates/tech-report.tsx +58 -72
- package/templates/theme-showcase.tsx +124 -415
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
|
-
// L[h=fill] > LC[p=6] > V[g=6] > (H[j=between a=center] > Hd"The night vault"[level=1] +
|
|
3
|
+
// L[h=fill] > LC[p=6] > V[g=6] > (H[j=between a=center] > Hd"The night vault"[level=1] + Tx"1 year"[t=body]) + (G[c={min:280} g=4] > (C > V[g=2] > Tx"Total value"[t=supporting] + (H[g=2] > Tx"$294,200"[t=display-3] + Tx"+14.8%"[t=body]))*4) + (G[c={min:280} g=4] > (C > V[g=4] > (H[j=between] > Hd"Vault value"[level=2] + Lk"View details") + (C > V[g=3] > Tx"Weekly closes"[t=supporting])) + (V[g=4] > (H[j=between] > Hd"Top holdings"[level=2] + Lk"View all") + UL)) + D + (H[j=between a=start] > (V[g=1] > Hd"Market at midnight"[level=2] + Tx"Past 24 hours under moonlight"[t=body]) + B"View more") + (G[c={min:280} g=4] > (C > V[g=3] > Hd"Index"[level=3] + Tx"$5,200"[t=body])*8) + (C > V[g=4] > Hd"Trending stocks"[level=3] + T)
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Portfolio Dashboard — the night vault: KPI tiles, a weekly value chart, the holdings list, and the market board. (Frame/responsive/container: see XLE header above.)
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
import {useState} from 'react';
|
|
10
|
-
|
|
11
8
|
import {VStack, HStack, Layout, LayoutContent} from '@astryxdesign/core/Layout';
|
|
12
9
|
import {Text, Heading} from '@astryxdesign/core/Text';
|
|
13
10
|
import {Card} from '@astryxdesign/core/Card';
|
|
@@ -15,10 +12,10 @@ import {Grid} from '@astryxdesign/core/Grid';
|
|
|
15
12
|
import {Link} from '@astryxdesign/core/Link';
|
|
16
13
|
import {Avatar} from '@astryxdesign/core/Avatar';
|
|
17
14
|
import {List, ListItem} from '@astryxdesign/core/List';
|
|
18
|
-
import {DropdownMenu} from '@astryxdesign/core/DropdownMenu';
|
|
19
15
|
import {Button} from '@astryxdesign/core/Button';
|
|
20
|
-
import {
|
|
21
|
-
import type {TableColumn} from '@astryxdesign/core/Table';
|
|
16
|
+
import {proportional, pixel} from '@astryxdesign/core/Table';
|
|
17
|
+
import type {TableColumn, PixelWidth} from '@astryxdesign/core/Table';
|
|
18
|
+
import {MetricCard, TableCard} from 'astryx-dracula/shared/metric-card';
|
|
22
19
|
import {Divider} from '@astryxdesign/core/Divider';
|
|
23
20
|
import {MetricDelta} from 'astryx-dracula/shared/metric-delta';
|
|
24
21
|
import {Sparkline, type SparkPoint} from 'astryx-dracula/shared/sparkline';
|
|
@@ -142,39 +139,13 @@ const topAssets = [
|
|
|
142
139
|
];
|
|
143
140
|
|
|
144
141
|
// 96 points per series = one tick every 15 minutes across a 24h window.
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
volatility: number,
|
|
153
|
-
N: number = 96,
|
|
154
|
-
): SparkPoint[] {
|
|
155
|
-
let s = seed >>> 0;
|
|
156
|
-
const rand = () => {
|
|
157
|
-
s = (Math.imul(s, 1664525) + 1013904223) >>> 0;
|
|
158
|
-
return s / 0x100000000;
|
|
159
|
-
};
|
|
160
|
-
const points: SparkPoint[] = [];
|
|
161
|
-
let drift = 0;
|
|
162
|
-
for (let i = 0; i < N; i++) {
|
|
163
|
-
const t = i / (N - 1);
|
|
164
|
-
const trend = start + (end - start) * t;
|
|
165
|
-
// Multiple overlaid waves at different frequencies for session-like rhythm
|
|
166
|
-
// plus high-frequency chop.
|
|
167
|
-
const wave =
|
|
168
|
-
Math.sin(t * Math.PI * 2.3 + seed * 0.13) * volatility * 1.4 +
|
|
169
|
-
Math.sin(t * Math.PI * 5.7 + seed * 0.41) * volatility * 0.9 +
|
|
170
|
-
Math.sin(t * Math.PI * 13.1 + seed * 0.07) * volatility * 0.5;
|
|
171
|
-
// Loosely-correlated random walk so adjacent ticks jitter rather than glide.
|
|
172
|
-
drift = drift * 0.55 + (rand() - 0.5) * volatility * 2.2;
|
|
173
|
-
// Occasional sharper spike to mimic news-driven moves.
|
|
174
|
-
const spike = rand() < 0.04 ? (rand() - 0.5) * volatility * 4 : 0;
|
|
175
|
-
points.push({id: `${seed}-${i}`, value: trend + wave + drift + spike});
|
|
176
|
-
}
|
|
177
|
-
return points;
|
|
142
|
+
// Static arrays: the dashboard metric tiles already establish the toSparkSeries
|
|
143
|
+
// precedent (static number rows + stable ids), so these print the same LCG
|
|
144
|
+
// output the generator produced instead of rerunning it per render. Every
|
|
145
|
+
// sample carries the slot it was generated for, so the bars key off data
|
|
146
|
+
// rather than off the array position.
|
|
147
|
+
function toSparkSeries(seed: number, values: number[]): SparkPoint[] {
|
|
148
|
+
return values.map((value, i) => ({id: `${seed}-${i}`, value}));
|
|
178
149
|
}
|
|
179
150
|
|
|
180
151
|
// Market index cards — 24h sparkline data (every 15min, 96 points)
|
|
@@ -185,7 +156,8 @@ const marketIndices = [
|
|
|
185
156
|
price: '43,821.67',
|
|
186
157
|
change: '+0.42%',
|
|
187
158
|
positive: true,
|
|
188
|
-
|
|
159
|
+
// prettier-ignore
|
|
160
|
+
spark: toSparkSeries(1337, [77.87, 75.96, 76.35, 77.99, 78.95, 77.57, 78.14, 75.12, 74.24, 74.03, 72.88, 71.93, 74.65, 71.43, 69.56, 74.19, 72.66, 72.02, 76.09, 74.07, 75.82, 78.14, 77.91, 80.15, 79.18, 76.96, 76.57, 81.07, 83.59, 83.21, 81.58, 83.82, 84.82, 88.56, 87.03, 89.64, 87.35, 89.63, 90.67, 87.45, 87.4, 86.09, 87.85, 82.63, 81.81, 84.78, 82.26, 83.25, 80.04, 80.63, 83.56, 84.28, 83.96, 84.49, 80.85, 79.3, 80.45, 84.58, 81.83, 83.45, 87.92, 90.46, 89.1, 88.1, 90.6, 87.26, 84.82, 84.27, 86.7, 82.89, 84.38, 89.24, 82.39, 82.18, 82.24, 81.38, 82.38, 78.55, 76.91, 75.31, 78.55, 76.46, 73.89, 76.36, 77.01, 79.15, 78.98, 79.79, 78.68, 77.35, 76.01, 84.41, 82.31, 81.97, 80.42, 80.29]),
|
|
189
161
|
},
|
|
190
162
|
{
|
|
191
163
|
name: 'NASDAQ',
|
|
@@ -193,7 +165,8 @@ const marketIndices = [
|
|
|
193
165
|
price: '18,942.18',
|
|
194
166
|
change: '-0.50%',
|
|
195
167
|
positive: false,
|
|
196
|
-
|
|
168
|
+
// prettier-ignore
|
|
169
|
+
spark: toSparkSeries(2042, [88.89, 94.82, 92.53, 93.22, 91.88, 91.07, 90.86, 89.3, 89.42, 85.69, 88.02, 82.52, 84.79, 86.08, 81.15, 78.83, 79.56, 80.01, 83.14, 84.1, 88.09, 84.29, 82.48, 84.9, 84.15, 86.04, 87.12, 86.85, 82.29, 84.8, 81.87, 79.88, 79.57, 78.77, 82.32, 83.19, 85.35, 86.71, 87.68, 80, 74.7, 73.28, 77.07, 71.96, 68.62, 70.7, 73.72, 74.67, 68.28, 71.29, 69.49, 71.73, 72.22, 73.75, 77.29, 79.86, 76.32, 78.94, 78.77, 77.52, 78.62, 80.01, 85.09, 86.36, 83.71, 88.49, 89.99, 86.02, 89.11, 83.46, 84.72, 85.43, 85.97, 84.73, 86.18, 88.06, 82.82, 82.32, 87.12, 82.77, 79.32, 84.78, 84.46, 80.3, 84.21, 82.15, 84.84, 75.1, 82.07, 85.92, 78.74, 82.77, 82.99, 87.84, 88.57, 90.07]),
|
|
197
170
|
},
|
|
198
171
|
{
|
|
199
172
|
name: 'S&P 500',
|
|
@@ -201,7 +174,8 @@ const marketIndices = [
|
|
|
201
174
|
price: '5,918.33',
|
|
202
175
|
change: '+0.21%',
|
|
203
176
|
positive: true,
|
|
204
|
-
|
|
177
|
+
// prettier-ignore
|
|
178
|
+
spark: toSparkSeries(3155, [73.95, 72.03, 71.08, 71.6, 73.2, 72.67, 74.14, 73.45, 76.38, 75.1, 77.61, 79.72, 77.52, 79.27, 78.25, 75.63, 76.23, 76.58, 72.42, 72.4, 69.84, 66.76, 67.12, 67.73, 64.69, 68.27, 65.01, 64.02, 66.05, 69.6, 68.7, 70.05, 69.54, 65.52, 66.42, 66.58, 69.36, 71.28, 71.66, 73.08, 70.32, 69.85, 72.45, 72.94, 72.02, 74.88, 71.61, 71.7, 72.69, 74.3, 74.46, 71.99, 70.45, 70.11, 72.53, 69.96, 71.6, 72, 74.4, 70.78, 72.74, 71.44, 71.37, 73.54, 71.05, 72.86, 70.79, 69.5, 73.43, 72.36, 73.76, 75.05, 77.58, 77.17, 78.5, 82.25, 78.78, 78.42, 78.07, 82.01, 78.82, 78.48, 78.36, 79.8, 79, 76.6, 75.18, 75.82, 78.39, 80.59, 80.93, 79.71, 78.44, 74.41, 74.09, 75.98]),
|
|
205
179
|
},
|
|
206
180
|
{
|
|
207
181
|
name: 'NYSE Composite',
|
|
@@ -209,7 +183,8 @@ const marketIndices = [
|
|
|
209
183
|
price: '19,752.41',
|
|
210
184
|
change: '-0.20%',
|
|
211
185
|
positive: false,
|
|
212
|
-
|
|
186
|
+
// prettier-ignore
|
|
187
|
+
spark: toSparkSeries(4287, [64.45, 61.11, 62.04, 60.88, 61.84, 66.42, 65.95, 68.68, 66.53, 66.2, 64.61, 66.78, 65.37, 64.69, 64.48, 60.48, 70.11, 69.62, 67.37, 70.31, 73.61, 74.74, 71.95, 68.83, 68.85, 66.69, 65.72, 64.01, 65.91, 64.38, 68.51, 63.62, 68.71, 66.45, 66.61, 70.22, 67.86, 68.96, 72.44, 74.53, 74.4, 75.52, 72.63, 69.2, 67.72, 72.46, 75.34, 74.64, 76.13, 73.74, 73.53, 74.47, 76.73, 76.4, 74.91, 74.34, 73.92, 70.94, 71.5, 68.68, 68.44, 65.97, 68.07, 69.63, 70.45, 68.45, 65.12, 63.93, 64.71, 66.68, 61.08, 58, 61.79, 63.61, 65.05, 62.4, 68.06, 65.87, 66.63, 68.18, 68.06, 67.32, 64.23, 65.72, 63.78, 62.09, 63.94, 61.55, 59.14, 59.95, 58.74, 59.44, 61.25, 63.96, 60.62, 59.61]),
|
|
213
188
|
},
|
|
214
189
|
{
|
|
215
190
|
name: 'NVIDIA Corp.',
|
|
@@ -217,7 +192,8 @@ const marketIndices = [
|
|
|
217
192
|
price: '$177.39',
|
|
218
193
|
change: '+0.93%',
|
|
219
194
|
positive: true,
|
|
220
|
-
|
|
195
|
+
// prettier-ignore
|
|
196
|
+
spark: toSparkSeries(5519, [66.26, 68.28, 68.39, 65.49, 62.97, 68.15, 71.28, 67.14, 71.53, 68.43, 69.39, 73.97, 67.92, 57.1, 61.58, 64.45, 59.15, 55.38, 56.78, 60.73, 63.46, 59.3, 56.58, 56.55, 59.43, 58.93, 57.88, 57.25, 56.11, 61.79, 62.87, 65.09, 66.15, 59.93, 60.64, 63.97, 65.3, 65.5, 66.02, 67.5, 67.43, 61.9, 59.69, 59.48, 59.88, 61.71, 59.95, 54.99, 52.18, 51.09, 52.02, 53.2, 55.88, 60.58, 56.65, 58.89, 55.9, 58.03, 60.31, 62.14, 57.94, 61.24, 60.94, 59.09, 64.39, 63.59, 64.45, 65.84, 66.83, 69.01, 68.41, 71.9, 74.79, 69.82, 67.76, 68.77, 67.7, 65.13, 62.97, 66.86, 69.31, 69.64, 67.68, 67.14, 67.12, 68.13, 61.27, 66.8, 63.53, 68.48, 64.28, 68.53, 69.09, 66.14, 66.67, 72.57]),
|
|
221
197
|
},
|
|
222
198
|
{
|
|
223
199
|
name: 'Intel Corp.',
|
|
@@ -225,7 +201,8 @@ const marketIndices = [
|
|
|
225
201
|
price: '$50.38',
|
|
226
202
|
change: '+4.89%',
|
|
227
203
|
positive: true,
|
|
228
|
-
|
|
204
|
+
// prettier-ignore
|
|
205
|
+
spark: toSparkSeries(6701, [40.29, 36.52, 38.65, 38.66, 38.73, 39.59, 39.74, 40.27, 37.68, 37.74, 38.19, 38.45, 34.69, 33.53, 36.19, 35.7, 37.18, 36.76, 36.8, 38.82, 38.54, 36.97, 38.21, 39.09, 43.08, 45.5, 42.94, 40.59, 40.6, 43.51, 42.51, 44.7, 47.77, 46.53, 44.78, 45.66, 45.29, 47.54, 45.72, 47.71, 47.55, 49.13, 47.53, 43.52, 41.69, 40.58, 41.74, 46.68, 47.53, 49.99, 46.64, 49.91, 49.01, 51.21, 47.53, 45.66, 47.51, 48.7, 50.76, 53.95, 48.88, 50.92, 51.45, 51.69, 49.34, 52.86, 55.07, 51.08, 50.1, 49.78, 49.18, 49.55, 49.15, 45.34, 47.77, 45.99, 43.61, 44.84, 48.01, 46.33, 43.42, 46.93, 46.35, 47.6, 45.09, 45.85, 43.72, 41.2, 42.89, 42.83, 43.48, 46.41, 49.01, 48.49, 48.68, 51.78]),
|
|
229
206
|
},
|
|
230
207
|
{
|
|
231
208
|
name: 'Nokia Corp.',
|
|
@@ -233,7 +210,8 @@ const marketIndices = [
|
|
|
233
210
|
price: '$8.82',
|
|
234
211
|
change: '+6.65%',
|
|
235
212
|
positive: true,
|
|
236
|
-
|
|
213
|
+
// prettier-ignore
|
|
214
|
+
spark: toSparkSeries(7823, [27.46, 26.98, 25.8, 25.95, 22.92, 23.25, 27.1, 28.27, 26.12, 28.96, 28.95, 27.78, 29.41, 30.28, 30.85, 32.49, 31.78, 32.11, 34.47, 36.23, 36.59, 37.96, 35.38, 36.79, 37.14, 37.87, 36.61, 35.2, 39.04, 40.18, 41.09, 41.92, 38.86, 36.21, 33.95, 35.27, 35.61, 35.38, 37.29, 37.02, 36.76, 34.27, 34.9, 34.79, 33.48, 34.53, 35.99, 38.24, 37.76, 39.23, 38.1, 36.08, 35.04, 36.38, 37.29, 39.2, 41.21, 42.11, 41.35, 38.23, 36.64, 37.97, 39.03, 39.17, 36.98, 35.69, 33.04, 31.86, 38.61, 35.08, 35.33, 36.35, 34.24, 36.73, 32.08, 32.11, 32.87, 31.23, 33.2, 33.36, 32.8, 31.44, 32.34, 29.65, 36.02, 38.33, 39.14, 42.58, 44.72, 43.06, 40.97, 40.47, 41.21, 40.21, 40.21, 40.11]),
|
|
237
215
|
},
|
|
238
216
|
{
|
|
239
217
|
name: 'Tesla, Inc.',
|
|
@@ -241,7 +219,8 @@ const marketIndices = [
|
|
|
241
219
|
price: '$360.59',
|
|
242
220
|
change: '-5.42%',
|
|
243
221
|
positive: false,
|
|
244
|
-
|
|
222
|
+
// prettier-ignore
|
|
223
|
+
spark: toSparkSeries(8945, [89.21, 88.42, 91.95, 94.9, 97.2, 93.76, 95.02, 93.99, 96.41, 93.71, 92.92, 95.4, 91.94, 97.48, 96.94, 95.45, 92.4, 93.76, 96.33, 97.28, 96.74, 92.99, 92.26, 90.48, 93.76, 92.11, 86.82, 83.2, 87.28, 88.08, 86.09, 81.46, 77.09, 79.91, 85.21, 80.13, 85.23, 80.03, 81.33, 84.31, 84.52, 84.57, 81.19, 81.81, 75.98, 77.99, 77.95, 75.93, 76.87, 81.09, 83.04, 82.12, 78.12, 78.69, 77.63, 79.19, 74.18, 75.44, 77.03, 77.73, 71.89, 76.98, 74.25, 77.95, 77.4, 81.04, 78.54, 77.9, 78.6, 76.53, 73.58, 71.02, 71.9, 70.44, 76.85, 77.69, 76.22, 81.78, 82.78, 85.32, 87.59, 91.37, 88.55, 86.27, 84.09, 86.52, 85.12, 84.64, 86.29, 82.58, 81.71, 86.28, 88.26, 83.47, 80.1, 79.92]),
|
|
245
224
|
},
|
|
246
225
|
];
|
|
247
226
|
|
|
@@ -264,7 +243,8 @@ const trendingStocks: StockRow[] = [
|
|
|
264
243
|
dailyPts: 1.35,
|
|
265
244
|
dailyPct: 0.72,
|
|
266
245
|
weekChg: 22.4,
|
|
267
|
-
|
|
246
|
+
// prettier-ignore
|
|
247
|
+
spark: toSparkSeries(11023, [62.59, 62.87, 66.28, 66.94, 63.05, 60.71, 60.87, 63.01, 65.63, 66.72, 66.14, 65.67, 63.28, 63.24, 64.03, 65.31, 69.18, 67.03, 67.02, 65.16, 63.09, 63.23, 59.79, 57.91, 60.58, 62.7, 59.12, 60.59, 60.07, 64.53, 64.61, 68.12, 66.44, 67.6, 65.99, 65.37, 67.56, 64.35, 65.35, 66.14, 65.38, 65.53, 63.89, 68.35, 70.22, 72.78, 73.31, 77.18]),
|
|
268
248
|
},
|
|
269
249
|
{
|
|
270
250
|
id: '2',
|
|
@@ -273,7 +253,8 @@ const trendingStocks: StockRow[] = [
|
|
|
273
253
|
dailyPts: 3.2,
|
|
274
254
|
dailyPct: 0.78,
|
|
275
255
|
weekChg: 18.6,
|
|
276
|
-
|
|
256
|
+
// prettier-ignore
|
|
257
|
+
spark: toSparkSeries(12591, [52.61, 54.67, 54.37, 52.39, 53.13, 54.24, 53.77, 56.96, 54.21, 56.36, 52.87, 52.91, 52.91, 53.06, 54.05, 56.77, 55.22, 53.5, 55.24, 55.21, 55.57, 57.52, 58.11, 60.48, 62.67, 64.05, 67.62, 67.64, 70.11, 69.7, 67.71, 66.07, 64.31, 61.5, 60.25, 60.01, 62.04, 63.71, 64.69, 65.02, 63.57, 64.63, 65.9, 61.94, 64.71, 63.25, 60.84, 62.99]),
|
|
277
258
|
},
|
|
278
259
|
{
|
|
279
260
|
id: '3',
|
|
@@ -282,7 +263,8 @@ const trendingStocks: StockRow[] = [
|
|
|
282
263
|
dailyPts: 1.65,
|
|
283
264
|
dailyPct: 0.93,
|
|
284
265
|
weekChg: 45.2,
|
|
285
|
-
|
|
266
|
+
// prettier-ignore
|
|
267
|
+
spark: toSparkSeries(13744, [42.97, 43.84, 42.55, 43.07, 41.45, 45.69, 47.46, 45.15, 47.75, 43.08, 44.46, 37.8, 37.94, 35.72, 35.14, 32.33, 41.42, 45.22, 46.61, 45, 44.99, 48.42, 56.57, 59.5, 54.67, 55.56, 53.34, 54.15, 56.29, 57.95, 59.47, 59.02, 58.39, 61.6, 59.14, 66.24, 68.92, 69.13, 73.86, 76.15, 73.14, 74.89, 71.4, 71.95, 74.65, 69.61, 63.98, 63.68]),
|
|
286
268
|
},
|
|
287
269
|
{
|
|
288
270
|
id: '4',
|
|
@@ -291,7 +273,8 @@ const trendingStocks: StockRow[] = [
|
|
|
291
273
|
dailyPts: -0.8,
|
|
292
274
|
dailyPct: -0.43,
|
|
293
275
|
weekChg: 15.3,
|
|
294
|
-
|
|
276
|
+
// prettier-ignore
|
|
277
|
+
spark: toSparkSeries(14882, [65.57, 69.23, 72.54, 70.58, 69.7, 67.79, 66.66, 69.84, 72.05, 73.69, 71.33, 72.56, 68.97, 67.02, 69.25, 68.04, 70.12, 73.38, 71.49, 70.24, 70.59, 69.81, 70.5, 66.45, 63.72, 61.63, 63.06, 60.22, 61.87, 61.91, 61.4, 61.43, 62.03, 63.27, 65.56, 61.76, 62.77, 62.52, 63.4, 65.86, 61.54, 60.71, 61.09, 66, 62.46, 64, 67.5, 65.32]),
|
|
295
278
|
},
|
|
296
279
|
{
|
|
297
280
|
id: '5',
|
|
@@ -300,7 +283,8 @@ const trendingStocks: StockRow[] = [
|
|
|
300
283
|
dailyPts: 2.1,
|
|
301
284
|
dailyPct: 1.37,
|
|
302
285
|
weekChg: 12.8,
|
|
303
|
-
|
|
286
|
+
// prettier-ignore
|
|
287
|
+
spark: toSparkSeries(15967, [50.19, 52.48, 56.14, 57.13, 53.58, 52.4, 49.76, 51.71, 53.73, 52.83, 47.01, 48.64, 46.49, 44.71, 45.84, 47.51, 51.21, 51.75, 47.07, 53.25, 53.02, 56.04, 53.9, 54.27, 53.09, 56.7, 54.86, 53.61, 54.39, 55.27, 58.44, 59.21, 58.89, 60.97, 63.5, 64.41, 64.28, 65.48, 63.86, 62.88, 65.82, 64.06, 60.89, 57.93, 60.15, 57.94, 57.86, 54.45]),
|
|
304
288
|
},
|
|
305
289
|
{
|
|
306
290
|
id: '6',
|
|
@@ -309,7 +293,8 @@ const trendingStocks: StockRow[] = [
|
|
|
309
293
|
dailyPts: 4.5,
|
|
310
294
|
dailyPct: 0.9,
|
|
311
295
|
weekChg: 35.1,
|
|
312
|
-
|
|
296
|
+
// prettier-ignore
|
|
297
|
+
spark: toSparkSeries(17105, [46.75, 47.76, 46.13, 45.91, 45.34, 49.3, 50.87, 48.9, 46.85, 49.07, 50.88, 54.37, 54.34, 54.17, 52.62, 52.71, 55.65, 55.18, 57.28, 59.71, 56.07, 57.36, 56.8, 52.95, 49.59, 50.35, 50.11, 48.92, 51.6, 52.4, 50.29, 52.17, 54.87, 55.44, 54.27, 52.27, 53.76, 55.31, 55.75, 56.16, 57.8, 58.73, 54.6, 53.58, 55.27, 55.62, 62.45, 62.94]),
|
|
313
298
|
},
|
|
314
299
|
{
|
|
315
300
|
id: '7',
|
|
@@ -318,7 +303,8 @@ const trendingStocks: StockRow[] = [
|
|
|
318
303
|
dailyPts: -20.67,
|
|
319
304
|
dailyPct: -5.42,
|
|
320
305
|
weekChg: -8.3,
|
|
321
|
-
|
|
306
|
+
// prettier-ignore
|
|
307
|
+
spark: toSparkSeries(18249, [83.75, 78.44, 81.1, 79.12, 76.61, 84.38, 84.64, 87.7, 86.31, 80.02, 74.82, 75.1, 75.36, 73.06, 74.86, 75.38, 78.07, 78.02, 75.42, 81.25, 85.54, 86.68, 84.46, 86.89, 80.57, 84.1, 82.48, 85.83, 87.06, 79.95, 74.26, 68.41, 64.98, 70.44, 71.17, 74.21, 69.56, 73.59, 70.47, 70.24, 65.12, 64.41, 69.18, 62.39, 63.5, 59.78, 59.96, 60.64]),
|
|
322
308
|
},
|
|
323
309
|
{
|
|
324
310
|
id: '8',
|
|
@@ -327,7 +313,8 @@ const trendingStocks: StockRow[] = [
|
|
|
327
313
|
dailyPts: 2.35,
|
|
328
314
|
dailyPct: 4.89,
|
|
329
315
|
weekChg: -12.5,
|
|
330
|
-
|
|
316
|
+
// prettier-ignore
|
|
317
|
+
spark: toSparkSeries(19388, [71.45, 75.23, 76.21, 71.96, 69.34, 69.92, 69.21, 65.68, 62.87, 64.07, 64.44, 62.98, 59.15, 61.12, 62.74, 64.13, 62.29, 65.14, 60.86, 58.23, 56.22, 57.6, 60.39, 57.87, 57.04, 54.84, 55.29, 56.38, 56.92, 58.48, 61.09, 59.01, 58.65, 61.44, 61.55, 58.75, 61.48, 60.6, 58.99, 61.4, 56.7, 57.42, 59.98, 57.74, 64.44, 59.11, 59.16, 59.45]),
|
|
331
318
|
},
|
|
332
319
|
{
|
|
333
320
|
id: '9',
|
|
@@ -336,7 +323,8 @@ const trendingStocks: StockRow[] = [
|
|
|
336
323
|
dailyPts: -1.2,
|
|
337
324
|
dailyPct: -0.73,
|
|
338
325
|
weekChg: 28.7,
|
|
339
|
-
|
|
326
|
+
// prettier-ignore
|
|
327
|
+
spark: toSparkSeries(20471, [44.8, 46.66, 43.7, 46.12, 47.65, 50.65, 48.05, 52.96, 54.41, 54.91, 53.77, 49.18, 50.01, 52.14, 49.46, 49.81, 52.08, 52.56, 50.02, 50.61, 56.27, 56.79, 62.63, 61.99, 65.99, 62.88, 59.6, 57.44, 58.61, 62.78, 62, 59.58, 57.36, 54.92, 59.1, 55.37, 64.03, 63.13, 63.88, 65.3, 65.22, 60.4, 62.84, 60.12, 59.42, 65.11, 58, 57.8]),
|
|
340
328
|
},
|
|
341
329
|
{
|
|
342
330
|
id: '10',
|
|
@@ -345,7 +333,8 @@ const trendingStocks: StockRow[] = [
|
|
|
345
333
|
dailyPts: 5.4,
|
|
346
334
|
dailyPct: 0.87,
|
|
347
335
|
weekChg: 42.1,
|
|
348
|
-
|
|
336
|
+
// prettier-ignore
|
|
337
|
+
spark: toSparkSeries(21556, [42.07, 41.72, 43.08, 43.95, 42.71, 43, 47.8, 51.53, 49.82, 51.72, 51.75, 54.86, 54.64, 54.28, 55.52, 55.2, 53.51, 49.34, 45.58, 51.95, 44.7, 46.57, 48.92, 47.16, 49.62, 47.2, 47.47, 49.84, 51.12, 53.9, 53.39, 51.34, 51.56, 47.93, 50.31, 50.69, 50.32, 50.04, 53.93, 52.8, 52.46, 55.01, 56.66, 58.11, 59.39, 60.81, 62.81, 64]),
|
|
349
338
|
},
|
|
350
339
|
];
|
|
351
340
|
|
|
@@ -479,6 +468,17 @@ function MarketCard({
|
|
|
479
468
|
const formatSigned = (value: number, digits: number, suffix = '') =>
|
|
480
469
|
`${value >= 0 ? '+' : ''}${value.toFixed(digits)}${suffix}`;
|
|
481
470
|
|
|
471
|
+
const chgColumns: Array<{
|
|
472
|
+
key: 'dailyPts' | 'dailyPct' | 'weekChg';
|
|
473
|
+
header: string;
|
|
474
|
+
width: PixelWidth;
|
|
475
|
+
digits: number;
|
|
476
|
+
suffix: string;
|
|
477
|
+
}> = [
|
|
478
|
+
{key: 'dailyPts', header: 'Chg (pts)', width: pixel(104), digits: 2, suffix: ''},
|
|
479
|
+
{key: 'dailyPct', header: 'Chg (%)', width: pixel(96), digits: 2, suffix: '%'},
|
|
480
|
+
{key: 'weekChg', header: '52W (%)', width: pixel(96), digits: 1, suffix: '%'},
|
|
481
|
+
];
|
|
482
482
|
const trendingColumns: TableColumn<StockRow>[] = [
|
|
483
483
|
{
|
|
484
484
|
key: 'ticker',
|
|
@@ -500,39 +500,17 @@ const trendingColumns: TableColumn<StockRow>[] = [
|
|
|
500
500
|
</Text>
|
|
501
501
|
),
|
|
502
502
|
},
|
|
503
|
-
{
|
|
504
|
-
key:
|
|
505
|
-
header:
|
|
506
|
-
width:
|
|
507
|
-
renderCell: (row: StockRow) =>
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
},
|
|
514
|
-
{
|
|
515
|
-
key: 'dailyPct',
|
|
516
|
-
header: 'Chg (%)',
|
|
517
|
-
width: pixel(96),
|
|
518
|
-
renderCell: (row: StockRow) => (
|
|
519
|
-
<MetricDelta
|
|
520
|
-
value={formatSigned(row.dailyPct, 2, '%')}
|
|
521
|
-
positive={row.dailyPct >= 0}
|
|
522
|
-
/>
|
|
523
|
-
),
|
|
524
|
-
},
|
|
525
|
-
{
|
|
526
|
-
key: 'weekChg',
|
|
527
|
-
header: '52W (%)',
|
|
528
|
-
width: pixel(96),
|
|
529
|
-
renderCell: (row: StockRow) => (
|
|
530
|
-
<MetricDelta
|
|
531
|
-
value={formatSigned(row.weekChg, 1, '%')}
|
|
532
|
-
positive={row.weekChg >= 0}
|
|
533
|
-
/>
|
|
534
|
-
),
|
|
535
|
-
},
|
|
503
|
+
...chgColumns.map(column => ({
|
|
504
|
+
key: column.key,
|
|
505
|
+
header: column.header,
|
|
506
|
+
width: column.width,
|
|
507
|
+
renderCell: (row: StockRow) => {
|
|
508
|
+
const value = row[column.key] as number;
|
|
509
|
+
return (
|
|
510
|
+
<MetricDelta value={formatSigned(value, column.digits, column.suffix)} positive={value >= 0} />
|
|
511
|
+
);
|
|
512
|
+
},
|
|
513
|
+
})),
|
|
536
514
|
{
|
|
537
515
|
key: 'spark',
|
|
538
516
|
header: '24h Trend',
|
|
@@ -549,37 +527,6 @@ const trendingColumns: TableColumn<StockRow>[] = [
|
|
|
549
527
|
},
|
|
550
528
|
];
|
|
551
529
|
|
|
552
|
-
function MetricCard({
|
|
553
|
-
value,
|
|
554
|
-
change,
|
|
555
|
-
label,
|
|
556
|
-
caption,
|
|
557
|
-
}: {
|
|
558
|
-
value: string;
|
|
559
|
-
change: string;
|
|
560
|
-
label: string;
|
|
561
|
-
caption: string;
|
|
562
|
-
}) {
|
|
563
|
-
const positive = !change.startsWith('-');
|
|
564
|
-
return (
|
|
565
|
-
<Card>
|
|
566
|
-
<VStack gap={2}>
|
|
567
|
-
<Text type="supporting" color="secondary">
|
|
568
|
-
{label}
|
|
569
|
-
</Text>
|
|
570
|
-
<HStack gap={2} vAlign="center">
|
|
571
|
-
<Text type="display-3" weight="semibold" hasTabularNumbers>
|
|
572
|
-
{value}
|
|
573
|
-
</Text>
|
|
574
|
-
<MetricDelta value={change} positive={positive} />
|
|
575
|
-
</HStack>
|
|
576
|
-
<Text type="supporting" color="secondary">
|
|
577
|
-
{caption}
|
|
578
|
-
</Text>
|
|
579
|
-
</VStack>
|
|
580
|
-
</Card>
|
|
581
|
-
);
|
|
582
|
-
}
|
|
583
530
|
|
|
584
531
|
function AssetRow({
|
|
585
532
|
ticker,
|
|
@@ -610,13 +557,9 @@ function AssetRow({
|
|
|
610
557
|
);
|
|
611
558
|
}
|
|
612
559
|
|
|
613
|
-
// ============= SIDENAV =============
|
|
614
|
-
|
|
615
560
|
// ============= MAIN COMPONENT =============
|
|
616
561
|
|
|
617
562
|
export default function DashboardPortfolio() {
|
|
618
|
-
const [timeRange, setTimeRange] = useState('1 year');
|
|
619
|
-
|
|
620
563
|
return (
|
|
621
564
|
<Layout
|
|
622
565
|
height="fill"
|
|
@@ -626,22 +569,9 @@ export default function DashboardPortfolio() {
|
|
|
626
569
|
{/* Page header */}
|
|
627
570
|
<HStack hAlign="between" vAlign="center">
|
|
628
571
|
<Heading level={1}>The night vault</Heading>
|
|
629
|
-
<
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
variant: 'secondary',
|
|
633
|
-
size: 'lg',
|
|
634
|
-
}}
|
|
635
|
-
hasChevron
|
|
636
|
-
items={[
|
|
637
|
-
{label: '1 month', onClick: () => setTimeRange('1 month')},
|
|
638
|
-
{label: '3 months', onClick: () => setTimeRange('3 months')},
|
|
639
|
-
{label: '6 months', onClick: () => setTimeRange('6 months')},
|
|
640
|
-
{label: '1 year', onClick: () => setTimeRange('1 year')},
|
|
641
|
-
{label: '5 years', onClick: () => setTimeRange('5 years')},
|
|
642
|
-
{label: 'All time', onClick: () => setTimeRange('All time')},
|
|
643
|
-
]}
|
|
644
|
-
/>
|
|
572
|
+
<Text type="body" color="secondary">
|
|
573
|
+
1 year
|
|
574
|
+
</Text>
|
|
645
575
|
</HStack>
|
|
646
576
|
|
|
647
577
|
{/* KPI metric cards */}
|
|
@@ -698,19 +628,11 @@ export default function DashboardPortfolio() {
|
|
|
698
628
|
</Grid>
|
|
699
629
|
|
|
700
630
|
{/* Trending stocks table */}
|
|
701
|
-
<
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
columns={trendingColumns}
|
|
707
|
-
idKey="id"
|
|
708
|
-
textOverflow="truncate"
|
|
709
|
-
hasHover
|
|
710
|
-
dividers="rows"
|
|
711
|
-
/>
|
|
712
|
-
</VStack>
|
|
713
|
-
</Card>
|
|
631
|
+
<TableCard
|
|
632
|
+
title="Trending stocks"
|
|
633
|
+
data={trendingStocks}
|
|
634
|
+
columns={trendingColumns}
|
|
635
|
+
/>
|
|
714
636
|
</VStack>
|
|
715
637
|
</LayoutContent>
|
|
716
638
|
}
|
package/templates/dashboard.tsx
CHANGED
|
@@ -11,12 +11,10 @@ import {VStack, HStack, Layout, LayoutContent} from '@astryxdesign/core/Layout';
|
|
|
11
11
|
import {Text, Heading} from '@astryxdesign/core/Text';
|
|
12
12
|
import {Card} from '@astryxdesign/core/Card';
|
|
13
13
|
import {Button} from '@astryxdesign/core/Button';
|
|
14
|
-
import {ProgressBar} from '@astryxdesign/core/ProgressBar';
|
|
15
14
|
import {Grid} from '@astryxdesign/core/Grid';
|
|
16
|
-
import {
|
|
15
|
+
import {proportional, pixel} from '@astryxdesign/core/Table';
|
|
17
16
|
import type {TableColumn} from '@astryxdesign/core/Table';
|
|
18
17
|
import {Divider} from '@astryxdesign/core/Divider';
|
|
19
|
-
import {Link} from '@astryxdesign/core/Link';
|
|
20
18
|
import {Icon} from '@astryxdesign/core/Icon';
|
|
21
19
|
|
|
22
20
|
// Same-route hash: demo links stay focusable anchors without escaping the
|
|
@@ -28,8 +26,8 @@ const SELF_HASH = '#/templates/dashboard';
|
|
|
28
26
|
import {RefreshCw, Square} from 'lucide-react';
|
|
29
27
|
import {ChartLegend} from 'astryx-dracula/shared/chart-legend';
|
|
30
28
|
import {CHART_HUES} from 'astryx-dracula/shared/chart-hues';
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
29
|
+
import type {SparkPoint} from 'astryx-dracula/shared/sparkline';
|
|
30
|
+
import {MetricCard, TableCard, BarCell, CountCell} from 'astryx-dracula/shared/metric-card';
|
|
33
31
|
import {CHART_PANEL_STYLE} from 'astryx-dracula/shared/chart-panel-style';
|
|
34
32
|
|
|
35
33
|
// ============= DATA =============
|
|
@@ -352,38 +350,22 @@ const topPagesColumns: TableColumn<PageRow>[] = [
|
|
|
352
350
|
header: 'Views',
|
|
353
351
|
width: proportional(1),
|
|
354
352
|
renderCell: (item: PageRow) => (
|
|
355
|
-
<
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
max={topPagesMaxViews}
|
|
359
|
-
label={`${item.page} views`}
|
|
360
|
-
isLabelHidden
|
|
361
|
-
/>
|
|
362
|
-
<Text type="body" hasTabularNumbers maxLines={1}>
|
|
363
|
-
{formatCount(item.views)}
|
|
364
|
-
</Text>
|
|
365
|
-
</VStack>
|
|
353
|
+
<BarCell label={`${item.page} views`} value={item.views} max={topPagesMaxViews}>
|
|
354
|
+
{formatCount(item.views)}
|
|
355
|
+
</BarCell>
|
|
366
356
|
),
|
|
367
357
|
},
|
|
368
358
|
{
|
|
369
359
|
key: 'newUsers',
|
|
370
360
|
header: 'New Users',
|
|
371
361
|
width: pixel(104),
|
|
372
|
-
renderCell: (item: PageRow) =>
|
|
373
|
-
<Text hasTabularNumbers maxLines={1}>
|
|
374
|
-
{item.newUsers}
|
|
375
|
-
</Text>
|
|
376
|
-
),
|
|
362
|
+
renderCell: (item: PageRow) => <CountCell>{item.newUsers}</CountCell>,
|
|
377
363
|
},
|
|
378
364
|
{
|
|
379
365
|
key: 'avgTime',
|
|
380
366
|
header: 'Avg. Time',
|
|
381
367
|
width: pixel(104),
|
|
382
|
-
renderCell: (item: PageRow) =>
|
|
383
|
-
<Text hasTabularNumbers maxLines={1}>
|
|
384
|
-
{item.avgTime}
|
|
385
|
-
</Text>
|
|
386
|
-
),
|
|
368
|
+
renderCell: (item: PageRow) => <CountCell>{item.avgTime}</CountCell>,
|
|
387
369
|
},
|
|
388
370
|
];
|
|
389
371
|
|
|
@@ -418,50 +400,27 @@ const topEventsColumns: TableColumn<EventRow>[] = [
|
|
|
418
400
|
header: 'Count',
|
|
419
401
|
width: proportional(1),
|
|
420
402
|
renderCell: (item: EventRow) => (
|
|
421
|
-
<
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
max={topEventsMaxCount}
|
|
425
|
-
label={`${item.count}`}
|
|
426
|
-
isLabelHidden
|
|
427
|
-
/>
|
|
428
|
-
<Text type="body" hasTabularNumbers maxLines={1}>
|
|
429
|
-
{formatCount(item.count)}
|
|
430
|
-
</Text>
|
|
431
|
-
</VStack>
|
|
403
|
+
<BarCell label={`${item.count}`} value={item.count} max={topEventsMaxCount}>
|
|
404
|
+
{formatCount(item.count)}
|
|
405
|
+
</BarCell>
|
|
432
406
|
),
|
|
433
407
|
},
|
|
434
408
|
{
|
|
435
409
|
key: 'users',
|
|
436
410
|
header: 'Users',
|
|
437
411
|
width: pixel(72),
|
|
438
|
-
renderCell: (item: EventRow) => (
|
|
439
|
-
<Text hasTabularNumbers maxLines={1}>
|
|
440
|
-
{formatCount(item.users)}
|
|
441
|
-
</Text>
|
|
442
|
-
),
|
|
412
|
+
renderCell: (item: EventRow) => <CountCell>{formatCount(item.users)}</CountCell>,
|
|
443
413
|
},
|
|
444
414
|
{
|
|
445
415
|
key: 'newUsers',
|
|
446
416
|
header: 'New Users',
|
|
447
417
|
width: pixel(104),
|
|
448
|
-
renderCell: (item: EventRow) => (
|
|
449
|
-
<Text hasTabularNumbers maxLines={1}>
|
|
450
|
-
{formatCount(item.newUsers)}
|
|
451
|
-
</Text>
|
|
452
|
-
),
|
|
418
|
+
renderCell: (item: EventRow) => <CountCell>{formatCount(item.newUsers)}</CountCell>,
|
|
453
419
|
},
|
|
454
420
|
];
|
|
455
421
|
|
|
456
422
|
// ============= CHART COMPONENTS =============
|
|
457
423
|
|
|
458
|
-
// Chart series colors: desktop glows orange, mobile glows cyan. Sourced from
|
|
459
|
-
// the shared categorical hues so data encoding can never reach for purple.
|
|
460
|
-
const chartColors = {
|
|
461
|
-
desktop: CHART_HUES.orange,
|
|
462
|
-
mobile: CHART_HUES.cyan,
|
|
463
|
-
};
|
|
464
|
-
|
|
465
424
|
// Legend rows go through shared/chart-legend.tsx, which makes `label` a
|
|
466
425
|
// REQUIRED field so a hue can never ship without the word that names it.
|
|
467
426
|
|
|
@@ -496,7 +455,7 @@ function ActiveUsersChart() {
|
|
|
496
455
|
width={14}
|
|
497
456
|
height={hAll - hDesktop}
|
|
498
457
|
rx={4}
|
|
499
|
-
fill={
|
|
458
|
+
fill={CHART_HUES.cyan}
|
|
500
459
|
/>
|
|
501
460
|
<rect
|
|
502
461
|
x={x}
|
|
@@ -504,7 +463,7 @@ function ActiveUsersChart() {
|
|
|
504
463
|
width={14}
|
|
505
464
|
height={hDesktop}
|
|
506
465
|
rx={4}
|
|
507
|
-
fill={
|
|
466
|
+
fill={CHART_HUES.orange}
|
|
508
467
|
/>
|
|
509
468
|
{(i % 6 === 0 || i === bars.length - 1) && (
|
|
510
469
|
<text
|
|
@@ -535,8 +494,8 @@ function ActiveUsersChart() {
|
|
|
535
494
|
</Card>
|
|
536
495
|
<ChartLegend
|
|
537
496
|
entries={[
|
|
538
|
-
{label: 'Desktop', color:
|
|
539
|
-
{label: 'Mobile', color:
|
|
497
|
+
{label: 'Desktop', color: CHART_HUES.orange},
|
|
498
|
+
{label: 'Mobile', color: CHART_HUES.cyan},
|
|
540
499
|
]}
|
|
541
500
|
gap={4}
|
|
542
501
|
caption={
|
|
@@ -551,44 +510,6 @@ function ActiveUsersChart() {
|
|
|
551
510
|
|
|
552
511
|
// ============= CARD COMPONENTS =============
|
|
553
512
|
|
|
554
|
-
function MetricCard({
|
|
555
|
-
label,
|
|
556
|
-
value,
|
|
557
|
-
change,
|
|
558
|
-
positive,
|
|
559
|
-
sparkline,
|
|
560
|
-
}: {
|
|
561
|
-
label: string;
|
|
562
|
-
value: string;
|
|
563
|
-
change: string;
|
|
564
|
-
positive: boolean;
|
|
565
|
-
sparkline: SparkPoint[];
|
|
566
|
-
}) {
|
|
567
|
-
return (
|
|
568
|
-
<Card>
|
|
569
|
-
<VStack gap={2}>
|
|
570
|
-
<Text type="supporting" color="secondary">
|
|
571
|
-
{label}
|
|
572
|
-
</Text>
|
|
573
|
-
<HStack gap={2} vAlign="center">
|
|
574
|
-
<Text type="display-3" weight="semibold" hasTabularNumbers>
|
|
575
|
-
{value}
|
|
576
|
-
</Text>
|
|
577
|
-
<MetricDelta value={change} positive={positive} />
|
|
578
|
-
</HStack>
|
|
579
|
-
<Text type="supporting" color="secondary">
|
|
580
|
-
Last 30 days vs. Previous
|
|
581
|
-
</Text>
|
|
582
|
-
<Sparkline
|
|
583
|
-
data={sparkline}
|
|
584
|
-
label={`${label} thirty-day trend`}
|
|
585
|
-
positive={positive}
|
|
586
|
-
/>
|
|
587
|
-
</VStack>
|
|
588
|
-
</Card>
|
|
589
|
-
);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
513
|
function StackedBarCard({
|
|
593
514
|
title,
|
|
594
515
|
data,
|
|
@@ -652,44 +573,6 @@ function StackedBarCard({
|
|
|
652
573
|
);
|
|
653
574
|
}
|
|
654
575
|
|
|
655
|
-
// ============= TABLE COMPONENTS =============
|
|
656
|
-
|
|
657
|
-
function TableCard<T extends {id: string}>({
|
|
658
|
-
title,
|
|
659
|
-
linkLabel,
|
|
660
|
-
linkHref,
|
|
661
|
-
data,
|
|
662
|
-
columns,
|
|
663
|
-
}: {
|
|
664
|
-
title: string;
|
|
665
|
-
linkLabel: string;
|
|
666
|
-
linkHref: string;
|
|
667
|
-
data: T[];
|
|
668
|
-
columns: TableColumn<T>[];
|
|
669
|
-
}) {
|
|
670
|
-
return (
|
|
671
|
-
<Card>
|
|
672
|
-
<VStack gap={6}>
|
|
673
|
-
<HStack hAlign="between" vAlign="center">
|
|
674
|
-
<Heading level={3}>{title}</Heading>
|
|
675
|
-
<Link href={linkHref}>{linkLabel}</Link>
|
|
676
|
-
</HStack>
|
|
677
|
-
<Table<T>
|
|
678
|
-
data={data}
|
|
679
|
-
columns={columns}
|
|
680
|
-
idKey="id"
|
|
681
|
-
density="compact"
|
|
682
|
-
dividers="rows"
|
|
683
|
-
textOverflow="truncate"
|
|
684
|
-
hasHover
|
|
685
|
-
/>
|
|
686
|
-
</VStack>
|
|
687
|
-
</Card>
|
|
688
|
-
);
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
// ============= SIDENAV =============
|
|
692
|
-
|
|
693
576
|
// ============= MAIN COMPONENT =============
|
|
694
577
|
|
|
695
578
|
// A definite ancestor, so `Layout height="fill"` (Layout.tsx:58-63, height:
|