feat: F008 AI need builder — full spec compliance (editable criteria, grouped sections, needService.create)

- NeedInput: Reset button, Asset-Type Quick Select chips, 3 typed examples
- ExtractedFieldRow: inline edit mode (pencil → TextField, Enter/blur commits)
- CriteriaReviewPanel: 3 grouped sections (Hard Facts, Soft Factors, Must-haves) + AI Assumptions + Missing Information; all 14 criteria fields shown and editable; onCriteriaChange propagates typed edits to parent
- FollowUpPanel: Rückfragen neu generieren button triggers onReparse
- NeedCardPreview: Need Title input, Confidence Summary with progress bar, Missing Critical Fields alert, low-confidence draft label
- AISearch page: editedCriteria state (mutable copy of parse result), handleReparse via generateFollowUpQuestions, handleSave calls needService.create() with full CreateNeedInput mapping, low-confidence → status DRAFT

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-16 12:35:57 +02:00
parent 73a6a167e0
commit 93b8000d48
6 changed files with 489 additions and 142 deletions
+17 -6
View File
@@ -1,5 +1,5 @@
import { Box, Button, Card, Typography } from '@mui/material'
import { ArrowRight } from 'lucide-react'
import { ArrowRight, RefreshCw } from 'lucide-react'
import type { FollowUpQuestion } from '../../domain/needBuilder'
import { FollowUpQuestionCard } from './FollowUpQuestionCard'
@@ -8,9 +8,10 @@ interface Props {
answers: Record<string, string>
onAnswer: (id: string, answer: string) => void
onContinue: () => void
onReparse?: () => void
}
export function FollowUpPanel({ questions, answers, onAnswer, onContinue }: Props) {
export function FollowUpPanel({ questions, answers, onAnswer, onContinue, onReparse }: Props) {
const requiredUnanswered = questions
.filter(q => q.importance === 'required')
.filter(q => !answers[q.id])
@@ -27,7 +28,7 @@ export function FollowUpPanel({ questions, answers, onAnswer, onContinue }: Prop
Rückfragen der KI
</Typography>
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mb: 2 }}>
Beantworten Sie die Pflichtfelder für optimale Ergebnisse.
Beantworten Sie die Pflichtfelder für optimale Ergebnisse. Optionale Fragen können übersprungen werden.
</Typography>
<Box sx={{ flex: 1, overflow: 'auto' }}>
@@ -43,12 +44,22 @@ export function FollowUpPanel({ questions, answers, onAnswer, onContinue }: Prop
</Box>
</Box>
<Box sx={{ pt: 2, mt: 'auto' }}>
<Box sx={{ pt: 2, mt: 'auto', display: 'flex', flexDirection: 'column', gap: 1 }}>
{requiredUnanswered.length > 0 && (
<Typography variant="caption" color="error" sx={{ display: 'block', mb: 1 }}>
{requiredUnanswered.length} Pflichtfeld{requiredUnanswered.length > 1 ? 'er' : ''} fehlt noch.
<Typography variant="caption" color="error">
{requiredUnanswered.length} Pflichtfeld{requiredUnanswered.length > 1 ? 'er fehlen' : ' fehlt'} noch.
</Typography>
)}
{onReparse && (
<Button
variant="outlined"
size="small"
startIcon={<RefreshCw size={14} />}
onClick={onReparse}
>
Rückfragen neu generieren
</Button>
)}
<Button
variant="contained"
fullWidth