Hotel Booking
This commit is contained in:
826
client/src/pages/customer/BookingSuccessPage.tsx
Normal file
826
client/src/pages/customer/BookingSuccessPage.tsx
Normal file
@@ -0,0 +1,826 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
useParams,
|
||||
useNavigate,
|
||||
Link
|
||||
} from 'react-router-dom';
|
||||
import {
|
||||
CheckCircle,
|
||||
Home,
|
||||
ListOrdered,
|
||||
Calendar,
|
||||
Users,
|
||||
CreditCard,
|
||||
MapPin,
|
||||
Mail,
|
||||
Phone,
|
||||
User,
|
||||
FileText,
|
||||
Building2,
|
||||
AlertCircle,
|
||||
Copy,
|
||||
Check,
|
||||
Loader2,
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import {
|
||||
getBookingById,
|
||||
generateQRCode,
|
||||
type Booking,
|
||||
} from '../../services/api/bookingService';
|
||||
import { confirmBankTransfer } from
|
||||
'../../services/api/paymentService';
|
||||
import Loading from '../../components/common/Loading';
|
||||
|
||||
const BookingSuccessPage: React.FC = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [booking, setBooking] = useState<Booking | null>(
|
||||
null
|
||||
);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [copiedBookingNumber, setCopiedBookingNumber] =
|
||||
useState(false);
|
||||
const [uploadingReceipt, setUploadingReceipt] =
|
||||
useState(false);
|
||||
const [receiptUploaded, setReceiptUploaded] =
|
||||
useState(false);
|
||||
const [selectedFile, setSelectedFile] =
|
||||
useState<File | null>(null);
|
||||
const [previewUrl, setPreviewUrl] =
|
||||
useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
fetchBookingDetails(Number(id));
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
const fetchBookingDetails = async (bookingId: number) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await getBookingById(bookingId);
|
||||
|
||||
if (
|
||||
response.success &&
|
||||
response.data?.booking
|
||||
) {
|
||||
const bookingData = response.data.booking;
|
||||
setBooking(bookingData);
|
||||
|
||||
// Redirect to deposit payment page if required and not yet paid
|
||||
if (
|
||||
bookingData.requires_deposit &&
|
||||
!bookingData.deposit_paid
|
||||
) {
|
||||
navigate(`/deposit-payment/${bookingId}`, { replace: true });
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
'Không thể tải thông tin đặt phòng'
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('Error fetching booking:', err);
|
||||
const message =
|
||||
err.response?.data?.message ||
|
||||
'Không thể tải thông tin đặt phòng';
|
||||
setError(message);
|
||||
toast.error(message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString('en-US', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
});
|
||||
};
|
||||
|
||||
const formatPrice = (price: number) => {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'VND',
|
||||
}).format(price);
|
||||
};
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case 'confirmed':
|
||||
return 'bg-green-100 text-green-800';
|
||||
case 'pending':
|
||||
return 'bg-yellow-100 text-yellow-800';
|
||||
case 'cancelled':
|
||||
return 'bg-red-100 text-red-800';
|
||||
case 'checked_in':
|
||||
return 'bg-blue-100 text-blue-800';
|
||||
case 'checked_out':
|
||||
return 'bg-gray-100 text-gray-800';
|
||||
default:
|
||||
return 'bg-gray-100 text-gray-800';
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
switch (status) {
|
||||
case 'confirmed':
|
||||
return 'Đã xác nhận';
|
||||
case 'pending':
|
||||
return 'Chờ xác nhận';
|
||||
case 'cancelled':
|
||||
return 'Đã hủy';
|
||||
case 'checked_in':
|
||||
return 'Đã nhận phòng';
|
||||
case 'checked_out':
|
||||
return 'Đã trả phòng';
|
||||
default:
|
||||
return status;
|
||||
}
|
||||
};
|
||||
|
||||
const copyBookingNumber = async () => {
|
||||
if (!booking?.booking_number) return;
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(
|
||||
booking.booking_number
|
||||
);
|
||||
setCopiedBookingNumber(true);
|
||||
toast.success('Đã sao chép mã đặt phòng');
|
||||
setTimeout(() => setCopiedBookingNumber(false), 2000);
|
||||
} catch (err) {
|
||||
toast.error('Không thể sao chép');
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileSelect = (
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
// Validate file type
|
||||
if (!file.type.startsWith('image/')) {
|
||||
toast.error('Vui lòng chọn file ảnh');
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate file size (max 5MB)
|
||||
if (file.size > 5 * 1024 * 1024) {
|
||||
toast.error('Kích thước ảnh không được vượt quá 5MB');
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedFile(file);
|
||||
|
||||
// Create preview
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
setPreviewUrl(reader.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
const handleUploadReceipt = async () => {
|
||||
if (!selectedFile || !booking) return;
|
||||
|
||||
try {
|
||||
setUploadingReceipt(true);
|
||||
|
||||
// Generate transaction ID based on booking number
|
||||
const transactionId =
|
||||
`TXN-${booking.booking_number}-${Date.now()}`;
|
||||
|
||||
const response = await confirmBankTransfer(
|
||||
booking.id,
|
||||
transactionId,
|
||||
selectedFile
|
||||
);
|
||||
|
||||
if (response.success) {
|
||||
toast.success(
|
||||
'✅ Đã gửi xác nhận thanh toán thành công! ' +
|
||||
'Chúng tôi sẽ xác nhận trong thời gian sớm nhất.'
|
||||
);
|
||||
setReceiptUploaded(true);
|
||||
|
||||
// Update booking payment status locally
|
||||
setBooking((prev) =>
|
||||
prev
|
||||
? {
|
||||
...prev,
|
||||
payment_status: 'paid',
|
||||
status: prev.status === 'pending'
|
||||
? 'confirmed'
|
||||
: prev.status
|
||||
}
|
||||
: null
|
||||
);
|
||||
} else {
|
||||
throw new Error(
|
||||
response.message ||
|
||||
'Không thể xác nhận thanh toán'
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('Error uploading receipt:', err);
|
||||
const message =
|
||||
err.response?.data?.message ||
|
||||
'Không thể gửi xác nhận thanh toán. ' +
|
||||
'Vui lòng thử lại.';
|
||||
toast.error(message);
|
||||
} finally {
|
||||
setUploadingReceipt(false);
|
||||
}
|
||||
};
|
||||
|
||||
const qrCodeUrl = booking
|
||||
? generateQRCode(
|
||||
booking.booking_number,
|
||||
booking.total_price
|
||||
)
|
||||
: null;
|
||||
|
||||
if (loading) {
|
||||
return <Loading fullScreen text="Đang tải..." />;
|
||||
}
|
||||
|
||||
if (error || !booking) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 py-8">
|
||||
<div className="max-w-4xl mx-auto px-4">
|
||||
<div
|
||||
className="bg-red-50 border border-red-200
|
||||
rounded-lg p-8 text-center"
|
||||
>
|
||||
<AlertCircle
|
||||
className="w-12 h-12 text-red-500
|
||||
mx-auto mb-3"
|
||||
/>
|
||||
<p className="text-red-700 font-medium mb-4">
|
||||
{error || 'Không tìm thấy đặt phòng'}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => navigate('/rooms')}
|
||||
className="inline-flex items-center gap-2 bg-indigo-600
|
||||
text-white px-3 py-2 rounded-md hover:bg-indigo-700
|
||||
disabled:bg-gray-400 mb-6 transition-colors"
|
||||
>
|
||||
Quay lại danh sách phòng
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const room = booking.room;
|
||||
const roomType = room?.room_type;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 py-8">
|
||||
<div className="max-w-4xl mx-auto px-4">
|
||||
{/* Success Header */}
|
||||
<div
|
||||
className="bg-white rounded-lg shadow-md
|
||||
p-8 mb-6 text-center"
|
||||
>
|
||||
<div
|
||||
className="w-20 h-20 bg-green-100
|
||||
rounded-full flex items-center
|
||||
justify-center mx-auto mb-4"
|
||||
>
|
||||
<CheckCircle
|
||||
className="w-12 h-12 text-green-600"
|
||||
/>
|
||||
</div>
|
||||
<h1
|
||||
className="text-3xl font-bold text-gray-900
|
||||
mb-2"
|
||||
>
|
||||
Đặt phòng thành công!
|
||||
</h1>
|
||||
<p className="text-gray-600 mb-4">
|
||||
Cảm ơn bạn đã đặt phòng tại khách sạn của chúng
|
||||
tôi
|
||||
</p>
|
||||
|
||||
{/* Booking Number */}
|
||||
<div
|
||||
className="inline-flex items-center gap-2
|
||||
bg-indigo-50 px-6 py-3 rounded-lg"
|
||||
>
|
||||
<span className="text-sm text-indigo-600
|
||||
font-medium"
|
||||
>
|
||||
Mã đặt phòng:
|
||||
</span>
|
||||
<span className="text-lg font-bold
|
||||
text-indigo-900"
|
||||
>
|
||||
{booking.booking_number}
|
||||
</span>
|
||||
<button
|
||||
onClick={copyBookingNumber}
|
||||
className="ml-2 p-1 hover:bg-indigo-100
|
||||
rounded transition-colors"
|
||||
title="Sao chép mã"
|
||||
>
|
||||
{copiedBookingNumber ? (
|
||||
<Check className="w-4 h-4 text-green-600" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4 text-indigo-600" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Status Badge */}
|
||||
<div className="mt-4">
|
||||
<span
|
||||
className={`inline-block px-4 py-2
|
||||
rounded-full text-sm font-medium
|
||||
${getStatusColor(booking.status)}`}
|
||||
>
|
||||
{getStatusText(booking.status)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Booking Details */}
|
||||
<div className="bg-white rounded-lg shadow-md
|
||||
p-6 mb-6"
|
||||
>
|
||||
<h2 className="text-xl font-bold text-gray-900
|
||||
mb-4"
|
||||
>
|
||||
Chi tiết đặt phòng
|
||||
</h2>
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* Room Information */}
|
||||
{roomType && (
|
||||
<div className="border-b pb-4">
|
||||
<div className="flex items-start gap-4">
|
||||
{roomType.images?.[0] && (
|
||||
<img
|
||||
src={roomType.images[0]}
|
||||
alt={roomType.name}
|
||||
className="w-24 h-24 object-cover
|
||||
rounded-lg"
|
||||
/>
|
||||
)}
|
||||
<div className="flex-1">
|
||||
<h3 className="font-bold text-lg
|
||||
text-gray-900"
|
||||
>
|
||||
{roomType.name}
|
||||
</h3>
|
||||
{room && (
|
||||
<p className="text-gray-600 text-sm">
|
||||
<MapPin className="w-4 h-4
|
||||
inline mr-1"
|
||||
/>
|
||||
Phòng {room.room_number} -
|
||||
Tầng {room.floor}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-indigo-600
|
||||
font-semibold mt-1"
|
||||
>
|
||||
{formatPrice(roomType.base_price)}/đêm
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Dates */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2
|
||||
gap-4"
|
||||
>
|
||||
<div>
|
||||
<p className="text-sm text-gray-600 mb-1">
|
||||
<Calendar className="w-4 h-4 inline mr-1" />
|
||||
Ngày nhận phòng
|
||||
</p>
|
||||
<p className="font-medium text-gray-900">
|
||||
{formatDate(booking.check_in_date)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-600 mb-1">
|
||||
<Calendar className="w-4 h-4 inline mr-1" />
|
||||
Ngày trả phòng
|
||||
</p>
|
||||
<p className="font-medium text-gray-900">
|
||||
{formatDate(booking.check_out_date)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Guest Count */}
|
||||
<div>
|
||||
<p className="text-sm text-gray-600 mb-1">
|
||||
<Users className="w-4 h-4 inline mr-1" />
|
||||
Số người
|
||||
</p>
|
||||
<p className="font-medium text-gray-900">
|
||||
{booking.guest_count} người
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Notes */}
|
||||
{booking.notes && (
|
||||
<div>
|
||||
<p className="text-sm text-gray-600 mb-1">
|
||||
<FileText className="w-4 h-4 inline mr-1" />
|
||||
Ghi chú
|
||||
</p>
|
||||
<p className="font-medium text-gray-900">
|
||||
{booking.notes}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Payment Method */}
|
||||
<div className="border-t pt-4">
|
||||
<p className="text-sm text-gray-600 mb-1">
|
||||
<CreditCard className="w-4 h-4 inline mr-1" />
|
||||
Phương thức thanh toán
|
||||
</p>
|
||||
<p className="font-medium text-gray-900">
|
||||
{booking.payment_method === 'cash'
|
||||
? '💵 Thanh toán tại chỗ'
|
||||
: '🏦 Chuyển khoản ngân hàng'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Total Price */}
|
||||
<div className="border-t pt-4">
|
||||
<div className="flex justify-between
|
||||
items-center"
|
||||
>
|
||||
<span className="text-lg font-semibold
|
||||
text-gray-900"
|
||||
>
|
||||
Tổng thanh toán
|
||||
</span>
|
||||
<span className="text-2xl font-bold
|
||||
text-indigo-600"
|
||||
>
|
||||
{formatPrice(booking.total_price)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Guest Information */}
|
||||
{booking.guest_info && (
|
||||
<div className="bg-white rounded-lg shadow-md
|
||||
p-6 mb-6"
|
||||
>
|
||||
<h2 className="text-xl font-bold text-gray-900
|
||||
mb-4"
|
||||
>
|
||||
Thông tin khách hàng
|
||||
</h2>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">
|
||||
<User className="w-4 h-4 inline mr-1" />
|
||||
Họ và tên
|
||||
</p>
|
||||
<p className="font-medium text-gray-900">
|
||||
{booking.guest_info.full_name}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">
|
||||
<Mail className="w-4 h-4 inline mr-1" />
|
||||
Email
|
||||
</p>
|
||||
<p className="font-medium text-gray-900">
|
||||
{booking.guest_info.email}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">
|
||||
<Phone className="w-4 h-4 inline mr-1" />
|
||||
Số điện thoại
|
||||
</p>
|
||||
<p className="font-medium text-gray-900">
|
||||
{booking.guest_info.phone}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Bank Transfer Instructions */}
|
||||
{booking.payment_method === 'bank_transfer' && (
|
||||
<div
|
||||
className="bg-blue-50 border border-blue-200
|
||||
rounded-lg p-6 mb-6"
|
||||
>
|
||||
<div className="flex items-start gap-3 mb-4">
|
||||
<Building2
|
||||
className="w-6 h-6 text-blue-600
|
||||
mt-1 flex-shrink-0"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<h3 className="font-bold text-blue-900 mb-2">
|
||||
Hướng dẫn chuyển khoản
|
||||
</h3>
|
||||
<div className="space-y-2 text-sm
|
||||
text-blue-800"
|
||||
>
|
||||
<p>
|
||||
Vui lòng chuyển khoản theo thông tin sau:
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1
|
||||
md:grid-cols-2 gap-4"
|
||||
>
|
||||
{/* Bank Info */}
|
||||
<div className="bg-white rounded-lg
|
||||
p-4 space-y-2"
|
||||
>
|
||||
<p>
|
||||
<strong>Ngân hàng:</strong>
|
||||
Vietcombank (VCB)
|
||||
</p>
|
||||
<p>
|
||||
<strong>Số tài khoản:</strong>
|
||||
0123456789
|
||||
</p>
|
||||
<p>
|
||||
<strong>Chủ tài khoản:</strong>
|
||||
KHACH SAN ABC
|
||||
</p>
|
||||
<p>
|
||||
<strong>Số tiền:</strong>{' '}
|
||||
<span className="text-indigo-600
|
||||
font-bold"
|
||||
>
|
||||
{formatPrice(booking.total_price)}
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
<strong>Nội dung:</strong>{' '}
|
||||
<span className="font-mono
|
||||
text-indigo-600"
|
||||
>
|
||||
{booking.booking_number}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* QR Code */}
|
||||
{qrCodeUrl && (
|
||||
<div className="bg-white rounded-lg
|
||||
p-4 flex flex-col items-center
|
||||
justify-center"
|
||||
>
|
||||
<p className="text-sm font-medium
|
||||
text-gray-700 mb-2"
|
||||
>
|
||||
Quét mã QR để chuyển khoản
|
||||
</p>
|
||||
<img
|
||||
src={qrCodeUrl}
|
||||
alt="QR Code"
|
||||
className="w-48 h-48 border-2
|
||||
border-gray-200 rounded-lg"
|
||||
/>
|
||||
<p className="text-xs text-gray-500
|
||||
mt-2 text-center"
|
||||
>
|
||||
Mã QR đã bao gồm đầy đủ thông tin
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-xs italic mt-2">
|
||||
💡 Lưu ý: Vui lòng ghi đúng mã đặt phòng
|
||||
vào nội dung chuyển khoản để chúng tôi
|
||||
có thể xác nhận thanh toán của bạn.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Upload Receipt Section */}
|
||||
{!receiptUploaded ? (
|
||||
<div className="border-t border-blue-200
|
||||
pt-4"
|
||||
>
|
||||
<h4 className="font-semibold text-blue-900
|
||||
mb-3"
|
||||
>
|
||||
📎 Xác nhận thanh toán
|
||||
</h4>
|
||||
<p className="text-sm text-blue-700 mb-3">
|
||||
Sau khi chuyển khoản, vui lòng tải lên
|
||||
ảnh biên lai để chúng tôi xác nhận nhanh
|
||||
hơn.
|
||||
</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
{/* File Input */}
|
||||
<div>
|
||||
<label
|
||||
htmlFor="receipt-upload"
|
||||
className="block w-full px-4 py-3
|
||||
border-2 border-dashed
|
||||
border-blue-300 rounded-lg
|
||||
text-center cursor-pointer
|
||||
hover:border-blue-400
|
||||
hover:bg-blue-100/50
|
||||
transition-colors"
|
||||
>
|
||||
<input
|
||||
id="receipt-upload"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleFileSelect}
|
||||
className="hidden"
|
||||
/>
|
||||
<div className="flex flex-col
|
||||
items-center gap-2"
|
||||
>
|
||||
{previewUrl ? (
|
||||
<>
|
||||
<img
|
||||
src={previewUrl}
|
||||
alt="Preview"
|
||||
className="w-32 h-32
|
||||
object-cover rounded"
|
||||
/>
|
||||
<p className="text-sm
|
||||
text-blue-600 font-medium"
|
||||
>
|
||||
{selectedFile?.name}
|
||||
</p>
|
||||
<p className="text-xs
|
||||
text-gray-500"
|
||||
>
|
||||
Click để chọn ảnh khác
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<FileText
|
||||
className="w-8 h-8
|
||||
text-blue-400"
|
||||
/>
|
||||
<p className="text-sm
|
||||
text-blue-600 font-medium"
|
||||
>
|
||||
Chọn ảnh biên lai
|
||||
</p>
|
||||
<p className="text-xs
|
||||
text-gray-500"
|
||||
>
|
||||
PNG, JPG, JPEG (Tối đa 5MB)
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Upload Button */}
|
||||
{selectedFile && (
|
||||
<button
|
||||
onClick={handleUploadReceipt}
|
||||
disabled={uploadingReceipt}
|
||||
className="w-full px-4 py-3
|
||||
bg-blue-600 text-white
|
||||
rounded-lg hover:bg-blue-700
|
||||
transition-colors font-semibold
|
||||
disabled:bg-gray-400
|
||||
disabled:cursor-not-allowed
|
||||
flex items-center
|
||||
justify-center gap-2"
|
||||
>
|
||||
{uploadingReceipt ? (
|
||||
<>
|
||||
<Loader2
|
||||
className="w-5 h-5
|
||||
animate-spin"
|
||||
/>
|
||||
Đang gửi...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CheckCircle
|
||||
className="w-5 h-5"
|
||||
/>
|
||||
Xác nhận đã thanh toán
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="border-t border-green-200
|
||||
pt-4 bg-green-50 rounded-lg p-4"
|
||||
>
|
||||
<div className="flex items-center
|
||||
gap-3"
|
||||
>
|
||||
<CheckCircle
|
||||
className="w-6 h-6 text-green-600
|
||||
flex-shrink-0"
|
||||
/>
|
||||
<div>
|
||||
<p className="font-semibold
|
||||
text-green-900"
|
||||
>
|
||||
Payment confirmation sent
|
||||
</p>
|
||||
<p className="text-sm text-green-700">
|
||||
We will confirm your order
|
||||
as soon as possible.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Important Notice */}
|
||||
<div
|
||||
className="bg-yellow-50 border border-yellow-200
|
||||
rounded-lg p-4 mb-6"
|
||||
>
|
||||
<p className="text-sm text-yellow-800">
|
||||
⚠️ <strong>Important Notice:</strong>
|
||||
</p>
|
||||
<ul className="text-sm text-yellow-700 mt-2
|
||||
space-y-1 ml-4 list-disc"
|
||||
>
|
||||
<li>
|
||||
Please bring your ID card when checking in
|
||||
</li>
|
||||
<li>
|
||||
Check-in time: 14:00 /
|
||||
Check-out time: 12:00
|
||||
</li>
|
||||
<li>
|
||||
If you cancel the booking, 20% of
|
||||
the total order value will be charged
|
||||
</li>
|
||||
{booking.payment_method === 'bank_transfer' && (
|
||||
<li>
|
||||
Please transfer within 24 hours
|
||||
to secure your room
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex flex-col sm:flex-row gap-4">
|
||||
<Link
|
||||
to="/bookings"
|
||||
className="flex-1 flex items-center
|
||||
justify-center gap-2 px-6 py-3
|
||||
bg-indigo-600 text-white rounded-lg
|
||||
hover:bg-indigo-700 transition-colors
|
||||
font-semibold"
|
||||
>
|
||||
<ListOrdered className="w-5 h-5" />
|
||||
Xem đơn của tôi
|
||||
</Link>
|
||||
<Link
|
||||
to="/"
|
||||
className="flex-1 flex items-center
|
||||
justify-center gap-2 px-6 py-3
|
||||
bg-gray-600 text-white rounded-lg
|
||||
hover:bg-gray-700 transition-colors
|
||||
font-semibold"
|
||||
>
|
||||
<Home className="w-5 h-5" />
|
||||
Về trang chủ
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BookingSuccessPage;
|
||||
Reference in New Issue
Block a user