Topic 4.7
Build: The Dashboard Loads Real Data
In one line
Turn the hand-typed dashboard into one that loads its own data: a config file, a loader, a cleaner, number formatting, a loading message, an error box and a row of key numbers. It answers Priya's question: can the dashboard read my exports even when they are messy?
Before you start
You will learn
- Split the app into a loader, a cleaner and helpers, each in its own file
- Show a loading message, an error box and key numbers on the page
- Run the whole dashboard from your Vite project
You write code in
In your Vite project lumen-dashboard/ (run npm run dev), and in the editor on this page
Files you touch
- package.json
- index.html
- src/main.js
- src/data/load.js
- src/data/clean.js
- src/utils/config.js
- src/utils/format.js
- src/charts/rides-line.js
Think of it like this
A restaurant kitchen has stations. One person receives deliveries and checks them, one washes and trims, one cooks, one plates. When a delivery is wrong, the receiver says so at the door, and nobody cooks a bad ingredient. Today you set up the receiving and washing stations of the dashboard: load.js receives, clean.js washes.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- module
- A JavaScript file that exports names and imports others.
load.jsis a module. - entry point
- The file the page starts with:
src/main.js. - loader
- Code that fetches and parses data, and turns every failure into a clear error.
- KPI
- Key performance indicator: one important number shown in a box, such as total rides.
- role="alert"
- An HTML attribute that makes a screen reader announce a message as soon as it appears.
Step by step
01Start from your Phase 3 project
You have lumen-dashboard/ running with npm run dev, D3 installed and the data in public/data/lumen/ (Topic 4.1). Delete the starter files you do not need: src/counter.js, src/style.css, src/assets/ and public/icons.svg.
Replace Vite's index.html with your Phase 3 page, then take its <script> out: the line-chart functions go into src/charts/rides-line.js (add export before function drawRidesLine), and the page loads /src/main.js instead. The Files section shows both in full.
02Add the small helpers first
Create src/utils/config.js (one line: DATA_URL) and src/utils/format.js. They have no imports, so they cannot fail in surprising ways.
03Add clean.js, then load.js
clean.js is the cleaner from Topic 4.4: it knows nothing about the network. load.js fetches (fetchText), checks the status and the columns, calls cleanDaily, and exports loadDaily, loadDailyReport, loadStations and loadTrips. Later charts only call these.
04Wire the page and check the three states
Replace index.html and src/main.js with the files below, save, and watch Vite reload. You see "Loading the data…" for a moment, then the key numbers. Now change DATA_URL to "/data/lumn/" (one letter missing): the red box appears with the missing-columns message and the key numbers stay empty. Put the letter back.
Try it yourself
- 1
Load the messy file
Change
daily.csvtodaily-dirty.csvinloadDailyReport(the file name insidefetchText). The page shows 30 days, 56,930 rides, the busiest day 28 Mar 2025 (2,484), the quietest 23 Mar 2025 (750) and 9 in "Rows cleaned". The label "Rides in the year" is now wrong for a month: a reason to take labels from the data later. The line chart is wrong too: it counts days from 1 January, so its labels and month ticks describe January, not March. Phase 6 gives it a real date scale. - 2
Change the language of the numbers
In
format.jschangeLOCALEto"de-DE". The total now reads933.651. Every chart that usesformatNumberchanges at once.
Code & diagrams
The preview cannot import your local files, so this example pastes config.js, format.js, clean.js, load.js and main.js into one module (the comments show where each file starts). In your project they are separate files. Change the DATA_URL line to "/data/lumn/" and run it to see the error box.
Preview
Console
Nothing logged yet.Expected console output
Loaded 365 days, 0 cleaning notesYour project files
Your lumen-dashboard project after this topic. Click a file to see it in full.
The whole file after this topic. If yours differs, you can copy this one and carry on.
{
"name": "lumen-dashboard",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"d3": "^7.9.0"
},
"devDependencies": {
"vite": "^8.3.0"
}
}Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Point DATA_URL at a path that does not exist
In src/utils/config.js change "/data/lumen/" to "/data/lumn/" and save.
Myth vs fact
Myth
A loader is just fetch plus parse.
Fact
The loader's real job is the failures: status codes, wrong files, bad columns, empty results. The success path is three lines.
Myth
Splitting code into files is only tidiness.
Fact
It lets you test the cleaner without a network, swap DATA_URL without touching charts, and have each later phase edit one file.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
In
main.jseveryawaitis insidetry/catch/finally, so the loading line always hides, even after an error. A common bug is hiding it only on success. - ▸
format.jsfixes the time zone offormatDayto UTC because the dates come from date-only text (midnight UTC). Formatting them in the viewer's zone would show the day before for anyone behind UTC.
Remember this
- 1
Priya's need is met. She can point the dashboard at her export and it reads it. If something is wrong, the page says which file and why. The March file's nine problems are handled by
cleanDaily, with a log of each. The dashboard now shows five key numbers for the real year: 365 days, 933,651 rides, the busiest day (18 Jul 2025, 4,380) and the quietest (14 Aug 2025, 439), and the number of cleaning notes (0 fordaily.csv). - 2
How it plugs into what exists. Your Phase 3 page stays:
index.htmlkeeps the bike header, the week bars, the stations-running-low bars and the chart plan. It gains a loading line (#loading), an error box (#error) and a row of key numbers (#kpis). The inline script leaves the page: the hand-made line chart becomessrc/charts/rides-line.js, the start-up code becomessrc/main.js(loaded as a module), and the 365 typed-in numbers are replaced by the realdaily.csv.config.jsholdsDATA_URL,format.jsturns numbers and days into text (a missing value becomes a dash, neverNaN),load.jsfetches and parses,clean.jsrepairs and logs. - 3
The three states in the page. While the request runs, the page shows "Loading the data…" (
role="status", so a screen reader announces it). If anything throws, the loader's message appears in the red box (role="alert"). On success the loading line hides and the key numbers appear.load.jsalso checks that the file has the columns it needs, so a wrong URL that a server answers with a web page (Topic 4.2) gives "daily.csv is missing these columns…" and not nonsense. - 4
What the dashboard can do now that it could not before: read CSV from a folder or, by changing
DATA_URL, from cloud storage; survive missing, comma and impossible values with a log; show loading and error states; and compute totals and extremes with d3-array. The year's line is now drawn from the file, still by hand. Still missing: the week and stations bars are still typed in, and the line's hand-made scale and ticks are Phase 3's. Phase 5 draws the station bars from data with D3's join, Phase 6 redraws the line with D3 scales and axes, and Phase 7 adds stacked bars and a heatmap. Dates still use the UTC clock for date-only values (Topic 4.3), which the line chart will name.
Explain it without notes
Why does main.js keep the loading line in a finally block?
Why does load.js check the columns after parsing?
What changes in the code if the data moves to a bucket?
Practice
Add loadStatus() to load.js that fetches status.json, throws a clear error when stations is not a list, and returns the stations.
Add a sixth key number, "Rainy days", to main.js: the count of rows with rain_mm > 0.
Trade-offs
- ↔
Eight small files cost a little more to navigate than one script, and pay back as soon as a second person works on the project or a second chart needs the same data.
Done when you can
My project has config.js, format.js, clean.js, load.js and main.js and runs with npm run dev.
The page shows the loading line, then five key numbers.
I made the error box appear on purpose and read its message.
I can say what the dashboard cannot do yet and which phase adds it.