forked from UKSOURCE/cms.hailearning.edu.vn
742 lines
48 KiB
Plaintext
742 lines
48 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">Edit content displayed on Pricing page</p>
|
|
</div>
|
|
<div>
|
|
<a href="<%= frontendUrl %>/pricing/" class="btn btn-outline-primary" target="_blank">
|
|
<i class="fas fa-external-link-alt me-2"></i>View Pricing Page
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<form method="POST" class="content-with-fixed-buttons" id="pricingForm" action="/admin/pricing/update">
|
|
<!-- Hidden inputs for JSON data -->
|
|
<input type="hidden" name="hero" id="heroJson">
|
|
<input type="hidden" name="pricingSection" id="pricingSectionJson">
|
|
<input type="hidden" name="plans" id="plansJson">
|
|
<input type="hidden" name="testimonials" id="testimonialsJson">
|
|
|
|
<!-- Navigation Tabs -->
|
|
<div class="card shadow-sm border-0 mb-4">
|
|
<div class="card-header bg-white border-bottom">
|
|
<ul class="nav nav-tabs card-header-tabs" role="tablist">
|
|
<li class="nav-item">
|
|
<a class="nav-link active" data-bs-toggle="tab" href="#hero" role="tab">
|
|
<i class="fas fa-home me-2"></i>Hero
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#pricingSection" role="tab">
|
|
<i class="fas fa-tags me-2"></i>Pricing Section
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#plans" role="tab">
|
|
<i class="fas fa-dollar-sign me-2"></i>Plans
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#testimonials" role="tab">
|
|
<i class="fas fa-quote-right me-2"></i>Testimonials
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
<div class="tab-content">
|
|
<!-- Hero Tab -->
|
|
<div class="tab-pane fade show active" id="hero" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<h6 class="fw-medium mb-3">Hero Section</h6>
|
|
<div class="row g-3">
|
|
<div class="col-md-5">
|
|
<label class="form-label fw-medium">Background Image</label>
|
|
<div class="input-group mb-2">
|
|
<input type="text" class="form-control" id="heroBackgroundImage"
|
|
name="heroBackgroundImage"
|
|
value="<%= data.hero?.backgroundImage || '' %>">
|
|
<button type="button"
|
|
class="btn btn-outline-primary btn-upload-image"
|
|
data-target-input="heroBackgroundImage"
|
|
data-image-type="pricing">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
<small class="text-muted">Recommended size: 1920x1080px</small>
|
|
</div>
|
|
<div class="col-md-7">
|
|
<div id="heroImagePreview" style="height: 200px;">
|
|
<% if (data.hero?.backgroundImage) { %>
|
|
<% let heroImgSrc=data.hero.backgroundImage; if (heroImgSrc &&
|
|
!heroImgSrc.startsWith('http://') &&
|
|
!heroImgSrc.startsWith('https://')) {
|
|
heroImgSrc=heroImgSrc.startsWith('/') ? heroImgSrc : '/' +
|
|
heroImgSrc; } %>
|
|
<img src="<%= heroImgSrc %>" class="img-thumbnail"
|
|
id="heroPreviewImg"
|
|
style="height: 200px; width: 100%; object-fit: cover;"
|
|
alt="Background image preview"
|
|
onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
|
|
<div class="border rounded p-5 text-center text-muted"
|
|
style="height: 200px; display: none; align-items: center; justify-content: center;">
|
|
Image preview
|
|
</div>
|
|
<% } else { %>
|
|
<div class="border rounded p-5 text-center text-muted"
|
|
style="height: 200px; display: flex; align-items: center; justify-content: center;">
|
|
Image preview
|
|
</div>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row g-3 mt-2">
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Title</label>
|
|
<input type="text" class="form-control" id="heroTitle" name="heroTitle"
|
|
value="<%= data.hero?.title || '' %>">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pricing Section Tab -->
|
|
<div class="tab-pane fade" id="pricingSection" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<h6 class="fw-medium mb-3">Pricing Section Header</h6>
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Subtitle</label>
|
|
<input type="text" class="form-control" id="pricingSectionSubtitle"
|
|
value="<%= data.pricingSection?.subtitle || '' %>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Heading</label>
|
|
<input type="text" class="form-control" id="pricingSectionHeading"
|
|
value="<%= data.pricingSection?.heading || '' %>">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Description</label>
|
|
<textarea class="form-control" id="pricingSectionDescription"
|
|
rows="3"><%= data.pricingSection?.description || '' %></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Plans Tab -->
|
|
<div class="tab-pane fade" id="plans" role="tabpanel">
|
|
<!-- Monthly Plans -->
|
|
<div class="card border shadow-sm mb-4">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h6 class="fw-medium mb-0">Monthly Plans</h6>
|
|
<button type="button" class="btn btn-primary btn-sm"
|
|
onclick="addPlan('monthly')">
|
|
<i class="fas fa-plus"></i> Add Plan
|
|
</button>
|
|
</div>
|
|
<div id="monthlyPlansContainer">
|
|
<% if (data.plans?.monthly && data.plans.monthly.length> 0) { %>
|
|
<% data.plans.monthly.forEach((plan, index)=> { %>
|
|
<div class="card mb-3 plan-item" data-type="monthly">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label">Plan Name</label>
|
|
<input type="text" class="form-control plan-name"
|
|
value="<%= plan.name || '' %>">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Price</label>
|
|
<input type="text" class="form-control plan-price"
|
|
value="<%= plan.price || '' %>">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Currency</label>
|
|
<input type="text"
|
|
class="form-control plan-currency"
|
|
value="<%= plan.currency || '$' %>">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Period</label>
|
|
<input type="text" class="form-control plan-period"
|
|
value="<%= plan.period || 'mo' %>">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">Style</label>
|
|
<select class="form-select plan-style">
|
|
<option value="default"
|
|
<%=plan.style==='default' ? 'selected' : ''
|
|
%>>Default</option>
|
|
<option value="style-2"
|
|
<%=plan.style==='style-2' ? 'selected' : ''
|
|
%>>Style 2 (Featured)</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Button Text</label>
|
|
<input type="text"
|
|
class="form-control plan-button-text"
|
|
value="<%= plan.buttonText || 'Get Started Today' %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Button Link</label>
|
|
<input type="text"
|
|
class="form-control plan-button-link"
|
|
value="<%= plan.buttonLink || '/pricing' %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Button Icon</label>
|
|
<input type="text"
|
|
class="form-control plan-button-icon"
|
|
value="<%= plan.buttonIcon || 'fa-solid fa-arrow-right' %>">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label">Features (one per
|
|
line)</label>
|
|
<textarea class="form-control plan-features"
|
|
rows="4"><%= (plan.features || []).join('\n') %></textarea>
|
|
</div>
|
|
</div>
|
|
<button type="button"
|
|
class="btn btn-outline-danger btn-sm mt-3"
|
|
onclick="removePlan(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Plan
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Yearly Plans -->
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h6 class="fw-medium mb-0">Yearly Plans</h6>
|
|
<button type="button" class="btn btn-primary btn-sm"
|
|
onclick="addPlan('yearly')">
|
|
<i class="fas fa-plus"></i> Add Plan
|
|
</button>
|
|
</div>
|
|
<div id="yearlyPlansContainer">
|
|
<% if (data.plans?.yearly && data.plans.yearly.length> 0) { %>
|
|
<% data.plans.yearly.forEach((plan, index)=> { %>
|
|
<div class="card mb-3 plan-item" data-type="yearly">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label">Plan Name</label>
|
|
<input type="text" class="form-control plan-name"
|
|
value="<%= plan.name || '' %>">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Price</label>
|
|
<input type="text" class="form-control plan-price"
|
|
value="<%= plan.price || '' %>">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Currency</label>
|
|
<input type="text"
|
|
class="form-control plan-currency"
|
|
value="<%= plan.currency || '$' %>">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Period</label>
|
|
<input type="text" class="form-control plan-period"
|
|
value="<%= plan.period || 'mo' %>">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">Style</label>
|
|
<select class="form-select plan-style">
|
|
<option value="default"
|
|
<%=plan.style==='default' ? 'selected' : ''
|
|
%>>Default</option>
|
|
<option value="style-2"
|
|
<%=plan.style==='style-2' ? 'selected' : ''
|
|
%>>Style 2 (Featured)</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Button Text</label>
|
|
<input type="text"
|
|
class="form-control plan-button-text"
|
|
value="<%= plan.buttonText || 'Get Started Today' %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Button Link</label>
|
|
<input type="text"
|
|
class="form-control plan-button-link"
|
|
value="<%= plan.buttonLink || '/pricing' %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Button Icon</label>
|
|
<input type="text"
|
|
class="form-control plan-button-icon"
|
|
value="<%= plan.buttonIcon || 'fa-solid fa-arrow-right' %>">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label">Features (one per
|
|
line)</label>
|
|
<textarea class="form-control plan-features"
|
|
rows="4"><%= (plan.features || []).join('\n') %></textarea>
|
|
</div>
|
|
</div>
|
|
<button type="button"
|
|
class="btn btn-outline-danger btn-sm mt-3"
|
|
onclick="removePlan(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Plan
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Testimonials Tab -->
|
|
<div class="tab-pane fade" id="testimonials" role="tabpanel">
|
|
<div class="card border shadow-sm mb-4">
|
|
<div class="card-body">
|
|
<h6 class="fw-medium mb-3">Testimonials Section Header</h6>
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Subtitle</label>
|
|
<input type="text" class="form-control" id="testimonialsSubtitle"
|
|
value="<%= data.testimonials?.subtitle || '' %>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Heading</label>
|
|
<input type="text" class="form-control" id="testimonialsHeading"
|
|
value="<%= data.testimonials?.heading || '' %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label fw-medium">Button Text</label>
|
|
<input type="text" class="form-control" id="testimonialsButtonText"
|
|
value="<%= data.testimonials?.buttonText || '' %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label fw-medium">Button Link</label>
|
|
<input type="text" class="form-control" id="testimonialsButtonLink"
|
|
value="<%= data.testimonials?.buttonLink || '' %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label fw-medium">Section Image</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" id="testimonialsImage"
|
|
value="<%= data.testimonials?.image || '' %>">
|
|
<button type="button"
|
|
class="btn btn-outline-primary btn-upload-image"
|
|
data-target-input="testimonialsImage" data-image-type="pricing">
|
|
<i class="fas fa-upload"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<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="addTestimonial()">
|
|
<i class="fas fa-plus"></i> Add Testimonial
|
|
</button>
|
|
</div>
|
|
<div id="testimonialsContainer">
|
|
<% if (data.testimonials?.items && data.testimonials.items.length> 0) { %>
|
|
<% data.testimonials.items.forEach((item, index)=> { %>
|
|
<div class="card mb-3 testimonial-item">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Name</label>
|
|
<input type="text"
|
|
class="form-control testimonial-name"
|
|
value="<%= item.name || '' %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Role/Type</label>
|
|
<input type="text"
|
|
class="form-control testimonial-role"
|
|
value="<%= item.role || '' %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Rating</label>
|
|
<select class="form-select testimonial-rating">
|
|
<option value="1" <%=item.rating===1
|
|
? 'selected' : '' %>>1 Star</option>
|
|
<option value="2" <%=item.rating===2
|
|
? 'selected' : '' %>>2 Stars</option>
|
|
<option value="3" <%=item.rating===3
|
|
? 'selected' : '' %>>3 Stars</option>
|
|
<option value="4" <%=item.rating===4
|
|
? 'selected' : '' %>>4 Stars</option>
|
|
<option value="5" <%=item.rating===5
|
|
? 'selected' : '' %>>5 Stars</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label">Content</label>
|
|
<textarea class="form-control testimonial-content"
|
|
rows="3"><%= item.content || '' %></textarea>
|
|
</div>
|
|
</div>
|
|
<button type="button"
|
|
class="btn btn-outline-danger btn-sm mt-3"
|
|
onclick="removeTestimonial(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Testimonial
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</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="pricingDataJson"><%- JSON.stringify(data) %></script>
|
|
|
|
<script>
|
|
let originalFormData = null;
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
try {
|
|
var jsonScript = document.getElementById('pricingDataJson');
|
|
originalFormData = JSON.parse(jsonScript.textContent);
|
|
} catch (e) {
|
|
console.error('Error parsing originalFormData:', e);
|
|
originalFormData = {};
|
|
}
|
|
updateAllJsonInputs();
|
|
initializeFormHandlers();
|
|
});
|
|
|
|
function initializeFormHandlers() {
|
|
const form = document.getElementById('pricingForm');
|
|
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 {
|
|
updateJsonData();
|
|
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';
|
|
}
|
|
});
|
|
|
|
// Image upload buttons
|
|
document.querySelectorAll('.btn-upload-image').forEach(button => {
|
|
button.addEventListener('click', function () {
|
|
const targetInput = this.dataset.targetInput;
|
|
const imageType = this.dataset.imageType;
|
|
openImageUploader(targetInput, imageType);
|
|
});
|
|
});
|
|
|
|
// Update preview when background image changes
|
|
document.getElementById('heroBackgroundImage').addEventListener('input', function () {
|
|
updateHeroImagePreview(this.value);
|
|
});
|
|
}
|
|
|
|
function updateHeroImagePreview(imagePath) {
|
|
const previewContainer = document.getElementById('heroImagePreview');
|
|
if (imagePath) {
|
|
let imgSrc = imagePath;
|
|
if (!imgSrc.startsWith('http://') && !imgSrc.startsWith('https://')) {
|
|
imgSrc = imgSrc.startsWith('/') ? imgSrc : '/' + imgSrc;
|
|
}
|
|
previewContainer.innerHTML = `
|
|
<img src="${imgSrc}" class="img-thumbnail" id="heroPreviewImg"
|
|
style="height: 200px; width: 100%; object-fit: cover;"
|
|
alt="Background image preview"
|
|
onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
|
|
<div class="border rounded p-5 text-center text-muted"
|
|
style="height: 200px; display: none; align-items: center; justify-content: center;">
|
|
Image preview
|
|
</div>
|
|
`;
|
|
} else {
|
|
previewContainer.innerHTML = `
|
|
<div class="border rounded p-5 text-center text-muted"
|
|
style="height: 200px; display: flex; align-items: center; justify-content: center;">
|
|
Image preview
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
function updateAllJsonInputs() {
|
|
updateJsonData();
|
|
}
|
|
|
|
function updateJsonData() {
|
|
// Hero data
|
|
const heroData = {
|
|
title: document.getElementById('heroTitle').value || '',
|
|
backgroundImage: document.getElementById('heroBackgroundImage').value || '',
|
|
shapeImage: originalFormData?.hero?.shapeImage || '/assets/img/inner-page/shape.png',
|
|
breadcrumb: originalFormData?.hero?.breadcrumb || [],
|
|
};
|
|
document.getElementById('heroJson').value = JSON.stringify(heroData);
|
|
|
|
// Pricing Section data
|
|
const pricingSectionData = {
|
|
subtitle: document.getElementById('pricingSectionSubtitle').value || '',
|
|
heading: document.getElementById('pricingSectionHeading').value || '',
|
|
description: document.getElementById('pricingSectionDescription').value || '',
|
|
};
|
|
document.getElementById('pricingSectionJson').value = JSON.stringify(pricingSectionData);
|
|
|
|
// Plans data
|
|
const monthlyPlans = [];
|
|
document.querySelectorAll('#monthlyPlansContainer .plan-item').forEach(item => {
|
|
const featuresText = item.querySelector('.plan-features').value || '';
|
|
monthlyPlans.push({
|
|
name: item.querySelector('.plan-name').value || '',
|
|
price: item.querySelector('.plan-price').value || '0',
|
|
currency: item.querySelector('.plan-currency').value || '$',
|
|
period: item.querySelector('.plan-period').value || 'mo',
|
|
style: item.querySelector('.plan-style').value || 'default',
|
|
buttonText: item.querySelector('.plan-button-text').value || 'Get Started Today',
|
|
buttonLink: item.querySelector('.plan-button-link').value || '/pricing',
|
|
buttonIcon: item.querySelector('.plan-button-icon').value || 'fa-solid fa-arrow-right',
|
|
features: featuresText.split('\n').filter(f => f.trim()),
|
|
});
|
|
});
|
|
|
|
const yearlyPlans = [];
|
|
document.querySelectorAll('#yearlyPlansContainer .plan-item').forEach(item => {
|
|
const featuresText = item.querySelector('.plan-features').value || '';
|
|
yearlyPlans.push({
|
|
name: item.querySelector('.plan-name').value || '',
|
|
price: item.querySelector('.plan-price').value || '0',
|
|
currency: item.querySelector('.plan-currency').value || '$',
|
|
period: item.querySelector('.plan-period').value || 'mo',
|
|
style: item.querySelector('.plan-style').value || 'default',
|
|
buttonText: item.querySelector('.plan-button-text').value || 'Get Started Today',
|
|
buttonLink: item.querySelector('.plan-button-link').value || '/pricing',
|
|
buttonIcon: item.querySelector('.plan-button-icon').value || 'fa-solid fa-arrow-right',
|
|
features: featuresText.split('\n').filter(f => f.trim()),
|
|
});
|
|
});
|
|
|
|
document.getElementById('plansJson').value = JSON.stringify({
|
|
monthly: monthlyPlans,
|
|
yearly: yearlyPlans,
|
|
});
|
|
|
|
// Testimonials data
|
|
const testimonialItems = [];
|
|
document.querySelectorAll('#testimonialsContainer .testimonial-item').forEach(item => {
|
|
testimonialItems.push({
|
|
name: item.querySelector('.testimonial-name').value || '',
|
|
role: item.querySelector('.testimonial-role').value || '',
|
|
rating: parseInt(item.querySelector('.testimonial-rating').value) || 5,
|
|
content: item.querySelector('.testimonial-content').value || '',
|
|
});
|
|
});
|
|
|
|
const testimonialsData = {
|
|
subtitle: document.getElementById('testimonialsSubtitle').value || '',
|
|
heading: document.getElementById('testimonialsHeading').value || '',
|
|
buttonText: document.getElementById('testimonialsButtonText').value || '',
|
|
buttonLink: document.getElementById('testimonialsButtonLink').value || '',
|
|
buttonIcon: originalFormData?.testimonials?.buttonIcon || 'fa-solid fa-arrow-right',
|
|
image: document.getElementById('testimonialsImage').value || '',
|
|
items: testimonialItems,
|
|
};
|
|
document.getElementById('testimonialsJson').value = JSON.stringify(testimonialsData);
|
|
}
|
|
|
|
function addPlan(type) {
|
|
const container = document.getElementById(type + 'PlansContainer');
|
|
const html = `
|
|
<div class="card mb-3 plan-item" data-type="${type}">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label">Plan Name</label>
|
|
<input type="text" class="form-control plan-name" value="">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Price</label>
|
|
<input type="text" class="form-control plan-price" value="">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Currency</label>
|
|
<input type="text" class="form-control plan-currency" value="$">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Period</label>
|
|
<input type="text" class="form-control plan-period" value="mo">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">Style</label>
|
|
<select class="form-select plan-style">
|
|
<option value="default" selected>Default</option>
|
|
<option value="style-2">Style 2 (Featured)</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Button Text</label>
|
|
<input type="text" class="form-control plan-button-text" value="Get Started Today">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Button Link</label>
|
|
<input type="text" class="form-control plan-button-link" value="/pricing">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Button Icon</label>
|
|
<input type="text" class="form-control plan-button-icon" value="fa-solid fa-arrow-right">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label">Features (one per line)</label>
|
|
<textarea class="form-control plan-features" rows="4"></textarea>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-danger btn-sm mt-3" onclick="removePlan(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Plan
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
}
|
|
|
|
function removePlan(button) {
|
|
if (confirm('Are you sure you want to remove this plan?')) {
|
|
button.closest('.plan-item').remove();
|
|
}
|
|
}
|
|
|
|
function addTestimonial() {
|
|
const container = document.getElementById('testimonialsContainer');
|
|
const html = `
|
|
<div class="card mb-3 testimonial-item">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Name</label>
|
|
<input type="text" class="form-control testimonial-name" value="">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Role/Type</label>
|
|
<input type="text" class="form-control testimonial-role" value="">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Rating</label>
|
|
<select class="form-select testimonial-rating">
|
|
<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-12">
|
|
<label class="form-label">Content</label>
|
|
<textarea class="form-control testimonial-content" rows="3"></textarea>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-danger btn-sm mt-3" onclick="removeTestimonial(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Testimonial
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
}
|
|
|
|
function removeTestimonial(button) {
|
|
if (confirm('Are you sure you want to remove this testimonial?')) {
|
|
button.closest('.testimonial-item').remove();
|
|
}
|
|
}
|
|
|
|
function resetForm() {
|
|
if (confirm('Are you sure you want to reset all changes?')) {
|
|
location.reload();
|
|
}
|
|
}
|
|
|
|
// Image uploader function
|
|
function openImageUploader(targetInput, imageType) {
|
|
const input = document.createElement('input');
|
|
input.type = 'file';
|
|
input.accept = 'image/*';
|
|
input.onchange = async function (e) {
|
|
const file = e.target.files[0];
|
|
if (!file) return;
|
|
|
|
const formData = new FormData();
|
|
formData.append('image', file);
|
|
|
|
try {
|
|
// Send imageType via query string as controller expects req.query.imageType
|
|
const uploadUrl = '/admin/upload/image?imageType=' + encodeURIComponent(imageType || 'general');
|
|
const response = await fetch(uploadUrl, {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
|
|
const result = await response.json();
|
|
if (result.success && result.path) {
|
|
document.getElementById(targetInput).value = result.path;
|
|
if (targetInput === 'heroBackgroundImage') {
|
|
updateHeroImagePreview(result.path);
|
|
}
|
|
} else {
|
|
alert('Upload failed: ' + (result.error || 'Unknown error'));
|
|
}
|
|
} catch (error) {
|
|
console.error('Upload error:', error);
|
|
alert('Upload failed. Please try again.');
|
|
}
|
|
};
|
|
input.click();
|
|
}
|
|
</script> |