Skip to main content

dashboard_01_calibration/
dashboard_01_calibration.rs

1//! dashboard_01_calibration — Aetna fixture paired with the shadcn
2//! dashboard-01-style reference.
3//!
4//! Run:
5//! `cargo run -p aetna-core --example dashboard_01_calibration`
6
7use aetna_core::prelude::*;
8
9fn main() -> std::io::Result<()> {
10    let viewport = Rect::new(0.0, 0.0, 1180.0, 780.0);
11    let out_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("out");
12
13    let name = "dashboard_01_calibration";
14    let theme = Theme::aetna_dark();
15    let mut root = dashboard_01_calibration();
16    let bundle = render_bundle_themed(&mut root, viewport, &theme);
17    let written = write_bundle(&bundle, &out_dir, name)?;
18    for p in &written {
19        println!("wrote {}", p.display());
20    }
21    if !bundle.lint.findings.is_empty() {
22        eprintln!(
23            "\nlint findings for {name} ({}):",
24            bundle.lint.findings.len()
25        );
26        eprint!("{}", bundle.lint.text());
27    }
28
29    Ok(())
30}
31
32fn dashboard_01_calibration() -> El {
33    row([dashboard_sidebar(), dashboard_main()])
34        .key("metric:root")
35        .gap(0.0)
36        .fill_size()
37        .align(Align::Stretch)
38        .fill(tokens::BACKGROUND)
39}
40
41fn dashboard_sidebar() -> El {
42    column([
43        row([
44            icon_cell("A"),
45            column([
46                text("Acme Inc.")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Enterprise")
51                    .caption()
52                    .ellipsis()
53                    .width(Size::Fill(1.0)),
54            ])
55            .gap(2.0)
56            .width(Size::Fill(1.0))
57            .height(Size::Hug),
58        ])
59        .gap(tokens::SPACE_2)
60        .height(Size::Fixed(44.0))
61        .align(Align::Center),
62        section_label("Platform"),
63        side_item("layout-dashboard", "Dashboard", true),
64        side_item("activity", "Lifecycle", false),
65        side_item("bar-chart", "Analytics", false),
66        side_item("folder", "Projects", false),
67        spacer().height(Size::Fixed(tokens::SPACE_4)),
68        section_label("Documents"),
69        side_item("file-text", "Data library", false),
70        side_item("download", "Reports", false),
71        side_item("users", "Team", false),
72        spacer(),
73        row([
74            icon_cell("AK"),
75            column([
76                text("Alicia Koch")
77                    .semibold()
78                    .ellipsis()
79                    .width(Size::Fill(1.0)),
80                text("alicia@example.com")
81                    .caption()
82                    .ellipsis()
83                    .width(Size::Fill(1.0)),
84            ])
85            .gap(2.0)
86            .width(Size::Fill(1.0))
87            .height(Size::Hug),
88        ])
89        .gap(tokens::SPACE_2)
90        .height(Size::Fixed(50.0))
91        .align(Align::Center),
92    ])
93    .gap(tokens::SPACE_2)
94    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
95    .key("metric:sidebar")
96    .width(Size::Fixed(244.0))
97    .height(Size::Fill(1.0))
98    .fill(tokens::CARD)
99    .stroke(tokens::BORDER)
100}
101
102fn section_label(label: &'static str) -> El {
103    text(label)
104        .caption()
105        .height(Size::Fixed(22.0))
106        .padding(Sides::xy(tokens::SPACE_2, 0.0))
107}
108
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
140
141fn dashboard_main() -> El {
142    column([
143        dashboard_header(),
144        column([
145            row([
146                metric_card(
147                    "bar-chart",
148                    "Total Revenue",
149                    "$1,250.00",
150                    "+12.5%",
151                    "Trending up this month",
152                    true,
153                ),
154                metric_card(
155                    "users",
156                    "New Customers",
157                    "1,234",
158                    "-20%",
159                    "Acquisition needs attention",
160                    false,
161                ),
162                metric_card(
163                    "folder",
164                    "Active Accounts",
165                    "45,678",
166                    "+12.5%",
167                    "Strong user retention",
168                    true,
169                ),
170                metric_card(
171                    "activity",
172                    "Growth Rate",
173                    "4.5%",
174                    "+4.5%",
175                    "Meets growth projections",
176                    true,
177                ),
178            ])
179            .gap(tokens::SPACE_4),
180            row([chart_card(), sales_card()])
181                .gap(tokens::SPACE_4)
182                .height(Size::Fixed(306.0))
183                .align(Align::Stretch),
184            documents_card(),
185        ])
186        .gap(tokens::SPACE_4)
187        .padding(tokens::SPACE_7)
188        .height(Size::Fill(1.0)),
189    ])
190    .width(Size::Fill(1.0))
191    .height(Size::Fill(1.0))
192}
193
194fn dashboard_header() -> El {
195    row([
196        icon_button("menu").ghost(),
197        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
198        h3("Documents").key("metric:page.title"),
199        spacer(),
200        text_input("Search...", &Selection::default(), "dashboard-search")
201            .key("metric:command.input")
202            .width(Size::Fixed(260.0)),
203        icon_button("plus").ghost(),
204        icon_button("bell").ghost(),
205    ])
206    .key("metric:header")
207    .gap(tokens::SPACE_3)
208    .height(Size::Fixed(56.0))
209    .padding(Sides::xy(tokens::SPACE_4, 0.0))
210    .align(Align::Center)
211    .stroke(tokens::BORDER)
212}
213
214fn metric_card(
215    icon_name: &'static str,
216    title: &'static str,
217    value: &'static str,
218    delta: &'static str,
219    note: &'static str,
220    positive: bool,
221) -> El {
222    let badge = if positive {
223        badge(delta).success()
224    } else {
225        badge(delta).warning()
226    };
227    let badge = if title == "Total Revenue" {
228        badge.key("metric:kpi.badge")
229    } else {
230        badge
231    };
232    let value = if title == "Total Revenue" {
233        h2(value).ellipsis().key("metric:kpi.value")
234    } else {
235        h2(value).ellipsis()
236    };
237    card([card_content([
238        row([
239            row([
240                icon(icon_name)
241                    .color(tokens::MUTED_FOREGROUND)
242                    .icon_size(tokens::ICON_XS),
243                text(title).muted().ellipsis().width(Size::Fill(1.0)),
244            ])
245            .gap(tokens::SPACE_1)
246            .width(Size::Fill(1.0))
247            .align(Align::Center),
248            badge,
249        ])
250        .gap(tokens::SPACE_2)
251        .align(Align::Center),
252        value,
253        text(note).caption().ellipsis().width(Size::Fill(1.0)),
254    ])
255    .padding(tokens::SPACE_4)
256    .gap(tokens::SPACE_2)])
257    .key(if title == "Total Revenue" {
258        "metric:kpi.card"
259    } else {
260        title
261    })
262    .width(Size::Fill(1.0))
263}
264
265fn chart_card() -> El {
266    card([
267        card_header([
268            card_title("Visitors for the last 6 months"),
269            card_description("Total visitors by channel."),
270        ])
271        .padding(tokens::SPACE_4),
272        card_content([row(chart_bars())
273            .gap(2.0)
274            .height(Size::Fill(1.0))
275            .align(Align::End)])
276        .padding(Sides {
277            left: tokens::SPACE_4,
278            right: tokens::SPACE_4,
279            top: 0.0,
280            bottom: tokens::SPACE_4,
281        })
282        .height(Size::Fill(1.0)),
283    ])
284    .key("metric:chart.card")
285    .width(Size::Fill(1.0))
286    .height(Size::Fill(1.0))
287}
288
289fn chart_bars() -> Vec<El> {
290    [
291        48.0, 72.0, 56.0, 90.0, 64.0, 80.0, 108.0, 84.0, 122.0, 96.0, 136.0, 118.0,
292    ]
293    .into_iter()
294    .flat_map(|height| {
295        [
296            bar(height, tokens::MUTED_FOREGROUND),
297            bar((height - 28.0_f32).max(24.0), tokens::INPUT),
298        ]
299    })
300    .collect()
301}
302
303fn bar(height: f32, color: Color) -> El {
304    El::new(Kind::Custom("chart_bar"))
305        .fill(color)
306        .radius(tokens::RADIUS_SM)
307        .width(Size::Fill(1.0))
308        .height(Size::Fixed(height))
309}
310
311fn sales_card() -> El {
312    card([
313        card_header([
314            card_title("Recent Sales"),
315            card_description("You made 265 sales this month."),
316        ])
317        .padding(tokens::SPACE_4),
318        card_content([
319            sale_row("OM", "Olivia Martin", "olivia@example.com", "+$1,999.00"),
320            sale_row("JL", "Jackson Lee", "jackson@example.com", "+$39.00"),
321            sale_row("IN", "Isabella Nguyen", "isabella@example.com", "+$299.00"),
322            sale_row("WK", "William Kim", "will@example.com", "+$99.00"),
323        ])
324        .gap(tokens::SPACE_2)
325        .padding(Sides {
326            left: tokens::SPACE_4,
327            right: tokens::SPACE_4,
328            top: 0.0,
329            bottom: tokens::SPACE_4,
330        }),
331    ])
332    .key("metric:sales.card")
333    .width(Size::Fixed(330.0))
334    .height(Size::Fill(1.0))
335}
336
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
358
359fn documents_card() -> El {
360    card([
361        card_header([card_title("Documents")]).padding(tokens::SPACE_4),
362        card_content([scroll([table([
363            table_header([table_row([
364                table_head("").width(Size::Fixed(35.0)),
365                table_head("Header").width(Size::Fill(1.8)),
366                table_head("Section Type").width(Size::Fill(1.0)),
367                table_head("Status").width(Size::Fixed(104.0)),
368                table_head("Target").width(Size::Fixed(64.0)),
369                table_head("Limit").width(Size::Fixed(64.0)),
370                table_head("Reviewer").width(Size::Fixed(128.0)),
371                table_head("").width(Size::Fixed(32.0)),
372            ])
373            .padding(Sides::xy(tokens::SPACE_4, 0.0))
374            .key("metric:table.header")]),
375            divider(),
376            table_body([
377                document_row(
378                    "Cover page",
379                    "Cover page",
380                    "In Process",
381                    "18",
382                    "5",
383                    "Eddie Lake",
384                    "info",
385                ),
386                document_row(
387                    "Table of contents",
388                    "Table of contents",
389                    "Done",
390                    "29",
391                    "24",
392                    "Eddie Lake",
393                    "success",
394                ),
395            ]),
396        ])])
397        .height(Size::Fill(1.0))])
398        .gap(0.0)
399        .padding(0.0)
400        .height(Size::Fill(1.0)),
401    ])
402    .key("metric:table.card")
403    .height(Size::Fill(1.0))
404}
405
406fn document_row(
407    header: &'static str,
408    section: &'static str,
409    status: &'static str,
410    target: &'static str,
411    limit: &'static str,
412    reviewer: &'static str,
413    tone: &'static str,
414) -> El {
415    let status_badge = match tone {
416        "success" => badge(status).success(),
417        _ => badge(status).info(),
418    };
419    table_row([
420        table_utility_cell("::"),
421        table_cell(text(header).label().small()).width(Size::Fill(1.8)),
422        table_cell(text(section).muted()).width(Size::Fill(1.0)),
423        table_cell(status_badge).width(Size::Fixed(104.0)),
424        table_cell(text(target).label().small()).width(Size::Fixed(64.0)),
425        table_cell(text(limit).label().small()).width(Size::Fixed(64.0)),
426        table_cell(text(reviewer).muted()).width(Size::Fixed(128.0)),
427        table_action_cell(),
428    ])
429    .padding(Sides::xy(tokens::SPACE_4, 0.0))
430    .key(if header == "Cover page" {
431        "metric:table.row"
432    } else {
433        header
434    })
435}
436
437fn table_utility_cell(label: &'static str) -> El {
438    table_cell(text(label).muted().center_text()).width(Size::Fixed(35.0))
439}
440
441fn table_action_cell() -> El {
442    stack([icon("more-horizontal")
443        .icon_size(tokens::ICON_SM)
444        .color(tokens::MUTED_FOREGROUND)])
445    .align(Align::Center)
446    .justify(Justify::Center)
447    .width(Size::Fixed(32.0))
448    .height(Size::Hug)
449}
450
451fn icon_cell(label: &'static str) -> El {
452    El::new(Kind::Custom("icon_cell"))
453        .style_profile(StyleProfile::Surface)
454        .text(label)
455        .text_align(TextAlign::Center)
456        .caption()
457        .font_weight(FontWeight::Semibold)
458        .fill(tokens::MUTED)
459        .radius(tokens::RADIUS_SM)
460        .width(Size::Fixed(30.0))
461        .height(Size::Fixed(30.0))
462}