feat(cra): Management-Fortschritts-Ansicht (Ticket-Status-Readback)
Liest den Lebenszyklus jedes Befunds (status + tracker_issue_url) aus dem Scanner zurück und rollt ihn zu einem Management-Bild auf: % erledigt, 4-Phasen (offen/in Arbeit/erledigt/ausgeschlossen), offenes Restrisiko nach Schweregrad, Fortschritt je CRA-Anforderung und eine Aufgaben-/Ticket-Tabelle mit Jira-Link. Neuer Endpoint GET/POST /api/v1/cra/progress (dünn → Service cra_progress, rein deterministisch, kein /assess-Schema-Drift). Frontend: ProgressView in Ebene 1 (CRACyberView), live je Scanner-Repo, sonst Demo-Status. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { CRAFinding } from './useCRADemo'
|
||||
|
||||
// Management progress: reads each finding's lifecycle (status + Jira ticket) back
|
||||
// from the scanner and rolls it up. Live for a linked scanner repo; for the demo
|
||||
// (no repo) it POSTs the demo findings with a sample status mix so the layout is
|
||||
// illustrative. Mirrors the backend compliance/services/cra_progress.py phases.
|
||||
|
||||
export interface ProgressTheme {
|
||||
finding_id: string
|
||||
title: string
|
||||
requirement: string
|
||||
requirement_title: string
|
||||
risk_level: string
|
||||
phase: string
|
||||
phase_label: string
|
||||
status: string
|
||||
tracker_url: string
|
||||
has_ticket: boolean
|
||||
location: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface ProgressReq {
|
||||
req_id: string
|
||||
title: string
|
||||
total: number
|
||||
offen: number
|
||||
in_arbeit: number
|
||||
erledigt: number
|
||||
ausgeschlossen: number
|
||||
phase: string
|
||||
phase_label: string
|
||||
completion_pct: number
|
||||
open_risk: string
|
||||
}
|
||||
|
||||
export interface CRAProgress {
|
||||
total: number
|
||||
actionable: number
|
||||
completion_pct: number
|
||||
by_phase: Record<string, number>
|
||||
by_status: Record<string, number>
|
||||
by_risk_open: Record<string, number>
|
||||
open_count: number
|
||||
requirements: ProgressReq[]
|
||||
themes: ProgressTheme[]
|
||||
source?: { scanner: boolean; pulled: number; repo_id?: string }
|
||||
}
|
||||
|
||||
// Demo status mix (no real repo): one fixed, two ticketed/in-progress, rest open.
|
||||
const DEMO_STATUS: Record<string, { status: string; tracker?: string }> = {
|
||||
'KH-CY-6': { status: 'resolved' },
|
||||
'KH-CY-4': { status: 'triaged', tracker: 'https://jira.example.com/browse/BP-204' },
|
||||
'KH-CY-3': { status: 'triaged', tracker: 'https://jira.example.com/browse/BP-203' },
|
||||
}
|
||||
|
||||
export function useCRAProgress(scannerRepo: string, demoFindings: CRAFinding[]) {
|
||||
const [progress, setProgress] = useState<CRAProgress | null>(null)
|
||||
const [live, setLive] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
;(async () => {
|
||||
if (scannerRepo) {
|
||||
try {
|
||||
const r = await fetch(`/api/v1/cra/progress?repo_id=${encodeURIComponent(scannerRepo)}`)
|
||||
if (r.ok) {
|
||||
const j = await r.json()
|
||||
if (!cancelled) { setProgress(j); setLive(true) }
|
||||
return
|
||||
}
|
||||
} catch { /* fall through to demo */ }
|
||||
}
|
||||
try {
|
||||
const findings = demoFindings.map((f) => ({
|
||||
id: f.id, cwe: f.cwe, severity: f.scanner_severity, title: f.title,
|
||||
status: DEMO_STATUS[f.id]?.status || 'open',
|
||||
tracker_issue_url: DEMO_STATUS[f.id]?.tracker || null,
|
||||
}))
|
||||
const r = await fetch('/api/v1/cra/progress', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ findings }),
|
||||
})
|
||||
if (r.ok) {
|
||||
const j = await r.json()
|
||||
if (!cancelled) { setProgress(j); setLive(false) }
|
||||
}
|
||||
} catch { /* leave null → view hides */ }
|
||||
})()
|
||||
return () => { cancelled = true }
|
||||
}, [scannerRepo, demoFindings])
|
||||
|
||||
return { progress, live }
|
||||
}
|
||||
Reference in New Issue
Block a user