```html

Making Dashboard Cards Interactive: Linking UI State to Tab Navigation in a Maintenance Hub

During a recent development session, we identified a UX gap in the JADA Maintenance Hub dashboard: the "Total Tasks" metric card displayed summary data but wasn't interactive. Users viewing the dashboard needed a direct path to the underlying task list without manually navigating tabs. This post covers the technical approach to linking dashboard cards to tab-based views, the infrastructure involved, and the deployment process.

The Problem

The JADA Maintenance Hub is a single-page application (SPA) served from S3 and distributed via CloudFront. It displays maintenance metrics in a dashboard view with multiple tabs (Overview, Systems, Crew, etc.). The "Total Tasks" card showed an aggregated count—82 tasks in this case—but clicking the card did nothing. Users had to manually click the "Systems" tab to see the task table. For a maintenance-critical application, this friction matters.

Architecture Overview

The maintenance hub stack looks like this:

  • S3 Bucket: maintenance.sailjada.com — hosts the single index.html file and any static assets
  • CloudFront Distribution: Points to the S3 bucket, handles caching and invalidation for rapid deployments
  • Route53: DNS record for maintenance.sailjada.com points to the CloudFront distribution
  • Frontend: Vanilla JavaScript (no framework), with tab navigation controlled by switchTab() function

The index.html file contains all HTML, CSS, and JavaScript inline—a monolithic but practical approach for a focused utility dashboard.

Technical Implementation

Understanding Tab Navigation

The existing tab system uses a simple pattern:

function switchTab(tabName) {
  // Hide all tabs
  document.querySelectorAll('[data-tab]').forEach(el => {
    el.style.display = 'none';
  });
  // Show selected tab
  document.querySelector(`[data-tab="${tabName}"]`).style.display = 'block';
}

Each tab is a <div> with a data-tab attribute matching its name: data-tab="systems", data-tab="overview", etc. The task table lives inside data-tab="systems".

Locating the Total Tasks Card

The dashboard uses an info-card CSS class for metric displays. We found the Total Tasks card by searching the file for "Total Tasks" text:

<div class="info-card">
  <div class="info-card-label">Total Tasks</div>
  <div class="info-card-value">82</div>
</div>

This card needed to become interactive without breaking the dashboard's visual design or existing functionality.

Making the Card Clickable

Rather than wrapping the card in an anchor tag (which could trigger unwanted browser behavior), we made two changes:

1. Add an onclick handler:

<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>

2. Create a CSS class for visual feedback:

.info-card-link {
  cursor: pointer;
  transition: background-color 0.2s, transform 0.1s;
}

.info-card-link:hover {
  background-color: rgba(100, 150, 255, 0.05);
  transform: scale(1.02);
}

.info-card-link:active {
  transform: scale(0.98);
}

We added the class to the card's existing info-card class list, providing visual affordance that the card is clickable without redesigning the UI.

Why This Approach?

  • No JavaScript framework overhead: The dashboard runs vanilla JS. Adding a simple onclick handler keeps dependencies minimal.
  • Immediate feedback: The hover and active states provide tactile confirmation that interaction is possible—critical for a tool used in noisy, visually challenging marine environments.
  • Graceful degradation: If JavaScript is disabled, the card displays normally without errors. The onclick just won't fire.
  • Minimal diff: Changes to index.html are surgical—three lines added, no restructuring of the tab system.

Deployment Process

1. Local Editing

Downloaded the current index.html from S3:

aws s3 cp s3://maintenance.sailjada.com/index.html /tmp/maintenance_index.html

Made edits locally, testing the onclick behavior and CSS hover states in a browser.

2. Upload to S3

aws s3 cp /tmp/maintenance_index.html s3://maintenance.sailjada.com/index.html \
  --content-type text/html \
  --metadata "deployment-date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')"

The --content-type flag ensures CloudFront serves the file with correct MIME type. The metadata tag tracks deployment timestamp for audit purposes.

3. Invalidate CloudFront Cache

S3 updates don't automatically propagate through CloudFront. We invalidated the cache:

aws cloudfront create-invalidation \
  --distribution-id E1A2B3C4D5E6F7 \
  --paths "/*"

E1A2B3C4D5E6F7 is the CloudFront distribution ID for maintenance.sailjada.com. The /* path invalidates all files, ensuring the updated index.html reaches all edge locations within seconds.

4. Verification

Tested the change by:

  • Visiting https://maintenance.sailjada.com in an incognito window (bypassing local cache)
  • Clicking the Total Tasks card and confirming the Systems tab appears
  • Hovering over the card to verify the hover state (subtle background color shift and scale animation)

Key Decisions

Inline onclick vs. event listener: We chose inline onclick over JavaScript event listeners because the change is one-off and the HTML is monolithic. In a larger application with separate HTML and JS files, an event listener would be preferred for separation of concerns.

CSS transition animation: The 0.2s transition on background-color and 0.1s on transform create a responsive feel without the jank of zero