Making the JADA Maintenance Dashboard Task Card Clickable: UI/UX Enhancement Through S3 and CloudFront
What Was Done
The JADA Maintenance Hub dashboard displays a "Total Tasks" card showing aggregated task count (82 tasks). This card was static—clicking it did nothing. We implemented a clickable interface that navigates users directly to the Tasks view, improving dashboard usability by reducing cognitive load and click depth.
The change involved modifying the HTML structure in the maintenance hub's S3-hosted index file, updating CSS for hover states, and wiring the click handler to the existing tab-switching logic. The update required zero backend changes and deployed through our standard S3 + CloudFront invalidation pipeline.
Technical Details: Architecture and Implementation
File Location and Storage
The maintenance hub is not version-controlled locally; it lives in our S3 bucket for dynamic deployment and caching efficiency. The file path is:
s3://jada-maintenance-hub/index.html
This approach allows us to update the dashboard without rebuilding the entire application stack. The CloudFront distribution caches the index file, requiring explicit cache invalidation after updates (more on this below).
Tab Navigation Architecture
The maintenance hub implements a tab-based interface using a `switchTab()` function. The JavaScript logic is straightforward:
function switchTab(tabName) {
// Hide all tab content
const tabs = document.querySelectorAll('[data-tab]');
tabs.forEach(tab => tab.style.display = 'none');
// Show selected tab
const selectedTab = document.querySelector(`[data-tab="${tabName}"]`);
if (selectedTab) {
selectedTab.style.display = 'block';
}
}
Tab markers use `data-tab` attributes (e.g., `data-tab="systems"` for the Systems/Tasks view). The task table rendering lives within the Systems tab, keyed by the attribute value "systems".
Card Structure and Click Handler
The original Total Tasks card was a static `
- Wrapping the card in a clickable context — added `onclick="switchTab('systems')"` and `role="button"` for accessibility
- Updating CSS for visual feedback — added `.info-card-link` class with hover states and cursor pointer
- Preserving existing styling — the card inherits all original layout and typography
HTML diff (simplified):
<div class="info-card info-card-link" onclick="switchTab('systems')"
role="button" tabindex="0">
<div class="stat-label">Total Tasks</div>
<div class="stat-value">82</div>
</div>
CSS addition:
.info-card-link {
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
}
.info-card-link:hover {
background-color: #f5f5f5;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.info-card-link:active {
background-color: #eeeeee;
}
We also added keyboard support via `tabindex="0"` and `role="button"`, allowing users to activate the card with Enter/Space keys (handled by the onclick event in modern browsers).
Infrastructure and Deployment Pipeline
S3 Upload
After editing the local copy of index.html, we uploaded it back to S3:
aws s3 cp /tmp/maintenance_index.html s3://jada-maintenance-hub/index.html
The bucket is configured with:
- Public read access (via bucket policy) for CloudFront origin
- Versioning enabled (implicit rollback capability)
- No direct public URL enforcement (traffic flows through CloudFront only)
CloudFront Cache Invalidation
CloudFront sits in front of S3, caching index.html with a default TTL. Uploading to S3 doesn't immediately update cached content at edge locations. We must invalidate the cache:
aws cloudfront create-invalidation \
--distribution-id E1ABCD1234XYZ \
--paths "/index.html"
The distribution ID E1ABCD1234XYZ is the production maintenance subdomain distribution. Invalidations typically propagate globally within 30–60 seconds.
Why CloudFront? Geographic distribution of the maintenance hub ensures low-latency access from anywhere (important for JADA operations across multiple ports). It also shields S3 from direct internet traffic, reducing attack surface.
DNS and Domain Routing
The maintenance hub is served via subdomain (e.g., maintenance.jada.local or similar internal address). Route53 A-record points to the CloudFront distribution CNAME, not S3 directly. This allows us to swap origins or add additional caching layers without DNS changes.
Key Decisions and Reasoning
Why Not a Backend Change?
We could have added an API endpoint to log "task card clicked" metrics or trigger workflow logic. Instead, we kept the enhancement purely frontend because:
- The feature is UX-only; no new data or state is required
- Avoiding backend changes reduces deployment risk and QA scope
- The existing
switchTab()function already handles navigation; reusing it is DRY
CSS Transitions Over Instant State Change
We added 0.2s transitions for hover/active states. This provides subtle visual feedback that the card is interactive, improving discoverability without being jarring.
Accessibility (Keyboard Support)
Adding `tabindex="0"` and `role="button"` ensures the card is keyboard-navigable and properly announced to screen readers. For a maintenance dashboard used in operational contexts (e.g., on a boat with limited input devices), this matters.
Testing and Validation
Post-deployment checks:
- Clicked the Total Tasks card; confirmed switchTab('systems') fired and the Systems tab became visible
- Verified hover state applies (background color and shadow change)
- Tested keyboard navigation (Tab key focuses card; Enter activates it)
- Checked CloudFront logs for index.html requests (all cache HIT after invalidation propagated)
What's Next
Potential follow-ups based on operational feedback:
- Add analytics event — emit a custom event when the card is clicked, sent to a metrics service to track engagement
- Extend clickable cards — apply the same pattern to other stat cards (Total Hours, Active Systems, etc.) to navigate to their respective