date drop added
This commit is contained in:
@@ -420,6 +420,70 @@ button, a, input, select, textarea {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Date Filter */
|
||||
.date-filter-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.date-filter-select {
|
||||
padding: 10px 14px;
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
cursor: pointer;
|
||||
min-height: 48px;
|
||||
min-width: 160px;
|
||||
transition: border-color 0.3s;
|
||||
appearance: auto;
|
||||
}
|
||||
|
||||
.date-filter-select:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.date-filter-custom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.date-filter-input {
|
||||
padding: 9px 12px;
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
min-height: 44px;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.date-filter-input:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.date-filter-separator {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.date-filter-apply {
|
||||
padding: 9px 18px !important;
|
||||
font-size: 14px !important;
|
||||
min-height: 44px !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Sync Button with Integrated Loading State */
|
||||
.sync-button {
|
||||
position: relative;
|
||||
@@ -1047,6 +1111,14 @@ button, a, input, select, textarea {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.date-filter-group {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.date-filter-select {
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -1074,6 +1146,28 @@ button, a, input, select, textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.date-filter-group {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.date-filter-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.date-filter-custom {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.date-filter-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.date-filter-separator {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.status-modal-content {
|
||||
padding: 25px 20px;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// Dashboard functionality
|
||||
|
||||
let currentFilter = 'all';
|
||||
let currentDateFilter = 'today';
|
||||
let customDateStart = '';
|
||||
let customDateEnd = '';
|
||||
let currentOrderIdForCancel = null;
|
||||
let pendingStatusChange = {
|
||||
orderId: null,
|
||||
@@ -118,6 +121,7 @@ function refreshAbandonedCallCount() {
|
||||
// Initialize dashboard
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
setupFilterButtons();
|
||||
setupDateFilter();
|
||||
audioNotification.init();
|
||||
refreshOrders();
|
||||
refreshAbandonedCallCount();
|
||||
@@ -136,19 +140,135 @@ function setupFilterButtons() {
|
||||
|
||||
filterButtons.forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
// Remove active class from all buttons
|
||||
filterButtons.forEach(btn => btn.classList.remove('active'));
|
||||
|
||||
// Add active class to clicked button
|
||||
this.classList.add('active');
|
||||
|
||||
// Update filter and refresh
|
||||
currentFilter = this.dataset.filter;
|
||||
refreshOrders();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setupDateFilter() {
|
||||
const select = document.getElementById('dateFilterSelect');
|
||||
const customPanel = document.getElementById('dateFilterCustom');
|
||||
const applyBtn = document.getElementById('dateFilterApply');
|
||||
const fromInput = document.getElementById('dateFilterFrom');
|
||||
const toInput = document.getElementById('dateFilterTo');
|
||||
|
||||
if (!select || !customPanel || !applyBtn || !fromInput || !toInput) return;
|
||||
|
||||
// Set default dates on the pickers to today
|
||||
const todayStr = formatDateForInput(new Date());
|
||||
fromInput.value = todayStr;
|
||||
toInput.value = todayStr;
|
||||
|
||||
select.addEventListener('change', function() {
|
||||
const val = this.value;
|
||||
currentDateFilter = val;
|
||||
|
||||
if (val === 'custom') {
|
||||
customPanel.style.display = 'flex';
|
||||
// Entering custom mode should immediately reflect the currently selected dates
|
||||
// (defaults to today on first use). Further changes require pressing Apply.
|
||||
if (fromInput.value && toInput.value) {
|
||||
customDateStart = fromInput.value;
|
||||
customDateEnd = toInput.value;
|
||||
}
|
||||
refreshOrders();
|
||||
} else {
|
||||
customPanel.style.display = 'none';
|
||||
refreshOrders();
|
||||
}
|
||||
|
||||
updateStatsTotalLabel();
|
||||
});
|
||||
|
||||
applyBtn.addEventListener('click', function() {
|
||||
if (!fromInput.value || !toInput.value) {
|
||||
showToast('Please select both start and end dates', 'error');
|
||||
return;
|
||||
}
|
||||
if (fromInput.value > toInput.value) {
|
||||
showToast('Start date must be before end date', 'error');
|
||||
return;
|
||||
}
|
||||
customDateStart = fromInput.value;
|
||||
customDateEnd = toInput.value;
|
||||
refreshOrders();
|
||||
updateStatsTotalLabel();
|
||||
});
|
||||
|
||||
// Allow pressing Enter in date inputs to apply
|
||||
fromInput.addEventListener('keydown', function(e) { if (e.key === 'Enter') applyBtn.click(); });
|
||||
toInput.addEventListener('keydown', function(e) { if (e.key === 'Enter') applyBtn.click(); });
|
||||
}
|
||||
|
||||
function getDateRangeParams() {
|
||||
const now = new Date();
|
||||
|
||||
switch (currentDateFilter) {
|
||||
case 'today': {
|
||||
const start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0);
|
||||
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
||||
return { startDate: Math.floor(start.getTime() / 1000), endDate: Math.floor(end.getTime() / 1000) };
|
||||
}
|
||||
case 'yesterday': {
|
||||
const yd = new Date(now);
|
||||
yd.setDate(yd.getDate() - 1);
|
||||
const start = new Date(yd.getFullYear(), yd.getMonth(), yd.getDate(), 0, 0, 0);
|
||||
const end = new Date(yd.getFullYear(), yd.getMonth(), yd.getDate(), 23, 59, 59, 999);
|
||||
return { startDate: Math.floor(start.getTime() / 1000), endDate: Math.floor(end.getTime() / 1000) };
|
||||
}
|
||||
case 'last7': {
|
||||
const start = new Date(now);
|
||||
start.setDate(start.getDate() - 6);
|
||||
start.setHours(0, 0, 0, 0);
|
||||
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
||||
return { startDate: Math.floor(start.getTime() / 1000), endDate: Math.floor(end.getTime() / 1000) };
|
||||
}
|
||||
case 'last30': {
|
||||
const start = new Date(now);
|
||||
start.setDate(start.getDate() - 29);
|
||||
start.setHours(0, 0, 0, 0);
|
||||
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
||||
return { startDate: Math.floor(start.getTime() / 1000), endDate: Math.floor(end.getTime() / 1000) };
|
||||
}
|
||||
case 'all':
|
||||
// Use a minimal unix start date to represent "all time" without relying on server defaults.
|
||||
return { startDate: 1 };
|
||||
case 'custom': {
|
||||
if (!customDateStart || !customDateEnd) return {};
|
||||
const start = new Date(customDateStart + 'T00:00:00');
|
||||
const end = new Date(customDateEnd + 'T23:59:59');
|
||||
return { startDate: Math.floor(start.getTime() / 1000), endDate: Math.floor(end.getTime() / 1000) };
|
||||
}
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function formatDateForInput(date) {
|
||||
const y = date.getFullYear();
|
||||
const m = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const d = String(date.getDate()).padStart(2, '0');
|
||||
return y + '-' + m + '-' + d;
|
||||
}
|
||||
|
||||
function updateStatsTotalLabel() {
|
||||
const label = document.getElementById('stat-total-label');
|
||||
if (!label) return;
|
||||
|
||||
const labels = {
|
||||
today: 'Total Today',
|
||||
yesterday: 'Total Yesterday',
|
||||
last7: 'Total (7 Days)',
|
||||
last30: 'Total (30 Days)',
|
||||
all: 'Total (All Time)',
|
||||
custom: 'Total (Custom)'
|
||||
};
|
||||
label.textContent = labels[currentDateFilter] || 'Total';
|
||||
}
|
||||
|
||||
function refreshOrders() {
|
||||
const syncButton = document.getElementById('syncButton');
|
||||
const syncText = syncButton ? syncButton.querySelector('.sync-text') : null;
|
||||
@@ -161,8 +281,15 @@ function refreshOrders() {
|
||||
}
|
||||
}
|
||||
|
||||
const statusParam = currentFilter === 'all' ? '' : currentFilter;
|
||||
const url = '/api/orders' + (statusParam ? '?status=' + statusParam : '');
|
||||
const params = new URLSearchParams();
|
||||
if (currentFilter !== 'all') {
|
||||
params.set('status', currentFilter);
|
||||
}
|
||||
const dateRange = getDateRangeParams();
|
||||
if (dateRange.startDate) params.set('startDate', dateRange.startDate);
|
||||
if (dateRange.endDate) params.set('endDate', dateRange.endDate);
|
||||
const qs = params.toString();
|
||||
const url = '/api/orders' + (qs ? '?' + qs : '');
|
||||
|
||||
fetch(url)
|
||||
.then(response => response.json())
|
||||
|
||||
Reference in New Issue
Block a user