"use strict"; // Class definition var WorkPrices = (function () { // Private variables var workId; var dealersTable; var saveTimeout = {}; // For debouncing save requests // Loading overlay functions var showLoadingOverlay = function (message) { if ($("#loading-overlay").length === 0) { $("body").append(`
Loading...
${message}
`); } else { $("#loading-overlay").show(); $("#loading-overlay .text-light:last").text(message); } }; var hideLoadingOverlay = function () { $("#loading-overlay").hide(); }; // Private functions var initTable = function () { dealersTable = $("#dealers-table").DataTable({ pageLength: -1, // Show all records lengthChange: false, // Hide length change dropdown searching: true, ordering: true, info: false, // Hide "Showing X of Y entries" responsive: true, dom: '<"top"f>rt<"bottom"p>', // Only show search and pagination paging: false, // Disable pagination }); }; var initEvents = function () { // Get work ID from URL var pathArray = window.location.pathname.split("/"); workId = pathArray[pathArray.length - 2]; // work/{id}/set-prices // Save single price with debouncing $(document).on("click", ".save-single", function () { var dealerId = $(this).data("dealer-id"); // Clear existing timeout for this dealer if (saveTimeout[dealerId]) { clearTimeout(saveTimeout[dealerId]); } // Set new timeout to prevent rapid clicks saveTimeout[dealerId] = setTimeout(function () { saveSinglePrice(dealerId); }, 300); // 300ms delay }); // Delete price $(document).on("click", ".delete-price", function () { var priceId = $(this).data("price-id"); var dealerId = $(this).data("dealer-id"); deletePrice(priceId, dealerId); }); // Save all prices $("#btn-save-all").on("click", function () { saveAllPrices(); }); // Status toggle $(document).on("change", ".status-input", function () { var dealerId = $(this).data("dealer-id"); var isChecked = $(this).is(":checked"); var label = $(this).siblings("label"); var checkbox = $(this); // Update visual immediately if (isChecked) { label.text("Aktif").removeClass("inactive").addClass("active"); } else { label .text("Nonaktif") .removeClass("active") .addClass("inactive"); } // Send AJAX request to update database toggleStatus(dealerId, isChecked, checkbox, label); }); // Format price input with thousand separator $(document).on("input", ".price-input", function () { var input = $(this); var value = input.val().replace(/[^\d]/g, ""); if (value === "") { input.val("0"); } else { var numValue = parseInt(value); input.val(numValue.toLocaleString("id-ID")); // Don't update original value here - let it be updated only when saving } }); // Format price inputs on page load $(".price-input").each(function () { var input = $(this); var value = input.val(); if (value && value !== "0") { var numValue = parseInt(value.replace(/[^\d]/g, "")); input.val(numValue.toLocaleString("id-ID")); // Store the original numeric value for comparison input.data("original-value", numValue.toString()); console.log( "Initialized price for dealer", input.attr("name").replace("price_", ""), ":", numValue ); } }); }; var saveSinglePrice = function (dealerId) { // Prevent multiple clicks var saveButton = $('.save-single[data-dealer-id="' + dealerId + '"]'); if (saveButton.hasClass("loading")) { return; // Already processing } var priceInput = $('input[name="price_' + dealerId + '"]'); var statusInput = $('input[name="status_' + dealerId + '"]'); var price = priceInput.val().replace(/[^\d]/g, ""); // Remove non-numeric characters var isActive = statusInput.is(":checked"); if (!price || parseInt(price) <= 0) { toastr.error("Harga harus lebih dari 0"); return; } // Get original price from data attribute (without separator) var originalPrice = priceInput.data("original-value") || "0"; var currentPrice = parseInt(price); var originalPriceInt = parseInt(originalPrice); console.log( "Debug - Original price:", originalPriceInt, "Current price:", currentPrice ); // If price hasn't actually changed, don't update if (currentPrice === originalPriceInt && originalPrice !== "0") { toastr.info("Harga tidak berubah, tidak perlu update"); return; } // If price has changed, update original value for next comparison if (currentPrice !== originalPriceInt) { priceInput.data("original-value", currentPrice.toString()); console.log( "Price changed from", originalPriceInt, "to", currentPrice ); } // Disable button and show loading state saveButton.addClass("loading").prop("disabled", true); var originalText = saveButton.text(); saveButton.text("Menyimpan..."); var data = { work_id: parseInt(workId), dealer_id: parseInt(dealerId), price: currentPrice, // Use the validated price currency: "IDR", is_active: isActive ? 1 : 0, }; // Debug: Log the data being sent console.log("Sending data:", data); console.log("Original price:", originalPriceInt); console.log("Current price:", currentPrice); $.ajax({ url: "/admin/work/" + workId + "/prices", method: "POST", data: data, headers: { "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"), }, beforeSend: function () { console.log("Sending AJAX request with data:", data); }, success: function (response) { // Re-enable button saveButton .removeClass("loading") .prop("disabled", false) .text(originalText); if (response.status === 200) { toastr.success(response.message); // Update UI updateRowAfterSave(dealerId, response.data); // Ensure consistent formatting after update var updatedPrice = priceInput.val().replace(/[^\d]/g, ""); if (updatedPrice && updatedPrice !== "0") { var formattedPrice = parseInt(updatedPrice).toLocaleString("id-ID"); priceInput.val(formattedPrice); } // Show brief loading message toastr.info( "Data berhasil disimpan, memperbarui tampilan..." ); } else { toastr.error(response.message || "Terjadi kesalahan"); } }, error: function (xhr) { // Re-enable button saveButton .removeClass("loading") .prop("disabled", false) .text(originalText); var message = "Terjadi kesalahan"; if (xhr.responseJSON) { if (xhr.responseJSON.message) { message = xhr.responseJSON.message; } if (xhr.responseJSON.errors) { // Show validation errors var errorMessages = []; for (var field in xhr.responseJSON.errors) { var fieldName = field; switch (field) { case "work_id": fieldName = "ID Pekerjaan"; break; case "dealer_id": fieldName = "ID Dealer"; break; case "price": fieldName = "Harga"; break; case "currency": fieldName = "Mata Uang"; break; case "is_active": fieldName = "Status Aktif"; break; } errorMessages.push( fieldName + ": " + xhr.responseJSON.errors[field][0] ); } message = errorMessages.join("\n"); } } toastr.error(message); }, }); }; var saveAllPrices = function () { // Prevent multiple clicks var saveAllButton = $("#btn-save-all"); if (saveAllButton.hasClass("loading")) { return; // Already processing } var prices = []; var hasValidPrice = false; $(".price-input").each(function () { var dealerId = $(this).attr("name").replace("price_", ""); var price = $(this).val().replace(/[^\d]/g, ""); // Remove non-numeric characters var statusInput = $('input[name="status_' + dealerId + '"]'); var isActive = statusInput.is(":checked"); if (price && parseInt(price) > 0) { hasValidPrice = true; prices.push({ dealer_id: dealerId, price: parseInt(price), currency: "IDR", is_active: isActive, }); } }); if (!hasValidPrice) { toastr.error("Minimal satu dealer harus memiliki harga yang valid"); return; } // Disable button and show loading state saveAllButton.addClass("loading").prop("disabled", true); var originalText = saveAllButton.text(); saveAllButton.text("Menyimpan..."); // Show confirmation $("#confirmMessage").text( "Apakah Anda yakin ingin menyimpan semua harga?" ); $("#confirmModal").modal("show"); $("#confirmAction") .off("click") .on("click", function () { $("#confirmModal").modal("hide"); $.ajax({ url: "/admin/work/" + workId + "/prices/bulk", method: "POST", data: { prices: prices, }, headers: { "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr( "content" ), }, success: function (response) { // Re-enable button saveAllButton .removeClass("loading") .prop("disabled", false) .text(originalText); if (response.status === 200) { toastr.success(response.message); // Show loading overlay showLoadingOverlay("Memperbarui data..."); // Reload page to update all data setTimeout(function () { location.reload(); }, 1000); } else { toastr.error( response.message || "Terjadi kesalahan" ); } }, error: function (xhr) { // Re-enable button saveAllButton .removeClass("loading") .prop("disabled", false) .text(originalText); var message = "Terjadi kesalahan"; if (xhr.responseJSON && xhr.responseJSON.message) { message = xhr.responseJSON.message; } toastr.error(message); }, }); }); }; var deletePrice = function (priceId, dealerId) { $("#confirmMessage").text( "Apakah Anda yakin ingin menghapus harga ini? Harga yang dihapus dapat dipulihkan dengan menyimpan ulang." ); $("#confirmModal").modal("show"); $("#confirmAction") .off("click") .on("click", function () { $("#confirmModal").modal("hide"); // Show loading overlay showLoadingOverlay("Menghapus harga..."); $.ajax({ url: "/admin/work/" + workId + "/prices/" + priceId, method: "DELETE", headers: { "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr( "content" ), }, success: function (response) { hideLoadingOverlay(); if (response.status === 200) { toastr.success(response.message); // Reset the row resetRow(dealerId); } else { toastr.error( response.message || "Terjadi kesalahan" ); } }, error: function (xhr) { hideLoadingOverlay(); var message = "Terjadi kesalahan"; if (xhr.responseJSON && xhr.responseJSON.message) { message = xhr.responseJSON.message; } toastr.error(message); }, }); }); }; // Handle delete button click $(document).on("click", ".delete-price", function () { var priceId = $(this).data("price-id"); var dealerId = $(this).data("dealer-id"); deletePrice(priceId, dealerId); }); var updateRowAfterSave = function (dealerId, data) { var row = $('[data-dealer-id="' + dealerId + '"]'); var priceInput = row.find('input[name="price_' + dealerId + '"]'); var statusInput = row.find('input[name="status_' + dealerId + '"]'); var label = statusInput.siblings("label").find(".status-text"); var actionCell = row.find("td:last"); // Update price input if data contains price if (data.price !== undefined) { // Only update if the price actually changed var currentDisplayValue = priceInput.val().replace(/[^\d]/g, ""); var newPrice = parseInt(data.price); if (parseInt(currentDisplayValue) !== newPrice) { priceInput.val(newPrice.toLocaleString("id-ID")); // Update the original value for future comparisons priceInput.data("original-value", newPrice.toString()); } } // If this is a new record (price = 0), update the save button if (data.price === 0) { actionCell .find(".save-single") .text("Simpan") .removeClass("btn-warning") .addClass("btn-success"); } // Update status if data contains is_active if (data.is_active !== undefined) { statusInput.prop("checked", data.is_active); if (data.is_active) { label.text("Aktif").removeClass("inactive").addClass("active"); } else { label .text("Nonaktif") .removeClass("active") .addClass("inactive"); } } // Update save button if this is a new price save (not just status toggle) if (data.price !== undefined) { actionCell .find(".save-single") .text("Update") .removeClass("btn-success") .addClass("btn-warning"); // Update delete button if (actionCell.find(".delete-price").length === 0) { // Add delete button if it doesn't exist var deleteBtn = ''; actionCell.find(".d-flex.flex-row.gap-1").append(deleteBtn); } else { // Update existing delete button with new price ID actionCell.find(".delete-price").attr("data-price-id", data.id); } } }; var toggleStatus = function (dealerId, isActive, checkbox, label) { var data = { dealer_id: parseInt(dealerId), is_active: isActive, }; $.ajax({ url: "/admin/work/" + workId + "/prices/toggle-status", method: "POST", data: data, headers: { "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"), }, beforeSend: function () { // Show brief loading indicator on checkbox checkbox.prop("disabled", true); }, success: function (response) { // Re-enable checkbox checkbox.prop("disabled", false); if (response.status === 200) { toastr.success(response.message); // Update UI if needed if (response.data) { // If this is a new record, update the row to show save button if (response.data.price === 0) { updateRowAfterSave(dealerId, response.data); } } } else { toastr.error(response.message || "Terjadi kesalahan"); // Revert checkbox state checkbox.prop("checked", !isActive); if (!isActive) { label.text("Aktif"); } else { label.text("Nonaktif"); } } }, error: function (xhr) { // Re-enable checkbox checkbox.prop("disabled", false); var message = "Terjadi kesalahan"; if (xhr.responseJSON) { if (xhr.responseJSON.message) { message = xhr.responseJSON.message; } if (xhr.responseJSON.errors) { var errorMessages = []; for (var field in xhr.responseJSON.errors) { var fieldName = field; switch (field) { case "dealer_id": fieldName = "ID Dealer"; break; case "is_active": fieldName = "Status Aktif"; break; } errorMessages.push( fieldName + ": " + xhr.responseJSON.errors[field][0] ); } message = errorMessages.join("\n"); } } toastr.error(message); // Revert checkbox state checkbox.prop("checked", !isActive); if (!isActive) { label.text("Aktif"); } else { label.text("Nonaktif"); } }, }); }; var resetRow = function (dealerId) { var row = $('[data-dealer-id="' + dealerId + '"]'); var priceInput = row.find('input[name="price_' + dealerId + '"]'); var statusInput = row.find('input[name="status_' + dealerId + '"]'); var label = statusInput.siblings("label").find(".status-text"); var actionCell = row.find("td:last"); // Reset price input priceInput.val("0"); // Reset status statusInput.prop("checked", false); label.text("Nonaktif").removeClass("active").addClass("inactive"); // Remove delete button and update save button actionCell.find(".delete-price").remove(); actionCell .find(".save-single") .text("Simpan") .removeClass("btn-warning") .addClass("btn-success"); }; // Public methods return { init: function () { initTable(); initEvents(); // Initialize price formatting on page load setTimeout(function () { $(".price-input").each(function () { var value = $(this).val(); if (value && value !== "0") { var numValue = parseInt(value.replace(/[^\d]/g, "")); if (!isNaN(numValue)) { $(this).val(numValue.toLocaleString("id-ID")); } } }); }, 100); // Cleanup timeouts on page unload $(window).on("beforeunload", function () { for (var dealerId in saveTimeout) { if (saveTimeout[dealerId]) { clearTimeout(saveTimeout[dealerId]); } } }); }, }; })(); // On document ready jQuery(document).ready(function () { WorkPrices.init(); });