updates
This commit is contained in:
@@ -12,7 +12,6 @@ import {
|
||||
Sparkles,
|
||||
ChevronRight,
|
||||
X,
|
||||
Download
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Loading, EmptyState } from '../../components/common';
|
||||
@@ -21,7 +20,8 @@ import { useFormatCurrency } from '../../hooks/useFormatCurrency';
|
||||
import { useCurrency } from '../../contexts/CurrencyContext';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { invoiceService, Invoice } from '../../services/api';
|
||||
import { paymentService, Payment } from '../../services/api';
|
||||
import { paymentService } from '../../services/api';
|
||||
import type { Payment } from '../../services/api/paymentService';
|
||||
import { promotionService, Promotion } from '../../services/api';
|
||||
import { formatDate } from '../../utils/format';
|
||||
|
||||
@@ -33,7 +33,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const [activeTab, setActiveTab] = useState<BusinessTab>('overview');
|
||||
|
||||
// Invoices State
|
||||
|
||||
const [invoices, setInvoices] = useState<Invoice[]>([]);
|
||||
const [invoicesLoading, setInvoicesLoading] = useState(true);
|
||||
const [invoiceFilters, setInvoiceFilters] = useState({
|
||||
@@ -45,7 +45,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
const [invoicesTotalItems, setInvoicesTotalItems] = useState(0);
|
||||
const invoicesPerPage = 10;
|
||||
|
||||
// Payments State
|
||||
|
||||
const [payments, setPayments] = useState<Payment[]>([]);
|
||||
const [paymentsLoading, setPaymentsLoading] = useState(true);
|
||||
const [paymentFilters, setPaymentFilters] = useState({
|
||||
@@ -59,7 +59,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
const [paymentsTotalItems, setPaymentsTotalItems] = useState(0);
|
||||
const paymentsPerPage = 5;
|
||||
|
||||
// Promotions State
|
||||
|
||||
const [promotions, setPromotions] = useState<Promotion[]>([]);
|
||||
const [promotionsLoading, setPromotionsLoading] = useState(true);
|
||||
const [showPromotionModal, setShowPromotionModal] = useState(false);
|
||||
@@ -116,7 +116,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
}
|
||||
}, [promotionFilters]);
|
||||
|
||||
// Invoices Functions
|
||||
|
||||
const fetchInvoices = async () => {
|
||||
try {
|
||||
setInvoicesLoading(true);
|
||||
@@ -168,12 +168,12 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
sent: { bg: 'bg-gradient-to-r from-blue-50 to-indigo-50', text: 'text-blue-800', label: 'Sent', border: 'border-blue-200' },
|
||||
paid: { bg: 'bg-gradient-to-r from-emerald-50 to-green-50', text: 'text-emerald-800', label: 'Paid', border: 'border-emerald-200' },
|
||||
overdue: { bg: 'bg-gradient-to-r from-rose-50 to-red-50', text: 'text-rose-800', label: 'Overdue', border: 'border-rose-200' },
|
||||
cancelled: { bg: 'bg-gradient-to-r from-slate-50 to-gray-50', text: 'text-slate-700', label: 'Cancelled', border: 'border-slate-200' },
|
||||
cancelled: { bg: 'bg-gradient-to-r from-rose-50 to-red-50', text: 'text-rose-800', label: '❌ Canceled', border: 'border-rose-200' },
|
||||
};
|
||||
return badges[status] || badges.draft;
|
||||
};
|
||||
|
||||
// Payments Functions
|
||||
|
||||
const fetchPayments = async () => {
|
||||
try {
|
||||
setPaymentsLoading(true);
|
||||
@@ -210,7 +210,42 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
// Promotions Functions
|
||||
const getPaymentStatusBadge = (status: string) => {
|
||||
const statusConfig: Record<string, { bg: string; text: string; label: string; border: string }> = {
|
||||
completed: {
|
||||
bg: 'bg-gradient-to-r from-emerald-50 to-green-50',
|
||||
text: 'text-emerald-800',
|
||||
label: '✅ Paid',
|
||||
border: 'border-emerald-200'
|
||||
},
|
||||
pending: {
|
||||
bg: 'bg-gradient-to-r from-amber-50 to-yellow-50',
|
||||
text: 'text-amber-800',
|
||||
label: '⏳ Pending',
|
||||
border: 'border-amber-200'
|
||||
},
|
||||
failed: {
|
||||
bg: 'bg-gradient-to-r from-rose-50 to-red-50',
|
||||
text: 'text-rose-800',
|
||||
label: '❌ Failed',
|
||||
border: 'border-rose-200'
|
||||
},
|
||||
refunded: {
|
||||
bg: 'bg-gradient-to-r from-slate-50 to-gray-50',
|
||||
text: 'text-slate-700',
|
||||
label: '💰 Refunded',
|
||||
border: 'border-slate-200'
|
||||
},
|
||||
};
|
||||
const config = statusConfig[status] || statusConfig.pending;
|
||||
return (
|
||||
<span className={`px-3 py-1.5 rounded-full text-xs font-semibold border shadow-sm ${config.bg} ${config.text} ${config.border}`}>
|
||||
{config.label}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const fetchPromotions = async () => {
|
||||
try {
|
||||
setPromotionsLoading(true);
|
||||
@@ -320,7 +355,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-slate-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 space-y-10 animate-fade-in">
|
||||
{/* Luxury Header */}
|
||||
{}
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-emerald-400/5 via-transparent to-purple-600/5 rounded-3xl blur-3xl"></div>
|
||||
<div className="relative bg-white/80 backdrop-blur-xl rounded-3xl shadow-2xl border border-emerald-200/30 p-8 md:p-10">
|
||||
@@ -347,7 +382,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Premium Tab Navigation */}
|
||||
{}
|
||||
<div className="mt-10 pt-8 border-t border-gradient-to-r from-transparent via-emerald-200/30 to-transparent">
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{tabs.map((tab) => {
|
||||
@@ -383,7 +418,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Overview Tab */}
|
||||
{}
|
||||
{activeTab === 'overview' && (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 lg:gap-8">
|
||||
<div
|
||||
@@ -487,10 +522,10 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Invoices Tab */}
|
||||
{}
|
||||
{activeTab === 'invoices' && (
|
||||
<div className="space-y-8">
|
||||
{/* Section Header */}
|
||||
{}
|
||||
<div className="bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
|
||||
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-6">
|
||||
<div className="space-y-3">
|
||||
@@ -517,7 +552,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
{}
|
||||
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="p-2 rounded-xl bg-gradient-to-br from-amber-500/10 to-amber-600/10 border border-amber-200/40">
|
||||
@@ -546,7 +581,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
<option value="sent">Sent</option>
|
||||
<option value="paid">Paid</option>
|
||||
<option value="overdue">Overdue</option>
|
||||
<option value="cancelled">Cancelled</option>
|
||||
<option value="cancelled">Canceled</option>
|
||||
</select>
|
||||
<div className="flex items-center gap-3 px-4 py-3.5 bg-gradient-to-r from-gray-50 to-white border-2 border-gray-200 rounded-xl">
|
||||
<Filter className="w-5 h-5 text-emerald-600" />
|
||||
@@ -557,7 +592,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Invoices Table */}
|
||||
{}
|
||||
{invoicesLoading && invoices.length === 0 ? (
|
||||
<Loading fullScreen text="Loading invoices..." />
|
||||
) : (
|
||||
@@ -664,10 +699,10 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Payments Tab */}
|
||||
{}
|
||||
{activeTab === 'payments' && (
|
||||
<div className="space-y-8">
|
||||
{/* Section Header */}
|
||||
{}
|
||||
<div className="bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -682,7 +717,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
{}
|
||||
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="p-2 rounded-xl bg-gradient-to-br from-amber-500/10 to-amber-600/10 border border-amber-200/40">
|
||||
@@ -728,7 +763,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Payments Table */}
|
||||
{}
|
||||
{paymentsLoading && payments.length === 0 ? (
|
||||
<Loading fullScreen text="Loading payments..." />
|
||||
) : (
|
||||
@@ -743,6 +778,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Customer</th>
|
||||
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Method</th>
|
||||
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Type</th>
|
||||
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Status</th>
|
||||
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Amount</th>
|
||||
<th className="px-8 py-5 text-left text-xs font-bold text-gray-700 uppercase tracking-wider border-b border-gray-200">Payment Date</th>
|
||||
</tr>
|
||||
@@ -758,7 +794,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</td>
|
||||
<td className="px-8 py-5 whitespace-nowrap">
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
{payment.booking?.user?.name || payment.booking?.user?.full_name || 'N/A'}
|
||||
{payment.booking?.user?.name || 'N/A'}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-8 py-5 whitespace-nowrap">
|
||||
@@ -779,6 +815,9 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-8 py-5 whitespace-nowrap">
|
||||
{getPaymentStatusBadge(payment.payment_status)}
|
||||
</td>
|
||||
<td className="px-8 py-5 whitespace-nowrap">
|
||||
<div className="text-sm font-bold bg-gradient-to-r from-emerald-600 to-emerald-700 bg-clip-text text-transparent">
|
||||
{formatCurrency(payment.amount)}
|
||||
@@ -786,7 +825,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</td>
|
||||
<td className="px-8 py-5 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-600">
|
||||
{new Date(payment.payment_date || payment.createdAt).toLocaleDateString('en-US')}
|
||||
{new Date(payment.payment_date || payment.createdAt || '').toLocaleDateString('en-US')}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -803,18 +842,22 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Summary Card */}
|
||||
{}
|
||||
<div className="bg-gradient-to-r from-emerald-500 via-emerald-600 to-purple-600 rounded-2xl shadow-2xl p-8 text-white">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-2 text-emerald-100">Total Revenue</h3>
|
||||
<p className="text-4xl font-bold">
|
||||
{formatCurrency(payments.reduce((sum, p) => sum + p.amount, 0))}
|
||||
{formatCurrency(payments
|
||||
.filter(p => p.payment_status === 'completed')
|
||||
.reduce((sum, p) => sum + p.amount, 0))}
|
||||
</p>
|
||||
<p className="text-sm mt-3 text-emerald-100/90">
|
||||
Total {payments.filter(p => p.payment_status === 'completed').length} paid transaction{payments.filter(p => p.payment_status === 'completed').length !== 1 ? 's' : ''}
|
||||
</p>
|
||||
<p className="text-sm mt-3 text-emerald-100/90">Total {payments.length} transaction{payments.length !== 1 ? 's' : ''}</p>
|
||||
</div>
|
||||
<div className="bg-white/20 backdrop-blur-sm p-6 rounded-2xl">
|
||||
<div className="text-5xl font-bold text-white/80">{payments.length}</div>
|
||||
<div className="text-5xl font-bold text-white/80">{payments.filter(p => p.payment_status === 'completed').length}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -823,10 +866,10 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Promotions Tab */}
|
||||
{}
|
||||
{activeTab === 'promotions' && (
|
||||
<div className="space-y-8">
|
||||
{/* Section Header */}
|
||||
{}
|
||||
<div className="bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
|
||||
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-6">
|
||||
<div className="space-y-3">
|
||||
@@ -856,7 +899,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
{}
|
||||
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="p-2 rounded-xl bg-gradient-to-br from-amber-500/10 to-amber-600/10 border border-amber-200/40">
|
||||
@@ -896,7 +939,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Promotions Table */}
|
||||
{}
|
||||
{promotionsLoading && promotions.length === 0 ? (
|
||||
<Loading fullScreen text="Loading promotions..." />
|
||||
) : (
|
||||
@@ -986,11 +1029,11 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Promotion Modal */}
|
||||
{}
|
||||
{showPromotionModal && (
|
||||
<div className="fixed inset-0 bg-black/70 backdrop-blur-md flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-3xl max-h-[90vh] overflow-hidden border border-gray-200">
|
||||
{/* Modal Header */}
|
||||
{}
|
||||
<div className="bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900 px-8 py-6 border-b border-slate-700">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
@@ -1010,7 +1053,7 @@ const BusinessDashboardPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Modal Content */}
|
||||
{}
|
||||
<div className="p-8 overflow-y-auto max-h-[calc(90vh-120px)] custom-scrollbar">
|
||||
<form onSubmit={handlePromotionSubmit} className="space-y-6">
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
|
||||
Reference in New Issue
Block a user