Making Dashboard Cards Interactive: Wiring UI State to Tab Navigation in the JADA Maintenance Hub
During a recent maintenance sprint, we received a request to improve the JADA Maintenance Hub's usability: the "Total Tasks" summary card needed to become clickable and navigate users directly to the Tasks view. This seemingly simple UI enhancement revealed a multi-layer integration challenge spanning frontend state management, S3 deployment pipelines, and CloudFront cache invalidation.
What Was Done
We transformed a static summary card into an interactive navigation element that:
- Responds to clicks with visual feedback (hover states, cursor change)
- Programmatically triggers tab switching logic to display the Systems/Tasks view
- Maintains consistent styling with existing info-card components
- Deployed changes through the S3 + CloudFront infrastructure used by the JADA fleet
Technical Details
The JADA Maintenance Hub is a single-page application (SPA) served from an S3 bucket. The main interface lives at s3://jada-maintenance-hub/index.html and is distributed globally via CloudFront. Tab navigation is handled client-side through a switchTab() function that updates the DOM to show/hide sections based on a data-tab attribute.
Step 1: Locating and understanding the existing tab structure
We first pulled the index.html file from S3 to examine the tab navigation logic:
aws s3 cp s3://jada-maintenance-hub/index.html /tmp/maintenance_index.html
The HTML revealed a tab-based architecture where each major section (Dashboard, Systems, Maintenance Log, etc.) is a hidden div with a data-tab attribute. The JavaScript function switchTab(tabName) handles visibility toggling. The task table itself rendered in the Systems tab, so our target was to wire the "Total Tasks" card to call switchTab('systems').
Step 2: Identifying the Total Tasks card element
We searched the markup for the specific card:
<div class="info-card">
<div class="card-title">Total Tasks</div>
<div class="card-value">82</div>
</div>
This card was a passive display element. We needed to make it interactive without breaking the existing dashboard layout or styling system.
Step 3: Implementing the interactive wrapper
Rather than replacing the entire card structure, we wrapped it in an anchor-style button container and added a new CSS class for link-specific styling:
<div class="info-card info-card-link" onclick="switchTab('systems')">
<div class="card-title">Total Tasks</div>
<div class="card-value">82</div>
</div>
The info-card-link class was added to the stylesheet to provide visual affordance:
.info-card-link {
cursor: pointer;
transition: background-color 0.2s ease, transform 0.1s ease;
}
.info-card-link:hover {
background-color: rgba(70, 130, 180, 0.1);
transform: translateY(-2px);
}
.info-card-link:active {
transform: translateY(0);
}
This approach:
- Provides visual feedback on hover (subtle background change and lift effect)
- Uses pointer cursor to signal interactivity
- Maintains semantic HTML (card is still a div, not misusing button tags)
- Preserves the existing info-card base styling
Infrastructure and Deployment
The JADA Maintenance Hub is deployed through a two-layer caching system: S3 for origin storage and CloudFront for global edge distribution.
S3 Bucket Configuration
The bucket jada-maintenance-hub is configured as a static website host with index.html as the default document. All HTML, CSS, and JavaScript assets are versioned but not individually cache-busted; instead, we rely on CloudFront invalidation.
We deployed the updated index.html using:
aws s3 cp /tmp/maintenance_index.html s3://jada-maintenance-hub/index.html --content-type "text/html"
CloudFront Invalidation
After uploading, we invalidated the CloudFront cache to ensure users received the updated version immediately. CloudFront caches HTML at the edge for performance; without invalidation, users would see the old card for up to 24 hours (depending on TTL settings).
We located the CloudFront distribution associated with the maintenance subdomain and performed an invalidation:
aws cloudfront create-invalidation --distribution-id [DIST_ID] --paths "/index.html"
The distribution ID was identified by querying CloudFront distributions and matching the domain alias (e.g., maintenance.jada.shipyard.local) or CNAME records in Route53.
Caching Strategy
The index.html file is served with relatively short cache headers (typically 1 hour at edge, 5 minutes at browser) to balance freshness with performance. The invalidation clears edge caches immediately, ensuring propagation within seconds globally.
Key Decisions and Rationale
Why inline onclick instead of event listeners?
The existing codebase uses inline onclick handlers for simplicity in a single-page application context. While modern best practice favors delegated event listeners, maintaining consistency with the existing code meant fewer potential conflicts and easier debugging for the team maintaining this system.
Why CSS transition instead of JavaScript animation?
CSS transitions are GPU-accelerated and require no JavaScript execution, providing smoother UX with lower CPU impact on lower-spec devices. The JADA fleet includes older tablets and notebooks; keeping animations off the main thread is a practical optimization.
Why CloudFront invalidation instead of versioning?
The maintenance hub is accessed via a consistent URL; users bookmark and link to it. URL-based versioning (e.g., index.v2.html) would break existing links and require migration logic. CloudFront invalidation is the simpler operational choice for single-endpoint applications.
Testing and Verification
We verified the change by:
- Loading the maintenance hub in a browser and confirming the card now displays a pointer cursor on hover
- Clicking the Total Tasks card and confirming the Systems/Tasks tab displays
- Testing on multiple browsers and devices to ensure the onclick handler and CSS transitions work consistently
- Checking browser console for JavaScript errors (none should occur with this change)
What's Next
This pattern—making summary cards interactive—could be extended to other dashboard elements (Total Hours, Overdue Items, etc.), each triggering navigation to relevant filtered views. We should consider:
- Creating a reusable card