feat: F008 AI need builder — smart parse, criteria review, follow-up & weighting
Domain: ParsedNeedCriteria, FollowUpQuestion, ParseNeedResult, NeedBuilderStep, WEIGHTING_KEYS/LABELS. Services: weightingService (asset-type profiles), aiService extended with parseNeed (keyword extraction mock, 1.4s delay) and generateFollowUpQuestions. Components: NeedBuilderProgress, NeedInput, ConfidenceFieldBadge, ExtractedFieldRow, CriteriaReviewPanel, FollowUpQuestionCard, FollowUpPanel, WeightingEditor, NeedCardPreview, NeedBuilderErrorState. Page: AINeedBuilderPage replaces AISearch with 4-step flow (input → review → weighting → save). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import type { AssetType } from './enums'
|
||||
|
||||
export interface ParsedNeedCriteria {
|
||||
assetType?: AssetType
|
||||
areaRange?: { min: number; max: number }
|
||||
preferredLocations?: string[]
|
||||
budgetRange?: { maxPerSqm: number; maxMonthlyTotal?: number; currency: string }
|
||||
timing?: { earliestMoveIn: string; latestMoveIn?: string; contractDurationMonths?: number; flexibleTiming: boolean }
|
||||
mustHaveCriteria?: string[]
|
||||
softFactors?: { minPrestige?: number; requireParking?: boolean; maxPublicTransportMinutes?: number; requireHighVisibility?: boolean }
|
||||
infrastructureRequirements?: string[]
|
||||
accessibilityRequirements?: string[]
|
||||
prestigeImportance?: 'LOW' | 'MEDIUM' | 'HIGH'
|
||||
flexibilityNeed?: 'LOW' | 'MEDIUM' | 'HIGH'
|
||||
expansionPotential?: boolean
|
||||
parkingNeed?: boolean
|
||||
visibilityNeed?: 'LOW' | 'MEDIUM' | 'HIGH'
|
||||
footfallNeed?: 'LOW' | 'MEDIUM' | 'HIGH'
|
||||
companyName?: string
|
||||
notes?: string
|
||||
}
|
||||
|
||||
export interface FollowUpQuestion {
|
||||
id: string
|
||||
questionText: string
|
||||
targetField: string
|
||||
reason: string
|
||||
suggestedAnswerOptions?: string[]
|
||||
importance: 'required' | 'recommended' | 'optional'
|
||||
}
|
||||
|
||||
export interface ParseNeedResult {
|
||||
extractedCriteria: ParsedNeedCriteria
|
||||
confidenceByField: Record<string, number>
|
||||
missingFields: string[]
|
||||
assumptions: string[]
|
||||
suggestedWeights: Record<string, number>
|
||||
followUpQuestionCandidates: FollowUpQuestion[]
|
||||
rawSummary: string
|
||||
promptVersion: string
|
||||
schemaVersion: string
|
||||
}
|
||||
|
||||
export const NeedBuilderStep = {
|
||||
IDLE: 'idle',
|
||||
PARSING: 'parsing',
|
||||
PARSED_REQUIRES_REVIEW: 'parsed_requires_review',
|
||||
CLARIFICATION_REQUIRED: 'clarification_required',
|
||||
WEIGHTING_REVIEW: 'weighting_review',
|
||||
READY_TO_SAVE: 'ready_to_save',
|
||||
SAVING: 'saving',
|
||||
SAVED: 'saved',
|
||||
ERROR: 'error',
|
||||
} as const
|
||||
export type NeedBuilderStep = typeof NeedBuilderStep[keyof typeof NeedBuilderStep]
|
||||
|
||||
export const WEIGHTING_KEYS = ['area', 'location', 'budget', 'timing', 'prestige', 'accessibility', 'expansionPotential', 'flexibility'] as const
|
||||
export type WeightingKey = typeof WEIGHTING_KEYS[number]
|
||||
|
||||
export const WEIGHTING_LABELS: Record<WeightingKey, string> = {
|
||||
area: 'Fläche',
|
||||
location: 'Standort',
|
||||
budget: 'Budget',
|
||||
timing: 'Verfügbarkeit',
|
||||
prestige: 'Prestige',
|
||||
accessibility: 'Erreichbarkeit',
|
||||
expansionPotential: 'Expansionspotenzial',
|
||||
flexibility: 'Flexibilität',
|
||||
}
|
||||
Reference in New Issue
Block a user