Making Dashboard Stat Cards Interactive: Linking the Total Tasks Counter to Task Management View
During a recent maintenance cycle on JADA's operational dashboards, we identified a UX gap: the "Total Tasks 82" stat card on the maintenance hub was displaying live task counts but wasn't actionable. Users had to navigate manually to the Systems tab to view the actual task list. This post details how we made that card clickable to improve workflow efficiency, including the infrastructure decisions and deployment pipeline.
The Problem
The maintenance hub dashboard at maintenance.jada.local displays several key performance indicators as static cards, including total active tasks. While the card was visually prominent and correctly tallying task counts, clicking it did nothing. Users performing quick status checks had to perform an extra navigation step to drill into the task details, breaking the natural interaction pattern.
Architecture Overview
The JADA maintenance hub is a single-page application (SPA) built with vanilla JavaScript, deployed as a static HTML file to AWS S3 with CloudFront distribution for global caching and low-latency access. The application uses tab-based navigation controlled by a JavaScript switch statement that manages visibility of different functional areas: Dashboard, Systems (tasks), Maintenance Log, and Configuration.
Key infrastructure components:
- Origin: S3 bucket (credentials withheld)
- CDN: CloudFront distribution with index.html cache policy
- DNS: Route53 alias record pointing CloudFront distribution to
maintenance.jada.local - Source file:
/index.htmlin S3 root
Technical Implementation
DOM Structure and Tab Navigation
The maintenance hub uses a data-attribute-based tab system. Each section is a div with data-tab="tab-name", and navigation logic lives in a function called switchTab(tabName). For example:
function switchTab(tabName) {
// Hide all tabs
document.querySelectorAll('[data-tab]').forEach(tab => {
tab.style.display = 'none';
});
// Show selected tab
document.querySelector(`[data-tab="${tabName}"]`).style.display = 'block';
// Update active indicator
document.querySelectorAll('[data-nav-item]').forEach(item => {
item.classList.remove('active');
});
document.querySelector(`[data-nav-item="${tabName}"]`).classList.add('active');
}
The Systems tab, where the task table lives, is identified by data-tab="systems". This is the target we needed to wire the stat card to.
Making the Stat Card Clickable
The "Total Tasks" display exists as an HTML card with class info-card. The solution involved three changes:
- Add onclick handler: Wrap the card content or add an onclick attribute to invoke
switchTab('systems') - Update CSS for interactivity: Add a new class
info-card-linkthat applies hover states and cursor feedback - Preserve semantics: Keep the card's existing content and structure; don't restructure the DOM
The HTML change was minimal:
<div class="info-card info-card-link" onclick="switchTab('systems')">
<div class="card-label">Total Tasks</div>
<div class="card-value">82</div>
</div>
CSS additions for the interactive state:
.info-card-link {
cursor: pointer;
transition: background-color 0.2s ease, transform 0.15s ease;
}
.info-card-link:hover {
background-color: rgba(66, 135, 245, 0.1);
transform: translateY(-2px);
}
.info-card-link:active {
transform: translateY(0);
}
These styles provide immediate visual feedback that the card is interactive without breaking the existing design language.
Deployment Pipeline
Local Changes and S3 Sync
The maintenance hub lives as a static file in S3, not in a traditional git repository. Updates required a download-modify-upload cycle:
# Download current index.html from S3
aws s3 cp s3://jada-maintenance-hub/index.html ./index.html
# Edit locally (add onclick and CSS)
vim index.html
# Upload back to S3
aws s3 cp ./index.html s3://jada-maintenance-hub/index.html --content-type "text/html"
Cache Invalidation
Static files in CloudFront are cached based on TTL and version headers. Simply uploading to S3 doesn't guarantee immediate updates to end users. CloudFront invalidation was required:
# Invalidate the CloudFront distribution cache
aws cloudfront create-invalidation --distribution-id DISTRIBUTION_ID --paths "/index.html"
This command clears the cached version of index.html from all CloudFront edge nodes, forcing a fresh fetch from origin on the next request. Without this step, users would see the old version for up to 24 hours (depending on cache TTL).
Why These Decisions
Why not restructure the DOM? The maintenance hub's JS relies on querySelectorors for tab logic. Restructuring cards risked breaking CSS layouts or event delegation. Adding a single class and onclick attribute minimized surface area for bugs.
Why use onclick instead of event listeners? For a single card with high confidence in where it'll be placed, inline onclick is acceptable and more maintainable than hunting for the card element in a large JS file. In a larger app, delegated event listeners would be preferred.
Why CSS classes for hover instead of inline styles? Separation of concerns: styles belong in the stylesheet, not inline attributes. This also ensures the hover and active states remain consistent if the card design changes later.
Why invalidate CloudFront? CloudFront's default behavior is to cache aggressively. Without invalidation, users in different geographic regions or on repeat visits would see stale HTML. The invalidation ensures a hard refresh of the origin within minutes.
Testing and Verification
Post-deployment validation involved:
- Opening the maintenance hub in a browser and confirming the Total Tasks card shows hover effects
- Clicking the card and verifying the Systems tab becomes active
- Checking browser DevTools to confirm CloudFront served the new version (cache-control headers, ETag changes)
- Testing from a different network to ensure the invalidation reached all edge nodes
What's Next
This change was a quick win, but opens the door for broader dashboard interactivity improvements. Future enhancements could include:
- Making other stat cards (maintenance log count, system alerts) clickable and contextual
- Adding a filter or search box that switches to the Systems tab with pre-filtered results
- Refactoring the entire SPA to use a proper state management library (Vue, React) and a build pipeline
For now, the maintenance hub users can click