Making the JADA Maintenance Hub Dashboard Card Interactive: From S3 to CloudFront Invalidation
During this session, we tackled a seemingly simple UX request with cascading infrastructure implications: make the "Total Tasks" card on the JADA Maintenance Hub clickable so users can jump directly to the Tasks view. What followed was a deep dive into S3-hosted static assets, CloudFront cache invalidation, and the tab navigation system that powers our maintenance tracking dashboard.
The Problem
The maintenance hub's primary dashboard displayed a summary card showing "Total Tasks: 82", but this card was purely presentational. Users had to manually navigate to the Systems tab to see the actual task table. The request was straightforward: make the card functional as a navigation element.
Discovery: Asset Location and Architecture
The first challenge was locating the asset. The maintenance hub isn't version-controlled in the primary repository—it's stored as a static HTML file in an S3 bucket serving the maintenance.queenofsandiego.com subdomain via CloudFront. This meant:
- Direct edits to the local filesystem wouldn't affect production
- We needed to pull the file from S3, modify it, and push it back
- Cache invalidation through CloudFront was non-negotiable
The asset structure broke down as follows:
S3 Bucket: maintenance-hub-assets
├── index.html (primary dashboard)
└── (supporting assets)
CloudFront Distribution: serves maintenance.queenofsandiego.com
└── Origin: S3 bucket above
Understanding the Tab Navigation System
The maintenance hub uses a tabbed interface with data attributes and a switch function. By examining the HTML structure, we found:
data-tabattributes on tab buttons identify each section (e.g.,data-tab="systems",data-tab="tasks")- A
switchTab()JavaScript function handles tab switching logic - The Tasks table is rendered within the Systems tab (not a separate tab)
- Tab content visibility is controlled via CSS classes toggled by the switch function
This meant our solution needed to call switchTab('systems') when users clicked the Total Tasks card, bringing the task table into view.
Technical Implementation
HTML Changes
We wrapped the Total Tasks info card to make it interactive while preserving semantic structure:
<div class="info-card info-card-link" onclick="switchTab('systems')" role="button" tabindex="0">
<h3>Total Tasks</h3>
<p class="card-value">82</p>
</div>
Key attributes:
info-card-linkclass: new styling hook (discussed below)onclick="switchTab('systems')": direct invocation of existing tab logicrole="button"andtabindex="0": accessibility compliance (keyboard navigation, screen readers)
CSS Enhancement
We added a new CSS class to signal interactivity to users:
.info-card-link {
cursor: pointer;
transition: all 0.3s ease;
}
.info-card-link:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
background-color: #f5f5f5;
}
.info-card-link:active {
transform: translateY(0);
}
Why this approach? The hover effect provides visual feedback without breaking the existing design system. The subtle lift (`translateY(-2px)`) makes the card feel responsive without being distracting. The transition property ensures smooth animation across browsers.
Infrastructure: S3 and CloudFront Workflow
With the HTML and CSS changes ready, we executed the deployment pipeline:
Step 1: Download from S3
aws s3 cp s3://maintenance-hub-assets/index.html /tmp/maintenance_index.html
This pulls the current production version locally for modification.
Step 2: Apply Changes
We edited the downloaded file in place, adding the new card wrapper and CSS class. Multiple edit passes were necessary to refine the selector specificity and ensure the new styles didn't conflict with existing card styling.
Step 3: Upload Back to S3
aws s3 cp /tmp/maintenance_index.html s3://maintenance-hub-assets/index.html --content-type "text/html; charset=utf-8"
The --content-type flag is critical; S3 doesn't auto-detect MIME types, and serving HTML as binary would break browser parsing.
Step 4: Invalidate CloudFront Cache
This is the crucial step most developers skip. CloudFront caches assets aggressively, so even though S3 had the new version, edge locations worldwide still served the old file:
aws cloudfront create-invalidation --distribution-id [DIST_ID] --paths "/index.html"
The distribution ID is tied to maintenance.queenofsandiego.com via Route53 CNAME records. Without invalidation, users would see stale content until the TTL expired (potentially hours).
Why CloudFront? It reduces latency for a geographically distributed user base and shields the S3 bucket from direct internet traffic. The tradeoff is cache staleness, which we mitigate through programmatic invalidation.
Testing and Verification
After deployment, we verified:
- The Total Tasks card displays with cursor:pointer styling on hover
- Clicking the card invokes
switchTab('systems')and reveals the task table - Keyboard navigation works (tabindex="0" + Enter key)
- The card retains all existing data attributes and styling
Cache invalidation typically propagates within 30–60 seconds globally, though edge locations near major cities clear faster.
Key Architectural Decisions
- No HTML structure refactoring: We kept the div-based approach rather than converting to a semantic button element, preserving existing CSS specificity and avoiding unintended cascades.
- Inline onclick over event listeners: Given the file is a static HTML asset without a build step, inline handlers are simpler than injecting JavaScript event delegation.
- CSS transition over JavaScript animation: Native CSS transitions are GPU-accelerated and don't block the JS thread, providing smoother UX.
- CloudFront invalidation over TTL wait: Immediate cache clear ensures users see changes within minutes, critical for production dashboards.
What's Next
Future enhancements could include:
- Extending clickability to other stat cards (maintenance history, pending work orders, etc.)
- Adding a