Command Palette

Search for a command to run...

Hectal
PHASE 4Beginner ~41 min· topic 7 of 7

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.js is 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.

Add clean.js, then load.jsdiagram
Rendering diagram…

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. 1

    Load the messy file

    Change daily.csv to daily-dirty.csv in loadDailyReport (the file name inside fetchText). 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. 2

    Change the language of the numbers

    In format.js change LOCALE to "de-DE". The total now reads 933.651. Every chart that uses formatNumber changes at once.

Code & diagrams

The dashboard as it stands after Phase 4 (modules inlined) New tab

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

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Loaded 365 days, 0 cleaning notes

Your project files

Your lumen-dashboard project after this topic. Click a file to see it in full.

package.jsonjson

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.

terminal
$ On the page (red box) and in the Console
── what you'll see ──
daily.csv is missing these columns: date, rides, temp_c, rain_mm. Is /data/lumn/daily.csv really the data file?

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.js every await is inside try/catch/finally, so the loading line always hides, even after an error. A common bug is hiding it only on success.

  • ▸

    format.js fixes the time zone of formatDay to 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. 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 for daily.csv).

  2. 2

    How it plugs into what exists. Your Phase 3 page stays: index.html keeps 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 becomes src/charts/rides-line.js, the start-up code becomes src/main.js (loaded as a module), and the 365 typed-in numbers are replaced by the real daily.csv. config.js holds DATA_URL, format.js turns numbers and days into text (a missing value becomes a dash, never NaN), load.js fetches and parses, clean.js repairs and logs.

  3. 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.js also 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. 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

01

Why does main.js keep the loading line in a finally block?

02

Why does load.js check the columns after parsing?

03

What changes in the code if the data moves to a bucket?

Practice

01

Add loadStatus() to load.js that fetches status.json, throws a clear error when stations is not a list, and returns the stations.

02

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.