forked from UKSOURCE/cms.hailearning.edu.vn
1276 lines
74 KiB
Plaintext
1276 lines
74 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 Us 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 Us Page
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<form method="POST" class="content-with-fixed-buttons" id="aboutUsForm"
|
|
action="<%= data && data._id ? ('/admin/about-us/' + data._id + '/update') : '#' %>">
|
|
<!-- Hidden inputs for JSON data -->
|
|
<input type="hidden" name="hero" id="heroJson">
|
|
<input type="hidden" name="introduction" id="introductionJson">
|
|
<input type="hidden" name="statistics" id="statisticsJson">
|
|
<input type="hidden" name="accommodation" id="accommodationJson">
|
|
<input type="hidden" name="activities" id="activitiesJson">
|
|
<input type="hidden" name="newsletter" id="newsletterJson">
|
|
<input type="hidden" name="events" id="eventsJson">
|
|
|
|
<!-- 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="#hero" role="tab">
|
|
<i class="fas fa-home me-2"></i>Hero
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#stats" role="tab">
|
|
<i class="fas fa-chart-bar me-2"></i>Stats
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#introduce" role="tab">
|
|
<i class="fas fa-info-circle me-2"></i>Introduce
|
|
</a>
|
|
</li>
|
|
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#activities" role="tab">
|
|
<i class="fas fa-running me-2"></i>Activities
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#newsletter" role="tab">
|
|
<i class="fas fa-envelope me-2"></i>Newsletter
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#features" role="tab">
|
|
<i class="fas fa-star me-2"></i>Features
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" data-bs-toggle="tab" href="#events" role="tab">
|
|
<i class="fas fa-calendar-alt me-2"></i>Events
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
<div class="tab-content">
|
|
<!-- Hero Tab -->
|
|
<div class="tab-pane fade show active" id="hero" 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">Background Image</label>
|
|
<div class="input-group mb-2">
|
|
<input type="text" class="form-control" id="heroBackgroundImage"
|
|
name="heroBackgroundImage" value="<%= data.hero?.backgroundImage || '' %>">
|
|
<button type="button"
|
|
class="btn btn-outline-primary btn-upload-image"
|
|
data-target-input="heroBackgroundImage" data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-7">
|
|
<% if (data.hero?.backgroundImage) { %>
|
|
<img src="<%= data.hero.backgroundImage %>" class="img-thumbnail uploaded-preview"
|
|
style="height: 300px; width: 100%; object-fit: cover;"
|
|
alt="Background 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="heroTitle" name="heroTitle"
|
|
value="<%= data.hero?.title || '' %>">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats Tab -->
|
|
<div class="tab-pane fade" id="stats" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h6 class="fw-medium mb-0">Statistics</h6>
|
|
<button type="button" class="btn btn-primary btn-sm" onclick="addStat()">
|
|
<i class="fas fa-plus"></i> Add Stat
|
|
</button>
|
|
</div>
|
|
<div id="statsContainer">
|
|
<% if (data.statistics && data.statistics.items && data.statistics.items.length > 0) { %>
|
|
<% data.statistics.items.forEach((stat, index)=> { %>
|
|
<div class="card mb-3 stat-item">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label">Number</label>
|
|
<input type="text" class="form-control"
|
|
name="statNumber_<%= index %>"
|
|
value="<%= stat.number %>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label">Description</label>
|
|
<input type="text" class="form-control"
|
|
name="statDescription_<%= index %>"
|
|
value="<%= stat.description %>">
|
|
</div>
|
|
</div>
|
|
<button type="button"
|
|
class="btn btn-outline-danger btn-sm mt-3"
|
|
onclick="removeStat(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Stat
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Introduce Tab -->
|
|
<div class="tab-pane fade" id="introduce" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Subtitle</label>
|
|
<input type="text" class="form-control" id="introSubtitle"
|
|
value="<%= data.introduction?.subtitle || '' %>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-medium">Title</label>
|
|
<input type="text" class="form-control" id="introTitle"
|
|
value="<%= data.introduction?.title || '' %>">
|
|
</div>
|
|
</div>
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<label class="form-label fw-medium">Description</label>
|
|
<textarea id="introDescription" class="form-control"
|
|
rows="4"><%= data.introduction?.description || '' %></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="row mt-3">
|
|
<div class="col-md-8">
|
|
<label class="form-label fw-medium">Main Image</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" id="introMainImage"
|
|
value="<%= data.introduction?.mainImage || '' %>">
|
|
<button type="button"
|
|
class="btn btn-outline-primary btn-upload-image"
|
|
data-target-input="introMainImage" data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<% if (data.introduction?.mainImage) { %>
|
|
<img src="<%= data.introduction.mainImage %>" class="img-thumbnail uploaded-preview"
|
|
style="height: 120px; width: 100%; object-fit: cover;"
|
|
alt="Main image preview">
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="my-4">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h6 class="fw-medium mb-0">Services</h6>
|
|
<button type="button" class="btn btn-primary btn-sm" onclick="addService()">
|
|
<i class="fas fa-plus"></i> Add Service
|
|
</button>
|
|
</div>
|
|
<div id="servicesContainer">
|
|
<% if (data.introduction?.services && data.introduction.services.length > 0) { %>
|
|
<% data.introduction.services.forEach((service, index)=> { %>
|
|
<div class="card mb-3 service-item">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Title</label>
|
|
<input type="text" class="form-control"
|
|
name="serviceTitle_<%= index %>"
|
|
value="<%= service.title %>">
|
|
</div>
|
|
<div class="col-md-8">
|
|
<label class="form-label">Description</label>
|
|
<textarea class="form-control"
|
|
name="serviceDescription_<%= index %>"
|
|
rows="2"><%= service.description %></textarea>
|
|
</div>
|
|
</div>
|
|
<button type="button"
|
|
class="btn btn-outline-danger btn-sm mt-3"
|
|
onclick="removeService(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Service
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<% }) %>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Activities Tab -->
|
|
<div class="tab-pane fade" id="activities" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<h6 class="fw-medium mb-3">Activities Header</h6>
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Subtitle</label>
|
|
<input type="text" class="form-control" id="activitiesSubtitle"
|
|
value="<%= data.activities?.subtitle || '' %>">
|
|
</div>
|
|
<div class="col-md-8">
|
|
<label class="form-label">Title</label>
|
|
<input type="text" class="form-control" id="activitiesTitle"
|
|
value="<%= data.activities?.title || '' %>">
|
|
</div>
|
|
</div>
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<label class="form-label">Description</label>
|
|
<textarea id="activitiesDescription" class="form-control"
|
|
rows="3"><%= data.activities?.description || '' %></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="my-4">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h6 class="fw-medium mb-0">Gallery Items</h6>
|
|
<button type="button" class="btn btn-primary btn-sm"
|
|
onclick="addGalleryItem()">
|
|
<i class="fas fa-plus"></i> Add Gallery Item
|
|
</button>
|
|
</div>
|
|
<div id="activitiesGalleryContainer">
|
|
<% if (data.activities?.gallery && data.activities.gallery.length > 0) { %>
|
|
<% data.activities.gallery.forEach((item, index)=> { %>
|
|
<div class="card mb-3 gallery-item">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Image</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control"
|
|
name="galleryImage_<%= index %>"
|
|
value="<%= item.image %>">
|
|
<button type="button"
|
|
class="btn btn-outline-primary btn-upload-image"
|
|
data-target-input="galleryImage_<%= index %>"
|
|
data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
<% if (item.image) { %>
|
|
<img src="<%= item.image %>" class="img-thumbnail uploaded-preview mt-2"
|
|
style="height: 100px; width: 100%; object-fit: cover;"
|
|
alt="Gallery image preview">
|
|
<% } %>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Title</label>
|
|
<input type="text" class="form-control"
|
|
name="galleryTitle_<%= index %>"
|
|
value="<%= item.title %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Description</label>
|
|
<textarea class="form-control"
|
|
name="galleryDescription_<%= index %>"
|
|
rows="3"><%= item.description %></textarea>
|
|
</div>
|
|
</div>
|
|
<button type="button"
|
|
class="btn btn-outline-danger btn-sm mt-3"
|
|
onclick="removeGalleryItem(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Gallery Item
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Newsletter Tab -->
|
|
<div class="tab-pane fade" id="newsletter" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<h6 class="fw-medium mb-3">Newsletter Settings</h6>
|
|
<div class="row g-3">
|
|
<div class="col-md-8">
|
|
<label class="form-label">Image Path</label>
|
|
<div class="input-group">
|
|
<input class="form-control" id="newsletterImagePath"
|
|
value="<%= data.newsletter?.imagePath || '' %>">
|
|
<button type="button"
|
|
class="btn btn-outline-primary btn-upload-image"
|
|
data-target-input="newsletterImagePath" data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<% if (data.newsletter?.imagePath) { %>
|
|
<img src="<%= data.newsletter.imagePath %>" class="img-thumbnail uploaded-preview"
|
|
style="height: 100px; width: 100%; object-fit: cover;"
|
|
alt="Newsletter image preview">
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<label class="form-label">Title</label>
|
|
<input class="form-control" id="newsletterTitle"
|
|
value="<%= data.newsletter?.title || '' %>">
|
|
</div>
|
|
</div>
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<label class="form-label">Description</label>
|
|
<textarea id="newsletterDescription" class="form-control"
|
|
rows="3"><%= data.newsletter?.description || '' %></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="row mt-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Button Text</label>
|
|
<input class="form-control" id="newsletterButtonText"
|
|
value="<%= data.newsletter?.buttonText || '' %>">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Features Tab (Accommodation) -->
|
|
<div class="tab-pane fade" id="features" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<h6 class="fw-medium mb-3">Accommodation Section</h6>
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Subtitle</label>
|
|
<input class="form-control" id="accommodationSubtitle"
|
|
value="<%= data.accommodation?.subtitle || '' %>">
|
|
</div>
|
|
<div class="col-md-8">
|
|
<label class="form-label">Title</label>
|
|
<input class="form-control" id="accommodationTitle"
|
|
value="<%= data.accommodation?.title || '' %>">
|
|
</div>
|
|
</div>
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-12">
|
|
<label class="form-label">Description</label>
|
|
<textarea class="form-control" id="accommodationDescription" rows="3"><%= data.accommodation?.description || '' %></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h6 class="fw-medium mb-0">Features</h6>
|
|
<button type="button" class="btn btn-primary btn-sm" onclick="addFeature()">
|
|
<i class="fas fa-plus"></i> Add Feature
|
|
</button>
|
|
</div>
|
|
<div id="featuresContainer">
|
|
<% if (data.accommodation?.features && data.accommodation.features.length > 0) { %>
|
|
<% data.accommodation.features.forEach((feature, index)=> { %>
|
|
<div class="card mb-3 feature-item">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Title</label>
|
|
<input type="text" class="form-control"
|
|
name="featureTitle_<%= index %>"
|
|
value="<%= feature.title %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Description</label>
|
|
<textarea class="form-control"
|
|
name="featureDescription_<%= index %>"
|
|
rows="2"><%= feature.description %></textarea>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Image</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" name="featureIcon_<%= index %>" value="<%= feature.icon || '' %>">
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="featureIcon_<%= index %>" data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
<% if (feature.icon) { %>
|
|
<img src="<%= feature.icon %>" class="img-thumbnail uploaded-preview mt-2" style="height: 100px; width: 100%; object-fit: cover;" alt="Feature image preview">
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
<button type="button"
|
|
class="btn btn-outline-danger btn-sm mt-3"
|
|
onclick="removeFeature(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Feature
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Events Tab -->
|
|
<div class="tab-pane fade" id="events" role="tabpanel">
|
|
<div class="card border shadow-sm">
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<h6 class="fw-medium mb-3">Events Section</h6>
|
|
<div class="row g-3">
|
|
<div class="col-md-12">
|
|
<label class="form-label">Title</label>
|
|
<input class="form-control" id="eventsTitle"
|
|
value="<%= data.events?.title || '' %>">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h6 class="fw-medium mb-0">Events</h6>
|
|
<button type="button" class="btn btn-primary btn-sm" onclick="addEvent()">
|
|
<i class="fas fa-plus"></i> Add Event
|
|
</button>
|
|
</div>
|
|
<div id="eventsContainer">
|
|
<% if (data.events?.items && data.events.items.length > 0) { %>
|
|
<% data.events.items.forEach((event, index)=> { %>
|
|
<div class="card mb-3 event-item">
|
|
<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="eventImage_<%= index %>"
|
|
value="<%= event.imageUrl %>">
|
|
<button type="button"
|
|
class="btn btn-outline-primary btn-upload-image"
|
|
data-target-input="eventImage_<%= index %>"
|
|
data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
<% if (event.imageUrl) { %>
|
|
<img src="<%= event.imageUrl %>"
|
|
class="img-thumbnail uploaded-preview mt-2"
|
|
style="height: 150px; width: 100%; object-fit: cover;"
|
|
alt="Event image preview">
|
|
<% } %>
|
|
</div>
|
|
<div class="col-md-9">
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Date</label>
|
|
<input type="text" class="form-control"
|
|
name="eventDate_<%= index %>"
|
|
value="<%= event.date %>">
|
|
</div>
|
|
<div class="col-md-8">
|
|
<label class="form-label">Title</label>
|
|
<input type="text" class="form-control"
|
|
name="eventTitle_<%= index %>"
|
|
value="<%= event.title %>">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label
|
|
class="form-label">Description</label>
|
|
<textarea class="form-control"
|
|
name="eventDescription_<%= index %>"
|
|
rows="2"><%= event.description %></textarea>
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label">Age Group</label>
|
|
<input type="text" class="form-control"
|
|
name="eventAge_<%= index %>"
|
|
value="<%= event.age || '' %>"
|
|
placeholder="e.g., Age Group: 10-14">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button type="button"
|
|
class="btn btn-outline-danger btn-sm mt-3"
|
|
onclick="removeEvent(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Event
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Fixed Bottom Buttons -->
|
|
<div class="fixed-bottom-buttons">
|
|
<button type="reset" class="btn btn-secondary" onclick="resetForm()">
|
|
<i class="fas fa-undo me-2"></i>Reset
|
|
</button>
|
|
<button type="submit" class="btn btn-primary" id="submitBtn" <%= data && data._id ? '' : 'disabled' %>>
|
|
<i class="fas fa-save me-2"></i>Save Changes
|
|
</button>
|
|
</div>
|
|
<% if (!data || !data._id) { %>
|
|
<div class="mt-3 alert alert-warning">
|
|
No About Us document found. To enable editing, please <a href="/admin/about-us/create">create an About Us record</a> first.
|
|
</div>
|
|
<% } %>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let originalFormData = null;
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
originalFormData = <%- JSON.stringify(data) %>;
|
|
updateAllJsonInputs(originalFormData);
|
|
initializeFormHandlers();
|
|
});
|
|
|
|
function initializeFormHandlers() {
|
|
const form = document.getElementById('aboutUsForm');
|
|
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);
|
|
alert('Failed to process form data. Please try again.');
|
|
submitBtn.disabled = false;
|
|
submitBtn.innerHTML = '<i class="fas fa-save me-2"></i>Save Changes';
|
|
}
|
|
});
|
|
|
|
document.querySelectorAll('.btn-upload-image').forEach(button => {
|
|
button.addEventListener('click', function () {
|
|
const targetInput = this.dataset.targetInput;
|
|
const imageType = this.dataset.imageType;
|
|
openImageUploader(targetInput, imageType);
|
|
});
|
|
});
|
|
}
|
|
|
|
function openImageUploader(targetInput, imageType) {
|
|
const fileInput = document.createElement('input');
|
|
fileInput.type = 'file';
|
|
fileInput.accept = 'image/*';
|
|
fileInput.style.display = 'none';
|
|
document.body.appendChild(fileInput);
|
|
|
|
function getPreviewDims(name) {
|
|
if (/hero/i.test(name)) return { h: '300px', w: '100%' };
|
|
if (/introMainImage/i.test(name)) return { h: '120px', w: '100%' };
|
|
if (/newsletter/i.test(name)) return { h: '100px', w: '100%' };
|
|
if (/^eventImage_/i.test(name)) return { h: '150px', w: '100%' };
|
|
if (/^galleryImage_/i.test(name)) return { h: '100px', w: '100%' };
|
|
return { h: '150px', w: '100%' };
|
|
}
|
|
|
|
fileInput.onchange = async function (e) {
|
|
const file = e.target.files[0];
|
|
if (!file) return;
|
|
|
|
try {
|
|
const formData = new FormData();
|
|
formData.append('image', file);
|
|
|
|
// Disable upload button and show loading
|
|
const uploadBtn = document.querySelector(`[data-target-input="${targetInput}"]`);
|
|
const originalBtnHtml = uploadBtn ? uploadBtn.innerHTML : 'Upload';
|
|
if (uploadBtn) {
|
|
uploadBtn.disabled = true;
|
|
uploadBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Uploading...';
|
|
}
|
|
|
|
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');
|
|
}
|
|
|
|
// Update input value
|
|
const input = document.getElementById(targetInput) || document.querySelector(`[name="${targetInput}"]`);
|
|
if (!input) {
|
|
throw new Error('Target input not found');
|
|
}
|
|
|
|
// Use absolute URL for preview when necessary
|
|
const previewUrl = (result.path && (result.path.startsWith('http://') || result.path.startsWith('https://'))) ? result.path : (window.location.origin + result.path);
|
|
|
|
input.value = result.path;
|
|
|
|
// Try to find an existing image preview inside the closest card or input group (prefer .uploaded-preview)
|
|
let card = input.closest('.card');
|
|
let previewImg = card ? card.querySelector('.uploaded-preview') : null;
|
|
|
|
if (!previewImg) {
|
|
// Look for a preview right after the input group
|
|
const parent = input.parentElement || input.closest('.input-group') || input.closest('div');
|
|
previewImg = parent ? parent.querySelector('.uploaded-preview') : null;
|
|
}
|
|
|
|
const dims = getPreviewDims(targetInput);
|
|
|
|
if (previewImg) {
|
|
previewImg.src = previewUrl;
|
|
previewImg.style.height = dims.h;
|
|
previewImg.style.width = dims.w;
|
|
} else {
|
|
// Create a preview image and attach it after the input group
|
|
const img = document.createElement('img');
|
|
img.src = previewUrl;
|
|
img.className = 'img-thumbnail uploaded-preview mt-2';
|
|
img.style.height = dims.h;
|
|
img.style.width = dims.w;
|
|
img.style.objectFit = 'cover';
|
|
img.alt = 'Image preview';
|
|
|
|
const parent = input.parentElement || input.closest('.input-group') || input.closest('div');
|
|
if (parent) parent.appendChild(img);
|
|
}
|
|
|
|
// Show success message
|
|
showToast('Success', 'Image uploaded successfully', 'success');
|
|
|
|
// Restore button
|
|
if (uploadBtn) {
|
|
uploadBtn.disabled = false;
|
|
uploadBtn.innerHTML = originalBtnHtml;
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Upload error:', error);
|
|
showToast('Error', 'Failed to upload image: ' + error.message, 'error');
|
|
|
|
// Restore button
|
|
const uploadBtn = document.querySelector(`[data-target-input="${targetInput}"]`);
|
|
if (uploadBtn) {
|
|
uploadBtn.disabled = false;
|
|
uploadBtn.innerHTML = uploadBtn.innerHTML.replace('Uploading...', 'Upload');
|
|
}
|
|
} finally {
|
|
document.body.removeChild(fileInput);
|
|
}
|
|
};
|
|
|
|
fileInput.click();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// Show 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>
|
|
`;
|
|
|
|
// 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 hide
|
|
toast.addEventListener('hidden.bs.toast', () => {
|
|
toast.remove();
|
|
});
|
|
}
|
|
|
|
function updateAllJsonInputs(data) {
|
|
// Hero
|
|
document.getElementById('heroJson').value = JSON.stringify(data.hero || {});
|
|
|
|
// Introduction
|
|
document.getElementById('introductionJson').value = JSON.stringify(data.introduction || {});
|
|
|
|
// Statistics
|
|
document.getElementById('statisticsJson').value = JSON.stringify(data.statistics || {});
|
|
|
|
// Accommodation
|
|
document.getElementById('accommodationJson').value = JSON.stringify(data.accommodation || {});
|
|
|
|
// Activities
|
|
document.getElementById('activitiesJson').value = JSON.stringify(data.activities || {});
|
|
|
|
// Newsletter
|
|
document.getElementById('newsletterJson').value = JSON.stringify(data.newsletter || {});
|
|
|
|
// Events
|
|
document.getElementById('eventsJson').value = JSON.stringify(data.events || {});
|
|
|
|
// Populate dynamic content
|
|
populateServicesFromData(data.introduction?.services || []);
|
|
populateFeaturesFromData(data.accommodation?.features || []);
|
|
populateEventsFromData(data.events?.items || []);
|
|
populateGalleryFromData(data.activities?.gallery || []);
|
|
populateStatsFromData(data.statistics?.items || []);
|
|
}
|
|
|
|
function addStat() {
|
|
const container = document.getElementById('statsContainer');
|
|
const index = container.children.length;
|
|
const html = `
|
|
<div class="card mb-3 stat-item">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label">Number</label>
|
|
<input type="text" class="form-control" name="statNumber_${index}">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label">Description</label>
|
|
<input type="text" class="form-control" name="statDescription_${index}">
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-danger btn-sm mt-3" onclick="removeStat(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Stat
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
}
|
|
|
|
function removeStat(button) {
|
|
button.closest('.stat-item').remove();
|
|
}
|
|
|
|
function addService() {
|
|
const container = document.getElementById('servicesContainer');
|
|
const index = container.children.length;
|
|
const html = `
|
|
<div class="card mb-3 service-item">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Title</label>
|
|
<input type="text" class="form-control" name="serviceTitle_${index}">
|
|
</div>
|
|
<div class="col-md-8">
|
|
<label class="form-label">Description</label>
|
|
<textarea class="form-control" name="serviceDescription_${index}" rows="2"></textarea>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-danger btn-sm mt-3" onclick="removeService(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Service
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
}
|
|
|
|
function removeService(button) {
|
|
button.closest('.service-item').remove();
|
|
}
|
|
|
|
function addFeature() {
|
|
const container = document.getElementById('featuresContainer');
|
|
const index = container.children.length;
|
|
const html = `
|
|
<div class="card mb-3 feature-item">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Title</label>
|
|
<input type="text" class="form-control" name="featureTitle_${index}">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Description</label>
|
|
<textarea class="form-control" name="featureDescription_${index}" rows="2"></textarea>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Image</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" name="featureIcon_${index}">
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="featureIcon_${index}" data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-danger btn-sm mt-3" onclick="removeFeature(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Feature
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
|
|
// Add event listener for new upload button inside the new feature
|
|
const newButton = container.lastElementChild.querySelector('.btn-upload-image');
|
|
if (newButton) {
|
|
newButton.addEventListener('click', function () {
|
|
const targetInput = this.dataset.targetInput;
|
|
const imageType = this.dataset.imageType;
|
|
openImageUploader(targetInput, imageType);
|
|
});
|
|
}
|
|
}
|
|
|
|
function removeFeature(button) {
|
|
button.closest('.feature-item').remove();
|
|
}
|
|
|
|
function addEvent() {
|
|
const container = document.getElementById('eventsContainer');
|
|
const index = container.children.length;
|
|
const html = `
|
|
<div class="card mb-3 event-item">
|
|
<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="eventImage_${index}">
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="eventImage_${index}" data-image-type="about">
|
|
<i class="fas fa-upload me-1"></i>Upload
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-9">
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Date</label>
|
|
<input type="text" class="form-control" name="eventDate_${index}">
|
|
</div>
|
|
<div class="col-md-8">
|
|
<label class="form-label">Title</label>
|
|
<input type="text" class="form-control" name="eventTitle_${index}">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label">Description</label>
|
|
<textarea class="form-control" name="eventDescription_${index}" rows="2"></textarea>
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label">Age Group</label>
|
|
<input type="text" class="form-control" name="eventAge_${index}" placeholder="e.g., Age Group: 10-14">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-danger btn-sm mt-3" onclick="removeEvent(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Event
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
|
|
// Add event listener for new upload button
|
|
const newButton = container.lastElementChild.querySelector('.btn-upload-image');
|
|
newButton.addEventListener('click', function () {
|
|
const targetInput = this.dataset.targetInput;
|
|
const imageType = this.dataset.imageType;
|
|
openImageUploader(targetInput, imageType);
|
|
});
|
|
}
|
|
|
|
function removeEvent(button) {
|
|
button.closest('.event-item').remove();
|
|
}
|
|
function updateJsonData() {
|
|
try {
|
|
// Hero
|
|
const heroData = {
|
|
title: (document.getElementById('heroTitle') || {}).value?.trim() || '',
|
|
backgroundImage: (document.getElementById('heroBackgroundImage') || {}).value?.trim() || ''
|
|
};
|
|
document.getElementById('heroJson').value = JSON.stringify(heroData);
|
|
|
|
// Introduction
|
|
const servicesData = Array.from(document.querySelectorAll('.service-item'))
|
|
.map((item) => {
|
|
const titleEl = item.querySelector('[name^="serviceTitle_"]') || item.querySelector('input');
|
|
const descEl = item.querySelector('[name^="serviceDescription_"]') || item.querySelector('textarea');
|
|
return {
|
|
title: (titleEl?.value || '').trim(),
|
|
description: (descEl?.value || '').trim()
|
|
};
|
|
})
|
|
.filter(s => s.title !== '' || s.description !== '');
|
|
|
|
const introductionData = {
|
|
subtitle: (document.getElementById('introSubtitle') || {}).value?.trim() || '',
|
|
title: (document.getElementById('introTitle') || {}).value?.trim() || '',
|
|
description: (document.getElementById('introDescription') || {}).value?.trim() || '',
|
|
mainImage: (document.getElementById('introMainImage') || {}).value?.trim() || '',
|
|
services: servicesData
|
|
};
|
|
document.getElementById('introductionJson').value = JSON.stringify(introductionData);
|
|
|
|
// Statistics
|
|
const statsItems = Array.from(document.querySelectorAll('.stat-item'))
|
|
.map((item) => {
|
|
const numEl = item.querySelector('[name^="statNumber_"]') || item.querySelectorAll('input')[0];
|
|
const descEl = item.querySelector('[name^="statDescription_"]') || item.querySelectorAll('input')[1];
|
|
return {
|
|
number: (numEl?.value || '').trim(),
|
|
description: (descEl?.value || '').trim()
|
|
};
|
|
})
|
|
.filter(stat => stat.number !== '' || stat.description !== '');
|
|
const statisticsData = { items: statsItems };
|
|
document.getElementById('statisticsJson').value = JSON.stringify(statisticsData);
|
|
|
|
// Accommodation
|
|
const featuresItems = Array.from(document.querySelectorAll('.feature-item'))
|
|
.map((item) => {
|
|
const titleEl = item.querySelector('[name^="featureTitle_"]') || item.querySelector('input');
|
|
const descEl = item.querySelector('[name^="featureDescription_"]') || item.querySelector('textarea');
|
|
const iconEl = item.querySelector('[name^="featureIcon_"]') || item.querySelector('input');
|
|
return {
|
|
title: (titleEl?.value || '').trim(),
|
|
description: (descEl?.value || '').trim(),
|
|
icon: (iconEl?.value || '').trim()
|
|
};
|
|
})
|
|
.filter(f => f.title !== '' || f.description !== '');
|
|
|
|
const accommodationData = {
|
|
subtitle: (document.getElementById('accommodationSubtitle') || {}).value?.trim() || '',
|
|
title: (document.getElementById('accommodationTitle') || {}).value?.trim() || '',
|
|
description: (document.getElementById('accommodationDescription') || {}).value?.trim() || '',
|
|
features: featuresItems
|
|
};
|
|
document.getElementById('accommodationJson').value = JSON.stringify(accommodationData);
|
|
|
|
// Activities
|
|
const galleryItems = Array.from(document.querySelectorAll('.gallery-item'))
|
|
.map((item) => {
|
|
const imageEl = item.querySelector('[name^="galleryImage_"]') || item.querySelector('input');
|
|
const titleEl = item.querySelector('[name^="galleryTitle_"]') || item.querySelectorAll('input')[1];
|
|
const descEl = item.querySelector('[name^="galleryDescription_"]') || item.querySelector('textarea');
|
|
return {
|
|
image: (imageEl?.value || '').trim(),
|
|
title: (titleEl?.value || '').trim(),
|
|
description: (descEl?.value || '').trim()
|
|
};
|
|
})
|
|
.filter(item => item.image !== '' || item.title !== '');
|
|
|
|
const activitiesData = {
|
|
subtitle: (document.getElementById('activitiesSubtitle') || {}).value?.trim() || '',
|
|
title: (document.getElementById('activitiesTitle') || {}).value?.trim() || '',
|
|
description: (document.getElementById('activitiesDescription') || {}).value?.trim() || '',
|
|
gallery: galleryItems
|
|
};
|
|
document.getElementById('activitiesJson').value = JSON.stringify(activitiesData);
|
|
|
|
// Newsletter
|
|
const newsletterData = {
|
|
imagePath: (document.getElementById('newsletterImagePath') || {}).value?.trim() || '',
|
|
title: (document.getElementById('newsletterTitle') || {}).value?.trim() || '',
|
|
description: (document.getElementById('newsletterDescription') || {}).value?.trim() || '',
|
|
buttonText: (document.getElementById('newsletterButtonText') || {}).value?.trim() || ''
|
|
};
|
|
document.getElementById('newsletterJson').value = JSON.stringify(newsletterData);
|
|
|
|
// Events
|
|
const eventsItems = Array.from(document.querySelectorAll('.event-item')).map((item) => {
|
|
const imageEl = item.querySelector('[name^="eventImage_"]') || item.querySelector('input');
|
|
const dateEl = item.querySelector('[name^="eventDate_"]') || item.querySelector('input[type="text"]');
|
|
const titleEl = item.querySelector('[name^="eventTitle_"]') || item.querySelector('input');
|
|
const descEl = item.querySelector('[name^="eventDescription_"]') || item.querySelector('textarea');
|
|
const ageEl = item.querySelector('[name^="eventAge_"]') || item.querySelectorAll('input')[3];
|
|
|
|
return {
|
|
imageUrl: (imageEl?.value || '').trim(),
|
|
date: (dateEl?.value || '').trim(),
|
|
title: (titleEl?.value || '').trim(),
|
|
description: (descEl?.value || '').trim(),
|
|
age: (ageEl?.value || '').trim()
|
|
};
|
|
}).filter(event => event.title !== '' || event.description !== '');
|
|
|
|
const eventsData = {
|
|
title: (document.getElementById('eventsTitle') || {}).value?.trim() || '',
|
|
items: eventsItems
|
|
};
|
|
document.getElementById('eventsJson').value = JSON.stringify(eventsData);
|
|
|
|
} catch (error) {
|
|
console.error('Error updating JSON data:', error);
|
|
throw new Error('Failed to process form data');
|
|
}
|
|
}
|
|
|
|
// Helpers to populate DOM lists from data
|
|
function populateStatsFromData(stats) {
|
|
const container = document.getElementById('statsContainer');
|
|
container.innerHTML = '';
|
|
(stats || []).forEach((stat, i) => {
|
|
addStat();
|
|
const el = container.lastElementChild;
|
|
el.querySelector(`[name="statNumber_${i}"]`).value = stat.number || '';
|
|
el.querySelector(`[name="statDescription_${i}"]`).value = stat.description || '';
|
|
});
|
|
}
|
|
|
|
function populateServicesFromData(services) {
|
|
const container = document.getElementById('servicesContainer');
|
|
container.innerHTML = '';
|
|
(services || []).forEach((s, i) => {
|
|
addService();
|
|
const el = container.lastElementChild;
|
|
el.querySelector(`[name="serviceTitle_${i}"]`).value = s.title || '';
|
|
el.querySelector(`[name="serviceDescription_${i}"]`).value = s.description || '';
|
|
});
|
|
}
|
|
|
|
function populateFeaturesFromData(features) {
|
|
const container = document.getElementById('featuresContainer');
|
|
container.innerHTML = '';
|
|
(features || []).forEach((f, i) => {
|
|
addFeature();
|
|
const el = container.lastElementChild;
|
|
el.querySelector(`[name="featureTitle_${i}"]`).value = f.title || '';
|
|
el.querySelector(`[name="featureDescription_${i}"]`).value = f.description || '';
|
|
// populate icon field and preview
|
|
const iconInput = el.querySelector(`[name="featureIcon_${i}"]`);
|
|
if (iconInput) iconInput.value = f.icon || '';
|
|
try {
|
|
let preview = el.querySelector('.uploaded-preview');
|
|
const iconPath = f.icon || '';
|
|
if (iconPath) {
|
|
const previewUrl = (iconPath.startsWith('http://') || iconPath.startsWith('https://')) ? iconPath : (window.location.origin + iconPath);
|
|
if (preview) {
|
|
preview.src = previewUrl;
|
|
preview.style.display = '';
|
|
} else if (iconInput) {
|
|
const parent = iconInput.parentElement || iconInput.closest('.input-group') || iconInput.closest('div');
|
|
if (parent) {
|
|
const img = document.createElement('img');
|
|
img.src = previewUrl;
|
|
img.className = 'img-thumbnail uploaded-preview mt-2';
|
|
img.style.height = '100px';
|
|
img.style.width = '100%';
|
|
img.style.objectFit = 'cover';
|
|
parent.insertAdjacentElement('afterend', img);
|
|
}
|
|
}
|
|
} else if (preview) {
|
|
preview.remove();
|
|
}
|
|
} catch (err) {
|
|
console.error('populateFeaturesFromData preview error', err);
|
|
}
|
|
});
|
|
}
|
|
|
|
function populateEventsFromData(events) {
|
|
const container = document.getElementById('eventsContainer');
|
|
container.innerHTML = '';
|
|
(events || []).forEach((ev, i) => {
|
|
addEvent();
|
|
const el = container.lastElementChild;
|
|
el.querySelector(`[name="eventImage_${i}"]`).value = ev.imageUrl || '';
|
|
el.querySelector(`[name="eventDate_${i}"]`).value = ev.date || '';
|
|
el.querySelector(`[name="eventTitle_${i}"]`).value = ev.title || '';
|
|
el.querySelector(`[name="eventDescription_${i}"]`).value = ev.description || '';
|
|
el.querySelector(`[name="eventAge_${i}"]`).value = ev.age || '';
|
|
// ensure preview exists or is updated for this event
|
|
try {
|
|
const input = el.querySelector(`[name="eventImage_${i}"]`);
|
|
const parent = input.parentElement || input.closest('.input-group') || input.closest('div');
|
|
let preview = el.querySelector('.uploaded-preview');
|
|
const imagePath = ev.imageUrl || '';
|
|
if (imagePath) {
|
|
const previewUrl = (imagePath.startsWith('http://') || imagePath.startsWith('https://')) ? imagePath : (window.location.origin + imagePath);
|
|
if (preview) {
|
|
preview.src = previewUrl;
|
|
preview.style.display = '';
|
|
} else if (parent) {
|
|
const img = document.createElement('img');
|
|
img.src = previewUrl;
|
|
img.className = 'img-thumbnail uploaded-preview mt-2';
|
|
img.style.height = '150px';
|
|
img.style.width = '100%';
|
|
img.style.objectFit = 'cover';
|
|
parent.insertAdjacentElement('afterend', img);
|
|
}
|
|
} else if (preview) {
|
|
preview.remove();
|
|
}
|
|
} catch (err) {
|
|
console.error('populateEventsFromData preview error', err);
|
|
}
|
|
});
|
|
}
|
|
|
|
function populateGalleryFromData(gallery) {
|
|
const container = document.getElementById('activitiesGalleryContainer');
|
|
container.innerHTML = '';
|
|
(gallery || []).forEach((item, i) => {
|
|
addGalleryItem();
|
|
const el = container.lastElementChild;
|
|
el.querySelector(`[name="galleryImage_${i}"]`).value = item.image || '';
|
|
el.querySelector(`[name="galleryTitle_${i}"]`).value = item.title || '';
|
|
el.querySelector(`[name="galleryDescription_${i}"]`).value = item.description || '';
|
|
// ensure preview exists or is updated
|
|
try {
|
|
const input = el.querySelector(`[name="galleryImage_${i}"]`);
|
|
const parent = input.parentElement || input.closest('.input-group') || input.closest('div');
|
|
let preview = el.querySelector('.uploaded-preview');
|
|
const imagePath = item.image || '';
|
|
if (imagePath) {
|
|
const previewUrl = (imagePath.startsWith('http://') || imagePath.startsWith('https://')) ? imagePath : (window.location.origin + imagePath);
|
|
if (preview) {
|
|
preview.src = previewUrl;
|
|
preview.style.display = '';
|
|
} else if (parent) {
|
|
const img = document.createElement('img');
|
|
img.src = previewUrl;
|
|
img.className = 'img-thumbnail uploaded-preview mt-2';
|
|
img.style.height = '100px';
|
|
img.style.width = '100%';
|
|
img.style.objectFit = 'cover';
|
|
parent.insertAdjacentElement('afterend', img);
|
|
}
|
|
} else if (preview) {
|
|
preview.remove();
|
|
}
|
|
} catch (err) {
|
|
console.error('populateGalleryFromData preview error', err);
|
|
}
|
|
});
|
|
}
|
|
|
|
function addGalleryItem() {
|
|
const container = document.getElementById('activitiesGalleryContainer');
|
|
const index = container.children.length;
|
|
const html = `
|
|
<div class="card mb-3 gallery-item">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Image</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" name="galleryImage_${index}">
|
|
<button type="button" class="btn btn-outline-primary btn-upload-image" data-target-input="galleryImage_${index}" data-image-type="about">
|
|
<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="galleryTitle_${index}">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Description</label>
|
|
<textarea class="form-control" name="galleryDescription_${index}" rows="3"></textarea>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-danger btn-sm mt-3" onclick="removeGalleryItem(this)">
|
|
<i class="fas fa-trash me-2"></i>Remove Gallery Item
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
|
|
// Add event listener for new upload button
|
|
const newButton = container.lastElementChild.querySelector('.btn-upload-image');
|
|
newButton.addEventListener('click', function () {
|
|
const targetInput = this.dataset.targetInput;
|
|
const imageType = this.dataset.imageType;
|
|
openImageUploader(targetInput, imageType);
|
|
});
|
|
}
|
|
|
|
function removeGalleryItem(button) {
|
|
button.closest('.gallery-item').remove();
|
|
}
|
|
</script> |