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:
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user