forked from UKSOURCE/cms.hailearning.edu.vn
760 lines
33 KiB
Plaintext
760 lines
33 KiB
Plaintext
<div class="container">
|
|
<div class="d-flex justify-content-between align-items-center mt-4 mb-4">
|
|
<div>
|
|
<h1 class="h3 mb-0" style="color: var(--primary-dark)">
|
|
Footer Management
|
|
</h1>
|
|
<p class="text-muted mb-0">Edit footer content and structure</p>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<form action="/admin/footer/update" method="POST" class="content-with-fixed-buttons" id="footerForm">
|
|
<!-- Hidden consolidated JSON for footer (used for single-payload submit) -->
|
|
<input type="hidden" name="footerJson" id="footerJson">
|
|
<input type="hidden" name="activeTab" id="activeTabInput" value="about">
|
|
<!-- 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="#about" role="tab">
|
|
<i class="fas fa-info-circle me-2"></i>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#logo" role="tab">
|
|
<i class="fas fa-image me-2"></i>Logo
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#contact" role="tab">
|
|
<i class="fas fa-address-book me-2"></i>Contact & Address
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#columns" role="tab">
|
|
<i class="fas fa-columns me-2"></i>Footer Columns
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#social" role="tab">
|
|
<i class="fas fa-share-alt me-2"></i>Social Links
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#copyright" role="tab">
|
|
<i class="fas fa-copyright me-2"></i>Copyright
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
<div class="tab-content">
|
|
<!-- About GGC Tab -->
|
|
<div class="tab-pane fade show active" id="about" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-header bg-light">
|
|
<h6 class="mb-0">About GGC Section</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-12">
|
|
<label class="form-label fw-medium">Title</label>
|
|
<input type="text" class="form-control" name="about[title]"
|
|
value="<%= (data.about && data.about.title) ? data.about.title : 'About GGC' %>" />
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-12">
|
|
<label class="form-label fw-medium">Description</label>
|
|
<textarea class="form-control" name="about[description]"
|
|
rows="4"><%= (data.about && data.about.description) ? data.about.description : '' %></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Map Link Text</label>
|
|
<input type="text" class="form-control" name="about[mapLink][text]"
|
|
value="<%= (data.about && data.about.mapLink && data.about.mapLink.text) ? data.about.mapLink.text : 'Check on google map' %>" />
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Map Link URL</label>
|
|
<input type="url" class="form-control" name="about[mapLink][url]"
|
|
value="<%= (data.about && data.about.mapLink && data.about.mapLink.url) ? data.about.mapLink.url : '' %>" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Logo Tab -->
|
|
<div class="tab-pane fade" id="logo" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-header bg-light">
|
|
<h6 class="mb-0">Logo Configuration</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Logo Image URL</label>
|
|
<div class="input-group mb-2">
|
|
<input type="text" class="form-control" id="logoImage" name="logo[src]"
|
|
value="<%= (data.logo && data.logo.src) ? data.logo.src : '' %>" />
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image"
|
|
data-target-input="logoImage" data-image-type="layout">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
<small class="form-text text-muted">Recommended size: 200x60px</small>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Logo Alt Text</label>
|
|
<input type="text" class="form-control" id="logoAlt" name="logo[alt]"
|
|
value="<%= (data.logo && data.logo.alt) ? data.logo.alt : '' %>" />
|
|
</div>
|
|
</div>
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<% if (data.logo && data.logo.src) { %>
|
|
<img src="<%= data.logo.src %>" class="img-thumbnail" style="
|
|
max-height: 100px;
|
|
max-width: 300px;
|
|
object-fit: contain;
|
|
background: var(--primary-color);
|
|
" alt="Logo preview" />
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Contact & Address Tab -->
|
|
<div class="tab-pane fade" id="contact" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-header bg-light">
|
|
<h6 class="mb-0">Contact & Address Information</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Address -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<h6 class="fw-medium mb-3">Address</h6>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Address Text</label>
|
|
<input type="text" class="form-control" name="address[text]" value="<%= data.address.text %>" />
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Address Line 2</label>
|
|
<input type="text" class="form-control" name="address[address2]"
|
|
value="<%= (data.address && data.address.address2) ? data.address.address2 : '' %>" />
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Map URL</label>
|
|
<input type="url" class="form-control" name="address[mapUrl]"
|
|
value="<%= data.address.mapUrl %>" />
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Map/Link 2 (optional)</label>
|
|
<input type="url" class="form-control" name="address[link2]"
|
|
value="<%= (data.address && data.address.link2) ? data.address.link2 : '' %>" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Contact Info -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<h6 class="fw-medium mb-3">Contact Information</h6>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label fw-medium">Phone Number</label>
|
|
<input type="text" class="form-control" name="contact[phone]"
|
|
value="<%= data.contact.phone %>" />
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label fw-medium">Working Hours</label>
|
|
<input type="text" class="form-control" name="contact[hours]"
|
|
value="<%= data.contact.hours %>" />
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label fw-medium">Email</label>
|
|
<input type="email" class="form-control" name="contact[email]"
|
|
value="<%= data.contact.email %>" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Footer Columns Tab -->
|
|
<div class="tab-pane fade" id="columns" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-header bg-light d-flex justify-content-between align-items-center">
|
|
<h6 class="mb-0">Footer Columns</h6>
|
|
<button type="button" class="btn btn-outline-primary btn-sm" id="addColumn">
|
|
<i class="fas fa-plus me-1"></i>Add Column
|
|
</button>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="columnsContainer">
|
|
<% data.columns.forEach((column, columnIndex)=> { %>
|
|
<div class="card mb-3 border" data-column-index="<%= columnIndex %>">
|
|
<div class="card-header bg-light">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h6 class="mb-0">Column <%= columnIndex + 1 %>
|
|
</h6>
|
|
<button type="button" class="btn btn-outline-danger btn-sm remove-column"
|
|
data-column-index="<%= columnIndex %>">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Column Title</label>
|
|
<input type="text" class="form-control" name="columns[<%= columnIndex %>][title]"
|
|
value="<%= column.title %>" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<h6 class="fw-medium mb-3">Links</h6>
|
|
<div class="column-links-container" data-column-index="<%= columnIndex %>">
|
|
<% column.links.forEach((link, linkIndex)=> {
|
|
%>
|
|
<div class="card mb-2 border" data-link-index="<%= linkIndex %>">
|
|
<div class="card-body">
|
|
<div class="row g-2">
|
|
<div class="col-md-5">
|
|
<label class="form-label form-label-sm">Link Title</label>
|
|
<input type="text" class="form-control form-control-sm"
|
|
name="columns[<%= columnIndex %>][links][<%= linkIndex %>][title]"
|
|
value="<%= link.title %>" />
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label form-label-sm">URL</label>
|
|
<input type="text" class="form-control form-control-sm"
|
|
name="columns[<%= columnIndex %>][links][<%= linkIndex %>][url]"
|
|
value="<%= link.url %>" placeholder="/about-us/" />
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label form-label-sm"> </label>
|
|
<button type="button"
|
|
class="btn btn-outline-danger btn-sm w-100 remove-link"
|
|
data-column-index="<%= columnIndex %>" data-link-index="<%= linkIndex %>">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-primary btn-sm add-link"
|
|
data-column-index="<%= columnIndex %>">
|
|
<i class="fas fa-plus me-1"></i>Add Link
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Social Links Tab -->
|
|
<div class="tab-pane fade" id="social" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-header bg-light d-flex justify-content-between align-items-center">
|
|
<h6 class="mb-0">Social Media Links</h6>
|
|
<button type="button" class="btn btn-outline-primary btn-sm" id="addSocialLink">
|
|
<i class="fas fa-plus me-1"></i>Add Social Link
|
|
</button>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="socialLinksContainer">
|
|
<% data.social.links.forEach((link, index)=> { %>
|
|
<div class="card mb-3 border" data-social-index="<%= index %>">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label fw-medium">Platform</label>
|
|
<input type="text" class="form-control" name="social[links][<%= index %>][platform]"
|
|
value="<%= link.platform %>" />
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label fw-medium">URL</label>
|
|
<input type="text" class="form-control" name="social[links][<%= index %>][url]"
|
|
value="<%= link.url %>" placeholder="https://facebook.com/..." />
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label fw-medium">Icon Class</label>
|
|
<input type="text" class="form-control" name="social[links][<%= index %>][icon]"
|
|
value="<%= link.icon %>" />
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label"> </label>
|
|
<button type="button" class="btn btn-outline-danger btn-sm w-100 remove-social-link"
|
|
data-social-index="<%= index %>">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Copyright Tab -->
|
|
<div class="tab-pane fade" id="copyright" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-header bg-light">
|
|
<h6 class="mb-0">Copyright Information</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<label class="form-label fw-medium">Copyright Text</label>
|
|
<textarea class="form-control" name="copyright[text]" rows="3">
|
|
<%= data.copyright.text %></textarea>
|
|
<small class="form-text text-muted">You can use HTML tags for formatting</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Move buttons to fixed bottom -->
|
|
<div class="fixed-bottom-buttons">
|
|
<button type="reset" class="btn btn-secondary">
|
|
<i class="fas fa-undo"></i>
|
|
<span>Reset</span>
|
|
</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-save"></i>
|
|
<span>Save Changes</span>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.content-with-fixed-buttons {
|
|
padding-bottom: 80px;
|
|
}
|
|
|
|
.fixed-bottom-buttons {
|
|
position: fixed;
|
|
bottom: 0;
|
|
right: 0;
|
|
padding: 1rem;
|
|
z-index: 1000;
|
|
width: 25%;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.fixed-bottom-buttons .btn {
|
|
min-width: 120px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.form-label-sm {
|
|
font-size: 0.75rem;
|
|
font-weight: 500;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.card-body .form-control-sm {
|
|
font-size: 0.75rem;
|
|
padding: 0.25rem 0.5rem;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
// Derive current counts from DOM to avoid template injection issues
|
|
let columnIndex =
|
|
document.querySelectorAll("#columnsContainer [data-column-index]")
|
|
.length || 0;
|
|
let socialLinkIndex =
|
|
document.querySelectorAll("#socialLinksContainer [data-social-index]")
|
|
.length || 0;
|
|
|
|
// Handle Active Tab Persistence
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const activeTabObj = urlParams.get('activeTab');
|
|
|
|
if (activeTabObj) {
|
|
const tabTrigger = document.querySelector(`a[href="#${activeTabObj}"]`);
|
|
if (tabTrigger) {
|
|
new bootstrap.Tab(tabTrigger).show();
|
|
// Update hidden input to match restored tab
|
|
document.getElementById('activeTabInput').value = activeTabObj;
|
|
}
|
|
}
|
|
|
|
// Update hidden input on tab change
|
|
document.querySelectorAll('a[data-bs-toggle="tab"]').forEach(tab => {
|
|
tab.addEventListener('shown.bs.tab', function (event) {
|
|
const targetId = event.target.getAttribute('href').substring(1); // remove #
|
|
document.getElementById('activeTabInput').value = targetId;
|
|
});
|
|
});
|
|
|
|
// Add Column
|
|
document.getElementById("addColumn").addEventListener("click", function () {
|
|
const container = document.getElementById("columnsContainer");
|
|
const newColumn = document.createElement("div");
|
|
newColumn.className = "card mb-3 border";
|
|
newColumn.dataset.columnIndex = columnIndex;
|
|
newColumn.innerHTML = `
|
|
<div class="card-header bg-light">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h6 class="mb-0">Column ${columnIndex + 1}</h6>
|
|
<button type="button" class="btn btn-outline-danger btn-sm remove-column" data-column-index="${columnIndex}">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Column Title</label>
|
|
<input type="text" class="form-control" name="columns[${columnIndex}][title]" value="">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<h6 class="fw-medium mb-3">Links</h6>
|
|
<div class="column-links-container" data-column-index="${columnIndex}">
|
|
</div>
|
|
<button type="button" class="btn btn-outline-primary btn-sm add-link" data-column-index="${columnIndex}">
|
|
<i class="fas fa-plus me-1"></i>Add Link
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
container.appendChild(newColumn);
|
|
columnIndex++;
|
|
});
|
|
|
|
// Add Social Link
|
|
document
|
|
.getElementById("addSocialLink")
|
|
.addEventListener("click", function () {
|
|
const container = document.getElementById("socialLinksContainer");
|
|
const newLink = document.createElement("div");
|
|
newLink.className = "card mb-3 border";
|
|
newLink.dataset.socialIndex = socialLinkIndex;
|
|
newLink.innerHTML = `
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label fw-medium">Platform</label>
|
|
<input type="text" class="form-control" name="social[links][${socialLinkIndex}][platform]" value="">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label fw-medium">URL</label>
|
|
<input type="text" class="form-control" name="social[links][${socialLinkIndex}][url]" value="" placeholder="https://facebook.com/...">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label fw-medium">Icon Class</label>
|
|
<input type="text" class="form-control" name="social[links][${socialLinkIndex}][icon]" value="">
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label"> </label>
|
|
<button type="button" class="btn btn-outline-danger btn-sm w-100 remove-social-link" data-social-index="${socialLinkIndex}">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
container.appendChild(newLink);
|
|
socialLinkIndex++;
|
|
});
|
|
|
|
// Remove Column
|
|
document.addEventListener("click", function (e) {
|
|
if (e.target.closest(".remove-column")) {
|
|
e.target.closest(".card").remove();
|
|
}
|
|
});
|
|
|
|
// Remove Social Link
|
|
document.addEventListener("click", function (e) {
|
|
if (e.target.closest(".remove-social-link")) {
|
|
e.target.closest(".card").remove();
|
|
}
|
|
});
|
|
|
|
// Add Link to Column
|
|
document.addEventListener("click", function (e) {
|
|
if (e.target.closest(".add-link")) {
|
|
const columnIndex = e.target.closest(".add-link").dataset.columnIndex;
|
|
const container = e.target
|
|
.closest(".card-body")
|
|
.querySelector(".column-links-container");
|
|
const linkIndex = container.children.length;
|
|
|
|
const newLink = document.createElement("div");
|
|
newLink.className = "card mb-2 border";
|
|
newLink.dataset.linkIndex = linkIndex;
|
|
newLink.innerHTML = `
|
|
<div class="card-body">
|
|
<div class="row g-2">
|
|
<div class="col-md-5">
|
|
<label class="form-label form-label-sm">Link Title</label>
|
|
<input type="text" class="form-control form-control-sm" name="columns[${columnIndex}][links][${linkIndex}][title]" value="">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label form-label-sm">URL</label>
|
|
<input type="text" class="form-control form-control-sm" name="columns[${columnIndex}][links][${linkIndex}][url]" value="" placeholder="/about-us/">
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label form-label-sm"> </label>
|
|
<button type="button" class="btn btn-outline-danger btn-sm w-100 remove-link" data-column-index="${columnIndex}" data-link-index="${linkIndex}">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
container.appendChild(newLink);
|
|
}
|
|
});
|
|
|
|
// Remove Link from Column
|
|
document.addEventListener("click", function (e) {
|
|
if (e.target.closest(".remove-link")) {
|
|
e.target.closest(".card").remove();
|
|
}
|
|
});
|
|
|
|
// Initialize 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);
|
|
});
|
|
});
|
|
|
|
// Form submission
|
|
document
|
|
.getElementById("footerForm")
|
|
.addEventListener("submit", function (e) {
|
|
// Build consolidated JSON payload for footer and set hidden input
|
|
try {
|
|
const buildFooterData = () => {
|
|
const getVal = (selector) => {
|
|
const el = document.querySelector(selector);
|
|
return el ? el.value : '';
|
|
};
|
|
|
|
const about = {
|
|
title: getVal('input[name="about[title]"]'),
|
|
description: getVal('textarea[name="about[description]"]'),
|
|
mapLink: {
|
|
text: getVal('input[name="about[mapLink][text]"]'),
|
|
url: getVal('input[name="about[mapLink][url]"]')
|
|
}
|
|
};
|
|
|
|
const logo = {
|
|
src: getVal('input[name="logo[src]"]'),
|
|
alt: getVal('input[name="logo[alt]"]')
|
|
};
|
|
|
|
const address = {
|
|
text: getVal('input[name="address[text]"]'),
|
|
address2: getVal('input[name="address[address2]"]'),
|
|
mapUrl: getVal('input[name="address[mapUrl]"]'),
|
|
link2: getVal('input[name="address[link2]"]')
|
|
};
|
|
|
|
const contact = {
|
|
phone: getVal('input[name="contact[phone]"]'),
|
|
hours: getVal('input[name="contact[hours]"]'),
|
|
email: getVal('input[name="contact[email]"]')
|
|
};
|
|
|
|
// Columns
|
|
const columns = [];
|
|
document.querySelectorAll('#columnsContainer > .card[data-column-index]').forEach(colCard => {
|
|
const colIndex = colCard.dataset.columnIndex;
|
|
const titleEl = colCard.querySelector(`input[name="columns[${colIndex}][title]"]`);
|
|
const column = { title: titleEl ? titleEl.value : '', links: [] };
|
|
const linksContainer = colCard.querySelector('.column-links-container');
|
|
if (linksContainer) {
|
|
linksContainer.querySelectorAll('.card[data-link-index]').forEach(linkCard => {
|
|
const linkTitle = linkCard.querySelector('input[name^="columns"][name$="[title]"]');
|
|
const linkUrl = linkCard.querySelector('input[name^="columns"][name$="[url]"]');
|
|
if (linkTitle && linkUrl) {
|
|
column.links.push({ title: linkTitle.value, url: linkUrl.value });
|
|
}
|
|
});
|
|
}
|
|
columns.push(column);
|
|
});
|
|
|
|
// Social links
|
|
const socialLinks = [];
|
|
document.querySelectorAll('#socialLinksContainer > .card[data-social-index]').forEach(sCard => {
|
|
const platform = sCard.querySelector('input[name^="social"][name$="[platform]"]')?.value || '';
|
|
const url = sCard.querySelector('input[name^="social"][name$="[url]"]')?.value || '';
|
|
const icon = sCard.querySelector('input[name^="social"][name$="[icon]"]')?.value || '';
|
|
if (platform || url || icon) socialLinks.push({ platform, url, icon });
|
|
});
|
|
|
|
const copyright = { text: getVal('textarea[name="copyright[text]"]') };
|
|
|
|
return { about, logo, address, contact, columns, social: { links: socialLinks }, copyright };
|
|
};
|
|
|
|
const payload = buildFooterData();
|
|
document.getElementById('footerJson').value = JSON.stringify(payload);
|
|
} catch (err) {
|
|
console.error('Error building footerJson payload:', err);
|
|
}
|
|
// Show loading state
|
|
const submitBtn = this.querySelector('button[type="submit"]');
|
|
const originalText = submitBtn.innerHTML;
|
|
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Saving...';
|
|
submitBtn.disabled = true;
|
|
});
|
|
});
|
|
|
|
function openImageUploader(targetInput, imageType) {
|
|
// Tạo input file ẩn
|
|
const fileInput = document.createElement("input");
|
|
fileInput.type = "file";
|
|
fileInput.accept = "image/*";
|
|
fileInput.style.display = "none";
|
|
document.body.appendChild(fileInput);
|
|
|
|
// Xử lý khi chọn file
|
|
fileInput.onchange = async function (e) {
|
|
const file = e.target.files[0];
|
|
if (!file) return;
|
|
|
|
try {
|
|
// Tạo FormData
|
|
const formData = new FormData();
|
|
formData.append("image", file);
|
|
|
|
// Disable nút upload và hiển thị loading
|
|
const uploadBtn = document.querySelector(
|
|
`[data-target-input="${targetInput}"]`
|
|
);
|
|
const originalBtnHtml = uploadBtn.innerHTML;
|
|
uploadBtn.disabled = true;
|
|
uploadBtn.innerHTML =
|
|
'<i class="fas fa-spinner fa-spin me-1"></i>Uploading...';
|
|
|
|
// Gửi request upload
|
|
const response = await fetch(
|
|
`/admin/upload/image?imageType=${imageType}`,
|
|
{
|
|
method: "POST",
|
|
body: formData,
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
throw new Error("Upload failed");
|
|
}
|
|
|
|
const result = await response.json();
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Upload failed");
|
|
}
|
|
|
|
// Cập nhật đường dẫn ảnh vào input (support name or id)
|
|
const input = document.querySelector(`input[name="${targetInput}"]`) || document.getElementById(targetInput) || document.querySelector(`#${targetInput}`);
|
|
if (!input) {
|
|
throw new Error("Target input not found");
|
|
}
|
|
|
|
input.value = result.path;
|
|
|
|
// Tìm hoặc tạo preview container
|
|
let imgPreview = input.parentElement.nextElementSibling;
|
|
while (imgPreview && !imgPreview.classList.contains("mt-3")) {
|
|
imgPreview = imgPreview.nextElementSibling;
|
|
}
|
|
|
|
if (!imgPreview) {
|
|
// Tạo mới phần tử preview nếu chưa có
|
|
imgPreview = document.createElement("div");
|
|
imgPreview.className = "mt-3";
|
|
const img = document.createElement("img");
|
|
img.className = "img-thumbnail";
|
|
img.style.maxHeight = "100px";
|
|
img.style.maxWidth = "300px";
|
|
img.style.objectFit = "contain";
|
|
img.style.background = "var(--primary-color)";
|
|
img.alt = "Image preview";
|
|
imgPreview.appendChild(img);
|
|
input.parentElement.parentElement.appendChild(imgPreview);
|
|
}
|
|
|
|
// Cập nhật ảnh preview
|
|
const img = imgPreview.querySelector("img");
|
|
if (img) {
|
|
img.src = result.path;
|
|
}
|
|
|
|
// Restore nút upload
|
|
uploadBtn.disabled = false;
|
|
uploadBtn.innerHTML = originalBtnHtml;
|
|
|
|
// Cleanup
|
|
document.body.removeChild(fileInput);
|
|
} catch (error) {
|
|
console.error("Upload error:", error);
|
|
alert("Upload failed: " + error.message);
|
|
|
|
// Restore nút upload
|
|
const uploadBtn = document.querySelector(
|
|
`[data-target-input="${targetInput}"]`
|
|
);
|
|
uploadBtn.disabled = false;
|
|
uploadBtn.innerHTML = originalBtnHtml;
|
|
|
|
// Cleanup
|
|
if (document.body.contains(fileInput)) {
|
|
document.body.removeChild(fileInput);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Trigger file selection
|
|
fileInput.click();
|
|
}
|
|
</script> |