Files
worker/html/receivables-management.html
2025-10-24 11:31:48 +07:00

919 lines
30 KiB
HTML

<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Quản lý Công nợ - EuroTile Dealer</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="assets/css/style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.receivables-container {
min-height: calc(100vh - 120px);
background: #f8f9fa;
padding: 15px;
}
.summary-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.summary-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 16px;
padding: 20px;
color: white;
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.2);
}
.summary-card.warning {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.summary-card.success {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.summary-title {
font-size: 0.9rem;
opacity: 0.9;
margin-bottom: 5px;
}
.summary-amount {
font-size: 1.8rem;
font-weight: 700;
margin-bottom: 10px;
}
.summary-detail {
font-size: 0.8rem;
opacity: 0.8;
display: flex;
align-items: center;
gap: 5px;
}
.filter-section {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
}
.filter-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 15px;
}
.filter-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.filter-label {
font-weight: 600;
color: #374151;
font-size: 0.9rem;
}
.filter-select,
.filter-input {
padding: 10px 12px;
border: 2px solid #e5e7eb;
border-radius: 8px;
font-size: 0.9rem;
transition: border-color 0.3s ease;
}
.filter-select:focus,
.filter-input:focus {
outline: none;
border-color: #667eea;
}
.receivables-list {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
}
.receivable-item {
display: flex;
align-items: center;
padding: 20px;
border-bottom: 1px solid #f3f4f6;
cursor: pointer;
transition: all 0.3s ease;
}
.receivable-item:hover {
background: #f9fafb;
}
.receivable-item:last-child {
border-bottom: none;
}
.customer-avatar {
width: 50px;
height: 50px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 700;
font-size: 1.2rem;
margin-right: 15px;
flex-shrink: 0;
}
.receivable-info {
flex: 1;
}
.customer-name {
font-weight: 700;
color: #1f2937;
margin-bottom: 5px;
font-size: 1rem;
}
.order-details {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 5px;
}
.order-id {
color: #6b7280;
font-size: 0.9rem;
}
.order-date {
color: #9ca3af;
font-size: 0.85rem;
}
.payment-info {
display: flex;
align-items: center;
gap: 10px;
}
.overdue-badge {
background: #fee2e2;
color: #dc2626;
padding: 4px 8px;
border-radius: 12px;
font-size: 0.75rem;
font-weight: 600;
}
.due-soon-badge {
background: #fef3c7;
color: #d97706;
padding: 4px 8px;
border-radius: 12px;
font-size: 0.75rem;
font-weight: 600;
}
.receivable-amount {
text-align: right;
margin-left: 15px;
flex-shrink: 0;
}
.amount-value {
font-size: 1.2rem;
font-weight: 700;
color: #1f2937;
margin-bottom: 5px;
}
.amount-overdue {
color: #dc2626;
}
.amount-due-soon {
color: #d97706;
}
.due-date {
font-size: 0.8rem;
color: #6b7280;
}
.action-buttons {
display: flex;
gap: 8px;
margin-left: 15px;
flex-shrink: 0;
}
.btn-action {
padding: 8px 12px;
border-radius: 6px;
border: none;
cursor: pointer;
font-size: 0.8rem;
font-weight: 600;
transition: all 0.3s ease;
}
.btn-remind {
background: #eff6ff;
color: #1d4ed8;
}
.btn-remind:hover {
background: #dbeafe;
}
.btn-collect {
background: #f0fdf4;
color: #16a34a;
}
.btn-collect:hover {
background: #dcfce7;
}
.btn-view {
background: #f3f4f6;
color: #374151;
}
.btn-view:hover {
background: #e5e7eb;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: #9ca3af;
}
.empty-state i {
font-size: 3rem;
margin-bottom: 20px;
color: #d1d5db;
}
.floating-action {
position: fixed;
bottom: 90px;
right: 20px;
width: 60px;
height: 60px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.5rem;
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
cursor: pointer;
transition: all 0.3s ease;
z-index: 100;
}
.floating-action:hover {
transform: translateY(-3px);
box-shadow: 0 12px 35px rgba(102, 126, 234, 0.5);
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 1000;
align-items: center;
justify-content: center;
padding: 20px;
}
.modal.show {
display: flex;
}
.modal-content {
background: white;
border-radius: 16px;
width: 100%;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
}
.modal-header {
padding: 20px 20px 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-title {
font-size: 1.2rem;
font-weight: 700;
color: #1f2937;
}
.modal-close {
background: none;
border: none;
font-size: 1.5rem;
color: #6b7280;
cursor: pointer;
}
.modal-body {
padding: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
font-weight: 600;
color: #374151;
margin-bottom: 8px;
font-size: 0.9rem;
}
.form-input,
.form-select,
.form-textarea {
width: 100%;
padding: 12px;
border: 2px solid #e5e7eb;
border-radius: 8px;
font-size: 0.9rem;
transition: border-color 0.3s ease;
}
.form-input:focus,
.form-select:focus,
.form-textarea:focus {
outline: none;
border-color: #667eea;
}
.form-textarea {
height: 100px;
resize: vertical;
}
.modal-actions {
padding: 0 20px 20px;
display: flex;
gap: 10px;
justify-content: flex-end;
}
.btn-cancel {
padding: 12px 20px;
background: #f3f4f6;
color: #374151;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
}
.btn-submit {
padding: 12px 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
}
@media (max-width: 768px) {
.receivables-container {
padding: 10px;
}
.summary-cards {
grid-template-columns: 1fr;
}
.filter-row {
grid-template-columns: 1fr;
}
.receivable-item {
padding: 15px;
}
.order-details {
flex-direction: column;
align-items: flex-start;
gap: 5px;
}
.action-buttons {
flex-direction: column;
gap: 5px;
}
.btn-action {
font-size: 0.75rem;
padding: 6px 8px;
}
}
</style>
</head>
<body>
<div class="page-wrapper">
<!-- Header -->
<div class="header">
<a href="index.html" class="back-button">
<i class="fas fa-arrow-left"></i>
</a>
<h1 class="header-title">Quản lý Công nợ</h1>
<button class="icon-button" onclick="exportReceivables()">
<i class="fas fa-download"></i>
</button>
</div>
<div class="receivables-container">
<!-- Summary Cards -->
<div class="summary-cards">
<div class="summary-card">
<div class="summary-title">Tổng công nợ</div>
<div class="summary-amount">892.500.000đ</div>
<div class="summary-detail">
<i class="fas fa-users"></i>
<span>45 khách hàng</span>
</div>
</div>
<div class="summary-card warning">
<div class="summary-title">Quá hạn thanh toán</div>
<div class="summary-amount">156.000.000đ</div>
<div class="summary-detail">
<i class="fas fa-exclamation-triangle"></i>
<span>12 đơn hàng</span>
</div>
</div>
<div class="summary-card success">
<div class="summary-title">Sắp đến hạn</div>
<div class="summary-amount">234.750.000đ</div>
<div class="summary-detail">
<i class="fas fa-clock"></i>
<span>18 đơn hàng (7 ngày tới)</span>
</div>
</div>
</div>
<!-- Filter Section -->
<div class="filter-section">
<div class="filter-row">
<div class="filter-group">
<label class="filter-label">Trạng thái</label>
<select class="filter-select" id="statusFilter" onchange="filterReceivables()">
<option value="">Tất cả</option>
<option value="overdue">Quá hạn</option>
<option value="due-soon">Sắp đến hạn</option>
<option value="normal">Bình thường</option>
</select>
</div>
<div class="filter-group">
<label class="filter-label">Khoảng tiền</label>
<select class="filter-select" id="amountFilter" onchange="filterReceivables()">
<option value="">Tất cả</option>
<option value="small">Dưới 10 triệu</option>
<option value="medium">10 - 50 triệu</option>
<option value="large">Trên 50 triệu</option>
</select>
</div>
<div class="filter-group">
<label class="filter-label">Tìm kiếm khách hàng</label>
<input type="text" class="filter-input" id="searchInput" placeholder="Tên hoặc mã đơn hàng..." onkeyup="filterReceivables()">
</div>
</div>
</div>
<!-- Receivables List -->
<div class="receivables-list" id="receivablesList">
<!-- Receivable Items will be populated by JavaScript -->
</div>
</div>
<!-- Bottom Navigation -->
<div class="bottom-nav">
<a href="index.html" class="nav-item">
<i class="fas fa-home"></i>
<span>Trang chủ</span>
</a>
<a href="loyalty.html" class="nav-item">
<i class="fas fa-star"></i>
<span>Hội viên</span>
</a>
<a href="promotions.html" class="nav-item">
<i class="fas fa-tags"></i>
<span>Khuyến mãi</span>
</a>
<a href="notifications.html" class="nav-item">
<i class="fas fa-bell"></i>
<span>Thông báo</span>
</a>
<a href="account.html" class="nav-item active">
<i class="fas fa-user"></i>
<span>Cài đặt</span>
</a>
</div>
<!-- Floating Action Button -->
<div class="floating-action" onclick="openAddReceivableModal()">
<i class="fas fa-plus"></i>
</div>
</div>
<!-- Reminder Modal -->
<div class="modal" id="reminderModal">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Gửi nhắc nợ</h3>
<button class="modal-close" onclick="closeModal('reminderModal')">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label class="form-label">Khách hàng</label>
<input type="text" class="form-input" id="reminderCustomer" readonly>
</div>
<div class="form-group">
<label class="form-label">Số tiền nợ</label>
<input type="text" class="form-input" id="reminderAmount" readonly>
</div>
<div class="form-group">
<label class="form-label">Phương thức nhắc nhở</label>
<select class="form-select" id="reminderMethod">
<option value="sms">Tin nhắn SMS</option>
<option value="call">Gọi điện thoại</option>
<option value="email">Email</option>
<option value="visit">Đến thăm trực tiếp</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Nội dung nhắc nhở</label>
<textarea class="form-textarea" id="reminderMessage" placeholder="Nhập nội dung nhắc nở...">Kính chào anh/chị,
EuroTile xin nhắc nhở về việc thanh toán công nợ đơn hàng. Vui lòng liên hệ với chúng tôi để sắp xếp thanh toán.
Xin cảm ơn!</textarea>
</div>
</div>
<div class="modal-actions">
<button class="btn-cancel" onclick="closeModal('reminderModal')">Hủy</button>
<button class="btn-submit" onclick="sendReminder()">Gửi nhắc nhở</button>
</div>
</div>
</div>
<!-- Collection Modal -->
<div class="modal" id="collectionModal">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Thu tiền</h3>
<button class="modal-close" onclick="closeModal('collectionModal')">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label class="form-label">Khách hàng</label>
<input type="text" class="form-input" id="collectionCustomer" readonly>
</div>
<div class="form-group">
<label class="form-label">Tổng số tiền nợ</label>
<input type="text" class="form-input" id="collectionTotalAmount" readonly>
</div>
<div class="form-group">
<label class="form-label">Số tiền thu</label>
<input type="number" class="form-input" id="collectionAmount" placeholder="Nhập số tiền thu được...">
</div>
<div class="form-group">
<label class="form-label">Phương thức thanh toán</label>
<select class="form-select" id="paymentMethod">
<option value="cash">Tiền mặt</option>
<option value="transfer">Chuyển khoản</option>
<option value="check">Séc</option>
<option value="card">Thẻ tín dụng</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Ghi chú</label>
<textarea class="form-textarea" id="collectionNote" placeholder="Ghi chú về việc thu tiền..."></textarea>
</div>
</div>
<div class="modal-actions">
<button class="btn-cancel" onclick="closeModal('collectionModal')">Hủy</button>
<button class="btn-submit" onclick="recordPayment()">Ghi nhận thanh toán</button>
</div>
</div>
</div>
<script>
// Sample receivables data
const receivablesData = [
{
id: 'DH001234',
customer: 'Công ty TNHH Xây dựng Minh An',
customerCode: 'KH001',
phone: '0912345678',
orderDate: '2023-10-15',
dueDate: '2023-11-15',
amount: 85000000,
status: 'overdue',
daysOverdue: 15
},
{
id: 'DH001235',
customer: 'Anh Nguyễn Văn Hùng',
customerCode: 'KH002',
phone: '0987654321',
orderDate: '2023-11-20',
dueDate: '2023-12-20',
amount: 25750000,
status: 'due-soon',
daysOverdue: 0
},
{
id: 'DH001236',
customer: 'Chị Trần Thị Mai',
customerCode: 'KH003',
phone: '0923456789',
orderDate: '2023-11-25',
dueDate: '2023-12-25',
amount: 12500000,
status: 'normal',
daysOverdue: 0
},
{
id: 'DH001237',
customer: 'Công ty CP Đầu tư Bất động sản ABC',
customerCode: 'KH004',
phone: '0945678912',
orderDate: '2023-10-10',
dueDate: '2023-11-10',
amount: 156000000,
status: 'overdue',
daysOverdue: 20
},
{
id: 'DH001238',
customer: 'Anh Lê Minh Tuấn',
customerCode: 'KH005',
phone: '0934567890',
orderDate: '2023-11-28',
dueDate: '2023-12-28',
amount: 18900000,
status: 'due-soon',
daysOverdue: 0
}
];
let filteredReceivables = [...receivablesData];
// Initialize page
document.addEventListener('DOMContentLoaded', function() {
renderReceivables();
});
function renderReceivables() {
const container = document.getElementById('receivablesList');
if (filteredReceivables.length === 0) {
container.innerHTML = `
<div class="empty-state">
<i class="fas fa-receipt"></i>
<h3>Không có công nợ nào</h3>
<p>Không tìm thấy công nợ phù hợp với bộ lọc hiện tại</p>
</div>
`;
return;
}
container.innerHTML = filteredReceivables.map(receivable => `
<div class="receivable-item" onclick="viewReceivableDetail('${receivable.id}')">
<div class="customer-avatar">
${receivable.customer.charAt(0).toUpperCase()}
</div>
<div class="receivable-info">
<div class="customer-name">${receivable.customer}</div>
<div class="order-details">
<span class="order-id">Đơn hàng: ${receivable.id}</span>
<span class="order-date">Ngày đặt: ${formatDate(receivable.orderDate)}</span>
</div>
<div class="payment-info">
${getStatusBadge(receivable.status, receivable.daysOverdue)}
</div>
</div>
<div class="receivable-amount">
<div class="amount-value ${getAmountClass(receivable.status)}">
${formatCurrency(receivable.amount)}
</div>
<div class="due-date">Hạn: ${formatDate(receivable.dueDate)}</div>
</div>
<div class="action-buttons">
<button class="btn-action btn-remind" onclick="event.stopPropagation(); openReminderModal('${receivable.id}')">
<i class="fas fa-bell"></i>
</button>
<button class="btn-action btn-collect" onclick="event.stopPropagation(); openCollectionModal('${receivable.id}')">
<i class="fas fa-money-bill-wave"></i>
</button>
<button class="btn-action btn-view" onclick="event.stopPropagation(); viewReceivableDetail('${receivable.id}')">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
`).join('');
}
function getStatusBadge(status, daysOverdue) {
switch(status) {
case 'overdue':
return `<span class="overdue-badge">Quá hạn ${daysOverdue} ngày</span>`;
case 'due-soon':
return `<span class="due-soon-badge">Sắp đến hạn</span>`;
default:
return '';
}
}
function getAmountClass(status) {
switch(status) {
case 'overdue':
return 'amount-overdue';
case 'due-soon':
return 'amount-due-soon';
default:
return '';
}
}
function filterReceivables() {
const statusFilter = document.getElementById('statusFilter').value;
const amountFilter = document.getElementById('amountFilter').value;
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
filteredReceivables = receivablesData.filter(receivable => {
// Status filter
if (statusFilter && receivable.status !== statusFilter) {
return false;
}
// Amount filter
if (amountFilter) {
const amount = receivable.amount;
if (amountFilter === 'small' && amount >= 10000000) return false;
if (amountFilter === 'medium' && (amount < 10000000 || amount > 50000000)) return false;
if (amountFilter === 'large' && amount <= 50000000) return false;
}
// Search filter
if (searchTerm) {
const searchableText = `${receivable.customer} ${receivable.id}`.toLowerCase();
if (!searchableText.includes(searchTerm)) return false;
}
return true;
});
renderReceivables();
}
function openReminderModal(receivableId) {
const receivable = receivablesData.find(r => r.id === receivableId);
if (!receivable) return;
document.getElementById('reminderCustomer').value = receivable.customer;
document.getElementById('reminderAmount').value = formatCurrency(receivable.amount);
document.getElementById('reminderModal').classList.add('show');
}
function openCollectionModal(receivableId) {
const receivable = receivablesData.find(r => r.id === receivableId);
if (!receivable) return;
document.getElementById('collectionCustomer').value = receivable.customer;
document.getElementById('collectionTotalAmount').value = formatCurrency(receivable.amount);
document.getElementById('collectionAmount').value = '';
document.getElementById('collectionModal').classList.add('show');
}
function closeModal(modalId) {
document.getElementById(modalId).classList.remove('show');
}
function sendReminder() {
const method = document.getElementById('reminderMethod').value;
const message = document.getElementById('reminderMessage').value;
if (!message.trim()) {
alert('Vui lòng nhập nội dung nhắc nhở');
return;
}
// Simulate sending reminder
alert('Đã gửi nhắc nhở thành công!');
closeModal('reminderModal');
}
function recordPayment() {
const amount = document.getElementById('collectionAmount').value;
const method = document.getElementById('paymentMethod').value;
if (!amount || amount <= 0) {
alert('Vui lòng nhập số tiền hợp lệ');
return;
}
// Simulate recording payment
alert('Đã ghi nhận thanh toán thành công!');
closeModal('collectionModal');
// Refresh the list (in a real app, this would update the backend)
setTimeout(() => {
window.location.reload();
}, 1000);
}
function viewReceivableDetail(receivableId) {
// Navigate to receivable detail page
alert(`Xem chi tiết công nợ ${receivableId}`);
}
function openAddReceivableModal() {
alert('Chức năng thêm công nợ mới sẽ được phát triển');
}
function exportReceivables() {
// Simulate export functionality
alert('Đang xuất báo cáo công nợ...');
}
// Utility functions
function formatCurrency(amount) {
return new Intl.NumberFormat('vi-VN', {
style: 'currency',
currency: 'VND',
minimumFractionDigits: 0
}).format(amount);
}
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('vi-VN');
}
// Close modals when clicking outside
window.onclick = function(event) {
const modals = document.querySelectorAll('.modal');
modals.forEach(modal => {
if (event.target === modal) {
modal.classList.remove('show');
}
});
}
</script>
</body>
</html>