Making the JADA Maintenance Dashboard Interactive: Clickable Stat Cards and Tab Navigation

The JADA maintenance hub dashboard is a critical operations tool for the team—it provides real-time visibility into active maintenance tasks, system status, and operational priorities. During this session, we tackled a usability gap: the "Total Tasks" stat card on the dashboard's main view was display-only, forcing users to manually navigate to the Tasks tab to see their work queue. This post covers how we made that card clickable and connected it to the underlying tab navigation system.

The Problem: Disconnected UI Elements

The maintenance dashboard displays several key metrics in card format on the Overview tab—Total Tasks, Active Systems, Overdue Items, etc. These cards were purely informational. When a user saw "Total Tasks: 82," they had to recognize this as a call-to-action, then manually click the Tasks tab to see the actual queue. For a distributed team managing a vessel's maintenance schedule, this friction point adds up.

The solution was straightforward at a high level: make the Total Tasks card clickable and programmatically switch to the Tasks tab (which in the codebase is labeled as the "Systems" tab where the task table lives). This required understanding the existing architecture and making surgical edits to both HTML markup and CSS.

Understanding the Existing Architecture

The maintenance hub is hosted as a static site in AWS S3 with CloudFront distribution for edge caching. The file structure is:

  • s3://jada-maintenance-hub/index.html — Single-page application serving all views
  • Tab navigation controlled via JavaScript switchTab() function
  • Tab identifiers tied to data-tab attributes in the HTML
  • CloudFront distribution invalidation required for cache clearing after updates

The dashboard uses a classic tab pattern: hidden div containers for each tab's content, with CSS class toggling to show/hide based on the active tab. The task list renders in the Systems tab, accessible via switchTab('systems').

Technical Implementation: Three-Step Process

Step 1: Download and Inspect Current Code

We pulled the live index.html directly from S3 to ensure we were editing the deployed version:

aws s3 cp s3://jada-maintenance-hub/index.html ./index.html --region us-west-2

Inspection revealed the Total Tasks card structure:

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

And the tab switch logic in the JavaScript section:

function switchTab(tabName) {
  document.querySelectorAll('[data-tab]').forEach(el => {
    el.classList.remove('active');
  });
  document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
}

Step 2: Markup and Event Handler Modifications

We wrapped the Total Tasks card in a clickable element and added an onclick handler:

<div class="info-card info-card-link" onclick="switchTab('systems')" role="button" tabindex="0">
  <h3>Total Tasks</h3>
  <p class="card-value">82</p>
</div>

Why this approach:

  • onclick attribute is immediate and requires no event listener boilerplate in a static HTML context
  • role="button" signals screen readers that this element is interactive
  • tabindex="0" makes the card keyboard-accessible for users navigating via arrow keys
  • New CSS class info-card-link allows visual distinction from non-interactive cards

Step 3: CSS for Visual Affordance

We added styling to signal interactivity:

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

.info-card-link:hover {
  background-color: #f0f0f0;
  transform: scale(1.02);
}

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

Design rationale:

  • cursor: pointer is the universal affordance for clickability
  • Subtle hover state (background shift + scale) provides tactile feedback without jarring the eye
  • Active state (scale-down) gives press feedback, critical for low-latency UX
  • All transitions use ease timing for smooth motion on lower-end hardware (relevant for maritime environments with older laptops)

Infrastructure and Deployment

Once the edits were complete, deployment required two steps:

Upload to S3

aws s3 cp ./index.html s3://jada-maintenance-hub/index.html --region us-west-2 --cache-control "public, max-age=3600"

Invalidate CloudFront Cache

Critical: CloudFront caches the index.html file aggressively. Without cache invalidation, users would see the old version for up to 24 hours (depending on TTL settings):

aws cloudfront create-invalidation --distribution-id [DISTRIBUTION_ID] --paths "/*"

The distribution ID is tied to the maintenance.sailjada.com subdomain via Route53 CNAME records. Invalidating /* clears all paths, ensuring the updated index.html reaches all edge locations within ~60 seconds.

Key Decisions and Trade-offs

Why not use a full JavaScript click listener: In a single static HTML file, adding a listener increases code complexity and requires managing event delegation. The onclick attribute is simpler, more maintainable, and sufficient for this use case.

Why keyboard accessibility matters: Maritime environments often involve gloved hands, motion, and split attention. Keyboard navigation (Tab + Enter) ensures the feature works when pointing is impractical. Screen reader support (role="button") serves crew members with visual impairments.

Why S3 + CloudFront instead of a traditional web server: Static hosting is cost-effective, infinitely scalable, and provides automatic geographic distribution. For a maintenance dashboard that doesn't require server-side computation, it's the right choice. The CloudFront invalidation step is a small operational cost for massive reliability and speed gains.

Testing and Validation

Post-deployment verification steps:

  • Clear browser cache and reload maintenance.sailjada.com
  • Click the Total Tasks card—should instantly switch to the Systems tab with task table visible
  • Tab to the card using keyboard, press Enter—same result
  • Verify hover/active states render correctly across browsers
  • Check mobile responsiveness (card should remain