feat: split step-3 into independent left/right grid columns with sticky local stepper
All checks were successful
Staging Build / build (push) Successful in 2m51s
All checks were successful
Staging Build / build (push) Successful in 2m51s
This commit is contained in:
@@ -15,6 +15,7 @@ import { StepCustomer } from './wizard/step-customer'
|
|||||||
import { StepBilling } from './wizard/step-billing'
|
import { StepBilling } from './wizard/step-billing'
|
||||||
import { StepSoftware } from './wizard/step-software'
|
import { StepSoftware } from './wizard/step-software'
|
||||||
import { StepSummary } from './wizard/step-summary'
|
import { StepSummary } from './wizard/step-summary'
|
||||||
|
import { SummarySidebar } from './wizard/summary-sidebar'
|
||||||
|
|
||||||
type CategorySelection = {
|
type CategorySelection = {
|
||||||
productId: string | null // selected product in this category
|
productId: string | null // selected product in this category
|
||||||
@@ -664,16 +665,9 @@ export function OrderWizard({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-5xl mx-auto px-4">
|
<div className="max-w-5xl mx-auto px-4">
|
||||||
{/* Sticky Header — Stepper für Step 3, normaler Stepper für andere Steps */}
|
{/* Global Stepper for Steps 1, 2, 4 */}
|
||||||
{step === 3 ? (
|
{step !== 3 && (
|
||||||
<div className="sticky top-0 z-30 w-full bg-slate-950/80 backdrop-blur-md pt-6 pb-4 border-b border-white/5 mb-6">
|
<div className="pt-12 pb-6">
|
||||||
<div className="text-center mb-4">
|
|
||||||
<p className="text-slate-400 text-sm">Schritt 3 von 4 — Software konfigurieren</p>
|
|
||||||
</div>
|
|
||||||
<ProgressStepper step={step} basketItemsCount={basketItems.length} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="pt-12">
|
|
||||||
<ProgressStepper step={step} basketItemsCount={basketItems.length} />
|
<ProgressStepper step={step} basketItemsCount={basketItems.length} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -739,8 +733,17 @@ export function OrderWizard({
|
|||||||
initial={{ opacity: 0, x: 20 }}
|
initial={{ opacity: 0, x: 20 }}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
animate={{ opacity: 1, x: 0 }}
|
||||||
exit={{ opacity: 0, x: -20 }}
|
exit={{ opacity: 0, x: -20 }}
|
||||||
className="space-y-6"
|
|
||||||
>
|
>
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 items-start">
|
||||||
|
{/* LEFT: Sticky header (stepper) + scrollable categories */}
|
||||||
|
<div className="lg:col-span-2 space-y-0">
|
||||||
|
{/* Sticky stepper header — only covers left column */}
|
||||||
|
<div className="sticky top-0 z-20 bg-slate-950/80 backdrop-blur-md pb-4 pt-6 border-b border-white/5 mb-6">
|
||||||
|
<p className="text-center text-slate-400 text-sm mb-4">Schritt 3 von 4 — Software konfigurieren</p>
|
||||||
|
<ProgressStepper step={step} basketItemsCount={basketItems.length} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Scrollable category content */}
|
||||||
<StepSoftware
|
<StepSoftware
|
||||||
visibleCategories={visibleCategories}
|
visibleCategories={visibleCategories}
|
||||||
products={products}
|
products={products}
|
||||||
@@ -754,6 +757,17 @@ export function OrderWizard({
|
|||||||
selectedBillingInterval={selectedBillingInterval}
|
selectedBillingInterval={selectedBillingInterval}
|
||||||
billingLabel={billingLabel}
|
billingLabel={billingLabel}
|
||||||
billingBadgeClass={billingBadgeClass}
|
billingBadgeClass={billingBadgeClass}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* RIGHT: Independent full-height sidebar column */}
|
||||||
|
<div className="lg:col-span-1 sticky top-6">
|
||||||
|
<SummarySidebar
|
||||||
|
visibleCategories={visibleCategories}
|
||||||
|
selections={selections}
|
||||||
|
products={products}
|
||||||
|
moduleQuantities={moduleQuantities}
|
||||||
|
billingLabel={billingLabel}
|
||||||
oneTimeTotal={oneTimeTotal}
|
oneTimeTotal={oneTimeTotal}
|
||||||
monthlyTotal={monthlyTotal}
|
monthlyTotal={monthlyTotal}
|
||||||
oneTimeNet={oneTimeNet}
|
oneTimeNet={oneTimeNet}
|
||||||
@@ -777,6 +791,8 @@ export function OrderWizard({
|
|||||||
nextStep={nextStep}
|
nextStep={nextStep}
|
||||||
prevStep={prevStep}
|
prevStep={prevStep}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { Badge } from '@/components/ui/badge'
|
|||||||
import { ShoppingCart, Check, AlertCircle } from 'lucide-react'
|
import { ShoppingCart, Check, AlertCircle } from 'lucide-react'
|
||||||
import * as Icons from 'lucide-react'
|
import * as Icons from 'lucide-react'
|
||||||
import { Category, Product, CategorySelection } from '@/lib/types'
|
import { Category, Product, CategorySelection } from '@/lib/types'
|
||||||
import { SummarySidebar } from './summary-sidebar'
|
|
||||||
|
|
||||||
interface StepSoftwareProps {
|
interface StepSoftwareProps {
|
||||||
visibleCategories: Category[]
|
visibleCategories: Category[]
|
||||||
@@ -26,30 +25,6 @@ interface StepSoftwareProps {
|
|||||||
selectedBillingInterval: 'one_time' | 'monthly'
|
selectedBillingInterval: 'one_time' | 'monthly'
|
||||||
billingLabel: (interval: string) => string
|
billingLabel: (interval: string) => string
|
||||||
billingBadgeClass: (interval: string) => string
|
billingBadgeClass: (interval: string) => string
|
||||||
|
|
||||||
// Sidebar props
|
|
||||||
oneTimeTotal: number
|
|
||||||
monthlyTotal: number
|
|
||||||
oneTimeNet: number
|
|
||||||
oneTimeTax: number
|
|
||||||
oneTimeGross: number
|
|
||||||
monthlyNet: number
|
|
||||||
monthlyTax: number
|
|
||||||
monthlyGross: number
|
|
||||||
updatePriceModifier: { label?: string }
|
|
||||||
allCategoriesFilled: boolean
|
|
||||||
productValidationErrors: string[]
|
|
||||||
basketItems: any[]
|
|
||||||
editingIdx: number | null
|
|
||||||
editBasketItem: (idx: number) => void
|
|
||||||
deleteBasketItem: (idx: number) => void
|
|
||||||
deviceName: string
|
|
||||||
setDeviceName: (name: string) => void
|
|
||||||
addToBasket: () => void
|
|
||||||
isNextStepDisabled: boolean
|
|
||||||
hasActiveSelection: boolean
|
|
||||||
nextStep: () => void
|
|
||||||
prevStep: () => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function CategoryIcon({ icon, className }: { icon?: string | null; className?: string }) {
|
function CategoryIcon({ icon, className }: { icon?: string | null; className?: string }) {
|
||||||
@@ -71,37 +46,12 @@ export function StepSoftware({
|
|||||||
selectedBillingInterval,
|
selectedBillingInterval,
|
||||||
billingLabel,
|
billingLabel,
|
||||||
billingBadgeClass,
|
billingBadgeClass,
|
||||||
oneTimeTotal,
|
|
||||||
monthlyTotal,
|
|
||||||
oneTimeNet,
|
|
||||||
oneTimeTax,
|
|
||||||
oneTimeGross,
|
|
||||||
monthlyNet,
|
|
||||||
monthlyTax,
|
|
||||||
monthlyGross,
|
|
||||||
updatePriceModifier,
|
|
||||||
allCategoriesFilled,
|
|
||||||
productValidationErrors,
|
|
||||||
basketItems,
|
|
||||||
editingIdx,
|
|
||||||
editBasketItem,
|
|
||||||
deleteBasketItem,
|
|
||||||
deviceName,
|
|
||||||
setDeviceName,
|
|
||||||
addToBasket,
|
|
||||||
isNextStepDisabled,
|
|
||||||
hasActiveSelection,
|
|
||||||
nextStep,
|
|
||||||
prevStep,
|
|
||||||
}: StepSoftwareProps) {
|
}: StepSoftwareProps) {
|
||||||
return (
|
return (
|
||||||
<div className="grid md:grid-cols-3 gap-6">
|
|
||||||
{/* Left: Category sections */}
|
|
||||||
<div className="md:col-span-2 space-y-8">
|
|
||||||
<Card className="glass-dark border-white/10">
|
<Card className="glass-dark border-white/10">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-2xl flex items-center gap-2 text-white">
|
<CardTitle className="text-2xl flex items-center gap-2 text-white">
|
||||||
<ShoppingCart className="w-6 h-6 text-primary" />
|
<ShoppingCart className="w-6 h-6 text-blue-400" />
|
||||||
Software wählen
|
Software wählen
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription className="text-slate-300">
|
<CardDescription className="text-slate-300">
|
||||||
@@ -341,38 +291,5 @@ export function StepSoftware({
|
|||||||
})}
|
})}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Right: Summary sidebar */}
|
|
||||||
<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>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,8 +69,7 @@ export function SummarySidebar({
|
|||||||
prevStep,
|
prevStep,
|
||||||
}: SummarySidebarProps) {
|
}: SummarySidebarProps) {
|
||||||
return (
|
return (
|
||||||
<div className="sticky top-32 max-h-[70vh] flex flex-col">
|
<Card className="glass-dark border-primary/20 backdrop-blur-lg bg-slate-950/75 flex flex-col max-h-[80vh]">
|
||||||
<Card className="glass-dark border-primary/20 backdrop-blur-lg bg-slate-950/75 flex flex-col max-h-[70vh]">
|
|
||||||
<CardHeader className="shrink-0">
|
<CardHeader className="shrink-0">
|
||||||
<CardTitle className="text-white">Zusammenfassung</CardTitle>
|
<CardTitle className="text-white">Zusammenfassung</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
@@ -294,6 +293,5 @@ export function SummarySidebar({
|
|||||||
</Button>
|
</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user