Merge branch 'staging' of https://gitea.hephex.de/admin/webshop into staging

# Conflicts:
#	shop/lib/actions/email-templates.ts
This commit is contained in:
DanielS
2026-08-27 00:34:39 +02:00
11 changed files with 435 additions and 152 deletions

View File

@@ -29,6 +29,7 @@ interface StepSoftwareProps {
/** Modul-IDs, die bereits lizenziert sind (Upgrade-Modus) */
existingModuleIds?: string[]
activeCategoryId: string | null
editingDeviceName?: string | null
}
export function CategoryIcon({ icon, className }: { icon?: string | null; className?: string }) {
@@ -52,6 +53,7 @@ export function StepSoftware({
billingBadgeClass,
existingModuleIds = [],
activeCategoryId,
editingDeviceName = null,
}: StepSoftwareProps) {
const currentCategory = visibleCategories.find(c => c.id === activeCategoryId) ?? visibleCategories[0] ?? null
@@ -69,15 +71,22 @@ export function StepSoftware({
return (
<Card className="glass-dark border-white/10 h-full flex flex-col rounded-2xl">
<CardHeader className="border-b border-white/5 pb-4">
<CardTitle className="text-xl flex items-center gap-2 text-white font-bold">
<ShoppingCart className="w-5 h-5 text-primary" />
Optionen wählen
<CardTitle className="text-xl flex items-center justify-between gap-2 text-white font-bold flex-wrap">
<div className="flex items-center gap-2">
<ShoppingCart className="w-5 h-5 text-primary" />
<span>Optionen wählen</span>
</div>
{editingDeviceName && (
<Badge className="bg-amber-500/20 text-amber-400 border border-amber-500/30 text-xs font-bold gap-1 px-2.5 py-1 rounded-lg animate-pulse shrink-0">
Bearbeite Kasse: &quot;{editingDeviceName}&quot;
</Badge>
)}
</CardTitle>
<CardDescription className="text-slate-400">
Passen Sie die Konfiguration für {currentCategory.name} an.
</CardDescription>
</CardHeader>
<CardContent className="flex-1 overflow-y-auto pt-6 space-y-6">
<CardContent className="flex-1 pt-6 space-y-6">
<AnimatePresence mode="wait">
<motion.div
key={currentCategory.id}
@@ -260,13 +269,14 @@ export function StepSoftware({
return (
<div
key={module.id}
title={isExistingLicense ? "Dieses Modul ist auf dieser Kasse bereits aktiv und dauerhaft lizenziert." : undefined}
className={`flex flex-col p-4 rounded-xl border transition-all duration-200 ${
isExistingLicense
? 'border-primary/20 bg-primary/5 opacity-75'
: disabled
? 'border-white/5 bg-white/5 opacity-40'
: checked
? 'border-primary/30 bg-primary/5'
? 'border-primary bg-primary/10 shadow-[0_0_15px_rgba(59,130,246,0.15)] text-white'
: 'border-white/5 bg-white/5 hover:bg-white/10'
}`}
>

View File

@@ -70,6 +70,8 @@ export function StepSummary({
onAddNewBasketItem,
onDeleteBasketItem,
}: StepSummaryProps) {
const [confirmDeleteIdx, setConfirmDeleteIdx] = React.useState<number | null>(null)
return (
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6 h-full items-stretch text-left">
{/* LINKER BEREICH: Scrollbares Bedienfeld */}
@@ -211,16 +213,26 @@ export function StepSummary({
{onDeleteBasketItem && (
<Button
type="button"
variant="ghost"
variant={confirmDeleteIdx === itemIdx ? "destructive" : "ghost"}
size="sm"
onClick={(e) => {
e.stopPropagation()
onDeleteBasketItem(itemIdx)
if (confirmDeleteIdx === itemIdx) {
onDeleteBasketItem(itemIdx)
setConfirmDeleteIdx(null)
} else {
setConfirmDeleteIdx(itemIdx)
}
}}
className="h-7 px-2 text-xs text-slate-400 hover:text-red-400 hover:bg-red-500/20 gap-1 rounded-lg"
title="Kasse löschen"
onMouseLeave={() => setConfirmDeleteIdx(null)}
className={`h-7 px-2 text-xs transition-all ${
confirmDeleteIdx === itemIdx
? 'bg-red-600 text-white hover:bg-red-700 font-bold px-3 shadow-[0_0_10px_rgba(220,38,38,0.5)]'
: 'text-slate-400 hover:text-red-400 hover:bg-red-500/20'
} gap-1 rounded-lg`}
>
<Icons.Trash2 className="w-3 h-3" /> Löschen
<Icons.Trash2 className="w-3.5 h-3.5" />
{confirmDeleteIdx === itemIdx ? 'Wirklich löschen?' : 'Löschen'}
</Button>
)}
</div>

View File

@@ -294,7 +294,11 @@ export function SummarySidebar({
onClick={nextStep}
disabled={isNextStepDisabled && basketItems.length === 0}
>
Weiter zu Schritt 4 <ChevronRight className="ml-1 w-4 h-4" />
{editingIdx !== null ? (
<>Änderungen für &quot;{basketItems[editingIdx]?.deviceName || `Kasse ${editingIdx + 1}`}&quot; übernehmen</>
) : (
<>Weiter zu Schritt 4 <ChevronRight className="ml-1 w-4 h-4" /></>
)}
</Button>
<Button variant="ghost" className="w-full text-slate-400 hover:text-white h-7 text-xs" onClick={prevStep}>
Zurück zum Abrechnungsmodell