feat: split WeightingEditor into Harte / Weiche Kriterien sections
Hard criteria (Fläche, Standort, Budget, Verfügbarkeit) in dark blue, soft criteria (Prestige, Erreichbarkeit, …) in purple — separated by a divider with group labels. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,16 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Box, Button, Card, Slider, Typography } from '@mui/material'
|
import { Box, Button, Card, Divider, Slider, Typography } from '@mui/material'
|
||||||
import { RotateCcw } from 'lucide-react'
|
import { RotateCcw } from 'lucide-react'
|
||||||
import { WEIGHTING_KEYS, WEIGHTING_LABELS } from '../../domain/needBuilder'
|
import { WEIGHTING_LABELS } from '../../domain/needBuilder'
|
||||||
import type { WeightingKey } from '../../domain/needBuilder'
|
import type { WeightingKey } from '../../domain/needBuilder'
|
||||||
import { weightingService } from '../../services/weightingService'
|
import { weightingService } from '../../services/weightingService'
|
||||||
|
|
||||||
|
const HARD_KEYS: WeightingKey[] = ['area', 'location', 'budget', 'timing']
|
||||||
|
const SOFT_KEYS: WeightingKey[] = [
|
||||||
|
'prestige', 'accessibility', 'expansionPotential', 'flexibility',
|
||||||
|
'visibility', 'footfall', 'talentAccess', 'esg', 'taxEnvironment',
|
||||||
|
]
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
weights: Record<WeightingKey, number>
|
weights: Record<WeightingKey, number>
|
||||||
onChange: (weights: Record<WeightingKey, number>) => void
|
onChange: (weights: Record<WeightingKey, number>) => void
|
||||||
@@ -28,6 +34,44 @@ function rawToWeights(raw: Record<WeightingKey, number>): Record<WeightingKey, n
|
|||||||
) as Record<WeightingKey, number>
|
) as Record<WeightingKey, number>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SliderGroup({
|
||||||
|
label, keys, raw, onSlider, color,
|
||||||
|
}: {
|
||||||
|
label: string
|
||||||
|
keys: WeightingKey[]
|
||||||
|
raw: Record<WeightingKey, number>
|
||||||
|
onSlider: (key: WeightingKey, v: number) => void
|
||||||
|
color: string
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Typography variant="caption" sx={{ fontWeight: 700, color, textTransform: 'uppercase', letterSpacing: 0.5, fontSize: '0.65rem', display: 'block', mb: 1.5 }}>
|
||||||
|
{label}
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px 32px' }}>
|
||||||
|
{keys.map(key => {
|
||||||
|
const importance = raw[key] ?? 3
|
||||||
|
return (
|
||||||
|
<Box key={key}>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 0.5 }}>
|
||||||
|
<Typography variant="body2" sx={{ fontWeight: 500 }}>{WEIGHTING_LABELS[key]}</Typography>
|
||||||
|
<Typography variant="caption" color="text.secondary">{IMPORTANCE_LABELS[importance]}</Typography>
|
||||||
|
</Box>
|
||||||
|
<Slider
|
||||||
|
value={importance}
|
||||||
|
min={1} max={5} step={1} marks
|
||||||
|
onChange={(_, v) => onSlider(key, v as number)}
|
||||||
|
size="small"
|
||||||
|
sx={{ color }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function WeightingEditor({ weights, onChange, assetType }: Props) {
|
export function WeightingEditor({ weights, onChange, assetType }: Props) {
|
||||||
const [raw, setRaw] = useState<Record<WeightingKey, number>>(() => toRaw(weights))
|
const [raw, setRaw] = useState<Record<WeightingKey, number>>(() => toRaw(weights))
|
||||||
|
|
||||||
@@ -65,33 +109,9 @@ export function WeightingEditor({ weights, onChange, assetType }: Props) {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Card sx={{ p: 3 }}>
|
<Card sx={{ p: 3 }}>
|
||||||
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px 32px' }}>
|
<SliderGroup label="Harte Kriterien" keys={HARD_KEYS} raw={raw} onSlider={handleSlider} color="#1e3a5f" />
|
||||||
{WEIGHTING_KEYS.map(key => {
|
<Divider sx={{ my: 2.5 }} />
|
||||||
const importance = raw[key] ?? 3
|
<SliderGroup label="Weiche Kriterien" keys={SOFT_KEYS} raw={raw} onSlider={handleSlider} color="#7c3aed" />
|
||||||
return (
|
|
||||||
<Box key={key}>
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 0.5 }}>
|
|
||||||
<Typography variant="body2" sx={{ fontWeight: 500 }}>
|
|
||||||
{WEIGHTING_LABELS[key]}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="caption" color="text.secondary">
|
|
||||||
{IMPORTANCE_LABELS[importance]}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Slider
|
|
||||||
value={importance}
|
|
||||||
min={1}
|
|
||||||
max={5}
|
|
||||||
step={1}
|
|
||||||
marks
|
|
||||||
onChange={(_, v) => handleSlider(key, v as number)}
|
|
||||||
size="small"
|
|
||||||
sx={{ color: '#1e3a5f' }}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</Box>
|
|
||||||
</Card>
|
</Card>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user