From 5d53acf5dc4ab1afdf87df7580a56940f4fceed5 Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Thu, 23 Apr 2026 12:28:49 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Upselling-Funnel=20Assessment=20?= =?UTF-8?q?=E2=86=92=20Compliance=20Optimizer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verbindet das kostenlose UCCA Assessment mit dem bezahlten Compliance Optimizer durch gezielte CTAs: - OptimizerUpsellCard: Kontextabhaengig (CONDITIONAL→prominent, YES→dezent) - Assessment Detail: "Optimieren" Button + CTA-Block nach Ergebnis - Advisory Board ResultView: CTA nach Wizard-Abschluss - Optimizer "new": Auto-Submit bei ?from_assessment={id} - Optimizer Liste + Detail: Links zum Quell-Assessment Co-Authored-By: Claude Opus 4.6 (1M context) --- .../advisory-board/_components/ResultView.tsx | 8 +++ .../sdk/compliance-optimizer/[id]/page.tsx | 5 ++ .../app/sdk/compliance-optimizer/new/page.tsx | 38 +++++++++++- .../app/sdk/compliance-optimizer/page.tsx | 11 ++++ .../app/sdk/use-cases/[id]/page.tsx | 27 ++++++++ .../OptimizerUpsellCard.tsx | 62 +++++++++++++++++++ 6 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 admin-compliance/components/sdk/compliance-optimizer/OptimizerUpsellCard.tsx diff --git a/admin-compliance/app/sdk/advisory-board/_components/ResultView.tsx b/admin-compliance/app/sdk/advisory-board/_components/ResultView.tsx index cf3cbed..ab01c49 100644 --- a/admin-compliance/app/sdk/advisory-board/_components/ResultView.tsx +++ b/admin-compliance/app/sdk/advisory-board/_components/ResultView.tsx @@ -2,6 +2,7 @@ import React from 'react' import { AssessmentResultCard } from '@/components/sdk/use-case-assessment/AssessmentResultCard' +import { OptimizerUpsellCard } from '@/components/sdk/compliance-optimizer/OptimizerUpsellCard' interface Props { result: unknown @@ -35,6 +36,13 @@ export function ResultView({ result, onGoToAssessment, onGoToOverview }: Props) {r.result && ( [0]['result']} /> )} + {r.result && r.assessment?.id && ( + + )} ) } diff --git a/admin-compliance/app/sdk/compliance-optimizer/[id]/page.tsx b/admin-compliance/app/sdk/compliance-optimizer/[id]/page.tsx index b1c47b5..20a6496 100644 --- a/admin-compliance/app/sdk/compliance-optimizer/[id]/page.tsx +++ b/admin-compliance/app/sdk/compliance-optimizer/[id]/page.tsx @@ -41,6 +41,11 @@ export default function OptimizationDetailPage() { ← Zurueck

{data.title || 'Optimierung'}

{new Date(data.created_at).toLocaleString('de-DE')} — v{data.constraint_version}

+ {data.assessment_id && ( + + Basierend auf Assessment + + )} diff --git a/admin-compliance/app/sdk/compliance-optimizer/new/page.tsx b/admin-compliance/app/sdk/compliance-optimizer/new/page.tsx index 1b7c3ab..6a8711e 100644 --- a/admin-compliance/app/sdk/compliance-optimizer/new/page.tsx +++ b/admin-compliance/app/sdk/compliance-optimizer/new/page.tsx @@ -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 { ZoneBadge } from '@/components/sdk/compliance-optimizer/ZoneBadge' interface DimensionField { @@ -63,9 +63,21 @@ const TOGGLE_DIMENSIONS = [ { key: 'logging_required', label: 'Protokollierungspflicht' }, ] -export default function NewOptimizationPage() { +function NewOptimizationPageInner() { const router = useRouter() + const searchParams = useSearchParams() + const fromAssessment = searchParams.get('from_assessment') + const [autoOptimizing, setAutoOptimizing] = useState(false) const [title, setTitle] = useState('') + + useEffect(() => { + if (!fromAssessment) return + setAutoOptimizing(true) + fetch(`/api/sdk/v1/maximizer/optimize-from-assessment/${fromAssessment}`, { method: 'POST' }) + .then(r => r.ok ? r.json() : Promise.reject('failed')) + .then(data => router.push(`/sdk/compliance-optimizer/${data.id}`)) + .catch(() => setAutoOptimizing(false)) + }, [fromAssessment, router]) const [config, setConfig] = useState>({ automation_level: 'assistive', decision_binding: 'non_binding', decision_impact: 'low', domain: 'general', data_type: 'non_personal', human_in_loop: 'required', @@ -104,6 +116,18 @@ export default function NewOptimizationPage() { } } + if (autoOptimizing) { + return ( +
+
+ 📊 +

Optimierung laeuft...

+

Assessment wird analysiert und optimale Konfiguration berechnet.

+
+
+ ) + } + return (

Neue Optimierung

@@ -161,3 +185,11 @@ export default function NewOptimizationPage() {
) } + +export default function NewOptimizationPage() { + return ( + Laden...}> + + + ) +} diff --git a/admin-compliance/app/sdk/compliance-optimizer/page.tsx b/admin-compliance/app/sdk/compliance-optimizer/page.tsx index 8573b8d..9641848 100644 --- a/admin-compliance/app/sdk/compliance-optimizer/page.tsx +++ b/admin-compliance/app/sdk/compliance-optimizer/page.tsx @@ -12,6 +12,7 @@ interface OptimizationSummary { created_at: string zone_map: Record max_safe_config?: { safety_score: number; utility_score: number } + assessment_id?: string } function countZones(zoneMap: Record) { @@ -83,6 +84,7 @@ export default function ComplianceOptimizerPage() { Titel Status Zonen + Quelle Datum @@ -104,6 +106,15 @@ export default function ComplianceOptimizerPage() { {zones.restricted > 0 && {zones.restricted} eingeschraenkt} {zones.safe} erlaubt + + {o.assessment_id ? ( + + Assessment + + ) : ( + Manuell + )} + {new Date(o.created_at).toLocaleDateString('de-DE')} diff --git a/admin-compliance/app/sdk/use-cases/[id]/page.tsx b/admin-compliance/app/sdk/use-cases/[id]/page.tsx index 9046973..ed8221e 100644 --- a/admin-compliance/app/sdk/use-cases/[id]/page.tsx +++ b/admin-compliance/app/sdk/use-cases/[id]/page.tsx @@ -4,6 +4,7 @@ import React, { useState, useEffect } from 'react' import { useParams, useRouter } from 'next/navigation' import Link from 'next/link' import { AssessmentResultCard } from '@/components/sdk/use-case-assessment/AssessmentResultCard' +import { OptimizerUpsellCard } from '@/components/sdk/compliance-optimizer/OptimizerUpsellCard' interface TriggeredRule { code: string @@ -138,6 +139,18 @@ export default function AssessmentDetailPage() { } } + const [optimizing, setOptimizing] = useState(false) + const handleOptimize = async () => { + setOptimizing(true) + try { + const res = await fetch(`/api/sdk/v1/maximizer/optimize-from-assessment/${assessmentId}`, { method: 'POST' }) + if (res.ok) { + const data = await res.json() + router.push(`/sdk/compliance-optimizer/${data.id}`) + } + } catch { /* silent */ } finally { setOptimizing(false) } + } + if (loading) { return (
@@ -234,6 +247,13 @@ export default function AssessmentDetailPage() { > ↓ JSON + [0]['result']} /> + {/* Compliance Optimizer Upsell */} + + {/* KI-Erklärung */} {assessment.explanation_text && (
diff --git a/admin-compliance/components/sdk/compliance-optimizer/OptimizerUpsellCard.tsx b/admin-compliance/components/sdk/compliance-optimizer/OptimizerUpsellCard.tsx new file mode 100644 index 0000000..5f8623e --- /dev/null +++ b/admin-compliance/components/sdk/compliance-optimizer/OptimizerUpsellCard.tsx @@ -0,0 +1,62 @@ +'use client' + +import Link from 'next/link' + +interface OptimizerUpsellCardProps { + feasibility: string + assessmentId: string + riskScore?: number +} + +export function OptimizerUpsellCard({ feasibility, assessmentId, riskScore }: OptimizerUpsellCardProps) { + const isRestricted = feasibility === 'CONDITIONAL' || feasibility === 'NO' + + if (isRestricted) { + return ( +
+
+ 📊 +
+

+ {feasibility === 'NO' ? 'Use Case aktuell nicht umsetzbar' : 'Use Case eingeschraenkt machbar'} +

+

+ Der Compliance Optimizer zeigt Ihnen die optimale Konfiguration, + um den regulatorischen Spielraum maximal auszunutzen — ohne Grenzen zu ueberschreiten. +

+ {riskScore != null && riskScore >= 50 && ( +

+ Risiko-Score {riskScore}/100 — besonders hohes Optimierungspotenzial. +

+ )} + + Jetzt optimieren → + +
+
+
+ ) + } + + return ( +
+
+
+

Regulatorischen Spielraum pruefen

+

+ Pruefen Sie ob Sie den regulatorischen Spielraum noch besser nutzen koennen. +

+
+ + Optional optimieren → + +
+
+ ) +}