feat(wizard): optimize step 3 layout and add category navigation
Some checks failed
Staging Build / build (push) Failing after 28s

This commit is contained in:
DanielS
2026-08-10 22:50:24 +02:00
parent be3b0f615d
commit ed654f542c
3 changed files with 229 additions and 143 deletions

View File

@@ -168,6 +168,7 @@ export function OrderWizard({
const [editingIdx, setEditingIdx] = useState<number | null>(null)
const [orderNotes, setOrderNotes] = useState<string>('')
const [toast, setToast] = useState<{ message: string; type: 'error' | 'success' } | null>(null)
const [activeCategoryId, setActiveCategoryId] = useState<string | null>(null)
useEffect(() => {
if (toast) {
@@ -235,6 +236,12 @@ export function OrderWizard({
})
}, [categories, selectedBillingInterval])
useEffect(() => {
if (visibleCategories.length > 0 && !visibleCategories.some(c => c.id === activeCategoryId)) {
setActiveCategoryId(visibleCategories[0].id)
}
}, [visibleCategories, activeCategoryId])
// Endkunden Filterung
const filteredEndCustomers = useMemo(() => {
const term = searchTerm.toLowerCase().trim()
@@ -843,8 +850,6 @@ export function OrderWizard({
nextStep={nextStep}
/>
</motion.div>
)}
{/* Step 3: Software Selector */}
{step === 3 && (
<motion.div
@@ -852,23 +857,76 @@ export function OrderWizard({
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
className="h-[calc(100vh-140px)] overflow-hidden"
>
<div className="grid grid-cols-1 lg:grid-cols-6 gap-6 items-start">
<div className="grid grid-cols-1 lg:grid-cols-6 gap-6 h-full items-stretch">
{/* FAR LEFT: License Lookup Panel (sticky) */}
<div className="lg:col-span-2 sticky top-6">
<LicenseLookupPanel />
</div>
{/* CENTER: Sticky stepper + scrollable categories */}
<div className="lg:col-span-2 flex flex-col max-h-[calc(100vh-4rem)] overflow-hidden">
{/* Sticky stepper header — only covers center column */}
<div className="shrink-0 bg-slate-950/80 backdrop-blur-md pb-4 pt-2 border-b border-white/5 mb-4">
<p className="text-center text-slate-400 text-sm mb-4">Schritt 3 von 4 Software konfigurieren</p>
<ProgressStepper step={step} basketItemsCount={basketItems.length} />
{/* LEFT: Categories Sidebar Menu */}
<div className="lg:col-span-2 flex flex-col justify-start pr-4 space-y-6">
<div>
<h1 className="text-3xl font-extrabold tracking-tight mb-2 text-gradient">
Software konfigurieren
</h1>
<p className="text-slate-400 text-sm">
Klicken Sie sich durch die Kategorien und wählen Sie Ihre Lizenzen.
</p>
</div>
{/* Scrollable category content area */}
<div className="flex flex-col gap-2">
{visibleCategories.map((cat) => {
const sel = selections[cat.id]
const hasSelection = sel?.productIds?.length > 0 || !!sel?.productId
const isRequired = cat.is_required
const isActive = activeCategoryId === cat.id
return (
<button
key={cat.id}
onClick={() => setActiveCategoryId(cat.id)}
className={`flex items-center gap-3 p-3.5 rounded-xl border transition-all duration-300 text-left relative overflow-hidden group ${
isActive
? 'bg-primary/10 border-primary text-white shadow-[0_0_20px_rgba(59,130,246,0.15)]'
: 'bg-white/5 border-white/5 text-slate-400 hover:bg-white/10 hover:border-white/10'
}`}
>
{/* Left glow line for active category */}
{isActive && (
<div className="absolute left-0 top-0 bottom-0 w-1 bg-primary" />
)}
<div className={`p-2 rounded-lg transition-colors ${
isActive ? 'bg-primary/20 text-primary' : 'bg-white/5 text-slate-400 group-hover:text-white'
}`}>
<CategoryIcon icon={cat.icon} className="w-4 h-4" />
</div>
<div className="flex-1 min-w-0">
<p className={`text-sm font-semibold truncate ${isActive ? 'text-white' : 'text-slate-300 group-hover:text-white'}`}>
{cat.name}
</p>
<p className="text-[11px] text-slate-500 truncate mt-0.5">
{cat.description || 'Optionen ansehen'}
</p>
</div>
{/* Status indicators */}
{hasSelection ? (
<div className="w-5 h-5 rounded-full bg-green-500/20 border border-green-500/35 flex items-center justify-center text-green-400 shrink-0">
<Check className="w-3 h-3" />
</div>
) : isRequired ? (
<div className="w-2 h-2 rounded-full bg-red-500 shrink-0 animate-pulse" />
) : (
<span className="text-[10px] text-slate-600 uppercase tracking-wider shrink-0 font-medium">Opt</span>
)}
</button>
)
})}
</div>
</div>
{/* CENTER: Scrollable Category Selection */}
<div className="lg:col-span-2 flex flex-col h-full overflow-hidden border-x border-white/5 px-4">
<div className="flex-1 overflow-y-auto pr-2 subpixel-antialiased scrollbar-thin scrollbar-thumb-white/10 scrollbar-track-transparent">
{/* Upgrade-Modus Hinweis-Banner */}
{upgradeMode && lockedDeviceId && (
@@ -895,46 +953,59 @@ export function OrderWizard({
billingLabel={billingLabel}
billingBadgeClass={billingBadgeClass}
existingModuleIds={existingModuleIds}
activeCategoryId={activeCategoryId}
/>
</div>
</div>
{/* RIGHT: Summary sidebar (sticky) */}
<div className="lg:col-span-2 sticky top-6">
<SummarySidebar
visibleCategories={visibleCategories}
selections={selections}
products={products}
moduleQuantities={moduleQuantities}
billingLabel={billingLabel}
oneTimeTotal={oneTimeTotal}
monthlyTotal={monthlyTotal}
oneTimeNet={oneTimeNet}
oneTimeTax={oneTimeTax}
oneTimeGross={oneTimeGross}
monthlyNet={monthlyNet}
monthlyTax={monthlyTax}
monthlyGross={monthlyGross}
updatePriceModifier={updatePriceModifier}
allCategoriesFilled={allCategoriesFilled}
productValidationErrors={productValidationErrors}
basketItems={basketItems}
editingIdx={editingIdx}
editBasketItem={editBasketItem}
deleteBasketItem={deleteBasketItem}
deviceName={deviceName}
setDeviceName={setDeviceName}
addToBasket={addToBasket}
isNextStepDisabled={isNextStepDisabled}
hasActiveSelection={hasActiveSelection}
nextStep={nextStep}
prevStep={prevStep}
/>
{/* RIGHT: Stepper Header + Summary sidebar */}
<div className="lg:col-span-2 flex flex-col h-full overflow-hidden pl-4">
{/* Steps indicator at the top right */}
<div className="shrink-0 pb-4 border-b border-white/5 mb-4">
<p className="text-right text-slate-400 text-xs mb-2">Schritt 3 von 4 Software konfigurieren</p>
<ProgressStepper step={step} basketItemsCount={basketItems.length} />
</div>
<div className="flex-1 overflow-y-auto pr-1">
<SummarySidebar
visibleCategories={visibleCategories}
selections={selections}
products={products}
moduleQuantities={moduleQuantities}
billingLabel={billingLabel}
oneTimeTotal={oneTimeTotal}
monthlyTotal={monthlyTotal}
oneTimeNet={oneTimeNet}
oneTimeTax={oneTimeTax}
oneTimeGross={oneTimeGross}
monthlyNet={monthlyNet}
monthlyTax={monthlyTax}
monthlyGross={monthlyGross}
updatePriceModifier={updatePriceModifier}
allCategoriesFilled={allCategoriesFilled}
productValidationErrors={productValidationErrors}
basketItems={basketItems}
editingIdx={editingIdx}
editBasketItem={editBasketItem}
deleteBasketItem={deleteBasketItem}
deviceName={deviceName}
setDeviceName={setDeviceName}
addToBasket={addToBasket}
isNextStepDisabled={isNextStepDisabled}
hasActiveSelection={hasActiveSelection}
nextStep={nextStep}
prevStep={prevStep}
/>
</div>
</div>
</div>
</motion.div>
)}
{/* Step 4: Verification & Submit */}
{step === 4 && (
<motion.div