feat(my-customers): flatten orders into per-device cards
- split orders by device_name into FlattenedDevice entries - strip device prefixes from product and module names - render one DeviceCard per Kasse in the customer accordion - add targeted upgrade URL with orderId, customer_id, device_id - wizard starts at step 3 in upgrade mode with locked customer - mark existing licensed modules as read-only with badge
This commit is contained in:
@@ -74,6 +74,9 @@ export function OrderWizard({
|
||||
initialOrder,
|
||||
isAdmin = false,
|
||||
companies = [],
|
||||
upgradeMode = false,
|
||||
initialEndCustomerId = null,
|
||||
lockedDeviceId = null,
|
||||
}: {
|
||||
products: Product[]
|
||||
categories: Category[]
|
||||
@@ -82,9 +85,13 @@ export function OrderWizard({
|
||||
initialOrder?: Order | null
|
||||
isAdmin?: boolean
|
||||
companies?: any[]
|
||||
upgradeMode?: boolean
|
||||
initialEndCustomerId?: string | null
|
||||
lockedDeviceId?: string | null
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const [step, setStep] = useState(initialOrder ? 3 : 1)
|
||||
// Upgrade-Mode: starte direkt bei Schritt 3 (Software)
|
||||
const [step, setStep] = useState(upgradeMode ? 3 : (initialOrder ? 3 : 1))
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
const [selectedCompanyId, setSelectedCompanyId] = useState<string | null>(
|
||||
initialOrder?.company_id ?? (isAdmin ? 'all' : null)
|
||||
@@ -138,7 +145,26 @@ export function OrderWizard({
|
||||
}
|
||||
})
|
||||
})
|
||||
const [deviceName, setDeviceName] = useState<string>('')
|
||||
// Upgrade-Mode: Ermittle bereits lizenzierte Modul-IDs für die Ziel-Kasse
|
||||
const existingModuleIds = useMemo<string[]>(() => {
|
||||
if (!upgradeMode || !initialOrder || !lockedDeviceId) return []
|
||||
const items = initialOrder.order_data?.items || []
|
||||
const deviceItems = items.filter(
|
||||
(item: any) => (item.device_name || 'Kasse 1') === lockedDeviceId
|
||||
)
|
||||
const moduleIds: string[] = []
|
||||
deviceItems.forEach((item: any) => {
|
||||
item.selected_modules?.forEach((mod: any) => {
|
||||
if (mod.module_id) moduleIds.push(mod.module_id)
|
||||
})
|
||||
})
|
||||
return moduleIds
|
||||
}, [upgradeMode, initialOrder, lockedDeviceId])
|
||||
|
||||
const [deviceName, setDeviceName] = useState<string>(() => {
|
||||
if (upgradeMode && lockedDeviceId) return `${lockedDeviceId} – Upgrade`
|
||||
return ''
|
||||
})
|
||||
const [editingIdx, setEditingIdx] = useState<number | null>(null)
|
||||
const [toast, setToast] = useState<{ message: string; type: 'error' | 'success' } | null>(null)
|
||||
|
||||
@@ -154,11 +180,12 @@ export function OrderWizard({
|
||||
|
||||
// Endkunden-State
|
||||
const [endCustomers, setEndCustomers] = useState<EndCustomer[]>(initialEndCustomers)
|
||||
const [selectedEndCustomerId, setSelectedEndCustomerId] = useState<string | null>(
|
||||
initialOrder
|
||||
? initialOrder.end_customer_id
|
||||
: (initialEndCustomers.length > 0 ? initialEndCustomers[0].id : null)
|
||||
)
|
||||
const [selectedEndCustomerId, setSelectedEndCustomerId] = useState<string | null>(() => {
|
||||
// Upgrade-Mode: Kundenauswahl aus URL-Param
|
||||
if (upgradeMode && initialEndCustomerId) return initialEndCustomerId
|
||||
if (initialOrder) return initialOrder.end_customer_id
|
||||
return initialEndCustomers.length > 0 ? initialEndCustomers[0].id : null
|
||||
})
|
||||
const selectedEndCustomer = endCustomers.find(c => c.id === selectedEndCustomerId) ?? null
|
||||
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
@@ -751,6 +778,17 @@ export function OrderWizard({
|
||||
<ProgressStepper step={step} basketItemsCount={basketItems.length} />
|
||||
</div>
|
||||
|
||||
{/* Upgrade-Modus Hinweis-Banner */}
|
||||
{upgradeMode && lockedDeviceId && (
|
||||
<div className="mb-4 p-3 rounded-xl bg-primary/10 border border-primary/20 text-xs text-primary/90 flex items-start gap-2">
|
||||
<span className="text-primary mt-0.5">⚡</span>
|
||||
<div>
|
||||
<p className="font-semibold">Upgrade-Modus: {lockedDeviceId}</p>
|
||||
<p className="text-primary/70 mt-0.5">Bereits lizenzierte Module sind ausgegraut und können nicht doppelt gebucht werden.</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Scrollable category content */}
|
||||
<StepSoftware
|
||||
visibleCategories={visibleCategories}
|
||||
@@ -765,6 +803,7 @@ export function OrderWizard({
|
||||
selectedBillingInterval={selectedBillingInterval}
|
||||
billingLabel={billingLabel}
|
||||
billingBadgeClass={billingBadgeClass}
|
||||
existingModuleIds={existingModuleIds}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user