// Trial Periods Page Management // This file handles the standalone trial periods page let trialPeriodsData = []; let filteredTrialPeriodsData = []; let trialCurrentPage = 1; let trialPageSize = 10; let trialTotalItems = 0; let trialTotalPages = 0; let customersMap = {}; // Map of customer ID to customer name let trialStartDateFilter = ''; let trialEndDateFilter = ''; let trialSortOrder = 'desc'; // Initialize trial periods page function initTrialPeriodsPage() { const addTrialBtn = document.getElementById('addTrialBtn'); const trialPeriodsSection = document.getElementById('trialPeriodsSection'); if (!trialPeriodsSection) return; // Add trial button click if (addTrialBtn) { addTrialBtn.addEventListener('click', function () { openAddTrialModal(); }); } // Pagination controls document.getElementById('trialFirstPage')?.addEventListener('click', () => { trialCurrentPage = 1; applyTrialFiltersAndSort(); }); document.getElementById('trialPrevPage')?.addEventListener('click', () => { if (trialCurrentPage > 1) { trialCurrentPage--; applyTrialFiltersAndSort(); } }); document.getElementById('trialNextPage')?.addEventListener('click', () => { if (trialCurrentPage < trialTotalPages) { trialCurrentPage++; applyTrialFiltersAndSort(); } }); document.getElementById('trialLastPage')?.addEventListener('click', () => { trialCurrentPage = trialTotalPages; applyTrialFiltersAndSort(); }); document.getElementById('trialPageSizeSelect')?.addEventListener('change', function () { trialPageSize = parseInt(this.value); trialCurrentPage = 1; applyTrialFiltersAndSort(); }); // Filter and sort event listeners document.getElementById('trialStartDateFilter')?.addEventListener('change', function () { trialStartDateFilter = this.value; trialCurrentPage = 1; applyTrialFiltersAndSort(); }); document.getElementById('trialEndDateFilter')?.addEventListener('change', function () { trialEndDateFilter = this.value; trialCurrentPage = 1; applyTrialFiltersAndSort(); }); document.getElementById('trialSortOrder')?.addEventListener('change', function () { trialSortOrder = this.value; trialCurrentPage = 1; applyTrialFiltersAndSort(); }); // Load customers map for displaying customer names loadCustomersMap(); } // Load customers map for displaying customer names in table async function loadCustomersMap() { try { console.log('Loading customers map...'); const response = await authenticatedFetch('/api/customers/list'); if (!response) { console.error('No response from API'); return; } const data = await response.json(); console.log('Customers data:', data); // Use the complete customer map for ID->Name lookups customersMap = data.customerMap || {}; console.log('Number of customer ID mappings:', Object.keys(customersMap).length); } catch (error) { console.error('Error loading customers map:', error); } } // Alias for loadCustomersMap - used by main.js when switching to trial periods section async function loadCustomersForDropdown() { return await loadCustomersMap(); } // Load all trial periods async function loadAllTrialPeriods() { try { const response = await authenticatedFetch('/api/trial-periods/all'); const data = await response.json(); trialPeriodsData = data.trialPeriods || []; applyTrialFiltersAndSort(); renderExpiryWarnings(); } catch (error) { console.error('Error loading trial periods:', error); trialPeriodsData = []; filteredTrialPeriodsData = []; renderTrialPeriodsTable(); } } // Apply filters and sorting to trial periods function applyTrialFiltersAndSort() { let filtered = [...trialPeriodsData]; // Apply date range filter if (trialStartDateFilter) { filtered = filtered.filter(period => { const endDate = new Date(period.endTime).toISOString().split('T')[0]; return endDate >= trialStartDateFilter; }); } if (trialEndDateFilter) { filtered = filtered.filter(period => { const endDate = new Date(period.endTime).toISOString().split('T')[0]; return endDate <= trialEndDateFilter; }); } // Sort by end time filtered.sort((a, b) => { const dateA = new Date(a.endTime); const dateB = new Date(b.endTime); return trialSortOrder === 'asc' ? dateA - dateB : dateB - dateA; }); filteredTrialPeriodsData = filtered; trialTotalItems = filteredTrialPeriodsData.length; trialTotalPages = Math.ceil(trialTotalItems / trialPageSize); renderTrialPeriodsTable(); updateTrialPagination(); } // Render expiry warning cards function renderExpiryWarnings() { const warningsContainer = document.getElementById('trialExpiryWarnings'); if (!warningsContainer) return; warningsContainer.innerHTML = ''; const now = new Date(); now.setHours(0, 0, 0, 0); // Set to start of today const warnings = []; trialPeriodsData.forEach(period => { const endTime = new Date(period.endTime); endTime.setHours(0, 0, 0, 0); // Set to start of day const daysUntilExpiry = Math.ceil((endTime - now) / (1000 * 60 * 60 * 24)); const customerName = customersMap[period.customerId] || period.customerId; if (daysUntilExpiry >= 0 && daysUntilExpiry <= 3) { warnings.push({ customerName, endTime: period.endTime, daysUntilExpiry, period }); } }); // Sort by days until expiry (most urgent first) warnings.sort((a, b) => a.daysUntilExpiry - b.daysUntilExpiry); warnings.forEach(warning => { const card = document.createElement('div'); let warningClass = 'warning-soon'; let iconClass = 'fa-info-circle'; let title = '试用即将到期'; let message = ''; if (warning.daysUntilExpiry === 0) { warningClass = 'warning-today'; iconClass = 'fa-exclamation-triangle'; title = '试用今日到期'; message = `${warning.customerName} 客户的试用期将于今天到期,请及时跟进!`; } else if (warning.daysUntilExpiry === 1) { warningClass = 'warning-tomorrow'; iconClass = 'fa-exclamation-circle'; title = '试用明日到期'; message = `${warning.customerName} 客户的试用期将于明天到期,请及时跟进!`; } else { warningClass = 'warning-soon'; iconClass = 'fa-info-circle'; title = '试用即将到期'; message = `${warning.customerName} 客户的试用期将于${warning.daysUntilExpiry}天后到期,请及时跟进!`; } const formattedEndTime = formatDateTime(warning.endTime); message += `
试用结束时间:${formattedEndTime}`; card.className = `expiry-warning-card ${warningClass}`; card.innerHTML = `
${title}
${message}
`; warningsContainer.appendChild(card); }); } // Render trial periods table function renderTrialPeriodsTable() { const tbody = document.getElementById('trialPeriodsBody'); if (!tbody) return; tbody.innerHTML = ''; if (filteredTrialPeriodsData.length === 0) { const row = document.createElement('tr'); row.innerHTML = `

👥 还没有客户试用信息

点击上方「添加试用时间」开始管理客户试用

`; tbody.appendChild(row); return; } // Paginate data const startIndex = (trialCurrentPage - 1) * trialPageSize; const endIndex = Math.min(startIndex + trialPageSize, filteredTrialPeriodsData.length); const pageData = filteredTrialPeriodsData.slice(startIndex, endIndex); pageData.forEach(period => { const row = document.createElement('tr'); row.classList.add('trial-row'); // 显示客户名称,如果找不到则显示ID const customerName = customersMap[period.customerId] || period.customerId; // 计算状态和到期天数 const now = new Date(); const endDate = new Date(period.endTime); const startDate = new Date(period.startTime); const daysUntilExpiry = Math.ceil((endDate - now) / (1000 * 60 * 60 * 24)); const isTrial = (period.isTrial !== undefined && period.isTrial !== null) ? period.isTrial : false; // 生成状态badge let statusBadge = ''; if (!isTrial) { statusBadge = ' 非试用'; } else if (daysUntilExpiry < 0) { statusBadge = ' 已过期'; } else if (daysUntilExpiry === 0) { statusBadge = ' 今日到期'; } else if (daysUntilExpiry <= 3) { statusBadge = ` ${daysUntilExpiry}天后到期`; } else if (daysUntilExpiry <= 7) { statusBadge = ` ${daysUntilExpiry}天后到期`; } else { statusBadge = ' 试用中'; } const startTime = formatDateTime(period.startTime); const endTime = formatDateTime(period.endTime); const createdAt = formatDateTime(period.createdAt); row.innerHTML = ` ${customerName} ${statusBadge} ${startTime} ${endTime} ${createdAt} `; tbody.appendChild(row); }); // Add event listeners tbody.querySelectorAll('.edit-btn').forEach(btn => { btn.addEventListener('click', function () { const periodId = this.getAttribute('data-id'); openEditTrialModal(periodId); }); }); tbody.querySelectorAll('.delete-btn').forEach(btn => { btn.addEventListener('click', function () { const periodId = this.getAttribute('data-id'); deleteTrialPeriodFromPage(periodId); }); }); } // Update trial pagination function updateTrialPagination() { const startItem = trialTotalItems === 0 ? 0 : (trialCurrentPage - 1) * trialPageSize + 1; const endItem = Math.min(trialCurrentPage * trialPageSize, trialTotalItems); document.getElementById('trialPaginationInfo').textContent = `显示 ${startItem}-${endItem} 共 ${trialTotalItems} 条`; document.getElementById('trialFirstPage').disabled = trialCurrentPage === 1; document.getElementById('trialPrevPage').disabled = trialCurrentPage === 1; document.getElementById('trialNextPage').disabled = trialCurrentPage === trialTotalPages; document.getElementById('trialLastPage').disabled = trialCurrentPage === trialTotalPages; // Update page numbers const pageNumbers = document.getElementById('trialPageNumbers'); pageNumbers.innerHTML = ''; const maxVisiblePages = 5; let startPage = Math.max(1, trialCurrentPage - Math.floor(maxVisiblePages / 2)); let endPage = Math.min(trialTotalPages, startPage + maxVisiblePages - 1); if (endPage - startPage < maxVisiblePages - 1) { startPage = Math.max(1, endPage - maxVisiblePages + 1); } for (let i = startPage; i <= endPage; i++) { const pageBtn = document.createElement('button'); pageBtn.className = 'page-number'; if (i === trialCurrentPage) { pageBtn.classList.add('active'); } pageBtn.textContent = i; pageBtn.addEventListener('click', () => { trialCurrentPage = i; applyTrialFiltersAndSort(); }); pageNumbers.appendChild(pageBtn); } } // Open add trial modal function openAddTrialModal() { console.log('Opening add trial modal'); document.getElementById('trialCustomerInput').value = ''; document.querySelector('input[name="isTrial"][value="true"]').checked = true; document.getElementById('trialStartTime').value = ''; document.getElementById('trialEndTime').value = ''; document.getElementById('addTrialPeriodModal').style.display = 'block'; } // Open edit trial modal function openEditTrialModal(periodId) { const period = filteredTrialPeriodsData.find(p => p.id === periodId); if (!period) return; document.getElementById('editTrialPeriodId').value = period.id; const startDate = new Date(period.startTime); const endDate = new Date(period.endTime); document.getElementById('editTrialStartTime').value = formatDateTimeLocal(startDate); document.getElementById('editTrialEndTime').value = formatDateTimeLocal(endDate); document.getElementById('editTrialPeriodModal').style.display = 'block'; } // Delete trial period from page async function deleteTrialPeriodFromPage(periodId) { if (!confirm('确定要删除这个试用时间记录吗?')) { return; } try { const response = await authenticatedFetch(`/api/trial-periods/${periodId}`, { method: 'DELETE' }); if (response.ok) { await loadAllTrialPeriods(); alert('试用时间删除成功!'); } else { alert('删除试用时间时出错'); } } catch (error) { console.error('Error deleting trial period:', error); alert('删除试用时间时出错'); } } // Create trial period from page async function createTrialPeriodFromPage() { const customerName = document.getElementById('trialCustomerInput').value.trim(); const isTrialValue = document.querySelector('input[name="isTrial"]:checked').value; const isTrial = isTrialValue === 'true'; const startTime = document.getElementById('trialStartTime').value; const endTime = document.getElementById('trialEndTime').value; if (!customerName) { alert('请输入客户名称'); return; } if (!startTime || !endTime) { alert('请填写开始时间和结束时间'); return; } // Find or create customer ID from customer name let customerId = null; for (const [id, name] of Object.entries(customersMap)) { if (name === customerName) { customerId = id; break; } } // If customer doesn't exist in map, generate a new ID if (!customerId) { customerId = customerName; // Use customer name as ID for now } const formData = { customerId: customerId, startTime: new Date(startTime).toISOString(), endTime: new Date(endTime).toISOString(), isTrial: isTrial }; try { const response = await authenticatedFetch('/api/trial-periods', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); if (response.ok) { document.getElementById('addTrialPeriodModal').style.display = 'none'; document.getElementById('addTrialPeriodForm').reset(); await loadAllTrialPeriods(); alert('试用时间添加成功!'); } else { alert('添加试用时间时出错'); } } catch (error) { console.error('Error creating trial period:', error); alert('添加试用时间时出错'); } } // Initialize when switching to trial periods section document.addEventListener('DOMContentLoaded', function () { initTrialPeriodsPage(); // Override the form submit for add trial period const addForm = document.getElementById('addTrialPeriodForm'); if (addForm) { addForm.addEventListener('submit', async function (e) { e.preventDefault(); await createTrialPeriodFromPage(); }); } });