--- title: "Building an Interactive Dashboard" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Building an Interactive Dashboard} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE ) ``` This article shows how to build a self-contained HTML dashboard from Brightspace data using R Markdown and Chart.js. The result is a single HTML file you can open in any browser, share with colleagues, or host on a web server -- no R or Shiny required to view it. ## Strategy R Markdown is ideal for this: R chunks compute and aggregate data, then knitr's inline R expressions inject the results directly into Chart.js JavaScript blocks. knitr knits the whole thing into a single self-contained HTML file. ## Step 1: Create the Rmd template Create a file called `dashboard.Rmd` with this YAML front matter. The `params` block lets you render the same template for different date ranges. ```yaml --- title: "Brightspace LMS Dashboard" output: html_document: self_contained: true theme: null params: from_date: !r Sys.Date() - 365 to_date: !r Sys.Date() --- ``` ## Step 2: Data preparation chunk Add a hidden R chunk that loads and aggregates data. This chunk runs but produces no visible output (`include=FALSE`). ```{r, eval=FALSE} # This chunk uses include=FALSE in the actual Rmd library(brightspaceR) library(dplyr) library(lubridate) library(jsonlite) # Helpers: convert R vectors to JS array literals js_labels <- function(x) toJSON(as.character(x), auto_unbox = FALSE) js_values <- function(x) toJSON(as.numeric(x), auto_unbox = FALSE) # Fetch datasets enrollments <- bs_get_dataset("User Enrollments") roles <- bs_get_dataset("Role Details") grades <- bs_get_dataset("Grade Results") org_units <- bs_get_dataset("Org Units") users <- bs_get_dataset("Users") # Apply date filter from params enrollments <- enrollments |> filter( as.Date(enrollment_date) >= params$from_date, as.Date(enrollment_date) <= params$to_date ) # KPIs total_users <- format(nrow(users), big.mark = ",") total_enrol <- format(nrow(enrollments), big.mark = ",") n_courses <- format( n_distinct(org_units$org_unit_id[org_units$type == "Course Offering"]), big.mark = "," ) avg_grade <- grades |> filter(!is.na(points_numerator), points_numerator >= 0) |> summarise(m = round(mean(points_numerator, na.rm = TRUE), 1)) |> pull(m) # Chart data role_counts <- enrollments |> bs_join_enrollments_roles(roles) |> count(role_name, sort = TRUE) |> head(8) monthly_trend <- enrollments |> mutate(month = floor_date(as.Date(enrollment_date), "month")) |> count(month) |> arrange(month) grade_dist <- grades |> filter(!is.na(points_numerator), points_numerator >= 0) |> mutate(bracket = cut(points_numerator, breaks = seq(0, 100, 10), include.lowest = TRUE, right = FALSE )) |> count(bracket) |> filter(!is.na(bracket)) top_courses <- enrollments |> bs_join_enrollments_orgunits(org_units) |> filter(type == "Course Offering") |> count(name, sort = TRUE) |> head(10) ``` ## Step 3: HTML layout with inline R Below the data chunk, add raw HTML for the dashboard layout. knitr evaluates inline R expressions everywhere in the document -- including inside HTML tags and ` # ``` When knitr processes the Rmd, the inline R expressions are replaced with their evaluated results: ``` Before knitr: labels: INLINE_R: js_labels(role_counts$role_name) After knitr: labels: ["Student","Instructor","TA","Observer"] ``` The `js_labels()` and `js_values()` helpers use `jsonlite::toJSON()` to produce valid JavaScript array literals. This is safer than manual `paste0()` because `toJSON()` handles quoting, escaping, and edge cases automatically. The same pattern applies to each chart: line charts for trends, bar charts for distributions, horizontal bars for top courses. Each `new Chart()` call references a `` id and uses inline R to inject labels and data arrays. ## Step 5: CSS styling Add a ` ``` ## Rendering ```{r} # Default: last 12 months rmarkdown::render("dashboard.Rmd", output_file = "brightspaceR_output/dashboard.html") browseURL("brightspaceR_output/dashboard.html") ``` ## Parameterised reports Render different versions from the same template without editing the Rmd: ```{r} # This semester rmarkdown::render("dashboard.Rmd", params = list(from_date = as.Date("2026-01-01"), to_date = Sys.Date()), output_file = "brightspaceR_output/dashboard_s1_2026.html" ) # Last year rmarkdown::render("dashboard.Rmd", params = list(from_date = as.Date("2025-01-01"), to_date = as.Date("2025-12-31")), output_file = "brightspaceR_output/dashboard_2025.html" ) ``` This makes it straightforward to generate quarterly or per-semester reports from a single template. ## Using with the MCP server The MCP server's `execute_r` tool can use either approach: 1. **Rmd rendering** (recommended for complex dashboards): Write the Rmd file from `execute_r`, then call `rmarkdown::render()`. This produces the cleanest output and supports params. 2. **HTML string** (faster, simpler): Build HTML with `paste0()` and `writeLines()`. No Rmd dependency, but harder to maintain for complex layouts. Both write to the output directory and can be opened with `browseURL()`. ## Why Chart.js instead of plotly? | | Chart.js | plotly | |---|---|---| | **Dependencies** | None (CDN) | `plotly` R package + htmlwidgets | | **File size** | ~80KB (CDN-loaded) | 3-5MB per file (self-contained) | | **Sharing** | Single HTML, opens anywhere | Single HTML, but large | | **R Markdown** | Works via inline R in `