forked from UKSOURCE/cms.hailearning.edu.vn
1034 lines
55 KiB
Plaintext
1034 lines
55 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)">
|
|
Visa Management
|
|
</h1>
|
|
<p class="text-muted mb-0">Manage visa information for different countries</p>
|
|
</div>
|
|
<div>
|
|
<button type="button" class="btn btn-primary" id="btnAddCountry">
|
|
<i class="fas fa-plus-circle me-2"></i>Add Country
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<!-- Table View -->
|
|
<div class="card shadow-sm border-0 mb-4" id="tableContainer">
|
|
<div class="card-header bg-white border-bottom">
|
|
<h6 class="mb-0">Countries List</h6>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th style="width: 8%" class="text-center">ID</th>
|
|
<th style="width: 10%" class="text-center">Flag</th>
|
|
<th style="width: 22%">Country Name</th>
|
|
<th style="width: 40%">Services</th>
|
|
<th style="width: 20%" class="text-center">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<% if (data && data.hero && data.hero.summaryList) { %>
|
|
<% data.hero.summaryList.forEach(function(country) { %>
|
|
<tr>
|
|
<td class="text-center align-middle">
|
|
<span class="badge bg-secondary">#<%= String(country.id).padStart(3, "0") %></span>
|
|
</td>
|
|
<td class="text-center align-middle">
|
|
<img src="<%= country.icon %>" alt="<%= country.name %>" style="width: 40px; height: 40px; object-fit: contain;">
|
|
</td>
|
|
<td class="align-middle">
|
|
<strong><%= country.name %></strong>
|
|
<div class="small text-muted">Slug: <%= country.slug %></div>
|
|
</td>
|
|
<td class="align-middle">
|
|
<% country.services.forEach(function(service) { %>
|
|
<span class="badge bg-light text-dark me-1"><%= service %></span>
|
|
<% }); %>
|
|
</td>
|
|
<td class="text-center align-middle">
|
|
<!-- <button class="btn btn-sm btn-outline-info" onclick="viewCountry('<%= country.id %>')" title="View">
|
|
<i class="fas fa-eye"></i>
|
|
</button> -->
|
|
<button class="btn btn-sm btn-outline-warning" onclick="editCountry('<%= country.id %>')" title="Edit">
|
|
<i class="fas fa-edit"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-danger" onclick="deleteCountry('<%= country.id %>')" title="Delete">
|
|
<i class="fas fa-trash-alt"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<% }); %>
|
|
<% } %>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form View -->
|
|
<div class="card shadow-sm border-0 mb-4" id="formContainer" style="display: none;">
|
|
<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-summary" role="tab">
|
|
<i class="fas fa-map-marker-alt me-2"></i>Summary
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#tab-detail" role="tab">
|
|
<i class="fas fa-file-alt me-2"></i>Details
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#tab-contact" role="tab">
|
|
<i class="fas fa-phone-alt me-2"></i>Contact
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
<form id="formCountry">
|
|
<input type="hidden" name="country_id" id="country_id" />
|
|
<div class="tab-content">
|
|
<!-- Tab 1: Summary -->
|
|
<div class="tab-pane fade show active" id="tab-summary" role="tabpanel">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Country Name</label>
|
|
<input type="text" name="name" class="form-control" placeholder="e.g. France" required />
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Country Icon (Flag)</label>
|
|
<div class="input-group mb-2">
|
|
<input type="text" name="icon" id="icon_input" class="form-control" placeholder="/uploads/visa/flag.png" required />
|
|
<button type="button" class="btn btn-outline-secondary" onclick="document.getElementById('fileFlagInput').click()">
|
|
<i class="fas fa-upload"></i>
|
|
</button>
|
|
</div>
|
|
<input type="file" id="fileFlagInput" style="display:none" accept="image/*" />
|
|
<div style="margin-top: 10px;">
|
|
<img id="preview_icon" src="" alt="Icon Preview" style="max-width: 100px; max-height: 100px; object-fit: contain; display: none; border-radius: 4px; border: 1px solid #ddd;">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Visa Services (4 services)</label>
|
|
</div>
|
|
<% for(let i=1; i <=4; i++) { %>
|
|
<div class="col-md-6">
|
|
<input type="text" name="services[]" class="form-control" placeholder="Service <%= i %>" required/>
|
|
</div>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tab 2: Details -->
|
|
<div class="tab-pane fade" id="tab-detail" role="tabpanel">
|
|
<div class="row g-4">
|
|
<!-- Basic Information -->
|
|
<div class="col-md-12">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-header bg-white">
|
|
<h6 class="mb-0"><i class="fas fa-info-circle me-2"></i>Basic Information</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Banner Title</label>
|
|
<input type="text" name="detail_title" class="form-control" placeholder="COUNTRY USA" required/>
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Main Image</label>
|
|
<div class="input-group mb-2">
|
|
<input type="text" id="mainImage_detail" name="mainImage" class="form-control" placeholder="/uploads/visa/details-1.jpg" required />
|
|
<button type="button" class="btn btn-outline-secondary" onclick="document.getElementById('fileDetailMainInput').click()">
|
|
<i class="fas fa-upload"></i>
|
|
</button>
|
|
</div>
|
|
<input type="file" id="fileDetailMainInput" style="display:none" accept="image/*"/>
|
|
<div style="margin-top: 10px;">
|
|
<img id="preview_main_detail" src="" alt="Main Image Preview" style="max-width: 200px; max-height: 150px; object-fit: cover; display: none; border-radius: 4px; border: 1px solid #ddd;">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Main Description</label>
|
|
<textarea name="description" class="form-control" rows="5" required></textarea>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Additional Info</label>
|
|
<textarea name="additionalInfo" class="form-control" rows="5" required></textarea>
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Tagline</label>
|
|
<input type="text" name="tagline" class="form-control" required />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Visa Types -->
|
|
<div class="col-md-12">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-header bg-white">
|
|
<h6 class="mb-0"><i class="fas fa-file-contract me-2"></i>Visa Types</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<% for(let i=1; i<=4; i++) { %>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Type <%= i %> Title</label>
|
|
<input type="text" name="visa_title_<%= i %>" class="form-control" placeholder="e.g. Tourist Visa" required />
|
|
<label class="form-label fw-medium mt-2">Description</label>
|
|
<textarea name="visa_desc_<%= i %>" class="form-control" rows="3" required></textarea>
|
|
</div>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Visa Process -->
|
|
<div class="col-md-12">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-header bg-white">
|
|
<h6 class="mb-0"><i class="fas fa-list-check me-2"></i>Visa Process</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<label class="form-label fw-medium">Process Title</label>
|
|
<input type="text" name="process_title" class="form-control" placeholder="e.g. USA Visa Process" required />
|
|
</div>
|
|
<div class="row g-3">
|
|
<% for(let i=1; i<=5; i++) { %>
|
|
<div class="col-md-12">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center gap-2 mb-2">
|
|
<span class="badge bg-secondary"><%= i %></span>
|
|
<input type="text" name="step_title[]" class="form-control form-control-sm" placeholder="Step <%= i %> Title" required />
|
|
</div>
|
|
<textarea name="step_desc[]" class="form-control form-control-sm" rows="2" placeholder="Step <%= i %> Description" required></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Gallery -->
|
|
<div class="col-md-12">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-header bg-white">
|
|
<h6 class="mb-0"><i class="fas fa-images me-2"></i>Gallery</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<% for(let i=1; i<=2; i++) { %>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Gallery Image <%= i %></label>
|
|
<div class="input-group mb-2">
|
|
<input type="text" class="form-control" id="bannerImageGallery<%= i %>" name="bannerImageGallery" placeholder="https://example.com/image.jpg" required />
|
|
<button type="button" class="btn btn-outline-secondary" onclick="document.getElementById('fileGalleryInput<%= i %>').click()">
|
|
<i class="fas fa-upload"></i>
|
|
</button>
|
|
</div>
|
|
<input type="file" id="fileGalleryInput<%= i %>" style="display:none" accept="image/*">
|
|
<div style="margin-top: 10px;">
|
|
<img id="preview_bannerImageGallery<%= i %>" src="" alt="Gallery Image Preview" style="max-width: 200px; max-height: 150px; object-fit: cover; display: none; border-radius: 4px; border: 1px solid #ddd;">
|
|
</div>
|
|
</div>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Visa Categories -->
|
|
<div class="col-md-12">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-header bg-white">
|
|
<h6 class="mb-0"><i class="fas fa-layer-group me-2"></i>Visa Categories</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<label class="form-label fw-medium">Categories Title</label>
|
|
<input type="text" name="visa_categories_title" class="form-control" placeholder="e.g. Types of USA Visas" required />
|
|
</div>
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium small">Group 1 (3 Items)</label>
|
|
<input type="text" name="category_group1[]" class="form-control form-control-sm mb-2" placeholder="Item 1" required />
|
|
<input type="text" name="category_group1[]" class="form-control form-control-sm mb-2" placeholder="Item 2" required />
|
|
<input type="text" name="category_group1[]" class="form-control form-control-sm" placeholder="Item 3" required />
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium small">Group 2 (2 Items)</label>
|
|
<input type="text" name="category_group2[]" class="form-control form-control-sm mb-2" placeholder="Item 1" required />
|
|
<input type="text" name="category_group2[]" class="form-control form-control-sm" placeholder="Item 2" required />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Service Options -->
|
|
<div class="col-md-12">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-header bg-white">
|
|
<h6 class="mb-0"><i class="fas fa-briefcase me-2"></i>Service Options</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<label class="form-label fw-medium">Service Title</label>
|
|
<input type="text" name="service_main_title" class="form-control" placeholder="e.g. Our Visa Service Options" required />
|
|
</div>
|
|
<div class="row g-3">
|
|
<% for(let i=1; i<=5; i++) { %>
|
|
<div class="col-md-12">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center gap-2 mb-2">
|
|
<span class="badge bg-secondary"><%= i %></span>
|
|
<input type="text" name="service_step_title[]" class="form-control form-control-sm" placeholder="Service <%= i %>" required />
|
|
</div>
|
|
<textarea name="service_step_desc[]" class="form-control form-control-sm" rows="2" placeholder="Description" required></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Related Countries -->
|
|
<div class="col-md-12">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-header bg-white">
|
|
<h6 class="mb-0"><i class="fas fa-globe me-2"></i>Related Countries (7)</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<% for(let i=0; i<7; i++) { %>
|
|
<div class="col-md-6">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<div class="row g-2">
|
|
<div class="col-md-7">
|
|
<label class="form-label fw-medium small">Country Name</label>
|
|
<input type="text" name="related_name[]" class="form-control form-control-sm" placeholder="e.g. Japan" required />
|
|
</div>
|
|
<div class="col-md-5">
|
|
<label class="form-label fw-medium small">Icon</label>
|
|
<div class="input-group input-group-sm">
|
|
<input type="text" name="related_icon[]" id="related_url_<%= i %>" class="form-control form-control-sm" placeholder="/uploads/visa/icon.png" required />
|
|
<input type="file" id="related_file_<%= i %>" class="d-none" accept="image/*">
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="document.getElementById('related_file_<%= i %>').click()">
|
|
<i class="fas fa-upload"></i>
|
|
</button>
|
|
</div>
|
|
<div style="margin-top: 5px;">
|
|
<img id="related_preview_<%= i %>" src="" alt="preview" style="max-width: 60px; max-height: 60px; object-fit: contain; display: none; border-radius: 4px; border: 1px solid #ddd;">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tab 3: Contact -->
|
|
<div class="tab-pane fade" id="tab-contact" role="tabpanel">
|
|
<div class="row g-3">
|
|
<div class="col-md-12">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-header bg-white">
|
|
<h6 class="mb-0"><i class="fas fa-phone-alt me-2"></i>Contact Information</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Image Contact</label>
|
|
<div class="input-group mb-2">
|
|
<input type="text" id="contact_image_input" name="contact_image" class="form-control" placeholder="/uploads/visa/contact-image.jpg" required />
|
|
<button type="button" class="btn btn-outline-secondary" onclick="document.getElementById('fileContactImageInput').click()">
|
|
<i class="fas fa-upload"></i>
|
|
</button>
|
|
</div>
|
|
<input type="file" id="fileContactImageInput" style="display:none" accept="image/*"/>
|
|
<div style="margin-top: 10px;">
|
|
<img id="preview_contact_image" src="" alt="Contact Image Preview" style="max-width: 200px; max-height: 150px; object-fit: cover; display: none; border-radius: 4px; border: 1px solid #ddd;">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Section Title</label>
|
|
<input type="text" name="contact_sectionTitle" class="form-control" placeholder="e.g. Visa & Immigration" required/>
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Help Text</label>
|
|
<input type="text" name="contact_helpText" class="form-control" placeholder="e.g. Need Help?" required/>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label fw-medium">Phone Label</label>
|
|
<input type="text" name="contact_phone_label" class="form-control" placeholder="Call Us" required />
|
|
<label class="form-label fw-medium mt-2">Phone Number</label>
|
|
<input type="text" name="contact_phone" class="form-control" placeholder="+009 438 222 9540" required />
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label fw-medium">Email Label</label>
|
|
<input type="text" name="contact_email_label" class="form-control" placeholder="Mail Us" required />
|
|
<label class="form-label fw-medium mt-2">Email Address</label>
|
|
<input type="email" name="contact_email" class="form-control" placeholder="info@example.com" required />
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label fw-medium">Location Label</label>
|
|
<input type="text" name="contact_location_label" class="form-control" placeholder="Location" required />
|
|
<label class="form-label fw-medium mt-2">Address</label>
|
|
<input type="text" name="contact_location" class="form-control" placeholder="City, Country" required />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="card-footer bg-light d-flex gap-2 justify-content-end">
|
|
<button type="button" class="btn btn-secondary" id="btnBackToList">
|
|
<i class="fas fa-arrow-left me-2"></i>Back to List
|
|
</button>
|
|
<button type="submit" form="formCountry" class="btn btn-primary">
|
|
<i class="fas fa-save me-2"></i>Save
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- View Info -->
|
|
<div class="card shadow-sm border-0 mb-4" id="viewContainer" style="display: none;">
|
|
<div class="card-header bg-white border-bottom">
|
|
<h6 class="mb-0">Country Information</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Country Name</label>
|
|
<div class="alert alert-light mb-0" id="view-name">-</div>
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Services</label>
|
|
<div class="alert alert-light mb-0" id="view-services">-</div>
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label fw-medium">Description</label>
|
|
<div class="alert alert-light mb-0" id="view-description">-</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Phone</label>
|
|
<div class="alert alert-light mb-0" id="view-phone">-</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Email</label>
|
|
<div class="alert alert-light mb-0" id="view-email">-</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer bg-light d-flex gap-2 justify-content-end">
|
|
<button type="button" class="btn btn-secondary" id="btnBackFromView">
|
|
<i class="fas fa-arrow-left me-2"></i>Back to List
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let countriesData = {};
|
|
|
|
// Image upload handler
|
|
function setupImageUploadHandlers() {
|
|
const imageInputs = ["fileFlagInput", "fileDetailMainInput", "fileGalleryInput1", "fileGalleryInput2", "fileContactImageInput"];
|
|
const previewMap = {
|
|
fileFlagInput: "preview_icon",
|
|
fileDetailMainInput: "preview_main_detail",
|
|
fileGalleryInput1: "preview_bannerImageGallery1",
|
|
fileGalleryInput2: "preview_bannerImageGallery2",
|
|
fileContactImageInput: "preview_contact_image"
|
|
};
|
|
const inputMap = {
|
|
fileFlagInput: "icon_input",
|
|
fileDetailMainInput: "mainImage_detail",
|
|
fileGalleryInput1: "bannerImageGallery1",
|
|
fileGalleryInput2: "bannerImageGallery2",
|
|
fileContactImageInput: "contact_image_input"
|
|
};
|
|
|
|
imageInputs.forEach((fileId) => {
|
|
const fileEl = document.getElementById(fileId);
|
|
if (!fileEl) return;
|
|
|
|
fileEl.addEventListener("change", function () {
|
|
const file = this.files[0];
|
|
if (!file) return;
|
|
|
|
if (!file.type.startsWith('image/')) {
|
|
showNotification('Please select a valid image file (JPG, PNG, GIF, etc.)', 'error');
|
|
return;
|
|
}
|
|
|
|
// Show preview immediately with base64
|
|
const reader = new FileReader();
|
|
const previewId = previewMap[fileId];
|
|
const preview = document.getElementById(previewId);
|
|
|
|
reader.onload = function (e) {
|
|
if (preview) {
|
|
preview.src = e.target.result;
|
|
preview.style.display = "block";
|
|
}
|
|
};
|
|
reader.readAsDataURL(file);
|
|
|
|
if (typeof uploadImageToServer === "function") {
|
|
uploadImageToServer(file, fileId, inputMap[fileId], previewId);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function uploadImageToServer(file, fileId, inputId, previewId) {
|
|
const formData = new FormData();
|
|
formData.append('image', file);
|
|
formData.append('imageType', 'visa');
|
|
|
|
fetch('/admin/upload/image?imageType=visa', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
const inputField = document.getElementById(inputId);
|
|
if (inputField) {
|
|
inputField.value = data.path || `/uploads/visa/${file.name}`;
|
|
}
|
|
// Update preview image
|
|
const preview = document.getElementById(previewId);
|
|
if (preview) {
|
|
preview.src = data.path || `/uploads/visa/${file.name}`;
|
|
preview.style.display = "block";
|
|
}
|
|
} else {
|
|
showNotification('Upload error: ' + (data.error || 'Unknown error'), 'error');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Upload failed:', error);
|
|
showNotification('Connection error: ' + error.message, 'error');
|
|
});
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
for (let i = 0; i < 7; i++) {
|
|
const fileInput = document.getElementById(`related_file_${i}`);
|
|
if (fileInput) {
|
|
fileInput.addEventListener("change", function () {
|
|
const file = this.files[0];
|
|
const urlInput = document.getElementById(`related_url_${i}`);
|
|
const preview = document.getElementById(`related_preview_${i}`);
|
|
|
|
if (file) {
|
|
if (!file.type.startsWith('image/')) {
|
|
showNotification('Please select a valid image file (JPG, PNG, GIF, etc.)', 'error');
|
|
return;
|
|
}
|
|
|
|
// Show preview immediately with base64
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => {
|
|
if (preview) {
|
|
preview.src = e.target.result;
|
|
preview.style.display = "block";
|
|
}
|
|
};
|
|
reader.readAsDataURL(file);
|
|
|
|
const formData = new FormData();
|
|
formData.append('image', file);
|
|
formData.append('imageType', 'visa');
|
|
|
|
fetch('/admin/upload/image?imageType=visa', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success && urlInput) {
|
|
urlInput.value = data.path || `/uploads/visa/${file.name}`;
|
|
// Update preview with server path
|
|
if (preview) {
|
|
preview.src = data.path || `/uploads/visa/${file.name}`;
|
|
}
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Upload failed:', error);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
setupImageUploadHandlers();
|
|
});
|
|
|
|
function showListView() {
|
|
document.getElementById("tableContainer").style.display = "block";
|
|
document.getElementById("formContainer").style.display = "none";
|
|
document.getElementById("viewContainer").style.display = "none";
|
|
}
|
|
|
|
function showFormView() {
|
|
document.getElementById("tableContainer").style.display = "none";
|
|
document.getElementById("formContainer").style.display = "block";
|
|
document.getElementById("viewContainer").style.display = "none";
|
|
}
|
|
|
|
function showViewView() {
|
|
document.getElementById("tableContainer").style.display = "none";
|
|
document.getElementById("formContainer").style.display = "none";
|
|
document.getElementById("viewContainer").style.display = "block";
|
|
}
|
|
|
|
function viewCountry(countryId) {
|
|
const country = countriesData[countryId];
|
|
if (country) {
|
|
document.getElementById("view-name").textContent = country.name || "-";
|
|
document.getElementById("view-services").textContent = country.services.join(", ") || "-";
|
|
|
|
if (country.detailedView && country.detailedView.activeCountry) {
|
|
const details = country.detailedView.activeCountry;
|
|
document.getElementById("view-description").textContent = details.description || "-";
|
|
document.getElementById("view-phone").textContent = details.contactInfo?.phone?.value || "-";
|
|
document.getElementById("view-email").textContent = details.contactInfo?.email?.value || "-";
|
|
}
|
|
|
|
showViewView();
|
|
}
|
|
}
|
|
|
|
async function editCountry(countryId) {
|
|
try {
|
|
const response = await fetch(`/admin/visa/edit/${countryId}`);
|
|
const result = await response.json();
|
|
|
|
if (!result.success) {
|
|
showNotification('Error: ' + result.error, 'error');
|
|
return;
|
|
}
|
|
|
|
const country = result.country;
|
|
|
|
document.getElementById("country_id").value = country.id;
|
|
document.querySelector('input[name="name"]').value = country.name || "";
|
|
document.getElementById("icon_input").value = country.icon || "";
|
|
|
|
// Update icon preview
|
|
if (country.icon) {
|
|
const iconPreview = document.getElementById("preview_icon");
|
|
if (iconPreview) {
|
|
iconPreview.src = country.icon;
|
|
iconPreview.style.display = "block";
|
|
}
|
|
}
|
|
|
|
const serviceInputs = document.querySelectorAll('input[name="services[]"]');
|
|
serviceInputs.forEach(input => input.value = "");
|
|
if (country.services) {
|
|
country.services.forEach((service, index) => {
|
|
if (serviceInputs[index]) serviceInputs[index].value = service;
|
|
});
|
|
}
|
|
|
|
const details = country.detailedView?.activeCountry;
|
|
if (details) {
|
|
document.querySelector('input[name="detail_title"]').value = details.title || "";
|
|
document.getElementById("mainImage_detail").value = details.mainImage || "";
|
|
|
|
// Update preview for main image
|
|
if (details.mainImage) {
|
|
const mainPreview = document.getElementById("preview_main_detail");
|
|
if (mainPreview) {
|
|
mainPreview.src = details.mainImage;
|
|
mainPreview.style.display = "block";
|
|
}
|
|
}
|
|
|
|
document.querySelector('textarea[name="description"]').value = details.description || "";
|
|
document.querySelector('textarea[name="additionalInfo"]').value = details.additionalInfo || "";
|
|
document.querySelector('input[name="tagline"]').value = details.tagline || "";
|
|
|
|
if (details.visaTypes) {
|
|
for(let i=1; i<=4; i++) {
|
|
const typeIdx = Math.floor((i-1)/2);
|
|
const itemIdx = (i-1) % 2;
|
|
document.querySelector(`input[name="visa_title_${i}"]`).value = details.visaTypes[typeIdx]?.items[itemIdx]?.title || "";
|
|
document.querySelector(`textarea[name="visa_desc_${i}"]`).value = details.visaTypes[typeIdx]?.items[itemIdx]?.description || "";
|
|
}
|
|
}
|
|
|
|
document.querySelector('input[name="process_title"]').value = details.visaProcess?.title || "";
|
|
const stepTitles = document.querySelectorAll('input[name="step_title[]"]');
|
|
const stepDescs = document.querySelectorAll('textarea[name="step_desc[]"]');
|
|
stepTitles.forEach(i => i.value = "");
|
|
stepDescs.forEach(i => i.value = "");
|
|
if (details.visaProcess?.steps) {
|
|
details.visaProcess.steps.forEach((step, index) => {
|
|
if (stepTitles[index]) stepTitles[index].value = step.title || "";
|
|
if (stepDescs[index]) stepDescs[index].value = step.description || "";
|
|
});
|
|
}
|
|
|
|
const galleryData = details.gallery || [];
|
|
const galleryInputs = document.querySelectorAll('input[name="bannerImageGallery"]');
|
|
galleryInputs.forEach((input, index) => {
|
|
input.value = galleryData[index] || "";
|
|
|
|
// Update gallery preview images
|
|
const previewId = `preview_bannerImageGallery${index + 1}`;
|
|
const preview = document.getElementById(previewId);
|
|
if (preview && galleryData[index]) {
|
|
preview.src = galleryData[index];
|
|
preview.style.display = "block";
|
|
}
|
|
});
|
|
|
|
const categories = details.visaCategories;
|
|
document.querySelector('input[name="visa_categories_title"]').value = categories?.title || "";
|
|
const group1Inputs = document.querySelectorAll('input[name="category_group1[]"]');
|
|
const group2Inputs = document.querySelectorAll('input[name="category_group2[]"]');
|
|
if (categories && Array.isArray(categories.steps)) {
|
|
const g1 = categories.steps[0];
|
|
if (g1) {
|
|
for (let i = 0; i <= 2; i++) {
|
|
if (group1Inputs[i]) group1Inputs[i].value = g1[i] || "";
|
|
}
|
|
}
|
|
const g2 = categories.steps[1];
|
|
if (g2) {
|
|
for (let i = 0; i <= 1; i++) {
|
|
if (group2Inputs[i]) group2Inputs[i].value = g2[i] || "";
|
|
}
|
|
}
|
|
}
|
|
|
|
const serviceOptions = details.visaService;
|
|
document.querySelector('input[name="service_main_title"]').value = serviceOptions?.title || "";
|
|
const serviceStepTitles = document.querySelectorAll('input[name="service_step_title[]"]');
|
|
const serviceStepDescs = document.querySelectorAll('textarea[name="service_step_desc[]"]');
|
|
const servicesData = serviceOptions?.steps || [];
|
|
serviceStepTitles.forEach((inputEl, index) => {
|
|
const stepData = servicesData[index];
|
|
inputEl.value = stepData?.title || "";
|
|
if (serviceStepDescs[index]) {
|
|
serviceStepDescs[index].value = stepData?.description || "";
|
|
}
|
|
});
|
|
|
|
const relatedCountries = country.detailedView?.relatedCountries || [];
|
|
const relatedNameInputs = document.querySelectorAll('input[name="related_name[]"]');
|
|
const relatedIconInputs = document.querySelectorAll('input[name="related_icon[]"]');
|
|
relatedNameInputs.forEach(input => input.value = "");
|
|
relatedIconInputs.forEach(input => input.value = "");
|
|
relatedCountries.forEach((country, index) => {
|
|
if (index < 7) {
|
|
if (relatedNameInputs[index]) relatedNameInputs[index].value = country.name || "";
|
|
if (relatedIconInputs[index]) relatedIconInputs[index].value = country.icon || "";
|
|
|
|
// Update related country preview images
|
|
const preview = document.getElementById(`related_preview_${index}`);
|
|
if (preview && country.icon) {
|
|
preview.src = country.icon;
|
|
preview.style.display = "block";
|
|
}
|
|
}
|
|
});
|
|
|
|
const contactInfo = country.detailedView?.contactInfo;
|
|
if (contactInfo) {
|
|
document.querySelector('input[name="contact_image"]').value = contactInfo.img || "";
|
|
|
|
// Update contact image preview
|
|
if (contactInfo.img) {
|
|
const contactPreview = document.getElementById("preview_contact_image");
|
|
if (contactPreview) {
|
|
contactPreview.src = contactInfo.img;
|
|
contactPreview.style.display = "block";
|
|
}
|
|
}
|
|
document.querySelector('input[name="contact_image"]').value = contactInfo.img || "";
|
|
document.querySelector('input[name="contact_sectionTitle"]').value = contactInfo.sectionTitle || "";
|
|
document.querySelector('input[name="contact_helpText"]').value = contactInfo.helpText || "";
|
|
document.querySelector('input[name="contact_phone_label"]').value = contactInfo.phone?.label || "";
|
|
document.querySelector('input[name="contact_phone"]').value = contactInfo.phone?.value || "";
|
|
document.querySelector('input[name="contact_email_label"]').value = contactInfo.email?.label || "";
|
|
document.querySelector('input[name="contact_email"]').value = contactInfo.email?.value || "";
|
|
document.querySelector('input[name="contact_location_label"]').value = contactInfo.location?.label || "";
|
|
document.querySelector('input[name="contact_location"]').value = contactInfo.location?.address || "";
|
|
|
|
}
|
|
}
|
|
|
|
showFormView();
|
|
|
|
} catch (error) {
|
|
console.error("Error:", error);
|
|
showNotification('Cannot connect to server. Please try again.', 'error');
|
|
}
|
|
}
|
|
|
|
async function deleteCountry(countryId) {
|
|
const confirmModal = document.createElement('div');
|
|
confirmModal.id = 'deleteConfirmModal';
|
|
confirmModal.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; z-index: 9999;';
|
|
confirmModal.innerHTML = `
|
|
<div style="background: white; padding: 25px; border-radius: 8px; max-width: 380px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); border-left: 4px solid #d9534f;">
|
|
<h5 style="color: #d9534f; margin: 0 0 15px 0; font-size: 16px; font-weight: 600;">Delete Country</h5>
|
|
<p style="color: #666; margin-bottom: 25px; font-size: 14px; line-height: 1.5;">Are you sure you want to delete this country? This action cannot be undone.</p>
|
|
<div style="display: flex; gap: 10px; justify-content: flex-end;">
|
|
<button class="cancelBtn" onclick="document.getElementById('deleteConfirmModal').remove()" style="padding: 10px 18px; border: 1px solid #ddd; background: #f5f5f5; color: #333; border-radius: 4px; cursor: pointer; font-weight: 500; font-size: 14px;">Cancel</button>
|
|
<button class="deleteBtn" onclick="performDelete(${countryId})" style="padding: 10px 18px; background: #d9534f; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: 500; font-size: 14px;">Delete</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
document.body.appendChild(confirmModal);
|
|
}
|
|
|
|
async function performDelete(countryId) {
|
|
const confirmModal = document.getElementById('deleteConfirmModal');
|
|
const deleteBtn = confirmModal.querySelector('.deleteBtn');
|
|
const cancelBtn = confirmModal.querySelector('.cancelBtn');
|
|
|
|
deleteBtn.disabled = true;
|
|
cancelBtn.disabled = true;
|
|
deleteBtn.style.opacity = '0.6';
|
|
deleteBtn.innerHTML = '<span style="display: inline-block; width: 12px; height: 12px; border: 2px solid white; border-top: transparent; border-radius: 50%; animation: spin 0.8s linear infinite; margin-right: 5px;"></span>Deleting...';
|
|
|
|
try {
|
|
const response = await fetch(`/admin/visa/delete/${countryId}`, {
|
|
method: 'DELETE',
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
confirmModal.remove();
|
|
showNotification(result.message || 'Country deleted successfully!', 'success');
|
|
setTimeout(() => location.reload(), 1500);
|
|
} else {
|
|
deleteBtn.disabled = false;
|
|
cancelBtn.disabled = false;
|
|
deleteBtn.style.opacity = '1';
|
|
deleteBtn.innerHTML = 'Delete';
|
|
showNotification('Error: ' + (result.error || 'Cannot delete this country'), 'error');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error("Error:", error.message);
|
|
deleteBtn.disabled = false;
|
|
cancelBtn.disabled = false;
|
|
deleteBtn.style.opacity = '1';
|
|
deleteBtn.innerHTML = 'Delete';
|
|
showNotification('Connection error: ' + error.message, 'error');
|
|
}
|
|
}
|
|
|
|
document.getElementById("btnAddCountry").addEventListener("click", function () {
|
|
document.getElementById("formCountry").reset();
|
|
document.getElementById("country_id").value = "";
|
|
|
|
// Clear all preview images
|
|
document.getElementById("preview_icon").style.display = "none";
|
|
document.getElementById("preview_main_detail").style.display = "none";
|
|
document.getElementById("preview_bannerImageGallery1").style.display = "none";
|
|
document.getElementById("preview_bannerImageGallery2").style.display = "none";
|
|
document.getElementById("preview_contact_image").style.display = "none";
|
|
|
|
// Clear related countries previews
|
|
for (let i = 0; i < 7; i++) {
|
|
const preview = document.getElementById(`related_preview_${i}`);
|
|
if (preview) {
|
|
preview.style.display = "none";
|
|
preview.src = "";
|
|
}
|
|
}
|
|
|
|
const relatedNameInputs = document.querySelectorAll('input[name="related_name[]"]');
|
|
const relatedIconInputs = document.querySelectorAll('input[name="related_icon[]"]');
|
|
relatedNameInputs.forEach(input => input.value = "");
|
|
relatedIconInputs.forEach(input => input.value = "");
|
|
|
|
const contactFields = ["contact_image", "contact_sectionTitle", "contact_helpText", "contact_phone_label", "contact_phone", "contact_email_label", "contact_email", "contact_location_label", "contact_location"];
|
|
contactFields.forEach(fieldName => {
|
|
const field = document.querySelector(`input[name="${fieldName}"]`);
|
|
if (field) field.value = "";
|
|
});
|
|
|
|
|
|
showFormView();
|
|
});
|
|
|
|
document.getElementById("btnBackToList").addEventListener("click", showListView);
|
|
document.getElementById("btnBackFromView").addEventListener("click", showListView);
|
|
|
|
document.getElementById('formCountry').addEventListener('submit', async function (e) {
|
|
e.preventDefault();
|
|
|
|
// Validate required fields
|
|
const requiredInputs = document.querySelectorAll('#formCountry input[required], #formCountry textarea[required]');
|
|
let emptyFields = [];
|
|
|
|
requiredInputs.forEach(input => {
|
|
if (!input.value || input.value.trim() === '') {
|
|
const label = input.parentElement.querySelector('.form-label')?.textContent || input.name;
|
|
emptyFields.push(label);
|
|
}
|
|
});
|
|
|
|
if (emptyFields.length > 0) {
|
|
showNotification(`Please fill in all required fields: ${emptyFields.join(', ')}`, 'warning');
|
|
return;
|
|
}
|
|
|
|
const btnSave = document.querySelector('.btn-primary');
|
|
const countryId = document.getElementById("country_id").value;
|
|
const galleryArray = Array.from(document.querySelectorAll('input[name="bannerImageGallery"]'))
|
|
.map(input => input.value)
|
|
.filter(url => url.trim() !== "");
|
|
const serviceSteps = Array.from(document.querySelectorAll('input[name="service_step_title[]"]'))
|
|
.map((input, index) => {
|
|
const title = input.value.trim();
|
|
const description = document.querySelectorAll('textarea[name="service_step_desc[]"]')[index].value.trim();
|
|
return title ? { title, description } : null;
|
|
})
|
|
.filter(item => item !== null);
|
|
|
|
const payload = {
|
|
id: countryId || null,
|
|
name: document.querySelector('input[name="name"]').value,
|
|
icon: document.querySelector('input[name="icon"]').value,
|
|
services: Array.from(document.querySelectorAll('input[name="services[]"]')).map(i => i.value),
|
|
detailedView: {
|
|
activeCountry: {
|
|
id: countryId || null,
|
|
title: document.querySelector('input[name="detail_title"]').value,
|
|
mainImage: document.getElementById('mainImage_detail').value || "",
|
|
description: document.querySelector('textarea[name="description"]').value,
|
|
additionalInfo: document.querySelector('textarea[name="additionalInfo"]').value,
|
|
tagline: document.querySelector('input[name="tagline"]').value,
|
|
visaTypes: [
|
|
{
|
|
items: [
|
|
{ title: document.querySelector('input[name="visa_title_1"]').value, description: document.querySelector('textarea[name="visa_desc_1"]').value },
|
|
{ title: document.querySelector('input[name="visa_title_2"]').value, description: document.querySelector('textarea[name="visa_desc_2"]').value }
|
|
]
|
|
},
|
|
{
|
|
items: [
|
|
{ title: document.querySelector('input[name="visa_title_3"]').value, description: document.querySelector('textarea[name="visa_desc_3"]').value },
|
|
{ title: document.querySelector('input[name="visa_title_4"]').value, description: document.querySelector('textarea[name="visa_desc_4"]').value }
|
|
]
|
|
}
|
|
],
|
|
visaProcess: {
|
|
title: document.querySelector('input[name="process_title"]').value,
|
|
steps: Array.from(document.querySelectorAll('input[name="step_title[]"]')).map((input, index) => ({
|
|
title: input.value,
|
|
description: document.querySelectorAll('textarea[name="step_desc[]"]')[index].value
|
|
}))
|
|
},
|
|
gallery: galleryArray,
|
|
visaCategories: {
|
|
title: document.querySelector('input[name="visa_categories_title"]').value,
|
|
steps: [
|
|
Array.from(document.querySelectorAll('input[name="category_group1[]"]')).map(input => input.value),
|
|
Array.from(document.querySelectorAll('input[name="category_group2[]"]')).map(input => input.value)
|
|
]
|
|
},
|
|
visaService: {
|
|
title: document.querySelector('input[name="service_main_title"]').value.trim(),
|
|
steps: serviceSteps
|
|
}
|
|
},
|
|
relatedCountries: Array.from({ length: 7 }).map((_, index) => ({
|
|
name: document.querySelectorAll('input[name="related_name[]"]')[index]?.value || "",
|
|
icon: document.getElementById(`related_url_${index}`)?.value || ""
|
|
})),
|
|
contactInfo: {
|
|
img: document.querySelector('input[name="contact_image"]').value,
|
|
sectionTitle: document.querySelector('input[name="contact_sectionTitle"]').value,
|
|
helpText: document.querySelector('input[name="contact_helpText"]').value,
|
|
phone: {
|
|
label: document.querySelector('input[name="contact_phone_label"]').value,
|
|
value: document.querySelector('input[name="contact_phone"]').value
|
|
},
|
|
email: {
|
|
label: document.querySelector('input[name="contact_email_label"]').value,
|
|
value: document.querySelector('input[name="contact_email"]').value
|
|
},
|
|
location: {
|
|
label: document.querySelector('input[name="contact_location_label"]').value,
|
|
address: document.querySelector('input[name="contact_location"]').value
|
|
}
|
|
}
|
|
}
|
|
};
|
|
console.log('Payload:', payload);
|
|
|
|
try {
|
|
btnSave.disabled = true;
|
|
btnSave.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Saving...';
|
|
|
|
const method = countryId ? 'PUT' : 'POST';
|
|
const url = countryId ? `/admin/visa/update/${countryId}` : '/admin/visa/add';
|
|
|
|
const response = await fetch(url, {
|
|
method: method,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(payload)
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
showNotification('Country saved successfully!', 'success');
|
|
setTimeout(() => location.reload(), 1500);
|
|
} else {
|
|
throw new Error(result.error || "Unknown error");
|
|
}
|
|
} catch (err) {
|
|
showNotification('Error: ' + err.message, 'error');
|
|
} finally {
|
|
btnSave.disabled = false;
|
|
btnSave.innerHTML = '<i class="fas fa-save me-2"></i>Save';
|
|
}
|
|
});
|
|
</script> |