Skip to content

Commit 2165cd1

Browse files
feat(ggplot2): implement dashboard-metrics-tiles (#7599)
## Implementation: `dashboard-metrics-tiles` - r/ggplot2 Implements the **r/ggplot2** version of `dashboard-metrics-tiles`. **File:** `plots/dashboard-metrics-tiles/implementations/r/ggplot2.R` **Parent Issue:** #3791 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/26203610104)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent aa3cdf2 commit 2165cd1

2 files changed

Lines changed: 450 additions & 0 deletions

File tree

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#' anyplot.ai
2+
#' dashboard-metrics-tiles: Real-Time Dashboard Tiles
3+
#' Library: ggplot2 3.5.1 | R 4.4.1
4+
#' Quality: 92/100 | Created: 2026-05-21
5+
6+
library(ggplot2)
7+
library(dplyr)
8+
library(scales)
9+
library(ragg)
10+
11+
set.seed(42)
12+
13+
# --- Theme tokens -----------------------------------------------------------
14+
THEME <- Sys.getenv("ANYPLOT_THEME", "light")
15+
PAGE_BG <- if (THEME == "light") "#FAF8F1" else "#1A1A17"
16+
ELEVATED_BG <- if (THEME == "light") "#FFFDF6" else "#242420"
17+
INK <- if (THEME == "light") "#1A1A17" else "#F0EFE8"
18+
INK_SOFT <- if (THEME == "light") "#4A4A44" else "#B8B7B0"
19+
20+
COL_GOOD <- "#009E73" # Okabe-Ito 1 — good / brand
21+
COL_WARNING <- "#E69F00" # Okabe-Ito 5 — warning
22+
COL_CRITICAL <- "#D55E00" # Okabe-Ito 2 — critical / bad
23+
24+
# --- Data -------------------------------------------------------------------
25+
# Server health metrics snapshot (6 tiles in 3x2 grid)
26+
metric_names <- c("CPU Usage", "Memory", "Response Time", "Disk I/O", "Throughput", "Error Rate")
27+
value_nums <- c(45.2, 72.1, 118, 38.6, 1247, 0.82)
28+
value_labels <- c("45.2%", "72.1%", "118 ms", "38.6%", "1,247 req/s", "0.82%")
29+
changes <- c(-5.2, 8.1, -14.7, 3.4, 12.3, -22.5)
30+
statuses <- c("good", "warning", "good", "good", "good", "good")
31+
up_is_good <- c(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE)
32+
33+
n_metrics <- length(metric_names)
34+
n_pts <- 24
35+
36+
status_colors <- ifelse(
37+
statuses == "critical", COL_CRITICAL,
38+
ifelse(statuses == "warning", COL_WARNING, COL_GOOD)
39+
)
40+
41+
change_colors <- ifelse(
42+
(changes > 0 & !up_is_good) | (changes < 0 & up_is_good),
43+
COL_CRITICAL, COL_GOOD
44+
)
45+
46+
arrows <- ifelse(changes > 0, "", "")
47+
change_labels <- paste0(arrows, " ", sprintf("%.1f", abs(changes)), "%")
48+
49+
metrics_df <- data.frame(
50+
metric = factor(metric_names, levels = metric_names),
51+
value_label = value_labels,
52+
change_label = change_labels,
53+
status_color = status_colors,
54+
change_color = change_colors,
55+
stringsAsFactors = FALSE
56+
)
57+
58+
# Generate sparkline histories (end pinned to current value, with slight trend)
59+
spark_list <- lapply(seq_len(n_metrics), function(i) {
60+
base <- value_nums[i]
61+
chg <- changes[i] / 100 * base
62+
steps <- rnorm(n_pts, mean = chg / n_pts, sd = base * 0.035)
63+
vals <- base - chg + cumsum(steps)
64+
vals[n_pts] <- base
65+
data.frame(
66+
metric = metric_names[i],
67+
t = seq_len(n_pts),
68+
val = vals,
69+
status_color = status_colors[i],
70+
stringsAsFactors = FALSE
71+
)
72+
})
73+
spark_df <- do.call(rbind, spark_list)
74+
spark_df$metric <- factor(spark_df$metric, levels = metric_names)
75+
76+
# Normalise each sparkline to [0.15, 0.65] within the panel's y space
77+
spark_df <- spark_df |>
78+
group_by(metric) |>
79+
mutate(val_norm = rescale(val, to = c(0.15, 0.65))) |>
80+
ungroup()
81+
82+
spark_end <- spark_df[spark_df$t == n_pts, ]
83+
84+
# Annotation positions within the normalised [−0.18, 1.55] y range
85+
label_df <- data.frame(
86+
metric = metrics_df$metric,
87+
x_mid = (n_pts + 1) / 2,
88+
y_value = 1.35,
89+
y_change = 1.08,
90+
y_name = -0.06,
91+
value_label = metrics_df$value_label,
92+
change_label = metrics_df$change_label,
93+
status_color = metrics_df$status_color,
94+
change_color = metrics_df$change_color,
95+
name_color = INK_SOFT,
96+
stringsAsFactors = FALSE
97+
)
98+
99+
# --- Plot -------------------------------------------------------------------
100+
p <- ggplot() +
101+
# Shaded area under sparkline
102+
geom_area(
103+
data = spark_df,
104+
aes(x = t, y = val_norm, fill = status_color, group = metric),
105+
alpha = 0.15,
106+
show.legend = FALSE
107+
) +
108+
# Sparkline
109+
geom_line(
110+
data = spark_df,
111+
aes(x = t, y = val_norm, color = status_color, group = metric),
112+
linewidth = 0.9,
113+
show.legend = FALSE
114+
) +
115+
# Terminal dot
116+
geom_point(
117+
data = spark_end,
118+
aes(x = t, y = val_norm, color = status_color),
119+
size = 2.0,
120+
show.legend = FALSE
121+
) +
122+
# KPI value — large, status-coloured
123+
geom_text(
124+
data = label_df,
125+
aes(x = x_mid, y = y_value, label = value_label, color = status_color),
126+
size = 7,
127+
fontface = "bold",
128+
show.legend = FALSE
129+
) +
130+
# Change indicator with directional arrow
131+
geom_text(
132+
data = label_df,
133+
aes(x = x_mid, y = y_change, label = change_label, color = change_color),
134+
size = 3.2,
135+
show.legend = FALSE
136+
) +
137+
# Metric name label at bottom of tile
138+
geom_text(
139+
data = label_df,
140+
aes(x = x_mid, y = y_name, label = metric, color = name_color),
141+
size = 3.5,
142+
fontface = "bold",
143+
show.legend = FALSE
144+
) +
145+
scale_color_identity() +
146+
scale_fill_identity() +
147+
facet_wrap(~metric, nrow = 2, ncol = 3) +
148+
scale_y_continuous(limits = c(-0.18, 1.55), expand = c(0, 0)) +
149+
scale_x_continuous(expand = expansion(mult = 0.05)) +
150+
labs(
151+
title = "Server Health · dashboard-metrics-tiles · r · ggplot2 · anyplot.ai"
152+
) +
153+
theme_minimal(base_size = 8) +
154+
theme(
155+
plot.background = element_rect(fill = PAGE_BG, color = PAGE_BG),
156+
panel.background = element_rect(fill = ELEVATED_BG, color = INK_SOFT, linewidth = 0.4),
157+
panel.grid = element_blank(),
158+
axis.text = element_blank(),
159+
axis.title = element_blank(),
160+
axis.ticks = element_blank(),
161+
strip.text = element_blank(),
162+
plot.title = element_text(color = INK, size = 11, hjust = 0.5),
163+
plot.margin = margin(t = 20, r = 20, b = 20, l = 20),
164+
panel.spacing.x = unit(1.5, "lines"),
165+
panel.spacing.y = unit(1.5, "lines")
166+
)
167+
168+
# --- Save -------------------------------------------------------------------
169+
ggsave(
170+
filename = sprintf("plot-%s.png", THEME),
171+
plot = p,
172+
device = ragg::agg_png,
173+
width = 8,
174+
height = 4.5,
175+
units = "in",
176+
dpi = 400
177+
)

0 commit comments

Comments
 (0)