feat(campaigns): campaign timing panel with Red/Blue aggregated metrics

Backend: GET /campaigns/{id}/timing-summary
  Aggregates timing across all campaign tests:
  - red_execution_secs: red_started_at → blue_started_at (minus paused)
  - blue_queue_secs:    blue_started_at → blue_work_started_at
  - blue_evaluation_secs: blue_work_started_at → validated (minus paused)
  - total_secs: sum of all three phases
  Returns totals + per-test breakdown sorted by total time desc.

Frontend: new CampaignTimingPanel component replaces WorklogTimeline
  - 4 summary cards: Red Execution / Blue Queue / Blue Evaluation / Total
  - Stacked horizontal bar showing time distribution
  - Per-test breakdown with individual mini-bars and phase durations
  - Shows 'No tests started yet' when no timing data available
This commit is contained in:
kitos
2026-06-02 11:06:42 +02:00
parent 5c5398683a
commit b438dd0af0
4 changed files with 318 additions and 3 deletions
+31
View File
@@ -194,6 +194,37 @@ export async function deleteCampaign(
});
}
// ── Timing summary ─────────────────────────────────────────────────
export interface CampaignTimingBreakdown {
test_id: string;
test_name: string;
state: string | null;
red_execution_secs: number;
blue_queue_secs: number;
blue_evaluation_secs: number;
total_secs: number;
has_timing: boolean;
}
export interface CampaignTimingSummary {
campaign_id: string;
campaign_name: string;
tests_total: number;
tests_with_timing: number;
red_execution_secs: number;
blue_queue_secs: number;
blue_evaluation_secs: number;
total_secs: number;
breakdown: CampaignTimingBreakdown[];
}
/** Get aggregated Red/Blue timing metrics for a campaign. */
export async function getCampaignTiming(campaignId: string): Promise<CampaignTimingSummary> {
const { data } = await client.get<CampaignTimingSummary>(`/campaigns/${campaignId}/timing-summary`);
return data;
}
/** Get execution history (child campaigns) for a recurring campaign. */
export async function getCampaignHistory(campaignId: string): Promise<{
campaign_id: string;