pictures in the blog
Some checks failed
CI / Check Style (push) Has been cancelled
CI / Run Clippy (push) Has been cancelled
CI / Run Tests (push) Has been cancelled

This commit is contained in:
Priec
2026-05-19 21:53:55 +02:00
parent 66e6e1bf9a
commit 6feb6f210d
10 changed files with 112 additions and 95 deletions

View File

@@ -71,7 +71,6 @@
<li><a href="/admin/dashboard" data-nav="/admin/dashboard">{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/admin/blog/articles" data-nav="/admin/blog">{{ t(key="admin-blog", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/admin/audio/albums" data-nav="/admin/audio">{{ t(key="admin-audio", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/admin/images" data-nav="/admin/images">{{ t(key="admin-images", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/admin/about" data-nav="/admin/about">{{ t(key="admin-about", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/" class="t-blue">{{ t(key="admin-exit", lang=lang | default(value='sk')) }}</a></li>
<li>
@@ -93,7 +92,6 @@
<li><a href="/admin/dashboard">{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/admin/blog/articles">{{ t(key="admin-blog", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/admin/audio/albums">{{ t(key="admin-audio", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/admin/images">{{ t(key="admin-images", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/admin/about">{{ t(key="admin-about", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/" class="t-blue">{{ t(key="admin-exit", lang=lang | default(value='sk')) }}</a></li>
<li>

View File

@@ -20,12 +20,56 @@
</label>
<label>
{{ t(key="featured-image-id", lang=lang | default(value='sk')) }}
<input type="text" name="featured_image_id" value="{% if article.featured_image_id %}{{ article.featured_image_id }}{% endif %}">
<input type="text" name="featured_image_id" data-blog-image-id value="{% if article.featured_image_id %}{{ article.featured_image_id }}{% endif %}">
</label>
<div>
<input type="file" accept="image/jpeg,image/png,image/webp,image/gif" data-blog-image-file class="file-input file-input-bordered">
<button type="button" class="btn btn-outline btn-sm" data-blog-image-upload data-uploading="{{ t(key="image-uploading", lang=lang | default(value='sk')) }}" data-ready="{{ t(key="upload-featured-image", lang=lang | default(value='sk')) }}">{{ t(key="upload-featured-image", lang=lang | default(value='sk')) }}</button>
<p class="text-sm opacity-70" data-blog-image-status>{{ t(key="image-upload-help", lang=lang | default(value='sk')) }}</p>
<img data-blog-image-preview alt="" class="mt-2 max-h-48 rounded border border-base-300 object-cover" {% if article.featured_image_id %}src="/images/{{ article.featured_image_id }}"{% endif %} style="{% if not article.featured_image_id %}display: none;{% endif %}">
</div>
<label>
<input type="checkbox" name="published" {% if article.published %}checked{% endif %}>
{{ t(key="published", lang=lang | default(value='sk')) }}
</label>
<button type="submit">{{ t(key="save", lang=lang | default(value='sk')) }}</button>
</form>
<script>
(function () {
const fileInput = document.querySelector('[data-blog-image-file]');
const idInput = document.querySelector('[data-blog-image-id]');
const uploadButton = document.querySelector('[data-blog-image-upload]');
const status = document.querySelector('[data-blog-image-status]');
const preview = document.querySelector('[data-blog-image-preview]');
if (!fileInput || !idInput || !uploadButton || !status || !preview) return;
function setPreview(filename) {
if (!filename) return;
preview.src = '/images/' + filename;
preview.style.display = '';
}
uploadButton.addEventListener('click', async function () {
const file = fileInput.files && fileInput.files[0];
if (!file) return;
uploadButton.disabled = true;
uploadButton.textContent = uploadButton.dataset.uploading;
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('/images/upload', { method: 'POST', body: formData });
if (!response.ok) throw new Error('upload failed');
const result = await response.json();
idInput.value = result.filename;
setPreview(result.filename);
status.textContent = '{{ t(key="image-uploaded", lang=lang | default(value='sk')) }}';
} catch (_error) {
status.textContent = '{{ t(key="image-upload-error", lang=lang | default(value='sk')) }}';
} finally {
uploadButton.disabled = false;
uploadButton.textContent = uploadButton.dataset.ready;
}
});
})();
</script>
{% endblock content %}

View File

@@ -32,7 +32,13 @@
<div class="form-control">
<label class="label"><span class="label-text">{{ t(key="featured-image-id", lang=lang | default(value='sk')) }}</span></label>
<input type="text" name="featured_image_id" class="input input-bordered w-full">
<div class="flex flex-wrap gap-2">
<input type="text" name="featured_image_id" data-blog-image-id class="input input-bordered min-w-0 flex-1">
<input type="file" accept="image/jpeg,image/png,image/webp,image/gif" data-blog-image-file class="file-input file-input-bordered min-w-0 flex-1">
<button type="button" class="btn btn-outline btn-sm" data-blog-image-upload data-uploading="{{ t(key="image-uploading", lang=lang | default(value='sk')) }}" data-ready="{{ t(key="upload-featured-image", lang=lang | default(value='sk')) }}">{{ t(key="upload-featured-image", lang=lang | default(value='sk')) }}</button>
</div>
<p class="text-sm opacity-70" data-blog-image-status>{{ t(key="image-upload-help", lang=lang | default(value='sk')) }}</p>
<img data-blog-image-preview alt="" class="mt-2 hidden max-h-48 rounded border border-base-300 object-cover">
</div>
<label class="label cursor-pointer justify-start gap-2">
@@ -48,4 +54,42 @@
</div>
</div>
</div>
<script>
(function () {
const fileInput = document.querySelector('[data-blog-image-file]');
const idInput = document.querySelector('[data-blog-image-id]');
const uploadButton = document.querySelector('[data-blog-image-upload]');
const status = document.querySelector('[data-blog-image-status]');
const preview = document.querySelector('[data-blog-image-preview]');
if (!fileInput || !idInput || !uploadButton || !status || !preview) return;
function setPreview(filename) {
if (!filename) return;
preview.src = '/images/' + filename;
preview.classList.remove('hidden');
}
uploadButton.addEventListener('click', async function () {
const file = fileInput.files && fileInput.files[0];
if (!file) return;
uploadButton.disabled = true;
uploadButton.textContent = uploadButton.dataset.uploading;
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('/images/upload', { method: 'POST', body: formData });
if (!response.ok) throw new Error('upload failed');
const result = await response.json();
idInput.value = result.filename;
setPreview(result.filename);
status.textContent = '{{ t(key="image-uploaded", lang=lang | default(value='sk')) }}';
} catch (_error) {
status.textContent = '{{ t(key="image-upload-error", lang=lang | default(value='sk')) }}';
} finally {
uploadButton.disabled = false;
uploadButton.textContent = uploadButton.dataset.ready;
}
});
})();
</script>
{% endblock content %}

View File

@@ -1,38 +0,0 @@
{% extends "admin/base.html" %}
{% block title %}{{ t(key="admin-images", lang=lang | default(value='sk')) }}{% endblock title %}
{% block content %}
<div class="space-y-2">
<div class="flex flex-wrap items-center justify-between gap-3">
<div>
<h1 class="text-2xl font-bold">{{ t(key="admin-images", lang=lang | default(value='sk')) }}</h1>
<p class="text-sm opacity-70">{{ t(key="admin-images-upload-desc", lang=lang | default(value='sk')) }}</p>
</div>
<a href="/admin/dashboard" class="btn btn-ghost btn-sm">{{ t(key="back-to-dashboard", lang=lang | default(value='sk')) }}</a>
</div>
<div class="card border border-base-300 bg-base-100 shadow-sm">
<div class="card-body">
{% if uploaded %}
<div class="alert mb-4">
<div>
<p class="font-medium">{{ t(key="uploaded-image-id", lang=lang | default(value='sk')) }}: {{ uploaded }}</p>
<p class="text-sm opacity-70">{{ t(key="url", lang=lang | default(value='sk')) }}: {{ uploaded_url }}</p>
</div>
</div>
{% endif %}
<form method="post" action="/admin/images/upload" enctype="multipart/form-data" class="space-y-2">
<div class="form-control">
<label class="label"><span class="label-text">{{ t(key="image-file", lang=lang | default(value='sk')) }}</span></label>
<input type="file" name="file" accept="image/jpeg,image/png,image/webp,image/gif" required class="file-input file-input-bordered w-full">
</div>
<div class="flex flex-wrap gap-2 pt-2">
<button type="submit" class="btn btn-neutral btn-sm">{{ t(key="upload", lang=lang | default(value='sk')) }}</button>
</div>
</form>
</div>
</div>
</div>
{% endblock content %}

View File

@@ -56,19 +56,5 @@
</div>
</div>
</article>
<article class="card">
<div class="term-head">
<span class="term-head-name">/admin/images</span>
<span class="term-head-meta term-tag is-green">{{ t(key="admin-images", lang=lang | default(value='sk')) }}</span>
</div>
<div class="card-body">
<h2 class="card-title text-base">{{ t(key="admin-images", lang=lang | default(value='sk')) }}</h2>
<p class="text-sm opacity-70">{{ t(key="admin-images-desc", lang=lang | default(value='sk')) }}</p>
<div class="pt-2">
<a href="/admin/images" class="btn btn-primary btn-sm">[ {{ t(key="open", lang=lang | default(value='sk')) }} → ]</a>
</div>
</div>
</article>
</div>
{% endblock content %}

View File

@@ -24,6 +24,9 @@
<span class="term-head-meta term-tag">{{ t(key="post", lang=lang | default(value='sk')) }}</span>
</div>
<div class="card-body">
{% if article.featured_image_id %}
<img src="/images/{{ article.featured_image_id }}" alt="" class="mb-3 max-h-64 w-full rounded object-cover">
{% endif %}
<h2 class="card-title text-base">
<a href="/blog/{{ article.slug }}">{{ article.title }}</a>
</h2>
@@ -59,6 +62,9 @@
<span class="term-head-meta term-tag">{{ t(key="post", lang=lang | default(value='sk')) }}</span>
</div>
<div class="card-body">
{% if article.featured_image_id %}
<img src="/images/{{ article.featured_image_id }}" alt="" class="mb-3 max-h-64 w-full rounded object-cover">
{% endif %}
<h2 class="card-title text-base">
<a href="/blog/{{ article.slug }}">{{ article.title }}</a>
</h2>

View File

@@ -21,6 +21,9 @@
<span class="term-head-meta term-tag is-blue">{{ t(key="readonly", lang=lang | default(value='sk')) }}</span>
</div>
<div class="card-body">
{% if article.featured_image_id %}
<img src="/images/{{ article.featured_image_id }}" alt="" class="mb-4 max-h-[28rem] w-full rounded object-cover">
{% endif %}
{% if article.excerpt %}
<p class="term-prose t-yellow"># {{ article.excerpt }}</p>
<div class="border-t border-base-300 pt-4"></div>
@@ -45,6 +48,9 @@
<span class="term-head-meta term-tag is-blue">{{ t(key="readonly", lang=lang | default(value='sk')) }}</span>
</div>
<div class="card-body">
{% if article.featured_image_id %}
<img src="/images/{{ article.featured_image_id }}" alt="" class="mb-4 max-h-[28rem] w-full rounded object-cover">
{% endif %}
{% if article.excerpt %}
<p class="term-prose t-yellow"># {{ article.excerpt }}</p>
<div class="border-t border-base-300 pt-4"></div>