fix(sdk): Align scope types with engine output + project isolation + optional block progress
Some checks failed
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 46s
CI/CD / test-python-backend-compliance (push) Successful in 42s
CI/CD / test-python-document-crawler (push) Successful in 29s
CI/CD / test-python-dsms-gateway (push) Successful in 25s
CI/CD / deploy-hetzner (push) Failing after 2s

Type alignment (root cause of client-side crash):
- RiskFlag: id/title/description → severity/category/message/recommendation
- ScopeGap: id/title/recommendation/relatedDocuments → gapType/currentState/targetState/effort
- NextAction: id/priority:number/effortDays → actionType/priority:string/estimatedEffort
- ScopeReasoning: details → factors + impact
- TriggeredHardTrigger: {rule: HardTriggerRule} → flat fields (ruleId, description, etc.)
- All UI components updated to match engine output shape

Project isolation:
- Scope localStorage key now includes projectId (prevents data leak between projects)

Optional block progress:
- Blocks with only optional questions now show green checkmark when any question answered

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-11 14:58:29 +01:00
parent 46048554cb
commit cb48b8289e
7 changed files with 185 additions and 152 deletions

View File

@@ -31,12 +31,12 @@ export function ScopeExportTab({ decision: decisionProp, answers: answersProp, s
const handleDownloadCSV = useCallback(() => {
if (!decision || !decision.requiredDocuments) return
const headers = ['Typ', 'Tiefe', 'Aufwand (Tage)', 'Pflicht', 'Hard-Trigger']
const headers = ['Typ', 'Priorität', 'Aufwand (Stunden)', 'Pflicht', 'Hard-Trigger']
const rows = decision.requiredDocuments.map((doc) => [
DOCUMENT_TYPE_LABELS[doc.documentType] || doc.documentType,
doc.depth,
doc.estimatedEffort || '0',
doc.required ? 'Ja' : 'Nein',
doc.priority,
String(doc.estimatedEffort || 0),
doc.requirement === 'mandatory' ? 'Ja' : 'Nein',
doc.triggeredBy.length > 0 ? 'Ja' : 'Nein',
])
@@ -58,8 +58,8 @@ export function ScopeExportTab({ decision: decisionProp, answers: answersProp, s
markdown += `**Datum:** ${new Date().toLocaleDateString('de-DE')}\n\n`
markdown += `## Einstufung\n\n`
markdown += `**Level:** ${decision.determinedLevel} - ${DEPTH_LEVEL_LABELS[decision.determinedLevel]}\n\n`
if (decision.reasoning) {
markdown += `**Begründung:** ${decision.reasoning}\n\n`
if (decision.reasoning && decision.reasoning.length > 0) {
markdown += `**Begründung:** ${decision.reasoning.map(r => r.description).filter(Boolean).join('. ')}\n\n`
}
if (decision.scores) {
@@ -73,10 +73,10 @@ export function ScopeExportTab({ decision: decisionProp, answers: answersProp, s
if (decision.triggeredHardTriggers && decision.triggeredHardTriggers.length > 0) {
markdown += `## Aktive Hard-Trigger\n\n`
decision.triggeredHardTriggers.forEach((trigger) => {
markdown += `- **${trigger.rule.label}**\n`
markdown += ` - ${trigger.rule.description}\n`
if (trigger.rule.legalReference) {
markdown += ` - Rechtsgrundlage: ${trigger.rule.legalReference}\n`
markdown += `- **${trigger.description}**\n`
markdown += ` - ${trigger.description}\n`
if (trigger.legalReference) {
markdown += ` - Rechtsgrundlage: ${trigger.legalReference}\n`
}
})
markdown += `\n`
@@ -84,12 +84,12 @@ export function ScopeExportTab({ decision: decisionProp, answers: answersProp, s
if (decision.requiredDocuments && decision.requiredDocuments.length > 0) {
markdown += `## Erforderliche Dokumente\n\n`
markdown += `| Typ | Tiefe | Aufwand | Pflicht | Hard-Trigger |\n`
markdown += `|-----|-------|---------|---------|-------------|\n`
markdown += `| Typ | Priorität | Aufwand (h) | Pflicht | Hard-Trigger |\n`
markdown += `|-----|-----------|-------------|---------|-------------|\n`
decision.requiredDocuments.forEach((doc) => {
markdown += `| ${DOCUMENT_TYPE_LABELS[doc.documentType] || doc.documentType} | ${doc.depth} | ${
doc.estimatedEffort || '0'
} | ${doc.required ? 'Ja' : 'Nein'} | ${doc.triggeredBy.length > 0 ? 'Ja' : 'Nein'} |\n`
markdown += `| ${DOCUMENT_TYPE_LABELS[doc.documentType] || doc.documentType} | ${doc.priority} | ${
doc.estimatedEffort || 0
} | ${doc.requirement === 'mandatory' ? 'Ja' : 'Nein'} | ${doc.triggeredBy.length > 0 ? 'Ja' : 'Nein'} |\n`
})
markdown += `\n`
}
@@ -97,19 +97,29 @@ export function ScopeExportTab({ decision: decisionProp, answers: answersProp, s
if (decision.riskFlags && decision.riskFlags.length > 0) {
markdown += `## Risiko-Flags\n\n`
decision.riskFlags.forEach((flag) => {
markdown += `### ${flag.title} (${flag.severity})\n\n`
markdown += `${flag.description}\n\n`
markdown += `### ${flag.message} (${flag.severity})\n\n`
if (flag.legalReference) markdown += `Rechtsgrundlage: ${flag.legalReference}\n\n`
markdown += `**Empfehlung:** ${flag.recommendation}\n\n`
})
}
if (decision.gaps && decision.gaps.length > 0) {
markdown += `## Gap-Analyse\n\n`
decision.gaps.forEach((gap) => {
markdown += `### ${gap.description} (${gap.severity})\n\n`
markdown += `- **Ist:** ${gap.currentState}\n`
markdown += `- **Soll:** ${gap.targetState}\n`
markdown += `- **Aufwand:** ~${gap.effort}h\n\n`
})
}
if (decision.nextActions && decision.nextActions.length > 0) {
markdown += `## Nächste Schritte\n\n`
decision.nextActions.forEach((action) => {
markdown += `${action.priority}. **${action.title}**\n`
decision.nextActions.forEach((action, idx) => {
markdown += `${idx + 1}. **${action.title}**\n`
markdown += ` ${action.description}\n`
if (action.estimatedEffort) {
markdown += ` Aufwand: ${action.estimatedEffort}\n`
markdown += ` Aufwand: ~${action.estimatedEffort}h\n`
}
markdown += `\n`
})