diff --git a/.DS_Store b/.DS_Store index 6a91ec3..279d76c 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/admin-server.js b/admin-server.js index 89d723c..840e446 100644 --- a/admin-server.js +++ b/admin-server.js @@ -140,12 +140,14 @@ app.post('/api/categories', (req, res) => { cats.push(name); writeCategories(cats); res.json({ ok: true, categories: cats }); + rebuildAndDeploy(); }); app.delete('/api/categories/:name', (req, res) => { const cats = readCategories().filter(c => c !== req.params.name); writeCategories(cats); res.json({ ok: true, categories: cats }); + rebuildAndDeploy(); }); // ── Homepage API ────────────────────────────────────────────── @@ -277,6 +279,84 @@ app.post('/api/erfolge/image', upload.single('image'), async (req, res) => { } }); +// ── Einzelerfolge (Markdown) API ────────────────────────────── +const ERFOLGE_CONTENT_DIR = path.join(__dirname, 'content/erfolge'); + +function parseFM(content) { + const match = content.match(/^---\r?\n([\s\S]+?)\r?\n---\r?\n([\s\S]*)$/); + if (!match) return { data: {}, content }; + const fm = match[1]; + const body = match[2]; + const data = {}; + fm.split('\n').forEach(line => { + const [key, ...rest] = line.split(':'); + if (key && rest.length) data[key.trim()] = rest.join(':').trim().replace(/^"|"$/g, ''); + }); + return { data, content: body }; +} + +app.get('/api/individual-successes', (req, res) => { + const files = fs.readdirSync(ERFOLGE_CONTENT_DIR).filter(f => f.endsWith('.md') && f !== '_index.md'); + const list = files.map(f => { + const c = fs.readFileSync(path.join(ERFOLGE_CONTENT_DIR, f), 'utf8'); + const { data } = parseFM(c); + return { fileName: f, title: data.title || f, rang: data.rang, image: data.image }; + }); + res.json(list); +}); + +app.get('/api/individual-success/:file', (req, res) => { + const c = fs.readFileSync(path.join(ERFOLGE_CONTENT_DIR, req.params.file), 'utf8'); + const { data, content } = parseFM(c); + res.json({ ...data, content }); +}); + +app.put('/api/individual-success/:file', (req, res) => { + const filePath = path.join(ERFOLGE_CONTENT_DIR, req.params.file); + const oldContent = fs.readFileSync(filePath, 'utf8'); + const { data: oldData } = parseFM(oldContent); + const { title, rang, summary, content } = req.body; + + const newData = { ...oldData, title, rang, summary }; + let fmStr = '---\n'; + for (const k in newData) { + if (newData[k]) fmStr += `${k}: "${newData[k]}"\n`; + } + fmStr += '---\n'; + fs.writeFileSync(filePath, fmStr + '\n' + content); + res.json({ ok: true }); + rebuildAndDeploy(); +}); + +app.post('/api/individual-success/:file/image', upload.single('image'), async (req, res) => { + try { + const baseName = req.params.file.replace('.md', ''); + const filename = `success-${baseName}-${Date.now()}.webp`; + await sharp(req.file.buffer) + .resize(1000, 1000, { fit: 'inside', withoutEnlargement: true }) + .webp({ quality: 85 }) + .toFile(path.join(ERFOLGE_IMG_DIR, filename)); + + // In Markdown Datei schreiben + const filePath = path.join(ERFOLGE_CONTENT_DIR, req.params.file); + const c = fs.readFileSync(filePath, 'utf8'); + const { data, content } = parseFM(c); + data.image = filename; + + let fmStr = '---\n'; + for (const k in data) { + if (data[k]) fmStr += `${k}: "${data[k]}"\n`; + } + fmStr += '---\n'; + fs.writeFileSync(filePath, fmStr + '\n' + content); + + res.json({ ok: true, image: filename }); + rebuildAndDeploy(); + } catch (e) { + res.status(500).json({ ok: false, error: e.message }); + } +}); + // ── Galerie Seiten-Texte API ────────────────────────────────── app.get('/api/galerie', (req, res) => { res.json(JSON.parse(fs.readFileSync(GALERIE_PAGE_FILE, 'utf8'))); diff --git a/admin.html b/admin.html index 28eb2de..fccbe3b 100644 --- a/admin.html +++ b/admin.html @@ -197,9 +197,9 @@ tailwind.config = {

Kategorien

-

3

+

- label Training, Wettkampf, Gürtel + label Training, Wettkampf, Gürtel
@@ -685,9 +685,17 @@ tailwind.config = {
- + + +

+ article + Einzelerfolge (Berichte) +

+

Hier kannst du die Berichte bearbeiten, die auf der Startseite unter "Highlights" erscheinen.

+
@@ -849,33 +857,57 @@ tailwind.config = {
{{ range (where .Site.RegularPages "Section" "erfolge") }} -
-
+
+
{{ .Params.rang | default "Highlight" }}

{{ .Title }}

{{ .Summary }}

- - Bericht lesen arrow_forward - +
-
+ + +
+ {{ if .Params.image }} + {{ .Title }} + {{ else }} Highlight Thumbnail + {{ end }}
{{ end }} @@ -361,7 +387,28 @@ async function submitKontakt(e) { btn.textContent = 'Absenden'; } } - +function toggleSuccess(btn) { + var card = btn.closest('.success-card'); + var body = card.querySelector('.success-body'); + var icon = btn.querySelector('.material-symbols-outlined'); + var label = btn.querySelector('.btn-label'); + + if (!body.style.maxHeight || body.style.maxHeight === '0px') { + body.style.maxHeight = body.scrollHeight + 'px'; + icon.style.transform = 'rotate(180deg)'; + label.textContent = 'Weniger anzeigen'; + } else { + body.style.maxHeight = '0'; + icon.style.transform = ''; + label.textContent = 'Bericht lesen'; + } +} + +function toggleMobileMenu() { + var menu = document.getElementById('mobileMenu'); + if (menu) menu.classList.toggle('hidden'); +} + diff --git a/static/.DS_Store b/static/.DS_Store index 587d7fc..b802328 100644 Binary files a/static/.DS_Store and b/static/.DS_Store differ