JezK
Edit File: tiffin-menu-admin.js
jQuery(document).ready(function($) { 'use strict'; // Global variables var tiffinData = window.tiffinMenuData || {}; var currentWeeklyMenuId = null; // Initialize init(); function init() { bindEvents(); getCurrentWeeklyMenuId(); } function bindEvents() { // Create new week modal $(document).on('click', '#create-new-week', openCreateWeekModal); $(document).on('click', '.close', closeModal); $(document).on('click', '.tiffin-modal', function(e) { if (e.target === this) { closeModal(); } }); // Create week form $(document).on('submit', '#create-week-form', handleCreateWeek); // Add menu items $(document).on('click', '.add-item-btn', handleAddMenuItem); // Delete menu items $(document).on('click', '.delete-item', handleDeleteMenuItem); // Save product assignments (legacy) $(document).on('click', '.save-assignment', handleSaveAssignment); // New product configuration handlers $(document).on('change', '.config-active', handleConfigActiveToggle); $(document).on('click', '.save-config', handleSaveConfiguration); // Auto-calculate week end date $(document).on('change', '#week_start_date', function() { var startDate = new Date($(this).val()); if (startDate) { var endDate = new Date(startDate); endDate.setDate(startDate.getDate() + 6); $('#week_end_date').val(formatDate(endDate)); } }); // Enable Enter key for adding items $(document).on('keypress', '.item-name-input', function(e) { if (e.which === 13) { $(this).closest('.add-item-form').find('.add-item-btn').click(); } }); } function getCurrentWeeklyMenuId() { var weeklyMenuElement = $('.add-item-btn').first(); if (weeklyMenuElement.length) { currentWeeklyMenuId = weeklyMenuElement.data('weekly-menu-id'); } } function openCreateWeekModal() { $('#create-week-modal').show(); // Set default dates (current week) var today = new Date(); var startOfWeek = new Date(today); var day = today.getDay(); var diff = today.getDate() - day + (day === 0 ? -6 : 1); // adjust when day is sunday startOfWeek.setDate(diff); var endOfWeek = new Date(startOfWeek); endOfWeek.setDate(startOfWeek.getDate() + 6); $('#week_start_date').val(formatDate(startOfWeek)); $('#week_end_date').val(formatDate(endOfWeek)); } function closeModal() { $('.tiffin-modal').hide(); $('#create-week-form')[0].reset(); } function handleCreateWeek(e) { e.preventDefault(); var formData = { action: 'save_weekly_menu', nonce: tiffinData.nonce, week_start_date: $('#week_start_date').val(), week_end_date: $('#week_end_date').val(), is_active: $('#is_active').is(':checked') ? 1 : 0 }; var submitBtn = $(this).find('button[type="submit"]'); submitBtn.addClass('loading').prop('disabled', true); $.ajax({ url: tiffinData.ajaxUrl, type: 'POST', data: formData, success: function(response) { if (response.success) { showNotice('Weekly menu created successfully!', 'success'); closeModal(); location.reload(); // Reload to show new menu } else { showNotice(response.data || 'Error creating weekly menu', 'error'); } }, error: function() { showNotice('AJAX error occurred', 'error'); }, complete: function() { submitBtn.removeClass('loading').prop('disabled', false); } }); } function handleAddMenuItem() { var $button = $(this); var $form = $button.closest('.add-item-form'); var $nameInput = $form.find('.item-name-input'); var $descInput = $form.find('.item-description-input'); var itemName = $nameInput.val().trim(); var description = $descInput.val().trim(); var dayOfWeek = $button.data('day'); var itemType = $button.data('type'); var weeklyMenuId = $button.data('weekly-menu-id'); if (!itemName) { showNotice('Please enter an item name', 'error'); $nameInput.focus(); return; } if (!dayOfWeek) { showNotice('Day of week is required', 'error'); return; } var formData = { action: 'save_menu_item', nonce: tiffinData.nonce, weekly_menu_id: weeklyMenuId, day_of_week: dayOfWeek, item_name: itemName, item_type: itemType, description: description }; $button.addClass('loading').prop('disabled', true); $.ajax({ url: tiffinData.ajaxUrl, type: 'POST', data: formData, success: function(response) { if (response.success) { // Add item to the list var $itemsList = $form.prev('.items-list'); var itemHtml = '<div class="menu-item" data-item-id="' + response.data.id + '">' + '<div>' + '<span class="item-name">' + escapeHtml(itemName) + '</span>'; if (description) { itemHtml += '<span class="item-description">' + escapeHtml(description) + '</span>'; } itemHtml += '</div>' + '<button class="button button-small delete-item" data-item-id="' + response.data.id + '">Delete</button>' + '</div>'; $itemsList.append(itemHtml); // Clear form $nameInput.val(''); $descInput.val(''); showNotice('Menu item added successfully!', 'success'); } else { showNotice(response.data || 'Error adding menu item', 'error'); } }, error: function() { showNotice('AJAX error occurred', 'error'); }, complete: function() { $button.removeClass('loading').prop('disabled', false); } }); } function handleDeleteMenuItem() { var $button = $(this); var itemId = $button.data('item-id'); var $menuItem = $button.closest('.menu-item'); var itemName = $menuItem.find('.item-name').text(); if (!confirm('Are you sure you want to delete "' + itemName + '"?')) { return; } var formData = { action: 'delete_menu_item', nonce: tiffinData.nonce, item_id: itemId }; $button.addClass('loading').prop('disabled', true); $.ajax({ url: tiffinData.ajaxUrl, type: 'POST', data: formData, success: function(response) { if (response.success) { $menuItem.fadeOut(300, function() { $(this).remove(); }); showNotice('Menu item deleted successfully!', 'success'); } else { showNotice(response.data || 'Error deleting menu item', 'error'); } }, error: function() { showNotice('AJAX error occurred', 'error'); }, complete: function() { $button.removeClass('loading').prop('disabled', false); } }); } function handleSaveAssignment() { var $button = $(this); var $section = $button.closest('.assignment-section'); var $card = $button.closest('.product-assignment-card'); var productId = $card.data('product-id'); var weeklyMenuId = $button.data('weekly-menu-id'); var dayOfWeek = $button.data('day'); var itemType = $button.data('type'); var quantity = $section.find('.quantity-input').val(); // Validate quantity quantity = parseInt(quantity) || 0; if (quantity < 0) { quantity = 0; } if (!dayOfWeek) { showNotice('Day of week is required', 'error'); return; } var formData = { action: 'save_product_menu_assignment', nonce: tiffinData.nonce, product_id: productId, weekly_menu_id: weeklyMenuId, day_of_week: dayOfWeek, item_type: itemType, quantity: quantity }; $button.addClass('loading').prop('disabled', true); $.ajax({ url: tiffinData.ajaxUrl, type: 'POST', data: formData, success: function(response) { if (response.success) { // Update the quantity input to ensure it shows the saved value $section.find('.quantity-input').val(quantity); // Show success feedback $button.text('Saved!').removeClass('loading'); setTimeout(function() { $button.text('Save'); }, 1500); showNotice('Assignment saved successfully!', 'success'); } else { showNotice(response.data || 'Error saving assignment', 'error'); } }, error: function() { showNotice('AJAX error occurred', 'error'); }, complete: function() { $button.removeClass('loading').prop('disabled', false); } }); } function handleConfigActiveToggle() { var $checkbox = $(this); var $section = $checkbox.closest('.config-section'); var $quantityInput = $section.find('.config-quantity'); var $preview = $section.find('.config-preview'); if ($checkbox.is(':checked')) { $quantityInput.prop('disabled', false); if ($quantityInput.val() == 0) { $quantityInput.val(1); } updateConfigPreview($section); } else { $quantityInput.prop('disabled', true); $preview.empty(); } } function handleSaveConfiguration() { var $button = $(this); var $section = $button.closest('.config-section'); var $card = $button.closest('.product-configuration-card'); var productId = $card.data('product-id'); var itemType = $button.data('type'); var $activeCheckbox = $section.find('.config-active'); var $quantityInput = $section.find('.config-quantity'); var isActive = $activeCheckbox.is(':checked') ? 1 : 0; var quantity = parseInt($quantityInput.val()) || 0; // Debug logging if (tiffinData.debug) { console.log('Save Configuration Debug:', { productId: productId, itemType: itemType, isActive: isActive, quantity: quantity, ajaxUrl: tiffinData.ajaxUrl, nonce: tiffinData.nonce }); } // Validate if (isActive && quantity <= 0) { showNotice('Please enter a valid quantity', 'error'); return; } var formData = { action: 'save_product_configuration', nonce: tiffinData.nonce, product_id: productId, item_type: itemType, quantity: quantity, is_active: isActive }; if (tiffinData.debug) { console.log('Form data being sent:', formData); } $button.addClass('loading').prop('disabled', true); $.ajax({ url: tiffinData.ajaxUrl, type: 'POST', data: formData, success: function(response) { if (tiffinData.debug) { console.log('AJAX Success Response:', response); } if (response.success) { // Update preview updateConfigPreview($section); // Update summary updateProductSummary($card); // Show success feedback $button.removeClass('loading').addClass('saved').text('Saved!'); setTimeout(function() { $button.removeClass('saved').text('Save'); }, 2000); showNotice('Configuration saved successfully!', 'success'); } else { if (tiffinData.debug) { console.error('AJAX Error Response:', response); } showNotice(response.data || 'Error saving configuration', 'error'); } }, error: function(xhr, status, error) { if (tiffinData.debug) { console.error('AJAX Request Failed:', { status: status, error: error, responseText: xhr.responseText }); } showNotice('AJAX error occurred: ' + error, 'error'); }, complete: function() { $button.removeClass('loading').prop('disabled', false); } }); } function updateConfigPreview($section) { var $activeCheckbox = $section.find('.config-active'); var $quantityInput = $section.find('.config-quantity'); var $preview = $section.find('.config-preview'); var $h4 = $section.find('h4'); if ($activeCheckbox.is(':checked') && $quantityInput.val() > 0) { var quantity = $quantityInput.val(); var itemType = $h4.text().toLowerCase(); var previewHtml = '<small class="preview-text">Daily: ' + quantity + ' ' + itemType + '</small>'; $preview.html(previewHtml); } else { $preview.empty(); } } function updateProductSummary($card) { var $summaryContent = $card.find('.summary-content'); var activeConfigs = []; $card.find('.config-section').each(function() { var $section = $(this); var $activeCheckbox = $section.find('.config-active'); var $quantityInput = $section.find('.config-quantity'); var itemType = $section.find('h4').text(); if ($activeCheckbox.is(':checked') && $quantityInput.val() > 0) { activeConfigs.push($quantityInput.val() + ' ' + itemType); } }); if (activeConfigs.length === 0) { $summaryContent.html('<em>No items configured for this product.</em>'); } else { var summaryHtml = '<ul class="summary-list">'; activeConfigs.forEach(function(config) { summaryHtml += '<li>' + config + '</li>'; }); summaryHtml += '</ul>'; $summaryContent.html(summaryHtml); } } function showNotice(message, type) { // Remove existing notices $('.tiffin-notice').remove(); var noticeClass = type === 'error' ? 'error' : 'success'; var notice = '<div class="tiffin-notice ' + noticeClass + '">' + escapeHtml(message) + '</div>'; // Add notice at the top of the wrap $('.wrap').prepend(notice); // Auto-hide after 5 seconds setTimeout(function() { $('.tiffin-notice').fadeOut(300, function() { $(this).remove(); }); }, 5000); // Scroll to top to show notice $('html, body').animate({ scrollTop: 0 }, 300); } function formatDate(date) { var year = date.getFullYear(); var month = String(date.getMonth() + 1).padStart(2, '0'); var day = String(date.getDate()).padStart(2, '0'); return year + '-' + month + '-' + day; } function escapeHtml(text) { var map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, function(m) { return map[m]; }); } // Additional AJAX handler for saving menu items (missing from PHP) function addMissingAjaxHandler() { // This function exists in case we need to add the save_menu_item AJAX handler // The handler should be added to the PHP file's init_hooks method } // Handle keyboard navigation $(document).on('keydown', function(e) { // ESC key closes modals if (e.keyCode === 27) { closeModal(); } }); // Auto-save functionality for assignments (optional) var autoSaveTimeout; $(document).on('input', '.quantity-input', function() { var $input = $(this); var $saveBtn = $input.siblings('.save-assignment'); // Clear existing timeout clearTimeout(autoSaveTimeout); // Set new timeout for auto-save (2 seconds after user stops typing) autoSaveTimeout = setTimeout(function() { if ($input.val() !== $input.data('original-value')) { $saveBtn.click(); } }, 2000); }); // Store original values for auto-save comparison $(document).on('focus', '.quantity-input', function() { $(this).data('original-value', $(this).val()); }); // Form validation function validateWeekForm() { var startDate = $('#week_start_date').val(); var endDate = $('#week_end_date').val(); if (!startDate || !endDate) { showNotice('Please select both start and end dates', 'error'); return false; } if (new Date(startDate) >= new Date(endDate)) { showNotice('End date must be after start date', 'error'); return false; } return true; } // Enhanced form submission with validation $(document).on('submit', '#create-week-form', function(e) { if (!validateWeekForm()) { e.preventDefault(); return false; } }); // Bulk actions (for future enhancement) function initBulkActions() { // Add bulk delete, bulk edit functionality // This can be implemented later if needed } // Export functionality (for future enhancement) function initExportFunctionality() { // Add export to CSV, PDF functionality // This can be implemented later if needed } // Initialize tooltips (if needed) function initTooltips() { // Add tooltips for better UX // This can be implemented later if needed } // Debug function for development function debugLog(message, data) { if (typeof console !== 'undefined' && console.log) { console.log('[Tiffin Menu Debug]', message, data || ''); } } // Public API for external scripts window.TiffinMenuAdmin = { showNotice: showNotice, refreshData: function() { location.reload(); }, debugLog: debugLog }; });