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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user