feat: Document recommendation UI in generator
New RecommendedDocuments component shown above the template library: - Evaluates scope answers + compliance level (L1-L4) - Groups templates into required/recommended/optional - Shows profile label (Startup/KMU/Extended/Enterprise) - Cards link to actual templates — click opens in generator - Optional section collapsed by default - Only visible when scope has been completed Renders as purple gradient panel with grid cards, each showing template name and availability status. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
'use client'
|
||||
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useSDK } from '@/lib/sdk'
|
||||
import { evaluateTemplateRecommendations, type TemplateRecommendation } from '../templateRecommendations'
|
||||
import { getProfileLabel } from '../scopeDefaults'
|
||||
import type { LegalTemplateResult } from '@/lib/sdk/types'
|
||||
import type { ComplianceDepthLevel } from '@/lib/sdk/compliance-scope-types/core-levels'
|
||||
|
||||
interface Props {
|
||||
allTemplates: LegalTemplateResult[]
|
||||
onUseTemplate: (t: LegalTemplateResult) => void
|
||||
}
|
||||
|
||||
export default function RecommendedDocuments({ allTemplates, onUseTemplate }: Props) {
|
||||
const { state } = useSDK()
|
||||
const [showOptional, setShowOptional] = useState(false)
|
||||
|
||||
const level = state?.complianceScope?.determinedLevel as ComplianceDepthLevel | undefined
|
||||
const scopeAnswers = state?.complianceScope?.answers || []
|
||||
|
||||
const recommendations = useMemo(() => {
|
||||
if (!level) return null
|
||||
return evaluateTemplateRecommendations(
|
||||
scopeAnswers,
|
||||
level,
|
||||
(state?.companyProfile as Record<string, unknown>) || {},
|
||||
)
|
||||
}, [level, scopeAnswers, state?.companyProfile])
|
||||
|
||||
if (!level || !recommendations || recommendations.length === 0) return null
|
||||
|
||||
// Match recommendations to actual templates in the library
|
||||
const templateMap = new Map<string, LegalTemplateResult>()
|
||||
for (const t of allTemplates) {
|
||||
if (t.templateType) templateMap.set(t.templateType, t)
|
||||
}
|
||||
|
||||
const required = recommendations.filter((r) => r.requirement === 'required')
|
||||
const recommended = recommendations.filter((r) => r.requirement === 'recommended')
|
||||
const optional = recommendations.filter((r) => r.requirement === 'optional')
|
||||
|
||||
const renderCard = (rec: TemplateRecommendation) => {
|
||||
const template = templateMap.get(rec.templateType)
|
||||
const exists = !!template
|
||||
|
||||
return (
|
||||
<div
|
||||
key={rec.templateType}
|
||||
className={`rounded-lg border p-3 text-sm ${
|
||||
exists
|
||||
? 'border-gray-200 bg-white hover:border-purple-300 cursor-pointer'
|
||||
: 'border-dashed border-gray-300 bg-gray-50'
|
||||
}`}
|
||||
onClick={() => exists && template && onUseTemplate(template)}
|
||||
>
|
||||
<div className="font-medium text-gray-900 truncate">{rec.label}</div>
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
{exists ? (
|
||||
<span className="text-purple-600">Vorlage verfuegbar</span>
|
||||
) : (
|
||||
<span className="text-gray-400">Noch nicht erstellt</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-gradient-to-br from-purple-50 to-white rounded-xl border border-purple-200 p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
Empfohlene Dokumente fuer Ihr Unternehmen
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
Basierend auf Ihrem Compliance-Profil ({getProfileLabel(level)})
|
||||
</p>
|
||||
</div>
|
||||
<span className="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-purple-100 text-purple-700">
|
||||
{level}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Required */}
|
||||
{required.length > 0 && (
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span className="text-sm font-medium text-red-700">Pflicht</span>
|
||||
<span className="text-xs text-gray-400">({required.length})</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-2">
|
||||
{required.map(renderCard)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Recommended */}
|
||||
{recommended.length > 0 && (
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span className="text-sm font-medium text-amber-700">Empfohlen</span>
|
||||
<span className="text-xs text-gray-400">({recommended.length})</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-2">
|
||||
{recommended.map(renderCard)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Optional (collapsed by default) */}
|
||||
{optional.length > 0 && (
|
||||
<div>
|
||||
<button
|
||||
onClick={() => setShowOptional(!showOptional)}
|
||||
className="text-sm text-gray-500 hover:text-purple-600 flex items-center gap-1"
|
||||
>
|
||||
<span>{showOptional ? '▼' : '▶'}</span>
|
||||
<span>Optional ({optional.length})</span>
|
||||
</button>
|
||||
{showOptional && (
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-2 mt-2">
|
||||
{optional.map(renderCard)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { CATEGORIES } from './_constants'
|
||||
import { getGeneratorDefaults, getProfileLabel } from './scopeDefaults'
|
||||
import TemplateLibrary from './_components/TemplateLibrary'
|
||||
import GeneratorSection from './_components/GeneratorSection'
|
||||
import RecommendedDocuments from './_components/RecommendedDocuments'
|
||||
|
||||
function DocumentGeneratorPageInner() {
|
||||
const { state } = useSDK()
|
||||
@@ -191,6 +192,12 @@ function DocumentGeneratorPageInner() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Recommended documents based on scope profile */}
|
||||
<RecommendedDocuments
|
||||
allTemplates={allTemplates}
|
||||
onUseTemplate={handleUseTemplate}
|
||||
/>
|
||||
|
||||
<TemplateLibrary
|
||||
allTemplates={allTemplates}
|
||||
filteredTemplates={filteredTemplates}
|
||||
|
||||
Reference in New Issue
Block a user