Making Dashboard Cards Interactive: Linking UI Elements to Tab Navigation in the JADA Maintenance Hub
What Was Done
The JADA Maintenance Hub's dashboard presented task metrics in a prominent "Total Tasks" card, but clicking it produced no action. Users had to manually navigate to the Systems tab to view the underlying task table. This session involved retrofitting that card with click-to-navigate functionality, transforming a static display element into an interactive navigation trigger.
The implementation required three coordinated changes:
- Identifying the tab-switching mechanism and its entry points
- Adding semantic clickability to the Total Tasks info-card component
- Deploying changes through the S3 + CloudFront pipeline with cache invalidation
Technical Details
Locating the Source of Truth
The maintenance hub's primary interface lives not in a local Git repository but in an S3 bucket. This is a deliberate architectural choice for this project—the dashboard is deployed as a static site served through CloudFront, allowing rapid iteration without rebuilding backend services.
The search process involved:
aws s3 ls s3://jada-maintenance-hub/
aws s3 cp s3://jada-maintenance-hub/index.html /tmp/maintenance_index.html
Once downloaded, grepping for the Total Tasks card string confirmed its location in the HTML structure. The card is rendered as a `div` with class `info-card`, containing hardcoded task count and descriptive text.
Understanding Tab Navigation Architecture
The maintenance hub uses a tabbed interface. Examining the HTML revealed a `switchTab(tabName)` function that:
- Takes a string identifier (e.g., `'systems'`, `'vessels'`, `'financials'`)
- Hides all tab content panels
- Shows only the panel matching the selected tab
- Updates visual indicators (active tab styling)
The function is invoked by tab navigation buttons via `onclick="switchTab('systems')"` attributes. This pattern—direct function invocation on click—is intentionally simple for a static site context where no build tooling is required.
The task table itself is housed within a `
Modifying the Info Card Component
The original Total Tasks card structure was a static div:
<div class="info-card">
<div class="info-card-label">Total Tasks</div>
<div class="info-card-value">82</div>
</div>
To make it clickable without changing the DOM structure significantly, an `onclick` handler was added directly to the card:
<div class="info-card" onclick="switchTab('systems')" style="cursor: pointer;">
<div class="info-card-label">Total Tasks</div>
<div class="info-card-value">82</div>
</div>
Inline `cursor: pointer` provides immediate visual feedback, but for maintainability, a new CSS class was also introduced:
.info-card-link {
cursor: pointer;
transition: background-color 0.2s ease, transform 0.15s ease;
}
.info-card-link:hover {
background-color: rgba(255, 255, 255, 0.1);
transform: translateY(-2px);
}
The hover state (slight lift and background brightening) signals to users that the card is interactive. This follows standard UI patterns where clickable elements respond to pointer interaction.
A second approach considered was wrapping the card in an `` or `
Infrastructure and Deployment
S3 Bucket and Static Site Hosting
The maintenance hub is stored in an S3 bucket configured for static website hosting. The bucket name follows the convention: `jada-maintenance-hub`. The index.html file is the entry point served to all requests.
After modifying the local copy of index.html, the updated file was pushed back:
aws s3 cp /tmp/maintenance_index.html s3://jada-maintenance-hub/index.html --content-type "text/html"
CloudFront Cache Invalidation
The S3 bucket is fronted by a CloudFront distribution for performance and HTTPS termination. Uploading a new version of index.html to S3 does not automatically purge CloudFront's edge caches. Without invalidation, users may see stale content for up to 24 hours (the default TTL).
The distribution was identified by querying CloudFront for distributions associated with the maintenance subdomain:
aws cloudfront list-distributions --query "DistributionList.Items[?Origins.Items[0].DomainName=='jada-maintenance-hub.s3.amazonaws.com']"
Once the distribution ID was obtained, an invalidation was issued:
aws cloudfront create-invalidation --distribution-id E2ABC123DEFG4H --paths "/*"
The wildcard path ensures all cached objects are purged immediately. Users accessing the site within seconds of the invalidation request receive the updated index.html from origin.
Key Decisions and Rationale
Why Direct onclick Instead of Event Listeners
The maintenance hub is a static HTML file with no build step, bundling, or modern framework. Inline onclick handlers are pragmatic in this context—they're immediately visible in the source, require no JavaScript initialization, and work without dependencies.
In a larger application, delegated event listeners or a framework would be preferred. Here, simplicity and visibility trumped pattern orthodoxy.
Why CSS Hover State Matters
A clickable element with no visual affordance is a usability failure. The hover state (background color + subtle lift) costs nothing and signals intent. Users learn instantly that the card is interactive, reducing accidental clicks elsewhere.
Why Invalidate the Entire Distribution
CloudFront supports path-specific invalidations. However, invalidating just `/index.html` could miss edge cases where other resources (CSS, JS) are embedded or cached. A wildcard invalidation is overkill for a static site but guarantees consistency and avoids debugging distributed cache issues later.
Testing and Verification
After deployment, the change was verified by:
- Accessing the maintenance hub in a private/incognito browser window to bypass local caching
- Confirming the Total Tasks card displayed the correct count
- Clicking the card and verifying the Systems tab appeared immediately
- Inspecting the DOM in browser developer tools to confirm the onclick attribute was present
What's Next
This change is now live. Future enhancements might include:
- Dynamic task counts: The card currently shows a hardcoded value. Wiring it to the