dashboard.js

48.64 KB
04/08/2025 16:01
JS
dashboard.js
/**
 * Admin Dashboard JavaScript
 * Handles all admin panel functionality
 */

// Global state
let currentUser = null;
let currentSection = 'dashboard';
let dashboardData = {};
let ordersData = [];
let productsData = [];
let customersData = [];
let inventoryData = [];

// Dynamic path detection
const BASE_PATH = (() => {
  const path = window.location.pathname.replace(/\/$/, ''); // Remove trailing slash
  const pathParts = path.split('/').filter(part => part !== ''); // Remove empty parts

  console.log('Current path:', path);
  console.log('Path parts:', pathParts);

  // Find admin folder index
  const adminIndex = pathParts.indexOf('admin');
  console.log('Admin index:', adminIndex);

  if (adminIndex > 0) {
    // Return base path up to admin folder (excluding admin)
    const basePath = '/' + pathParts.slice(0, adminIndex).join('/');
    console.log('Detected BASE_PATH:', basePath);
    return basePath;
  }

  // Default to root if admin not found in path
  console.log('Admin not found in path, using empty BASE_PATH');
  return '';
})();

const ADMIN_BASE_PATH = BASE_PATH + '/admin';

// API Base URLs
const API_BASE = BASE_PATH + '/api/admin';
const AUTH_API = BASE_PATH + '/api';

// Initialize dashboard
document.addEventListener('DOMContentLoaded', function() {
  checkAuth();
  setupEventListeners();
  loadDashboardData();
});

// Authentication check
async function checkAuth() {
  const token = localStorage.getItem('adminToken');
  if (!token) {
    window.location.href = ADMIN_BASE_PATH + '/login.html';
    return;
  } try {
    const response = await fetch(`${AUTH_API}/auth/me`, {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    });

    if (!response.ok) {
      throw new Error('Authentication failed');
    }

    const data = await response.json();
    if (data.success && data.data.user.role === 'admin') {
      currentUser = data.data.user;
      document.getElementById('userName').textContent = currentUser.first_name || 'Admin';
    } else {
      throw new Error('Not authorized');
    }
  } catch (error) {
    console.error('Auth check failed:', error);
    localStorage.removeItem('adminToken');
    window.location.href = ADMIN_BASE_PATH + '/login.html';
  }
}

// Setup event listeners
function setupEventListeners() {
  // Navigation
  document.querySelectorAll('.nav-link').forEach(link => {
    link.addEventListener('click', function(e) {
      e.preventDefault();
      const section = this.dataset.section;
      showSection(section);
    });
  });

  // Sidebar toggle for mobile
  const sidebarToggle = document.getElementById('sidebarToggle');
  if (sidebarToggle) {
    sidebarToggle.addEventListener('click', function() {
      document.getElementById('sidebar').classList.toggle('mobile-open');
    });
  }

  // Order status filter
  const orderStatus = document.getElementById('orderStatus');
  if (orderStatus) {
    orderStatus.addEventListener('change', function() {
      loadOrders();
    });
  }

  // Customer search
  const customerSearch = document.getElementById('customerSearch');
  if (customerSearch) {
    let timeout;
    customerSearch.addEventListener('input', function() {
      clearTimeout(timeout);
      timeout = setTimeout(() => {
        loadCustomers();
      }, 500);
    });
  }

  // Modal event listeners
  setupModalEventListeners();
}

// Setup modal event listeners
function setupModalEventListeners() {
  // Close modal when clicking outside
  const editProductModal = document.getElementById('editProductModal');
  if (editProductModal) {
    editProductModal.addEventListener('click', function(e) {
      if (e.target === this) {
        closeEditProductModal();
      }
    });
  }

  // Close modal with Escape key
  document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape') {
      const modal = document.getElementById('editProductModal');
      if (modal && modal.classList.contains('show')) {
        closeEditProductModal();
      }
    }
  });

  // Form validation on input
  const editProductForm = document.getElementById('editProductForm');
  if (editProductForm) {
    // Real-time SKU validation
    const skuInput = document.getElementById('editProductSku');
    if (skuInput) {
      skuInput.addEventListener('blur', function() {
        validateSKU(this.value, document.getElementById('editProductId').value);
      });
    }

    // Price validation
    const priceInputs = ['editProductPrice', 'editProductSalePrice'];
    priceInputs.forEach(inputId => {
      const input = document.getElementById(inputId);
      if (input) {
        input.addEventListener('input', function() {
          if (this.value < 0) {
            this.value = 0;
          }
        });
      }
    });

    // Stock validation
    const stockInputs = ['editProductStock', 'editProductMinStock'];
    stockInputs.forEach(inputId => {
      const input = document.getElementById(inputId);
      if (input) {
        input.addEventListener('input', function() {
          if (this.value < 0) {
            this.value = 0;
          }
        });
      }
    });

    // Image URL preview
    const imageInput = document.getElementById('editProductImage');
    if (imageInput) {
      imageInput.addEventListener('blur', function() {
        previewProductImage(this.value);
      });
    }

    // Tag input formatting
    const tagsInput = document.getElementById('editProductTags');
    if (tagsInput) {
      tagsInput.addEventListener('blur', function() {
        formatTags(this);
      });
    }
  }
}

// Preview product image
function previewProductImage(imageUrl) {
  if (!imageUrl || imageUrl.trim() === '') {
    return;
  }

  // Create or update image preview
  let previewContainer = document.getElementById('imagePreview');
  if (!previewContainer) {
    previewContainer = document.createElement('div');
    previewContainer.id = 'imagePreview';
    previewContainer.style.marginTop = '0.5rem';

    const imageInput = document.getElementById('editProductImage');
    imageInput.parentNode.appendChild(previewContainer);
  }

  // Create image element
  const img = document.createElement('img');
  img.style.maxWidth = '150px';
  img.style.maxHeight = '150px';
  img.style.borderRadius = '0.375rem';
  img.style.border = '1px solid var(--border-color)';
  img.style.objectFit = 'cover';

  img.onload = function() {
    previewContainer.innerHTML = '';
    previewContainer.appendChild(img);
  };

  img.onerror = function() {
    previewContainer.innerHTML = '<small style="color: var(--danger-color);">Invalid image URL</small>';
  };

  img.src = imageUrl.trim();
}

// Format tags input
function formatTags(input) {
  const tags = input.value
    .split(',')
    .map(tag => tag.trim())
    .filter(tag => tag !== '')
    .join(', ');
  input.value = tags;
}

// Show specific section
function showSection(sectionName) {
  // Update navigation
  document.querySelectorAll('.nav-link').forEach(link => {
    link.classList.remove('active');
  });
  document.querySelector(`[data-section="${sectionName}"]`).classList.add('active');

  // Update page content
  document.querySelectorAll('.page-section').forEach(section => {
    section.classList.remove('active');
  });
  document.getElementById(`${sectionName}-section`).classList.add('active');

  // Update page title
  const titles = {
    'dashboard': 'Dashboard',
    'orders': 'Orders',
    'products': 'Products',
    'inventory': 'Inventory',
    'customers': 'Customers',
    'analytics': 'Analytics',
    'settings': 'Settings'
  };
  document.getElementById('pageTitle').textContent = titles[sectionName] || sectionName;

  currentSection = sectionName;

  // Load section-specific data
  switch (sectionName) {
    case 'dashboard':
      loadDashboardData();
      break;
    case 'orders':
      loadOrders();
      break;
    case 'products':
      loadProducts();
      break;
    case 'inventory':
      loadInventory();
      break;
    case 'customers':
      loadCustomers();
      break;
  }
}

// API request helper
async function apiRequest(endpoint, options = {}) {
  const token = localStorage.getItem('adminToken');
  const config = {
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    },
    ...options
  };

  try {
    const response = await fetch(`${API_BASE}${endpoint}`, config);
    const data = await response.json();

    if (!response.ok) {
      throw new Error(data.error || 'Request failed');
    }

    return data;
  } catch (error) {
    console.error('API Request failed:', error);
    showAlert('Error: ' + error.message, 'danger');
    throw error;
  }
}

// Load dashboard data
async function loadDashboardData() {
  try {
    // Load dashboard stats
    const response = await apiRequest('/dashboard');
    dashboardData = response.data;

    updateDashboardStats(dashboardData);
    loadDashboardCharts();
    loadRecentOrders();
  } catch (error) {
    console.error('Failed to load dashboard:', error);
  }
}

// Update dashboard statistics
function updateDashboardStats(data) {
  if (data.sales) {
    document.getElementById('totalRevenue').textContent =
      `₿${formatNumber(data.sales.total_revenue)}`;
    document.getElementById('revenueChange').textContent =
      `${data.sales.revenue_change >= 0 ? '+' : ''}${data.sales.revenue_change}%`;
    document.getElementById('revenueChange').className =
      `stat-change ${data.sales.revenue_change >= 0 ? 'positive' : 'negative'}`;
  }

  if (data.orders) {
    document.getElementById('totalOrders').textContent =
      formatNumber(data.orders.total_orders);
    document.getElementById('ordersChange').textContent =
      `${data.orders.orders_change >= 0 ? '+' : ''}${data.orders.orders_change}%`;
    document.getElementById('ordersChange').className =
      `stat-change ${data.orders.orders_change >= 0 ? 'positive' : 'negative'}`;
  }

  if (data.products) {
    document.getElementById('totalProducts').textContent =
      formatNumber(data.products.total_products);
    document.getElementById('productsChange').textContent =
      `${data.products.products_change >= 0 ? '+' : ''}${data.products.products_change}%`;
    document.getElementById('productsChange').className =
      `stat-change ${data.products.products_change >= 0 ? 'positive' : 'negative'}`;
  }

  if (data.customers) {
    document.getElementById('totalCustomers').textContent =
      formatNumber(data.customers.total_customers);
    document.getElementById('customersChange').textContent =
      `${data.customers.customers_change >= 0 ? '+' : ''}${data.customers.customers_change}%`;
    document.getElementById('customersChange').className =
      `stat-change ${data.customers.customers_change >= 0 ? 'positive' : 'negative'}`;
  }
}

// Load dashboard charts
function loadDashboardCharts() {
  // Sales trend chart
  if (dashboardData.sales && dashboardData.sales.trend) {
    const salesCtx = document.getElementById('salesChart');

    // Set canvas height
    salesCtx.style.height = '300px';
    salesCtx.style.maxHeight = '300px';

    new Chart(salesCtx.getContext('2d'), {
      type: 'line',
      data: {
        labels: dashboardData.sales.trend.labels || [],
        datasets: [{
          label: 'ยอดขาย (Sales)',
          data: dashboardData.sales.trend.data || [],
          borderColor: '#2563eb',
          backgroundColor: 'rgba(37, 99, 235, 0.1)',
          tension: 0.4,
          borderWidth: 2,
          pointBackgroundColor: '#2563eb',
          pointBorderColor: '#ffffff',
          pointBorderWidth: 2,
          pointRadius: 4,
          pointHoverRadius: 6,
          fill: true
        }]
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        layout: {
          padding: {
            top: 20,
            right: 20,
            bottom: 20,
            left: 20
          }
        },
        plugins: {
          legend: {
            display: true,
            position: 'top',
            labels: {
              font: {
                family: 'Inter, Noto Sans Thai, sans-serif',
                size: 12
              },
              usePointStyle: true,
              padding: 20
            }
          }
        },
        scales: {
          x: {
            grid: {
              display: true,
              color: 'rgba(0, 0, 0, 0.05)'
            },
            ticks: {
              font: {
                family: 'Inter, Noto Sans Thai, sans-serif',
                size: 11
              }
            }
          },
          y: {
            beginAtZero: true,
            grid: {
              display: true,
              color: 'rgba(0, 0, 0, 0.05)'
            },
            ticks: {
              font: {
                family: 'Inter, Noto Sans Thai, sans-serif',
                size: 11
              },
              callback: function(value) {
                return '₿' + formatNumber(value);
              }
            }
          }
        },
        interaction: {
          intersect: false,
          mode: 'index'
        }
      }
    });
  }

  // Order status chart
  if (dashboardData.orders && dashboardData.orders.by_status) {
    const statusCtx = document.getElementById('statusChart');
    const statusData = dashboardData.orders.by_status;

    // Set canvas height
    statusCtx.style.height = '300px';
    statusCtx.style.maxHeight = '300px';

    new Chart(statusCtx.getContext('2d'), {
      type: 'doughnut',
      data: {
        labels: Object.keys(statusData).map(status => {
          const statusMap = {
            'pending': 'รอดำเนินการ',
            'confirmed': 'ยืนยันแล้ว',
            'processing': 'กำลังจัดเตรียม',
            'shipped': 'จัดส่งแล้ว',
            'delivered': 'ส่งมอบแล้ว',
            'cancelled': 'ยกเลิก'
          };
          return statusMap[status] || status;
        }),
        datasets: [{
          data: Object.keys(statusData).map(status => statusData[status].count),
          backgroundColor: [
            '#2563eb',
            '#16a34a',
            '#d97706',
            '#dc2626',
            '#64748b',
            '#8b5cf6'
          ],
          borderWidth: 0,
          hoverBorderWidth: 2,
          hoverBorderColor: '#ffffff'
        }]
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        layout: {
          padding: {
            top: 20,
            right: 20,
            bottom: 20,
            left: 20
          }
        },
        plugins: {
          legend: {
            display: true,
            position: 'bottom',
            labels: {
              font: {
                family: 'Inter, Noto Sans Thai, sans-serif',
                size: 12
              },
              usePointStyle: true,
              padding: 15,
              generateLabels: function(chart) {
                const data = chart.data;
                if (data.labels.length && data.datasets.length) {
                  return data.labels.map((label, i) => {
                    const dataset = data.datasets[0];
                    const value = dataset.data[i];
                    return {
                      text: `${label} (${value})`,
                      fillStyle: dataset.backgroundColor[i],
                      strokeStyle: dataset.backgroundColor[i],
                      lineWidth: 0,
                      pointStyle: 'circle'
                    };
                  });
                }
                return [];
              }
            }
          },
          tooltip: {
            titleFont: {
              family: 'Inter, Noto Sans Thai, sans-serif'
            },
            bodyFont: {
              family: 'Inter, Noto Sans Thai, sans-serif'
            },
            callbacks: {
              label: function(context) {
                const total = context.dataset.data.reduce((a, b) => a + b, 0);
                const percentage = ((context.parsed / total) * 100).toFixed(1);
                return `${context.label}: ${context.parsed} คำสั่งซื้อ (${percentage}%)`;
              }
            }
          }
        },
        cutout: '60%'
      }
    });
  }
}

// Load recent orders
async function loadRecentOrders() {
  try {
    const response = await apiRequest('/orders?limit=5');
    const orders = response.data.orders || [];

    renderOrdersTable(orders, 'recentOrdersTable', true);
  } catch (error) {
    console.error('Failed to load recent orders:', error);
    document.getElementById('recentOrdersTable').innerHTML =
      '<tr><td colspan="6">Failed to load orders</td></tr>';
  }
}

// Load all orders
async function loadOrders() {
  try {
    const status = document.getElementById('orderStatus')?.value || '';
    const queryParams = status ? `?status=${status}` : '';

    const response = await apiRequest(`/orders${queryParams}`);
    ordersData = response.data.orders || [];

    renderOrdersTable(ordersData, 'ordersTable');
  } catch (error) {
    console.error('Failed to load orders:', error);
    document.getElementById('ordersTable').innerHTML =
      '<tr><td colspan="8">Failed to load orders</td></tr>';
  }
}

// Render orders table
function renderOrdersTable(orders, tableId, isRecent = false) {
  const tbody = document.getElementById(tableId);

  if (!orders || orders.length === 0) {
    tbody.innerHTML = `<tr><td colspan="${isRecent ? 6 : 8}">No orders found</td></tr>`;
    return;
  }

  tbody.innerHTML = orders.map(order => {
    const statusClass = getStatusClass(order.status);
    const paymentClass = getStatusClass(order.payment_status);

    return `
            <tr>
                <td><strong>${order.order_number}</strong></td>
                <td>${order.first_name || ''} ${order.last_name || ''}</td>
                ${!isRecent ? `<td>${order.email}</td>` : ''}
                <td><span class="status-badge ${statusClass}">${order.status}</span></td>
                ${!isRecent ? `<td><span class="status-badge ${paymentClass}">${order.payment_status}</span></td>` : ''}
                <td>₿${formatNumber(order.total_amount)}</td>
                <td>${formatDate(order.created_at)}</td>
                <td>
                    <button class="btn btn-outline btn-sm" onclick="viewOrder('${order.id}')">
                        <i class="fas fa-eye"></i>
                    </button>
                    <button class="btn btn-outline btn-sm" onclick="updateOrderStatus('${order.id}')">
                        <i class="fas fa-edit"></i>
                    </button>
                </td>
            </tr>
        `;
  }).join('');
}

// Load products
async function loadProducts() {
  try {
    const response = await apiRequest('/products');
    productsData = response.data.products || [];

    renderProductsTable(productsData);
  } catch (error) {
    console.error('Failed to load products:', error);
    document.getElementById('productsTable').innerHTML =
      '<tr><td colspan="8">Failed to load products</td></tr>';
  }
}

// Render products table
function renderProductsTable(products) {
  const tbody = document.getElementById('productsTable');

  if (!products || products.length === 0) {
    tbody.innerHTML = '<tr><td colspan="8">No products found</td></tr>';
    return;
  }

  tbody.innerHTML = products.map(product => {
    const statusClass = product.status === 'active' ? 'success' : 'secondary';
    const stockClass = product.inventory_quantity <= product.min_stock_level ? 'danger' : 'success';

    return `
            <tr>
                <td>
                    <img src="${product.primary_image || 'assets/images/placeholder.jpg'}"
                         alt="${product.name}" style="width: 40px; height: 40px; object-fit: cover; border-radius: 4px;">
                </td>
                <td><strong>${product.name}</strong></td>
                <td>${product.sku}</td>
                <td>${product.category_name || 'Uncategorized'}</td>
                <td>₿${formatNumber(product.base_price)}</td>
                <td><span class="status-badge ${stockClass}">${product.inventory_quantity || 0}</span></td>
                <td><span class="status-badge ${statusClass}">${product.status}</span></td>
                <td>
                    <button class="btn btn-outline btn-sm" onclick="editProduct(${product.id})">
                        <i class="fas fa-edit"></i>
                    </button>
                    <button class="btn btn-outline btn-sm" onclick="deleteProduct('${product.id}')">
                        <i class="fas fa-trash"></i>
                    </button>
                </td>
            </tr>
        `;
  }).join('');
}

// Load inventory
async function loadInventory() {
  try {
    const response = await apiRequest('/inventory');
    inventoryData = response.data.inventory || [];

    // Update inventory stats
    if (response.data.stats) {
      document.getElementById('lowStockCount').textContent = response.data.stats.low_stock_count || 0;
      document.getElementById('outOfStockCount').textContent = response.data.stats.out_of_stock_count || 0;
      document.getElementById('inventoryValue').textContent = `₿${formatNumber(response.data.stats.inventory_value || 0)}`;
    }

    renderInventoryTable(inventoryData);
  } catch (error) {
    console.error('Failed to load inventory:', error);
    document.getElementById('inventoryTable').innerHTML =
      '<tr><td colspan="6">Failed to load inventory</td></tr>';
  }
}

// Render inventory table
function renderInventoryTable(inventory) {
  const tbody = document.getElementById('inventoryTable');

  if (!inventory || inventory.length === 0) {
    tbody.innerHTML = '<tr><td colspan="6">No inventory data found</td></tr>';
    return;
  }

  tbody.innerHTML = inventory.map(item => {
    let statusClass = 'success';
    let statusText = 'In Stock';

    if (item.current_stock === 0) {
      statusClass = 'danger';
      statusText = 'Out of Stock';
    } else if (item.current_stock <= item.min_stock_level) {
      statusClass = 'warning';
      statusText = 'Low Stock';
    }

    return `
            <tr>
                <td><strong>${item.name}</strong></td>
                <td>${item.sku}</td>
                <td>${item.current_stock}</td>
                <td>${item.min_stock_level}</td>
                <td><span class="status-badge ${statusClass}">${statusText}</span></td>
                <td>
                    <button class="btn btn-outline btn-sm" onclick="adjustStock('${item.product_id}')">
                        <i class="fas fa-edit"></i> Adjust
                    </button>
                </td>
            </tr>
        `;
  }).join('');
}

// Load customers
async function loadCustomers() {
  try {
    const search = document.getElementById('customerSearch')?.value || '';
    const queryParams = search ? `?search=${encodeURIComponent(search)}` : '';

    const response = await apiRequest(`/customers${queryParams}`);
    customersData = response.data.users || [];

    renderCustomersTable(customersData);
  } catch (error) {
    console.error('Failed to load customers:', error);
    document.getElementById('customersTable').innerHTML =
      '<tr><td colspan="8">Failed to load customers</td></tr>';
  }
}

// Render customers table
function renderCustomersTable(customers) {
  const tbody = document.getElementById('customersTable');

  if (!customers || customers.length === 0) {
    tbody.innerHTML = '<tr><td colspan="8">No customers found</td></tr>';
    return;
  }

  tbody.innerHTML = customers.map(customer => {
    const statusClass = customer.is_active ? 'success' : 'secondary';

    return `
            <tr>
                <td><strong>${customer.first_name || ''} ${customer.last_name || ''}</strong></td>
                <td>${customer.email}</td>
                <td>${customer.phone || 'N/A'}</td>
                <td>${customer.total_orders || 0}</td>
                <td>₿${formatNumber(customer.total_spent || 0)}</td>
                <td><span class="status-badge ${statusClass}">${customer.is_active ? 'Active' : 'Inactive'}</span></td>
                <td>${formatDate(customer.created_at)}</td>
                <td>
                    <button class="btn btn-outline btn-sm" onclick="viewCustomer('${customer.id}')">
                        <i class="fas fa-eye"></i>
                    </button>
                </td>
            </tr>
        `;
  }).join('');
}

// Utility functions
function formatNumber(number) {
  return new Intl.NumberFormat('th-TH', {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2
  }).format(number || 0);
}

function formatDate(dateString) {
  const date = new Date(dateString);
  return date.toLocaleDateString('th-TH', {
    year: 'numeric',
    month: 'short',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit'
  });
}

function getStatusClass(status) {
  const statusClasses = {
    'pending': 'warning',
    'confirmed': 'info',
    'processing': 'info',
    'shipped': 'success',
    'delivered': 'success',
    'cancelled': 'danger',
    'refunded': 'secondary',
    'paid': 'success',
    'failed': 'danger',
    'partially_refunded': 'warning'
  };

  return statusClasses[status] || 'secondary';
}

function showAlert(message, type = 'info') {
  const alertDiv = document.createElement('div');
  alertDiv.className = `alert alert-${type}`;
  alertDiv.textContent = message;

  document.querySelector('.content-area').insertBefore(
    alertDiv,
    document.querySelector('.content-area').firstChild
  );

  setTimeout(() => {
    alertDiv.remove();
  }, 5000);
}

// Action functions
function viewOrder(orderId) {
  // TODO: Implement order detail modal
  console.log('View order:', orderId);
  showAlert('Order detail view coming soon', 'info');
}

function updateOrderStatus(orderId) {
  // TODO: Implement order status update modal
  console.log('Update order status:', orderId);
  showAlert('Order status update coming soon', 'info');
}

function editProduct(productId) {
  console.log('Edit product:', productId);

  // Find the product in the loaded data
  const product = productsData.find(p => p.id === productId);
  if (!product) {
    showAlert('Product not found', 'danger');
    return;
  }

  // Populate the form with product data
  populateEditProductForm(product);

  // Load categories for dropdown
  loadCategoriesForEdit();

  // Show the modal
  showEditProductModal();
}

// Show edit product modal
function showEditProductModal() {
  const modal = document.getElementById('editProductModal');
  modal.classList.add('show');
  modal.style.display = 'flex';
  document.body.style.overflow = 'hidden'; // Prevent background scroll
}

// Close edit product modal
function closeEditProductModal() {
  const modal = document.getElementById('editProductModal');
  modal.classList.remove('show');
  modal.style.display = 'none';
  document.body.style.overflow = 'auto'; // Restore background scroll

  // Clear form
  const form = document.getElementById('editProductForm');
  form.reset();
  form.classList.remove('form-loading');

  // Clear custom validation messages
  const inputs = form.querySelectorAll('input, select, textarea');
  inputs.forEach(input => {
    input.setCustomValidity('');
  });

  // Remove image preview
  const imagePreview = document.getElementById('imagePreview');
  if (imagePreview) {
    imagePreview.remove();
  }

  // Reset button state
  const updateBtn = document.querySelector('#editProductModal .btn-primary');
  updateBtn.innerHTML = '<i class="fas fa-save"></i> Update Product';
  updateBtn.disabled = false;
}

// Populate edit form with product data
function populateEditProductForm(product) {
  document.getElementById('editProductId').value = product.id;
  document.getElementById('editProductName').value = product.name || '';
  document.getElementById('editProductSku').value = product.sku || '';
  document.getElementById('editProductCategory').value = product.category_id || '';
  document.getElementById('editProductStatus').value = product.status || 'active';
  document.getElementById('editProductDescription').value = product.description || '';
  document.getElementById('editProductPrice').value = product.base_price || '';
  document.getElementById('editProductSalePrice').value = product.sale_price || '';
  document.getElementById('editProductStock').value = product.inventory_quantity || '';
  document.getElementById('editProductMinStock').value = product.min_stock_level || '5';
  document.getElementById('editProductImage').value = product.primary_image || '';
  document.getElementById('editProductWeight').value = product.weight || '';
  document.getElementById('editProductTags').value = product.tags ? product.tags.join(', ') : '';

  // Clear any previous image preview
  const existingPreview = document.getElementById('imagePreview');
  if (existingPreview) {
    existingPreview.remove();
  }

  // Show image preview if image URL exists
  if (product.primary_image) {
    previewProductImage(product.primary_image);
  }
}

// Load categories for edit dropdown
async function loadCategoriesForEdit() {
  try {
    const response = await apiRequest('/categories');
    const categories = response.data.categories || [];

    const categorySelect = document.getElementById('editProductCategory');
    categorySelect.innerHTML = '<option value="">Select Category</option>';

    categories.forEach(category => {
      const option = document.createElement('option');
      option.value = category.id;
      option.textContent = category.name;
      categorySelect.appendChild(option);
    });
  } catch (error) {
    console.error('Failed to load categories:', error);
    showAlert('Failed to load categories', 'warning');
  }
}

// Update product
async function updateProduct() {
  const form = document.getElementById('editProductForm');

  // Basic form validation
  if (!form.checkValidity()) {
    form.reportValidity();
    return;
  }

  // Collect form data
  const productData = {
    id: document.getElementById('editProductId').value,
    name: document.getElementById('editProductName').value.trim(),
    sku: document.getElementById('editProductSku').value.trim(),
    category_id: document.getElementById('editProductCategory').value || null,
    status: document.getElementById('editProductStatus').value,
    description: document.getElementById('editProductDescription').value.trim(),
    base_price: parseFloat(document.getElementById('editProductPrice').value),
    sale_price: document.getElementById('editProductSalePrice').value ?
      parseFloat(document.getElementById('editProductSalePrice').value) : null,
    inventory_quantity: parseInt(document.getElementById('editProductStock').value),
    min_stock_level: parseInt(document.getElementById('editProductMinStock').value) || 5,
    primary_image: document.getElementById('editProductImage').value.trim() || null,
    weight: document.getElementById('editProductWeight').value ?
      parseFloat(document.getElementById('editProductWeight').value) : null,
    tags: document.getElementById('editProductTags').value
      .split(',')
      .map(tag => tag.trim())
      .filter(tag => tag !== '')
  };

  // Enhanced validation
  const validationErrors = validateProductForm(productData);
  if (validationErrors.length > 0) {
    showAlert('Validation errors: ' + validationErrors.join(', '), 'danger');
    return;
  }

  // Validate SKU uniqueness
  const isSkuValid = await validateSKU(productData.sku, productData.id);
  if (!isSkuValid) {
    return;
  }

  // Show loading state
  const updateBtn = document.querySelector('#editProductModal .btn-primary');
  const originalText = updateBtn.innerHTML;
  updateBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Updating...';
  updateBtn.disabled = true;

  // Disable form to prevent multiple submissions
  form.classList.add('form-loading');

  try {
    // Update product via API
    const response = await apiRequest(`/products/${productData.id}`, {
      method: 'PUT',
      body: JSON.stringify(productData)
    });

    if (response.success) {
      showAlert('Product updated successfully!', 'success');

      // Close modal
      closeEditProductModal();

      // Reload products table
      await loadProducts();

      // Update products data array
      const productIndex = productsData.findIndex(p => p.id === productData.id);
      if (productIndex !== -1) {
        productsData[productIndex] = {...productsData[productIndex], ...response.data.product};
      }

      // Scroll to updated product if possible
      scrollToProduct(productData.id);
    } else {
      throw new Error(response.message || 'Failed to update product');
    }
  } catch (error) {
    console.error('Failed to update product:', error);
    showAlert('Failed to update product: ' + error.message, 'danger');
  } finally {
    // Restore button and form state
    updateBtn.innerHTML = originalText;
    updateBtn.disabled = false;
    form.classList.remove('form-loading');
  }
}

// Scroll to product in table after update
function scrollToProduct(productId) {
  setTimeout(() => {
    const productRow = document.querySelector(`button[onclick="editProduct('${productId}')"]`)?.closest('tr');
    if (productRow) {
      productRow.scrollIntoView({behavior: 'smooth', block: 'center'});
      productRow.style.backgroundColor = '#fef3c7';
      setTimeout(() => {
        productRow.style.backgroundColor = '';
      }, 2000);
    }
  }, 100);
}

function deleteProduct(productId) {
  if (confirm('Are you sure you want to delete this product?')) {
    // TODO: Implement product deletion
    console.log('Delete product:', productId);
    showAlert('Product deletion coming soon', 'info');
  }
}

function adjustStock(productId) {
  // TODO: Implement stock adjustment modal
  console.log('Adjust stock for product:', productId);
  showAlert('Stock adjustment coming soon', 'info');
}

function viewCustomer(customerId) {
  // TODO: Implement customer detail modal
  console.log('View customer:', customerId);
  showAlert('Customer detail view coming soon', 'info');
}

function logout() {
  if (confirm('Are you sure you want to logout?')) {
    localStorage.removeItem('adminToken');
    window.location.href = ADMIN_BASE_PATH + '/login.html';
  }
}

// Validate SKU uniqueness
async function validateSKU(sku, excludeProductId = null) {
  if (!sku || sku.trim() === '') {
    return true;
  }

  try {
    const response = await apiRequest(`/products/validate-sku?sku=${encodeURIComponent(sku.trim())}&exclude=${excludeProductId || ''}`);

    const skuInput = document.getElementById('editProductSku');
    if (response.data && response.data.exists) {
      skuInput.setCustomValidity('SKU already exists');
      showAlert('SKU already exists, please choose a different one', 'warning');
      return false;
    } else {
      skuInput.setCustomValidity('');
      return true;
    }
  } catch (error) {
    console.warn('SKU validation failed:', error);
    return true; // Allow submission if validation fails
  }
}

// Enhanced form validation
function validateProductForm(formData) {
  const errors = [];

  // Name validation
  if (!formData.name || formData.name.length < 2) {
    errors.push('Product name must be at least 2 characters long');
  }

  // SKU validation
  if (!formData.sku || formData.sku.length < 2) {
    errors.push('SKU must be at least 2 characters long');
  }

  // Price validation
  if (!formData.base_price || formData.base_price <= 0) {
    errors.push('Base price must be greater than 0');
  }

  // Sale price validation
  if (formData.sale_price && formData.sale_price >= formData.base_price) {
    errors.push('Sale price must be less than base price');
  }

  // Stock validation
  if (formData.inventory_quantity < 0) {
    errors.push('Stock quantity cannot be negative');
  }

  // URL validation for image
  if (formData.primary_image) {
    try {
      new URL(formData.primary_image);
    } catch {
      errors.push('Primary image must be a valid URL');
    }
  }

  return errors;
}

// ==================== Settings Management ====================

// Show Settings Tab
function showSettingsTab(tabName) {
  // Hide all tabs
  const tabs = document.querySelectorAll('.settings-tab');
  tabs.forEach(tab => tab.classList.remove('active'));

  // Hide all tab buttons
  const tabBtns = document.querySelectorAll('.settings-tabs .tab-btn');
  tabBtns.forEach(btn => btn.classList.remove('active'));

  // Show selected tab
  const targetTab = document.getElementById(tabName + 'Tab');
  if (targetTab) {
    targetTab.classList.add('active');
  }

  // Activate corresponding button
  event.target.classList.add('active');
}

// Settings Management Functions
function addBankAccount() {
  const bankAccounts = document.getElementById('bankAccounts');
  const newAccount = document.createElement('div');
  newAccount.className = 'bank-account';
  newAccount.innerHTML = `
    <div class="form-row">
      <div class="form-group">
        <label>Bank</label>
        <select>
          <option value="scb">SCB - ไทยพาณิชย์</option>
          <option value="kbank">KBANK - กสิกรไทย</option>
          <option value="bbl">BBL - กรุงเทพ</option>
          <option value="ktb">KTB - กรุงไทย</option>
          <option value="ttb">TTB - ทีเม่</option>
        </select>
      </div>
      <div class="form-group">
        <label>Account Number</label>
        <input type="text" placeholder="Bank account number">
      </div>
    </div>
    <div class="form-group">
      <label>Account Name</label>
      <input type="text" placeholder="Account holder name">
    </div>
    <button type="button" class="btn btn-danger btn-sm" onclick="removeBankAccount(this)" style="float: right;">
      <i class="fas fa-trash"></i> Remove Account
    </button>
  `;
  bankAccounts.appendChild(newAccount);
}

function removeBankAccount(button) {
  button.closest('.bank-account').remove();
}

function addShippingZone() {
  const shippingZones = document.getElementById('shippingZones');
  const newZone = document.createElement('div');
  newZone.className = 'shipping-zone';
  newZone.innerHTML = `
    <div class="form-row">
      <div class="form-group">
        <label>Zone Name</label>
        <input type="text" placeholder="Zone name">
      </div>
      <div class="form-group">
        <label>Shipping Fee</label>
        <div class="input-group">
          <input type="number" min="0">
          <span class="input-suffix">บาท</span>
        </div>
      </div>
    </div>
    <div class="form-group">
      <label>Provinces</label>
      <input type="text" placeholder="Provinces list, separated by comma">
    </div>
    <button type="button" class="btn btn-danger btn-sm" onclick="removeShippingZone(this)" style="float: right;">
      <i class="fas fa-trash"></i> Remove Zone
    </button>
  `;
  shippingZones.appendChild(newZone);
}

function removeShippingZone(button) {
  button.closest('.shipping-zone').remove();
}

function addCategory() {
  const categoriesList = document.getElementById('categoriesList');
  const newCategory = document.createElement('div');
  newCategory.className = 'category-item';
  newCategory.innerHTML = `
    <input type="text" placeholder="Category name">
    <button type="button" class="btn btn-danger btn-sm" onclick="removeCategory(this)">
      <i class="fas fa-trash"></i>
    </button>
  `;
  categoriesList.appendChild(newCategory);
}

function removeCategory(button) {
  const categoryItems = document.querySelectorAll('.category-item');
  if (categoryItems.length > 1) {
    button.closest('.category-item').remove();
  } else {
    showAlert('At least one category is required', 'warning');
  }
}

function addSize() {
  const sizesList = document.getElementById('sizesList');
  const newSize = document.createElement('div');
  newSize.className = 'size-item';
  newSize.innerHTML = `
    <input type="text" placeholder="Size">
    <button type="button" class="btn btn-danger btn-sm" onclick="removeSize(this)">
      <i class="fas fa-trash"></i>
    </button>
  `;
  sizesList.appendChild(newSize);
}

function removeSize(button) {
  const sizeItems = document.querySelectorAll('.size-item');
  if (sizeItems.length > 1) {
    button.closest('.size-item').remove();
  } else {
    showAlert('At least one size is required', 'warning');
  }
}

function addColor() {
  const colorsList = document.getElementById('colorsList');
  const newColor = document.createElement('div');
  newColor.className = 'color-item';
  newColor.innerHTML = `
    <input type="text" placeholder="Color name">
    <input type="color" value="#ffffff" title="Select color">
    <button type="button" class="btn btn-danger btn-sm" onclick="removeColor(this)">
      <i class="fas fa-trash"></i>
    </button>
  `;
  colorsList.appendChild(newColor);
}

function removeColor(button) {
  button.closest('.color-item').remove();
}

function generateJWTSecret() {
  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?';
  let result = '';
  for (let i = 0; i < 64; i++) {
    result += characters.charAt(Math.floor(Math.random() * characters.length));
  }
  document.getElementById('jwtSecret').value = result;
  showAlert('New JWT secret generated successfully', 'success');
}

function saveSettings() {
  // Collect all settings data
  const settings = {
    general: {
      storeName: document.getElementById('storeName').value,
      storeDescription: document.getElementById('storeDescription').value,
      storePhone: document.getElementById('storePhone').value,
      storeEmail: document.getElementById('storeEmail').value,
      storeAddress: document.getElementById('storeAddress').value,
      storeFacebook: document.getElementById('storeFacebook').value,
      storeLine: document.getElementById('storeLine').value
    },
    payment: {
      promptPayNumber: document.getElementById('promptPayNumber').value,
      promptPayName: document.getElementById('promptPayName').value,
      enableCOD: document.getElementById('enableCOD').checked,
      bankAccounts: collectBankAccounts()
    },
    shipping: {
      defaultShippingFee: parseInt(document.getElementById('defaultShippingFee').value),
      freeShippingMin: parseInt(document.getElementById('freeShippingMin').value),
      shippingDaysMin: parseInt(document.getElementById('shippingDaysMin').value),
      shippingDaysMax: parseInt(document.getElementById('shippingDaysMax').value),
      shippingZones: collectShippingZones()
    },
    products: {
      categories: collectCategories(),
      sizes: collectSizes(),
      colors: collectColors()
    },
    notifications: {
      adminEmail: document.getElementById('adminEmail').value,
      notifyNewOrder: document.getElementById('notifyNewOrder').checked,
      notifyPaymentReceived: document.getElementById('notifyPaymentReceived').checked,
      notifyLowStock: document.getElementById('notifyLowStock').checked,
      sendOrderConfirmation: document.getElementById('sendOrderConfirmation').checked,
      sendShippingNotification: document.getElementById('sendShippingNotification').checked,
      lineNotifyToken: document.getElementById('lineNotifyToken').value
    },
    system: {
      language: document.getElementById('systemLanguage').value,
      currency: document.getElementById('systemCurrency').value,
      timezone: document.getElementById('systemTimezone').value,
      apiBaseUrl: document.getElementById('apiBaseUrl').value,
      enableDebugMode: document.getElementById('enableDebugMode').checked,
      enableMaintenanceMode: document.getElementById('enableMaintenanceMode').checked,
      jwtSecret: document.getElementById('jwtSecret').value,
      sessionTimeout: parseInt(document.getElementById('sessionTimeout').value)
    }
  };

  // Save to localStorage
  localStorage.setItem('adminSettings', JSON.stringify(settings));

  // Save to API
  fetch(API_BASE + '/settings', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + localStorage.getItem('adminToken')
    },
    body: JSON.stringify(settings)
  })
    .then(response => response.json())
    .then(data => {
      if (data.success) {
        showAlert('Settings saved successfully!', 'success');
      } else {
        showAlert('Error: ' + data.message, 'danger');
      }
    })
    .catch(error => {
      console.error('Error saving settings:', error);
      showAlert('Settings saved locally (API unavailable)', 'warning');
    });
}

function collectBankAccounts() {
  const accounts = [];
  document.querySelectorAll('.bank-account').forEach(account => {
    const bankSelect = account.querySelector('select');
    const accountNumber = account.querySelector('input[placeholder="Bank account number"]');
    const accountName = account.querySelector('input[placeholder="Account holder name"]');

    if (bankSelect.value && accountNumber.value && accountName.value) {
      accounts.push({
        bank: bankSelect.value,
        accountNumber: accountNumber.value,
        accountName: accountName.value
      });
    }
  });
  return accounts;
}

function collectShippingZones() {
  const zones = [];
  document.querySelectorAll('.shipping-zone').forEach(zone => {
    const zoneName = zone.querySelector('input[placeholder="Zone name"]');
    const shippingFee = zone.querySelector('input[type="number"]');
    const provinces = zone.querySelector('input[placeholder*="Provinces"]');

    if (zoneName.value && shippingFee.value && provinces.value) {
      zones.push({
        name: zoneName.value,
        fee: parseInt(shippingFee.value),
        provinces: provinces.value.split(',').map(p => p.trim())
      });
    }
  });
  return zones;
}

function collectCategories() {
  return Array.from(document.querySelectorAll('.category-item input'))
    .map(input => input.value.trim())
    .filter(value => value !== '');
}

function collectSizes() {
  return Array.from(document.querySelectorAll('.size-item input'))
    .map(input => input.value.trim())
    .filter(value => value !== '');
}

function collectColors() {
  const colors = [];
  document.querySelectorAll('.color-item').forEach(item => {
    const nameInput = item.querySelector('input[type="text"]');
    const colorInput = item.querySelector('input[type="color"]');
    if (nameInput.value.trim()) {
      colors.push({
        name: nameInput.value.trim(),
        hex: colorInput.value
      });
    }
  });
  return colors;
}

function resetSettings() {
  if (confirm('Are you sure you want to reset all settings to default?')) {
    localStorage.removeItem('adminSettings');
    location.reload();
  }
}

function exportSettings() {
  const settings = localStorage.getItem('adminSettings');
  if (settings) {
    const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(settings);
    const downloadAnchorNode = document.createElement('a');
    downloadAnchorNode.setAttribute("href", dataStr);
    downloadAnchorNode.setAttribute("download", "admin_settings_" + new Date().toISOString().slice(0, 10) + ".json");
    document.body.appendChild(downloadAnchorNode);
    downloadAnchorNode.click();
    downloadAnchorNode.remove();
    showAlert('Settings exported successfully!', 'success');
  } else {
    showAlert('No settings found to export', 'warning');
  }
}

function importSettings() {
  const input = document.createElement('input');
  input.type = 'file';
  input.accept = '.json';
  input.onchange = function(event) {
    const file = event.target.files[0];
    if (file) {
      const reader = new FileReader();
      reader.onload = function(e) {
        try {
          const settings = JSON.parse(e.target.result);
          localStorage.setItem('adminSettings', JSON.stringify(settings));
          populateSettingsForm(settings);
          showAlert('Settings imported successfully!', 'success');
        } catch (error) {
          showAlert('Invalid settings file', 'danger');
        }
      };
      reader.readAsText(file);
    }
  };
  input.click();
}

function loadSettings() {
  const savedSettings = localStorage.getItem('adminSettings');
  if (savedSettings) {
    const settings = JSON.parse(savedSettings);
    populateSettingsForm(settings);
  }
}

function populateSettingsForm(settings) {
  if (settings.general) {
    document.getElementById('storeName').value = settings.general.storeName || '';
    document.getElementById('storeDescription').value = settings.general.storeDescription || '';
    document.getElementById('storePhone').value = settings.general.storePhone || '';
    document.getElementById('storeEmail').value = settings.general.storeEmail || '';
    document.getElementById('storeAddress').value = settings.general.storeAddress || '';
    document.getElementById('storeFacebook').value = settings.general.storeFacebook || '';
    document.getElementById('storeLine').value = settings.general.storeLine || '';
  }

  if (settings.payment) {
    document.getElementById('promptPayNumber').value = settings.payment.promptPayNumber || '';
    document.getElementById('promptPayName').value = settings.payment.promptPayName || '';
    document.getElementById('enableCOD').checked = settings.payment.enableCOD || false;
  }

  // Populate other settings as needed...
}

// Initialize settings when page loads
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', loadSettings);
} else {
  loadSettings();
}