import React, { useEffect, useState } from 'react'; import { Plus, Search, Edit, Trash2, X, Image as ImageIcon, Eye, EyeOff, Loader2 } from 'lucide-react'; import { toast } from 'react-toastify'; import Loading from '../../components/common/Loading'; import Pagination from '../../components/common/Pagination'; import { ConfirmationDialog } from '../../components/common'; import bannerServiceModule from '../../services/api/bannerService'; import type { Banner } from '../../services/api/bannerService'; const { getAllBanners, createBanner, updateBanner, deleteBanner, uploadBannerImage, } = bannerServiceModule as any; const BannerManagementPage: React.FC = () => { const [banners, setBanners] = useState([]); const [loading, setLoading] = useState(true); const [showModal, setShowModal] = useState(false); const [editingBanner, setEditingBanner] = useState(null); const [deleteConfirm, setDeleteConfirm] = useState<{ show: boolean; id: number | null }>({ show: false, id: null, }); const [filters, setFilters] = useState({ search: '', position: '', }); const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const itemsPerPage = 10; const [formData, setFormData] = useState({ title: '', description: '', image_url: '', link: '', position: 'home', display_order: 0, is_active: true, start_date: '', end_date: '', }); const [imageFile, setImageFile] = useState(null); const [imagePreview, setImagePreview] = useState(null); const [uploadingImage, setUploadingImage] = useState(false); const [useFileUpload, setUseFileUpload] = useState(true); useEffect(() => { setCurrentPage(1); }, [filters]); useEffect(() => { fetchBanners(); }, [filters, currentPage]); const fetchBanners = async () => { try { setLoading(true); const response = await getAllBanners({ position: filters.position || undefined, page: currentPage, limit: itemsPerPage, }); let allBanners = response.data?.banners || []; if (filters.search) { allBanners = allBanners.filter((banner: Banner) => banner.title.toLowerCase().includes(filters.search.toLowerCase()) ); } setBanners(allBanners); setTotalPages(Math.ceil(allBanners.length / itemsPerPage)); } catch (error: any) { toast.error(error.response?.data?.message || 'Unable to load banners'); } finally { setLoading(false); } }; const handleImageFileChange = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; if (!file.type.startsWith('image/')) { toast.error('Please select an image file'); return; } if (file.size > 5 * 1024 * 1024) { toast.error('Image size must be less than 5MB'); return; } setImageFile(file); const reader = new FileReader(); reader.onloadend = () => { setImagePreview(reader.result as string); }; reader.readAsDataURL(file); try { setUploadingImage(true); const response = await uploadBannerImage(file); if (response.status === 'success' || response.success) { setFormData({ ...formData, image_url: response.data.image_url }); toast.success('Image uploaded successfully'); } } catch (error: any) { toast.error(error.response?.data?.message || 'Failed to upload image'); setImageFile(null); setImagePreview(null); } finally { setUploadingImage(false); } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!formData.image_url && !imageFile) { toast.error('Please upload an image or provide an image URL'); return; } try { let imageUrl = formData.image_url; if (imageFile && !imageUrl) { setUploadingImage(true); const uploadResponse = await uploadBannerImage(imageFile); if (uploadResponse.status === 'success' || uploadResponse.success) { imageUrl = uploadResponse.data.image_url; } else { throw new Error('Failed to upload image'); } setUploadingImage(false); } const submitData = { ...formData, image_url: imageUrl, start_date: formData.start_date || undefined, end_date: formData.end_date || undefined, }; if (editingBanner) { await updateBanner(editingBanner.id, submitData); toast.success('Banner updated successfully'); } else { await createBanner(submitData); toast.success('Banner created successfully'); } setShowModal(false); resetForm(); fetchBanners(); } catch (error: any) { toast.error(error.response?.data?.message || 'An error occurred'); setUploadingImage(false); } }; const handleEdit = (banner: Banner) => { setEditingBanner(banner); setFormData({ title: banner.title || '', description: '', image_url: banner.image_url || '', link: banner.link_url || '', position: banner.position || 'home', display_order: banner.display_order || 0, is_active: banner.is_active ?? true, start_date: banner.start_date ? banner.start_date.split('T')[0] : '', end_date: banner.end_date ? banner.end_date.split('T')[0] : '', }); setImageFile(null); const previewUrl = banner.image_url ? (banner.image_url.startsWith('http') ? banner.image_url : `${import.meta.env.VITE_API_URL?.replace('/api', '') || 'http://localhost:8000'}/${banner.image_url}`) : null; setImagePreview(previewUrl); setUseFileUpload(false); setShowModal(true); }; const handleDelete = async () => { if (!deleteConfirm.id) return; try { await deleteBanner(deleteConfirm.id); toast.success('Banner deleted successfully'); setDeleteConfirm({ show: false, id: null }); fetchBanners(); } catch (error: any) { toast.error(error.response?.data?.message || 'Failed to delete banner'); } }; const resetForm = () => { setFormData({ title: '', description: '', image_url: '', link: '', position: 'home', display_order: 0, is_active: true, start_date: '', end_date: '', }); setImageFile(null); setImagePreview(null); setUseFileUpload(true); setEditingBanner(null); }; const toggleActive = async (banner: Banner) => { try { await updateBanner(banner.id, { is_active: !banner.is_active, }); toast.success(`Banner ${!banner.is_active ? 'activated' : 'deactivated'}`); fetchBanners(); } catch (error: any) { toast.error(error.response?.data?.message || 'Failed to update banner'); } }; if (loading && banners.length === 0) { return ; } return (

Banner Management

Manage promotional banners and advertisements

{}
setFilters({ ...filters, search: e.target.value })} className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#d4af37] focus:border-[#d4af37]" />
{}
{banners.length === 0 ? ( ) : ( banners.map((banner) => ( )) )}
Image Title Position Order Status Actions
No banners found
{banner.image_url ? ( {banner.title} ) : (
)}
{banner.title}
{banner.position} {banner.display_order}
{} {totalPages > 1 && (
)} {} {showModal && (

{editingBanner ? 'Edit Banner' : 'Create Banner'}

{editingBanner ? 'Modify banner information' : 'Create a new promotional banner'}

setFormData({ ...formData, title: e.target.value })} className="w-full px-4 py-3 bg-white border-2 border-slate-200 rounded-xl focus:border-amber-400 focus:ring-4 focus:ring-amber-100 transition-all duration-200 text-slate-700 font-medium shadow-sm" placeholder="Enter banner title" />
{}
{useFileUpload ? (
) : (
setFormData({ ...formData, image_url: e.target.value })} placeholder="https://example.com/image.jpg" className="w-full px-4 py-3 bg-white border-2 border-slate-200 rounded-xl focus:border-amber-400 focus:ring-4 focus:ring-amber-100 transition-all duration-200 text-slate-700 font-medium shadow-sm" /> {formData.image_url && (
Preview { (e.target as HTMLImageElement).style.display = 'none'; }} />
)}
)}