"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();
});