91 lines
2.9 KiB
TypeScript
91 lines
2.9 KiB
TypeScript
/**
|
|
* @component SalesNotification
|
|
* @purpose Live purchase toast notification popup matching Screenshots 1, 4 & reference specs.
|
|
* @a11y Accessible dismissal button
|
|
*/
|
|
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import Image from 'next/image';
|
|
import { X, CheckCircle } from 'lucide-react';
|
|
|
|
export function SalesNotification() {
|
|
const [visible, setVisible] = useState(false);
|
|
const [currentNotification, setCurrentNotification] = useState({
|
|
name: 'Swaraa (California)',
|
|
item: 'HP LaserJet P1108 Mono Single Function Laser Printer',
|
|
time: '46 minutes ago',
|
|
image: '',
|
|
});
|
|
|
|
const notifications = [
|
|
{
|
|
name: 'Swaraa (California)',
|
|
item: 'HP LaserJet P1108 Mono Single Function Laser Printer',
|
|
time: '46 minutes ago',
|
|
image: '',
|
|
},
|
|
{
|
|
name: 'Nia (London)',
|
|
item: 'Apple Airpods Pro (2nd Gen) with MagSafe Charging Case',
|
|
time: '25 minutes ago',
|
|
image: '',
|
|
},
|
|
{
|
|
name: 'Liam (New York)',
|
|
item: 'Sony WH-1000XM5 Wireless Headphones',
|
|
time: '12 minutes ago',
|
|
image: '',
|
|
},
|
|
];
|
|
|
|
useEffect(() => {
|
|
let index = 0;
|
|
const interval = setInterval(() => {
|
|
index = (index + 1) % notifications.length;
|
|
setCurrentNotification(notifications[index]);
|
|
setVisible(true);
|
|
setTimeout(() => setVisible(false), 5000);
|
|
}, 12000);
|
|
|
|
// Initial show after 3 seconds
|
|
const initialTimer = setTimeout(() => {
|
|
setVisible(true);
|
|
setTimeout(() => setVisible(false), 5000);
|
|
}, 3000);
|
|
|
|
return () => {
|
|
clearInterval(interval);
|
|
clearTimeout(initialTimer);
|
|
};
|
|
}, []);
|
|
|
|
if (!visible) return null;
|
|
|
|
return (
|
|
<div className="fixed bottom-4 left-4 z-40 bg-white rounded-xl border border-gray-200 p-3 shadow-xl max-w-xs flex items-center gap-3 animate-in slide-in-from-bottom-5 duration-300">
|
|
<div className="relative w-12 h-12 rounded-lg border border-gray-100 overflow-hidden bg-gray-50 shrink-0">
|
|
<Image src={currentNotification.image} alt={currentNotification.item} fill className="object-cover" />
|
|
</div>
|
|
<div className="flex-1 min-w-0 text-[11px]">
|
|
<div className="text-gray-500 font-medium">
|
|
<strong className="text-gray-900 font-bold">{currentNotification.name}</strong> purchased
|
|
</div>
|
|
<div className="font-bold text-[#1877f2] truncate">{currentNotification.item}</div>
|
|
<div className="text-[9px] text-gray-400 flex items-center gap-1 mt-0.5">
|
|
<span>{currentNotification.time}</span>
|
|
<span>•</span>
|
|
<span className="text-[#01b574] font-semibold flex items-center gap-0.5">
|
|
<CheckCircle className="w-2.5 h-2.5" /> Verified
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<button
|
|
onClick={() => setVisible(false)}
|
|
className="p-1 text-gray-400 hover:text-gray-600 rounded-full hover:bg-gray-100 transition-colors"
|
|
>
|
|
<X className="w-3.5 h-3.5" />
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|