forked from UKSOURCE/cms.hailearning.edu.vn
first commit
This commit is contained in:
661
views/admin/form/index.ejs
Normal file
661
views/admin/form/index.ejs
Normal file
@@ -0,0 +1,661 @@
|
||||
<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);">Form Management</h1>
|
||||
<p class="text-muted mb-0">Edit form content for Academics page</p>
|
||||
</div>
|
||||
<div>
|
||||
<a href="/admin/academics" class="btn btn-outline-primary">
|
||||
<i class="fas fa-arrow-left me-1"></i>Back to Academics
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<form id="formManagementForm" action="/admin/form/update" method="POST" class="content-with-fixed-buttons">
|
||||
<input type="hidden" name="admissionJson" id="admissionJson">
|
||||
<input type="hidden" name="applyJson" id="applyJson">
|
||||
<input type="hidden" name="applicationFormJson" id="applicationFormJson">
|
||||
|
||||
<!-- 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="#admission" role="tab">
|
||||
<i class="fas fa-door-open me-2"></i>Admission
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="tab" href="#apply" role="tab">
|
||||
<i class="fas fa-check-circle me-2"></i>Apply
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="tab" href="#application-form" role="tab">
|
||||
<i class="fas fa-file-alt me-2"></i>Application Form
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<!-- Admission Tab -->
|
||||
<div class="tab-pane fade show active" id="admission" role="tabpanel">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-12">
|
||||
<label class="form-label fw-medium">Background Image</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="admissionBgImage" name="admissionBgImage" value="<%= form.admission && form.admission.background_image ? form.admission.background_image : '' %>">
|
||||
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="admissionBgImage" data-image-type="academics">
|
||||
<i class="fas fa-upload me-1"></i>Upload
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="image-preview mt-2">
|
||||
<% if (form.admission && form.admission.background_image) { %>
|
||||
<img src="<%= form.admission.background_image %>" alt="Background preview" class="img-thumbnail" style="height:200px;width:100%;object-fit:cover;">
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-medium">Title</label>
|
||||
<input type="text" class="form-control" id="admissionTitle" name="admissionTitle" value="<%= form.admission && form.admission.title ? form.admission.title : '' %>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-medium">Year</label>
|
||||
<input type="text" class="form-control" id="admissionYear" name="admissionYear" value="<%= form.admission && form.admission.year ? form.admission.year : '' %>">
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<label class="form-label fw-medium">Description</label>
|
||||
<textarea class="form-control" id="admissionDescription" name="admissionDescription" rows="3"><%= form.admission && form.admission.description ? form.admission.description : '' %></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Admission Form Fields -->
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Form Fields</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="admissionFormFields">
|
||||
<% (form.admission && form.admission.form && form.admission.form.fields || []).forEach((field, index) => { %>
|
||||
<div class="mb-3 border p-3">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Field Type</label>
|
||||
<input type="text" class="form-control" value="<%= field.type %>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Placeholder</label>
|
||||
<input type="text" class="form-control" value="<%= field.placeholder %>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% }); %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-medium">Form Button Text</label>
|
||||
<input type="text" class="form-control" id="admissionFormButton" name="admissionFormButton" value="<%= form.admission && form.admission.form && form.admission.form.button ? form.admission.form.button.text : '' %>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-medium">Form Button URL</label>
|
||||
<input type="text" class="form-control" id="admissionFormButtonUrl" name="admissionFormButtonUrl" value="<%= form.admission && form.admission.form && form.admission.form.button ? form.admission.form.button.url : '' %>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply Tab -->
|
||||
<div class="tab-pane fade" id="apply" 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="applyTitle" name="applyTitle" value="<%= form.apply && form.apply.title ? form.apply.title : '' %>">
|
||||
</div>
|
||||
|
||||
<!-- Apply Steps -->
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Application Steps</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="applySteps">
|
||||
<% (form.apply && form.apply.steps || []).forEach((step, index) => { %>
|
||||
<div class="mb-3 border p-3">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Title</label>
|
||||
<input type="text" class="form-control" name="applyStepTitle_<%= index %>" value="<%= step.title %>">
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<label class="form-label">Description</label>
|
||||
<textarea class="form-control" name="applyStepDescription_<%= index %>" rows="2"><%= step.description %></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% }); %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Application Form Tab -->
|
||||
<div class="tab-pane fade" id="application-form" 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="applicationFormTitle" name="applicationFormTitle" value="<%= form.application_form && form.application_form.title ? form.application_form.title : '' %>">
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<label class="form-label fw-medium">Question</label>
|
||||
<input type="text" class="form-control" id="applicationFormQuestion" name="applicationFormQuestion" value="<%= form.application_form && form.application_form.question ? form.application_form.question : '' %>">
|
||||
</div>
|
||||
|
||||
<!-- Button Section -->
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Button</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Text</label>
|
||||
<input type="text" class="form-control" id="applicationFormButtonText" name="applicationFormButtonText" value="<%= form.application_form && form.application_form.button ? form.application_form.button.text : '' %>">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Icon</label>
|
||||
<input type="text" class="form-control" id="applicationFormButtonIcon" name="applicationFormButtonIcon" value="<%= form.application_form && form.application_form.button ? form.application_form.button.icon : '' %>">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">URL</label>
|
||||
<input type="text" class="form-control" id="applicationFormButtonUrl" name="applicationFormButtonUrl" value="<%= form.application_form && form.application_form.button ? form.application_form.button.url : '' %>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Links Section -->
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Links</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="applicationFormLinks">
|
||||
<% (form.application_form && form.application_form.links || []).forEach((link, index) => { %>
|
||||
<div class="mb-3 border p-3">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Text</label>
|
||||
<input type="text" class="form-control" name="applicationFormLinkText_<%= index %>" value="<%= link.text %>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">URL</label>
|
||||
<input type="text" class="form-control" name="applicationFormLinkUrl_<%= index %>" value="<%= link.url %>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% }); %>
|
||||
</div>
|
||||
</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"></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>
|
||||
|
||||
<!-- Thêm input file ẩn cho upload ảnh -->
|
||||
<input type="file" id="directImageUpload" style="display: none;">
|
||||
<input type="hidden" id="currentImageType" name="imageType">
|
||||
<input type="hidden" id="currentTargetInput" name="targetInput">
|
||||
|
||||
<script>
|
||||
let originalFormData = null;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Lưu trữ dữ liệu gốc để so sánh thay đổi
|
||||
originalFormData = <%- JSON.stringify(form) %>;
|
||||
|
||||
// Khởi tạo form handlers
|
||||
initializeFormHandlers();
|
||||
});
|
||||
|
||||
function initializeFormHandlers() {
|
||||
// Form submission
|
||||
const form = document.querySelector('form');
|
||||
form.addEventListener('submit', handleFormSubmit);
|
||||
|
||||
// Upload ảnh
|
||||
document.querySelectorAll('.btn-upload-image').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const targetInput = this.dataset.targetInput;
|
||||
const imageType = this.dataset.imageType || 'academics';
|
||||
openImageUploader(targetInput, imageType);
|
||||
});
|
||||
});
|
||||
|
||||
// Khởi tạo direct image upload
|
||||
initializeDirectImageUpload();
|
||||
}
|
||||
|
||||
function initializeDirectImageUpload() {
|
||||
const fileInput = document.getElementById('directImageUpload');
|
||||
fileInput.addEventListener('change', handleDirectImageUpload);
|
||||
}
|
||||
|
||||
function openImageUploader(targetInput, imageType) {
|
||||
// Lưu thông tin upload hiện tại
|
||||
document.getElementById('currentImageType').value = imageType;
|
||||
document.getElementById('currentTargetInput').value = targetInput;
|
||||
|
||||
// Kích hoạt input file
|
||||
const fileInput = document.getElementById('directImageUpload');
|
||||
fileInput.click();
|
||||
}
|
||||
|
||||
async function handleDirectImageUpload(e) {
|
||||
if (!this.files || !this.files[0]) return;
|
||||
|
||||
const file = this.files[0];
|
||||
const imageType = document.getElementById('currentImageType').value;
|
||||
const targetInput = document.getElementById('currentTargetInput').value;
|
||||
|
||||
try {
|
||||
// Disable nút upload và hiển thị loading
|
||||
const uploadButton = document.querySelector(`[data-target-input="${targetInput}"]`);
|
||||
if (uploadButton) {
|
||||
uploadButton.disabled = true;
|
||||
uploadButton.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Uploading...';
|
||||
}
|
||||
|
||||
// Tạo form data
|
||||
const formData = new FormData();
|
||||
formData.append('image', file);
|
||||
|
||||
// Upload ảnh
|
||||
const response = await fetch(`/admin/upload/image?imageType=${imageType}`, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success && result.path) {
|
||||
// Tìm input cần cập nhật, ưu tiên theo name, nếu không có thì tìm theo id
|
||||
const inputElement = document.querySelector(`[name="${targetInput}"]`) || document.getElementById(targetInput);
|
||||
if (inputElement) {
|
||||
inputElement.value = result.path;
|
||||
// Cập nhật preview
|
||||
updateImagePreview(inputElement, result.path);
|
||||
}
|
||||
|
||||
showToast('Success', 'Image uploaded successfully', 'success');
|
||||
} else {
|
||||
throw new Error(result.error || 'Upload failed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Upload error:', error);
|
||||
showToast('Error', 'Failed to upload image: ' + error.message, 'error');
|
||||
} finally {
|
||||
// Reset nút upload
|
||||
const uploadButton = document.querySelector(`[data-target-input="${targetInput}"]`);
|
||||
if (uploadButton) {
|
||||
uploadButton.disabled = false;
|
||||
uploadButton.innerHTML = '<i class="fas fa-upload me-1"></i>Upload';
|
||||
}
|
||||
// Reset file input
|
||||
this.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
function updateImagePreview(inputElement, imagePath) {
|
||||
// Tìm hoặc tạo phần tử preview theo cách giống như Partnerships
|
||||
let imgPreview = inputElement.parentElement.nextElementSibling;
|
||||
while (imgPreview && !imgPreview.classList.contains('mt-2')) {
|
||||
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-2';
|
||||
const img = document.createElement('img');
|
||||
img.className = 'img-thumbnail';
|
||||
|
||||
// Set style dựa vào loại ảnh
|
||||
const targetInput = inputElement.name || inputElement.id;
|
||||
if (targetInput.toLowerCase().includes('logo')) {
|
||||
img.style.height = '100px';
|
||||
img.style.objectFit = 'contain';
|
||||
} else {
|
||||
img.style.height = '200px';
|
||||
img.style.width = '100%';
|
||||
img.style.objectFit = 'cover';
|
||||
}
|
||||
|
||||
img.alt = 'Image preview';
|
||||
imgPreview.appendChild(img);
|
||||
inputElement.parentElement.parentElement.appendChild(imgPreview);
|
||||
}
|
||||
|
||||
// Cập nhật đường dẫn hình ảnh
|
||||
const img = imgPreview.querySelector('img');
|
||||
if (img) {
|
||||
img.src = imagePath;
|
||||
}
|
||||
}
|
||||
|
||||
function handleFormSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
try {
|
||||
console.log('=== Form Submit Debug ===');
|
||||
|
||||
// Chuẩn bị dữ liệu form
|
||||
const formData = prepareFormData();
|
||||
console.log('Complete form data:', formData);
|
||||
|
||||
// Log từng section
|
||||
Object.keys(formData).forEach(key => {
|
||||
console.log(`${key} data:`, formData[key]);
|
||||
});
|
||||
|
||||
// Validate dữ liệu
|
||||
const errors = validateFormData(formData);
|
||||
if (errors.length > 0) {
|
||||
console.log('Validation errors:', errors);
|
||||
showToast('Error', errors.join('. '), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Log hidden inputs trước khi cập nhật
|
||||
Object.keys(formData).forEach(key => {
|
||||
const input = document.getElementById(`${key}Json`);
|
||||
console.log(`Hidden input ${key}Json:`, {
|
||||
element: input,
|
||||
value: input?.value
|
||||
});
|
||||
});
|
||||
|
||||
// Cập nhật hidden inputs (map key -> input id)
|
||||
const keyToInputId = {
|
||||
admission: 'admissionJson',
|
||||
apply: 'applyJson',
|
||||
application_form: 'applicationFormJson'
|
||||
};
|
||||
|
||||
Object.keys(formData).forEach(key => {
|
||||
const inputId = keyToInputId[key] || `${key}Json`;
|
||||
const input = document.getElementById(inputId);
|
||||
if (input) {
|
||||
const jsonValue = JSON.stringify(formData[key]);
|
||||
input.value = jsonValue;
|
||||
console.log(`Updated ${inputId} with:`, jsonValue);
|
||||
} else {
|
||||
console.warn(`Hidden input not found: ${inputId}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Thông báo đang lưu
|
||||
showToast('Info', 'Saving changes...', 'info');
|
||||
|
||||
// Log form final state
|
||||
console.log('Form final state:', {
|
||||
admissionJson: document.getElementById('admissionJson')?.value,
|
||||
applyJson: document.getElementById('applyJson')?.value,
|
||||
applicationFormJson: document.getElementById('applicationFormJson')?.value
|
||||
});
|
||||
|
||||
// Submit form
|
||||
this.submit();
|
||||
} catch (error) {
|
||||
console.error('Form submission error:', error);
|
||||
console.error('Error stack:', error.stack);
|
||||
showToast('Error', 'Error processing form: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function prepareFormData() {
|
||||
return {
|
||||
admission: prepareAdmissionData(),
|
||||
apply: prepareApplyData(),
|
||||
application_form: prepareApplicationFormData()
|
||||
};
|
||||
}
|
||||
|
||||
function prepareAdmissionData() {
|
||||
try {
|
||||
// Basic info
|
||||
const data = {
|
||||
background_image: document.getElementById('admissionBgImage')?.value || '',
|
||||
title: document.getElementById('admissionTitle')?.value || '',
|
||||
year: document.getElementById('admissionYear')?.value || '',
|
||||
description: document.getElementById('admissionDescription')?.value || '',
|
||||
form: {
|
||||
fields: [],
|
||||
button: {
|
||||
text: document.getElementById('admissionFormButton')?.value || '',
|
||||
url: document.getElementById('admissionFormButtonUrl')?.value || '#'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Form fields
|
||||
const formFields = document.querySelectorAll('#admissionFormFields > div');
|
||||
if (formFields) {
|
||||
data.form.fields = Array.from(formFields).map(field => {
|
||||
const inputs = field.querySelectorAll('input');
|
||||
return {
|
||||
type: inputs[0]?.value || '',
|
||||
placeholder: inputs[1]?.value || ''
|
||||
};
|
||||
}).filter(field => field.type || field.placeholder);
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error preparing admission data:', error);
|
||||
return {
|
||||
background_image: '',
|
||||
title: '',
|
||||
year: '',
|
||||
description: '',
|
||||
form: {
|
||||
fields: [],
|
||||
button: {
|
||||
text: '',
|
||||
url: '#'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function prepareApplyData() {
|
||||
try {
|
||||
const data = {
|
||||
title: document.getElementById('applyTitle')?.value || '',
|
||||
steps: []
|
||||
};
|
||||
|
||||
// Steps
|
||||
const steps = document.querySelectorAll('#applySteps > div');
|
||||
if (steps) {
|
||||
data.steps = Array.from(steps).map(step => ({
|
||||
title: step.querySelector('input[name^="applyStepTitle_"]')?.value || '',
|
||||
description: step.querySelector('textarea[name^="applyStepDescription_"]')?.value || ''
|
||||
})).filter(step => step.title || step.description);
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error preparing apply data:', error);
|
||||
return {
|
||||
title: '',
|
||||
steps: []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function prepareApplicationFormData() {
|
||||
try {
|
||||
console.log('=== Debug Application Form Data ===');
|
||||
|
||||
// Log các elements
|
||||
console.log('Title element:', document.getElementById('applicationFormTitle'));
|
||||
console.log('Question element:', document.getElementById('applicationFormQuestion'));
|
||||
console.log('Button text element:', document.getElementById('applicationFormButtonText'));
|
||||
console.log('Button icon element:', document.getElementById('applicationFormButtonIcon'));
|
||||
|
||||
const data = {
|
||||
title: document.getElementById('applicationFormTitle')?.value || '',
|
||||
question: document.getElementById('applicationFormQuestion')?.value || '',
|
||||
button: {
|
||||
text: document.getElementById('applicationFormButtonText')?.value || '',
|
||||
icon: document.getElementById('applicationFormButtonIcon')?.value || '',
|
||||
url: document.getElementById('applicationFormButtonUrl')?.value || '#'
|
||||
},
|
||||
links: []
|
||||
};
|
||||
|
||||
// Log giá trị cơ bản
|
||||
console.log('Basic data:', {
|
||||
title: data.title,
|
||||
question: data.question,
|
||||
button: data.button
|
||||
});
|
||||
|
||||
// Debug links
|
||||
const linksContainer = document.getElementById('applicationFormLinks');
|
||||
console.log('Links container:', linksContainer);
|
||||
|
||||
const linkDivs = document.querySelectorAll('#applicationFormLinks > div');
|
||||
console.log('Found link divs:', linkDivs.length);
|
||||
|
||||
if (linkDivs.length > 0) {
|
||||
data.links = Array.from(linkDivs).map((link, index) => {
|
||||
const textInput = link.querySelector('input[name^="applicationFormLinkText_"]');
|
||||
const urlInput = link.querySelector('input[name^="applicationFormLinkUrl_"]');
|
||||
|
||||
return {
|
||||
text: textInput?.value || '',
|
||||
url: urlInput?.value || ''
|
||||
};
|
||||
}).filter(link => link.text || link.url);
|
||||
}
|
||||
|
||||
console.log('Final application form data:', data);
|
||||
return data;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error preparing application form data:', error);
|
||||
console.error('Error stack:', error.stack);
|
||||
return {
|
||||
title: '',
|
||||
question: '',
|
||||
button: {
|
||||
text: '',
|
||||
icon: '',
|
||||
url: '#'
|
||||
},
|
||||
links: []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function validateFormData(data) {
|
||||
const errors = [];
|
||||
|
||||
// Validate admission
|
||||
if (!data.admission.title) errors.push('Admission title is required');
|
||||
if (!data.admission.year) errors.push('Admission year is required');
|
||||
|
||||
// Validate apply
|
||||
if (!data.apply.title) errors.push('Apply title is required');
|
||||
if (!Array.isArray(data.apply.steps)) errors.push('Apply steps must be an array');
|
||||
|
||||
// Validate application form
|
||||
if (!data.application_form.title) errors.push('Application form title is required');
|
||||
if (!data.application_form.question) errors.push('Application form question is required');
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
function showToast(title, message, type = 'info') {
|
||||
if (window.toastManager) {
|
||||
window.toastManager[type](message);
|
||||
return;
|
||||
}
|
||||
|
||||
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>
|
||||
`;
|
||||
|
||||
// Add toast to container
|
||||
let container = document.querySelector('.toast-container');
|
||||
if (!container) {
|
||||
container = document.createElement('div');
|
||||
container.className = 'toast-container position-fixed top-0 end-0 p-3';
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
container.appendChild(toast);
|
||||
|
||||
// Show toast
|
||||
const bsToast = new bootstrap.Toast(toast, {
|
||||
animation: true,
|
||||
autohide: true,
|
||||
delay: 3000
|
||||
});
|
||||
bsToast.show();
|
||||
|
||||
// Remove toast after hidden
|
||||
toast.addEventListener('hidden.bs.toast', () => {
|
||||
toast.remove();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user