refactor: split AppShell, match-detail panels, extract useCompareData
AppShell.tsx: 627→116 lines - appShellConfig.ts: NavItem/WorkspaceConfig types, WORKSPACE_CONFIG, nav helpers - AppShellSidebar.tsx: Sidebar component with visual constants - AppShellTopBar.tsx: TopBar component Match-detail panels: - ScoreBreakdownPanel: 377→303 lines (scoreBreakdownConstants.ts + CriterionRow.tsx extracted) - LocationIntelligencePanel: 383→333 lines (SoftFactorBar.tsx extracted) - FutureAvailabilityContextPanel: 386→351 lines (futureAvailabilityConstants.tsx extracted) Compare.tsx: 485→441 lines - useCompareData hook: all queries and derived state extracted to hooks/useCompareData.ts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import { WorkspaceType } from '../../domain/enums'
|
||||
import type { LucideIcon } from 'lucide-react'
|
||||
import {
|
||||
LayoutDashboard,
|
||||
Building2,
|
||||
Target,
|
||||
CheckSquare,
|
||||
Search,
|
||||
List,
|
||||
Columns2,
|
||||
ClipboardList,
|
||||
Radar,
|
||||
MessageSquare,
|
||||
Kanban,
|
||||
BellRing,
|
||||
Plus,
|
||||
} from 'lucide-react'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface NavItem {
|
||||
path: string
|
||||
label: string
|
||||
icon: LucideIcon
|
||||
}
|
||||
|
||||
export interface WorkspaceConfig {
|
||||
label: string
|
||||
abbreviation: string
|
||||
icon: LucideIcon
|
||||
firstPath: string
|
||||
navItems: NavItem[]
|
||||
chipColor: string
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Workspace configuration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export const WORKSPACE_CONFIG: Record<WorkspaceType, WorkspaceConfig> = {
|
||||
[WorkspaceType.SUPPLY]: {
|
||||
label: 'Verwaltung',
|
||||
abbreviation: 'VW',
|
||||
icon: Building2,
|
||||
firstPath: '/supply/dashboard',
|
||||
chipColor: '#1e3a5f',
|
||||
navItems: [
|
||||
{ path: '/supply/dashboard', label: 'Übersicht', icon: LayoutDashboard },
|
||||
{ path: '/supply/properties', label: 'Meine Objekte', icon: Building2 },
|
||||
{ path: '/supply/reminder-manager', label: 'Reminder Manager', icon: BellRing },
|
||||
{ path: '/supply/market-leads', label: 'Markt-Leads', icon: Target },
|
||||
{ path: '/supply/anfragen', label: 'Anfragencenter', icon: MessageSquare },
|
||||
{ path: '/supply/data-quality', label: 'Datenpflege', icon: CheckSquare },
|
||||
{ path: '/supply/market-intelligence', label: 'Markt Intelligence', icon: Radar },
|
||||
{ path: '/supply/my-listings', label: 'Meine Inserate', icon: ClipboardList },
|
||||
{ path: '/supply/new-listing', label: 'Neues Inserat', icon: Plus },
|
||||
],
|
||||
},
|
||||
[WorkspaceType.DEMAND]: {
|
||||
label: 'Suche',
|
||||
abbreviation: 'SU',
|
||||
icon: Search,
|
||||
firstPath: '/demand/ai-search',
|
||||
chipColor: '#1a7a4a',
|
||||
navItems: [
|
||||
{ path: '/demand/ai-search', label: 'Flächensuche', icon: Search },
|
||||
{ path: '/demand/results', label: 'Ergebnisse', icon: List },
|
||||
{ path: '/demand/compare', label: 'Vergleich', icon: Columns2 },
|
||||
{ path: '/demand/pipeline', label: 'Deal Pipeline', icon: Kanban },
|
||||
{ path: '/demand/anfragen', label: 'Anfragen', icon: MessageSquare },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
// Ordered list for rendering workspace tabs
|
||||
export const WORKSPACE_ORDER: WorkspaceType[] = [
|
||||
WorkspaceType.SUPPLY,
|
||||
WorkspaceType.DEMAND,
|
||||
]
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function getWorkspaceFromPath(pathname: string): WorkspaceType | null {
|
||||
if (pathname.startsWith('/supply')) return WorkspaceType.SUPPLY
|
||||
if (pathname.startsWith('/demand')) return WorkspaceType.DEMAND
|
||||
return null
|
||||
}
|
||||
|
||||
export function getPageNameFromPath(pathname: string): string {
|
||||
for (const ws of Object.values(WORKSPACE_CONFIG)) {
|
||||
for (const item of ws.navItems) {
|
||||
if (item.path === pathname) return item.label
|
||||
}
|
||||
}
|
||||
if (/^\/demand\/results\/.+/.test(pathname)) return 'Match Detail'
|
||||
if (/^\/demand\/property\//.test(pathname)) return 'Objekt Detail'
|
||||
if (/^\/supply\/properties\/.+/.test(pathname)) return 'Objekt Detail'
|
||||
if (pathname === '/demand/anfragen') return 'Anfragen'
|
||||
const segment = pathname.split('/').filter(Boolean).pop() ?? ''
|
||||
return segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' ')
|
||||
}
|
||||
|
||||
export function getUserInitials(name: string): string {
|
||||
return name
|
||||
.split(' ')
|
||||
.map((n) => n[0])
|
||||
.join('')
|
||||
.toUpperCase()
|
||||
.slice(0, 2)
|
||||
}
|
||||
Reference in New Issue
Block a user