import React, { useEffect, useState } from 'react'; import { Plus, Search, Edit, Trash2, X, Tag } from 'lucide-react'; import { promotionService, Promotion } from '../../services/api'; import { toast } from 'react-toastify'; import Loading from '../../components/common/Loading'; import Pagination from '../../components/common/Pagination'; import { useFormatCurrency } from '../../hooks/useFormatCurrency'; import { useCurrency } from '../../contexts/CurrencyContext'; const PromotionManagementPage: React.FC = () => { const { currency } = useCurrency(); const { formatCurrency } = useFormatCurrency(); const [promotions, setPromotions] = useState([]); const [loading, setLoading] = useState(true); const [showModal, setShowModal] = useState(false); const [editingPromotion, setEditingPromotion] = useState(null); const [filters, setFilters] = useState({ search: '', status: '', type: '', }); const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [totalItems, setTotalItems] = useState(0); const itemsPerPage = 5; const [formData, setFormData] = useState({ code: '', name: '', description: '', discount_type: 'percentage' as 'percentage' | 'fixed', discount_value: 0, min_booking_amount: 0, max_discount_amount: 0, start_date: '', end_date: '', usage_limit: 0, status: 'active' as 'active' | 'inactive' | 'expired', }); useEffect(() => { setCurrentPage(1); }, [filters]); useEffect(() => { fetchPromotions(); }, [filters, currentPage]); const fetchPromotions = async () => { try { setLoading(true); const response = await promotionService.getPromotions({ ...filters, page: currentPage, limit: itemsPerPage, }); setPromotions(response.data.promotions); if (response.data.pagination) { setTotalPages(response.data.pagination.totalPages); setTotalItems(response.data.pagination.total); } } catch (error: any) { toast.error(error.response?.data?.message || 'Unable to load promotions list'); } finally { setLoading(false); } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { if (editingPromotion) { await promotionService.updatePromotion(editingPromotion.id, formData); toast.success('Promotion updated successfully'); } else { await promotionService.createPromotion(formData); toast.success('Promotion added successfully'); } setShowModal(false); resetForm(); fetchPromotions(); } catch (error: any) { toast.error(error.response?.data?.message || 'An error occurred'); } }; const handleEdit = (promotion: Promotion) => { setEditingPromotion(promotion); setFormData({ code: promotion.code, name: promotion.name, description: promotion.description || '', discount_type: promotion.discount_type, discount_value: promotion.discount_value, min_booking_amount: promotion.min_booking_amount || 0, max_discount_amount: promotion.max_discount_amount || 0, start_date: promotion.start_date?.split('T')[0] || '', end_date: promotion.end_date?.split('T')[0] || '', usage_limit: promotion.usage_limit || 0, status: promotion.status, }); setShowModal(true); }; const handleDelete = async (id: number) => { if (!window.confirm('Are you sure you want to delete this promotion?')) return; try { await promotionService.deletePromotion(id); toast.success('Promotion deleted successfully'); fetchPromotions(); } catch (error: any) { toast.error(error.response?.data?.message || 'Unable to delete promotion'); } }; const resetForm = () => { setEditingPromotion(null); setFormData({ code: '', name: '', description: '', discount_type: 'percentage', discount_value: 0, min_booking_amount: 0, max_discount_amount: 0, start_date: '', end_date: '', usage_limit: 0, status: 'active', }); }; const getStatusBadge = (status: string) => { const badges: Record = { active: { bg: 'bg-gradient-to-r from-emerald-50 to-green-50', text: 'text-emerald-800', label: 'Active', border: 'border-emerald-200' }, inactive: { bg: 'bg-gradient-to-r from-slate-50 to-gray-50', text: 'text-slate-700', label: 'Inactive', border: 'border-slate-200' }, expired: { bg: 'bg-gradient-to-r from-rose-50 to-red-50', text: 'text-rose-800', label: 'Expired', border: 'border-rose-200' }, }; const badge = badges[status] || badges.active; return ( {badge.label} ); }; if (loading) { return ; } return (
{/* Luxury Header */}

Promotion Management

Manage discount codes and promotion programs

{/* Luxury Filters */}
setFilters({ ...filters, search: e.target.value })} className="w-full pl-12 pr-4 py-3.5 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 placeholder-slate-400 font-medium shadow-sm hover:shadow-md" />
{/* Luxury Table */}
{promotions.map((promotion, index) => ( ))}
Code Program Name Value Period Used Status Actions
{promotion.code}
{promotion.name}
{promotion.description}
{promotion.discount_type === 'percentage' ? `${promotion.discount_value}%` : formatCurrency(promotion.discount_value)}
{promotion.start_date ? new Date(promotion.start_date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : 'N/A'} {promotion.end_date ? new Date(promotion.end_date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : 'N/A'}
{promotion.used_count || 0} / {promotion.usage_limit || '∞'}
{getStatusBadge(promotion.status)}
{/* Luxury Modal */} {showModal && (
{/* Modal Header */}

{editingPromotion ? 'Update Promotion' : 'Add New Promotion'}

{editingPromotion ? 'Modify promotion details' : 'Create a new promotion program'}

{/* Modal Content */}
setFormData({ ...formData, code: e.target.value.toUpperCase() })} 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 font-mono" placeholder="e.g: SUMMER2024" required />
setFormData({ ...formData, name: 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="e.g: Summer Sale" required />