updates
This commit is contained in:
@@ -1,20 +1,23 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Search, Eye, XCircle, CheckCircle, Loader2 } from 'lucide-react';
|
||||
import { bookingService, Booking } from '../../services/api';
|
||||
import { Search, Eye, XCircle, CheckCircle, Loader2, FileText } from 'lucide-react';
|
||||
import { bookingService, Booking, invoiceService } 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 { parseDateLocal } from '../../utils/format';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const BookingManagementPage: React.FC = () => {
|
||||
const { formatCurrency } = useFormatCurrency();
|
||||
const navigate = useNavigate();
|
||||
const [bookings, setBookings] = useState<Booking[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [selectedBooking, setSelectedBooking] = useState<Booking | null>(null);
|
||||
const [showDetailModal, setShowDetailModal] = useState(false);
|
||||
const [updatingBookingId, setUpdatingBookingId] = useState<number | null>(null);
|
||||
const [cancellingBookingId, setCancellingBookingId] = useState<number | null>(null);
|
||||
const [creatingInvoice, setCreatingInvoice] = useState(false);
|
||||
const [filters, setFilters] = useState({
|
||||
search: '',
|
||||
status: '',
|
||||
@@ -80,6 +83,32 @@ const BookingManagementPage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateInvoice = async (bookingId: number) => {
|
||||
try {
|
||||
setCreatingInvoice(true);
|
||||
// Ensure bookingId is a number
|
||||
const invoiceData = {
|
||||
booking_id: Number(bookingId),
|
||||
};
|
||||
|
||||
const response = await invoiceService.createInvoice(invoiceData);
|
||||
|
||||
if (response.status === 'success' && response.data?.invoice) {
|
||||
toast.success('Invoice created successfully!');
|
||||
setShowDetailModal(false);
|
||||
navigate(`/admin/invoices/${response.data.invoice.id}`);
|
||||
} else {
|
||||
throw new Error('Failed to create invoice');
|
||||
}
|
||||
} catch (error: any) {
|
||||
const errorMessage = error.response?.data?.detail || error.response?.data?.message || error.message || 'Unable to create invoice';
|
||||
toast.error(errorMessage);
|
||||
console.error('Invoice creation error:', error);
|
||||
} finally {
|
||||
setCreatingInvoice(false);
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusBadge = (status: string) => {
|
||||
const badges: Record<string, { bg: string; text: string; label: string; border: string }> = {
|
||||
pending: {
|
||||
@@ -622,7 +651,24 @@ const BookingManagementPage: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{/* Modal Footer */}
|
||||
<div className="mt-8 pt-6 border-t border-slate-200 flex justify-end">
|
||||
<div className="mt-8 pt-6 border-t border-slate-200 flex justify-between items-center">
|
||||
<button
|
||||
onClick={() => handleCreateInvoice(selectedBooking.id)}
|
||||
disabled={creatingInvoice}
|
||||
className="flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-amber-500 to-amber-600 text-white rounded-xl font-semibold hover:from-amber-600 hover:to-amber-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 shadow-lg hover:shadow-xl"
|
||||
>
|
||||
{creatingInvoice ? (
|
||||
<>
|
||||
<Loader2 className="w-5 h-5 animate-spin" />
|
||||
Creating Invoice...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<FileText className="w-5 h-5" />
|
||||
Create Invoice
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowDetailModal(false)}
|
||||
className="px-8 py-3 bg-gradient-to-r from-slate-700 to-slate-800 text-white rounded-xl font-semibold hover:from-slate-800 hover:to-slate-900 transition-all duration-200 shadow-lg hover:shadow-xl border border-slate-600"
|
||||
|
||||
Reference in New Issue
Block a user