feat: role-aware UX, market intelligence, score fix & layout scroll

- Role-based workspace access: Property Manager gets Supply+Demand,
  Operations restricted to Super Admin + Reviewer only
- Root redirect routes each persona to their first workspace
- Match Center: replaced 3-panel with auto-sorted flat list + drawer
- AI Search: unified form with dual-action (Jetzt suchen / Als Suchprofil speichern)
- CompareTray hidden on non-Demand routes; Vergleichen removed from Supply
- LocationIntelligencePanel: city KPIs, rent trends, soft factors, comparables
- NegotiationInsightsPanel: price positioning, active demand, selling arguments
- scoreCalculator: cap hardMatchScore and softFactorScore to max 100
- Layout: add display:flex to overflow:hidden wrappers so inner scroll works
  (MatchCenter drawer, MarketIntelligence, SourceMonitoring, SignalPipeline)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-17 00:58:10 +02:00
parent e13fe470fb
commit efc406ace9
37 changed files with 4871 additions and 459 deletions
+2 -2
View File
@@ -353,7 +353,7 @@ export function calculateScore(need: Need, property: Property): MatchEngineOutpu
]
const hardWeightSum = HARD_CRITERION_KEYS.reduce((s, k) => s + profile[k], 0)
const hardRaw = hardFactors.reduce((s, f) => s + f.contribution, 0)
const hardMatchScore = hardWeightSum > 0 ? Math.round(hardRaw / hardWeightSum) : 0
const hardMatchScore = hardWeightSum > 0 ? Math.min(100, Math.round(hardRaw / hardWeightSum)) : 0
// ── Soft factor scoring ────────────────────────────────────────────────────
const softFactors: ScoreFactor[] = SOFT_FACTOR_KEYS
@@ -361,7 +361,7 @@ export function calculateScore(need: Need, property: Property): MatchEngineOutpu
.map(k => scoreSoftFactor(k, profile[k], property))
const softWeightSum = SOFT_FACTOR_KEYS.reduce((s, k) => s + (profile[k] ?? 0), 0)
const softRaw = softFactors.reduce((s, f) => s + f.contribution, 0)
const softFactorScore = softWeightSum > 0 ? Math.round(softRaw / softWeightSum) : 50
const softFactorScore = softWeightSum > 0 ? Math.min(100, Math.round(softRaw / softWeightSum)) : 50
// ── Modifiers ──────────────────────────────────────────────────────────────
const dqMod = calcDataQualityModifier(property)