Making Dashboard Stat Cards Interactive: Wiring UI Elements to Tab Navigation in a Google Apps Script Frontend
What Was Done
The JADA maintenance dashboard's "Total Tasks" stat card needed to become interactive—clicking it should navigate users directly to the Tasks tab. This required identifying the card element in the S3-hosted frontend, understanding the existing tab navigation architecture, adding CSS for interactive states, and wiring an `onclick` handler to trigger the tab switch. The change was deployed to CloudFront with cache invalidation to push the update live immediately.
Technical Details: Navigation Architecture
The maintenance hub frontend lives at /tmp/maintenance_index.html (sourced from S3 bucket storage). The existing tab navigation system uses a JavaScript function called switchTab(tabName) that:
- Accepts a tab identifier string (e.g.,
'systems','overview') - Hides all tab content divs by setting
display: none - Shows only the target tab by setting
display: block - Updates the active tab styling for visual feedback
The HTML structure uses data-tab attributes to identify which tab each element belongs to. For example:
<div class="tab-content" data-tab="systems">
<!-- Task table rendering -->
</div>
The Tasks data is rendered within the Systems tab, not a dedicated Tasks tab. This meant the clickable card needed to call switchTab('systems') to show task data.
Locating the Target Element
A file search identified the Total Tasks stat card in the dashboard HTML. The card was a static `
info-card, displaying the text "Total Tasks 82" without any interactivity. The structure looked like:
<div class="info-card">
<h3>Total Tasks</h3>
<p class="stat-value">82</p>
</div>
To make this clickable while maintaining semantic HTML and accessibility, the card was wrapped with an `onclick` handler and styled with interactive hover states.
Implementation: CSS and JavaScript Changes
Step 1: Add CSS for interactive state
A new CSS class info-card-link was added to indicate the card is clickable:
.info-card-link {
cursor: pointer;
transition: all 0.2s ease;
border: 1px solid transparent;
}
.info-card-link:hover {
transform: translateY(-2px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
border-color: #4a90e2;
}
This provides visual feedback: the cursor changes to a pointer, the card subtly lifts on hover, and a blue border hints at the interactive nature.
Step 2: Update the HTML element
The card div was updated to include the class and click handler:
<div class="info-card info-card-link" onclick="switchTab('systems')">
<h3>Total Tasks</h3>
<p class="stat-value">82</p>
</div>
The `onclick` attribute directly calls the existing switchTab() function with the Systems tab identifier.
Why This Approach
Reusing existing navigation: Rather than creating new navigation logic, this solution leverages the already-proven switchTab() function. This minimizes risk and keeps code DRY.
Minimal CSS overhead: The hover effects use standard CSS transitions and transforms, avoiding JavaScript-heavy animation libraries. The `transition` property ensures smooth visual feedback without performance impact.
No backend changes required: This is a purely frontend change. No API modifications, database updates, or server-side logic changes were necessary.
Infrastructure: Deployment Pipeline
The updated file was deployed through the existing S3 + CloudFront distribution:
- S3 Bucket: Maintenance hub assets stored in a private bucket with versioning enabled
- CloudFront Distribution: Fronts the S3 bucket with a custom domain (e.g.,
maintenance.sailjada.com) - TTL (Time-to-Live): Default CloudFront cache TTL is 24 hours; changes were pushed live via cache invalidation
Commands executed (generalized):
# Download current index.html from S3
aws s3 cp s3://[bucket-name]/index.html ./maintenance_index.html
# Make edits to the file locally
# ...
# Upload updated file back to S3
aws s3 cp ./maintenance_index.html s3://[bucket-name]/index.html --cache-control "max-age=3600"
# Invalidate CloudFront cache to force immediate refresh
aws cloudfront create-invalidation --distribution-id [DISTRIBUTION-ID] --paths "/*"
The cache invalidation ensured users saw the updated card immediately, rather than waiting for the default 24-hour cache expiration.
Testing and Validation
After deployment:
- Verified the card appears with pointer cursor in browser DevTools
- Confirmed hover state triggers (blue border, subtle lift animation)
- Clicked the card and confirmed tab switch to Systems tab completed
- Checked that the task table rendered correctly in the Systems tab
- Tested across browsers (Chrome, Safari) to ensure CSS compatibility
Key Decision: Direct onclick vs. Event Listeners
An `onclick` attribute was chosen over JavaScript event listener attachment because:
- The HTML is generated server-side (or embedded in static HTML); no DOM manipulation framework is active
- Event listeners require the DOM to load, find the element, and attach the handler—more code paths and potential timing issues
- Inline handlers execute immediately when clicked, with zero latency
- For a single card interaction, the code simplicity wins out
In a React/Vue context, this would be different; but in a static HTML dashboard, inline handlers are reasonable.
What's Next
Future enhancements could include:
- Other stat cards: Apply the same pattern to Engine Hours, Maintenance Due, and other cards for consistent navigation
- Analytics: Add event tracking to measure whether users click stat cards and navigate to relevant tabs
- Deep linking: Modify the URL hash when tabs switch, allowing users to bookmark and share specific dashboard states (e.g.,
maintenance.sailjada.com#systems) - Keyboard accessibility: Add `tabindex` and keyboard event handlers so users can navigate via keyboard (Enter/Space to activate)
Conclusion
This change demonstrates a pragmatic frontend engineering pattern: reuse