/** * Zentrales Findings-Panel für Progressionsgraph-QA (Phase B.2). */ import React, { useMemo, useState } from 'react' import { offerCanExpandSlots, offerNeedsNewSlot, offerSourceLabel, resolveOfferSlotIndex, } from '../utils/progressionGraphDraft' function severityStyle(pathQa) { if (!pathQa) return {} return { background: pathQa.overall_ok ? 'color-mix(in srgb, var(--accent) 8%, var(--surface2))' : 'color-mix(in srgb, var(--danger) 8%, var(--surface2))', } } function GapOfferCard({ offer, slotCount, draft, onApplyDraft, onInsertSlot, onGenerateAi, generatingOfferId, aiBusy, }) { const defaultSlot = resolveOfferSlotIndex(draft, offer) const [slotPick, setSlotPick] = useState( defaultSlot != null && Number.isFinite(defaultSlot) ? String(defaultSlot) : '', ) const needsInsert = offerNeedsNewSlot(offer) const canInsert = offerCanExpandSlots(draft, offer) const slotOptions = useMemo(() => { const rows = [] for (let i = 0; i < slotCount; i += 1) { rows.push({ value: String(i), label: `Slot ${i + 1}` }) } return rows }, [slotCount]) const applyToSlot = () => { const idx = slotPick !== '' ? Number(slotPick) : defaultSlot if (!Number.isFinite(idx)) { alert('Bitte einen Slot wählen.') return } onApplyDraft(offer, idx) } return (
  • {offerSourceLabel(offer.source)} {offer.phase ? ` · ${offer.phase}` : ''} {offer.has_ai_payload ? ' · KI-Entwurf bereit' : ''}
    {offer.title_hint || offer.proposal_title || 'Übungsvorschlag'}
    {offer.rationale ? (

    {offer.rationale}

    ) : null} {offer.from_title && offer.to_title ? (

    Zwischen „{offer.from_title}“ und „{offer.to_title}“

    ) : null}
    {offer.has_ai_payload ? ( ) : ( )} {needsInsert ? ( ) : null}
  • ) } export default function ProgressionFindingsPanel({ pathQa = null, gapFillOffers = [], draft = null, slotCount = 0, loading = false, error = '', onEvaluate, onApplyGapOffer, onInsertGapSlot, onGenerateGapAi, generatingOfferId = null, aiBusy = false, evaluateDisabled = false, }) { return (

    Graph-Bewertung

    Prüft den Slot-Stand und listet KI-Angebote für leere Stufen und Lücken.

    {error ? (

    {error}

    ) : null} {pathQa ? (
    Pfad-QS: {pathQa.overall_ok ? 'OK' : 'Hinweise'} {pathQa.quality_score != null ? ` (${Math.round(Number(pathQa.quality_score) * 100)} %)` : ''} {pathQa.topic_coverage ? (

    {pathQa.topic_coverage}

    ) : null} {Array.isArray(pathQa.issues) && pathQa.issues.length > 0 ? ( ) : null} {Array.isArray(pathQa.recommendations) && pathQa.recommendations.length > 0 ? ( <>

    Empfehlungen

    ) : null} {Number(pathQa.off_topic_count) > 0 ? (

    {pathQa.off_topic_count} Schritt(e) ohne Bezug zum Pfad-Thema.

    ) : null}
    ) : (

    Noch keine Bewertung. Roadmap anlegen, dann „Graph bewerten“ oder „Übungen matchen“.

    )}

    KI-Angebote {gapFillOffers.length > 0 ? `(${gapFillOffers.length})` : ''}

    {gapFillOffers.length === 0 ? (

    Keine offenen Angebote. Nach Match oder Bewertung erscheinen Vorschläge für leere Slots und Lücken.

    ) : ( )}
    ) }