forked from UKSOURCE/cms.hailearning.edu.vn
1024 lines
47 KiB
Plaintext
1024 lines
47 KiB
Plaintext
<div class="container">
|
|
<div class="d-flex justify-content-between align-items-center mt-4 mb-4">
|
|
<div>
|
|
<h1 class="h3 mb-0" style="color: var(--primary-dark);"><%= title %></h1>
|
|
<p class="text-muted mb-0">Edit content displayed on About page</p>
|
|
</div>
|
|
<div>
|
|
<a href="<%= frontendUrl %>/about-us/" class="btn btn-outline-primary" target="_blank">
|
|
<i class="fas fa-external-link-alt me-2"></i>View About Page
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<form action="/admin/about/update" method="POST" class="content-with-fixed-buttons" id="aboutForm">
|
|
<!-- Hidden inputs for JSON data -->
|
|
<input type="hidden" name="banner" id="bannerJson">
|
|
<input type="hidden" name="about" id="aboutJson">
|
|
<input type="hidden" name="values" id="valuesJson">
|
|
<input type="hidden" name="education" id="educationJson">
|
|
<input type="hidden" name="advantages" id="advantagesJson">
|
|
<input type="hidden" name="academic_board" id="academicBoardJson">
|
|
|
|
<!-- 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="#banner" role="tab">
|
|
<i class="fas fa-image me-2"></i>Banner
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#about-section" role="tab">
|
|
<i class="fas fa-info-circle me-2"></i>About
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#values" role="tab">
|
|
<i class="fas fa-star me-2"></i>Values
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#education" role="tab">
|
|
<i class="fas fa-graduation-cap me-2"></i>Education
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#advantages" role="tab">
|
|
<i class="fas fa-check-circle me-2"></i>Advantages
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#academic-board" role="tab">
|
|
<i class="fas fa-users me-2"></i>Academic Board
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
<div class="tab-content">
|
|
<!-- Banner Tab -->
|
|
<div class="tab-pane fade show active" id="banner" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-5">
|
|
<label class="form-label fw-medium">Banner Image</label>
|
|
<div class="input-group mb-2">
|
|
<input type="text" class="form-control" id="bannerImage" name="bannerImage" value="<%= data.banner.image %>">
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="bannerImage" data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
<small class="form-text text-muted">Recommended size: 1920x1080px</small>
|
|
</div>
|
|
<div class="col-md-7">
|
|
<% if (data.banner.image) { %>
|
|
<img src="<%= data.banner.image %>" class="img-thumbnail" style="height: 300px; width: 100%; object-fit: cover;" alt="Banner image preview">
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
<div class="row mt-3">
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Title</label>
|
|
<input type="text" class="form-control" id="bannerTitle" name="bannerTitle" value="<%= data.banner.title %>">
|
|
</div>
|
|
</div>
|
|
<div class="row mt-3">
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Description</label>
|
|
<textarea class="form-control" id="bannerText" name="bannerText" rows="3"><%= data.banner.text %></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- About Section Tab -->
|
|
<div class="tab-pane fade" id="about-section" role="tabpanel">
|
|
<div class="card border shadow-sm mb-4">
|
|
<div class="card-body">
|
|
<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="aboutTitle" name="aboutTitle" value="<%= data.about.title %>">
|
|
</div>
|
|
|
|
<!-- Paragraphs -->
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Paragraphs</label>
|
|
<div id="paragraphsContainer">
|
|
<% data.about.paragraphs.forEach((paragraph, index) => { %>
|
|
<div class="input-group mb-3">
|
|
<textarea class="form-control" name="paragraph_<%= index %>" rows="3"><%= paragraph %></textarea>
|
|
<button type="button" class="btn btn-outline-danger" onclick="removeParagraph(this)">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
<% }); %>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="addParagraph()">
|
|
<i class="fas fa-plus me-2"></i>Add Paragraph
|
|
</button>
|
|
</div>
|
|
|
|
<!-- List Items -->
|
|
<div class="col-md-12 mt-4">
|
|
<label class="form-label fw-medium">List Items</label>
|
|
<div id="listItemsContainer">
|
|
<% data.about.list_items.forEach((item, index) => { %>
|
|
<div class="input-group mb-3">
|
|
<input type="text" class="form-control" name="listItem_<%= index %>" value="<%= item %>">
|
|
<button type="button" class="btn btn-outline-danger" onclick="removeListItem(this)">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
<% }); %>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="addListItem()">
|
|
<i class="fas fa-plus me-2"></i>Add List Item
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Button -->
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Button Text</label>
|
|
<input type="text" class="form-control" id="aboutButtonText" name="aboutButtonText" value="<%= data.about.button.text %>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Button URL</label>
|
|
<input type="text" class="form-control" id="aboutButtonUrl" name="aboutButtonUrl" value="<%= data.about.button.url %>">
|
|
</div>
|
|
|
|
<!-- Image -->
|
|
<div class="col-md-12 mt-4">
|
|
<label class="form-label fw-medium">Section Image</label>
|
|
<div class="row">
|
|
<div class="col-md-5">
|
|
<div class="input-group mb-2">
|
|
<input type="text" class="form-control" id="aboutImage" name="aboutImage" value="<%= data.about.image %>">
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="aboutImage" data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-7">
|
|
<% if (data.about.image) { %>
|
|
<img src="<%= data.about.image %>" class="img-thumbnail" style="height: 300px; width: 100%; object-fit: contain;" alt="Section image preview">
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quote -->
|
|
<div class="col-12 mt-4">
|
|
<div class="card border">
|
|
<div class="card-header bg-light">
|
|
<h6 class="mb-0">Quote Section</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label">Quote Mark Image</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" id="quoteMarkImage" name="quoteMarkImage" value="<%= data.about.quote.mark_image %>">
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="quoteMarkImage" data-image-type="icons">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label">Quote Title</label>
|
|
<input type="text" class="form-control" id="quoteTitle" name="quoteTitle" value="<%= data.about.quote.title %>">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label">Quote Text</label>
|
|
<textarea class="form-control" id="quoteText" name="quoteText" rows="3"><%= data.about.quote.text %></textarea>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label">Author</label>
|
|
<input type="text" class="form-control" id="quoteAuthor" name="quoteAuthor" value="<%= data.about.quote.author %>">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Values Tab -->
|
|
<div class="tab-pane fade" id="values" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<!-- Background Image -->
|
|
<div class="row mb-4">
|
|
<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="valuesBackgroundImage" name="valuesBackgroundImage" value="<%= data.values.background_image %>">
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="valuesBackgroundImage" data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-7">
|
|
<% if (data.values.background_image) { %>
|
|
<img src="<%= data.values.background_image %>" class="img-thumbnail" style="height: 200px; width: 100%; object-fit: contain;" alt="Background image preview">
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Values Items -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h6 class="fw-medium mb-0">Values</h6>
|
|
<button type="button" class="btn btn-primary btn-sm" onclick="addValue()">
|
|
<i class="fas fa-plus"></i>
|
|
</button>
|
|
</div>
|
|
<div id="valuesContainer">
|
|
<% data.values.items.forEach((item, index) => { %>
|
|
<div class="card mb-3 value-item">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h6 class="mb-0 text-decoration-underline">Value Information</h6>
|
|
<button type="button" class="btn btn-danger btn-sm" onclick="removeValue(this)">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Icon</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" name="valueIcon_<%= index %>" value="<%= item.icon %>">
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="valueIcon_<%= index %>" data-image-type="icons">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
<% if (item.icon) { %>
|
|
<div class="text-center mt-2">
|
|
<img src="<%= item.icon %>" class="img-thumbnail" style="height: 40px; width: 40px; object-fit: contain;" alt="Icon preview">
|
|
</div>
|
|
<% } %>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Title</label>
|
|
<input type="text" class="form-control" name="valueTitle_<%= index %>" value="<%= item.title %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Text</label>
|
|
<textarea class="form-control" name="valueText_<%= index %>" rows="2"><%= item.text %></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Education Tab -->
|
|
<div class="tab-pane fade" id="education" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<!-- Student Images -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Student Image 1</label>
|
|
<div class="input-group mb-2">
|
|
<input type="text" class="form-control" id="student1Image" name="student1Image" value="<%= data.education.images.student1 %>">
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="student1Image" data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
<% if (data.education.images.student1) { %>
|
|
<img src="<%= data.education.images.student1 %>" class="img-thumbnail mt-2" style="height: 300px; width: 100%; object-fit: cover;" alt="Student 1 image preview">
|
|
<% } %>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Student Image 2 / YouTube Video</label>
|
|
<div class="input-group mb-2">
|
|
<input type="text" class="form-control" id="student2Image" name="student2Image" value="<%= data.education.images.student2 %>" placeholder="Enter YouTube URL (e.g., https://youtu.be/VIDEO_ID)">
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="student2Image" data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload Image
|
|
</button>
|
|
</div>
|
|
<div class="form-text text-muted mb-2">
|
|
Enter YouTube URL or upload an image file (jpg, png, gif).
|
|
</div>
|
|
<% if (data.education.images.student2) { %>
|
|
<% if (data.education.images.student2.includes('youtu.be') || data.education.images.student2.includes('youtube.com')) { %>
|
|
<div class="youtube-preview mt-2" style="height: 300px; width: 100%;">
|
|
<iframe width="100%" height="100%" src="<%= data.education.images.student2.replace('youtu.be/', 'youtube.com/embed/').replace('watch?v=', 'embed/') %>" frameborder="0" allowfullscreen></iframe>
|
|
</div>
|
|
<% } else { %>
|
|
<img src="<%= data.education.images.student2 %>" class="img-thumbnail mt-2" style="height: 300px; width: 100%; object-fit: cover;" alt="Student 2 image preview">
|
|
<% } %>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Education Content -->
|
|
<div class="row g-3">
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Subtitle</label>
|
|
<input type="text" class="form-control" id="educationSubtitle" name="educationSubtitle" value="<%= data.education.subtitle %>">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Title</label>
|
|
<input type="text" class="form-control" id="educationTitle" name="educationTitle" value="<%= data.education.title %>">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Text</label>
|
|
<textarea class="form-control" id="educationText" name="educationText" rows="4"><%= data.education.text %></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Advantages Tab -->
|
|
<div class="tab-pane fade" id="advantages" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<label class="form-label fw-medium">Title</label>
|
|
<input type="text" class="form-control" id="advantagesTitle" name="advantagesTitle" value="<%= data.advantages.title %>">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Advantages Items -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<label class="form-label fw-medium">Advantages</label>
|
|
<div id="advantagesContainer">
|
|
<% data.advantages.items.forEach((item, index) => { %>
|
|
<div class="card mb-3 advantage-item">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-2">
|
|
<label class="form-label">Number</label>
|
|
<input type="text" class="form-control" name="advantageNumber_<%= index %>" value="<%= item.number %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Title</label>
|
|
<input type="text" class="form-control" name="advantageTitle_<%= index %>" value="<%= item.title %>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label">Text</label>
|
|
<textarea class="form-control" name="advantageText_<%= index %>" rows="2"><%= item.text %></textarea>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-danger btn-sm mt-3" onclick="removeAdvantage(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Advantage
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-primary" onclick="addAdvantage()">
|
|
<i class="fas fa-plus me-2"></i>Add Advantage
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Academic Board Tab -->
|
|
<div class="tab-pane fade" id="academic-board" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<label class="form-label fw-medium">Title</label>
|
|
<input type="text" class="form-control" id="academicBoardTitle" name="academicBoardTitle" value="<%= data.academic_board.title %>">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Board Members -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<label class="form-label fw-medium">Board Members</label>
|
|
<div id="boardMembersContainer">
|
|
<% data.academic_board.members.forEach((member, index) => { %>
|
|
<div class="card mb-3 board-member">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label">Image</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" name="memberImage_<%= index %>" value="<%= member.image %>">
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="memberImage_<%= index %>" data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
<% if (member.image) { %>
|
|
<img src="<%= member.image %>" class="img-thumbnail mt-2" style="height: 300px; width: 100%; object-fit: cover;" alt="Member image preview">
|
|
<% } %>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">Title</label>
|
|
<input type="text" class="form-control" name="memberTitle_<%= index %>" value="<%= member.title %>">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">Name</label>
|
|
<input type="text" class="form-control" name="memberName_<%= index %>" value="<%= member.name %>">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">Color</label>
|
|
<select class="form-select" name="memberColor_<%= index %>">
|
|
<option value="#065c2e" <%= member.color === '#065c2e' ? 'selected' : '' %>>Green</option>
|
|
<option value="#ffa500" <%= member.color === '#ffa500' ? 'selected' : '' %>>Orange</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-danger btn-sm mt-3" onclick="removeBoardMember(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Member
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-primary" onclick="addBoardMember()">
|
|
<i class="fas fa-plus me-2"></i>Add Board Member
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Fixed Bottom Buttons -->
|
|
<div class="fixed-bottom-buttons">
|
|
<button type="reset" class="btn btn-secondary">
|
|
<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>
|
|
|
|
<!-- Scripts -->
|
|
<script>
|
|
let originalFormData = null;
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Initialize form data
|
|
// eslint-disable-next-line no-undef
|
|
originalFormData = <%- JSON.stringify(data) %>;
|
|
|
|
// Set initial JSON values
|
|
updateAllJsonInputs(originalFormData);
|
|
|
|
// Initialize form handlers
|
|
initializeFormHandlers();
|
|
});
|
|
|
|
function initializeFormHandlers() {
|
|
// Form submission
|
|
const form = document.getElementById('aboutForm');
|
|
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);
|
|
showError('Failed to process form data. Please try again.');
|
|
submitBtn.disabled = false;
|
|
submitBtn.innerHTML = '<i class="fas fa-save me-2"></i>Save Changes';
|
|
}
|
|
});
|
|
|
|
// 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);
|
|
});
|
|
});
|
|
|
|
// Initialize YouTube URL input handler
|
|
const student2Input = document.getElementById('student2Image');
|
|
if (student2Input) {
|
|
student2Input.addEventListener('input', function() {
|
|
updateStudent2Preview(this.value);
|
|
});
|
|
}
|
|
}
|
|
|
|
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 (keep relative path for storage)
|
|
const input = document.getElementById(targetInput) || document.querySelector(`[name="${targetInput}"]`);
|
|
if (!input) {
|
|
throw new Error('Target input not found');
|
|
}
|
|
input.value = result.path;
|
|
|
|
// Build absolute URL for preview (works when frontend is on different origin)
|
|
let previewUrl = result.path;
|
|
if (!/^https?:\/\//i.test(previewUrl)) {
|
|
previewUrl = window.location.origin.replace(/\/$/, '') + previewUrl;
|
|
}
|
|
|
|
// Find a sensible container for preview: look for existing .mt-2 near the input, or append to input's column
|
|
let containerForPreview = input.closest('.col-md-7') || input.closest('.col-md-6') || input.parentElement;
|
|
let imgPreview = containerForPreview.querySelector('.mt-2');
|
|
if (!imgPreview) {
|
|
imgPreview = document.createElement('div');
|
|
imgPreview.className = 'mt-2';
|
|
const img = document.createElement('img');
|
|
img.className = 'img-thumbnail';
|
|
|
|
// Set style dựa vào loại ảnh
|
|
if (targetInput.toLowerCase().includes('logo') || targetInput.toLowerCase().includes('icon')) {
|
|
img.style.height = '100px';
|
|
img.style.objectFit = 'contain';
|
|
} else if (targetInput.toLowerCase().includes('member')) {
|
|
img.style.height = '300px';
|
|
img.style.width = '100%';
|
|
img.style.objectFit = 'cover';
|
|
} else {
|
|
img.style.height = '200px';
|
|
img.style.width = '100%';
|
|
img.style.objectFit = 'cover';
|
|
}
|
|
|
|
img.alt = 'Image preview';
|
|
imgPreview.appendChild(img);
|
|
containerForPreview.appendChild(imgPreview);
|
|
}
|
|
|
|
// Cập nhật ảnh preview
|
|
const imgEl = imgPreview.querySelector('img');
|
|
if (imgEl) imgEl.src = previewUrl;
|
|
|
|
// Nếu là student2Image, cập nhật preview (handles youtube or image)
|
|
if (targetInput === 'student2Image') {
|
|
updateStudent2Preview(result.path.startsWith('/') ? previewUrl : result.path);
|
|
}
|
|
|
|
// Hiển thị thông báo thành công
|
|
if (window.toastManager) {
|
|
window.toastManager.success('Image uploaded successfully');
|
|
} else {
|
|
showToast('Success', 'Image uploaded successfully', 'success');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Upload error:', error);
|
|
if (window.toastManager) {
|
|
window.toastManager.error('Failed to upload image: ' + error.message);
|
|
} else {
|
|
showToast('Error', 'Failed to upload image: ' + error.message, 'error');
|
|
}
|
|
} finally {
|
|
// Restore nút upload
|
|
const uploadBtn = document.querySelector(`[data-target-input="${targetInput}"]`);
|
|
if (uploadBtn) {
|
|
uploadBtn.disabled = false;
|
|
uploadBtn.innerHTML = originalBtnHtml;
|
|
}
|
|
|
|
// Xóa input file
|
|
document.body.removeChild(fileInput);
|
|
}
|
|
};
|
|
|
|
// Trigger click để mở dialog chọn file
|
|
fileInput.click();
|
|
}
|
|
|
|
// Function to update Student 2 preview (YouTube or Image)
|
|
function updateStudent2Preview(url) {
|
|
const input = document.getElementById('student2Image');
|
|
const parentElement = input.closest('.col-md-6');
|
|
|
|
// Remove existing preview
|
|
const existingPreview = parentElement.querySelector('.youtube-preview, .img-thumbnail');
|
|
if (existingPreview) {
|
|
existingPreview.remove();
|
|
}
|
|
|
|
if (!url || url.trim() === '') {
|
|
return;
|
|
}
|
|
|
|
const trimmedUrl = url.trim();
|
|
|
|
// Check if it's a YouTube URL
|
|
if (trimmedUrl.includes('youtu.be') || trimmedUrl.includes('youtube.com')) {
|
|
// Create YouTube embed preview
|
|
const youtubePreview = document.createElement('div');
|
|
youtubePreview.className = 'youtube-preview mt-2';
|
|
youtubePreview.style.height = '300px';
|
|
youtubePreview.style.width = '100%';
|
|
|
|
const iframe = document.createElement('iframe');
|
|
iframe.width = '100%';
|
|
iframe.height = '100%';
|
|
iframe.frameBorder = '0';
|
|
iframe.allowFullscreen = true;
|
|
|
|
// Convert YouTube URL to embed format
|
|
let embedUrl = trimmedUrl;
|
|
if (embedUrl.includes('youtu.be/')) {
|
|
embedUrl = embedUrl.replace('youtu.be/', 'youtube.com/embed/');
|
|
} else if (embedUrl.includes('watch?v=')) {
|
|
embedUrl = embedUrl.replace('watch?v=', 'embed/');
|
|
}
|
|
|
|
iframe.src = embedUrl;
|
|
youtubePreview.appendChild(iframe);
|
|
parentElement.appendChild(youtubePreview);
|
|
} else if (trimmedUrl.match(/\.(jpg|jpeg|png|gif|webp)$/i)) {
|
|
// It's an image URL
|
|
const imgPreview = document.createElement('img');
|
|
imgPreview.className = 'img-thumbnail mt-2';
|
|
imgPreview.style.height = '300px';
|
|
imgPreview.style.width = '100%';
|
|
imgPreview.style.objectFit = 'cover';
|
|
imgPreview.alt = 'Student 2 image preview';
|
|
imgPreview.src = trimmedUrl;
|
|
parentElement.appendChild(imgPreview);
|
|
}
|
|
}
|
|
|
|
// Hiển thị toast message
|
|
function showToast(title, message, type = 'info') {
|
|
const toast = document.createElement('div');
|
|
toast.className = `toast align-items-center text-white bg-${type === 'error' ? 'danger' : type} border-0`;
|
|
toast.setAttribute('role', 'alert');
|
|
toast.setAttribute('aria-live', 'assertive');
|
|
toast.setAttribute('aria-atomic', 'true');
|
|
|
|
toast.innerHTML = `
|
|
<div class="d-flex">
|
|
<div class="toast-body">
|
|
<strong>${title}:</strong> ${message}
|
|
</div>
|
|
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
|
</div>
|
|
`;
|
|
|
|
// Thêm toast vào container
|
|
const container = document.querySelector('.toast-container');
|
|
if (!container) {
|
|
const newContainer = document.createElement('div');
|
|
newContainer.className = 'toast-container position-fixed top-0 end-0 p-3';
|
|
document.body.appendChild(newContainer);
|
|
newContainer.appendChild(toast);
|
|
} else {
|
|
container.appendChild(toast);
|
|
}
|
|
|
|
// Hiển thị toast
|
|
const bsToast = new bootstrap.Toast(toast, {
|
|
animation: true,
|
|
autohide: true,
|
|
delay: 3000
|
|
});
|
|
bsToast.show();
|
|
|
|
// Xóa toast sau khi ẩn
|
|
toast.addEventListener('hidden.bs.toast', () => {
|
|
toast.remove();
|
|
});
|
|
}
|
|
|
|
function resetForm() {
|
|
if (confirm('Are you sure you want to reset all changes?')) {
|
|
updateAllJsonInputs(originalFormData);
|
|
location.reload();
|
|
}
|
|
}
|
|
|
|
function showError(message) {
|
|
const alertHtml = `
|
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
|
${message}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
`;
|
|
document.querySelector('.container').insertAdjacentHTML('afterbegin', alertHtml);
|
|
}
|
|
|
|
function updateAllJsonInputs(data) {
|
|
document.getElementById('bannerJson').value = JSON.stringify(data.banner || {});
|
|
document.getElementById('aboutJson').value = JSON.stringify(data.about || {});
|
|
document.getElementById('valuesJson').value = JSON.stringify(data.values || {});
|
|
document.getElementById('educationJson').value = JSON.stringify(data.education || {});
|
|
document.getElementById('advantagesJson').value = JSON.stringify(data.advantages || {});
|
|
document.getElementById('academicBoardJson').value = JSON.stringify(data.academic_board || {});
|
|
}
|
|
|
|
// Dynamic form functions
|
|
function addParagraph() {
|
|
const container = document.getElementById('paragraphsContainer');
|
|
const index = container.children.length;
|
|
const html = `
|
|
<div class="input-group mb-3">
|
|
<textarea class="form-control" name="paragraph_${index}" rows="3"></textarea>
|
|
<button type="button" class="btn btn-outline-danger" onclick="removeParagraph(this)">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
`;
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
}
|
|
|
|
function removeParagraph(button) {
|
|
button.closest('.input-group').remove();
|
|
}
|
|
|
|
function addListItem() {
|
|
const container = document.getElementById('listItemsContainer');
|
|
const index = container.children.length;
|
|
const html = `
|
|
<div class="input-group mb-3">
|
|
<input type="text" class="form-control" name="listItem_${index}">
|
|
<button type="button" class="btn btn-outline-danger" onclick="removeListItem(this)">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
`;
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
}
|
|
|
|
function removeListItem(button) {
|
|
button.closest('.input-group').remove();
|
|
}
|
|
|
|
function addValue() {
|
|
const container = document.getElementById('valuesContainer');
|
|
const index = container.children.length;
|
|
const html = `
|
|
<div class="card mb-3 value-item">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h6 class="mb-0 text-decoration-underline">Value Information</h6>
|
|
<button type="button" class="btn btn-danger btn-sm" onclick="removeValue(this)">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Icon</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" name="valueIcon_${index}">
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="valueIcon_${index}" data-image-type="icons">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Title</label>
|
|
<input type="text" class="form-control" name="valueTitle_${index}">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Text</label>
|
|
<textarea class="form-control" name="valueText_${index}" rows="2"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
}
|
|
|
|
function removeValue(button) {
|
|
button.closest('.value-item').remove();
|
|
}
|
|
|
|
function addAdvantage() {
|
|
const container = document.getElementById('advantagesContainer');
|
|
const index = container.children.length;
|
|
const html = `
|
|
<div class="card mb-3 advantage-item">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-2">
|
|
<label class="form-label">Number</label>
|
|
<input type="text" class="form-control" name="advantageNumber_${index}">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Title</label>
|
|
<input type="text" class="form-control" name="advantageTitle_${index}">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label">Text</label>
|
|
<textarea class="form-control" name="advantageText_${index}" rows="2"></textarea>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-danger btn-sm mt-3" onclick="removeAdvantage(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Advantage
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
}
|
|
|
|
function removeAdvantage(button) {
|
|
button.closest('.advantage-item').remove();
|
|
}
|
|
|
|
function addBoardMember() {
|
|
const container = document.getElementById('boardMembersContainer');
|
|
const index = container.children.length;
|
|
const html = `
|
|
<div class="card mb-3 board-member">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label">Image</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" name="memberImage_${index}">
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="memberImage_${index}" data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">Title</label>
|
|
<input type="text" class="form-control" name="memberTitle_${index}">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">Name</label>
|
|
<input type="text" class="form-control" name="memberName_${index}">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">Color</label>
|
|
<select class="form-select" name="memberColor_${index}">
|
|
<option value="#065c2e">Green</option>
|
|
<option value="#ffa500">Orange</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-danger btn-sm mt-3" onclick="removeBoardMember(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Member
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
}
|
|
|
|
function removeBoardMember(button) {
|
|
button.closest('.board-member').remove();
|
|
}
|
|
|
|
function updateJsonData() {
|
|
try {
|
|
// Update banner data
|
|
const bannerData = {
|
|
image: document.getElementById('bannerImage').value.trim(),
|
|
title: document.getElementById('bannerTitle').value.trim(),
|
|
text: document.getElementById('bannerText').value.trim()
|
|
};
|
|
document.getElementById('bannerJson').value = JSON.stringify(bannerData);
|
|
|
|
// Update about data
|
|
const aboutData = {
|
|
title: document.getElementById('aboutTitle').value.trim(),
|
|
paragraphs: Array.from(document.querySelectorAll('[name^="paragraph_"]'))
|
|
.map(el => el.value.trim())
|
|
.filter(text => text !== ''),
|
|
list_items: Array.from(document.querySelectorAll('[name^="listItem_"]'))
|
|
.map(el => el.value.trim())
|
|
.filter(text => text !== ''),
|
|
button: {
|
|
text: document.getElementById('aboutButtonText').value.trim(),
|
|
url: document.getElementById('aboutButtonUrl').value.trim()
|
|
},
|
|
image: document.getElementById('aboutImage').value.trim(),
|
|
quote: {
|
|
mark_image: document.getElementById('quoteMarkImage').value.trim(),
|
|
title: document.getElementById('quoteTitle').value.trim(),
|
|
text: document.getElementById('quoteText').value.trim(),
|
|
author: document.getElementById('quoteAuthor').value.trim()
|
|
}
|
|
};
|
|
document.getElementById('aboutJson').value = JSON.stringify(aboutData);
|
|
|
|
// Update values data
|
|
const valuesData = {
|
|
background_image: document.getElementById('valuesBackgroundImage').value.trim(),
|
|
items: Array.from(document.querySelectorAll('.value-item'))
|
|
.map((item, index) => ({
|
|
icon: item.querySelector(`[name="valueIcon_${index}"]`).value.trim(),
|
|
title: item.querySelector(`[name="valueTitle_${index}"]`).value.trim(),
|
|
text: item.querySelector(`[name="valueText_${index}"]`).value.trim()
|
|
}))
|
|
.filter(item => item.title !== '' || item.text !== '')
|
|
};
|
|
document.getElementById('valuesJson').value = JSON.stringify(valuesData);
|
|
|
|
// Update education data
|
|
const educationData = {
|
|
images: {
|
|
student1: document.getElementById('student1Image').value.trim(),
|
|
student2: document.getElementById('student2Image').value.trim()
|
|
},
|
|
subtitle: document.getElementById('educationSubtitle').value.trim(),
|
|
title: document.getElementById('educationTitle').value.trim(),
|
|
text: document.getElementById('educationText').value.trim()
|
|
};
|
|
document.getElementById('educationJson').value = JSON.stringify(educationData);
|
|
|
|
// Update advantages data
|
|
const advantagesData = {
|
|
title: document.getElementById('advantagesTitle').value.trim(),
|
|
items: Array.from(document.querySelectorAll('.advantage-item'))
|
|
.map((item, index) => ({
|
|
number: item.querySelector(`[name="advantageNumber_${index}"]`).value.trim(),
|
|
title: item.querySelector(`[name="advantageTitle_${index}"]`).value.trim(),
|
|
text: item.querySelector(`[name="advantageText_${index}"]`).value.trim()
|
|
}))
|
|
.filter(item => item.title !== '' || item.text !== '')
|
|
};
|
|
document.getElementById('advantagesJson').value = JSON.stringify(advantagesData);
|
|
|
|
// Update academic board data
|
|
const academicBoardData = {
|
|
title: document.getElementById('academicBoardTitle').value.trim(),
|
|
members: Array.from(document.querySelectorAll('.board-member'))
|
|
.map((item, index) => ({
|
|
image: item.querySelector(`[name="memberImage_${index}"]`).value.trim(),
|
|
title: item.querySelector(`[name="memberTitle_${index}"]`).value.trim(),
|
|
name: item.querySelector(`[name="memberName_${index}"]`).value.trim(),
|
|
color: item.querySelector(`[name="memberColor_${index}"]`).value
|
|
}))
|
|
.filter(member => member.name !== '' || member.title !== '')
|
|
};
|
|
document.getElementById('academicBoardJson').value = JSON.stringify(academicBoardData);
|
|
} catch (error) {
|
|
console.error('Error updating JSON data:', error);
|
|
throw new Error('Failed to process form data');
|
|
}
|
|
}
|
|
</script> |