forked from UKSOURCE/cms.hailearning.edu.vn
feat: Implement core admin panel functionalities including appointment, contact, and pricing management with associated models, controllers, views, and routes.
This commit is contained in:
791
views/admin/appointment/index.ejs
Normal file
791
views/admin/appointment/index.ejs
Normal file
@@ -0,0 +1,791 @@
|
||||
<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 Appointment page</p>
|
||||
</div>
|
||||
<div>
|
||||
<a href="<%= frontendUrl %>/make-appointment/" class="btn btn-outline-primary" target="_blank">
|
||||
<i class="fas fa-external-link-alt me-2"></i>View Appointment Page
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<form method="POST" class="content-with-fixed-buttons" id="appointmentForm"
|
||||
action="/admin/appointment/update">
|
||||
<!-- Hidden inputs for JSON data -->
|
||||
<input type="hidden" name="hero" id="heroJson">
|
||||
<input type="hidden" name="visaOptions" id="visaOptionsJson">
|
||||
<input type="hidden" name="form" id="formJson">
|
||||
|
||||
<!-- 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="#visaOptions" role="tab">
|
||||
<i class="fas fa-passport me-2"></i>Visa Options
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="tab" href="#form" role="tab">
|
||||
<i class="fas fa-envelope me-2"></i>Form
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="tab" href="#submissions" role="tab">
|
||||
<i class="fas fa-list me-2"></i>Submissions
|
||||
</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="appointment">
|
||||
<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-6">
|
||||
<label class="form-label fw-medium">Title</label>
|
||||
<input type="text" class="form-control" id="heroTitle" name="heroTitle"
|
||||
value="<%= data.hero?.title || '' %>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-medium">Subtitle</label>
|
||||
<input type="text" class="form-control" id="heroSubtitle"
|
||||
name="heroSubtitle" value="<%= data.hero?.subtitle || '' %>">
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<label class="form-label fw-medium">Heading</label>
|
||||
<input type="text" class="form-control" id="heroHeading"
|
||||
name="heroHeading" value="<%= data.hero?.heading || '' %>">
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<label class="form-label fw-medium">Description</label>
|
||||
<textarea class="form-control" id="heroDescription"
|
||||
name="heroDescription"
|
||||
rows="2"><%= data.hero?.description || '' %></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Visa Options Tab -->
|
||||
<div class="tab-pane fade" id="visaOptions" role="tabpanel">
|
||||
<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">Visa Options</h6>
|
||||
<button type="button" class="btn btn-primary btn-sm"
|
||||
onclick="addVisaOption()">
|
||||
<i class="fas fa-plus"></i> Add Option
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-muted small">These options will appear in the visa type selection
|
||||
dropdown on the appointment form.</p>
|
||||
<div id="visaOptionsContainer">
|
||||
<% if (data.visaOptions && data.visaOptions.length> 0) { %>
|
||||
<% data.visaOptions.forEach((option, index)=> { %>
|
||||
<div class="input-group mb-2 visa-option-item">
|
||||
<span class="input-group-text"><i
|
||||
class="fas fa-passport"></i></span>
|
||||
<input type="text" class="form-control visa-option-input"
|
||||
value="<%= option %>" placeholder="Enter visa option">
|
||||
<button type="button" class="btn btn-outline-danger"
|
||||
onclick="removeVisaOption(this)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
<% }); %>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Tab -->
|
||||
<div class="tab-pane fade" id="form" role="tabpanel">
|
||||
<div class="card border shadow-sm">
|
||||
<div class="card-body">
|
||||
<h6 class="fw-medium mb-3">Form Settings</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Form Heading</label>
|
||||
<input type="text" class="form-control" id="formHeading"
|
||||
value="<%= data.form?.heading || '' %>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Submit Button Text</label>
|
||||
<input type="text" class="form-control" id="formSubmitButtonText"
|
||||
value="<%= data.form?.submitButton?.text || 'Request Appointment' %>">
|
||||
</div>
|
||||
<!-- Hidden fields for submitButton icon and buttonClass -->
|
||||
<input type="hidden" id="formSubmitButtonIcon"
|
||||
value="<%= data.form?.submitButton?.icon || 'fa-solid fa-arrow-right' %>">
|
||||
<input type="hidden" id="formSubmitButtonClass"
|
||||
value="<%= data.form?.submitButton?.buttonClass || 'theme-btn' %>">
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="fw-medium mb-0">Form Fields</h6>
|
||||
<button type="button" class="btn btn-primary btn-sm"
|
||||
onclick="addFormField()">
|
||||
<i class="fas fa-plus"></i> Add Field
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="formFieldsContainer">
|
||||
<% if (data.form?.fields && data.form.fields.length> 0) { %>
|
||||
<% data.form.fields.forEach((field, index)=> { %>
|
||||
<div class="card mb-3 form-field-item">
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Field Name</label>
|
||||
<input type="text"
|
||||
class="form-control field-name-input"
|
||||
value="<%= field.name || '' %>"
|
||||
placeholder="e.g., name">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Label</label>
|
||||
<input type="text"
|
||||
class="form-control field-label-input"
|
||||
value="<%= field.label || '' %>"
|
||||
placeholder="e.g., Your Name">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Type</label>
|
||||
<select class="form-select field-type-select">
|
||||
<option value="text" <%=field.type==='text'
|
||||
? 'selected' : '' %>>Text</option>
|
||||
<option value="email" <%=field.type==='email'
|
||||
? 'selected' : '' %>>Email</option>
|
||||
<option value="tel" <%=field.type==='tel'
|
||||
? 'selected' : '' %>>Phone</option>
|
||||
<option value="textarea"
|
||||
<%=field.type==='textarea' ? 'selected' : ''
|
||||
%>>Textarea</option>
|
||||
<option value="date" <%=field.type==='date'
|
||||
? 'selected' : '' %>>Date</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Col Class</label>
|
||||
<select class="form-select field-col-select">
|
||||
<option value="col-lg-4"
|
||||
<%=field.colClass==='col-lg-4' ? 'selected'
|
||||
: '' %>>1/3 Width</option>
|
||||
<option value="col-lg-6"
|
||||
<%=field.colClass==='col-lg-6' ? 'selected'
|
||||
: '' %>>1/2 Width</option>
|
||||
<option value="col-lg-12"
|
||||
<%=field.colClass==='col-lg-12' ? 'selected'
|
||||
: '' %>>Full Width</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Required</label>
|
||||
<div class="form-check mt-2">
|
||||
<input
|
||||
class="form-check-input field-required-check"
|
||||
type="checkbox" <%=field.required
|
||||
? 'checked' : '' %>>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Placeholder</label>
|
||||
<input type="text"
|
||||
class="form-control field-placeholder-input"
|
||||
value="<%= field.placeholder || '' %>">
|
||||
</div>
|
||||
</div>
|
||||
<button type="button"
|
||||
class="btn btn-outline-danger btn-sm mt-3"
|
||||
onclick="removeFormField(this)">
|
||||
<i class="fas fa-trash me-2"></i>Remove Field
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<% }); %>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Submissions Tab -->
|
||||
<div class="tab-pane fade" id="submissions" role="tabpanel">
|
||||
<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">Recent Submissions</h6>
|
||||
</div>
|
||||
|
||||
<!-- Date Filter -->
|
||||
<div class="row g-2 mb-4 align-items-end" id="filterContainer">
|
||||
<input type="hidden" id="filterTab" value="submissions">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small text-muted">Start Date</label>
|
||||
<input type="date" class="form-control form-control-sm"
|
||||
id="filterStartDate" value="<%= locals.startDate || '' %>">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small text-muted">End Date</label>
|
||||
<input type="date" class="form-control form-control-sm"
|
||||
id="filterEndDate" value="<%= locals.endDate || '' %>">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<button type="button" class="btn btn-sm btn-primary w-100"
|
||||
onclick="applyDateFilter()">
|
||||
<i class="fas fa-filter me-1"></i> Filter
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<a href="/admin/appointment?tab=submissions"
|
||||
class="btn btn-sm btn-outline-secondary w-100">
|
||||
<i class="fas fa-times me-1"></i> Clear
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Name</th>
|
||||
<th>Contact</th>
|
||||
<th>Appt Date</th>
|
||||
<th>Visa Types</th>
|
||||
<th>Message</th>
|
||||
<th>Status</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% if (locals.submissions && submissions.length> 0) { %>
|
||||
<% submissions.forEach(submission=> { %>
|
||||
<tr>
|
||||
<td>
|
||||
<%= new
|
||||
Date(submission.createdAt).toLocaleDateString()
|
||||
%>
|
||||
<br>
|
||||
<small class="text-muted">
|
||||
<%= new
|
||||
Date(submission.createdAt).toLocaleTimeString([],
|
||||
{hour: '2-digit' , minute:'2-digit'}) %>
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<%= submission.name %>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex flex-column">
|
||||
<a href="mailto:<%= submission.email %>"
|
||||
class="text-decoration-none"><i
|
||||
class="fas fa-envelope me-1"></i>
|
||||
<%= submission.email %>
|
||||
</a>
|
||||
<% if(submission.phone) { %>
|
||||
<span class="text-muted small"><i
|
||||
class="fas fa-phone me-1"></i>
|
||||
<%= submission.phone %>
|
||||
</span>
|
||||
<% } %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<%= submission.appointmentDate || '-' %>
|
||||
</td>
|
||||
<td>
|
||||
<% if (submission.visaTypes &&
|
||||
submission.visaTypes.length> 0) { %>
|
||||
<% submission.visaTypes.forEach(type=> { %>
|
||||
<span
|
||||
class="badge bg-light text-dark border me-1">
|
||||
<%= type %>
|
||||
</span>
|
||||
<% }); %>
|
||||
<% } else { %>
|
||||
<span class="text-muted">-</span>
|
||||
<% } %>
|
||||
</td>
|
||||
<td>
|
||||
<% if (submission.message) { %>
|
||||
<div title="<%= submission.message %>"
|
||||
style="max-width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
<%= submission.message %>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<span class="text-muted">-</span>
|
||||
<% } %>
|
||||
</td>
|
||||
<td>
|
||||
<% let statusClass='bg-secondary' ;
|
||||
if(submission.status==='pending' )
|
||||
statusClass='bg-warning text-dark' ;
|
||||
if(submission.status==='confirmed' )
|
||||
statusClass='bg-success' ;
|
||||
if(submission.status==='completed' )
|
||||
statusClass='bg-info text-dark' ;
|
||||
if(submission.status==='cancelled' )
|
||||
statusClass='bg-danger' ; %>
|
||||
<span
|
||||
class="badge <%= statusClass %> rounded-pill">
|
||||
<%= submission.status %>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-primary"
|
||||
onclick="openStatusModal('<%= submission._id %>', '<%= submission.status %>')"
|
||||
title="Update Status">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<% }); %>
|
||||
<% } else { %>
|
||||
<tr>
|
||||
<td colspan="8"
|
||||
class="text-center py-4 text-muted">No
|
||||
submissions found</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mt-3 text-end">
|
||||
<small class="text-muted">Showing last 50 submissions</small>
|
||||
</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>
|
||||
|
||||
<!-- Status Update Modal -->
|
||||
<div class="modal fade" id="statusModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Update Status</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="statusForm">
|
||||
<input type="hidden" id="statusSubmissionId">
|
||||
<div class="mb-3">
|
||||
<label for="statusSelect" class="form-label">Status</label>
|
||||
<select class="form-select" id="statusSelect">
|
||||
<option value="pending">Pending</option>
|
||||
<option value="confirmed">Confirmed</option>
|
||||
<option value="completed">Completed</option>
|
||||
<option value="cancelled">Cancelled</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveStatus()">Save changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="application/json" id="appointmentDataJson"><%- JSON.stringify(data) %></script>
|
||||
|
||||
<script>
|
||||
let originalFormData = null;
|
||||
let statusModal = null;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
try {
|
||||
var jsonScript = document.getElementById('appointmentDataJson');
|
||||
originalFormData = JSON.parse(jsonScript.textContent);
|
||||
} catch (e) {
|
||||
console.error('Error parsing originalFormData:', e);
|
||||
originalFormData = {};
|
||||
}
|
||||
|
||||
// Check for tab parameter in URL
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const tab = urlParams.get('tab');
|
||||
if (tab) {
|
||||
const triggerEl = document.querySelector(`a[href="#${tab}"]`);
|
||||
if (triggerEl) {
|
||||
const tabInstance = new bootstrap.Tab(triggerEl);
|
||||
tabInstance.show();
|
||||
}
|
||||
}
|
||||
|
||||
// Move modal to body to prevent backdrop issues
|
||||
const statusModalEl = document.getElementById('statusModal');
|
||||
if (statusModalEl) {
|
||||
document.body.appendChild(statusModalEl);
|
||||
}
|
||||
statusModal = new bootstrap.Modal(statusModalEl);
|
||||
|
||||
updateAllJsonInputs();
|
||||
initializeFormHandlers();
|
||||
});
|
||||
|
||||
function applyDateFilter() {
|
||||
const startDate = document.getElementById('filterStartDate').value;
|
||||
const endDate = document.getElementById('filterEndDate').value;
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('tab', 'submissions');
|
||||
|
||||
if (startDate) {
|
||||
url.searchParams.set('startDate', startDate);
|
||||
} else {
|
||||
url.searchParams.delete('startDate');
|
||||
}
|
||||
|
||||
if (endDate) {
|
||||
url.searchParams.set('endDate', endDate);
|
||||
} else {
|
||||
url.searchParams.delete('endDate');
|
||||
}
|
||||
|
||||
window.location.href = url.toString();
|
||||
}
|
||||
|
||||
function openStatusModal(id, currentStatus) {
|
||||
document.getElementById('statusSubmissionId').value = id;
|
||||
document.getElementById('statusSelect').value = currentStatus;
|
||||
statusModal.show();
|
||||
}
|
||||
|
||||
async function saveStatus() {
|
||||
const id = document.getElementById('statusSubmissionId').value;
|
||||
const status = document.getElementById('statusSelect').value;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/admin/appointments/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ status })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
// Determine CSS class for the notification or badge
|
||||
// Since this is generic, we'll reload or update UI manually if complex.
|
||||
// Reload is safest to show updated table state (including sorting/filtering if any)
|
||||
// But let's try to be smooth:
|
||||
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert('Failed to update status: ' + (result.error || 'Unknown error'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating status:', error);
|
||||
alert('Error updating status');
|
||||
}
|
||||
}
|
||||
|
||||
function initializeFormHandlers() {
|
||||
const form = document.getElementById('appointmentForm');
|
||||
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 || '',
|
||||
subtitle: document.getElementById('heroSubtitle').value || '',
|
||||
heading: document.getElementById('heroHeading').value || '',
|
||||
description: document.getElementById('heroDescription').value || '',
|
||||
};
|
||||
document.getElementById('heroJson').value = JSON.stringify(heroData);
|
||||
|
||||
// Visa options
|
||||
const visaOptions = [];
|
||||
document.querySelectorAll('.visa-option-input').forEach(input => {
|
||||
if (input.value.trim()) {
|
||||
visaOptions.push(input.value.trim());
|
||||
}
|
||||
});
|
||||
document.getElementById('visaOptionsJson').value = JSON.stringify(visaOptions);
|
||||
|
||||
// Form data
|
||||
const fields = [];
|
||||
document.querySelectorAll('.form-field-item').forEach(item => {
|
||||
fields.push({
|
||||
name: item.querySelector('.field-name-input').value || '',
|
||||
label: item.querySelector('.field-label-input').value || '',
|
||||
type: item.querySelector('.field-type-select').value || 'text',
|
||||
placeholder: item.querySelector('.field-placeholder-input').value || '',
|
||||
required: item.querySelector('.field-required-check').checked,
|
||||
colClass: item.querySelector('.field-col-select').value || 'col-lg-12',
|
||||
});
|
||||
});
|
||||
|
||||
const formData = {
|
||||
heading: document.getElementById('formHeading').value || '',
|
||||
fields: fields,
|
||||
submitButton: {
|
||||
text: document.getElementById('formSubmitButtonText').value || 'Request Appointment',
|
||||
icon: document.getElementById('formSubmitButtonIcon').value || 'fa-solid fa-arrow-right',
|
||||
buttonClass: document.getElementById('formSubmitButtonClass').value || 'theme-btn',
|
||||
},
|
||||
};
|
||||
document.getElementById('formJson').value = JSON.stringify(formData);
|
||||
}
|
||||
|
||||
function addVisaOption() {
|
||||
const container = document.getElementById('visaOptionsContainer');
|
||||
const html = `
|
||||
<div class="input-group mb-2 visa-option-item">
|
||||
<span class="input-group-text"><i class="fas fa-passport"></i></span>
|
||||
<input type="text" class="form-control visa-option-input" value="" placeholder="Enter visa option">
|
||||
<button type="button" class="btn btn-outline-danger" onclick="removeVisaOption(this)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
container.insertAdjacentHTML('beforeend', html);
|
||||
}
|
||||
|
||||
function removeVisaOption(button) {
|
||||
button.closest('.visa-option-item').remove();
|
||||
}
|
||||
|
||||
function addFormField() {
|
||||
const container = document.getElementById('formFieldsContainer');
|
||||
const html = `
|
||||
<div class="card mb-3 form-field-item">
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Field Name</label>
|
||||
<input type="text" class="form-control field-name-input" value="" placeholder="e.g., name">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Label</label>
|
||||
<input type="text" class="form-control field-label-input" value="" placeholder="e.g., Your Name">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Type</label>
|
||||
<select class="form-select field-type-select">
|
||||
<option value="text" selected>Text</option>
|
||||
<option value="email">Email</option>
|
||||
<option value="tel">Phone</option>
|
||||
<option value="textarea">Textarea</option>
|
||||
<option value="date">Date</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Col Class</label>
|
||||
<select class="form-select field-col-select">
|
||||
<option value="col-lg-4">1/3 Width</option>
|
||||
<option value="col-lg-6">1/2 Width</option>
|
||||
<option value="col-lg-12" selected>Full Width</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Required</label>
|
||||
<div class="form-check mt-2">
|
||||
<input class="form-check-input field-required-check" type="checkbox">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Placeholder</label>
|
||||
<input type="text" class="form-control field-placeholder-input" value="">
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-danger btn-sm mt-3" onclick="removeFormField(this)">
|
||||
<i class="fas fa-trash me-2"></i>Remove Field
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
container.insertAdjacentHTML('beforeend', html);
|
||||
}
|
||||
|
||||
function removeFormField(button) {
|
||||
button.closest('.form-field-item').remove();
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
if (confirm('Are you sure you want to reset all changes?')) {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
// Image uploader function (reuse from shared)
|
||||
function openImageUploader(targetInput, imageType) {
|
||||
// Open upload modal or trigger file input
|
||||
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 {
|
||||
const response = await fetch('/admin/upload/image', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (result.success && result.imagePath) {
|
||||
document.getElementById(targetInput).value = result.imagePath;
|
||||
if (targetInput === 'heroBackgroundImage') {
|
||||
updateHeroImagePreview(result.imagePath);
|
||||
}
|
||||
} else {
|
||||
alert('Upload failed: ' + (result.error || 'Unknown error'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Upload error:', error);
|
||||
alert('Upload failed. Please try again.');
|
||||
}
|
||||
};
|
||||
input.click();
|
||||
}
|
||||
</script>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
||||
<div class="col-md-4 border-end">
|
||||
<div class="p-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 50px; height: 50px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-home fa-lg" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -29,7 +29,7 @@
|
||||
<div class="col-md-4 border-end">
|
||||
<div class="p-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 50px; height: 50px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-bars fa-lg" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -47,7 +47,7 @@
|
||||
<div class="col-md-4">
|
||||
<div class="p-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 50px; height: 50px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-window-minimize fa-lg" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -67,7 +67,7 @@
|
||||
<div class="col-md-4 border-end border-top">
|
||||
<div class="p-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 50px; height: 50px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-users fa-lg" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -85,7 +85,7 @@
|
||||
<div class="col-md-4 border-end border-top">
|
||||
<div class="p-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 50px; height: 50px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-envelope fa-lg" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -103,7 +103,7 @@
|
||||
<div class="col-md-4 border-top">
|
||||
<div class="p-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 50px; height: 50px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-question-circle fa-lg" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -121,7 +121,43 @@
|
||||
<div class="col-md-4 border-end border-top">
|
||||
<div class="p-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 50px; height: 50px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-calendar-check fa-lg" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-0">Appointment</h5>
|
||||
<p class="text-muted mb-0 small">Manage appointment page</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/admin/appointment" class="btn btn-sm btn-primary w-100 mt-2">
|
||||
<i class="fas fa-edit me-2"></i>Edit
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 border-end border-top">
|
||||
<div class="p-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 50px; height: 50px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-tags fa-lg" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-0">Pricing</h5>
|
||||
<p class="text-muted mb-0 small">Manage pricing page</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/admin/pricing" class="btn btn-sm btn-primary w-100 mt-2">
|
||||
<i class="fas fa-edit me-2"></i>Edit
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 border-end border-top">
|
||||
<div class="p-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 50px; height: 50px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-file-contract fa-lg" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -139,7 +175,7 @@
|
||||
<div class="col-md-4 border-end border-top">
|
||||
<div class="p-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 50px; height: 50px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-plane fa-lg" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -157,7 +193,7 @@
|
||||
<div class="col-md-4 border-top">
|
||||
<div class="p-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 50px; height: 50px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-shield-alt fa-lg" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -175,7 +211,7 @@
|
||||
<div class="col-md-4 border-end border-top">
|
||||
<div class="p-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 50px; height: 50px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-campground fa-lg" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -193,7 +229,7 @@
|
||||
<div class="col-md-4 border-end border-top">
|
||||
<div class="p-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 50px; height: 50px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-running fa-lg" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -234,7 +270,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
style="width: 32px; height: 32px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-bars" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -253,7 +289,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
style="width: 32px; height: 32px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-home" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -272,7 +308,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
style="width: 32px; height: 32px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-info-circle" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -291,7 +327,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
style="width: 32px; height: 32px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-users" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -310,7 +346,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
style="width: 32px; height: 32px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-question-circle" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -329,7 +365,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
style="width: 32px; height: 32px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-file-contract" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -348,7 +384,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
style="width: 32px; height: 32px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-plane" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -367,7 +403,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
style="width: 32px; height: 32px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-shield-alt" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -386,7 +422,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
style="width: 32px; height: 32px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-campground" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -402,11 +438,11 @@
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
style="width: 32px; height: 32px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-sitemap" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -425,7 +461,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
style="width: 32px; height: 32px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-envelope" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -444,7 +480,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-2"
|
||||
style="width: 32px; height: 32px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-campground" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -475,7 +511,7 @@
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 40px; height: 40px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-info-circle" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
@@ -485,22 +521,25 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center me-3"
|
||||
style="width: 40px; height: 40px; background-color: rgba(184, 183, 106, 0.1);">
|
||||
<i class="fas fa-user" style="color: var(--primary-color);"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-muted small">Logged in as</div>
|
||||
<div class="fw-bold" style="color: var(--primary-color);"><%= user.username %></div>
|
||||
<div class="fw-bold" style="color: var(--primary-color);">
|
||||
<%= user.username %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert mt-3 mb-0" style="background-color: rgba(184, 183, 106, 0.05); border-left: 4px solid var(--primary-color);" role="alert">
|
||||
|
||||
<div class="alert mt-3 mb-0"
|
||||
style="background-color: rgba(184, 183, 106, 0.05); border-left: 4px solid var(--primary-color);" role="alert">
|
||||
<div class="d-flex">
|
||||
<div class="me-3">
|
||||
<i class="fas fa-lightbulb fa-lg" style="color: var(--primary-color);"></i>
|
||||
@@ -520,36 +559,36 @@
|
||||
color: var(--primary-color);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: var(--primary-dark);
|
||||
border-color: var(--primary-dark);
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.btn-outline-primary {
|
||||
color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
|
||||
.btn-outline-primary:hover {
|
||||
background-color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.card-header h5 {
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.badge {
|
||||
background-color: var(--primary-color) !important;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
742
views/admin/pricing/index.ejs
Normal file
742
views/admin/pricing/index.ejs
Normal file
@@ -0,0 +1,742 @@
|
||||
<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>
|
||||
@@ -1,122 +1,119 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link rel="icon" type="image/png" href="/uploads/layout/favicon.png" />
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>
|
||||
<%= typeof title !=='undefined' ? title + ' | ' : '' %>CMS-GGCamp
|
||||
</title>
|
||||
<!-- Bootstrap CSS -->
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<!-- Font Awesome -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
|
||||
/>
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #b8b76a;
|
||||
--primary-light: #c9c88a;
|
||||
--primary-dark: #9a994a;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: var(--primary-dark);
|
||||
border-color: var(--primary-dark);
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
color: var(--primary-color);
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
<%- style %>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<nav class="col-md-2 d-none d-md-block bg-light sidebar">
|
||||
<div class="position-sticky pt-3">
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/dashboard">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/home">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/header">Header & Menu</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/footer">Footer</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/about">About</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/about-us">About Us</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/contact">Contact</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/faq">FAQ</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/terms-conditions">Terms & Conditions</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/safety">Safety</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/camp-location">Camp Location</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/form">Form</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/upload">Upload</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<head>
|
||||
<link rel="icon" type="image/png" href="/uploads/layout/favicon.png" />
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>
|
||||
<%= typeof title !=='undefined' ? title + ' | ' : '' %>CMS-GGCamp
|
||||
</title>
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #b8b76a;
|
||||
--primary-light: #c9c88a;
|
||||
--primary-dark: #9a994a;
|
||||
}
|
||||
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||
<!-- Flash Messages Data (Hidden) -->
|
||||
<% if(typeof success_msg !=='undefined' || typeof error_msg
|
||||
!=='undefined' || typeof error !=='undefined' ) { %>
|
||||
.btn-primary {
|
||||
background-color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: var(--primary-dark);
|
||||
border-color: var(--primary-dark);
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
color: var(--primary-color);
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
<%- style %>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<nav class="col-md-2 d-none d-md-block bg-light sidebar">
|
||||
<div class="position-sticky pt-3">
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/dashboard">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/home">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/header">Header & Menu</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/footer">Footer</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/about">About</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/about-us">About Us</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/contact">Contact</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/appointment">Appointment</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/faq">FAQ</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/terms-conditions">Terms & Conditions</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/safety">Safety</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/camp-location">Camp Location</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/form">Form</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/upload">Upload</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||
<!-- Flash Messages Data (Hidden) -->
|
||||
<% if(typeof success_msg !=='undefined' || typeof error_msg !=='undefined' || typeof error !=='undefined' ) { %>
|
||||
<div id="flash-messages-data" style="display: none">
|
||||
<%- JSON.stringify({ success_msg: typeof success_msg !=='undefined'
|
||||
&& success_msg ? success_msg : null, error_msg: typeof error_msg
|
||||
!=='undefined' && error_msg ? error_msg : null, error: typeof error
|
||||
!=='undefined' && error ? error : null }) %>
|
||||
<%- JSON.stringify({ success_msg: typeof success_msg !=='undefined' && success_msg ? success_msg : null,
|
||||
error_msg: typeof error_msg !=='undefined' && error_msg ? error_msg : null, error: typeof error
|
||||
!=='undefined' && error ? error : null }) %>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="py-3"><%- body %></div>
|
||||
</main>
|
||||
</div>
|
||||
<div class="py-3"><%- body %></div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<%- script %>
|
||||
</body>
|
||||
</html>
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<%- script %>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -727,91 +727,71 @@
|
||||
About
|
||||
</a>
|
||||
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item <%= currentPath === '/admin/about-us' ? 'active' : '' %>"
|
||||
href="/admin/about-us"
|
||||
>About us</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item <%= currentPath === '/admin/safety' ? 'active' : '' %>"
|
||||
href="/admin/safety"
|
||||
>Safety</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item <%= currentPath === '/admin/FAQ' ? 'active' : '' %>"
|
||||
href="/admin/faq"
|
||||
>FAQ</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item <%= currentPath === '/admin/insurance' ? 'active' : '' %>"
|
||||
href="/admin/insurance"
|
||||
>Insurance</a
|
||||
>
|
||||
</li>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a class="dropdown-item <%= currentPath === '/admin/about-us' ? 'active' : '' %>"
|
||||
href="/admin/about-us">About us</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item <%= currentPath === '/admin/safety' ? 'active' : '' %>"
|
||||
href="/admin/safety">Safety</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item <%= currentPath === '/admin/FAQ' ? 'active' : '' %>" href="/admin/faq">FAQ</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item <%= currentPath === '/admin/insurance' ? 'active' : '' %>"
|
||||
href="/admin/insurance">Insurance</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="dropdown-item <%= currentPath === '/admin/travel' ? 'active' : '' %>"
|
||||
href="/admin/travel">Travel</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item <%= currentPath === '/admin/terms-conditions' ? 'active' : '' %>"
|
||||
href="/admin/terms-conditions">Terms & Conditions</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <%= currentPath === '/admin/contact' ? 'active' : '' %>" href="/admin/contact">Contact
|
||||
Us</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <%= currentPath === '/admin/appointment' ? 'active' : '' %>"
|
||||
href="/admin/appointment">Appointment</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <%= currentPath === '/admin/pricing' ? 'active' : '' %>"
|
||||
href="/admin/pricing">Pricing</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <%= currentPath === '/admin/camp-location' ? 'active' : '' %>"
|
||||
href="/admin/camp-location">Camp Location</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <%= currentPath === '/admin/activity' ? 'active' : '' %>" href="/admin/activity">Activity
|
||||
& Booking</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item <%= currentPath === '/admin/travel' ? 'active' : '' %>"
|
||||
href="/admin/travel"
|
||||
>Travel</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item <%= currentPath === '/admin/terms-conditions' ? 'active' : '' %>"
|
||||
href="/admin/terms-conditions"
|
||||
>Terms & Conditions</a
|
||||
>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link <%= currentPath === '/admin/contact' ? 'active' : '' %>"
|
||||
href="/admin/contact"
|
||||
>Contact Us</a
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link <%= currentPath === '/admin/camp-location' ? 'active' : '' %>"
|
||||
href="/admin/camp-location"
|
||||
>Camp Location</a
|
||||
>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link <%= currentPath === '/admin/activity' ? 'active' : '' %>"
|
||||
href="/admin/activity"
|
||||
>Activity & Booking</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</ul>
|
||||
<div class="d-flex align-items-center">
|
||||
<% if (locals.user) { %>
|
||||
<a href="/admin/dashboard" class="btn btn-primary me-2">
|
||||
<i class="fas fa-tachometer-alt me-1"></i>Dashboard
|
||||
</a>
|
||||
<a href="/auth/logout" class="btn btn-outline-danger">
|
||||
<i class="fas fa-sign-out-alt me-1"></i>Logout
|
||||
</a>
|
||||
<% } else { %>
|
||||
<a href="/auth/login" class="btn btn-outline-primary">
|
||||
<i class="fas fa-sign-in-alt me-1"></i>Login
|
||||
</a>
|
||||
<% } %>
|
||||
<a href="/admin/dashboard" class="btn btn-primary me-2">
|
||||
<i class="fas fa-tachometer-alt me-1"></i>Dashboard
|
||||
</a>
|
||||
<a href="/auth/logout" class="btn btn-outline-danger">
|
||||
<i class="fas fa-sign-out-alt me-1"></i>Logout
|
||||
</a>
|
||||
<% } else { %>
|
||||
<a href="/auth/login" class="btn btn-outline-primary">
|
||||
<i class="fas fa-sign-in-alt me-1"></i>Login
|
||||
</a>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -821,12 +801,12 @@
|
||||
<main>
|
||||
<!-- Flash Messages Data (Hidden) -->
|
||||
<% if(typeof success_msg !=='undefined' || typeof error_msg !=='undefined' || typeof error !=='undefined' ) { %>
|
||||
<div id="flash-messages-data" style="display: none">
|
||||
<%- JSON.stringify({ success_msg: typeof success_msg !=='undefined' && success_msg ? success_msg : null,
|
||||
<div id="flash-messages-data" style="display: none">
|
||||
<%- JSON.stringify({ success_msg: typeof success_msg !=='undefined' && success_msg ? success_msg : null,
|
||||
error_msg: typeof error_msg !=='undefined' && error_msg ? error_msg : null, error: typeof error !=='undefined'
|
||||
&& error ? error : null }) %>
|
||||
</div>
|
||||
<% } %> <%- body %>
|
||||
</div>
|
||||
<% } %> <%- body %>
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
@@ -841,14 +821,14 @@
|
||||
<h5 class="mb-3">Links</h5>
|
||||
<ul class="list-unstyled">
|
||||
<% if (locals.user) { %>
|
||||
<li class="mb-2">
|
||||
<a href="/admin/dashboard" class="text-decoration-none hover-opacity">Dashboard</a>
|
||||
</li>
|
||||
<% } else { %>
|
||||
<li class="mb-2">
|
||||
<a href="/auth/login" class="text-decoration-none hover-opacity">Login</a>
|
||||
</li>
|
||||
<% } %>
|
||||
<li class="mb-2">
|
||||
<a href="/admin/dashboard" class="text-decoration-none hover-opacity">Dashboard</a>
|
||||
</li>
|
||||
<% } else { %>
|
||||
<li class="mb-2">
|
||||
<a href="/auth/login" class="text-decoration-none hover-opacity">Login</a>
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
@@ -865,7 +845,7 @@
|
||||
<div class="text-center">
|
||||
<p class="mb-0">
|
||||
© <%= new Date().getFullYear() %> CMS-GGCamp. All rights
|
||||
reserved.
|
||||
reserved.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -928,7 +908,7 @@
|
||||
formattedType + '</span><i class="fas fa-chevron-right nav-submenu-indicator"></i>';
|
||||
|
||||
if (window.location.pathname.includes("/admin/level") && window.location.search.includes(
|
||||
"type=" + type)) {
|
||||
"type=" + type)) {
|
||||
link.classList.add("active");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user