databuild/plans/webapp_v1/chunk-7-jobs-pages.md

6.3 KiB

Chunk 7: Jobs Pages

Parent: Build Graph Dashboard
Previous: Chunk 6: Partition Pages
Next: Chunk 8: Graph Analysis

Overview

Implement job listing and individual job metrics pages with performance data, success rates, and execution history.

Scope

In Scope

  • Jobs listing page with high-level metadata
  • Individual job metrics and performance pages
  • Success rate tracking and trends
  • Recent job runs with execution details
  • Average duration and timing analysis
  • Job label URL encoding for safe navigation

Out of Scope

  • Complex performance analytics
  • Historical trend analysis beyond recent runs
  • Job configuration editing
  • Advanced filtering beyond basic search

Technical Approach

Data Sources

From Build Graph Service API and build event log:

  • Job list from graph analysis
  • Job execution history from build events
  • Performance metrics aggregated from event data
  • Success/failure rates from job events

URL Encoding

// Handle job labels in URLs (similar to partition refs)
function encodeJobLabel(label: string): string {
  return btoa(label).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}

function decodeJobLabel(encoded: string): string {
  const padded = encoded.replace(/-/g, '+').replace(/_/g, '/');
  return atob(padded);
}

Component Structure

const JobsList = {
  oninit: () => {
    this.jobs = [];
    this.searchTerm = '';
    this.loadJobs();
  },
  
  view: () => [
    m('.jobs-header', [
      m('h1', 'Jobs'),
      m('input', {
        placeholder: 'Search jobs...',
        oninput: (e) => this.searchTerm = e.target.value,
      })
    ]),
    
    m('.jobs-table', [
      m('table.table', [
        m('thead', [
          m('tr', [
            m('th', 'Job Label'),
            m('th', 'Success Rate'),
            m('th', 'Avg Duration'),
            m('th', 'Recent Runs'),
            m('th', 'Last Run'),
          ])
        ]),
        m('tbody', this.filteredJobs().map(job =>
          m('tr', [
            m('td', m('a', { 
              href: `/jobs/${encodeJobLabel(job.label)}` 
            }, job.label)),
            m('td', [
              m('.badge', job.success_rate >= 0.9 ? 'success' : 'warning'),
              ` ${Math.round(job.success_rate * 100)}%`
            ]),
            m('td', formatDuration(job.avg_duration)),
            m('td', job.recent_runs),
            m('td', formatTime(job.last_run)),
          ])
        ))
      ])
    ])
  ]
};

const JobMetrics = {
  oninit: (vnode) => {
    this.jobLabel = decodeJobLabel(vnode.attrs.label);
    this.metrics = null;
    this.recentRuns = [];
    this.loadJobMetrics();
  },
  
  view: () => [
    m('.job-header', [
      m('h1', this.jobLabel),
      m('.job-stats', [
        m('.stat', [
          m('.stat-title', 'Success Rate'),
          m('.stat-value', `${Math.round(this.metrics?.success_rate * 100)}%`),
        ]),
        m('.stat', [
          m('.stat-title', 'Avg Duration'),
          m('.stat-value', formatDuration(this.metrics?.avg_duration)),
        ]),
        m('.stat', [
          m('.stat-title', 'Total Runs'),
          m('.stat-value', this.metrics?.total_runs),
        ]),
      ])
    ]),
    
    m('.job-content', [
      m('.performance-chart', [
        m('h2', 'Performance Trends'),
        m('.chart-placeholder', 'Success rate and duration trends over time'),
        // Simple trend visualization or table
        m('table.table', [
          m('thead', [
            m('tr', [
              m('th', 'Date'),
              m('th', 'Success Rate'),
              m('th', 'Avg Duration'),
            ])
          ]),
          m('tbody', this.metrics?.daily_stats?.map(stat =>
            m('tr', [
              m('td', formatDate(stat.date)),
              m('td', `${Math.round(stat.success_rate * 100)}%`),
              m('td', formatDuration(stat.avg_duration)),
            ])
          ))
        ])
      ]),
      
      m('.recent-runs', [
        m('h2', 'Recent Runs'),
        m('table.table', [
          m('thead', [
            m('tr', [
              m('th', 'Build Request'),
              m('th', 'Partitions'),
              m('th', 'Status'),
              m('th', 'Duration'),
              m('th', 'Started'),
            ])
          ]),
          m('tbody', this.recentRuns.map(run =>
            m('tr', [
              m('td', m('a', { href: `/builds/${run.build_id}` }, run.build_id)),
              m('td', run.partitions.join(', ')),
              m('td', m('.badge', run.status)),
              m('td', formatDuration(run.duration)),
              m('td', formatTime(run.started_at)),
            ])
          ))
        ])
      ])
    ])
  ]
};

Metrics Calculation

From build event log:

  • Success rate: completed jobs / total jobs
  • Average duration: mean time from SCHEDULED to COMPLETED/FAILED
  • Recent runs: last 50 job executions
  • Daily aggregations for trend analysis

Implementation Strategy

  1. Create Data Layer

    • API integration for job data
    • Metrics calculation from build events
    • Search and filtering logic
  2. Build Jobs List Page

    • Job table with search
    • High-level metrics display
    • Performance indicators
  3. Individual Job Pages

    • Detailed metrics display
    • Performance trends
    • Recent runs history
  4. Performance Visualization

    • Simple trend charts or tables
    • Success rate indicators
    • Duration analysis

Deliverables

  • Jobs listing page with search and metrics
  • Individual job metrics pages
  • Success rate tracking and display
  • Performance trends visualization
  • Recent job runs history
  • Job label URL encoding/decoding

Success Criteria

  • Jobs list loads with accurate metrics
  • Individual job pages show detailed performance data
  • Success rates are calculated correctly
  • Performance trends are meaningful
  • Recent runs link to build details
  • URL encoding handles all job label formats

Testing

  • Test jobs list search functionality
  • Verify individual job pages load correctly
  • Validate metrics calculations against known data
  • Test performance trend accuracy
  • Check recent runs links and data
  • Validate URL encoding with complex job labels
  • Test with jobs that have no runs yet