feat: Implement admin management for FAQ, Testimonial, and Video Gallery sections with new controllers, views, and routing.

This commit is contained in:
LNHA
2026-02-05 16:03:37 +07:00
parent b4891101e7
commit 6e0a97edef
11 changed files with 1229 additions and 1911 deletions

View File

@@ -0,0 +1,216 @@
<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 FAQ section content</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="faqForm" action="/admin/faq/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-question-circle me-2"></i>FAQ 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 class="col-md-12">
<label class="form-label fw-medium">Description</label>
<textarea class="form-control" name="description" id="description"
rows="2"><%= data.description || '' %></textarea>
</div>
</div>
<!-- CTA Button Section -->
<h6 class="fw-medium mb-3">CTA Button</h6>
<div class="row g-3 mb-4">
<div class="col-md-6">
<label class="form-label fw-medium">Button Label</label>
<input type="text" class="form-control" name="ctaLabel"
value="<%= data.ctaButton && data.ctaButton.label ? data.ctaButton.label : '' %>">
</div>
<div class="col-md-6">
<label class="form-label fw-medium">Button Link</label>
<input type="text" class="form-control" name="ctaHref"
value="<%= data.ctaButton && data.ctaButton.href ? data.ctaButton.href : '' %>">
</div>
</div>
<hr class="my-4">
<!-- FAQ Items -->
<div class="d-flex justify-content-between align-items-center mb-3">
<h6 class="fw-medium mb-0">Frequently Asked Questions</h6>
<button type="button" class="btn btn-primary btn-sm" onclick="addFaqItem()">
<i class="fas fa-plus"></i> Add Question
</button>
</div>
<div id="faqItemsContainer">
<% if (data.items && data.items.length> 0) { %>
<% data.items.forEach((item, index)=> { %>
<div class="card mb-3 faq-item">
<div class="card-body">
<div class="row g-3">
<div class="col-md-12">
<label class="form-label">Question</label>
<input type="text" class="form-control"
name="itemQuestion_<%= index %>"
value="<%= item.question || '' %>">
</div>
<div class="col-md-12">
<label class="form-label">Answer</label>
<textarea class="form-control" name="itemAnswer_<%= index %>"
rows="3"><%= item.answer || '' %></textarea>
</div>
</div>
<button type="button" class="btn btn-outline-danger btn-sm mt-3"
onclick="removeFaqItem(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="faqDataJson"><%- JSON.stringify(data) %></script>
<script>
let originalFormData = null;
document.addEventListener('DOMContentLoaded', function () {
try {
var jsonScript = document.getElementById('faqDataJson');
originalFormData = JSON.parse(jsonScript.textContent);
} catch (e) {
console.error('Error parsing originalFormData:', e);
originalFormData = { items: [] };
}
initializeFormHandlers();
});
function initializeFormHandlers() {
const form = document.getElementById('faqForm');
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';
}
});
}
function updateItemsJson() {
const items = [];
document.querySelectorAll('.faq-item').forEach((item, index) => {
const question = item.querySelector(`[name="itemQuestion_${index}"]`)?.value ||
item.querySelector('[name^="itemQuestion_"]')?.value || '';
const answer = item.querySelector(`[name="itemAnswer_${index}"]`)?.value ||
item.querySelector('[name^="itemAnswer_"]')?.value || '';
items.push({ question, answer });
});
document.getElementById('itemsJson').value = JSON.stringify(items);
}
function addFaqItem() {
const container = document.getElementById('faqItemsContainer');
const index = container.querySelectorAll('.faq-item').length;
const html = `
<div class="card mb-3 faq-item">
<div class="card-body">
<div class="row g-3">
<div class="col-md-12">
<label class="form-label">Question</label>
<input type="text" class="form-control" name="itemQuestion_${index}" value="">
</div>
<div class="col-md-12">
<label class="form-label">Answer</label>
<textarea class="form-control" name="itemAnswer_${index}" rows="3"></textarea>
</div>
</div>
<button type="button" class="btn btn-outline-danger btn-sm mt-3"
onclick="removeFaqItem(this)">
<i class="fas fa-trash me-2"></i>Remove
</button>
</div>
</div>
`;
container.insertAdjacentHTML('beforeend', html);
}
function removeFaqItem(button) {
if (confirm('Are you sure you want to remove this question?')) {
button.closest('.faq-item').remove();
reindexItems();
}
}
function reindexItems() {
document.querySelectorAll('.faq-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();
}
}
</script>