Files

438 lines
22 KiB
Plaintext

<div class="container">
<div class="d-flex justify-content-between align-items-center mt-4 mb-4">
<div>
<h1 class="h3 mb-0" style="color: var(--primary-dark);">
<%= title %>
</h1>
<p class="text-muted mb-0">Manage testimonials section</p>
</div>
<div>
<a href="<%= frontendUrl %>/" class="btn btn-outline-primary" target="_blank">
<i class="fas fa-external-link-alt me-2"></i>View Homepage
</a>
</div>
</div>
<div class="row">
<div class="col-12">
<form method="POST" class="content-with-fixed-buttons" id="testimonialForm"
action=" /update">
<!-- Hidden input for items JSON data -->
<input type="hidden" name="items" id="itemsJson">
<div class="card shadow-sm border-0 mb-4">
<div class="card-header bg-white border-bottom">
<h5 class="mb-0"><i class="fas fa-quote-left me-2"></i>Testimonials Settings</h5>
</div>
<div class="card-body">
<!-- Header Section -->
<div class="row g-3 mb-4">
<div class="col-md-6">
<label class="form-label fw-medium">Heading</label>
<input type="text" class="form-control" name="heading" id="heading"
value="<%= data.heading || '' %>">
</div>
<div class="col-md-6">
<label class="form-label fw-medium">Subheading</label>
<input type="text" class="form-control" name="subheading" id="subheading"
value="<%= data.subheading || '' %>">
</div>
</div>
<!-- Video Section -->
<div class="row g-3 mb-4">
<div class="col-md-6">
<label class="form-label fw-medium">Video URL</label>
<input type="text" class="form-control" name="videoUrl" id="videoUrl"
value="<%= data.videoUrl || '' %>"
placeholder="https://www.youtube.com/watch?v=...">
<small class="text-muted">YouTube video URL</small>
</div>
<div class="col-md-6">
<label class="form-label fw-medium">Video Thumbnail</label>
<div class="input-group">
<input type="text" class="form-control" name="videoThumbnail" id="videoThumbnail"
value="<%= data.videoThumbnail || '' %>">
<button type="button" class="btn btn-outline-primary btn-upload-image"
data-target-input="videoThumbnail" data-image-type="testimonial">
<i class="fas fa-upload me-1"></i>Upload
</button>
</div>
<% if (data.videoThumbnail) { %>
<img src="<%= data.videoThumbnail %>" class="img-thumbnail mt-2"
style="max-height: 100px;" alt="Thumbnail preview">
<% } %>
</div>
</div>
<hr class="my-4">
<!-- Testimonial Items -->
<div class="d-flex justify-content-between align-items-center mb-3">
<h6 class="fw-medium mb-0">Testimonial Items</h6>
<button type="button" class="btn btn-primary btn-sm" onclick="addTestimonialItem()">
<i class="fas fa-plus"></i> Add Testimonial
</button>
</div>
<div id="testimonialItemsContainer">
<% if (data.items && data.items.length> 0) { %>
<% data.items.forEach((item, index)=> { %>
<div class="card mb-3 testimonial-item">
<div class="card-body">
<div class="row g-3">
<div class="col-md-3">
<label class="form-label">Name</label>
<input type="text" class="form-control" name="itemName_<%= index %>"
value="<%= item.name || '' %>">
</div>
<div class="col-md-3">
<label class="form-label">Role</label>
<input type="text" class="form-control" name="itemRole_<%= index %>"
value="<%= item.role || '' %>">
</div>
<div class="col-md-3">
<label class="form-label">Country</label>
<input type="text" class="form-control"
name="itemCountry_<%= index %>"
value="<%= item.country || '' %>">
</div>
<div class="col-md-3">
<label class="form-label">Rating (1-5)</label>
<select class="form-select" name="itemRating_<%= index %>">
<% for (let i=1; i <=5; i++) { %>
<option value="<%= i %>" <%=item.rating===i ? 'selected'
: '' %>>
<%= i %> Star<%= i> 1 ? 's' : '' %>
</option>
<% } %>
</select>
</div>
<div class="col-md-8">
<label class="form-label">Comment</label>
<textarea class="form-control" name="itemComment_<%= index %>"
rows="3"><%= item.comment || '' %></textarea>
</div>
<div class="col-md-4">
<label class="form-label">Avatar</label>
<div class="input-group">
<input type="text" class="form-control"
name="itemAvatar_<%= index %>"
value="<%= item.avatar || '' %>">
<button type="button"
class="btn btn-outline-primary btn-upload-image"
data-target-input="itemAvatar_<%= index %>"
data-image-type="testimonial">
<i class="fas fa-upload me-1"></i>Upload
</button>
</div>
<% if (item.avatar) { %>
<img src="<%= item.avatar %>"
class="img-thumbnail mt-2 avatar-preview"
style="max-height: 60px; max-width: 60px; border-radius: 50%;"
alt="Avatar">
<% } %>
</div>
</div>
<button type="button" class="btn btn-outline-danger btn-sm mt-3"
onclick="removeTestimonialItem(this)">
<i class="fas fa-trash me-2"></i>Remove
</button>
</div>
</div>
<% }); %>
<% } %>
</div>
</div>
</div>
<!-- Fixed Bottom Buttons -->
<div class="fixed-bottom-buttons">
<button type="reset" class="btn btn-secondary" onclick="resetForm()">
<i class="fas fa-undo me-2"></i>Reset
</button>
<button type="submit" class="btn btn-primary" id="submitBtn">
<i class="fas fa-save me-2"></i>Save Changes
</button>
</div>
</form>
</div>
</div>
</div>
<script type="application/json" id="testimonialDataJson"><%- JSON.stringify(data) %></script>
<script>
let originalFormData = null;
document.addEventListener('DOMContentLoaded', function () {
try {
var jsonScript = document.getElementById('testimonialDataJson');
originalFormData = JSON.parse(jsonScript.textContent);
} catch (e) {
console.error('Error parsing originalFormData:', e);
originalFormData = { items: [] };
}
initializeFormHandlers();
});
function initializeFormHandlers() {
const form = document.getElementById('testimonialForm');
form.addEventListener('submit', async function (e) {
e.preventDefault();
const submitBtn = document.getElementById('submitBtn');
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Saving...';
try {
updateItemsJson();
this.submit();
} catch (error) {
console.error('Error updating data:', error);
alert('Failed to process form data. Please try again.');
submitBtn.disabled = false;
submitBtn.innerHTML = '<i class="fas fa-save me-2"></i>Save Changes';
}
});
document.querySelectorAll('.btn-upload-image').forEach(button => {
button.addEventListener('click', function () {
const targetInput = this.dataset.targetInput;
const imageType = this.dataset.imageType;
openImageUploader(targetInput, imageType);
});
});
}
function updateItemsJson() {
const items = [];
document.querySelectorAll('.testimonial-item').forEach((item, index) => {
const name = item.querySelector(`[name="itemName_${index}"]`)?.value ||
item.querySelector('[name^="itemName_"]')?.value || '';
const role = item.querySelector(`[name="itemRole_${index}"]`)?.value ||
item.querySelector('[name^="itemRole_"]')?.value || '';
const country = item.querySelector(`[name="itemCountry_${index}"]`)?.value ||
item.querySelector('[name^="itemCountry_"]')?.value || '';
const rating = parseInt(item.querySelector(`[name="itemRating_${index}"]`)?.value ||
item.querySelector('[name^="itemRating_"]')?.value || '5');
const comment = item.querySelector(`[name="itemComment_${index}"]`)?.value ||
item.querySelector('[name^="itemComment_"]')?.value || '';
const avatar = item.querySelector(`[name="itemAvatar_${index}"]`)?.value ||
item.querySelector('[name^="itemAvatar_"]')?.value || '';
items.push({ name, role, country, rating, comment, avatar });
});
document.getElementById('itemsJson').value = JSON.stringify(items);
}
function addTestimonialItem() {
const container = document.getElementById('testimonialItemsContainer');
const index = container.querySelectorAll('.testimonial-item').length;
const html = `
<div class="card mb-3 testimonial-item">
<div class="card-body">
<div class="row g-3">
<div class="col-md-3">
<label class="form-label">Name</label>
<input type="text" class="form-control" name="itemName_${index}" value="">
</div>
<div class="col-md-3">
<label class="form-label">Role</label>
<input type="text" class="form-control" name="itemRole_${index}" value="">
</div>
<div class="col-md-3">
<label class="form-label">Country</label>
<input type="text" class="form-control" name="itemCountry_${index}" value="">
</div>
<div class="col-md-3">
<label class="form-label">Rating (1-5)</label>
<select class="form-select" name="itemRating_${index}">
<option value="1">1 Star</option>
<option value="2">2 Stars</option>
<option value="3">3 Stars</option>
<option value="4">4 Stars</option>
<option value="5" selected>5 Stars</option>
</select>
</div>
<div class="col-md-8">
<label class="form-label">Comment</label>
<textarea class="form-control" name="itemComment_${index}" rows="3"></textarea>
</div>
<div class="col-md-4">
<label class="form-label">Avatar</label>
<div class="input-group">
<input type="text" class="form-control" name="itemAvatar_${index}" value="">
<button type="button" class="btn btn-outline-primary btn-upload-image"
data-target-input="itemAvatar_${index}" data-image-type="testimonial">
<i class="fas fa-upload me-1"></i>Upload
</button>
</div>
</div>
</div>
<button type="button" class="btn btn-outline-danger btn-sm mt-3"
onclick="removeTestimonialItem(this)">
<i class="fas fa-trash me-2"></i>Remove
</button>
</div>
</div>
`;
container.insertAdjacentHTML('beforeend', html);
// Re-attach upload handlers for new item
container.querySelector('.testimonial-item:last-child .btn-upload-image').addEventListener('click', function () {
const targetInput = this.dataset.targetInput;
const imageType = this.dataset.imageType;
openImageUploader(targetInput, imageType);
});
}
function removeTestimonialItem(button) {
if (confirm('Are you sure you want to remove this testimonial?')) {
button.closest('.testimonial-item').remove();
reindexItems();
}
}
function reindexItems() {
document.querySelectorAll('.testimonial-item').forEach((item, index) => {
item.querySelectorAll('[name^="item"]').forEach(input => {
const baseName = input.name.replace(/_\d+$/, '');
input.name = `${baseName}_${index}`;
});
});
}
function resetForm() {
if (confirm('Are you sure you want to reset all changes?')) {
location.reload();
}
}
function openImageUploader(targetInput, imageType) {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'image/*';
fileInput.style.display = 'none';
document.body.appendChild(fileInput);
fileInput.onchange = async function (e) {
const file = e.target.files[0];
if (!file) return;
try {
const formData = new FormData();
formData.append('image', file);
const uploadBtn = document.querySelector(`[data-target-input="${targetInput}"]`);
const originalBtnHtml = uploadBtn ? uploadBtn.innerHTML : 'Upload';
if (uploadBtn) {
uploadBtn.disabled = true;
uploadBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Uploading...';
}
const response = await fetch(`/admin/upload/image?imageType=${imageType}`, {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error('Upload failed');
}
const result = await response.json();
if (!result.success) {
throw new Error(result.error || 'Upload failed');
}
const input = document.getElementById(targetInput) || document.querySelector(`[name="${targetInput}"]`);
if (!input) {
throw new Error('Target input not found');
}
input.value = result.path;
// Update preview if exists
const previewUrl = (result.path && (result.path.startsWith('http://') || result.path.startsWith('https://')))
? result.path
: (window.location.origin + result.path);
const parent = input.closest('.col-md-4') || input.closest('div');
let previewImg = parent.querySelector('.avatar-preview, .img-thumbnail');
if (previewImg) {
previewImg.src = previewUrl;
} else {
const img = document.createElement('img');
img.src = previewUrl;
img.className = 'img-thumbnail mt-2';
img.style.maxHeight = '60px';
img.alt = 'Preview';
input.closest('.input-group').after(img);
}
if (uploadBtn) {
uploadBtn.disabled = false;
uploadBtn.innerHTML = originalBtnHtml;
}
showToast('Success', 'Image uploaded successfully', 'success');
} catch (error) {
console.error('Upload error:', error);
showToast('Error', 'Failed to upload image: ' + error.message, 'error');
const uploadBtn = document.querySelector(`[data-target-input="${targetInput}"]`);
if (uploadBtn) {
uploadBtn.disabled = false;
uploadBtn.innerHTML = '<i class="fas fa-upload me-1"></i>Upload';
}
} finally {
document.body.removeChild(fileInput);
}
};
fileInput.click();
}
function showToast(title, message, type = 'info') {
const toast = document.createElement('div');
toast.className = `toast align-items-center text-white bg-${type === 'error' ? 'danger' : type} border-0`;
toast.setAttribute('role', 'alert');
toast.setAttribute('aria-live', 'assertive');
toast.setAttribute('aria-atomic', 'true');
toast.innerHTML = `
<div class="d-flex">
<div class="toast-body">
<strong>${title}:</strong> ${message}
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
`;
let container = document.querySelector('.toast-container');
if (!container) {
container = document.createElement('div');
container.className = 'toast-container position-fixed top-0 end-0 p-3';
document.body.appendChild(container);
}
container.appendChild(toast);
const bsToast = new bootstrap.Toast(toast, {
animation: true,
autohide: true,
delay: 3000
});
bsToast.show();
toast.addEventListener('hidden.bs.toast', () => {
toast.remove();
});
}
</script>