feat: Use-Cases/UCCA Module auf 100% — Interface Fix, Search/Offset/Total, Explain/Export, Edit-Mode
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 35s
CI / test-python-backend-compliance (push) Successful in 33s
CI / test-python-document-crawler (push) Successful in 23s
CI / test-python-dsms-gateway (push) Successful in 18s

Kritische Bug Fixes:
- [id]/page.tsx: FullAssessment Interface repariert (nested result → flat fields)
- resultForCard baut explizit aus flachen Assessment-Feldern (feasibility, risk_score etc.)
- Use-Case-Text-Pfad: assessment.intake?.use_case_text statt assessment.use_case_text
- rule_code/code Mapping beim Übergeben an AssessmentResultCard

Backend (A2+A3):
- store.go: AssessmentFilters um Search + Offset erweitert
- ListAssessments: COUNT-Query (total), ILIKE-Search auf title, OFFSET-Pagination
- ListAssessments Signatur: ([]Assessment, int, error)
- Handler: search/offset aus Query-Params, total in Response
- import "strconv" hinzugefügt

Neue Features:
- KI-Erklärung Button (POST /explain) mit lila Erklärungsbox
- Export-Buttons Markdown + JSON (Download-Links)
- Edit-Mode in new/page.tsx: useSearchParams(?edit=id), Form vorausfüllen
- Bedingte PUT/POST Logik; nach Edit → Detail-Seite Redirect
- Suspense-Wrapper für useSearchParams (Next.js 15 Requirement)

Backend Edit:
- store.go: UpdateAssessment() Methode (UPDATE-Query)
- ucca_handlers.go: UpdateAssessment Handler (re-evaluiert Intake)
- main.go: PUT /ucca/assessments/:id Route registriert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-03 15:13:42 +01:00
parent d4845adea7
commit 312c2c9b60
5 changed files with 417 additions and 104 deletions

View File

@@ -6,6 +6,7 @@ import (
"encoding/hex"
"fmt"
"net/http"
"strconv"
"strings"
"time"
@@ -213,15 +214,22 @@ func (h *UCCAHandlers) ListAssessments(c *gin.Context) {
Feasibility: c.Query("feasibility"),
Domain: c.Query("domain"),
RiskLevel: c.Query("risk_level"),
Search: c.Query("search"),
}
if limit, err := strconv.Atoi(c.DefaultQuery("limit", "0")); err == nil {
filters.Limit = limit
}
if offset, err := strconv.Atoi(c.DefaultQuery("offset", "0")); err == nil {
filters.Offset = offset
}
assessments, err := h.store.ListAssessments(c.Request.Context(), tenantID, filters)
assessments, total, err := h.store.ListAssessments(c.Request.Context(), tenantID, filters)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"assessments": assessments})
c.JSON(http.StatusOK, gin.H{"assessments": assessments, "total": total})
}
// ============================================================================
@@ -269,6 +277,78 @@ func (h *UCCAHandlers) DeleteAssessment(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "deleted"})
}
// ============================================================================
// PUT /sdk/v1/ucca/assessments/:id - Update an existing assessment
// ============================================================================
// UpdateAssessment re-evaluates and updates an existing assessment
func (h *UCCAHandlers) UpdateAssessment(c *gin.Context) {
id, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
return
}
var intake ucca.UseCaseIntake
if err := c.ShouldBindJSON(&intake); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Re-run evaluation with updated intake
var result *ucca.AssessmentResult
var policyVersion string
if h.policyEngine != nil {
result = h.policyEngine.Evaluate(&intake)
policyVersion = h.policyEngine.GetPolicyVersion()
} else {
result = h.legacyRuleEngine.Evaluate(&intake)
policyVersion = "1.0.0-legacy"
}
hash := sha256.Sum256([]byte(intake.UseCaseText))
hashStr := hex.EncodeToString(hash[:])
updated := &ucca.Assessment{
Title: intake.Title,
PolicyVersion: policyVersion,
Intake: intake,
UseCaseTextStored: intake.StoreRawText,
UseCaseTextHash: hashStr,
Feasibility: result.Feasibility,
RiskLevel: result.RiskLevel,
Complexity: result.Complexity,
RiskScore: result.RiskScore,
TriggeredRules: result.TriggeredRules,
RequiredControls: result.RequiredControls,
RecommendedArchitecture: result.RecommendedArchitecture,
ForbiddenPatterns: result.ForbiddenPatterns,
ExampleMatches: result.ExampleMatches,
DSFARecommended: result.DSFARecommended,
Art22Risk: result.Art22Risk,
TrainingAllowed: result.TrainingAllowed,
Domain: intake.Domain,
}
if !intake.StoreRawText {
updated.Intake.UseCaseText = ""
}
if updated.Title == "" {
updated.Title = fmt.Sprintf("Assessment vom %s", time.Now().Format("02.01.2006 15:04"))
}
if err := h.store.UpdateAssessment(c.Request.Context(), id, updated); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
assessment, err := h.store.GetAssessment(c.Request.Context(), id)
if err != nil || assessment == nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "assessment not found after update"})
return
}
c.JSON(http.StatusOK, assessment)
}
// ============================================================================
// GET /sdk/v1/ucca/patterns - Get pattern catalog
// ============================================================================