e536247c20
Wire the 195 Clean-Room QUAIDAL controls (from breakpilot-core migration 011)
into the compliance SaaS UI.
Backend:
- GET /api/v1/quaidal/stats - counts by kind + source provenance
- GET /api/v1/quaidal/controls - list, optional kind= filter
- GET /api/v1/quaidal/controls/{id} - single derived control
- GET /api/v1/quaidal/criteria - 10 QKB criteria
- GET /api/v1/quaidal/criteria/{id} - QKB with QB/MA/QM tree
Frontend:
- /sdk/quality: new "Trainingsdaten-Qualität (BSI QUAIDAL)" tab with
10 QKB cards and a drill-down modal showing the full QB→MA→QM tree
plus original BSI source link and license note.
- /sdk/ai-act: Art. 10 tile on each high-risk/unacceptable result,
linking to /sdk/quality?category=data_quality.
Pattern matches existing IACE module DIN-reference handling:
own wording, source section + URL preserved for due diligence.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
87 lines
2.4 KiB
TypeScript
87 lines
2.4 KiB
TypeScript
'use client'
|
|
|
|
import { useCallback, useEffect, useState } from 'react'
|
|
|
|
export interface QuaidalExternalRef {
|
|
framework: string
|
|
citation: string | null
|
|
}
|
|
|
|
export interface QuaidalSource {
|
|
framework: string
|
|
section: string
|
|
url: string | null
|
|
commit_sha: string | null
|
|
title_original: string | null
|
|
license_note: string | null
|
|
}
|
|
|
|
export interface QuaidalControl {
|
|
derived_id: string
|
|
kind: 'criterion' | 'building_block' | 'measure' | 'metric'
|
|
canonical_name: string
|
|
description: string
|
|
regulation_anchor: string | null
|
|
related_quaidal_ids: string[]
|
|
external_refs: QuaidalExternalRef[]
|
|
source: QuaidalSource
|
|
plagiarism_score: number | null
|
|
}
|
|
|
|
export interface QuaidalStats {
|
|
counts_by_kind: Record<string, number>
|
|
source_framework: string
|
|
source_commit_sha: string | null
|
|
license_note: string | null
|
|
}
|
|
|
|
export interface QuaidalCriterionTree {
|
|
criterion: QuaidalControl
|
|
building_blocks: QuaidalControl[]
|
|
measures: QuaidalControl[]
|
|
metrics: QuaidalControl[]
|
|
}
|
|
|
|
const API_BASE = '/api/sdk/v1/quaidal'
|
|
|
|
export function useQuaidalData() {
|
|
const [criteria, setCriteria] = useState<QuaidalControl[]>([])
|
|
const [stats, setStats] = useState<QuaidalStats | null>(null)
|
|
const [loading, setLoading] = useState(true)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
const loadAll = useCallback(async () => {
|
|
setLoading(true)
|
|
setError(null)
|
|
try {
|
|
const [criteriaRes, statsRes] = await Promise.all([
|
|
fetch(`${API_BASE}/criteria`, { cache: 'no-store' }),
|
|
fetch(`${API_BASE}/stats`, { cache: 'no-store' }),
|
|
])
|
|
if (criteriaRes.ok) {
|
|
const data = (await criteriaRes.json()) as QuaidalControl[]
|
|
setCriteria(Array.isArray(data) ? data : [])
|
|
} else {
|
|
setError(`Criteria endpoint returned ${criteriaRes.status}`)
|
|
}
|
|
if (statsRes.ok) {
|
|
setStats(await statsRes.json())
|
|
}
|
|
} catch (err) {
|
|
setError(String(err))
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => { loadAll() }, [loadAll])
|
|
|
|
return { criteria, stats, loading, error, reload: loadAll }
|
|
}
|
|
|
|
export async function fetchCriterionTree(sectionId: string): Promise<QuaidalCriterionTree | null> {
|
|
const res = await fetch(`${API_BASE}/criteria/${encodeURIComponent(sectionId)}`, { cache: 'no-store' })
|
|
if (!res.ok) return null
|
|
return (await res.json()) as QuaidalCriterionTree
|
|
}
|