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

@@ -1,7 +1,7 @@
'use client'
import React, { useState } from 'react'
import { useRouter } from 'next/navigation'
import React, { useState, useEffect, Suspense } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
import { AssessmentResultCard } from '@/components/sdk/use-case-assessment/AssessmentResultCard'
// =============================================================================
@@ -38,10 +38,15 @@ const DOMAINS = [
// MAIN COMPONENT
// =============================================================================
export default function NewUseCasePage() {
function NewUseCasePageInner() {
const router = useRouter()
const searchParams = useSearchParams()
const editId = searchParams.get('edit')
const isEditMode = !!editId
const [currentStep, setCurrentStep] = useState(1)
const [isSubmitting, setIsSubmitting] = useState(false)
const [editLoading, setEditLoading] = useState(false)
const [result, setResult] = useState<unknown>(null)
const [error, setError] = useState<string | null>(null)
@@ -93,6 +98,52 @@ export default function NewUseCasePage() {
setForm(prev => ({ ...prev, ...updates }))
}
// Pre-fill form when in edit mode
useEffect(() => {
if (!editId) return
setEditLoading(true)
fetch(`/api/sdk/v1/ucca/assessments/${editId}`)
.then(r => r.json())
.then(data => {
const intake = data.intake || {}
setForm({
title: data.title || '',
use_case_text: intake.use_case_text || '',
domain: data.domain || 'general',
personal_data: intake.data_types?.personal_data || false,
special_categories: intake.data_types?.article_9_data || false,
minors_data: intake.data_types?.minor_data || false,
health_data: intake.data_types?.health_data || false,
biometric_data: intake.data_types?.biometric_data || false,
financial_data: intake.data_types?.financial_data || false,
purpose_profiling: intake.purpose?.profiling || false,
purpose_automated_decision: intake.purpose?.automated_decision || intake.purpose?.decision_making || false,
purpose_marketing: intake.purpose?.marketing || false,
purpose_analytics: intake.purpose?.analytics || false,
purpose_service_delivery: intake.purpose?.service_delivery || intake.purpose?.customer_support || false,
automation: intake.automation || 'assistive',
hosting_provider: intake.hosting?.provider || 'self_hosted',
hosting_region: intake.hosting?.region || 'eu',
model_rag: intake.model_usage?.rag || false,
model_finetune: intake.model_usage?.finetune || false,
model_training: intake.model_usage?.training || false,
model_inference: intake.model_usage?.inference ?? true,
legal_basis: intake.legal_basis || 'consent',
international_transfer: intake.international_transfer?.enabled || false,
transfer_countries: intake.international_transfer?.countries || [],
transfer_mechanism: intake.international_transfer?.mechanism || 'none',
retention_days: intake.retention?.days || 90,
retention_purpose: intake.retention?.purpose || '',
has_dpa: intake.contracts?.has_dpa || false,
has_aia_documentation: intake.contracts?.has_aia_documentation || false,
has_risk_assessment: intake.contracts?.has_risk_assessment || false,
subprocessors: intake.contracts?.subprocessors || '',
})
})
.catch(() => {})
.finally(() => setEditLoading(false))
}, [editId])
const handleSubmit = async () => {
setIsSubmitting(true)
setError(null)
@@ -146,8 +197,13 @@ export default function NewUseCasePage() {
store_raw_text: true,
}
const response = await fetch('/api/sdk/v1/ucca/assess', {
method: 'POST',
const url = isEditMode
? `/api/sdk/v1/ucca/assessments/${editId}`
: '/api/sdk/v1/ucca/assess'
const method = isEditMode ? 'PUT' : 'POST'
const response = await fetch(url, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(intake),
})
@@ -157,6 +213,11 @@ export default function NewUseCasePage() {
throw new Error(errData?.error || `HTTP ${response.status}`)
}
if (isEditMode) {
router.push(`/sdk/use-cases/${editId}`)
return
}
const data = await response.json()
setResult(data)
} catch (err) {
@@ -201,12 +262,23 @@ export default function NewUseCasePage() {
<div className="max-w-3xl mx-auto space-y-6">
{/* Header */}
<div>
<h1 className="text-2xl font-bold text-gray-900">Neues Use Case Assessment</h1>
<h1 className="text-2xl font-bold text-gray-900">
{isEditMode ? 'Assessment bearbeiten' : 'Neues Use Case Assessment'}
</h1>
<p className="mt-1 text-gray-500">
Beschreiben Sie Ihren KI-Anwendungsfall Schritt fuer Schritt
{isEditMode
? 'Angaben anpassen und Assessment neu bewerten'
: 'Beschreiben Sie Ihren KI-Anwendungsfall Schritt fuer Schritt'}
</p>
</div>
{/* Edit loading indicator */}
{editLoading && (
<div className="bg-purple-50 border border-purple-200 rounded-lg p-4 text-purple-700 text-sm">
Lade Assessment-Daten...
</div>
)}
{/* Step Indicator */}
<div className="flex items-center gap-2">
{WIZARD_STEPS.map((step, idx) => (
@@ -591,7 +663,7 @@ export default function NewUseCasePage() {
Bewerte...
</>
) : (
'Assessment starten'
isEditMode ? 'Speichern & neu bewerten' : 'Assessment starten'
)}
</button>
)}
@@ -599,3 +671,11 @@ export default function NewUseCasePage() {
</div>
)
}
export default function NewUseCasePage() {
return (
<Suspense fallback={<div className="flex items-center justify-center h-64 text-gray-500">Lade...</div>}>
<NewUseCasePageInner />
</Suspense>
)
}