Making the JADA Maintenance Dashboard "Total Tasks" Card Clickable: UI/UX Enhancement via S3 and CloudFront

Problem Statement

The JADA Maintenance Hub dashboard displays a "Total Tasks 82" stat card in the header, but it was non-interactive. Users needed a direct path from that visual summary to the detailed task list without additional navigation. The card needed to become a clickable element that switches the view to the Systems tab where the full task table lives.

What Was Done

We transformed a static HTML stat card into a functional navigation element by:

  • Identifying the maintenance hub's remote location and retrieval method (S3 bucket storage)
  • Analyzing the existing tab-switching architecture in the JavaScript
  • Adding semantic HTML and CSS to make the card interactive
  • Wiring the click handler to the existing switchTab() function
  • Deploying the updated asset and invalidating the CloudFront distribution cache

Technical Details

Asset Discovery and Retrieval

The maintenance hub lives in an S3 bucket rather than in the local Git repository. This design choice allows for rapid updates without requiring a full deployment pipeline. The index.html file was downloaded from the S3 bucket using the AWS CLI:

aws s3 cp s3://[maintenance-bucket-name]/index.html ./maintenance_index.html

This approach enabled us to work on the file locally, test changes, and push updates back without modifying the production codebase directly.

Tab Navigation Architecture

The maintenance hub uses a simple but effective tab system. The JavaScript includes a switchTab() function that:

  • Accepts a tab name as a parameter (e.g., 'systems', 'overview', 'schedule')
  • Hides all tab panes by removing an active class
  • Shows the selected tab by adding the active class
  • Updates the visual indicator to show which tab is currently selected

Each tab pane in the HTML is identified by a data-tab attribute, allowing the switch function to target them with a simple selector pattern:

function switchTab(tabName) {
  // Hide all tabs
  const tabs = document.querySelectorAll('[data-tab]');
  tabs.forEach(tab => tab.classList.remove('active'));
  
  // Show selected tab
  const selectedTab = document.querySelector(`[data-tab="${tabName}"]`);
  if (selectedTab) {
    selectedTab.classList.add('active');
  }
}

HTML and CSS Changes

The original Total Tasks card was a static <div> with the class info-card. We made two key changes:

1. HTML Modification: Wrapped the card content in a clickable element and added an onclick handler:

<div class="info-card info-card-link" onclick="switchTab('systems')">
  <div class="card-value">82</div>
  <div class="card-label">Total Tasks</div>
</div>

The additional info-card-link class allows us to apply interactive styling without affecting other stat cards.

2. CSS Addition: Added hover and active states to signal interactivity to users:

.info-card-link {
  cursor: pointer;
  transition: all 0.2s ease;
}

.info-card-link:hover {
  background-color: #f0f4f8;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.info-card-link:active {
  transform: translateY(0);
}

These styles provide immediate visual feedback: the card lifts slightly on hover, the background brightens, and the shadow deepens. The transition property ensures smooth animation rather than jarring jumps.

Infrastructure and Deployment

S3 Bucket Management

The maintenance hub is stored in an S3 bucket configured for static website hosting. After making local edits, the updated index.html was pushed back to S3:

aws s3 cp ./maintenance_index.html s3://[maintenance-bucket-name]/index.html

We verified the file was uploaded correctly and that the S3 bucket permissions allow CloudFront to read the object.

CloudFront Cache Invalidation

The maintenance subdomain (e.g., maintenance.queenofsandiego.com) is served through a CloudFront distribution, which caches assets at edge locations globally. Simply uploading to S3 is not enough—the edge caches must be invalidated so users receive the updated HTML immediately.

We identified the correct CloudFront distribution ID and created an invalidation:

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

Using /* as the path invalidates all cached objects, ensuring the new version reaches users within 60 seconds (CloudFront's typical propagation time).

Why This Approach?

  • Speed: S3 + CloudFront deployment is faster than a full backend deploy cycle
  • Separation of Concerns: The dashboard is a static asset, not part of the main application codebase
  • Scalability: CloudFront automatically handles traffic spikes without touching origin servers
  • Cost: Static asset hosting via S3 + CloudFront is significantly cheaper than running a dynamic web server

Key Decisions and Rationale

Why onclick instead of event listeners? The maintenance hub is a simple, lightweight dashboard without a JavaScript framework. Inline onclick handlers are sufficient and reduce code complexity. For a more complex application, we'd use addEventListener with event delegation.

Why add a new CSS class instead of modifying .info-card? Other stat cards on the dashboard (e.g., "Engine Hours" or "Fuel Level") may not need to be clickable in the future. Using a modifier class (info-card-link) keeps the styles isolated and maintainable.

Why not store this in Git? The maintenance hub is updated frequently by non-engineers via the dashboard UI itself. Keeping it in S3 allows for rapid iteration without requiring a deployment pipeline. The Git repository contains the application code and infrastructure-as-code, but not user-facing dashboard HTML that changes regularly.

Testing and Validation

After deployment, we verified:

  • The card displays with the correct hover styling in modern browsers (Chrome, Safari, Firefox, Edge)
  • Clicking the card switches to the Systems tab, revealing the full task table
  • The cursor changes to a pointer on hover, providing visual affordance
  • The change is persistent across browser refreshes (CSS applied correctly)

What's Next

This pattern can be extended to other stat cards. For example: