feat: F015 shortlist management
3-panel Shortlists page with left list, center detail, right decision brief panel. AddToShortlistDialog wired into result feed cards and match detail. Full DRAFT→REVIEW_READY→FINALIZED workflow, AI-generated decision brief draft, duplicate prevention, and compare-view integration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,45 @@ import type { AssetType } from '../domain/enums'
|
||||
import type { ParseNeedResult, ParsedNeedCriteria, FollowUpQuestion } from '../domain/needBuilder'
|
||||
import type { UnifiedMatchResult } from '../domain/unifiedResult'
|
||||
|
||||
// ── Decision Brief ────────────────────────────────────────────────────────────
|
||||
|
||||
export interface DecisionBrief {
|
||||
id: string
|
||||
shortlistId: string
|
||||
summary: string
|
||||
sections: { title: string; body: string }[]
|
||||
generatedAt: string
|
||||
isDraft: true
|
||||
}
|
||||
|
||||
function buildMockDecisionBrief(shortlistId: string): DecisionBrief {
|
||||
return {
|
||||
id: crypto.randomUUID(),
|
||||
shortlistId,
|
||||
summary: 'Die Shortlist enthält qualitativ hochwertige Matches mit starker Standortübereinstimmung. Die verfügbaren Flächen decken den Bedarf gut ab. Zwei Objekte eignen sich als Erstbesichtigungen.',
|
||||
sections: [
|
||||
{
|
||||
title: 'Zusammenfassung der Objekte',
|
||||
body: 'Die Shortlist umfasst mehrere Objekte aus dem verifizierten Portfolio. Die Matchscores liegen zwischen 74 und 88, was auf eine gute bis sehr gute Übereinstimmung mit den Suchkriterien hinweist.',
|
||||
},
|
||||
{
|
||||
title: 'Standortbewertung',
|
||||
body: 'Die Mehrheit der Objekte befindet sich in bevorzugten Lagen. Die ÖV-Anbindung ist bei allen Objekten als gut bis sehr gut einzustufen.',
|
||||
},
|
||||
{
|
||||
title: 'Budgetanalyse',
|
||||
body: 'Die Mietpreise liegen im budgetkonformen Bereich. Keine der Optionen überschreitet das maximale Budget pro m².',
|
||||
},
|
||||
{
|
||||
title: 'Empfohlene nächste Schritte',
|
||||
body: '1. Besichtigung der Top-2-Objekte vereinbaren. 2. Detaillierte Flächenpläne anfordern. 3. Vertragskonditionen prüfen lassen.',
|
||||
},
|
||||
],
|
||||
generatedAt: new Date().toISOString(),
|
||||
isDraft: true,
|
||||
}
|
||||
}
|
||||
|
||||
// ── Compare Summary ───────────────────────────────────────────────────────────
|
||||
|
||||
export interface ComparisonSummary {
|
||||
@@ -284,7 +323,7 @@ function mockParseNeed(input: string): ParseNeedResult {
|
||||
timing,
|
||||
mustHaveCriteria,
|
||||
infrastructureRequirements: [],
|
||||
accessibilityRequirements: mustHaveCriteria.includes('Gute ÖV-Anbindung') ? ['ÖV-Anbindung'] : [],
|
||||
accessibilityRequirements: [],
|
||||
prestigeImportance,
|
||||
flexibilityNeed: lower.includes('flexibel') ? 'HIGH' : 'MEDIUM',
|
||||
expansionPotential: lower.includes('wachstum') || lower.includes('expansion'),
|
||||
@@ -377,4 +416,9 @@ export const aiService = {
|
||||
const result = mockParseNeed(JSON.stringify(criteria))
|
||||
return { data: result.followUpQuestionCandidates }
|
||||
},
|
||||
|
||||
async generateDecisionBrief(shortlistId: string): Promise<ItemResponse<DecisionBrief>> {
|
||||
await new Promise(r => setTimeout(r, 1800))
|
||||
return { data: buildMockDecisionBrief(shortlistId) }
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user