script.js

76.42 KB
08/07/2025 02:26
JS
script.js
// Receipt Designer Application
class ReceiptDesigner {
  constructor() {
    this.currentTemplate = null;
    this.selectedElement = null;
    this.zoomLevel = 100;
    this.history = [];
    this.historyIndex = -1;
    this.isWYSIWYGMode = false;
    this.inlineToolbar = null;
    this.debounceTimer = null;
    this.companySaveTimer = null;

    this.initializeApp();
    this.bindEvents();
    this.setupTemplates();
    this.initializeDefaultMode();

    // Add security protections
    this.addCSPProtection();
    this.validateReceiptData();
  }

  initializeApp() {
    // Initialize DOM elements
    this.receiptPage = document.getElementById('receiptPage');
    this.formatToolbar = document.getElementById('formatToolbar');
    this.logoModal = document.getElementById('logoModal');

    // Setup default receipt data with localStorage support
    this.receiptData = {
      company: this.loadCompanyData() || {
        name: 'บริษัท ตัวอย่าง จำกัด',
        address: '123 ถนนสุขุมวิท แขวงคลองตัน เขตวัฒนา กรุงเทพฯ 10110',
        phone: '02-123-4567',
        email: 'info@company.com',
        taxId: '0123456789012'
      },
      customer: {
        name: 'คุณลูกค้า ตัวอย่าง',
        address: '456 ถนนรัชดา เขตห้วยขวาง กรุงเทพฯ 10310',
        phone: '08-1234-5678'
      },
      receipt: {
        number: 'R2024001',
        date: new Date().toLocaleDateString('th-TH'),
        dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toLocaleDateString('th-TH')
      },
      items: [
        {description: 'สินค้า/บริการ 1', quantity: 2, price: 500, total: 1000},
        {description: 'สินค้า/บริการ 2', quantity: 1, price: 750, total: 750},
        {description: 'สินค้า/บริการ 3', quantity: 3, price: 300, total: 900}
      ],
      totals: {
        subtotal: 2650,
        vat: 185.5,
        total: 2835.5
      }
    };
  }

  bindEvents() {
    // Template selection
    document.querySelectorAll('.template-card').forEach(card => {
      card.addEventListener('click', (e) => {
        const template = e.currentTarget.dataset.template;
        this.selectTemplate(template);
      });
    });

    // Toolbar buttons
    document.getElementById('newReceipt').addEventListener('click', () => this.newReceipt());
    document.getElementById('printReceipt').addEventListener('click', () => this.printReceipt());
    document.getElementById('exportPdf').addEventListener('click', () => this.exportToPDF());

    // Company data buttons
    document.getElementById('resetCompanyData').addEventListener('click', () => this.resetCompanyData());

    document.getElementById('undo').addEventListener('click', () => this.undo());
    document.getElementById('redo').addEventListener('click', () => this.redo());
    document.getElementById('zoomIn').addEventListener('click', () => this.zoomIn());
    document.getElementById('zoomOut').addEventListener('click', () => this.zoomOut());

    // Mode buttons
    document.getElementById('editMode').addEventListener('click', () => this.enableWYSIWYGMode());
    document.getElementById('previewMode').addEventListener('click', () => this.disableWYSIWYGMode());

    // Format toolbar events
    this.bindFormatToolbarEvents();

    // Text selection for WYSIWYG
    document.addEventListener('mouseup', () => this.handleTextSelection());
    document.addEventListener('keyup', () => this.handleTextSelection());

    // Tool buttons
    document.getElementById('addLogo').addEventListener('click', () => this.showLogoModal());
    document.getElementById('addField').addEventListener('click', () => this.addField());

    // Color and font changes
    document.getElementById('primaryColor').addEventListener('change', (e) => {
      this.updatePrimaryColor(e.target.value);
    });

    document.getElementById('secondaryColor').addEventListener('change', (e) => {
      this.updateSecondaryColor(e.target.value);
    });

    document.getElementById('fontFamily').addEventListener('change', (e) => {
      this.updateFontFamily(e.target.value);
    });

    // VAT Rate change
    document.getElementById('vatRate').addEventListener('change', () => {
      this.calculateTotals();
      this.updateVatLabel();
    });

    // Modal events
    document.querySelector('.modal-close').addEventListener('click', () => this.hideModal());
    document.getElementById('logoUpload').addEventListener('click', () => {
      document.getElementById('logoInput').click();
    });

    document.getElementById('logoInput').addEventListener('change', (e) => {
      this.handleLogoUpload(e);
    });

    // Drag and drop for logo
    const logoUpload = document.getElementById('logoUpload');
    logoUpload.addEventListener('dragover', (e) => {
      e.preventDefault();
      logoUpload.style.backgroundColor = '#f0f8ff';
    });

    logoUpload.addEventListener('dragleave', () => {
      logoUpload.style.backgroundColor = '';
    });

    logoUpload.addEventListener('drop', (e) => {
      e.preventDefault();
      logoUpload.style.backgroundColor = '';
      const files = e.dataTransfer.files;
      if (files.length > 0) {
        this.processLogoFile(files[0]);
      }
    });

    // Click outside to deselect
    document.addEventListener('click', (e) => {
      if (!e.target.closest('.editable') &&
        !e.target.closest('.format-toolbar')) {
        this.deselectElement();
        this.hideInlineToolbar();
      }
    });
  }

  // WYSIWYG Mode Functions
  enableWYSIWYGMode() {
    this.isWYSIWYGMode = true;
    document.body.classList.add('wysiwyg-mode');

    // Update mode buttons
    document.getElementById('editMode').classList.add('active');
    document.getElementById('previewMode').classList.remove('active');

    // Don't show format toolbar immediately - it will appear on text selection
    // this.formatToolbar.style.display = 'block';

    // Make all editable elements content editable
    this.makeElementsContentEditable(true);

    // Show edit mode indicator
    this.showEditModeIndicator();
  }

  disableWYSIWYGMode() {
    this.isWYSIWYGMode = false;
    document.body.classList.remove('wysiwyg-mode');

    // Update mode buttons
    document.getElementById('editMode').classList.remove('active');
    document.getElementById('previewMode').classList.add('active');

    // Hide format toolbar
    this.formatToolbar.style.display = 'none';

    // Disable content editing
    this.makeElementsContentEditable(false);

    // Hide indicator
    this.hideEditModeIndicator();
  }

  makeElementsContentEditable(enable) {
    const editableElements = this.receiptPage.querySelectorAll('.editable');
    editableElements.forEach(element => {
      // ไม่ให้แก้ไขช่องยอดรวม ภาษี และรวมทั้งสิ้น
      const isCalculatedField = element.classList.contains('item-total') ||
        element.closest('.total-box') ||
        element.closest('.total-section') ||
        element.closest('[class*="total"]');

      if (enable && !isCalculatedField) {
        element.setAttribute('contenteditable', 'true');
        element.addEventListener('input', this.handleContentEdit.bind(this));
        element.addEventListener('focus', this.handleElementFocus.bind(this));
        element.addEventListener('blur', this.handleElementBlur.bind(this));
      } else {
        element.removeAttribute('contenteditable');
        element.removeEventListener('input', this.handleContentEdit.bind(this));
        element.removeEventListener('focus', this.handleElementFocus.bind(this));
        element.removeEventListener('blur', this.handleElementBlur.bind(this));
      }
    });
  }

  handleContentEdit(e) {
    const element = e.target;

    // Sync content with receiptData
    this.syncElementToReceiptData(element);

    // ตรวจสอบว่าเป็นการแก้ไขข้อมูลในตารางสินค้าหรือไม่
    if (element.classList.contains('item-quantity') ||
      element.classList.contains('item-price') ||
      element.closest('.items-table')) {
      // คำนวณยอดรวมใหม่หลังจากการแก้ไข
      setTimeout(() => this.calculateTotals(), 300);
    }

    // Debounced save
    this.debouncedSave();
  }

  handleElementFocus(e) {
    this.selectedElement = e.target;
    e.target.classList.add('selected');
  }

  handleElementBlur(e) {
    e.target.classList.remove('selected');
    this.debouncedSave();
  }

  handleTextSelection() {
    if (!this.isWYSIWYGMode) return;

    const selection = window.getSelection();
    if (selection.rangeCount > 0 && !selection.isCollapsed) {
      const range = selection.getRangeAt(0);
      const selectedElement = range.commonAncestorContainer.nodeType === Node.TEXT_NODE ?
        range.commonAncestorContainer.parentElement : range.commonAncestorContainer;

      if (selectedElement.closest('.editable')) {
        this.showInlineToolbar(range);
        this.updateToolbarState();
      }
    } else {
      this.hideInlineToolbar();
    }
  }

  showInlineToolbar(range) {
    const rect = range.getBoundingClientRect();
    const toolbar = this.formatToolbar;

    // Show the toolbar
    toolbar.style.display = 'block';

    // Position it above the selection
    const toolbarRect = toolbar.getBoundingClientRect();
    let left = rect.left + rect.width / 2 - toolbarRect.width / 2;
    let top = rect.top - toolbarRect.height - 10;

    // Keep toolbar within viewport
    const viewport = {
      width: window.innerWidth,
      height: window.innerHeight
    };

    if (left < 10) left = 10;
    if (left + toolbarRect.width > viewport.width - 10) {
      left = viewport.width - toolbarRect.width - 10;
    }

    if (top < 10) {
      // Show below selection if no room above
      top = rect.bottom + 10;
    }

    toolbar.style.left = left + 'px';
    toolbar.style.top = top + 'px';
  }

  hideInlineToolbar() {
    if (this.formatToolbar) {
      this.formatToolbar.style.display = 'none';
    }
  }

  updateToolbarState() {
    // Update toolbar button states based on current selection
    const selection = window.getSelection();
    if (selection.rangeCount === 0) return;

    // Check if formatting is applied
    const isBold = document.queryCommandState('bold');
    const isItalic = document.queryCommandState('italic');
    const isUnderline = document.queryCommandState('underline');

    // Update button states
    this.toggleToolbarButton('bold', isBold);
    this.toggleToolbarButton('italic', isItalic);
    this.toggleToolbarButton('underline', isUnderline);
  }

  toggleToolbarButton(command, isActive) {
    const button = document.querySelector(`[data-command="${command}"]`);
    if (button) {
      if (isActive) {
        button.classList.add('active');
      } else {
        button.classList.remove('active');
      }
    }
  }

  bindFormatToolbarEvents() {
    // Format buttons
    const toolbarButtons = document.querySelectorAll('.format-toolbar .toolbar-btn[data-command]');
    toolbarButtons.forEach(button => {
      button.addEventListener('click', (e) => {
        e.preventDefault();
        const command = button.dataset.command;
        this.executeFormatCommand(command);
      });
    });

    // Font size select
    const fontSizeSelect = document.getElementById('fontSizeSelect');
    if (fontSizeSelect) {
      fontSizeSelect.addEventListener('change', (e) => {
        this.applyFontSize(e.target.value);
      });
    }

    // Text color
    const textColor = document.getElementById('textColor');
    if (textColor) {
      textColor.addEventListener('change', (e) => {
        this.applyTextColor(e.target.value);
      });
    }

    // Add item button
    const addItemBtn = document.getElementById('addItemBtn');
    if (addItemBtn) {
      addItemBtn.addEventListener('click', () => {
        this.addNewItem();
      });
    }
  }

  executeFormatCommand(command = '') {
    document.execCommand(command, false, null);
    this.updateToolbarState();
    this.debouncedSave();
  }

  applyFontSize(size) {
    const selection = window.getSelection();
    if (selection.rangeCount > 0) {
      const range = selection.getRangeAt(0);
      if (!range.collapsed) {
        document.execCommand('fontSize', false, '7');
        const selectedElement = range.commonAncestorContainer.parentElement;
        const fontElements = selectedElement.querySelectorAll('font[size="7"]');
        fontElements.forEach(el => {
          el.style.fontSize = size + 'px';
          el.removeAttribute('size');
        });
        this.debouncedSave();
      }
    }
  }

  applyTextColor(color) {
    document.execCommand('foreColor', false, color);
    this.debouncedSave();
  }

  syncElementToReceiptData(element) {
    const classList = Array.from(element.classList);
    const content = this.validateString(element.textContent.trim());
    let isCompanyData = false;

    if (classList.includes('company-name')) {
      this.receiptData.company.name = content;
      isCompanyData = true;
    } else if (classList.includes('company-address')) {
      this.receiptData.company.address = content;
      isCompanyData = true;
    } else if (classList.includes('company-phone')) {
      // ลบข้อความ "โทร: " ออกหากมี
      const phoneContent = content.replace(/^โทร:\s*/, '').trim();
      this.receiptData.company.phone = phoneContent;
      isCompanyData = true;
    } else if (classList.includes('company-email')) {
      // ลบข้อความ "อีเมล: " ออกหากมี
      const emailContent = content.replace(/^อีเมล:\s*/, '').trim();
      this.receiptData.company.email = emailContent;
      isCompanyData = true;
    } else if (classList.includes('company-tax-id')) {
      // ลบข้อความ "เลขประจำตัวผู้เสียภาษี: " ออกหากมี
      const taxIdContent = content.replace(/^เลขประจำตัวผู้เสียภาษี:\s*/, '').trim();
      this.receiptData.company.taxId = taxIdContent;
      isCompanyData = true;
    } else if (classList.includes('customer-name')) {
      this.receiptData.customer.name = content;
    } else if (classList.includes('customer-address')) {
      this.receiptData.customer.address = content;
    } else if (classList.includes('customer-phone')) {
      this.receiptData.customer.phone = content;
    } else if (classList.includes('receipt-number')) {
      // ลบเครื่องหมาย # หากมี
      const receiptNumber = content.replace(/^#/, '').trim();
      this.receiptData.receipt.number = receiptNumber;
    } else if (classList.includes('receipt-date')) {
      this.receiptData.receipt.date = content;
    } else if (classList.includes('receipt-due-date')) {
      this.receiptData.receipt.dueDate = content;
    }

    // บันทึกข้อมูลบริษัทอัตโนมัติเมื่อมีการแก้ไข
    if (isCompanyData) {
      this.debouncedSaveCompany();
    }
  }

  debouncedSave() {
    clearTimeout(this.debounceTimer);
    this.debounceTimer = setTimeout(() => {
      this.saveToHistory();
    }, 500);
  }

  debouncedSaveCompany() {
    clearTimeout(this.companySaveTimer);
    this.companySaveTimer = setTimeout(() => {
      this.saveCompanyData();
    }, 1000);
  }

  showEditModeIndicator() {
    let indicator = document.querySelector('.edit-mode-indicator');
    if (!indicator) {
      indicator = document.createElement('div');
      indicator.className = 'edit-mode-indicator';
      indicator.innerHTML = '<i class="fas fa-edit"></i> โหมดแก้ไข';
      document.body.appendChild(indicator);
    }
    indicator.style.display = 'flex';
  }

  hideEditModeIndicator() {
    const indicator = document.querySelector('.edit-mode-indicator');
    if (indicator) {
      indicator.style.display = 'none';
    }
  }

  setupTemplates() {
    this.templates = {
      modern: {
        name: 'โมเดิร์น',
        html: this.getModernTemplate()
      },
      classic: {
        name: 'คลาสสิก',
        html: this.getClassicTemplate()
      },
      minimal: {
        name: 'มินิมอล',
        html: this.getMinimalTemplate()
      }
    };
  }

  selectTemplate(templateName) {
    // Update UI
    document.querySelectorAll('.template-card').forEach(card => {
      card.classList.remove('active');
    });
    document.querySelector(`[data-template="${templateName}"]`).classList.add('active');

    this.currentTemplate = templateName;
    this.receiptPage.innerHTML = this.templates[templateName].html;

    // Populate data into template
    this.populateTemplateData();

    // Make elements editable
    this.makeElementsEditable();

    // Apply WYSIWYG mode if enabled
    if (this.isWYSIWYGMode) {
      this.makeElementsContentEditable(true);
    }

    // อัปเดตข้อความภาษีให้ตรงกับอัตราที่เลือก
    this.updateVatLabel();

    // Enable buttons
    document.getElementById('printReceipt').disabled = false;
    document.getElementById('exportPdf').disabled = false;

    // Save to history
    this.saveToHistory();
  }

  populateTemplateData() {
    // Populate company data
    const companyName = this.receiptPage.querySelector('.company-name');
    if (companyName) companyName.textContent = this.receiptData.company.name;

    const companyAddress = this.receiptPage.querySelector('.company-address');
    if (companyAddress) companyAddress.textContent = this.receiptData.company.address;

    const companyPhone = this.receiptPage.querySelector('.company-phone');
    if (companyPhone) companyPhone.textContent = this.receiptData.company.phone;

    const companyEmail = this.receiptPage.querySelector('.company-email');
    if (companyEmail) companyEmail.textContent = this.receiptData.company.email;

    const companyTaxId = this.receiptPage.querySelector('.company-tax-id');
    if (companyTaxId) companyTaxId.textContent = this.receiptData.company.taxId;

    // Populate customer data
    const customerName = this.receiptPage.querySelector('.customer-name');
    if (customerName) customerName.textContent = this.receiptData.customer.name;

    const customerAddress = this.receiptPage.querySelector('.customer-address');
    if (customerAddress) customerAddress.textContent = this.receiptData.customer.address;

    const customerPhone = this.receiptPage.querySelector('.customer-phone');
    if (customerPhone) customerPhone.textContent = this.receiptData.customer.phone;

    // Populate receipt data
    const receiptNumber = this.receiptPage.querySelector('.receipt-number');
    if (receiptNumber) receiptNumber.textContent = this.receiptData.receipt.number;

    const receiptDate = this.receiptPage.querySelector('.receipt-date');
    if (receiptDate) receiptDate.textContent = this.receiptData.receipt.date;

    const receiptDueDate = this.receiptPage.querySelector('.receipt-due-date');
    if (receiptDueDate) receiptDueDate.textContent = this.receiptData.receipt.dueDate;

    // Populate items table
    this.populateItemsTable();
  }

  populateItemsTable() {
    const itemsTableBody = this.receiptPage.querySelector('.items-table tbody');
    if (!itemsTableBody) return;

    // Clear existing rows except template
    const rows = itemsTableBody.querySelectorAll('tr:not(.item-template)');
    rows.forEach(row => row.remove());

    // Add items
    this.receiptData.items.forEach((item, index) => {
      this.addItemRow(item, index);
    });

    // Update totals
    this.updateTotals();
  }

  makeElementsEditable() {
    const editableElements = this.receiptPage.querySelectorAll('.editable');
    editableElements.forEach(element => {
      // Basic click handler for selection (always active)
      element.addEventListener('click', (e) => {
        e.stopPropagation();
        this.selectElement(element);
      });

      // Input handler for traditional editing
      element.addEventListener('input', () => {
        if (!this.isWYSIWYGMode) {
          this.saveToHistory();
        }
      });

      element.addEventListener('blur', () => {
        if (!this.isWYSIWYGMode) {
          this.saveToHistory();
        }
      });
    });

    // Setup add/remove buttons for items table
    this.setupItemTableControls();
  }

  setupItemTableControls() {
    // Setup existing remove buttons
    this.setupRemoveButtons();
  }

  setupRemoveButtons() {
    const removeButtons = this.receiptPage.querySelectorAll('.remove-item-btn');
    removeButtons.forEach((button) => {
      button.addEventListener('click', (e) => {
        const row = e.target.closest('tr');
        this.deleteRow(row);
      });
    });
  }

  addNewItem() {
    const newItem = {
      description: 'สินค้า/บริการใหม่',
      quantity: 1,
      price: 0,
      total: 0
    };

    this.receiptData.items.push(newItem);
    this.addItemRow(newItem, this.receiptData.items.length - 1);
    this.updateTotals();
    this.saveToHistory();
  }

  addItemRow(item, index) {
    const itemsTableBody = this.receiptPage.querySelector('.items-table tbody');
    if (!itemsTableBody) return;

    const row = document.createElement('tr');
    row.className = 'item-row';
    row.innerHTML = `
      <td class="editable item-description">${item.description}</td>
      <td class="editable item-quantity" data-type="number">${item.quantity}</td>
      <td class="editable item-price" data-type="number">${item.price.toLocaleString()}</td>
      <td class="item-total">${item.total.toLocaleString()}</td>
      <td class="item-actions">
        <button type="button" class="remove-item-btn" title="ลบรายการ">
          <i class="fas fa-trash"></i>
        </button>
      </td>
    `;

    itemsTableBody.appendChild(row);

    // Setup event listeners for this row
    this.setupRowEventListeners(row, index);
  }

  setupRowEventListeners(row, index) {
    const descriptionCell = row.querySelector('.item-description');
    const quantityCell = row.querySelector('.item-quantity');
    const priceCell = row.querySelector('.item-price');
    const totalCell = row.querySelector('.item-total');
    const removeBtn = row.querySelector('.remove-item-btn');

    // Description change
    descriptionCell.addEventListener('input', () => {
      this.receiptData.items[index].description = this.validateString(descriptionCell.textContent);
      this.debouncedSave();
    });

    // Quantity change
    quantityCell.addEventListener('input', () => {
      const quantity = this.validateNumber(quantityCell.textContent);
      this.receiptData.items[index].quantity = quantity;
      this.updateItemTotal(index);
      this.debouncedSave();
    });

    // Price change
    priceCell.addEventListener('input', () => {
      const priceText = priceCell.textContent.replace(/,/g, '');
      const price = this.validateNumber(priceText);
      this.receiptData.items[index].price = price;
      this.updateItemTotal(index);
      this.debouncedSave();
    });

    // Remove button
    removeBtn.addEventListener('click', (e) => {
      this.deleteRow(row);
    });

    // Make editable if in WYSIWYG mode
    if (this.isWYSIWYGMode) {
      descriptionCell.setAttribute('contenteditable', 'true');
      quantityCell.setAttribute('contenteditable', 'true');
      priceCell.setAttribute('contenteditable', 'true');
    }
  }

  updateItemTotal(index) {
    const item = this.receiptData.items[index];
    item.total = item.quantity * item.price;

    // Update display
    const rows = this.receiptPage.querySelectorAll('.item-row');
    if (rows[index]) {
      const totalCell = rows[index].querySelector('.item-total');
      totalCell.textContent = item.total.toLocaleString();
    }

    this.updateTotals();
  }



  updateTotals() {
    const subtotal = this.receiptData.items.reduce((sum, item) => sum + item.total, 0);
    const vat = subtotal * 0.07; // 7% VAT
    const total = subtotal + vat;

    this.receiptData.totals = {
      subtotal: subtotal,
      vat: vat,
      total: total
    };

    // Update display
    const subtotalElement = this.receiptPage.querySelector('.subtotal');
    if (subtotalElement) subtotalElement.textContent = subtotal.toLocaleString();

    const vatElement = this.receiptPage.querySelector('.vat');
    if (vatElement) vatElement.textContent = vat.toLocaleString();

    const totalElement = this.receiptPage.querySelector('.total');
    if (totalElement) totalElement.textContent = total.toLocaleString();
  }

  selectElement(element) {
    // Remove previous selection
    if (this.selectedElement) {
      this.selectedElement.classList.remove('selected');
    }

    // Select new element
    this.selectedElement = element;
    element.classList.add('selected');
  }

  deselectElement() {
    if (this.selectedElement) {
      this.selectedElement.classList.remove('selected');
      this.selectedElement = null;
    }
  }

  updatePrimaryColor(color) {
    document.documentElement.style.setProperty('--primary-color', color);
    const primaryElements = this.receiptPage.querySelectorAll('.receipt-header, .items-table th, .receipt-title');
    primaryElements.forEach(el => {
      if (el.classList.contains('items-table')) {
        el.style.backgroundColor = color;
      } else {
        el.style.color = color;
      }
    });
  }

  updateSecondaryColor(color) {
    document.documentElement.style.setProperty('--secondary-color', color);
  }

  updateFontFamily(font) {
    this.receiptPage.style.fontFamily = font;
  }

  showLogoModal() {
    this.logoModal.classList.add('show');
  }

  hideModal() {
    this.logoModal.classList.remove('show');
  }

  handleLogoUpload(event) {
    const file = event.target.files[0];
    if (file) {
      this.processLogoFile(file);
    }
  }

  processLogoFile(file) {
    if (!file.type.startsWith('image/')) {
      alert('กรุณาเลือกไฟล์รูปภาพเท่านั้น');
      return;
    }

    const reader = new FileReader();
    reader.onload = (e) => {
      const logoContainer = this.receiptPage.querySelector('.company-logo');
      if (logoContainer) {
        logoContainer.innerHTML = `<img src="${e.target.result}" alt="โลโก้บริษัท" style="max-width: 150px; max-height: 80px;">`;
      } else {
        // Add logo to header
        const header = this.receiptPage.querySelector('.receipt-header .company-info');
        if (header) {
          const logoDiv = document.createElement('div');
          logoDiv.className = 'company-logo';
          logoDiv.innerHTML = `<img src="${e.target.result}" alt="โลโก้บริษัท" style="max-width: 150px; max-height: 80px; margin-bottom: 1rem;">`;
          header.insertBefore(logoDiv, header.firstChild);
        }
      }
      this.hideModal();
      this.saveToHistory();
    };
    reader.readAsDataURL(file);
  }

  addField() {
    const field = document.createElement('div');
    field.className = 'editable custom-field';
    field.contentEditable = true;
    field.textContent = 'ฟิลด์ใหม่';
    field.style.margin = '0.5rem 0';
    field.style.padding = '0.5rem';
    field.style.border = '1px dashed #ccc';

    // Add to customer section
    const customerSection = this.receiptPage.querySelector('.customer-section');
    if (customerSection) {
      customerSection.appendChild(field);
      this.makeElementsEditable();
      this.saveToHistory();
    }
  }

  zoomIn() {
    if (this.zoomLevel < 200) {
      this.zoomLevel += 10;
      this.updateZoom();
    }
  }

  zoomOut() {
    if (this.zoomLevel > 50) {
      this.zoomLevel -= 10;
      this.updateZoom();
    }
  }

  updateZoom() {
    this.receiptPage.style.transform = `scale(${this.zoomLevel / 100})`;
    this.receiptPage.style.transformOrigin = 'top center';
    document.querySelector('.zoom-level').textContent = this.zoomLevel + '%';
  }

  saveToHistory() {
    const state = {
      html: this.receiptPage.innerHTML,
      template: this.currentTemplate,
      timestamp: Date.now()
    };

    // Remove future states if we're not at the end
    this.history = this.history.slice(0, this.historyIndex + 1);

    // Add new state
    this.history.push(state);
    this.historyIndex = this.history.length - 1;

    // Limit history size
    if (this.history.length > 50) {
      this.history = this.history.slice(-50);
      this.historyIndex = this.history.length - 1;
    }
  }

  undo() {
    if (this.historyIndex > 0) {
      this.historyIndex--;
      const state = this.history[this.historyIndex];
      this.receiptPage.innerHTML = state.html;
      this.currentTemplate = state.template;
      this.makeElementsEditable();
      this.deselectElement();
    }
  }

  redo() {
    if (this.historyIndex < this.history.length - 1) {
      this.historyIndex++;
      const state = this.history[this.historyIndex];
      this.receiptPage.innerHTML = state.html;
      this.currentTemplate = state.template;
      this.makeElementsEditable();
      this.deselectElement();
    }
  }

  newReceipt() {
    if (confirm('สร้างใบเสร็จใหม่? ข้อมูลปัจจุบันจะหายไป')) {
      this.receiptPage.innerHTML = `
                <div class="welcome-message">
                    <i class="fas fa-arrow-left"></i>
                    <p>เลือกเทมเพลตจากด้านซ้ายเพื่อเริ่มออกแบบใบเสร็จ</p>
                </div>
            `;
      this.currentTemplate = null;
      this.deselectElement();
      document.getElementById('printReceipt').disabled = true;
      document.getElementById('exportPdf').disabled = true;

      // Clear template selection
      document.querySelectorAll('.template-card').forEach(card => {
        card.classList.remove('active');
      });
    }
  }

  printReceipt() {
    if (this.currentTemplate) {
      window.print();
    }
  }

  async exportToPDF() {
    if (!this.currentTemplate) return;

    try {
      // Show loading indicator
      const exportBtn = document.getElementById('exportPdf');
      const originalText = exportBtn.innerHTML;
      exportBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> กำลังสร้าง PDF...';
      exportBtn.disabled = true;

      // Get the receipt page element
      const receiptElement = document.getElementById('receiptPage');

      // Temporarily hide selection highlights and management buttons
      const selectedElements = receiptElement.querySelectorAll('.selected');
      const actionButtons = receiptElement.querySelectorAll('.item-actions, .table-actions, .action-btn, .remove-item-btn');

      selectedElements.forEach(el => {
        el.classList.remove('selected');
        el.style.outline = 'none';
        el.style.background = 'transparent';
      });

      actionButtons.forEach(el => {
        el.style.display = 'none';
      });

      // Clone the element to avoid modifying the original
      const clonedElement = receiptElement.cloneNode(true);

      // Add PDF optimization class
      clonedElement.classList.add('pdf-optimized');

      // Clean up cloned element for PDF - remove all management elements
      const clonedActionElements = clonedElement.querySelectorAll(
        '.item-actions, .table-actions, .action-btn, .remove-item-btn, .toolbar-btn'
      );
      clonedActionElements.forEach(el => el.remove());

      // Remove management column headers and adjust table structure
      const tables = clonedElement.querySelectorAll('table');
      tables.forEach(table => {
        // Remove management column header
        const headers = table.querySelectorAll('th');
        headers.forEach(header => {
          if (header.textContent.includes('จัดการ') || header.textContent.includes('Management')) {
            header.remove();
          }
        });

        // Remove management cells from all rows
        const rows = table.querySelectorAll('tr');
        rows.forEach(row => {
          const cells = row.querySelectorAll('td');
          cells.forEach(cell => {
            if (cell.classList.contains('item-actions') ||
              cell.querySelector('.remove-item-btn') ||
              cell.querySelector('.action-btn')) {
              cell.remove();
            }
          });
        });
      });

      // Enhance styling for PDF with explicit color settings
      clonedElement.style.transform = 'scale(1)';
      clonedElement.style.transformOrigin = 'top left';
      clonedElement.style.boxShadow = 'none';
      clonedElement.style.border = 'none';
      clonedElement.style.background = '#ffffff';
      clonedElement.style.backgroundColor = '#ffffff';

      // Force all elements to have proper colors and backgrounds
      const allElements = clonedElement.querySelectorAll('*');
      allElements.forEach(el => {
        // Remove any problematic styling
        el.style.textRendering = 'optimizeLegibility';
        el.style.webkitFontSmoothing = 'antialiased';
        el.style.mozOsxFontSmoothing = 'grayscale';

        // Force text color
        if (!el.style.color || el.style.color === '') {
          el.style.color = '#000000';
        }

        // Ensure no transparent backgrounds that might cause issues
        if (el.style.backgroundColor === 'transparent' || el.style.backgroundColor === '') {
          el.style.backgroundColor = 'inherit';
        }

        // Remove shadows and effects
        el.style.boxShadow = 'none';
        el.style.textShadow = 'none';
        el.style.filter = 'none';

        // Fix gradient backgrounds to solid colors
        if (el.style.background && el.style.background.includes('gradient')) {
          el.style.background = '#ffffff';
          el.style.backgroundColor = '#ffffff';
        }
      });

      // Fix table styling specifically
      const pdfTables = clonedElement.querySelectorAll('table');
      pdfTables.forEach(table => {
        table.style.pageBreakInside = 'avoid';
        table.style.borderCollapse = 'collapse';
        table.style.width = '100%';
        table.style.backgroundColor = '#ffffff';
        table.style.color = '#000000';

        // Fix table cell padding and borders
        const cells = table.querySelectorAll('th, td');
        cells.forEach(cell => {
          cell.style.padding = '8px';
          cell.style.border = '1px solid #333333';
          cell.style.fontSize = '14px';
          cell.style.lineHeight = '1.4';
          cell.style.color = '#000000';
          cell.style.backgroundColor = '#ffffff';
        });

        // Style table headers with visible background
        const headers = table.querySelectorAll('th');
        headers.forEach(th => {
          th.style.backgroundColor = '#f5f5f5';
          th.style.color = '#000000';
          th.style.fontWeight = '600';
          th.style.textAlign = 'center';
        });
      });

      // Fix number alignment in tables
      const numberCells = clonedElement.querySelectorAll('.item-quantity, .item-price, .item-total');
      numberCells.forEach(cell => {
        cell.style.textAlign = 'right';
        cell.style.fontFamily = 'monospace, Sarabun';
      });      // Create a temporary container optimized for PDF rendering
      const tempContainer = document.createElement('div');
      tempContainer.style.cssText = `
        position: absolute;
        left: -9999px;
        top: 0;
        width: 794px;
        min-height: 1123px;
        padding: 40px;
        background: #ffffff !important;
        background-color: #ffffff !important;
        font-family: 'Sarabun', Arial, sans-serif;
        font-size: 14px;
        line-height: 1.4;
        color: #000000 !important;
        box-sizing: border-box;
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
        overflow: visible;
      `;

      // Ensure cloned element has proper styling
      clonedElement.style.cssText = `
        background: #ffffff !important;
        background-color: #ffffff !important;
        color: #000000 !important;
        width: 100% !important;
        min-height: auto !important;
        padding: 0 !important;
        margin: 0 !important;
        box-shadow: none !important;
        border: none !important;
      `;

      tempContainer.appendChild(clonedElement);
      document.body.appendChild(tempContainer);

      // Wait for fonts to load
      await document.fonts.ready;

      // Add a delay to ensure proper rendering
      await new Promise(resolve => setTimeout(resolve, 500));

      // Generate canvas with debug settings
      console.log('Starting PDF generation...');
      const canvas = await html2canvas(tempContainer, {
        scale: 1.5,
        useCORS: true,
        allowTaint: true,
        backgroundColor: '#ffffff',
        logging: true,
        removeContainer: false,
        imageTimeout: 10000,
        scrollX: 0,
        scrollY: 0,
        windowWidth: 794,
        windowHeight: 1123,
        onclone: (clonedDoc) => {
          // Force all elements in cloned document to have proper colors
          const allEls = clonedDoc.querySelectorAll('*');
          allEls.forEach(el => {
            el.style.color = '#000000';
            if (el.style.backgroundColor === 'transparent') {
              el.style.backgroundColor = '#ffffff';
            }
          });
          console.log('Canvas cloning completed');
        },
        ignoreElements: (element) => {
          return element.classList.contains('item-actions') ||
            element.classList.contains('table-actions') ||
            element.classList.contains('action-btn');
        }
      });

      console.log('Canvas generated:', canvas.width, 'x', canvas.height);

      // Check if canvas is valid and not completely black
      const ctx = canvas.getContext('2d');
      const imageData = ctx.getImageData(0, 0, Math.min(100, canvas.width), Math.min(100, canvas.height));
      const pixels = imageData.data;
      let isAllBlack = true;

      // Check first 100x100 pixels to see if it's all black
      for (let i = 0; i < pixels.length; i += 4) {
        const r = pixels[i];
        const g = pixels[i + 1];
        const b = pixels[i + 2];
        if (r > 10 || g > 10 || b > 10) { // Allow for slight variations
          isAllBlack = false;
          break;
        }
      }

      if (isAllBlack) {
        console.warn('Canvas appears to be all black, attempting alternative method...');

        // Show debug canvas to user
        const debugDiv = document.createElement('div');
        debugDiv.style.cssText = `
          position: fixed;
          top: 10px;
          right: 10px;
          z-index: 10000;
          background: white;
          border: 2px solid red;
          padding: 10px;
          max-width: 300px;
        `;
        debugDiv.innerHTML = `
          <p style="color: red; margin: 0 0 10px 0;">Debug: Canvas รูปแบบไม่ถูกต้อง</p>
          <canvas id="debugCanvas" style="max-width: 280px; border: 1px solid #ccc;"></canvas>
          <button onclick="this.parentElement.remove()" style="margin-top: 10px;">ปิด</button>
        `;
        document.body.appendChild(debugDiv);

        const debugCanvas = debugDiv.querySelector('#debugCanvas');
        debugCanvas.width = Math.min(280, canvas.width);
        debugCanvas.height = Math.min(200, canvas.height);
        const debugCtx = debugCanvas.getContext('2d');
        debugCtx.drawImage(canvas, 0, 0, debugCanvas.width, debugCanvas.height);

        // Try with minimal settings
        const fallbackCanvas = await html2canvas(tempContainer, {
          scale: 1,
          backgroundColor: '#ffffff',
          logging: true,
          useCORS: false,
          allowTaint: false,
          onrendered: (canvas) => {
            console.log('Fallback canvas rendered');
          }
        });

        if (fallbackCanvas && fallbackCanvas.width > 0 && fallbackCanvas.height > 0) {
          console.log('Using fallback canvas');
          canvas = fallbackCanvas;
        } else {
          throw new Error('ไม่สามารถสร้าง Canvas ได้ กรุณาลองใหม่อีกครั้ง');
        }
      }

      // Clean up temporary container
      document.body.removeChild(tempContainer);

      // Restore original elements
      actionButtons.forEach(el => {
        el.style.display = '';
      });

      // Create PDF from canvas
      await this.createPDFFromCanvas(canvas);

    } catch (error) {
      console.error('Export PDF Error:', error);
      this.showNotification('❌ เกิดข้อผิดพลาดในการส่งออก PDF: ' + error.message, 'error');
    } finally {
      // Restore button
      const exportBtn = document.getElementById('exportPdf');
      exportBtn.innerHTML = '<i class="fas fa-file-pdf"></i> Export PDF';
      exportBtn.disabled = false;
    }
  }

  async createPDFFromCanvas(canvas) {
    // Create PDF with better settings
    const {jsPDF} = window.jspdf;
    const pdf = new jsPDF({
      orientation: 'portrait',
      unit: 'mm',
      format: 'a4',
      compress: true,
      precision: 2
    });

    // Calculate optimal dimensions with better fitting
    const pdfWidth = 210; // A4 width in mm
    const pdfHeight = 297; // A4 height in mm
    const margin = 10; // Small margin for better appearance

    const availableWidth = pdfWidth - (margin * 2);
    const availableHeight = pdfHeight - (margin * 2);

    // Calculate scaling to fit content properly
    const scaleX = availableWidth / (canvas.width / 3.78); // Convert pixels to mm
    const scaleY = availableHeight / (canvas.height / 3.78);
    const scale = Math.min(scaleX, scaleY, 1); // Don't scale up

    const imgWidth = (canvas.width / 3.78) * scale;
    const imgHeight = (canvas.height / 3.78) * scale;

    // Center the content
    const offsetX = margin + (availableWidth - imgWidth) / 2;
    const offsetY = margin;

    // Convert canvas to optimized image with white background
    const tempCanvas = document.createElement('canvas');
    tempCanvas.width = canvas.width;
    tempCanvas.height = canvas.height;
    const tempCtx = tempCanvas.getContext('2d');

    // Fill with white background first
    tempCtx.fillStyle = '#ffffff';
    tempCtx.fillRect(0, 0, tempCanvas.width, tempCanvas.height);

    // Then draw the original canvas on top
    tempCtx.drawImage(canvas, 0, 0);

    const imgData = tempCanvas.toDataURL('image/jpeg', 0.95);

    // Smart page handling
    if (imgHeight <= availableHeight) {
      // Single page - center content
      pdf.addImage(imgData, 'JPEG', offsetX, offsetY, imgWidth, imgHeight);
    } else {
      // Multiple pages with proper breaks
      let currentY = offsetY;
      let remainingHeight = imgHeight;
      let sourceY = 0;

      while (remainingHeight > 0) {
        const currentPageHeight = Math.min(remainingHeight, availableHeight);
        const sourceHeight = (currentPageHeight / imgHeight) * canvas.height;

        // Create canvas section for current page
        const pageCanvas = document.createElement('canvas');
        const pageCtx = pageCanvas.getContext('2d');
        pageCanvas.width = canvas.width;
        pageCanvas.height = sourceHeight;

        // Fill with white background
        pageCtx.fillStyle = '#ffffff';
        pageCtx.fillRect(0, 0, pageCanvas.width, pageCanvas.height);

        // Draw section of original canvas
        pageCtx.drawImage(canvas, 0, sourceY, canvas.width, sourceHeight, 0, 0, canvas.width, sourceHeight);

        const pageImgData = pageCanvas.toDataURL('image/jpeg', 0.95);

        // Add to PDF
        pdf.addImage(pageImgData, 'JPEG', offsetX, currentY, imgWidth, currentPageHeight);

        // Prepare for next page
        remainingHeight -= currentPageHeight;
        sourceY += sourceHeight;

        if (remainingHeight > 0) {
          pdf.addPage();
          currentY = offsetY;
        }
      }
    }

    // Add metadata to PDF
    pdf.setProperties({
      title: `ใบเสร็จรับเงิน ${this.receiptData.receipt.number}`,
      subject: 'Receipt',
      author: this.receiptData.company.name,
      creator: 'Receipt Designer',
      producer: 'Receipt Designer'
    });

    // Generate clean filename
    const receiptNumber = this.receiptData.receipt.number?.replace(/[^a-zA-Z0-9]/g, '') || 'R001';
    const now = new Date();
    const dateStr = now.toISOString().slice(0, 10).replace(/-/g, '');
    const timeStr = now.toTimeString().slice(0, 5).replace(':', '');
    const filename = `Receipt-${receiptNumber}-${dateStr}-${timeStr}.pdf`;

    // Save PDF
    pdf.save(filename);

    // Success message with file info
    this.showNotification(`✅ ส่งออก PDF สำเร็จ! ไฟล์: ${filename}`, 'success');
  }

  showNotification(message, type = 'info') {
    // Create notification element
    const notification = document.createElement('div');
    notification.className = `notification notification-${type}`;
    notification.textContent = message;
    notification.style.cssText = `
      position: fixed;
      top: 20px;
      right: 20px;
      padding: 1rem 1.5rem;
      border-radius: 6px;
      color: white;
      font-weight: 500;
      z-index: 10000;
      max-width: 300px;
      box-shadow: 0 4px 12px rgba(0,0,0,0.15);
      transform: translateX(100%);
      transition: transform 0.3s ease;
    `;

    // Set background color based on type
    switch (type) {
      case 'success':
        notification.style.background = '#27ae60';
        break;
      case 'error':
        notification.style.background = '#e74c3c';
        break;
      default:
        notification.style.background = '#3498db';
    }

    document.body.appendChild(notification);

    // Animate in
    setTimeout(() => {
      notification.style.transform = 'translateX(0)';
    }, 100);

    // Auto remove after 3 seconds
    setTimeout(() => {
      notification.style.transform = 'translateX(100%)';
      setTimeout(() => {
        if (notification.parentNode) {
          document.body.removeChild(notification);
        }
      }, 300);
    }, 3000);
  }

  // รายการสินค้า Methods
  addRow() {
    if (!this.currentTemplate) return;

    const table = this.receiptPage.querySelector('.items-table tbody');
    if (!table) return;

    const newIndex = table.children.length;
    const newRow = document.createElement('tr');
    newRow.setAttribute('data-row-index', newIndex);

    // สร้างแถวใหม่ตามรูปแบบของเทมเพลต
    if (this.currentTemplate === 'modern') {
      newRow.innerHTML = `
        <td class="editable item-description">รายการใหม่</td>
        <td class="editable item-quantity" data-type="number">1</td>
        <td class="editable item-price" data-type="number">0</td>
        <td class="editable item-total" data-type="number">0</td>
        <td class="item-actions">
          <button class="action-btn delete-row" title="ลบรายการ" onclick="receiptDesigner.deleteRow(${newIndex})">
            <i class="fas fa-trash"></i>
          </button>
        </td>
      `;
    } else if (this.currentTemplate === 'classic') {
      newRow.style.borderBottom = '1px solid #2c3e50';
      newRow.innerHTML = `
        <td class="editable item-description">รายการใหม่</td>
        <td class="editable item-quantity" style="text-align: center;" data-type="number">1</td>
        <td class="editable item-price" style="text-align: right;" data-type="number">0</td>
        <td class="editable item-total" style="text-align: right;" data-type="number">0</td>
        <td class="item-actions" style="text-align: center;">
          <button class="action-btn delete-row" title="ลบรายการ" onclick="receiptDesigner.deleteRow(${newIndex})">
            <i class="fas fa-trash"></i>
          </button>
        </td>
      `;
    } else if (this.currentTemplate === 'minimal') {
      newRow.style.borderBottom = '1px solid #f5f5f5';
      newRow.innerHTML = `
        <td class="editable item-description" style="padding: 0.75rem 0;">รายการใหม่</td>
        <td class="editable item-quantity" style="padding: 0.75rem 0; text-align: center;" data-type="number">1</td>
        <td class="editable item-price" style="padding: 0.75rem 0; text-align: right;" data-type="number">0</td>
        <td class="editable item-total" style="padding: 0.75rem 0; text-align: right;" data-type="number">0</td>
        <td class="item-actions" style="padding: 0.75rem 0; text-align: center;">
          <button class="action-btn delete-row" title="ลบรายการ" onclick="receiptDesigner.deleteRow(${newIndex})" style="background: none; border: none; color: #e74c3c; cursor: pointer; font-size: 0.8rem;">
            <i class="fas fa-trash"></i>
          </button>
        </td>
      `;
    }

    table.appendChild(newRow);

    // อัปเดต index ของแถวทั้งหมด
    this.updateRowIndexes();

    // ทำให้แถวใหม่แก้ไขได้
    this.makeElementsEditable();

    // เพิ่มข้อมูลในอาร์เรย์
    this.receiptData.items.push({
      description: 'รายการใหม่',
      quantity: 1,
      price: 0,
      total: 0
    });

    // คำนวณยอดรวมใหม่
    this.calculateTotals();

    this.saveToHistory();
    this.showNotification('✅ เพิ่มรายการใหม่แล้ว', 'success');
  }

  deleteRow(indexOrRow) {
    if (!this.currentTemplate) return;

    const table = this.receiptPage.querySelector('.items-table tbody, .items-table');
    const rows = table.querySelectorAll('tr');

    if (rows.length <= 1) {
      this.showNotification('⚠️ ต้องมีรายการอย่างน้อย 1 รายการ', 'error');
      return;
    }

    let index;
    let targetRow;

    // ตรวจสอบว่าส่งมา index หรือ DOM element
    if (typeof indexOrRow === 'number') {
      index = indexOrRow;
      targetRow = rows[index];
    } else {
      // ส่งมา DOM element
      targetRow = indexOrRow;
      index = Array.from(rows).indexOf(targetRow);
    }

    if (index >= 0 && index < rows.length && targetRow) {
      // ลบแถวจาก DOM
      targetRow.remove();

      // ลบข้อมูลจากอาร์เรย์
      this.receiptData.items.splice(index, 1);

      // อัปเดต index ของแถวทั้งหมด
      this.updateRowIndexes();

      // คำนวณยอดรวมใหม่
      this.calculateTotals();

      this.saveToHistory();
      this.showNotification('✅ ลบรายการแล้ว', 'success');
    }
  }

  updateRowIndexes() {
    const table = this.receiptPage.querySelector('.items-table tbody, .items-table');
    const rows = table.querySelectorAll('tr');

    rows.forEach((row, index) => {
      row.setAttribute('data-row-index', index);
      const deleteBtn = row.querySelector('.delete-row');
      if (deleteBtn) {
        deleteBtn.setAttribute('onclick', `receiptDesigner.deleteRow(${index})`);
      }
    });
  }

  calculateTotals() {
    if (!this.currentTemplate) return;

    const table = this.receiptPage.querySelector('.items-table tbody');
    const rows = table.querySelectorAll('tr');

    let subtotal = 0;

    // คำนวณยอดรวมของแต่ละรายการ และรวมทั้งหมด
    rows.forEach((row, index) => {
      const quantityEl = row.querySelector('.item-quantity');
      const priceEl = row.querySelector('.item-price');
      const totalEl = row.querySelector('.item-total');

      if (quantityEl && priceEl && totalEl) {
        const quantity = parseFloat(quantityEl.textContent.replace(/,/g, '')) || 0;
        const price = parseFloat(priceEl.textContent.replace(/,/g, '')) || 0;
        const total = quantity * price;

        // อัปเดตยอดรวมในตาราง
        totalEl.textContent = total.toLocaleString();

        // อัปเดตข้อมูลในอาร์เรย์
        if (this.receiptData.items[index]) {
          this.receiptData.items[index].quantity = quantity;
          this.receiptData.items[index].price = price;
          this.receiptData.items[index].total = total;
        }

        subtotal += total;
      }
    });

    // คำนวณภาษี
    const vatSelector = document.getElementById('vatRate');
    const vatRate = vatSelector ? (parseFloat(vatSelector.value) / 100) : 0.07;
    const vat = subtotal * vatRate;
    const total = subtotal + vat;

    // อัปเดตข้อมูลยอดรวม
    this.receiptData.totals.subtotal = subtotal;
    this.receiptData.totals.vat = vat;
    this.receiptData.totals.total = total;

    // อัปเดตการแสดงผลยอดรวม
    this.updateTotalDisplay();

    this.saveToHistory();
    this.showNotification('✅ คำนวณยอดรวมแล้ว', 'success');
  }

  updateTotalDisplay() {
    const subTotal = document.getElementById('subTotal');
    const vatTotal = document.getElementById('vatTotal');
    const grandTotal = document.getElementById('grandTotal');
    if (subTotal) {
      subTotal.textContent = this.receiptData.totals.subtotal.toLocaleString();
    }
    if (vatTotal) {
      vatTotal.textContent = this.receiptData.totals.vat.toLocaleString();
    }
    if (grandTotal) {
      grandTotal.textContent = this.receiptData.totals.total.toLocaleString();
    }

    // อัปเดตข้อความภาษีให้ตรงกับอัตราที่เลือก
    this.updateVatLabel();
  }

  // อัปเดต makeElementsEditable เพื่อจัดการการเปลี่ยนแปลงตัวเลข
  makeElementsEditable() {
    const editableElements = this.receiptPage.querySelectorAll('.editable');
    editableElements.forEach(element => {
      // ลบ event listeners เก่า
      element.removeEventListener('click', this.selectElementHandler);
      element.removeEventListener('input', this.inputHandler);
      element.removeEventListener('blur', this.blurHandler);

      // เพิ่ม event listeners ใหม่
      this.selectElementHandler = (e) => {
        e.stopPropagation();
        this.selectElement(element);
      };

      this.inputHandler = () => {
        // ถ้าเป็นฟิลด์ตัวเลขในตาราง ให้คำนวณใหม่
        if (element.dataset.type === 'number' ||
          element.classList.contains('item-quantity') ||
          element.classList.contains('item-price')) {
          setTimeout(() => this.calculateTotals(), 300);
        }
        this.saveToHistory();
      };

      this.blurHandler = () => {
        this.saveToHistory();
      };

      element.addEventListener('click', this.selectElementHandler);
      element.addEventListener('input', this.inputHandler);
      element.addEventListener('blur', this.blurHandler);
    });
  }

  // Template Methods
  getModernTemplate() {
    return `
            <div class="receipt-header">
                <div class="company-info">
                    <h1 class="editable company-name">${this.receiptData.company.name}</h1>
                    <p class="editable company-address">${this.receiptData.company.address}</p>
                    <p class="editable company-phone">โทร: ${this.receiptData.company.phone}</p>
                    <p class="editable company-email">อีเมล: ${this.receiptData.company.email}</p>
                    <p class="editable company-tax-id">เลขประจำตัวผู้เสียภาษี: ${this.receiptData.company.taxId}</p>
                </div>
                <div class="receipt-info">
                    <h2 class="receipt-title editable">ใบเสร็จรับเงิน</h2>
                    <div class="receipt-details">
                        <div class="detail-row">
                            <span>เลขที่:</span>
                            <span class="editable">${this.receiptData.receipt.number}</span>
                        </div>
                        <div class="detail-row">
                            <span>วันที่:</span>
                            <span class="editable">${this.receiptData.receipt.date}</span>
                        </div>
                        <div class="detail-row">
                            <span>ครบกำหนด:</span>
                            <span class="editable">${this.receiptData.receipt.dueDate}</span>
                        </div>
                    </div>
                </div>
            </div>

            <div class="customer-section">
                <h3>ลูกค้า</h3>
                <p class="editable customer-name">${this.receiptData.customer.name}</p>
                <p class="editable">${this.receiptData.customer.address}</p>
                <p class="editable">โทร: ${this.receiptData.customer.phone}</p>
            </div>

            <table class="items-table">
                <thead>
                    <tr>
                        <th class="editable">รายการ</th>
                        <th class="editable">จำนวน</th>
                        <th class="editable">ราคาต่อหน่วย</th>
                        <th class="editable">รวม</th>
                        <th class="item-actions" width="80">จัดการ</th>
                    </tr>
                </thead>
                <tbody>
                    ${this.receiptData.items.map((item, index) => `
                        <tr data-row-index="${index}">
                            <td class="editable item-description">${item.description}</td>
                            <td class="editable item-quantity" data-type="number">${item.quantity}</td>
                            <td class="editable item-price" data-type="number">${item.price.toLocaleString()}</td>
                            <td class="item-total">${item.total.toLocaleString()}</td>
                            <td class="item-actions">
                                <button class="remove-item-btn" title="ลบรายการ">
                                    <i class="fas fa-trash"></i>
                                </button>
                            </td>
                        </tr>
                    `).join('')}
                </tbody>
            </table>

            <div class="total-section">
                <div class="total-box">
                    <div class="total-row">
                        <span>ยอดรวม:</span>
                        <span id=subTotal>${this.receiptData.totals.subtotal.toLocaleString()}</span>
                    </div>
                    <div class="total-row">
                        <span id="vatLabel">ภาษีมูลค่าเพิ่ม:</span>
                        <span id=vatTotal>${this.receiptData.totals.vat.toLocaleString()}</span>
                    </div>
                    <div class="total-row">
                        <span>รวมทั้งสิ้น:</span>
                        <span id=grandTotal>${this.receiptData.totals.total.toLocaleString()}</span>
                    </div>
                </div>
            </div>

            <div class="footer-section">
                <p class="editable">ขอบคุณสำหรับการใช้บริการ</p>
                <p class="editable">โปรดเก็บใบเสร็จนี้ไว้เป็นหลักฐาน</p>
            </div>
        `;
  }

  getClassicTemplate() {
    return `
            <div style="text-align: center; margin-bottom: 2rem; border-bottom: 3px double #2c3e50; padding-bottom: 1rem;">
                <h1 class="editable company-name" style="font-size: 2rem; margin-bottom: 0.5rem;">${this.receiptData.company.name}</h1>
                <p class="editable company-address" style="margin-bottom: 0.25rem;">${this.receiptData.company.address}</p>
                <p class="editable company-phone">โทร: ${this.receiptData.company.phone} | <span class="editable company-email">อีเมล: ${this.receiptData.company.email}</span></p>
                <p class="editable company-tax-id">เลขประจำตัวผู้เสียภาษี: ${this.receiptData.company.taxId}</p>
            </div>

            <div style="text-align: center; margin-bottom: 2rem;">
                <h2 class="receipt-title editable" style="font-size: 1.8rem; color: #2c3e50; text-decoration: underline;">ใบเสร็จรับเงิน</h2>
            </div>

            <div style="display: flex; justify-content: space-between; margin-bottom: 2rem;">
                <div>
                    <p><strong>เลขที่:</strong> <span class="editable">${this.receiptData.receipt.number}</span></p>
                    <p><strong>วันที่:</strong> <span class="editable">${this.receiptData.receipt.date}</span></p>
                </div>
                <div>
                    <p><strong>ครบกำหนด:</strong> <span class="editable">${this.receiptData.receipt.dueDate}</span></p>
                </div>
            </div>

            <div class="customer-section" style="margin-bottom: 2rem; padding: 1rem; background: #f8f9fa; border-left: 4px solid #2c3e50;">
                <h3 style="margin-bottom: 1rem;">รายละเอียดลูกค้า</h3>
                <p><strong>ชื่อ:</strong> <span class="editable customer-name">${this.receiptData.customer.name}</span></p>
                <p><strong>ที่อยู่:</strong> <span class="editable">${this.receiptData.customer.address}</span></p>
                <p><strong>โทรศัพท์:</strong> <span class="editable">${this.receiptData.customer.phone}</span></p>
            </div>

            <table class="items-table" style="border: 2px solid #2c3e50;">
                <thead>
                    <tr style="background: #2c3e50;">
                        <th class="editable">รายการสินค้า/บริการ</th>
                        <th class="editable">จำนวน</th>
                        <th class="editable">ราคาต่อหน่วย (บาท)</th>
                        <th class="editable">จำนวนเงิน (บาท)</th>
                        <th class="item-actions" width="80" style="color: white;">จัดการ</th>
                    </tr>
                </thead>
                <tbody>
                    ${this.receiptData.items.map((item, index) => `
                        <tr style="border-bottom: 1px solid #2c3e50;" data-row-index="${index}">
                            <td class="editable item-description">${item.description}</td>
                            <td class="editable item-quantity" style="text-align: center;" data-type="number">${item.quantity}</td>
                            <td class="editable item-price" style="text-align: right;" data-type="number">${item.price.toLocaleString()}</td>
                            <td class="item-total" style="text-align: right;">${item.total.toLocaleString()}</td>
                            <td class="item-actions" style="text-align: center;">
                                <button class="remove-item-btn" title="ลบรายการ">
                                    <i class="fas fa-trash"></i>
                                </button>
                            </td>
                        </tr>
                    `).join('')}
                </tbody>
            </table>

            <div style="display: flex; justify-content: flex-end; margin: 2rem 0;">
                <div style="border: 2px solid #2c3e50; padding: 1rem; background: white;">
                    <div style="display: flex; justify-content: space-between; margin-bottom: 0.5rem; min-width: 200px;">
                        <span>รวมเป็นเงิน:</span>
                        <span>${this.receiptData.totals.subtotal.toLocaleString()} บาท</span>
                    </div>
                    <div style="display: flex; justify-content: space-between; margin-bottom: 0.5rem;">
                        <span id="vatLabel">ภาษีมูลค่าเพิ่ม:</span>
                        <span>${this.receiptData.totals.vat.toLocaleString()} บาท</span>
                    </div>
                    <div style="display: flex; justify-content: space-between; font-weight: bold; font-size: 1.1rem; border-top: 1px solid #2c3e50; padding-top: 0.5rem;">
                        <span>รวมทั้งสิ้น:</span>
                        <span>${this.receiptData.totals.total.toLocaleString()} บาท</span>
                    </div>
                </div>
            </div>

            <div style="text-align: center; margin-top: 3rem; border-top: 1px solid #ccc; padding-top: 1rem;">
                <p class="editable" style="font-style: italic;">ขอบพระคุณที่ใช้บริการ</p>
                <p class="editable" style="font-size: 0.9rem; color: #666;">กรุณาเก็บใบเสร็จนี้ไว้เป็นหลักฐานการชำระเงิน</p>
            </div>
        `;
  }

  getMinimalTemplate() {
    return `
            <div style="margin-bottom: 3rem;">
                <div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 2rem;">
                    <div>
                        <h1 class="editable company-name" style="font-size: 1.5rem; font-weight: 300; margin-bottom: 0.5rem;">${this.receiptData.company.name}</h1>
                        <p class="editable company-email" style="color: #666; font-size: 0.9rem;">${this.receiptData.company.email}</p>
                    </div>
                    <div style="text-align: right;">
                        <h2 class="receipt-title editable" style="font-size: 1.2rem; font-weight: 300; color: #333;">RECEIPT</h2>
                        <p class="editable receipt-number" style="color: #666; font-size: 0.9rem;">#${this.receiptData.receipt.number}</p>
                    </div>
                </div>

                <div style="display: flex; justify-content: space-between; margin-bottom: 3rem; font-size: 0.9rem;">
                    <div>
                        <p style="color: #999; margin-bottom: 0.25rem;">จาก</p>
                        <p class="editable company-name" style="margin-bottom: 0.25rem;">${this.receiptData.company.name}</p>
                        <p class="editable company-address" style="color: #666;">${this.receiptData.company.address}</p>
                    </div>
                    <div style="text-align: right;">
                        <p style="color: #999; margin-bottom: 0.25rem;">ถึง</p>
                        <p class="editable customer-name" style="margin-bottom: 0.25rem;">${this.receiptData.customer.name}</p>
                        <p class="editable" style="color: #666;">${this.receiptData.customer.address}</p>
                    </div>
                </div>
            </div>

            <table style="width: 100%; border-collapse: collapse; margin-bottom: 2rem; font-size: 0.9rem;">
                <thead>
                    <tr style="border-bottom: 1px solid #eee;">
                        <th class="editable" style="text-align: left; padding: 0.75rem 0; font-weight: 500; color: #666;">รายการ</th>
                        <th class="editable" style="text-align: center; padding: 0.75rem 0; font-weight: 500; color: #666;">จำนวน</th>
                        <th class="editable" style="text-align: right; padding: 0.75rem 0; font-weight: 500; color: #666;">ราคา</th>
                        <th class="editable" style="text-align: right; padding: 0.75rem 0; font-weight: 500; color: #666;">รวม</th>
                        <th class="item-actions" style="width: 60px; text-align: center; padding: 0.75rem 0; font-weight: 500; color: #666;">จัดการ</th>
                    </tr>
                </thead>
                <tbody>
                    ${this.receiptData.items.map((item, index) => `
                        <tr style="border-bottom: 1px solid #f5f5f5;" data-row-index="${index}">
                            <td class="editable item-description" style="padding: 0.75rem 0;">${item.description}</td>
                            <td class="editable item-quantity" style="padding: 0.75rem 0; text-align: center;" data-type="number">${item.quantity}</td>
                            <td class="editable item-price" style="padding: 0.75rem 0; text-align: right;" data-type="number">${item.price.toLocaleString()}</td>
                            <td class="item-total" style="padding: 0.75rem 0; text-align: right;">${item.total.toLocaleString()}</td>
                            <td class="item-actions" style="padding: 0.75rem 0; text-align: center;">
                                <button class="action-btn delete-row" title="ลบรายการ" onclick="receiptDesigner.deleteRow(${index})" style="background: none; border: none; color: #e74c3c; cursor: pointer; font-size: 0.8rem;">
                                    <i class="fas fa-trash"></i>
                                </button>
                            </td>
                        </tr>
                    `).join('')}
                </tbody>
            </table>

            <div style="display: flex; justify-content: flex-end; margin-bottom: 3rem;">
                <div style="min-width: 200px;">
                    <div style="display: flex; justify-content: space-between; margin-bottom: 0.5rem; font-size: 0.9rem;">
                        <span style="color: #666;">ยอดรวม:</span>
                        <span>${this.receiptData.totals.subtotal.toLocaleString()}</span>
                    </div>
                    <div style="display: flex; justify-content: space-between; margin-bottom: 0.5rem; font-size: 0.9rem;">
                        <span style="color: #666;" id="vatLabel">ภาษี:</span>
                        <span>${this.receiptData.totals.vat.toLocaleString()}</span>
                    </div>
                    <div style="display: flex; justify-content: space-between; font-size: 1.1rem; font-weight: 500; border-top: 1px solid #eee; padding-top: 0.75rem;">
                        <span>รวมทั้งสิ้น:</span>
                        <span>${this.receiptData.totals.total.toLocaleString()}</span>
                    </div>
                </div>
            </div>

            <div style="text-align: center; color: #999; font-size: 0.8rem;">
                <p class="editable">วันที่: ${this.receiptData.receipt.date}</p>
                <p class="editable" style="margin-top: 1rem;">ขอบคุณสำหรับการใช้บริการ</p>
            </div>
        `;
  }

  // Initialize default mode to preview
  initializeDefaultMode() {
    // Start in preview mode
    this.disableWYSIWYGMode();
  }

  // Enhanced sync function for all editable elements
  syncAllElementsToReceiptData() {
    const editableElements = this.receiptPage.querySelectorAll('.editable');
    editableElements.forEach(element => {
      this.syncElementToReceiptData(element);
    });
  }

  // Update display from receiptData (reverse sync)
  updateDisplayFromReceiptData() {
    this.populateTemplateData();
  }

  // Security functions for XSS prevention
  sanitizeHTML(str) {
    if (typeof str !== 'string') return '';

    // Create a temporary div to safely extract text content
    const temp = document.createElement('div');
    temp.textContent = str;
    return temp.innerHTML;
  }

  sanitizeInput(input) {
    if (typeof input !== 'string') return '';

    // Remove script tags and dangerous attributes
    return input
      .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
      .replace(/javascript:/gi, '')
      .replace(/on\w+\s*=/gi, '')
      .replace(/data:text\/html/gi, '')
      .trim();
  }

  validateNumber(value) {
    const num = parseFloat(value);
    return isNaN(num) ? 0 : Math.max(0, num);
  }

  validateString(value, maxLength = 1000) {
    if (typeof value !== 'string') return '';
    return this.sanitizeInput(value).substring(0, maxLength);
  }

  // Enhanced input validation and CSP protection
  validateReceiptData() {
    // Validate company data
    this.receiptData.company.name = this.validateString(this.receiptData.company.name, 200);
    this.receiptData.company.address = this.validateString(this.receiptData.company.address, 500);
    this.receiptData.company.phone = this.validateString(this.receiptData.company.phone, 50);
    this.receiptData.company.email = this.validateString(this.receiptData.company.email, 100);
    this.receiptData.company.taxId = this.validateString(this.receiptData.company.taxId, 50);

    // Validate customer data
    this.receiptData.customer.name = this.validateString(this.receiptData.customer.name, 200);
    this.receiptData.customer.address = this.validateString(this.receiptData.customer.address, 500);
    this.receiptData.customer.phone = this.validateString(this.receiptData.customer.phone, 50);

    // Validate receipt data
    this.receiptData.receipt.number = this.validateString(this.receiptData.receipt.number, 50);
    this.receiptData.receipt.date = this.validateString(this.receiptData.receipt.date, 50);
    this.receiptData.receipt.dueDate = this.validateString(this.receiptData.receipt.dueDate, 50);

    // Validate items
    this.receiptData.items = this.receiptData.items.filter(item => {
      item.description = this.validateString(item.description, 200);
      item.quantity = this.validateNumber(item.quantity);
      item.price = this.validateNumber(item.price);
      item.total = this.validateNumber(item.total);

      // Remove empty items
      return item.description.length > 0;
    });

    // Validate totals
    this.receiptData.totals.subtotal = this.validateNumber(this.receiptData.totals.subtotal);
    this.receiptData.totals.vat = this.validateNumber(this.receiptData.totals.vat);
    this.receiptData.totals.total = this.validateNumber(this.receiptData.totals.total);
  }

  // Content Security Policy helpers
  addCSPProtection() {
    // Prevent inline script execution
    const meta = document.createElement('meta');
    meta.httpEquiv = 'Content-Security-Policy';
    meta.content = "default-src 'self'; script-src 'self' 'unsafe-eval' https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdnjs.cloudflare.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self';";
    document.head.appendChild(meta);
  }

  // Company Data Management
  loadCompanyData() {
    try {
      const savedData = localStorage.getItem('receiptCompanyData');
      return savedData ? JSON.parse(savedData) : null;
    } catch (error) {
      console.error('Error loading company data:', error);
      return null;
    }
  }

  saveCompanyData() {
    try {
      localStorage.setItem('receiptCompanyData', JSON.stringify(this.receiptData.company));
    } catch (error) {
      console.error('Error saving company data:', error);
    }
  }

  resetCompanyData() {
    if (confirm('คุณต้องการรีเซ็ตข้อมูลบริษัทเป็นค่าเริ่มต้นหรือไม่?')) {
      localStorage.removeItem('receiptCompanyData');
      this.receiptData.company = {
        name: 'บริษัท ตัวอย่าง จำกัด',
        address: '123 ถนนสุขุมวิท แขวงคลองตัน เขตวัฒนา กรุงเทพฯ 10110',
        phone: '02-123-4567',
        email: 'info@company.com',
        taxId: '0123456789012'
      };

      // อัปเดตหน้าจอใหม่
      if (this.currentTemplate) {
        this.selectTemplate(this.currentTemplate);
      }

      this.showNotification('✅ รีเซ็ตข้อมูลบริษัทแล้ว', 'success');
    }
  }

  updateVatLabel() {
    const vatSelector = document.getElementById('vatRate');
    const vatRate = vatSelector ? parseFloat(vatSelector.value) : 7;
    const vatLabels = this.receiptPage.querySelectorAll('#vatLabel');

    let labelText;
    if (vatRate === 0) {
      labelText = 'ภาษี:';
    } else {
      labelText = `ภาษีมูลค่าเพิ่ม ${vatRate}%:`;
    }

    vatLabels.forEach(label => {
      label.textContent = labelText;
    });
  }
}

// Initialize the application when DOM is loaded
let receiptDesigner;
document.addEventListener('DOMContentLoaded', () => {
  receiptDesigner = new ReceiptDesigner();
});