feat(wizard): add eye-catcher shake animation for missing required categories
All checks were successful
Staging Build / build (push) Successful in 3m41s
All checks were successful
Staging Build / build (push) Successful in 3m41s
This commit is contained in:
@@ -194,3 +194,29 @@ option {
|
|||||||
color: oklch(var(--oklch-text-main)) !important;
|
color: oklch(var(--oklch-text-main)) !important;
|
||||||
background-color: oklch(var(--oklch-surface)) !important;
|
background-color: oklch(var(--oklch-surface)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Eye-Catcher & Rüttel-Animation für fehlende Pflichtkategorien */
|
||||||
|
@keyframes shake {
|
||||||
|
0%, 100% { transform: translateX(0); }
|
||||||
|
15%, 55% { transform: translateX(-4px); }
|
||||||
|
35%, 75% { transform: translateX(4px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes eye-catcher-glow {
|
||||||
|
0%, 100% {
|
||||||
|
box-shadow: 0 0 15px rgba(244, 63, 94, 0.6), inset 0 0 8px rgba(244, 63, 94, 0.3);
|
||||||
|
border-color: rgba(244, 63, 94, 0.9);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 0 28px rgba(239, 68, 68, 0.9), inset 0 0 14px rgba(239, 68, 68, 0.5);
|
||||||
|
border-color: rgba(239, 68, 68, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-shake {
|
||||||
|
animation: shake 0.6s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-eye-catcher {
|
||||||
|
animation: eye-catcher-glow 1.2s ease-in-out infinite, shake 0.8s ease-in-out infinite !important;
|
||||||
|
}
|
||||||
@@ -933,26 +933,42 @@ export function OrderWizard({
|
|||||||
const hasSelection = (sel?.productIds && sel.productIds.length > 0) || !!sel?.productId
|
const hasSelection = (sel?.productIds && sel.productIds.length > 0) || !!sel?.productId
|
||||||
const isRequired = cat.is_required
|
const isRequired = cat.is_required
|
||||||
const isActive = activeCategoryId === cat.id
|
const isActive = activeCategoryId === cat.id
|
||||||
|
const isMissingRequired = isRequired && !hasSelection
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={cat.id}
|
key={cat.id}
|
||||||
onClick={() => setActiveCategoryId(cat.id)}
|
onClick={() => setActiveCategoryId(cat.id)}
|
||||||
className={`flex items-center gap-2.5 p-2.5 rounded-xl border transition-all duration-300 text-left relative overflow-hidden group ${isActive
|
className={`flex items-center gap-2.5 p-2.5 rounded-xl border transition-all duration-300 text-left relative overflow-hidden group ${
|
||||||
? 'bg-primary/10 border-primary text-white shadow-[0_0_15px_rgba(59,130,246,0.2)]'
|
isMissingRequired
|
||||||
: 'bg-slate-900/50 border-white/5 text-slate-400 hover:bg-slate-900 hover:border-white/20 hover:text-white'
|
? 'animate-eye-catcher bg-red-500/10 border-red-500 text-red-400 font-bold shadow-[0_0_20px_rgba(239,68,68,0.4)]'
|
||||||
|
: isActive
|
||||||
|
? 'bg-primary/10 border-primary text-white shadow-[0_0_15px_rgba(59,130,246,0.2)]'
|
||||||
|
: 'bg-slate-900/50 border-white/5 text-slate-400 hover:bg-slate-900 hover:border-white/20 hover:text-white'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{isActive && (
|
{isActive && !isMissingRequired && (
|
||||||
<div className="absolute left-0 top-0 bottom-0 w-1 bg-primary" />
|
<div className="absolute left-0 top-0 bottom-0 w-1 bg-primary" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={`p-1.5 rounded-lg transition-colors ${isActive ? 'bg-primary/20 text-primary' : 'bg-white/5 text-slate-400 group-hover:text-white'}`}>
|
<div className={`p-1.5 rounded-lg transition-colors ${
|
||||||
|
isMissingRequired
|
||||||
|
? 'bg-red-500/20 text-red-400'
|
||||||
|
: isActive
|
||||||
|
? 'bg-primary/20 text-primary'
|
||||||
|
: 'bg-white/5 text-slate-400 group-hover:text-white'
|
||||||
|
}`}>
|
||||||
<CategoryIcon icon={cat.icon} className="w-3.5 h-3.5" />
|
<CategoryIcon icon={cat.icon} className="w-3.5 h-3.5" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className={`text-xs font-semibold truncate ${isActive ? 'text-white' : 'text-slate-300 group-hover:text-white'}`}>
|
<p className={`text-xs font-semibold truncate ${
|
||||||
|
isMissingRequired
|
||||||
|
? 'text-red-300 font-bold'
|
||||||
|
: isActive
|
||||||
|
? 'text-white'
|
||||||
|
: 'text-slate-300 group-hover:text-white'
|
||||||
|
}`}>
|
||||||
{cat.name}
|
{cat.name}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -962,8 +978,8 @@ export function OrderWizard({
|
|||||||
✓
|
✓
|
||||||
</span>
|
</span>
|
||||||
) : isRequired ? (
|
) : isRequired ? (
|
||||||
<span className="text-[9px] px-1.5 py-0.2 rounded-full font-semibold shrink-0 border border-amber-500/30 text-amber-400 bg-amber-500/10">
|
<span className="text-[9px] px-2 py-0.5 rounded-full font-extrabold shrink-0 border border-red-500 text-red-400 bg-red-500/20 animate-pulse tracking-wider">
|
||||||
!
|
FEHLT!
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -88,34 +88,52 @@ export function StepSoftware({
|
|||||||
className="space-y-6"
|
className="space-y-6"
|
||||||
>
|
>
|
||||||
{/* Category header */}
|
{/* Category header */}
|
||||||
<div className="flex items-center gap-3 bg-white/5 p-4 rounded-xl border border-white/10">
|
{(() => {
|
||||||
<div className="w-8 h-8 rounded-lg bg-primary/20 flex items-center justify-center">
|
const isMissingRequired = currentCategory.is_required && !sel?.productId && (!sel?.productIds || sel.productIds.length === 0)
|
||||||
<CategoryIcon icon={currentCategory.icon} className="w-4 h-4 text-primary" />
|
return (
|
||||||
</div>
|
<div className={`flex items-center gap-3 p-4 rounded-xl border transition-all ${
|
||||||
<div>
|
isMissingRequired
|
||||||
<h3 className="font-bold text-white text-sm">{currentCategory.name}</h3>
|
? 'animate-eye-catcher bg-red-500/10 border-red-500 text-red-400 font-bold'
|
||||||
{currentCategory.description && (
|
: 'bg-white/5 border-white/10'
|
||||||
<p className="text-slate-400 text-xs mt-0.5">{currentCategory.description}</p>
|
}`}>
|
||||||
)}
|
<div className={`w-8 h-8 rounded-lg flex items-center justify-center ${
|
||||||
</div>
|
isMissingRequired ? 'bg-red-500/20 text-red-400' : 'bg-primary/20 text-primary'
|
||||||
{sel?.productIds && sel.productIds.length > 0 ? (
|
}`}>
|
||||||
<Badge className="ml-auto bg-green-500/20 text-green-400 border border-green-500/30 text-xs rounded-full">
|
<CategoryIcon icon={currentCategory.icon} className="w-4 h-4" />
|
||||||
<Check className="w-3 h-3 mr-1" /> {sel.productIds.length} Ausgewählt
|
</div>
|
||||||
</Badge>
|
<div>
|
||||||
) : sel?.productId ? (
|
<h3 className="font-bold text-white text-sm flex items-center gap-2">
|
||||||
<Badge className="ml-auto bg-green-500/20 text-green-400 border border-green-500/30 text-xs rounded-full">
|
{currentCategory.name}
|
||||||
<Check className="w-3 h-3 mr-1" /> Ausgewählt
|
{isMissingRequired && (
|
||||||
</Badge>
|
<span className="text-[10px] bg-red-500/20 text-red-400 border border-red-500/40 px-2 py-0.5 rounded-full font-extrabold animate-pulse tracking-wider">
|
||||||
) : currentCategory.is_required ? (
|
PFLICHTFELD AUSWÄHLEN!
|
||||||
<Badge variant="destructive" className="ml-auto opacity-90 text-xs rounded-full bg-red-500/20 text-red-400 border border-red-500/30">
|
</span>
|
||||||
<AlertCircle className="w-3 h-3 mr-1" /> Erforderlich
|
)}
|
||||||
</Badge>
|
</h3>
|
||||||
) : (
|
{currentCategory.description && (
|
||||||
<Badge variant="outline" className="ml-auto border-white/10 text-slate-400 text-xs rounded-full bg-white/5">
|
<p className="text-slate-400 text-xs mt-0.5">{currentCategory.description}</p>
|
||||||
Optional
|
)}
|
||||||
</Badge>
|
</div>
|
||||||
)}
|
{sel?.productIds && sel.productIds.length > 0 ? (
|
||||||
</div>
|
<Badge className="ml-auto bg-green-500/20 text-green-400 border border-green-500/30 text-xs rounded-full">
|
||||||
|
<Check className="w-3 h-3 mr-1" /> {sel.productIds.length} Ausgewählt
|
||||||
|
</Badge>
|
||||||
|
) : sel?.productId ? (
|
||||||
|
<Badge className="ml-auto bg-green-500/20 text-green-400 border border-green-500/30 text-xs rounded-full">
|
||||||
|
<Check className="w-3 h-3 mr-1" /> Ausgewählt
|
||||||
|
</Badge>
|
||||||
|
) : currentCategory.is_required ? (
|
||||||
|
<Badge variant="destructive" className="ml-auto opacity-90 text-xs rounded-full bg-red-500/30 text-red-400 border border-red-500/50 animate-pulse font-extrabold">
|
||||||
|
<AlertCircle className="w-3 h-3 mr-1 text-red-400" /> Bitte auswählen!
|
||||||
|
</Badge>
|
||||||
|
) : (
|
||||||
|
<Badge variant="outline" className="ml-auto border-white/10 text-slate-400 text-xs rounded-full bg-white/5">
|
||||||
|
Optional
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})()}
|
||||||
|
|
||||||
{catProducts.length === 0 ? (
|
{catProducts.length === 0 ? (
|
||||||
<p className="text-slate-500 text-sm italic">
|
<p className="text-slate-500 text-sm italic">
|
||||||
|
|||||||
Reference in New Issue
Block a user