How the live dashboard actually works.
The dashboard on the Data Analysis page isn't a screenshot or a static mockup — it's pulling live records from a real Airtable base, aggregating them in JavaScript, and rendering charts and KPIs in real time. Here's the full technical stack, layer by layer.
Four components working together.
The dashboard connects a WordPress frontend to Airtable as the data source, with all aggregation and visualization logic running entirely in the browser. The Airtable API key never touches the browser — all data routes through a server-side PHP proxy, identical to the one powering the Process Automation demo.
WordPress Frontend
A hand-coded PHP page template renders the filter controls, KPI cards, bar chart, donut chart, trend bars, and data table. Vanilla JavaScript handles all data fetching, aggregation, filtering, and DOM rendering — no charting library, no framework, no build step.
SO Airtable Connector Plugin
The same custom WordPress plugin that powers the Process Automation demo registers the so_airtable_proxy read endpoint. It holds the API key in wp-config.php as a PHP constant — never in the database, never visible in the browser or network traffic.
Airtable REST API
The plugin makes server-side wp_remote_get calls to Airtable's v0 API across four tables in the Northshore Community Services demo base: Clients, Programs, Service Requests, and Monthly Metrics. Each fetch returns a full record set which JavaScript then processes locally.
Client-Side Aggregation & Rendering
Once data arrives in the browser, JavaScript takes over completely — counting records, grouping by status or program, calculating percentages, and rendering every chart and KPI card from scratch. Filters update the view instantly without another round trip to the server.
What the dashboard actually renders.
Each component is built from raw Airtable record data — no pre-aggregated fields, no summary tables. The math happens in the browser on every filter change.
Fetching data — proxy to dashboard.
The dashboard fires parallel fetches to the proxy endpoint for each table on page load. Once all four resolve, the render pipeline runs once with the complete dataset. This is the actual JavaScript running on this page right now.
function fetchTable(table, params) {
var url = ajaxUrl
+ "?action=so_airtable_proxy"
+ "&table=" + encodeURIComponent(table)
+ (params ? "&" + params : "");
return fetch(url).then(function(r) { return r.json(); });
}
// Fire all four fetches in parallel — render only after all resolve
Promise.all([
fetchTable("Clients"),
fetchTable("Programs"),
fetchTable("tblfrY1tR2ZMg3MHT"), // Service Requests — ID required, name fails
fetchTable("tblSJUdMl5UgfrP2m") // Monthly Metrics — ID required, name fails
]).then(function(results) {
dashboardData.clients = results[0].records || [];
dashboardData.programs = results[1].records || [];
dashboardData.serviceReqs = results[2].records || [];
dashboardData.monthlyMetrics = results[3].records || [];
renderDashboard(); // single render pass with complete dataset
}).catch(function(err) {
showError("Dashboard failed to load. Please refresh.");
});
function renderDonut(serviceReqs, programs) {
// Count requests per program
var counts = {};
serviceReqs.forEach(function(r) {
var prog = r.fields["Programs"]; // "Programs" plural — actual API field name
if (Array.isArray(prog)) {
prog.forEach(function(p) {
counts[p] = (counts[p] || 0) + 1;
});
}
});
var total = Object.values(counts).reduce(function(a, b) { return a + b; }, 0);
if (total === 0) return;
// Build conic-gradient string from percentage slices
var gradient = "";
var cursor = 0;
var colors = ["#e882a5","#333333","#fef2f5","#d46f92","#a0a0a0"];
var i = 0;
Object.keys(counts).forEach(function(prog) {
var pct = (counts[prog] / total) * 100;
gradient += colors[i % colors.length]
+ " " + cursor + "% "
+ (cursor + pct) + "%,";
cursor += pct;
i++;
});
donutEl.style.background =
"conic-gradient(" + gradient.slice(0,-1) + ")";
}
What the Airtable API actually requires.
The Northshore demo base has two tables that don't respond to name-based API calls — they require their table IDs instead. This is a real quirk of how Airtable assigns internal names, and it's exactly the kind of thing that only shows up in production.
tblfrY1tR2ZMg3MHT) and Monthly Metrics (tblSJUdMl5UgfrP2m) must be called by ID. Using their display names returns a 404 from the Airtable API. Clients and Programs work fine by name.
Programs with an S — not Program. A mismatch here returns undefined silently, not an error.
Phone Number (two words) in the API, not Phone. These naming inconsistencies are worth documenting — they're invisible until something breaks.
Page load to dashboard render.
From the moment the page loads to the moment charts appear, here's every step the data takes.
Browser — Four Parallel Fetches
On page load, JavaScript fires four simultaneous fetch() calls to the so_airtable_proxy AJAX endpoint — one per Airtable table. Promise.all() holds the render until all four resolve.
WordPress — Table Allowlist Check
The proxy plugin checks each requested table against its hardcoded allowlist. Permitted tables proceed; anything not on the list returns a 403. The API key is read from wp-config.php — never from the request.
PHP — Server-Side Airtable Request
wp_remote_get() calls api.airtable.com/v0/{base}/{table} with the Bearer token in the Authorization header. The full JSON response is passed back through the AJAX endpoint to the browser.
Browser — Aggregation & Render
Once all four datasets arrive, JavaScript runs a single render pass: counting records, building chart data structures, calculating percentages, and injecting HTML into every dashboard component simultaneously.
Filter Interaction — Local Only
When a filter changes, no new network request fires. JavaScript re-runs the aggregation and render functions against the already-fetched dataset in memory. Filters are instant because all the data is already in the browser.
Everything used to build this dashboard.
No charting libraries, no data processing frameworks. Every visualization is built from raw record counts and CSS — which means it loads fast, works everywhere, and requires no dependencies to maintain.
wp_remote_get, table allowlist, API key in wp-config.php
I can build a live dashboard from your data.
Whether your data lives in Airtable, Google Sheets, or a spreadsheet you've outgrown — this same pattern can surface it as a real-time dashboard your team actually uses. Book a free consult to talk through your setup.
Need someone who turns raw data into clear insight?
I'm available for short-term projects and part-time engagements — dashboard builds, data pipeline design, and Airtable architecture without the overhead of a full-time hire.