Beta-2.1 - The clearer, the faster #22
@@ -18,7 +18,16 @@
 | 
				
			|||||||
        <button id="remove-all-hero" class="up-btn">🗑 Delete all</button>
 | 
					        <button id="remove-all-hero" class="up-btn">🗑 Delete all</button>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <input type="file" id="upload-hero" accept=".png,.jpg,.jpeg,.webp" multiple hidden>
 | 
					      <input type="file" id="upload-hero" accept=".png,.jpg,.jpeg,.webp" multiple hidden>
 | 
				
			||||||
 | 
					      <span id="hero-count" class="photo-count"></span>
 | 
				
			||||||
      <div id="hero"></div>
 | 
					      <div id="hero"></div>
 | 
				
			||||||
 | 
					        <input type="file" id="upload-hero-bottom" accept=".png,.jpg,.jpeg,.webp" multiple hidden>
 | 
				
			||||||
 | 
					        <div id="bottom-hero-upload" class="upload-actions-row bottom-action-row">
 | 
				
			||||||
 | 
					          <span id="hero-count-bottom" class="photo-count"></span>
 | 
				
			||||||
 | 
					          <label for="upload-hero-bottom" class="up-btn">
 | 
				
			||||||
 | 
					            📸 Upload photos
 | 
				
			||||||
 | 
					          </label>
 | 
				
			||||||
 | 
					          <button id="remove-all-hero-bottom" class="up-btn bottom-remove-btn">🗑 Delete all</button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- Gallery Upload Section -->
 | 
					    <!-- Gallery Upload Section -->
 | 
				
			||||||
@@ -31,8 +40,17 @@
 | 
				
			|||||||
        </label>
 | 
					        </label>
 | 
				
			||||||
        <button id="remove-all-gallery" class="up-btn">🗑 Delete all</button>
 | 
					        <button id="remove-all-gallery" class="up-btn">🗑 Delete all</button>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					      <span id="gallery-count" class="photo-count"></span>
 | 
				
			||||||
      <input type="file" id="upload-gallery" accept=".png,.jpg,.jpeg,.webp" multiple hidden>
 | 
					      <input type="file" id="upload-gallery" accept=".png,.jpg,.jpeg,.webp" multiple hidden>
 | 
				
			||||||
      <div id="gallery"></div>
 | 
					      <div id="gallery"></div>
 | 
				
			||||||
 | 
					        <input type="file" id="upload-gallery-bottom" accept=".png,.jpg,.jpeg,.webp" multiple hidden>
 | 
				
			||||||
 | 
					        <div id="bottom-gallery-upload" class="upload-actions-row bottom-action-row">
 | 
				
			||||||
 | 
					          <span id="gallery-count-bottom" class="photo-count"></span>
 | 
				
			||||||
 | 
					          <label for="upload-gallery-bottom" class="up-btn">
 | 
				
			||||||
 | 
					            📸 Upload photos
 | 
				
			||||||
 | 
					          </label>
 | 
				
			||||||
 | 
					          <button id="remove-all-gallery-bottom" class="up-btn bottom-remove-btn">🗑 Delete all</button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <div class="section">
 | 
					  <div class="section">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,11 +54,35 @@ function renderGallery() {
 | 
				
			|||||||
    renderTags(i, img.tags || []);
 | 
					    renderTags(i, img.tags || []);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Show/hide Remove All button
 | 
					  // Update gallery count (top)
 | 
				
			||||||
 | 
					  const galleryCount = document.getElementById('gallery-count');
 | 
				
			||||||
 | 
					  if (galleryCount) {
 | 
				
			||||||
 | 
					    galleryCount.innerHTML = `<p>${galleryImages.length} photos</p>`;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Update gallery count (bottom)
 | 
				
			||||||
 | 
					  const galleryCountBottom = document.getElementById('gallery-count-bottom');
 | 
				
			||||||
 | 
					  if (galleryCountBottom) {
 | 
				
			||||||
 | 
					    galleryCountBottom.innerHTML = `<p>${galleryImages.length} photos</p>`;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Show/hide Remove All button (top)
 | 
				
			||||||
  const removeAllBtn = document.getElementById('remove-all-gallery');
 | 
					  const removeAllBtn = document.getElementById('remove-all-gallery');
 | 
				
			||||||
  if (removeAllBtn) {
 | 
					  if (removeAllBtn) {
 | 
				
			||||||
    removeAllBtn.style.display = galleryImages.length > 0 ? 'inline-block' : 'none';
 | 
					    removeAllBtn.style.display = galleryImages.length > 0 ? 'inline-block' : 'none';
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Show/hide bottom upload row
 | 
				
			||||||
 | 
					  const bottomGalleryUpload = document.getElementById('bottom-gallery-upload');
 | 
				
			||||||
 | 
					  if (bottomGalleryUpload) {
 | 
				
			||||||
 | 
					    bottomGalleryUpload.style.display = galleryImages.length > 0 ? 'flex' : 'none';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Show/hide Remove All button (bottom)
 | 
				
			||||||
 | 
					  const removeAllBtnBottom = document.getElementById('remove-all-gallery-bottom');
 | 
				
			||||||
 | 
					  if (removeAllBtnBottom) {
 | 
				
			||||||
 | 
					    removeAllBtnBottom.style.display = galleryImages.length > 0 ? 'inline-block' : 'none';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// --- Render tags for a single image ---
 | 
					// --- Render tags for a single image ---
 | 
				
			||||||
@@ -244,11 +268,35 @@ function renderHero() {
 | 
				
			|||||||
    container.appendChild(div);
 | 
					    container.appendChild(div);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Show/hide Remove All button
 | 
					  // Update hero count (top)
 | 
				
			||||||
 | 
					  const heroCount = document.getElementById('hero-count');
 | 
				
			||||||
 | 
					  if (heroCount) {
 | 
				
			||||||
 | 
					    heroCount.innerHTML = `<p>${heroImages.length} photos</p>`;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Update hero count (bottom)
 | 
				
			||||||
 | 
					  const heroCountBottom = document.getElementById('hero-count-bottom');
 | 
				
			||||||
 | 
					  if (heroCountBottom) {
 | 
				
			||||||
 | 
					    heroCountBottom.innerHTML = `<p>${heroImages.length} photos</p>`;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Show/hide Remove All button (top)
 | 
				
			||||||
  const removeAllBtn = document.getElementById('remove-all-hero');
 | 
					  const removeAllBtn = document.getElementById('remove-all-hero');
 | 
				
			||||||
  if (removeAllBtn) {
 | 
					  if (removeAllBtn) {
 | 
				
			||||||
    removeAllBtn.style.display = heroImages.length > 0 ? 'inline-block' : 'none';
 | 
					    removeAllBtn.style.display = heroImages.length > 0 ? 'inline-block' : 'none';
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Show/hide bottom upload row
 | 
				
			||||||
 | 
					  const bottomHeroUpload = document.getElementById('bottom-hero-upload');
 | 
				
			||||||
 | 
					  if (bottomHeroUpload) {
 | 
				
			||||||
 | 
					    bottomHeroUpload.style.display = heroImages.length > 0 ? 'flex' : 'none';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Show/hide Remove All button (bottom)
 | 
				
			||||||
 | 
					  const removeAllBtnBottom = document.getElementById('remove-all-hero-bottom');
 | 
				
			||||||
 | 
					  if (removeAllBtnBottom) {
 | 
				
			||||||
 | 
					    removeAllBtnBottom.style.display = heroImages.length > 0 ? 'inline-block' : 'none';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// --- Save gallery to server ---
 | 
					// --- Save gallery to server ---
 | 
				
			||||||
@@ -429,9 +477,13 @@ document.addEventListener('DOMContentLoaded', () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // Bulk delete buttons
 | 
					  // Bulk delete buttons
 | 
				
			||||||
  const removeAllGalleryBtn = document.getElementById('remove-all-gallery');
 | 
					  const removeAllGalleryBtn = document.getElementById('remove-all-gallery');
 | 
				
			||||||
 | 
					  const removeAllGalleryBtnBottom = document.getElementById('remove-all-gallery-bottom');
 | 
				
			||||||
  const removeAllHeroBtn = document.getElementById('remove-all-hero');
 | 
					  const removeAllHeroBtn = document.getElementById('remove-all-hero');
 | 
				
			||||||
 | 
					  const removeAllHeroBtnBottom = document.getElementById('remove-all-hero-bottom');
 | 
				
			||||||
  if (removeAllGalleryBtn) removeAllGalleryBtn.onclick = () => showDeleteModal('gallery-all');
 | 
					  if (removeAllGalleryBtn) removeAllGalleryBtn.onclick = () => showDeleteModal('gallery-all');
 | 
				
			||||||
 | 
					  if (removeAllGalleryBtnBottom) removeAllGalleryBtnBottom.onclick = () => showDeleteModal('gallery-all');
 | 
				
			||||||
  if (removeAllHeroBtn) removeAllHeroBtn.onclick = () => showDeleteModal('hero-all');
 | 
					  if (removeAllHeroBtn) removeAllHeroBtn.onclick = () => showDeleteModal('hero-all');
 | 
				
			||||||
 | 
					  if (removeAllHeroBtnBottom) removeAllHeroBtnBottom.onclick = () => showDeleteModal('hero-all');
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// --- Initialize ---
 | 
					// --- Initialize ---
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ function hideLoader() {
 | 
				
			|||||||
  if (loader) loader.classList.remove("active");
 | 
					  if (loader) loader.classList.remove("active");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// --- Upload gallery images ---
 | 
					// --- Upload gallery images ---
 | 
				
			||||||
const galleryInput = document.getElementById('upload-gallery');
 | 
					const galleryInput = document.getElementById('upload-gallery');
 | 
				
			||||||
if (galleryInput) {
 | 
					if (galleryInput) {
 | 
				
			||||||
@@ -62,3 +63,55 @@ if (heroInput) {
 | 
				
			|||||||
    } finally { e.target.value = ''; }
 | 
					    } finally { e.target.value = ''; }
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- Upload gallery images (bottom button) ---
 | 
				
			||||||
 | 
					const galleryInputBottom = document.getElementById('upload-gallery-bottom');
 | 
				
			||||||
 | 
					if (galleryInputBottom) {
 | 
				
			||||||
 | 
					  galleryInputBottom.addEventListener('change', async (e) => {
 | 
				
			||||||
 | 
					    const files = e.target.files;
 | 
				
			||||||
 | 
					    if (!files.length) return;
 | 
				
			||||||
 | 
					    showLoader("Uploading photos...");
 | 
				
			||||||
 | 
					    const formData = new FormData();
 | 
				
			||||||
 | 
					    for (const file of files) formData.append('files', file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const res = await fetch('/api/gallery/upload', { method: 'POST', body: formData });
 | 
				
			||||||
 | 
					      const data = await res.json();
 | 
				
			||||||
 | 
					      hideLoader();
 | 
				
			||||||
 | 
					      if (res.ok) {
 | 
				
			||||||
 | 
					        showToast(`✅ ${data.uploaded.length} gallery image(s) uploaded!`, "success");
 | 
				
			||||||
 | 
					        if (typeof refreshGallery === "function") refreshGallery();
 | 
				
			||||||
 | 
					      } else showToast('Error: ' + data.error, "error");
 | 
				
			||||||
 | 
					    } catch(err) {
 | 
				
			||||||
 | 
					      hideLoader();
 | 
				
			||||||
 | 
					      console.error(err);
 | 
				
			||||||
 | 
					      showToast('Server error!', "error");
 | 
				
			||||||
 | 
					    } finally { e.target.value = ''; }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- Upload hero images (bottom button) ---
 | 
				
			||||||
 | 
					const heroInputBottom = document.getElementById('upload-hero-bottom');
 | 
				
			||||||
 | 
					if (heroInputBottom) {
 | 
				
			||||||
 | 
					  heroInputBottom.addEventListener('change', async (e) => {
 | 
				
			||||||
 | 
					    const files = e.target.files;
 | 
				
			||||||
 | 
					    if (!files.length) return;
 | 
				
			||||||
 | 
					    showLoader("Uploading hero photos...");
 | 
				
			||||||
 | 
					    const formData = new FormData();
 | 
				
			||||||
 | 
					    for (const file of files) formData.append('files', file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const res = await fetch('/api/hero/upload', { method: 'POST', body: formData });
 | 
				
			||||||
 | 
					      const data = await res.json();
 | 
				
			||||||
 | 
					      hideLoader();
 | 
				
			||||||
 | 
					      if (res.ok) {
 | 
				
			||||||
 | 
					        showToast(`✅ ${data.uploaded.length} hero image(s) uploaded!`, "success");
 | 
				
			||||||
 | 
					        if (typeof refreshHero === "function") refreshHero();
 | 
				
			||||||
 | 
					      } else showToast('Error: ' + data.error, "error");
 | 
				
			||||||
 | 
					    } catch(err) {
 | 
				
			||||||
 | 
					      hideLoader();
 | 
				
			||||||
 | 
					      console.error(err);
 | 
				
			||||||
 | 
					      showToast('Server error!', "error");
 | 
				
			||||||
 | 
					    } finally { e.target.value = ''; }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -241,7 +241,7 @@ h2 {
 | 
				
			|||||||
  display: grid;
 | 
					  display: grid;
 | 
				
			||||||
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
 | 
					  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
 | 
				
			||||||
  gap: 15px;
 | 
					  gap: 15px;
 | 
				
			||||||
  margin-top: 30px;
 | 
					  margin: 30px 0 0 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* --- Photo Card --- */
 | 
					/* --- Photo Card --- */
 | 
				
			||||||
@@ -521,18 +521,23 @@ h2 {
 | 
				
			|||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
  gap: 16px;
 | 
					  gap: 16px;
 | 
				
			||||||
  margin-bottom: 10px;
 | 
					  margin-bottom: 10px;
 | 
				
			||||||
 | 
					  flex-wrap: wrap;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* --- Remove All Buttons --- */
 | 
					/* --- Remove All Buttons --- */
 | 
				
			||||||
#remove-all-hero, #remove-all-gallery {
 | 
					#remove-all-hero, #remove-all-gallery, .bottom-remove-btn {
 | 
				
			||||||
  background: #2d2d2d;
 | 
					  background: #2d2d2d;
 | 
				
			||||||
  color: white;
 | 
					  color: white;
 | 
				
			||||||
  display: none;
 | 
					  display: none;
 | 
				
			||||||
  margin-bottom: 6px;
 | 
					  margin-bottom: 6px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.bottom-remove-btn {
 | 
				
			||||||
 | 
					  display: inherit;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#remove-all-gallery:hover,
 | 
					#remove-all-gallery:hover,
 | 
				
			||||||
#remove-all-hero:hover {
 | 
					#remove-all-hero:hover, .bottom-remove-btn:hover {
 | 
				
			||||||
  background: rgb(121, 26, 19);
 | 
					  background: rgb(121, 26, 19);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -541,6 +546,7 @@ h2 {
 | 
				
			|||||||
    flex-direction: column;
 | 
					    flex-direction: column;
 | 
				
			||||||
    align-items: stretch;
 | 
					    align-items: stretch;
 | 
				
			||||||
    gap: 8px;
 | 
					    gap: 8px;
 | 
				
			||||||
 | 
					    flex-wrap: wrap;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1037,3 +1043,11 @@ justify-content: center;
 | 
				
			|||||||
    flex-wrap: nowrap;
 | 
					    flex-wrap: nowrap;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#hero-count-bottom, #gallery-count-bottom {
 | 
				
			||||||
 | 
					  flex-basis: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.bottom-action-row {
 | 
				
			||||||
 | 
					  margin-top: 30px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user