Improve admin panel and sale countdown banner

- Remove 'apply to all' option from sale manager - only selected items
- Fix PLA Translucent to be spool-only (no refill option)
- Sale countdown shows actual max percentage from database
- Update banner design: blue-purple-orange gradient instead of red
- Remove fire emoji and promotional text
- Make percentage number larger and yellow for emphasis
- Change 'do' to 'od' in discount text
- Add shimmer animation for subtle effect
This commit is contained in:
DaX
2025-07-11 11:07:14 +02:00
parent d18e312607
commit 7d4e696fcd
4 changed files with 44 additions and 31 deletions

View File

@@ -175,7 +175,10 @@ export default function Home() {
</a> </a>
</div> </div>
<SaleCountdown /> <SaleCountdown
hasActiveSale={filaments.some(f => f.sale_active === true)}
maxSalePercentage={Math.max(...filaments.filter(f => f.sale_active).map(f => f.sale_percentage || 0), 0)}
/>
<FilamentTableV2 <FilamentTableV2
key={resetKey} key={resetKey}

View File

@@ -9,7 +9,12 @@ interface TimeLeft {
seconds: number; seconds: number;
} }
export function SaleCountdown() { interface SaleCountdownProps {
hasActiveSale: boolean;
maxSalePercentage?: number;
}
export function SaleCountdown({ hasActiveSale, maxSalePercentage = 5 }: SaleCountdownProps) {
const [timeLeft, setTimeLeft] = useState<TimeLeft>({ days: 0, hours: 0, minutes: 0, seconds: 0 }); const [timeLeft, setTimeLeft] = useState<TimeLeft>({ days: 0, hours: 0, minutes: 0, seconds: 0 });
const [mounted, setMounted] = useState(false); const [mounted, setMounted] = useState(false);
@@ -58,15 +63,21 @@ export function SaleCountdown() {
return <div className="h-24" />; // Placeholder to prevent hydration mismatch return <div className="h-24" />; // Placeholder to prevent hydration mismatch
} }
// Only show countdown if there are active sales
if (!hasActiveSale) return null;
const isActive = timeLeft.days > 0 || timeLeft.hours > 0 || timeLeft.minutes > 0 || timeLeft.seconds > 0; const isActive = timeLeft.days > 0 || timeLeft.hours > 0 || timeLeft.minutes > 0 || timeLeft.seconds > 0;
if (!isActive) return null; if (!isActive) return null;
return ( return (
<div className="bg-gradient-to-r from-red-500 to-red-600 text-white py-4 px-6 rounded-lg shadow-lg mb-8 animate-pulse"> <div className="relative bg-gradient-to-r from-blue-500 via-purple-500 to-orange-500 text-white py-4 px-6 rounded-lg shadow-lg mb-8 overflow-hidden">
<div className="text-center"> <div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/10 to-transparent animate-shimmer"></div>
<h3 className="text-lg sm:text-xl font-bold mb-2">🔥 AKCIJA SE ZAVRŠAVA!</h3> <div className="relative text-center z-10">
<p className="text-sm sm:text-base mb-3">Popust od 5% važi još:</p> <h3 className="text-xl sm:text-2xl font-bold mb-2">AKCIJA SE ZAVRŠAVA!</h3>
<p className="text-sm sm:text-base mb-3">
Popust od <span className="text-2xl sm:text-3xl font-extrabold text-yellow-300 mx-1">{maxSalePercentage}%</span> važi još:
</p>
<div className="flex justify-center gap-2 sm:gap-4"> <div className="flex justify-center gap-2 sm:gap-4">
<div className="bg-white/20 backdrop-blur rounded-lg px-2 sm:px-3 py-2 min-w-[60px] sm:min-w-[70px]"> <div className="bg-white/20 backdrop-blur rounded-lg px-2 sm:px-3 py-2 min-w-[60px] sm:min-w-[70px]">
@@ -89,10 +100,6 @@ export function SaleCountdown() {
<div className="text-xs sm:text-sm opacity-90">sek</div> <div className="text-xs sm:text-sm opacity-90">sek</div>
</div> </div>
</div> </div>
<p className="text-xs sm:text-sm mt-3 opacity-90">
Poruči sada i uštedi na Bambu Lab filamentima!
</p>
</div> </div>
</div> </div>
); );

View File

@@ -14,13 +14,17 @@ export function SaleManager({ filaments, selectedFilaments, onSaleUpdate }: Sale
const [saleStartDate, setSaleStartDate] = useState(''); const [saleStartDate, setSaleStartDate] = useState('');
const [saleEndDate, setSaleEndDate] = useState(''); const [saleEndDate, setSaleEndDate] = useState('');
const [enableSale, setEnableSale] = useState(true); const [enableSale, setEnableSale] = useState(true);
const [applyToAll, setApplyToAll] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const handleSaleUpdate = async () => { const handleSaleUpdate = async () => {
if (selectedFilaments.size === 0) {
alert('Molimo izaberite filamente za primenu popusta');
return;
}
setLoading(true); setLoading(true);
try { try {
const filamentIds = applyToAll ? undefined : Array.from(selectedFilaments); const filamentIds = Array.from(selectedFilaments);
try { try {
await filamentService.updateBulkSale({ await filamentService.updateBulkSale({
@@ -45,7 +49,6 @@ export function SaleManager({ filaments, selectedFilaments, onSaleUpdate }: Sale
setSaleStartDate(''); setSaleStartDate('');
setSaleEndDate(''); setSaleEndDate('');
setEnableSale(true); setEnableSale(true);
setApplyToAll(false);
} catch (error) { } catch (error) {
console.error('Error updating sale:', error); console.error('Error updating sale:', error);
alert('Greška pri ažuriranju popusta'); alert('Greška pri ažuriranju popusta');
@@ -79,23 +82,9 @@ export function SaleManager({ filaments, selectedFilaments, onSaleUpdate }: Sale
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<label className="flex items-center gap-2 mb-4">
<input
type="checkbox"
checked={applyToAll}
onChange={(e) => setApplyToAll(e.target.checked)}
className="w-4 h-4 text-purple-600"
/>
<span className="text-sm text-gray-700 dark:text-gray-300">
Primeni na sve filamente
</span>
</label>
{!applyToAll && (
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4"> <p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
Izabrano: {selectedFilaments.size} filament{selectedFilaments.size === 1 ? '' : 'a'} Izabrano: {selectedFilaments.size} filament{selectedFilaments.size === 1 ? '' : 'a'}
</p> </p>
)}
</div> </div>
<div> <div>
@@ -166,7 +155,7 @@ export function SaleManager({ filaments, selectedFilaments, onSaleUpdate }: Sale
<button <button
onClick={handleSaleUpdate} onClick={handleSaleUpdate}
className="px-4 py-2 bg-purple-500 text-white rounded hover:bg-purple-600 disabled:opacity-50" className="px-4 py-2 bg-purple-500 text-white rounded hover:bg-purple-600 disabled:opacity-50"
disabled={loading || (!applyToAll && selectedFilaments.size === 0)} disabled={loading || selectedFilaments.size === 0}
> >
{loading ? 'Ažuriranje...' : 'Primeni'} {loading ? 'Ažuriranje...' : 'Primeni'}
</button> </button>

View File

@@ -29,3 +29,17 @@
transition: none !important; transition: none !important;
} }
} }
/* Shimmer animation for sale banner */
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(200%);
}
}
.animate-shimmer {
animation: shimmer 3s ease-in-out infinite;
}