Making Dashboard Cards Interactive: Linking UI Elements to Tab Navigation in a Maintenance Hub
During a recent maintenance hub update, we needed to improve discoverability of the Tasks view by making the "Total Tasks" summary card clickable. This seemingly simple UX enhancement required coordination across multiple layers: DOM manipulation, event handling, CSS state management, S3 deployment, and CloudFront cache invalidation. Here's how we approached it.
The Problem
The JADA maintenance hub displays a dashboard with several info cards showing high-level metrics: Total Tasks, Active Systems, Pending Work, etc. Users viewing the cockpit dashboard saw "Total Tasks: 82" but had no obvious way to drill into that data. The detailed task table existed in the Systems tab, but required users to manually navigate there. We needed to make that card a direct gateway to the underlying data.
Architecture Overview
The maintenance hub is a single-page application (SPA) served from an S3 bucket fronted by CloudFront:
- S3 Bucket:
maintenance.jada.s3.amazonaws.com - CloudFront Distribution:
maintenance.sailjada.com(subdomain routed via Route53) - Primary File:
/index.html(contains all markup, styles, and navigation logic) - Tab System: Client-side switching via JavaScript `switchTab()` function with
data-tabattributes
The application uses a tabbed interface where each tab is hidden/shown via CSS classes and data attributes. The Tasks data lives in a table rendered when the "systems" tab is active.
Technical Implementation
Step 1: Identify the Target Element
We first downloaded the current index.html from S3 to understand the DOM structure:
aws s3 cp s3://maintenance.jada.s3.amazonaws.com/index.html . --profile jada
Located the Total Tasks card markup:
<div class="info-card">
<div class="card-label">Total Tasks</div>
<div class="card-value">82</div>
</div>
And identified the existing tab navigation function within the <script> section:
function switchTab(tabName) {
// Hide all tabs
document.querySelectorAll('[data-tab]').forEach(el => {
el.classList.remove('active');
});
// Show selected tab
document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
}
The target tab was identified as data-tab="systems", which contains the task table.
Step 2: Make the Card Clickable
We wrapped the card in a clickable container and added an onclick handler:
<div class="info-card info-card-link" onclick="switchTab('systems')">
<div class="card-label">Total Tasks</div>
<div class="card-value">82</div>
</div>
Why this approach? We chose inline event binding over event delegation because:
- The card is static (not dynamically generated)
- Keeps the handler logic co-located with the markup for readability
- Avoids adding a wrapper
<button>element that would require additional CSS reset - The existing
switchTab()` function is already global and tested
Step 3: Add Interactive Styling
Added CSS class info-card-link to signal interactivity and provide visual feedback:
.info-card-link {
cursor: pointer;
transition: all 0.2s ease;
}
.info-card-link:hover {
background-color: rgba(52, 152, 219, 0.1);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.info-card-link:active {
transform: translateY(0);
}
Design rationale:
cursor: pointersignals clickability to users- Subtle background color change on hover (10% opacity blue) doesn't distract but confirms interactivity
- Slight upward translation (2px) on hover creates tactile feedback
- Enhanced shadow adds depth for the hover state
- Active state removes translation to provide immediate click feedback
Deployment Process
S3 Upload
After editing, we uploaded the modified index.html back to S3:
aws s3 cp index.html s3://maintenance.jada.s3.amazonaws.com/index.html --profile jada --cache-control "max-age=3600"
We set a 1-hour cache-control header because this file changes infrequently and we want to avoid serving stale content for too long if we need to push updates.
CloudFront Cache Invalidation
S3 caching alone isn't sufficient; CloudFront sits between users and S3. We need to invalidate the cache so users get the fresh version immediately:
aws cloudfront create-invalidation \
--distribution-id E1ABC2DEF3GHIJ \
--paths "/index.html" \
--profile jada
Where Why CloudFront invalidation matters: Without this step, users in various geographic regions would continue seeing the old cached version for up to 24 hours (the default TTL). Invalidation ensures deployment consistency across all users within seconds. After deployment, we verified:E1ABC2DEF3GHIJ is the CloudFront distribution ID for maintenance.sailjada.com
Testing & Verification
maintenance.sailjada.com and confirmed the Total Tasks card rendered with updated stylingKey Decisions & Tradeoffs