forked from UKSOURCE/cms.hailearning.edu.vn
fix: add leading slashes to image paths in service data
This commit is contained in:
@@ -149,7 +149,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Features List -->
|
||||
<div class="row">
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="fw-medium mb-0">Features</h6>
|
||||
@@ -228,7 +228,7 @@
|
||||
</div>
|
||||
|
||||
<!-- FAQ List -->
|
||||
<div class="row">
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="fw-medium mb-0">FAQ Items</h6>
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
<a href="/admin/service/<%= service.slug %>/details" class="btn btn-sm btn-outline-warning" title="Edit Details">
|
||||
<i class="fas fa-cog"></i>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteService(<%= index %>)" title="Delete Service">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteService('<%= service.slug %>')" title="Delete Service">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -570,18 +570,32 @@ function addService() {
|
||||
showSuccess('Service added successfully!');
|
||||
}
|
||||
|
||||
function deleteService(index) {
|
||||
const service = servicesData[index];
|
||||
if (!service) return;
|
||||
function deleteService(slug) {
|
||||
const serviceIndex = servicesData.findIndex(service => service && service.slug === slug);
|
||||
if (serviceIndex === -1) {
|
||||
showError('Service not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
const service = servicesData[serviceIndex];
|
||||
|
||||
// Set service name in modal
|
||||
document.getElementById('deleteServiceName').textContent = service.name || 'Unnamed Service';
|
||||
const serviceNameElement = document.getElementById('deleteServiceName');
|
||||
if (serviceNameElement) {
|
||||
serviceNameElement.textContent = service.name || 'Unnamed Service';
|
||||
}
|
||||
|
||||
// Show custom modal
|
||||
showDeleteModal();
|
||||
|
||||
// Handle confirm delete
|
||||
const confirmBtn = document.getElementById('confirmDeleteBtn');
|
||||
if (!confirmBtn) {
|
||||
showError('Delete button not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove any existing event listeners by cloning the button
|
||||
const newConfirmBtn = confirmBtn.cloneNode(true);
|
||||
confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn);
|
||||
|
||||
@@ -593,36 +607,54 @@ function deleteService(index) {
|
||||
// Disable cancel button and close button during delete
|
||||
const cancelBtn = document.querySelector('.custom-modal .btn-secondary');
|
||||
const closeBtn = document.querySelector('.custom-modal-close');
|
||||
cancelBtn.disabled = true;
|
||||
closeBtn.disabled = true;
|
||||
if (cancelBtn) cancelBtn.disabled = true;
|
||||
if (closeBtn) closeBtn.disabled = true;
|
||||
|
||||
// Add loading overlay to modal
|
||||
showModalLoading();
|
||||
|
||||
try {
|
||||
// Simulate API call delay for better UX
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await new Promise(resolve => setTimeout(resolve, 800));
|
||||
|
||||
// Check if service still exists (in case of concurrent modifications)
|
||||
const currentServiceIndex = servicesData.findIndex(service => service && service.slug === slug);
|
||||
if (currentServiceIndex === -1) {
|
||||
throw new Error('Service no longer exists.');
|
||||
}
|
||||
|
||||
// Store service name for success message
|
||||
const serviceName = servicesData[currentServiceIndex].name || 'Unnamed Service';
|
||||
|
||||
// Perform delete
|
||||
servicesData.splice(index, 1);
|
||||
servicesData.splice(currentServiceIndex, 1);
|
||||
updateServicesTable();
|
||||
updateStatistics();
|
||||
|
||||
// Hide loading overlay first
|
||||
hideModalLoading();
|
||||
|
||||
// Reset button states
|
||||
this.disabled = false;
|
||||
this.innerHTML = '<i class="fas fa-trash me-2"></i>Delete Service';
|
||||
if (cancelBtn) cancelBtn.disabled = false;
|
||||
if (closeBtn) closeBtn.disabled = false;
|
||||
|
||||
// Hide modal
|
||||
closeDeleteModal();
|
||||
|
||||
// Show success message
|
||||
showSuccess(`Service "${service.name}" deleted successfully!`);
|
||||
showSuccess(`Service "${serviceName}" deleted successfully!`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error deleting service:', error);
|
||||
showError('Failed to delete service. Please try again.');
|
||||
} finally {
|
||||
// Reset button states
|
||||
|
||||
// Reset button states on error
|
||||
this.disabled = false;
|
||||
this.innerHTML = '<i class="fas fa-trash me-2"></i>Delete Service';
|
||||
cancelBtn.disabled = false;
|
||||
closeBtn.disabled = false;
|
||||
if (cancelBtn) cancelBtn.disabled = false;
|
||||
if (closeBtn) closeBtn.disabled = false;
|
||||
|
||||
// Hide loading overlay
|
||||
hideModalLoading();
|
||||
@@ -632,6 +664,11 @@ function deleteService(index) {
|
||||
|
||||
function showModalLoading() {
|
||||
const modal = document.getElementById('customDeleteModal');
|
||||
if (!modal) return;
|
||||
|
||||
// Remove any existing loading overlay first
|
||||
hideModalLoading();
|
||||
|
||||
const loadingOverlay = document.createElement('div');
|
||||
loadingOverlay.className = 'modal-loading-overlay';
|
||||
loadingOverlay.innerHTML = `
|
||||
@@ -640,7 +677,11 @@ function showModalLoading() {
|
||||
<p class="mt-2 mb-0">Deleting service...</p>
|
||||
</div>
|
||||
`;
|
||||
modal.appendChild(loadingOverlay);
|
||||
|
||||
const modalContent = modal.querySelector('.custom-modal-content');
|
||||
if (modalContent) {
|
||||
modalContent.appendChild(loadingOverlay);
|
||||
}
|
||||
}
|
||||
|
||||
function hideModalLoading() {
|
||||
@@ -650,8 +691,36 @@ function hideModalLoading() {
|
||||
}
|
||||
}
|
||||
|
||||
function closeDeleteModal() {
|
||||
const modal = document.getElementById('customDeleteModal');
|
||||
if (!modal) return;
|
||||
|
||||
// Check if loading - don't close if loading
|
||||
if (document.querySelector('.modal-loading-overlay')) {
|
||||
return;
|
||||
}
|
||||
|
||||
modal.classList.remove('show');
|
||||
|
||||
setTimeout(() => {
|
||||
modal.style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}, 300);
|
||||
|
||||
// Remove ESC key listener
|
||||
document.removeEventListener('keydown', handleEscKey);
|
||||
|
||||
// Clean up any existing event listeners
|
||||
const backdrop = modal.querySelector('.custom-modal-backdrop');
|
||||
if (backdrop) {
|
||||
backdrop.onclick = null;
|
||||
}
|
||||
}
|
||||
|
||||
function showDeleteModal() {
|
||||
const modal = document.getElementById('customDeleteModal');
|
||||
if (!modal) return;
|
||||
|
||||
modal.style.display = 'flex';
|
||||
document.body.style.overflow = 'hidden';
|
||||
|
||||
@@ -662,11 +731,14 @@ function showDeleteModal() {
|
||||
|
||||
// Close modal when clicking backdrop (only if not loading)
|
||||
const backdrop = modal.querySelector('.custom-modal-backdrop');
|
||||
backdrop.onclick = function() {
|
||||
if (!document.querySelector('.modal-loading-overlay')) {
|
||||
closeDeleteModal();
|
||||
}
|
||||
};
|
||||
if (backdrop) {
|
||||
backdrop.onclick = function(e) {
|
||||
// Only close if clicking the backdrop itself, not its children
|
||||
if (e.target === backdrop && !document.querySelector('.modal-loading-overlay')) {
|
||||
closeDeleteModal();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Close modal with ESC key (only if not loading)
|
||||
document.addEventListener('keydown', handleEscKey);
|
||||
@@ -728,7 +800,7 @@ function updateServicesTable() {
|
||||
<a href="/admin/service/${service.slug || 'unknown'}/details" class="btn btn-sm btn-outline-warning" title="Edit Details">
|
||||
<i class="fas fa-cog"></i>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteService(${index})" title="Delete Service">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteService('${service.slug || 'unknown'}')" title="Delete Service">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -963,6 +1035,7 @@ function showError(message) {
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.custom-modal-header {
|
||||
@@ -970,7 +1043,7 @@ function showError(message) {
|
||||
padding: 20px 25px 15px;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
display: flex;
|
||||
justify-content: between;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -995,13 +1068,20 @@ function showError(message) {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.custom-modal-close:hover {
|
||||
.custom-modal-close:hover:not(:disabled) {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.custom-modal-close:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.custom-modal-body {
|
||||
padding: 25px;
|
||||
text-align: center;
|
||||
@@ -1094,9 +1174,12 @@ function showError(message) {
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.custom-modal-close:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
/* Ensure modal content has relative positioning for overlay */
|
||||
.custom-modal-content {
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user