forked from UKSOURCE/cms.lams
618 lines
35 KiB
Plaintext
618 lines
35 KiB
Plaintext
<div class="container">
|
|
<div class="d-flex justify-content-between align-items-center mt-4 mb-4 flex-wrap gap-2">
|
|
<div>
|
|
<h1 class="h3 mb-0" style="color: var(--primary-dark);"><%= title %></h1>
|
|
<p class="text-muted mb-0">Edit content displayed on Contact Us page</p>
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<% if (frontendUrl) { %>
|
|
<a href="<%= frontendUrl %>/contact" class="btn btn-outline-primary" target="_blank" rel="noopener">
|
|
<i class="fas fa-external-link-alt me-2"></i>View Contact Page
|
|
</a>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="POST" class="content-with-fixed-buttons" id="contactForm" action="/admin/contact/update">
|
|
<input type="hidden" name="metadata" id="metadataJson">
|
|
<input type="hidden" name="hero" id="heroJson">
|
|
<input type="hidden" name="infoCards" id="infoCardsJson">
|
|
<input type="hidden" name="form" id="formJson">
|
|
<input type="hidden" name="faq" id="faqJson">
|
|
<input type="hidden" name="cta" id="ctaJson">
|
|
|
|
<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="#tab-meta" role="tab"><i class="fas fa-tags me-2"></i>Metadata</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#tab-hero" role="tab"><i class="fas fa-star me-2"></i>Hero</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#tab-info" role="tab"><i class="fas fa-address-card me-2"></i>Info Cards</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#tab-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="#tab-faq" role="tab"><i class="fas fa-question-circle me-2"></i>FAQ</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#tab-cta" role="tab"><i class="fas fa-bullhorn me-2"></i>CTA</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#tab-submissions" role="tab"><i class="fas fa-list me-2"></i>Submissions</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="tab-content">
|
|
|
|
<%# ── TAB: Metadata ── %>
|
|
<div class="tab-pane fade show active" id="tab-meta" role="tabpanel">
|
|
<div class="row g-3">
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Title (SEO)</label>
|
|
<input type="text" class="form-control" id="metaTitle" value="<%= data.metadata?.title || '' %>">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Description (SEO)</label>
|
|
<textarea class="form-control" id="metaDescription" rows="2"><%= data.metadata?.description || '' %></textarea>
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Keywords</label>
|
|
<input type="text" class="form-control" id="metaKeywords" value="<%= data.metadata?.keywords || '' %>">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">OG Image</label>
|
|
<input type="text" class="form-control" id="metaOgImage" value="<%= data.metadata?.ogImage || '' %>">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<%# ── TAB: Hero ── %>
|
|
<div class="tab-pane fade" id="tab-hero" role="tabpanel">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Badge</label>
|
|
<input type="text" class="form-control" id="heroBadge" value="<%= data.hero?.badge || '' %>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Title — Highlight word</label>
|
|
<input type="text" class="form-control" id="heroTitleHighlight" value="<%= data.hero?.titleHighlight || '' %>">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Title — Main text (before highlight)</label>
|
|
<input type="text" class="form-control" id="heroTitleMain" value="<%= data.hero?.titleMain || '' %>">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Description</label>
|
|
<textarea class="form-control" id="heroDescription" rows="3"><%= data.hero?.description || '' %></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<%# ── TAB: Info Cards ── %>
|
|
<div class="tab-pane fade" id="tab-info" role="tabpanel">
|
|
<h6 class="fw-semibold mb-3">Contact Information</h6>
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-12">
|
|
<label class="form-label small fw-medium">Section title</label>
|
|
<input type="text" class="form-control" id="contactInfoTitle" value="<%= data.infoCards?.contactInfo?.title || '' %>">
|
|
</div>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<span class="small fw-medium">Channels</span>
|
|
<button type="button" class="btn btn-sm btn-primary" id="addChannelBtn"><i class="fas fa-plus me-1"></i>Add channel</button>
|
|
</div>
|
|
<div id="channelsContainer" class="vstack gap-2 mb-4">
|
|
<% (data.infoCards?.contactInfo?.channels || []).forEach(function(ch) { %>
|
|
<div class="card channel-row border">
|
|
<div class="card-body">
|
|
<div class="row g-2 align-items-end">
|
|
<div class="col-md-3">
|
|
<label class="form-label small">Icon</label>
|
|
<div class="input-group">
|
|
<span class="input-group-text icon-preview-cell" style="min-width:38px">
|
|
<% if (ch.icon) { %><i class="<%= ch.icon %>"></i><% } %>
|
|
</span>
|
|
<input type="text" class="form-control ch-icon"
|
|
value="<%= ch.icon || '' %>" placeholder="Click to pick..." readonly
|
|
style="cursor:pointer;background:#fff" onclick="IconPicker.open(this)">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label small">Title</label>
|
|
<input type="text" class="form-control ch-title" value="<%= ch.title || '' %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small">Detail</label>
|
|
<input type="text" class="form-control ch-detail" value="<%= ch.detail || '' %>">
|
|
</div>
|
|
<div class="col-md-2 d-flex align-items-end">
|
|
<button type="button" class="btn btn-outline-danger btn-sm w-100 remove-channel"><i class="fas fa-trash"></i></button>
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label small">Sub Detail (optional)</label>
|
|
<input type="text" class="form-control ch-subdetail" value="<%= ch.subDetail || '' %>">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
</div>
|
|
|
|
<hr class="my-4">
|
|
<h6 class="fw-semibold mb-3">Support Hours</h6>
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-12">
|
|
<label class="form-label small fw-medium">Section title</label>
|
|
<input type="text" class="form-control" id="supportHoursTitle" value="<%= data.infoCards?.supportHours?.title || '' %>">
|
|
</div>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<span class="small fw-medium">Hours</span>
|
|
<button type="button" class="btn btn-sm btn-secondary" id="addHourBtn"><i class="fas fa-plus me-1"></i>Add row</button>
|
|
</div>
|
|
<div id="hoursContainer" class="vstack gap-2 mb-4">
|
|
<% (data.infoCards?.supportHours?.hours || []).forEach(function(h) { %>
|
|
<div class="input-group hour-row">
|
|
<input type="text" class="form-control hr-day" placeholder="Day" value="<%= h.day || '' %>">
|
|
<input type="text" class="form-control hr-time" placeholder="Time" value="<%= h.time || '' %>">
|
|
<button type="button" class="btn btn-outline-danger remove-hour"><i class="fas fa-times"></i></button>
|
|
</div>
|
|
<% }); %>
|
|
</div>
|
|
<div class="row g-2">
|
|
<div class="col-md-4">
|
|
<label class="form-label small">Footer text</label>
|
|
<input type="text" class="form-control" id="footerText" value="<%= data.infoCards?.supportHours?.footer?.text || '' %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small">Footer link text</label>
|
|
<input type="text" class="form-control" id="footerLinkText" value="<%= data.infoCards?.supportHours?.footer?.linkText || '' %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small">Footer link href</label>
|
|
<input type="text" class="form-control" id="footerLinkHref" value="<%= data.infoCards?.supportHours?.footer?.linkHref || '' %>">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<%# ── TAB: Form ── %>
|
|
<div class="tab-pane fade" id="tab-form" role="tabpanel">
|
|
<% const fm = data.form || {}; const fld = fm.fields || {}; %>
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Heading</label>
|
|
<input type="text" class="form-control" id="formHeading" value="<%= fm.heading || '' %>">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Description</label>
|
|
<textarea class="form-control" id="formDescription" rows="2"><%= fm.description || '' %></textarea>
|
|
</div>
|
|
</div>
|
|
<h6 class="text-uppercase text-muted small mb-3">Fields</h6>
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-6"><label class="form-label small">First Name — label</label><input type="text" class="form-control" id="fld_fn_label" value="<%= fld.firstName?.label || '' %>"></div>
|
|
<div class="col-md-6"><label class="form-label small">First Name — placeholder</label><input type="text" class="form-control" id="fld_fn_ph" value="<%= fld.firstName?.placeholder || '' %>"></div>
|
|
<div class="col-md-6"><label class="form-label small">Last Name — label</label><input type="text" class="form-control" id="fld_ln_label" value="<%= fld.lastName?.label || '' %>"></div>
|
|
<div class="col-md-6"><label class="form-label small">Last Name — placeholder</label><input type="text" class="form-control" id="fld_ln_ph" value="<%= fld.lastName?.placeholder || '' %>"></div>
|
|
<div class="col-md-6"><label class="form-label small">Email — label</label><input type="text" class="form-control" id="fld_em_label" value="<%= fld.email?.label || '' %>"></div>
|
|
<div class="col-md-6"><label class="form-label small">Email — placeholder</label><input type="text" class="form-control" id="fld_em_ph" value="<%= fld.email?.placeholder || '' %>"></div>
|
|
<div class="col-md-6"><label class="form-label small">Phone — label</label><input type="text" class="form-control" id="fld_ph_label" value="<%= fld.phone?.label || '' %>"></div>
|
|
<div class="col-md-6"><label class="form-label small">Phone — placeholder</label><input type="text" class="form-control" id="fld_ph_ph" value="<%= fld.phone?.placeholder || '' %>"></div>
|
|
<div class="col-md-6"><label class="form-label small">Message — label</label><input type="text" class="form-control" id="fld_msg_label" value="<%= fld.message?.label || '' %>"></div>
|
|
<div class="col-md-6"><label class="form-label small">Message — placeholder</label><input type="text" class="form-control" id="fld_msg_ph" value="<%= fld.message?.placeholder || '' %>"></div>
|
|
</div>
|
|
<h6 class="text-uppercase text-muted small mb-3">Inquiry Type (dropdown)</h6>
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-6"><label class="form-label small">Label</label><input type="text" class="form-control" id="fld_iq_label" value="<%= fld.inquiryType?.label || '' %>"></div>
|
|
<div class="col-md-6"><label class="form-label small">Placeholder</label><input type="text" class="form-control" id="fld_iq_ph" value="<%= fld.inquiryType?.placeholder || '' %>"></div>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<span class="small fw-medium">Inquiry options</span>
|
|
<button type="button" class="btn btn-sm btn-secondary" id="addIqBtn"><i class="fas fa-plus me-1"></i>Add option</button>
|
|
</div>
|
|
<div id="iqOptionsContainer" class="vstack gap-2 mb-4">
|
|
<% (fld.inquiryType?.options || []).forEach(function(opt) { %>
|
|
<div class="input-group iq-opt-row">
|
|
<input type="text" class="form-control iq-label" placeholder="Label" value="<%= opt.label || '' %>">
|
|
<input type="text" class="form-control iq-value" placeholder="Value" value="<%= opt.value || '' %>">
|
|
<button type="button" class="btn btn-outline-danger remove-iq-opt"><i class="fas fa-times"></i></button>
|
|
</div>
|
|
<% }); %>
|
|
</div>
|
|
<div class="row g-3">
|
|
<div class="col-md-4"><label class="form-label small">Submit label</label><input type="text" class="form-control" id="formSubmitLabel" value="<%= fm.submitLabel || '' %>"></div>
|
|
<div class="col-md-4"><label class="form-label small">Success message</label><input type="text" class="form-control" id="formSuccessMsg" value="<%= fm.successMessage || '' %>"></div>
|
|
<div class="col-md-4"><label class="form-label small">Error message</label><input type="text" class="form-control" id="formErrorMsg" value="<%= fm.errorMessage || '' %>"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<%# ── TAB: FAQ ── %>
|
|
<div class="tab-pane fade" id="tab-faq" role="tabpanel">
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-12"><label class="form-label fw-medium">Title</label><input type="text" class="form-control" id="faqTitle" value="<%= data.faq?.title || '' %>"></div>
|
|
<div class="col-md-12"><label class="form-label fw-medium">Subtitle</label><textarea class="form-control" id="faqSubtitle" rows="2"><%= data.faq?.subtitle || '' %></textarea></div>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<span class="small fw-medium">FAQ items</span>
|
|
<button type="button" class="btn btn-sm btn-primary" id="addFaqBtn"><i class="fas fa-plus me-1"></i>Add item</button>
|
|
</div>
|
|
<div id="faqContainer" class="vstack gap-3">
|
|
<% (data.faq?.items || []).forEach(function(item) { %>
|
|
<div class="card faq-row border">
|
|
<div class="card-body">
|
|
<div class="row g-2">
|
|
<div class="col-md-11">
|
|
<label class="form-label small">Question</label>
|
|
<input type="text" class="form-control faq-question" value="<%= item.question || '' %>">
|
|
</div>
|
|
<div class="col-md-1 d-flex align-items-end">
|
|
<button type="button" class="btn btn-outline-danger btn-sm w-100 remove-faq"><i class="fas fa-trash"></i></button>
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label small">Answer</label>
|
|
<textarea class="form-control faq-answer" rows="2"><%= item.answer || '' %></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
</div>
|
|
</div>
|
|
|
|
<%# ── TAB: CTA ── %>
|
|
<div class="tab-pane fade" id="tab-cta" role="tabpanel">
|
|
<div class="row g-3">
|
|
<div class="col-md-12"><label class="form-label fw-medium">Title</label><input type="text" class="form-control" id="ctaTitle" value="<%= data.cta?.title || '' %>"></div>
|
|
<div class="col-md-12"><label class="form-label fw-medium">Description</label><textarea class="form-control" id="ctaDescription" rows="2"><%= data.cta?.description || '' %></textarea></div>
|
|
<div class="col-md-3"><label class="form-label small">Primary label</label><input type="text" class="form-control" id="ctaPrimaryLabel" value="<%= data.cta?.primaryButton?.label || '' %>"></div>
|
|
<div class="col-md-3"><label class="form-label small">Primary href</label><input type="text" class="form-control" id="ctaPrimaryHref" value="<%= data.cta?.primaryButton?.href || '' %>"></div>
|
|
<div class="col-md-3"><label class="form-label small">Secondary label</label><input type="text" class="form-control" id="ctaSecondaryLabel" value="<%= data.cta?.secondaryButton?.label || '' %>"></div>
|
|
<div class="col-md-3"><label class="form-label small">Secondary href</label><input type="text" class="form-control" id="ctaSecondaryHref" value="<%= data.cta?.secondaryButton?.href || '' %>"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<%# ── TAB: Submissions ── %>
|
|
<div class="tab-pane fade" id="tab-submissions" role="tabpanel">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h6 class="fw-medium mb-0">Recent Submissions</h6>
|
|
</div>
|
|
<div class="row g-2 mb-4 align-items-end">
|
|
<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-2">
|
|
<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/contact?tab=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>Email</th><th>Phone</th><th>Message</th><th>Status</th><th>Action</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<% if (locals.submissions && submissions.length > 0) { %>
|
|
<% submissions.forEach(function(s) { %>
|
|
<tr>
|
|
<td><%= new Date(s.createdAt).toLocaleDateString() %> <%= new Date(s.createdAt).toLocaleTimeString([], {hour:'2-digit',minute:'2-digit'}) %></td>
|
|
<td><%= s.name %></td>
|
|
<td><a href="mailto:<%= s.email %>"><%= s.email %></a></td>
|
|
<td><%= s.phone || '-' %></td>
|
|
<td><div style="max-width:300px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="<%= s.message %>"><%= s.message %></div></td>
|
|
<td>
|
|
<% let cls='bg-secondary'; if(s.status==='pending') cls='bg-warning text-dark'; if(s.status==='read') cls='bg-info text-dark'; if(s.status==='replied') cls='bg-success'; %>
|
|
<span class="badge <%= cls %> rounded-pill"><%= s.status %></span>
|
|
</td>
|
|
<td>
|
|
<button type="button" class="btn btn-sm btn-outline-primary" onclick="openStatusModal('<%= s._id %>', '<%= s.status %>')" title="Update Status"><i class="fas fa-edit"></i></button>
|
|
</td>
|
|
</tr>
|
|
<% }); %>
|
|
<% } else { %>
|
|
<tr><td colspan="7" 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 class="fixed-bottom-buttons">
|
|
<button type="button" class="btn btn-secondary" onclick="resetContactForm()"><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>
|
|
|
|
<!-- Status 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"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" id="statusSubmissionId">
|
|
<label class="form-label">Status</label>
|
|
<select class="form-select" id="statusSelect">
|
|
<option value="pending">Pending</option>
|
|
<option value="read">Read</option>
|
|
<option value="replied">Replied</option>
|
|
<option value="archived">Archived</option>
|
|
</select>
|
|
</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>
|
|
let statusModal = null;
|
|
|
|
// window.CONTACT_ICON_OPTIONS = <%- JSON.stringify(iconOptions) %>;
|
|
|
|
function resetContactForm() {
|
|
if (confirm('Reset all unsaved changes?')) location.reload();
|
|
}
|
|
|
|
function applyDateFilter() {
|
|
const start = document.getElementById('filterStartDate').value;
|
|
const end = document.getElementById('filterEndDate').value;
|
|
const url = new URL(window.location.href);
|
|
url.searchParams.set('tab', 'tab-submissions');
|
|
if (start) url.searchParams.set('startDate', start); else url.searchParams.delete('startDate');
|
|
if (end) url.searchParams.set('endDate', end); else url.searchParams.delete('endDate');
|
|
window.location.href = url.toString();
|
|
}
|
|
|
|
function openStatusModal(id, status) {
|
|
document.getElementById('statusSubmissionId').value = id;
|
|
document.getElementById('statusSelect').value = status;
|
|
statusModal.show();
|
|
}
|
|
|
|
async function saveStatus() {
|
|
const id = document.getElementById('statusSubmissionId').value;
|
|
const status = document.getElementById('statusSelect').value;
|
|
try {
|
|
const res = await fetch('/api/contact/submissions/' + id + '/status', {
|
|
method: 'PATCH',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ status }),
|
|
});
|
|
if (res.ok) { statusModal.hide(); location.reload(); }
|
|
else alert('Failed to update status.');
|
|
} catch (e) { alert('Error: ' + e.message); }
|
|
}
|
|
|
|
// ── Dynamic rows ──────────────────────────────
|
|
|
|
// function iconSelectHtml(selected) {
|
|
// var opts = window.CONTACT_ICON_OPTIONS || [];
|
|
// var html = '';
|
|
// for (var i = 0; i < opts.length; i++) {
|
|
// var o = opts[i];
|
|
// html += '<option value="' + escHtml(o.value) + '"' + (o.value === selected ? ' selected' : '') + '>' + escHtml(o.label) + '</option>';
|
|
// }
|
|
// return html;
|
|
// }
|
|
|
|
function buildChannelRow(ch) {
|
|
ch = ch || {};
|
|
return '<div class="card channel-row border"><div class="card-body"><div class="row g-2 align-items-end">' +
|
|
'<div class="col-md-3"><label class="form-label small">Icon</label>' + iconPickerInput('ch-icon', ch.icon) + '</div>' +
|
|
'<div class="col-md-3"><label class="form-label small">Title</label><input type="text" class="form-control ch-title" value="' + escHtml(ch.title) + '"></div>' +
|
|
'<div class="col-md-4"><label class="form-label small">Detail</label><input type="text" class="form-control ch-detail" value="' + escHtml(ch.detail) + '"></div>' +
|
|
'<div class="col-md-2 d-flex align-items-end"><button type="button" class="btn btn-outline-danger btn-sm w-100 remove-channel"><i class="fas fa-trash"></i></button></div>' +
|
|
'<div class="col-md-12"><label class="form-label small">Sub Detail (optional)</label><input type="text" class="form-control ch-subdetail" value="' + escHtml(ch.subDetail) + '"></div>' +
|
|
'</div></div></div>';
|
|
}
|
|
|
|
function buildHourRow(h) {
|
|
h = h || {};
|
|
return '<div class="input-group hour-row">' +
|
|
'<input type="text" class="form-control hr-day" placeholder="Day" value="' + escHtml(h.day) + '">' +
|
|
'<input type="text" class="form-control hr-time" placeholder="Time" value="' + escHtml(h.time) + '">' +
|
|
'<button type="button" class="btn btn-outline-danger remove-hour"><i class="fas fa-times"></i></button></div>';
|
|
}
|
|
|
|
function buildIqOptRow(opt) {
|
|
opt = opt || {};
|
|
return '<div class="input-group iq-opt-row">' +
|
|
'<input type="text" class="form-control iq-label" placeholder="Label" value="' + escHtml(opt.label) + '">' +
|
|
'<input type="text" class="form-control iq-value" placeholder="Value" value="' + escHtml(opt.value) + '">' +
|
|
'<button type="button" class="btn btn-outline-danger remove-iq-opt"><i class="fas fa-times"></i></button></div>';
|
|
}
|
|
|
|
function buildFaqRow(item) {
|
|
item = item || {};
|
|
return '<div class="card faq-row border"><div class="card-body"><div class="row g-2">' +
|
|
'<div class="col-md-11"><label class="form-label small">Question</label><input type="text" class="form-control faq-question" value="' + escHtml(item.question) + '"></div>' +
|
|
'<div class="col-md-1 d-flex align-items-end"><button type="button" class="btn btn-outline-danger btn-sm w-100 remove-faq"><i class="fas fa-trash"></i></button></div>' +
|
|
'<div class="col-md-12"><label class="form-label small">Answer</label><textarea class="form-control faq-answer" rows="2">' + escHtml(item.answer) + '</textarea></div>' +
|
|
'</div></div></div>';
|
|
}
|
|
|
|
function escHtml(s) {
|
|
return String(s || '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
|
}
|
|
|
|
function syncHiddenJson() {
|
|
// metadata
|
|
document.getElementById('metadataJson').value = JSON.stringify({
|
|
title: document.getElementById('metaTitle').value.trim(),
|
|
description: document.getElementById('metaDescription').value.trim(),
|
|
keywords: document.getElementById('metaKeywords').value.trim(),
|
|
ogImage: document.getElementById('metaOgImage').value.trim(),
|
|
});
|
|
|
|
// hero
|
|
document.getElementById('heroJson').value = JSON.stringify({
|
|
badge: document.getElementById('heroBadge').value.trim(),
|
|
titleMain: document.getElementById('heroTitleMain').value.trim(),
|
|
titleHighlight: document.getElementById('heroTitleHighlight').value.trim(),
|
|
description: document.getElementById('heroDescription').value.trim(),
|
|
});
|
|
|
|
// infoCards — channels
|
|
const channels = [];
|
|
document.querySelectorAll('#channelsContainer .channel-row').forEach(function(row) {
|
|
channels.push({
|
|
icon: row.querySelector('.ch-icon').value.trim(),
|
|
title: row.querySelector('.ch-title').value.trim(),
|
|
detail: row.querySelector('.ch-detail').value.trim(),
|
|
subDetail: row.querySelector('.ch-subdetail').value.trim(),
|
|
});
|
|
});
|
|
// infoCards — hours
|
|
const hours = [];
|
|
document.querySelectorAll('#hoursContainer .hour-row').forEach(function(row) {
|
|
hours.push({
|
|
day: row.querySelector('.hr-day').value.trim(),
|
|
time: row.querySelector('.hr-time').value.trim(),
|
|
});
|
|
});
|
|
document.getElementById('infoCardsJson').value = JSON.stringify({
|
|
contactInfo: {
|
|
title: document.getElementById('contactInfoTitle').value.trim(),
|
|
channels: channels,
|
|
},
|
|
supportHours: {
|
|
title: document.getElementById('supportHoursTitle').value.trim(),
|
|
hours: hours,
|
|
footer: {
|
|
text: document.getElementById('footerText').value.trim(),
|
|
linkText: document.getElementById('footerLinkText').value.trim(),
|
|
linkHref: document.getElementById('footerLinkHref').value.trim(),
|
|
},
|
|
},
|
|
});
|
|
|
|
// inquiry options
|
|
const iqOptions = [];
|
|
document.querySelectorAll('#iqOptionsContainer .iq-opt-row').forEach(function(row) {
|
|
var l = row.querySelector('.iq-label').value.trim();
|
|
var v = row.querySelector('.iq-value').value.trim();
|
|
if (l || v) iqOptions.push({ label: l, value: v });
|
|
});
|
|
|
|
// form
|
|
document.getElementById('formJson').value = JSON.stringify({
|
|
heading: document.getElementById('formHeading').value.trim(),
|
|
description: document.getElementById('formDescription').value.trim(),
|
|
fields: {
|
|
firstName: { name: 'first_name', label: document.getElementById('fld_fn_label').value.trim(), placeholder: document.getElementById('fld_fn_ph').value.trim(), required: true },
|
|
lastName: { name: 'last_name', label: document.getElementById('fld_ln_label').value.trim(), placeholder: document.getElementById('fld_ln_ph').value.trim(), required: true },
|
|
email: { name: 'email', label: document.getElementById('fld_em_label').value.trim(), placeholder: document.getElementById('fld_em_ph').value.trim(), required: true },
|
|
phone: { name: 'phone_number', label: document.getElementById('fld_ph_label').value.trim(), placeholder: document.getElementById('fld_ph_ph').value.trim(), required: false },
|
|
inquiryType: { name: 'inquiry_type', label: document.getElementById('fld_iq_label').value.trim(), placeholder: document.getElementById('fld_iq_ph').value.trim(), required: true, options: iqOptions },
|
|
message: { name: 'message_content', label: document.getElementById('fld_msg_label').value.trim(), placeholder: document.getElementById('fld_msg_ph').value.trim(), required: true },
|
|
},
|
|
submitLabel: document.getElementById('formSubmitLabel').value.trim(),
|
|
successMessage: document.getElementById('formSuccessMsg').value.trim(),
|
|
errorMessage: document.getElementById('formErrorMsg').value.trim(),
|
|
});
|
|
|
|
// faq
|
|
const faqItems = [];
|
|
document.querySelectorAll('#faqContainer .faq-row').forEach(function(row) {
|
|
faqItems.push({
|
|
question: row.querySelector('.faq-question').value.trim(),
|
|
answer: row.querySelector('.faq-answer').value.trim(),
|
|
});
|
|
});
|
|
document.getElementById('faqJson').value = JSON.stringify({
|
|
title: document.getElementById('faqTitle').value.trim(),
|
|
subtitle: document.getElementById('faqSubtitle').value.trim(),
|
|
items: faqItems,
|
|
});
|
|
|
|
// cta
|
|
document.getElementById('ctaJson').value = JSON.stringify({
|
|
title: document.getElementById('ctaTitle').value.trim(),
|
|
description: document.getElementById('ctaDescription').value.trim(),
|
|
primaryButton: { label: document.getElementById('ctaPrimaryLabel').value.trim(), href: document.getElementById('ctaPrimaryHref').value.trim() },
|
|
secondaryButton: { label: document.getElementById('ctaSecondaryLabel').value.trim(), href: document.getElementById('ctaSecondaryHref').value.trim() },
|
|
});
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const form = document.getElementById('contactForm');
|
|
form.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const btn = document.getElementById('submitBtn');
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Saving...';
|
|
try {
|
|
syncHiddenJson();
|
|
form.submit();
|
|
} catch (err) {
|
|
console.error(err);
|
|
alert('Could not prepare form data. Please try again.');
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="fas fa-save me-2"></i>Save Changes';
|
|
}
|
|
});
|
|
|
|
document.getElementById('addChannelBtn').addEventListener('click', function() {
|
|
document.getElementById('channelsContainer').insertAdjacentHTML('beforeend', buildChannelRow());
|
|
});
|
|
document.getElementById('channelsContainer').addEventListener('click', function(e) {
|
|
if (e.target.closest('.remove-channel')) e.target.closest('.channel-row').remove();
|
|
});
|
|
|
|
document.getElementById('addHourBtn').addEventListener('click', function() {
|
|
document.getElementById('hoursContainer').insertAdjacentHTML('beforeend', buildHourRow());
|
|
});
|
|
document.getElementById('hoursContainer').addEventListener('click', function(e) {
|
|
if (e.target.closest('.remove-hour')) e.target.closest('.hour-row').remove();
|
|
});
|
|
|
|
document.getElementById('addIqBtn').addEventListener('click', function() {
|
|
document.getElementById('iqOptionsContainer').insertAdjacentHTML('beforeend', buildIqOptRow());
|
|
});
|
|
document.getElementById('iqOptionsContainer').addEventListener('click', function(e) {
|
|
if (e.target.closest('.remove-iq-opt')) e.target.closest('.iq-opt-row').remove();
|
|
});
|
|
|
|
document.getElementById('addFaqBtn').addEventListener('click', function() {
|
|
document.getElementById('faqContainer').insertAdjacentHTML('beforeend', buildFaqRow());
|
|
});
|
|
document.getElementById('faqContainer').addEventListener('click', function(e) {
|
|
if (e.target.closest('.remove-faq')) e.target.closest('.faq-row').remove();
|
|
});
|
|
|
|
// Modal setup
|
|
const statusModalEl = document.getElementById('statusModal');
|
|
document.body.appendChild(statusModalEl);
|
|
statusModal = new bootstrap.Modal(statusModalEl);
|
|
|
|
// Tab from URL
|
|
const tab = new URLSearchParams(window.location.search).get('tab');
|
|
if (tab) {
|
|
const el = document.querySelector('a[href="#' + tab + '"]');
|
|
if (el) new bootstrap.Tab(el).show();
|
|
}
|
|
});
|
|
</script> |