feat: Add gallery management and dynamic API-based data loading
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- Introduced a gallery management section in `admin.astro` for uploading, listing, and deleting gallery images. - Added dynamic fetching of events and gallery images from the backend in `index.astro`. - Updated authentication to handle gallery-related UI visibility and actions.
This commit is contained in:
@ -67,6 +67,23 @@ const title = 'Admin';
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section id="sec-gallery" style="display:none">
|
||||||
|
<h2>Gallery verwalten</h2>
|
||||||
|
<div class="events-row">
|
||||||
|
<div class="card">
|
||||||
|
<h3>Neues Gallery-Bild</h3>
|
||||||
|
<label>Bild-Datei<input id="gal-file" type="file" accept="image/*" /></label>
|
||||||
|
<label>Alt-Text<input id="gal-alt" placeholder="Bildbeschreibung" /></label>
|
||||||
|
<button id="btn-create-gal">Bild hochladen</button>
|
||||||
|
<div id="gal-create-msg" class="muted"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card" style="min-width:380px;">
|
||||||
|
<h3>Gallery-Liste</h3>
|
||||||
|
<div id="gallery-list" class="grid"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section id="sec-publish" style="display:none">
|
<section id="sec-publish" style="display:none">
|
||||||
<h2>Veröffentlichen</h2>
|
<h2>Veröffentlichen</h2>
|
||||||
<label>Commit-Message<input id="pub-msg" placeholder="Änderungen beschreiben" value="Update events" /></label>
|
<label>Commit-Message<input id="pub-msg" placeholder="Änderungen beschreiben" value="Update events" /></label>
|
||||||
@ -91,15 +108,18 @@ const title = 'Admin';
|
|||||||
document.getElementById('auth-status').textContent = `Angemeldet als ${me.user?.giteaUsername || 'Admin'}`;
|
document.getElementById('auth-status').textContent = `Angemeldet als ${me.user?.giteaUsername || 'Admin'}`;
|
||||||
// UI-Bereiche für eingeloggte Nutzer einblenden
|
// UI-Bereiche für eingeloggte Nutzer einblenden
|
||||||
document.getElementById('sec-events').style.display = '';
|
document.getElementById('sec-events').style.display = '';
|
||||||
|
document.getElementById('sec-gallery').style.display = '';
|
||||||
document.getElementById('sec-publish').style.display = '';
|
document.getElementById('sec-publish').style.display = '';
|
||||||
// Direkt Events laden und auf Sektion fokussieren
|
// Direkt Events laden und auf Sektion fokussieren
|
||||||
await loadEvents();
|
await loadEvents();
|
||||||
|
await loadGallery();
|
||||||
document.getElementById('sec-events').scrollIntoView({ behavior: 'smooth' });
|
document.getElementById('sec-events').scrollIntoView({ behavior: 'smooth' });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const el = document.getElementById('auth-status');
|
const el = document.getElementById('auth-status');
|
||||||
el.textContent = 'Nicht angemeldet';
|
el.textContent = 'Nicht angemeldet';
|
||||||
// Kein Auto-Redirect, damit keine Schleife entsteht. Login-Button verwenden.
|
// Kein Auto-Redirect, damit keine Schleife entsteht. Login-Button verwenden.
|
||||||
document.getElementById('sec-events').style.display = 'none';
|
document.getElementById('sec-events').style.display = 'none';
|
||||||
|
document.getElementById('sec-gallery').style.display = 'none';
|
||||||
document.getElementById('sec-publish').style.display = 'none';
|
document.getElementById('sec-publish').style.display = 'none';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,6 +313,59 @@ const title = 'Admin';
|
|||||||
} catch(e){ msg.textContent = 'Fehler: '+e.message }
|
} catch(e){ msg.textContent = 'Fehler: '+e.message }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ========== Gallery ==========
|
||||||
|
async function loadGallery() {
|
||||||
|
const listEl = document.getElementById('gallery-list');
|
||||||
|
listEl.innerHTML = '<div class="muted">Lade...</div>';
|
||||||
|
try {
|
||||||
|
const data = await api('/api/gallery');
|
||||||
|
listEl.innerHTML = '';
|
||||||
|
const galleryImages = (data.images || []).slice();
|
||||||
|
galleryImages.sort((a,b) => (a.displayOrder??0) - (b.displayOrder??0));
|
||||||
|
|
||||||
|
galleryImages.forEach((img) => {
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'card';
|
||||||
|
card.innerHTML = `
|
||||||
|
<img src="${API_BASE}${img.imageUrl}" alt="${img.altText}" class="thumb" />
|
||||||
|
<div class="muted">${img.altText || ''}</div>
|
||||||
|
<div class="row-buttons">
|
||||||
|
<button data-id="${img.id}" class="btn-del-gal">Löschen</button>
|
||||||
|
</div>`;
|
||||||
|
listEl.appendChild(card);
|
||||||
|
});
|
||||||
|
listEl.querySelectorAll('.btn-del-gal').forEach(btn => {
|
||||||
|
btn.addEventListener('click', async () => {
|
||||||
|
const id = btn.getAttribute('data-id');
|
||||||
|
if (!id) return;
|
||||||
|
if (!confirm('Bild wirklich löschen?')) return;
|
||||||
|
try { await api(`/api/gallery/${id}`, { method: 'DELETE' }); await loadGallery(); } catch(e){ alert('Fehler: '+e.message); }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
listEl.innerHTML = '<div class="muted">Fehler beim Laden</div>';
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('btn-create-gal').addEventListener('click', async () => {
|
||||||
|
const file = /** @type {HTMLInputElement} */ (document.getElementById('gal-file')).files[0];
|
||||||
|
const alt = (document.getElementById('gal-alt')).value.trim();
|
||||||
|
const msg = document.getElementById('gal-create-msg');
|
||||||
|
if (!file) {
|
||||||
|
msg.textContent = 'Bitte Datei auswählen';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
msg.textContent = 'Lade Bild hoch...';
|
||||||
|
try {
|
||||||
|
await uploadGalleryImage(file, alt);
|
||||||
|
msg.textContent = 'Bild hochgeladen';
|
||||||
|
(document.getElementById('gal-file')).value = '';
|
||||||
|
(document.getElementById('gal-alt')).value = '';
|
||||||
|
await loadGallery();
|
||||||
|
} catch(e){ msg.textContent = 'Fehler: '+e.message }
|
||||||
|
});
|
||||||
|
|
||||||
refreshAuth();
|
refreshAuth();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -8,14 +8,39 @@ import ImageCarousel from "../components/ImageCarousel.astro";
|
|||||||
import Contact from "../components/Contact.astro";
|
import Contact from "../components/Contact.astro";
|
||||||
import About from "../components/About.astro";
|
import About from "../components/About.astro";
|
||||||
|
|
||||||
const events = [
|
const API_BASE = 'https://cms.gallus-pub.ch';
|
||||||
|
|
||||||
];
|
// Fetch events from backend API
|
||||||
|
let events = [];
|
||||||
|
try {
|
||||||
|
const eventsResponse = await fetch(`${API_BASE}/api/events/public`);
|
||||||
|
if (eventsResponse.ok) {
|
||||||
|
const eventsData = await eventsResponse.json();
|
||||||
|
events = (eventsData.events || []).map((ev: any) => ({
|
||||||
|
image: `${API_BASE}${ev.imageUrl}`,
|
||||||
|
title: ev.title,
|
||||||
|
date: ev.date,
|
||||||
|
description: ev.description
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch events:', error);
|
||||||
|
}
|
||||||
|
|
||||||
const images = [
|
// Fetch gallery images from backend API
|
||||||
{ src: "/static/images/gallery/miyma9zc-8he1di.webp", alt: "Schwarzes bild" },
|
let images = [];
|
||||||
{ src: "/static/images/gallery/miyrhqng-i2kgtx.webp", alt: "test" }
|
try {
|
||||||
];
|
const galleryResponse = await fetch(`${API_BASE}/api/gallery/public`);
|
||||||
|
if (galleryResponse.ok) {
|
||||||
|
const galleryData = await galleryResponse.json();
|
||||||
|
images = (galleryData.images || []).map((img: any) => ({
|
||||||
|
src: `${API_BASE}${img.imageUrl}`,
|
||||||
|
alt: img.altText
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch gallery:', error);
|
||||||
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|||||||
Reference in New Issue
Block a user