eshop
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-06-16 16:35:50 +02:00
parent c4f60dd8d7
commit baf7522273
87 changed files with 3270 additions and 3483 deletions

View File

@@ -1,36 +0,0 @@
{% extends "admin/base.html" %}
{% block title %}{{ t(key="edit-about", 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="edit-about", lang=lang | default(value='sk')) }}</h1>
<p class="text-sm opacity-70">{{ t(key="update-about-page", lang=lang | default(value='sk')) }}</p>
</div>
<a href="/about" class="btn btn-ghost btn-sm">{{ t(key="view-page", lang=lang | default(value='sk')) }}</a>
</div>
<div class="card border border-base-300 bg-base-100 shadow-sm">
<div class="card-body">
<form method="post" action="/admin/about" class="space-y-2">
<div class="form-control">
<label class="label"><span class="label-text">{{ t(key="title", lang=lang | default(value='sk')) }}</span></label>
<input type="text" name="title" value="{{ page.title }}" required class="input input-bordered w-full">
</div>
<div class="form-control">
<label class="label"><span class="label-text">{{ t(key="content", lang=lang | default(value='sk')) }}</span></label>
<textarea name="content" rows="16" required class="textarea textarea-bordered w-full">{{ page.content }}</textarea>
</div>
<div class="flex flex-wrap gap-2 pt-2">
<button type="submit" class="btn btn-neutral btn-sm">{{ t(key="save", lang=lang | default(value='sk')) }}</button>
<a href="/admin/dashboard" class="btn btn-ghost btn-sm">{{ t(key="cancel", lang=lang | default(value='sk')) }}</a>
</div>
</form>
</div>
</div>
</div>
{% endblock content %}

View File

@@ -1,81 +0,0 @@
{% extends "admin/base.html" %}
{% block title %}{{ t(key="albums-title", lang=lang | default(value='sk')) }}{% endblock title %}
{% block crumb %}audio/albums{% endblock crumb %}
{% block content %}
<header class="term-cmd">
<div>
<h1 class="term-title">{{ t(key="albums-title", lang=lang | default(value='sk')) }}</h1>
<p class="term-sub">{{ t(key="admin-albums-desc", lang=lang | default(value='sk')) }}</p>
</div>
<div class="term-cmd-actions">
<a href="/admin/audio/albums/create" class="btn btn-primary btn-sm">{{ t(key="new-album", lang=lang | default(value='sk')) }}</a>
<a href="/admin/audio/tracks" class="btn btn-outline btn-sm">{{ t(key="songs-title", lang=lang | default(value='sk')) }}</a>
</div>
</header>
<div class="term-note">
<p class="term-note-title">{{ t(key="admin-albums-before", lang=lang | default(value='sk')) }}</p>
<div class="term-step">
<span class="term-step-n">[1]</span>
<span>{{ t(key="admin-albums-step-upload", lang=lang | default(value='sk')) }}</span>
</div>
<div class="term-step">
<span class="term-step-n">[2]</span>
<span>{{ t(key="admin-albums-step-create", lang=lang | default(value='sk')) }}</span>
</div>
</div>
<div class="card">
<div class="term-head">
<span class="term-head-name">~/audio/albums/</span>
<span class="term-head-meta term-tag is-purple">{{ albums | length }} {{ t(key="albums-title", lang=lang | default(value='sk')) }}</span>
</div>
<div class="card-body">
{% if albums | length > 0 %}
<div class="overflow-x-auto">
<table class="table">
<thead>
<tr>
<th>{{ t(key="album", lang=lang | default(value='sk')) }}</th>
<th>{{ t(key="status", lang=lang | default(value='sk')) }}</th>
<th>{{ t(key="songs-title", lang=lang | default(value='sk')) }}</th>
<th class="text-right">{{ t(key="actions", lang=lang | default(value='sk')) }}</th>
</tr>
</thead>
<tbody>
{% for row in albums %}
<tr>
<td class="font-medium">{{ row.album.title }}</td>
<td>
{% if row.album.published %}
<span class="term-tag is-green">{{ t(key="published", lang=lang | default(value='sk')) }}</span>
{% else %}
<span class="term-tag">{{ t(key="draft", lang=lang | default(value='sk')) }}</span>
{% endif %}
</td>
<td>{{ row.track_count }}</td>
<td>
<div class="flex flex-wrap gap-2">
<a href="/admin/audio/albums/{{ row.album.id }}/tracks" class="btn btn-primary btn-sm">{{ t(key="open-edit", lang=lang | default(value='sk')) }}</a>
<a href="/audio/albums/{{ row.album.slug }}" class="btn btn-ghost btn-sm">{{ t(key="view", lang=lang | default(value='sk')) }}</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="term-empty">
<p class="font-medium">{{ t(key="admin-no-albums", lang=lang | default(value='sk')) }}</p>
<p class="term-empty-cmd">{{ t(key="admin-create-album-empty", lang=lang | default(value='sk')) }}</p>
<div class="pt-2">
<a href="/admin/audio/albums/create" class="btn btn-primary btn-sm">{{ t(key="new-album", lang=lang | default(value='sk')) }}</a>
</div>
</div>
{% endif %}
</div>
</div>
{% endblock content %}

View File

@@ -1,93 +0,0 @@
{% extends "admin/base.html" %}
{% block title %}{{ t(key="new-album", lang=lang | default(value='sk')) }}{% endblock title %}
{% block crumb %}audio/new-album{% endblock crumb %}
{% block content %}
<header class="term-cmd">
<div>
<h1 class="term-title">{{ t(key="new-album", lang=lang | default(value='sk')) }}</h1>
<p class="term-sub">{{ t(key="admin-new-album-desc", lang=lang | default(value='sk')) }}</p>
</div>
<div class="term-cmd-actions">
<a href="/admin/audio/albums" class="btn btn-outline btn-sm">{{ t(key="cancel", lang=lang | default(value='sk')) }}</a>
</div>
</header>
<div class="card">
<div class="term-head">
<span class="term-head-name">~/audio/albums/new</span>
</div>
<div class="card-body">
<form method="post" action="/admin/audio/albums/create" enctype="multipart/form-data" class="space-y-2">
<div class="form-control">
<label class="label"><span class="label-text t-green">{{ t(key="album-title-label", lang=lang | default(value='sk')) }}</span></label>
<input type="text" name="title" required class="input input-bordered w-full">
</div>
<div class="form-control">
<label class="label"><span class="label-text t-green">{{ t(key="artist", lang=lang | default(value='sk')) }}</span></label>
<input type="text" name="artist" class="input input-bordered w-full">
</div>
<div class="form-control">
<label class="label"><span class="label-text t-green">{{ t(key="release-date", lang=lang | default(value='sk')) }}</span></label>
<input type="date" name="release_date" class="input input-bordered w-full">
</div>
<div class="form-control">
<label class="label"><span class="label-text t-green">{{ t(key="cover-image", lang=lang | default(value='sk')) }}</span></label>
<input type="file" name="cover" accept="image/png,image/jpeg,image/webp,image/gif" class="file-input file-input-bordered w-full">
<p class="term-help">{{ t(key="cover-help", lang=lang | default(value='sk')) }}</p>
</div>
<div class="form-control">
<label class="label"><span class="label-text t-green">{{ t(key="description", lang=lang | default(value='sk')) }}</span></label>
<textarea name="description" rows="5" class="textarea textarea-bordered w-full"></textarea>
</div>
<div class="term-formdiv"></div>
<div class="form-control">
<label class="label"><span class="label-text t-green">{{ t(key="songs-in-album", lang=lang | default(value='sk')) }}</span></label>
{% if available_tracks | length > 0 %}
<div class="term-picklist">
{% for song in available_tracks %}
<label class="term-pick">
<input type="checkbox" name="track_ids" value="{{ song.id }}" class="checkbox checkbox-sm">
<span class="min-w-0 flex-1 font-medium">{{ song.title }}</span>
{% if song.published %}
<span class="term-tag is-green">{{ t(key="published", lang=lang | default(value='sk')) }}</span>
{% else %}
<span class="term-tag">{{ t(key="draft", lang=lang | default(value='sk')) }}</span>
{% endif %}
</label>
{% endfor %}
</div>
<p class="term-help">{{ t(key="free-songs-help", lang=lang | default(value='sk')) }}</p>
{% else %}
<div class="term-picklist">
<div class="term-pick">
<span class="term-help" style="margin:0">
{{ t(key="no-free-songs", lang=lang | default(value='sk')) }}
<a href="/admin/audio/tracks/upload" class="t-blue">{{ t(key="upload-song-first", lang=lang | default(value='sk')) }}</a>,
{{ t(key="create-empty-add-later", lang=lang | default(value='sk')) }}
</span>
</div>
</div>
{% endif %}
</div>
<label class="label cursor-pointer justify-start gap-2">
<input type="checkbox" name="published" class="checkbox checkbox-sm">
<span class="label-text">{{ t(key="publish-album-now", lang=lang | default(value='sk')) }}</span>
</label>
<div class="flex flex-wrap gap-2 pt-2">
<button type="submit" class="btn btn-primary btn-sm">{{ t(key="create-album", lang=lang | default(value='sk')) }}</button>
<a href="/admin/audio/albums" class="btn btn-ghost btn-sm">{{ t(key="cancel", lang=lang | default(value='sk')) }}</a>
</div>
</form>
</div>
</div>
{% endblock content %}

View File

@@ -1,99 +0,0 @@
{% extends "admin/base.html" %}
{% block title %}{{ t(key="songs-title-admin", lang=lang | default(value='sk')) }}{% endblock title %}
{% block crumb %}audio/songs{% endblock crumb %}
{% block content %}
<header class="term-cmd">
<div>
<h1 class="term-title">{{ t(key="songs-title-admin", lang=lang | default(value='sk')) }}</h1>
<p class="term-sub">{{ t(key="admin-songs-desc", lang=lang | default(value='sk')) }}</p>
</div>
<div class="term-cmd-actions">
<a href="/admin/audio/tracks/upload" class="btn btn-primary btn-sm">{{ t(key="upload-song", lang=lang | default(value='sk')) }}</a>
<a href="/admin/audio/albums" class="btn btn-outline btn-sm">{{ t(key="albums-title", lang=lang | default(value='sk')) }}</a>
</div>
</header>
<div class="term-note">
<p class="term-note-title">{{ t(key="admin-audio-how", lang=lang | default(value='sk')) }}</p>
<div class="term-step">
<span class="term-step-n">[1]</span>
<span>{{ t(key="admin-audio-step-upload", lang=lang | default(value='sk')) }}</span>
</div>
<div class="term-step">
<span class="term-step-n">[2]</span>
<span>{{ t(key="admin-audio-step-album", lang=lang | default(value='sk')) }}</span>
</div>
<p class="term-note-foot">{{ t(key="admin-audio-note", lang=lang | default(value='sk')) }}</p>
</div>
<div class="card">
<div class="term-head">
<span class="term-head-name">~/audio/songs/</span>
<span class="term-head-meta term-tag is-green">{{ tracks | length }} {{ t(key="songs-title", lang=lang | default(value='sk')) }}</span>
</div>
<div class="card-body">
{% if tracks | length > 0 %}
<div class="overflow-x-auto">
<table class="table">
<thead>
<tr>
<th>{{ t(key="song", lang=lang | default(value='sk')) }}</th>
<th>{{ t(key="where", lang=lang | default(value='sk')) }}</th>
<th>{{ t(key="status", lang=lang | default(value='sk')) }}</th>
<th class="text-right">{{ t(key="actions", lang=lang | default(value='sk')) }}</th>
</tr>
</thead>
<tbody>
{% for track in tracks %}
<tr>
<td class="font-medium">{{ track.title }}</td>
<td>
{% if track.album_id %}
<span class="term-tag is-purple">{{ t(key="in-album", lang=lang | default(value='sk')) }}</span>
{% else %}
<span class="term-tag is-blue">{{ t(key="single", lang=lang | default(value='sk')) }}</span>
{% endif %}
</td>
<td>
{% if track.published %}
<span class="term-tag is-green">{{ t(key="published", lang=lang | default(value='sk')) }}</span>
{% else %}
<span class="term-tag">{{ t(key="draft", lang=lang | default(value='sk')) }}</span>
{% endif %}
</td>
<td>
<div class="flex flex-wrap gap-2">
<a href="/audio/tracks/{{ track.id }}/stream" class="btn btn-ghost btn-sm">{{ t(key="play", lang=lang | default(value='sk')) }}</a>
{% if track.published %}
<form method="post" action="/admin/audio/tracks/{{ track.id }}/unpublish">
<button type="submit" class="btn btn-ghost btn-sm">{{ t(key="unpublish", lang=lang | default(value='sk')) }}</button>
</form>
{% else %}
<form method="post" action="/admin/audio/tracks/{{ track.id }}/publish">
<button type="submit" class="btn btn-ghost btn-sm t-green">{{ t(key="publish", lang=lang | default(value='sk')) }}</button>
</form>
{% endif %}
<form method="post" action="/admin/audio/tracks/{{ track.id }}/delete">
<button type="submit" class="btn btn-ghost btn-sm t-red">{{ t(key="delete", lang=lang | default(value='sk')) }}</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="term-empty">
<p class="font-medium">{{ t(key="admin-no-songs", lang=lang | default(value='sk')) }}</p>
<p class="term-empty-cmd">{{ t(key="admin-upload-first-song", lang=lang | default(value='sk')) }}</p>
<div class="pt-2">
<a href="/admin/audio/tracks/upload" class="btn btn-primary btn-sm">{{ t(key="upload-song", lang=lang | default(value='sk')) }}</a>
</div>
</div>
{% endif %}
</div>
</div>
{% endblock content %}

View File

@@ -1,123 +0,0 @@
{% extends "admin/base.html" %}
{% block title %}{{ album.title }} - {{ t(key="admin-tracklist", lang=lang | default(value='sk')) }}{% endblock title %}
{% block crumb %}audio/{{ album.slug }}{% endblock crumb %}
{% block content %}
<header class="term-cmd">
<div>
<h1 class="term-title">{{ album.title }}</h1>
<p class="term-sub">
{{ t(key="album", lang=lang | default(value='sk')) }} &middot; {{ tracks | length }} {{ t(key="songs-title", lang=lang | default(value='sk')) }} &middot;
{% if album.published %}<span class="t-green">{{ t(key="published", lang=lang | default(value='sk')) }}</span>{% else %}<span class="t-yellow">{{ t(key="draft", lang=lang | default(value='sk')) }}</span>{% endif %}
</p>
</div>
<div class="term-cmd-actions">
<a href="/admin/audio/albums/{{ album.id }}/tracks/upload" class="btn btn-primary btn-sm">{{ t(key="upload-song-into-album", lang=lang | default(value='sk')) }}</a>
<a href="/audio/albums/{{ album.slug }}" class="btn btn-outline btn-sm">{{ t(key="view", lang=lang | default(value='sk')) }}</a>
<a href="/admin/audio/albums" class="btn btn-outline btn-sm">{{ t(key="albums-title", lang=lang | default(value='sk')) }}</a>
</div>
</header>
<div class="term-note">
<p class="term-note-title">{{ t(key="admin-two-ways-title", lang=lang | default(value='sk')) }}</p>
<div class="term-step">
<span class="term-step-n">[a]</span>
<span>{{ t(key="admin-two-ways-upload", lang=lang | default(value='sk')) }}</span>
</div>
<div class="term-step">
<span class="term-step-n">[b]</span>
<span>{{ t(key="admin-two-ways-pick", lang=lang | default(value='sk')) }}</span>
</div>
</div>
<div class="card">
<div class="term-head">
<span class="term-head-name">~/audio/{{ album.slug }}/tracklist</span>
<span class="term-head-meta term-tag is-purple">{{ tracks | length }} {{ t(key="songs-title", lang=lang | default(value='sk')) }}</span>
</div>
<div class="card-body">
{% if available_tracks | length > 0 %}
<form method="post" action="/admin/audio/albums/{{ album.id }}/tracks/add" class="space-y-2">
<div class="form-control">
<label class="label"><span class="label-text t-green">{{ t(key="admin-add-existing-song", lang=lang | default(value='sk')) }}</span></label>
<select name="track_id" required class="select select-bordered w-full">
{% for song in available_tracks %}
<option value="{{ song.id }}">{{ song.title }}</option>
{% endfor %}
</select>
<p class="term-help">{{ t(key="admin-existing-song-help", lang=lang | default(value='sk')) }}</p>
</div>
<button type="submit" class="btn btn-outline btn-sm">{{ t(key="admin-add-to-album", lang=lang | default(value='sk')) }}</button>
</form>
<div class="term-formdiv"></div>
{% endif %}
{% if tracks | length > 0 %}
<div class="overflow-x-auto">
<table class="table">
<thead>
<tr>
<th>#</th>
<th>{{ t(key="song", lang=lang | default(value='sk')) }}</th>
<th>{{ t(key="status", lang=lang | default(value='sk')) }}</th>
<th>{{ t(key="featured", lang=lang | default(value='sk')) }}</th>
<th class="text-right">{{ t(key="actions", lang=lang | default(value='sk')) }}</th>
</tr>
</thead>
<tbody>
{% for track in tracks %}
<tr>
<td class="t-dim">{% if track.track_number %}{{ track.track_number }}{% else %}&mdash;{% endif %}</td>
<td class="font-medium">{{ track.title }}</td>
<td>
{% if track.published %}
<span class="term-tag is-green">{{ t(key="published", lang=lang | default(value='sk')) }}</span>
{% else %}
<span class="term-tag">{{ t(key="draft", lang=lang | default(value='sk')) }}</span>
{% endif %}
</td>
<td>
{% if track.featured %}
<span class="term-tag is-aqua">{{ t(key="featured", lang=lang | default(value='sk')) }}</span>
{% else %}
<span class="t-dim"></span>
{% endif %}
</td>
<td>
<div class="flex flex-wrap gap-2">
<a href="/audio/tracks/{{ track.id }}/stream" class="btn btn-ghost btn-sm">{{ t(key="play", lang=lang | default(value='sk')) }}</a>
{% if track.published %}
<form method="post" action="/admin/audio/tracks/{{ track.id }}/unpublish">
<button type="submit" class="btn btn-ghost btn-sm">{{ t(key="unpublish", lang=lang | default(value='sk')) }}</button>
</form>
{% else %}
<form method="post" action="/admin/audio/tracks/{{ track.id }}/publish">
<button type="submit" class="btn btn-ghost btn-sm t-green">{{ t(key="publish", lang=lang | default(value='sk')) }}</button>
</form>
{% endif %}
<form method="post" action="/admin/audio/tracks/{{ track.id }}/remove-from-album">
<button type="submit" class="btn btn-ghost btn-sm">{{ t(key="remove-from-album", lang=lang | default(value='sk')) }}</button>
</form>
<form method="post" action="/admin/audio/tracks/{{ track.id }}/delete">
<button type="submit" class="btn btn-ghost btn-sm t-red">{{ t(key="delete", lang=lang | default(value='sk')) }}</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="term-empty">
<p class="font-medium">{{ t(key="admin-album-empty", lang=lang | default(value='sk')) }}</p>
<p class="term-empty-cmd">{{ t(key="admin-album-empty-help", lang=lang | default(value='sk')) }}</p>
<div class="pt-2">
<a href="/admin/audio/albums/{{ album.id }}/tracks/upload" class="btn btn-primary btn-sm">{{ t(key="upload-song-into-album", lang=lang | default(value='sk')) }}</a>
</div>
</div>
{% endif %}
</div>
</div>
{% endblock content %}

View File

@@ -1,76 +0,0 @@
{% extends "admin/base.html" %}
{% block title %}{{ t(key="upload-song-title", lang=lang | default(value='sk')) }}{% endblock title %}
{% block crumb %}audio/upload{% endblock crumb %}
{% block content %}
<header class="term-cmd">
<div>
<h1 class="term-title">{{ t(key="upload-song-title", lang=lang | default(value='sk')) }}</h1>
{% if album %}
<p class="term-sub">{{ t(key="upload-into-album-help", lang=lang | default(value='sk')) }} "{{ album.title }}".</p>
{% else %}
<p class="term-sub">{{ t(key="upload-single-help", lang=lang | default(value='sk')) }}</p>
{% endif %}
</div>
<div class="term-cmd-actions">
{% if album %}
<a href="/admin/audio/albums/{{ album.id }}/tracks" class="btn btn-outline btn-sm">{{ t(key="cancel", lang=lang | default(value='sk')) }}</a>
{% else %}
<a href="/admin/audio/tracks" class="btn btn-outline btn-sm">{{ t(key="cancel", lang=lang | default(value='sk')) }}</a>
{% endif %}
</div>
</header>
<div class="card">
<div class="term-head">
<span class="term-head-name">{% if album %}~/audio/{{ album.slug }}/upload{% else %}~/audio/songs/upload{% endif %}</span>
</div>
<div class="card-body">
{% if album %}
<form method="post" action="/admin/audio/albums/{{ album.id }}/tracks/upload-file" enctype="multipart/form-data" class="space-y-2">
{% else %}
<form method="post" action="/admin/audio/tracks/upload-file" enctype="multipart/form-data" class="space-y-2">
{% endif %}
<div class="form-control">
<label class="label"><span class="label-text t-green">1. {{ t(key="audio-file", lang=lang | default(value='sk')) }}</span></label>
<input type="file" name="file" accept="audio/mpeg,audio/wav,audio/ogg,audio/flac,audio/aac,audio/mp4,audio/webm" required class="file-input file-input-bordered w-full">
<p class="term-help">{{ t(key="audio-file-help", lang=lang | default(value='sk')) }}</p>
</div>
<div class="form-control">
<label class="label"><span class="label-text t-green">2. {{ t(key="title", lang=lang | default(value='sk')) }}</span></label>
<input type="text" name="title" class="input input-bordered w-full">
<p class="term-help">{{ t(key="title-help", lang=lang | default(value='sk')) }}</p>
</div>
{% if album %}
<div class="form-control">
<label class="label"><span class="label-text t-green">3. {{ t(key="track-number", lang=lang | default(value='sk')) }}</span></label>
<input type="number" name="track_number" min="1" class="input input-bordered w-full" placeholder="1">
<p class="term-help">{{ t(key="track-number-help", lang=lang | default(value='sk')) }}</p>
</div>
{% endif %}
<label class="label cursor-pointer justify-start gap-2">
<input type="checkbox" name="featured" class="checkbox checkbox-sm">
<span class="label-text">{{ t(key="featured-help", lang=lang | default(value='sk')) }}</span>
</label>
<label class="label cursor-pointer justify-start gap-2">
<input type="checkbox" name="published" class="checkbox checkbox-sm">
<span class="label-text">{{ t(key="publish-song-now", lang=lang | default(value='sk')) }}</span>
</label>
<div class="flex flex-wrap gap-2 pt-2">
<button type="submit" class="btn btn-primary btn-sm">{{ t(key="upload-song", lang=lang | default(value='sk')) }}</button>
{% if album %}
<a href="/admin/audio/albums/{{ album.id }}/tracks" class="btn btn-ghost btn-sm">{{ t(key="cancel", lang=lang | default(value='sk')) }}</a>
{% else %}
<a href="/admin/audio/tracks" class="btn btn-ghost btn-sm">{{ t(key="cancel", lang=lang | default(value='sk')) }}</a>
{% endif %}
</div>
</form>
</div>
</div>
{% endblock content %}

View File

@@ -44,114 +44,127 @@
<script defer src="/static/vendor/alpine/alpinejs-3.14.9.min.js"></script>
</head>
<body
class="flex min-h-screen flex-col bg-surface text-on-surface antialiased dark:bg-surface-dark dark:text-on-surface-dark">
<header
class="sticky top-0 z-30 border-b border-outline bg-surface/95 backdrop-blur dark:border-outline-dark dark:bg-surface-dark/95">
<nav x-data="{ mobile: false }" class="mx-auto flex max-w-6xl items-center gap-4 px-4 py-3">
<a href="/admin/dashboard"
class="text-lg font-bold tracking-tight text-on-surface-strong dark:text-on-surface-dark-strong">
{{ t(key="admin-title", lang=lang | default(value='sk')) }}
x-data="{ showSidebar: false }"
class="min-h-screen bg-surface text-on-surface antialiased dark:bg-surface-dark dark:text-on-surface-dark">
<!-- dark overlay for the open sidebar on small screens -->
<div x-cloak x-show="showSidebar" x-transition.opacity aria-hidden="true"
@click="showSidebar = false"
class="fixed inset-0 z-30 bg-black/50 md:hidden"></div>
<!-- sidebar -->
<nav aria-label="{{ t(key='menu', lang=lang | default(value='sk')) }}"
x-bind:class="showSidebar ? 'translate-x-0' : '-translate-x-60'"
class="fixed inset-y-0 left-0 z-40 flex w-60 flex-col border-r border-outline bg-surface-alt transition-transform duration-300 md:translate-x-0 dark:border-outline-dark dark:bg-surface-dark-alt">
<a href="/admin/dashboard"
class="flex h-16 items-center gap-2 border-b border-outline px-6 text-lg font-bold tracking-tight text-on-surface-strong dark:border-outline-dark dark:text-on-surface-dark-strong">
{{ t(key="admin-title", lang=lang | default(value='sk')) }}
</a>
<div class="flex flex-1 flex-col gap-1 overflow-y-auto p-4">
<a href="/admin/dashboard" data-nav="/admin/dashboard"
class="flex items-center gap-3 rounded-radius px-3 py-2 text-sm font-medium text-on-surface transition hover:bg-surface hover:text-primary aria-[current=page]:bg-primary aria-[current=page]:text-on-primary dark:text-on-surface-dark dark:hover:bg-surface-dark dark:hover:text-primary-dark dark:aria-[current=page]:bg-primary-dark dark:aria-[current=page]:text-on-primary-dark">
{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}
</a>
<a href="/admin/catalog/products" data-nav="/admin/catalog/products"
class="flex items-center gap-3 rounded-radius px-3 py-2 text-sm font-medium text-on-surface transition hover:bg-surface hover:text-primary aria-[current=page]:bg-primary aria-[current=page]:text-on-primary dark:text-on-surface-dark dark:hover:bg-surface-dark dark:hover:text-primary-dark dark:aria-[current=page]:bg-primary-dark dark:aria-[current=page]:text-on-primary-dark">
{{ t(key="admin-products", lang=lang | default(value='sk')) }}
</a>
<a href="/admin/catalog/categories" data-nav="/admin/catalog/categories"
class="flex items-center gap-3 rounded-radius px-3 py-2 text-sm font-medium text-on-surface transition hover:bg-surface hover:text-primary aria-[current=page]:bg-primary aria-[current=page]:text-on-primary dark:text-on-surface-dark dark:hover:bg-surface-dark dark:hover:text-primary-dark dark:aria-[current=page]:bg-primary-dark dark:aria-[current=page]:text-on-primary-dark">
{{ t(key="admin-categories", lang=lang | default(value='sk')) }}
</a>
<a href="/admin/orders" data-nav="/admin/orders"
class="flex items-center gap-3 rounded-radius px-3 py-2 text-sm font-medium text-on-surface transition hover:bg-surface hover:text-primary aria-[current=page]:bg-primary aria-[current=page]:text-on-primary dark:text-on-surface-dark dark:hover:bg-surface-dark dark:hover:text-primary-dark dark:aria-[current=page]:bg-primary-dark dark:aria-[current=page]:text-on-primary-dark">
{{ t(key="admin-orders", lang=lang | default(value='sk')) }}
</a>
</div>
<!-- desktop links -->
<ul class="ml-2 hidden items-center gap-1 md:flex">
<li><a href="/admin/dashboard" data-nav="/admin/dashboard" class="rounded-radius px-3 py-1.5 text-sm font-medium text-on-surface transition hover:bg-surface-alt hover:text-primary aria-[current=page]:text-primary aria-[current=page]:font-semibold dark:text-on-surface-dark dark:hover:bg-surface-dark-alt dark:hover:text-primary-dark dark:aria-[current=page]:text-primary-dark">{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/admin/blog/articles" data-nav="/admin/blog" class="rounded-radius px-3 py-1.5 text-sm font-medium text-on-surface transition hover:bg-surface-alt hover:text-primary aria-[current=page]:text-primary aria-[current=page]:font-semibold dark:text-on-surface-dark dark:hover:bg-surface-dark-alt dark:hover:text-primary-dark dark:aria-[current=page]:text-primary-dark">{{ t(key="admin-blog", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/admin/audio/albums" data-nav="/admin/audio" class="rounded-radius px-3 py-1.5 text-sm font-medium text-on-surface transition hover:bg-surface-alt hover:text-primary aria-[current=page]:text-primary aria-[current=page]:font-semibold dark:text-on-surface-dark dark:hover:bg-surface-dark-alt dark:hover:text-primary-dark dark:aria-[current=page]:text-primary-dark">{{ t(key="admin-audio", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/admin/about" data-nav="/admin/about" class="rounded-radius px-3 py-1.5 text-sm font-medium text-on-surface transition hover:bg-surface-alt hover:text-primary aria-[current=page]:text-primary aria-[current=page]:font-semibold dark:text-on-surface-dark dark:hover:bg-surface-dark-alt dark:hover:text-primary-dark dark:aria-[current=page]:text-primary-dark">{{ t(key="admin-about", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/" class="rounded-radius px-3 py-1.5 text-sm font-medium text-info transition hover:bg-surface-alt dark:hover:bg-surface-dark-alt">{{ t(key="admin-exit", lang=lang | default(value='sk')) }}</a></li>
<li>
<form method="post" action="/admin/logout">
<button type="submit" class="rounded-radius px-3 py-1.5 text-sm font-medium text-danger transition hover:bg-surface-alt dark:hover:bg-surface-dark-alt">{{ t(key="logout", lang=lang | default(value='sk')) }}</button>
</form>
</li>
</ul>
<div class="border-t border-outline p-4 dark:border-outline-dark">
<a href="/" class="flex items-center gap-3 rounded-radius px-3 py-2 text-sm font-medium text-info transition hover:bg-surface dark:hover:bg-surface-dark">
{{ t(key="admin-exit", lang=lang | default(value='sk')) }}
</a>
<form method="post" action="/admin/logout">
<button type="submit" class="flex w-full items-center gap-3 rounded-radius px-3 py-2 text-left text-sm font-medium text-danger transition hover:bg-surface dark:hover:bg-surface-dark">
{{ t(key="logout", lang=lang | default(value='sk')) }}
</button>
</form>
</div>
</nav>
<div class="ml-auto flex items-center gap-1">
<!-- settings (language + theme) dropdown -->
<div x-data="{ open: false }" @keydown.escape="open = false" class="relative">
<button type="button" @click="open = !open" :aria-expanded="open"
aria-label="{{ t(key='settings', lang=lang | default(value='sk')) }}"
title="{{ t(key='settings', lang=lang | default(value='sk')) }}"
class="inline-flex size-9 items-center justify-center rounded-radius text-on-surface transition hover:bg-surface-alt dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round"
d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>
</button>
<div x-show="open" x-cloak @click.outside="open = false"
x-transition.origin.top.right
class="absolute right-0 mt-2 w-56 rounded-radius border border-outline bg-surface p-2 shadow-lg dark:border-outline-dark dark:bg-surface-dark-alt">
<form method="post" action="/lang" hx-boost="false">
<p class="px-2 py-1 text-xs font-semibold uppercase tracking-wide text-on-surface/60 dark:text-on-surface-dark/60">
{{ t(key="settings-language", lang=lang | default(value='sk')) }}
</p>
<button type="submit" name="lang" value="en"
class="flex w-full items-center justify-between rounded-radius px-2 py-1.5 text-sm text-on-surface transition hover:bg-surface-alt dark:text-on-surface-dark dark:hover:bg-surface-dark">
<span>{{ t(key="language-en", lang=lang | default(value='sk')) }}</span>
{% if lang | default(value='sk') == "en" %}<span class="text-primary dark:text-primary-dark"></span>{% endif %}
</button>
<button type="submit" name="lang" value="sk"
class="flex w-full items-center justify-between rounded-radius px-2 py-1.5 text-sm text-on-surface transition hover:bg-surface-alt dark:text-on-surface-dark dark:hover:bg-surface-dark">
<span>{{ t(key="language-sk", lang=lang | default(value='sk')) }}</span>
{% if lang | default(value='sk') == "sk" %}<span class="text-primary dark:text-primary-dark"></span>{% endif %}
</button>
</form>
<p class="mt-1 px-2 py-1 text-xs font-semibold uppercase tracking-wide text-on-surface/60 dark:text-on-surface-dark/60">
{{ t(key="settings-theme", lang=lang | default(value='sk')) }}
</p>
<div x-data="{ theme: currentTheme() }" @theme:changed.document="theme = $event.detail">
<button type="button" @click="setTheme('system')"
class="flex w-full items-center justify-between rounded-radius px-2 py-1.5 text-sm text-on-surface transition hover:bg-surface-alt dark:text-on-surface-dark dark:hover:bg-surface-dark">
<span>{{ t(key="theme-system", lang=lang | default(value='sk')) }}</span>
<span x-show="theme === 'system'" class="text-primary dark:text-primary-dark"></span>
</button>
<button type="button" @click="setTheme('light')"
class="flex w-full items-center justify-between rounded-radius px-2 py-1.5 text-sm text-on-surface transition hover:bg-surface-alt dark:text-on-surface-dark dark:hover:bg-surface-dark">
<span>{{ t(key="theme-light", lang=lang | default(value='sk')) }}</span>
<span x-show="theme === 'light'" class="text-primary dark:text-primary-dark"></span>
</button>
<button type="button" @click="setTheme('dark')"
class="flex w-full items-center justify-between rounded-radius px-2 py-1.5 text-sm text-on-surface transition hover:bg-surface-alt dark:text-on-surface-dark dark:hover:bg-surface-dark">
<span>{{ t(key="theme-dark", lang=lang | default(value='sk')) }}</span>
<span x-show="theme === 'dark'" class="text-primary dark:text-primary-dark"></span>
</button>
</div>
</div>
</div>
<!-- content column -->
<div class="flex min-h-screen flex-col md:ml-60">
<header class="sticky top-0 z-20 flex h-16 items-center gap-4 border-b border-outline bg-surface/95 px-4 backdrop-blur dark:border-outline-dark dark:bg-surface-dark/95">
<button type="button" @click="showSidebar = !showSidebar" :aria-expanded="showSidebar"
aria-label="{{ t(key='menu', lang=lang | default(value='sk')) }}"
class="inline-flex size-9 items-center justify-center rounded-radius text-on-surface transition hover:bg-surface-alt md:hidden dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
</svg>
</button>
<!-- mobile hamburger -->
<button type="button" @click="mobile = !mobile" :aria-expanded="mobile"
aria-label="{{ t(key='menu', lang=lang | default(value='sk')) }}"
class="inline-flex size-9 items-center justify-center rounded-radius text-on-surface transition hover:bg-surface-alt md:hidden dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">
<span class="text-sm font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">
{% block crumb %}{{ t(key="admin-title", lang=lang | default(value='sk')) }}{% endblock crumb %}
</span>
<!-- settings (language + theme) dropdown -->
<div x-data="{ open: false }" @keydown.escape="open = false" class="relative ml-auto">
<button type="button" @click="open = !open" :aria-expanded="open"
aria-label="{{ t(key='settings', lang=lang | default(value='sk')) }}"
class="inline-flex size-9 items-center justify-center rounded-radius text-on-surface transition hover:bg-surface-alt dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round"
d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>
</button>
</div>
<!-- mobile menu panel -->
<ul x-show="mobile" x-cloak @click.outside="mobile = false" x-transition
class="absolute inset-x-0 top-full mx-4 mt-2 flex flex-col gap-1 rounded-radius border border-outline bg-surface p-2 shadow-lg md:hidden dark:border-outline-dark dark:bg-surface-dark-alt">
<li><a href="/admin/dashboard" class="block rounded-radius px-3 py-2 text-sm font-medium text-on-surface transition hover:bg-surface-alt hover:text-primary dark:text-on-surface-dark dark:hover:bg-surface-dark dark:hover:text-primary-dark">{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/admin/blog/articles" class="block rounded-radius px-3 py-2 text-sm font-medium text-on-surface transition hover:bg-surface-alt hover:text-primary dark:text-on-surface-dark dark:hover:bg-surface-dark dark:hover:text-primary-dark">{{ t(key="admin-blog", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/admin/audio/albums" class="block rounded-radius px-3 py-2 text-sm font-medium text-on-surface transition hover:bg-surface-alt hover:text-primary dark:text-on-surface-dark dark:hover:bg-surface-dark dark:hover:text-primary-dark">{{ t(key="admin-audio", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/admin/about" class="block rounded-radius px-3 py-2 text-sm font-medium text-on-surface transition hover:bg-surface-alt hover:text-primary dark:text-on-surface-dark dark:hover:bg-surface-dark dark:hover:text-primary-dark">{{ t(key="admin-about", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/" class="block rounded-radius px-3 py-2 text-sm font-medium text-info hover:bg-surface-alt dark:hover:bg-surface-dark">{{ t(key="admin-exit", lang=lang | default(value='sk')) }}</a></li>
<li>
<form method="post" action="/admin/logout">
<button type="submit" class="block w-full rounded-radius px-3 py-2 text-left text-sm font-medium text-danger hover:bg-surface-alt dark:hover:bg-surface-dark">{{ t(key="logout", lang=lang | default(value='sk')) }}</button>
<div x-show="open" x-cloak @click.outside="open = false" x-transition.origin.top.right
class="absolute right-0 mt-2 w-56 rounded-radius border border-outline bg-surface p-2 shadow-lg dark:border-outline-dark dark:bg-surface-dark-alt">
<form method="post" action="/lang" hx-boost="false">
<p class="px-2 py-1 text-xs font-semibold uppercase tracking-wide text-on-surface/60 dark:text-on-surface-dark/60">
{{ t(key="settings-language", lang=lang | default(value='sk')) }}
</p>
<button type="submit" name="lang" value="en"
class="flex w-full items-center justify-between rounded-radius px-2 py-1.5 text-sm text-on-surface transition hover:bg-surface-alt dark:text-on-surface-dark dark:hover:bg-surface-dark">
<span>English</span>
{% if lang | default(value='sk') == "en" %}<span class="text-primary dark:text-primary-dark"></span>{% endif %}
</button>
<button type="submit" name="lang" value="sk"
class="flex w-full items-center justify-between rounded-radius px-2 py-1.5 text-sm text-on-surface transition hover:bg-surface-alt dark:text-on-surface-dark dark:hover:bg-surface-dark">
<span>Slovenčina</span>
{% if lang | default(value='sk') == "sk" %}<span class="text-primary dark:text-primary-dark"></span>{% endif %}
</button>
</form>
</li>
</ul>
</nav>
</header>
<p class="mt-1 px-2 py-1 text-xs font-semibold uppercase tracking-wide text-on-surface/60 dark:text-on-surface-dark/60">
{{ t(key="settings-theme", lang=lang | default(value='sk')) }}
</p>
<div x-data="{ theme: currentTheme() }" @theme:changed.document="theme = $event.detail">
<button type="button" @click="setTheme('system')"
class="flex w-full items-center justify-between rounded-radius px-2 py-1.5 text-sm text-on-surface transition hover:bg-surface-alt dark:text-on-surface-dark dark:hover:bg-surface-dark">
<span>{{ t(key="theme-system", lang=lang | default(value='sk')) }}</span>
<span x-show="theme === 'system'" class="text-primary dark:text-primary-dark"></span>
</button>
<button type="button" @click="setTheme('light')"
class="flex w-full items-center justify-between rounded-radius px-2 py-1.5 text-sm text-on-surface transition hover:bg-surface-alt dark:text-on-surface-dark dark:hover:bg-surface-dark">
<span>{{ t(key="theme-light", lang=lang | default(value='sk')) }}</span>
<span x-show="theme === 'light'" class="text-primary dark:text-primary-dark"></span>
</button>
<button type="button" @click="setTheme('dark')"
class="flex w-full items-center justify-between rounded-radius px-2 py-1.5 text-sm text-on-surface transition hover:bg-surface-alt dark:text-on-surface-dark dark:hover:bg-surface-dark">
<span>{{ t(key="theme-dark", lang=lang | default(value='sk')) }}</span>
<span x-show="theme === 'dark'" class="text-primary dark:text-primary-dark"></span>
</button>
</div>
</div>
</div>
</header>
<main class="mx-auto w-full max-w-6xl flex-1 px-4 py-8">
{% block content %}{% endblock content %}
</main>
<main class="mx-auto w-full max-w-5xl flex-1 px-4 py-8">
{% block content %}{% endblock content %}
</main>
</div>
</body>
</html>

View File

@@ -1,59 +0,0 @@
{% extends "admin/base.html" %}
{% block title %}{{ t(key="edit-article", lang=lang | default(value='sk')) }}{% endblock title %}
{% block head %}{% endblock head %}
{% 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="edit-article", lang=lang | default(value='sk')) }}</h1>
</div>
<a href="/admin/blog/articles" class="btn btn-ghost btn-sm">{{ t(key="back-to-articles", lang=lang | default(value='sk')) }}</a>
</div>
<div class="card border border-base-300 bg-base-100 shadow-sm">
<div class="card-body">
<form method="post" action="/admin/blog/articles/{{ article.id }}" class="space-y-2">
<div class="form-control">
<label class="label"><span class="label-text">{{ t(key="title", lang=lang | default(value='sk')) }}</span></label>
<input type="text" name="title" value="{{ article.title }}" required class="input input-bordered w-full">
</div>
<div class="form-control">
<label class="label"><span class="label-text">{{ t(key="excerpt", lang=lang | default(value='sk')) }}</span></label>
<textarea name="excerpt" rows="4" class="textarea textarea-bordered w-full">{% if article.excerpt %}{{ article.excerpt }}{% endif %}</textarea>
</div>
<div class="form-control">
<label class="label"><span class="label-text">{{ t(key="content", lang=lang | default(value='sk')) }}</span></label>
<textarea name="content" data-rich-content class="hidden">{{ article.content }}</textarea>
<input type="hidden" name="featured_image_id" data-featured-image-id value="{% if article.featured_image_id %}{{ article.featured_image_id }}{% endif %}">
<div data-rich-editor class="blog-editor"></div>
<div data-image-size-controls class="blog-image-size-controls hidden">
<span>{{ t(key="image-size", lang=lang | default(value='sk')) }}</span>
<button type="button" data-image-size="small">{{ t(key="image-size-small", lang=lang | default(value='sk')) }}</button>
<button type="button" data-image-size="medium">{{ t(key="image-size-medium", lang=lang | default(value='sk')) }}</button>
<button type="button" data-image-size="full">{{ t(key="image-size-full", lang=lang | default(value='sk')) }}</button>
<label>
<span>{{ t(key="image-width-px", lang=lang | default(value='sk')) }}</span>
<input type="number" min="40" max="1200" step="10" data-image-width class="input input-bordered input-sm">
</label>
</div>
<p class="text-sm opacity-70" data-rich-status data-uploading='{{ t(key="image-uploading", lang=lang | default(value='sk')) }}' data-uploaded='{{ t(key="image-uploaded", lang=lang | default(value='sk')) }}' data-error='{{ t(key="image-upload-error", lang=lang | default(value='sk')) }}'></p>
</div>
<label class="label cursor-pointer justify-start gap-2">
<input type="checkbox" name="published" class="checkbox checkbox-sm" {% if article.published %}checked{% endif %}>
<span class="label-text">{{ t(key="published", lang=lang | default(value='sk')) }}</span>
</label>
<div class="flex flex-wrap gap-2 pt-2">
<button type="submit" class="btn btn-neutral btn-sm">{{ t(key="save", lang=lang | default(value='sk')) }}</button>
<a href="/admin/blog/articles" class="btn btn-ghost btn-sm">{{ t(key="cancel", lang=lang | default(value='sk')) }}</a>
</div>
</form>
</div>
</div>
</div>
{% endblock content %}

View File

@@ -1,63 +0,0 @@
{% extends "admin/base.html" %}
{% block title %}{{ t(key="admin-blog-articles", 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-blog-articles", lang=lang | default(value='sk')) }}</h1>
<p class="text-sm opacity-70">{{ t(key="admin-blog-index-desc", lang=lang | default(value='sk')) }}</p>
</div>
<a href="/admin/blog/articles/new" class="btn btn-neutral btn-sm">{{ t(key="new-article", lang=lang | default(value='sk')) }}</a>
</div>
<div class="card border border-base-300 bg-base-100 shadow-sm">
<div class="card-body">
{% if articles | length > 0 %}
<div class="overflow-x-auto">
<table class="table">
<thead>
<tr>
<th>{{ t(key="title", lang=lang | default(value='sk')) }}</th>
<th>{{ t(key="status", lang=lang | default(value='sk')) }}</th>
<th class="text-right">{{ t(key="actions", lang=lang | default(value='sk')) }}</th>
</tr>
</thead>
<tbody>
{% for article in articles %}
<tr>
<td class="font-medium">{{ article.title }}</td>
<td>
{% if article.published %}
<span class="badge">{{ t(key="published", lang=lang | default(value='sk')) }}</span>
{% else %}
<span class="badge opacity-70">{{ t(key="draft", lang=lang | default(value='sk')) }}</span>
{% endif %}
</td>
<td>
<div class="flex gap-2">
<a href="/admin/blog/articles/{{ article.id }}/edit" class="btn btn-ghost btn-sm">{{ t(key="edit", lang=lang | default(value='sk')) }}</a>
<form method="post" action="/admin/blog/articles/{{ article.id }}/delete">
<button type="submit" class="btn btn-ghost btn-sm">{{ t(key="delete", lang=lang | default(value='sk')) }}</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center">
<p class="font-medium">{{ t(key="admin-no-articles", lang=lang | default(value='sk')) }}</p>
<p class="text-sm opacity-70">{{ t(key="admin-create-first-post", lang=lang | default(value='sk')) }}</p>
<div class="pt-2">
<a href="/admin/blog/articles/new" class="btn btn-neutral btn-sm">{{ t(key="new-article", lang=lang | default(value='sk')) }}</a>
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% endblock content %}

View File

@@ -1,60 +0,0 @@
{% extends "admin/base.html" %}
{% block title %}{{ t(key="new-article", lang=lang | default(value='sk')) }}{% endblock title %}
{% block head %}{% endblock head %}
{% 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="new-article", lang=lang | default(value='sk')) }}</h1>
<p class="text-sm opacity-70">{{ t(key="admin-blog-create-desc", lang=lang | default(value='sk')) }}</p>
</div>
<a href="/admin/blog/articles" class="btn btn-ghost btn-sm">{{ t(key="back-to-articles", lang=lang | default(value='sk')) }}</a>
</div>
<div class="card border border-base-300 bg-base-100 shadow-sm">
<div class="card-body">
<form method="post" action="/admin/blog/articles" class="space-y-2">
<div class="form-control">
<label class="label"><span class="label-text">{{ t(key="title", lang=lang | default(value='sk')) }}</span></label>
<input type="text" name="title" required class="input input-bordered w-full">
</div>
<div class="form-control">
<label class="label"><span class="label-text">{{ t(key="excerpt", lang=lang | default(value='sk')) }}</span></label>
<textarea name="excerpt" rows="4" class="textarea textarea-bordered w-full"></textarea>
</div>
<div class="form-control">
<label class="label"><span class="label-text">{{ t(key="content", lang=lang | default(value='sk')) }}</span></label>
<textarea name="content" data-rich-content class="hidden"></textarea>
<input type="hidden" name="featured_image_id" data-featured-image-id>
<div data-rich-editor class="blog-editor"></div>
<div data-image-size-controls class="blog-image-size-controls hidden">
<span>{{ t(key="image-size", lang=lang | default(value='sk')) }}</span>
<button type="button" data-image-size="small">{{ t(key="image-size-small", lang=lang | default(value='sk')) }}</button>
<button type="button" data-image-size="medium">{{ t(key="image-size-medium", lang=lang | default(value='sk')) }}</button>
<button type="button" data-image-size="full">{{ t(key="image-size-full", lang=lang | default(value='sk')) }}</button>
<label>
<span>{{ t(key="image-width-px", lang=lang | default(value='sk')) }}</span>
<input type="number" min="40" max="1200" step="10" data-image-width class="input input-bordered input-sm">
</label>
</div>
<p class="text-sm opacity-70" data-rich-status data-uploading='{{ t(key="image-uploading", lang=lang | default(value='sk')) }}' data-uploaded='{{ t(key="image-uploaded", lang=lang | default(value='sk')) }}' data-error='{{ t(key="image-upload-error", lang=lang | default(value='sk')) }}'></p>
</div>
<label class="label cursor-pointer justify-start gap-2">
<input type="checkbox" name="published" class="checkbox checkbox-sm">
<span class="label-text">{{ t(key="published", lang=lang | default(value='sk')) }}</span>
</label>
<div class="flex flex-wrap gap-2 pt-2">
<button type="submit" class="btn btn-neutral btn-sm">{{ t(key="create", lang=lang | default(value='sk')) }}</button>
<a href="/admin/blog/articles" class="btn btn-ghost btn-sm">{{ t(key="cancel", lang=lang | default(value='sk')) }}</a>
</div>
</form>
</div>
</div>
</div>
{% endblock content %}

View File

@@ -0,0 +1,65 @@
{% extends "admin/base.html" %}
{% block title %}{{ t(key="admin-categories", lang=lang | default(value='sk')) }}{% endblock title %}
{% block crumb %}{{ t(key="admin-categories", lang=lang | default(value='sk')) }}{% endblock crumb %}
{% block content %}
<div class="flex flex-wrap items-end justify-between gap-3">
<div>
<h1 class="text-2xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="admin-categories", lang=lang | default(value='sk')) }}</h1>
<p class="text-sm text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="admin-categories-desc", lang=lang | default(value='sk')) }}</p>
</div>
<a href="/admin/catalog/categories/new"
class="inline-flex items-center justify-center rounded-radius bg-primary px-4 py-2 text-sm font-medium tracking-wide text-on-primary transition hover:opacity-75 dark:bg-primary-dark dark:text-on-primary-dark">
{{ t(key="new-category", lang=lang | default(value='sk')) }}
</a>
</div>
<div class="mt-6 overflow-hidden rounded-radius border border-outline dark:border-outline-dark">
{% if categories | length > 0 %}
<table class="w-full text-left text-sm">
<thead class="border-b border-outline bg-surface-alt text-xs uppercase tracking-wide text-on-surface/70 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark/70">
<tr>
<th class="px-4 py-3 font-semibold">{{ t(key="name", lang=lang | default(value='sk')) }}</th>
<th class="px-4 py-3 font-semibold">{{ t(key="admin-products", lang=lang | default(value='sk')) }}</th>
<th class="px-4 py-3 font-semibold">{{ t(key="status", lang=lang | default(value='sk')) }}</th>
<th class="px-4 py-3 text-right font-semibold">{{ t(key="actions", lang=lang | default(value='sk')) }}</th>
</tr>
</thead>
<tbody class="divide-y divide-outline dark:divide-outline-dark">
{% for row in categories %}
<tr class="hover:bg-surface-alt dark:hover:bg-surface-dark-alt">
<td class="px-4 py-3 font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ row.category.name }}</td>
<td class="px-4 py-3 tabular-nums">{{ row.product_count }}</td>
<td class="px-4 py-3">
{% if row.category.published %}
<span class="inline-flex rounded-full bg-success/15 px-2 py-0.5 text-xs font-medium text-success">{{ t(key="published", lang=lang | default(value='sk')) }}</span>
{% else %}
<span class="inline-flex rounded-full bg-surface-alt px-2 py-0.5 text-xs font-medium text-on-surface/70 dark:bg-surface-dark-alt dark:text-on-surface-dark/70">{{ t(key="draft", lang=lang | default(value='sk')) }}</span>
{% endif %}
</td>
<td class="px-4 py-3">
<div class="flex flex-wrap justify-end gap-2">
<a href="/admin/catalog/categories/{{ row.category.id }}/edit"
class="inline-flex items-center rounded-radius border border-outline px-3 py-1.5 text-xs font-medium text-on-surface transition hover:bg-surface-alt dark:border-outline-dark dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">{{ t(key="edit", lang=lang | default(value='sk')) }}</a>
<form method="post" action="/admin/catalog/categories/{{ row.category.id }}/delete"
onsubmit="return confirm('{{ t(key="confirm-delete", lang=lang | default(value='sk')) }}')">
<button type="submit" class="inline-flex items-center rounded-radius border border-outline px-3 py-1.5 text-xs font-medium text-danger transition hover:bg-danger/10 dark:border-outline-dark">{{ t(key="delete", lang=lang | default(value='sk')) }}</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="flex flex-col items-center gap-3 px-4 py-16 text-center">
<p class="text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="admin-no-categories", lang=lang | default(value='sk')) }}</p>
<a href="/admin/catalog/categories/new"
class="inline-flex items-center justify-center rounded-radius bg-primary px-4 py-2 text-sm font-medium text-on-primary transition hover:opacity-75 dark:bg-primary-dark dark:text-on-primary-dark">
{{ t(key="new-category", lang=lang | default(value='sk')) }}
</a>
</div>
{% endif %}
</div>
{% endblock content %}

View File

@@ -0,0 +1,70 @@
{% extends "admin/base.html" %}
{% set editing = category %}
{% block title %}{% if editing %}{{ t(key="edit-category", lang=lang | default(value='sk')) }}{% else %}{{ t(key="new-category", lang=lang | default(value='sk')) }}{% endif %}{% endblock title %}
{% block crumb %}{{ t(key="admin-categories", lang=lang | default(value='sk')) }}{% endblock crumb %}
{% block content %}
<div class="flex items-center justify-between gap-3">
<h1 class="text-2xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">
{% if editing %}{{ t(key="edit-category", lang=lang | default(value='sk')) }}{% else %}{{ t(key="new-category", lang=lang | default(value='sk')) }}{% endif %}
</h1>
<a href="/admin/catalog/categories"
class="inline-flex items-center rounded-radius border border-outline px-3 py-2 text-sm font-medium text-on-surface transition hover:bg-surface-alt dark:border-outline-dark dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">{{ t(key="cancel", lang=lang | default(value='sk')) }}</a>
</div>
<form method="post" enctype="multipart/form-data"
action="{% if editing %}/admin/catalog/categories/{{ category.id }}{% else %}/admin/catalog/categories{% endif %}"
class="mt-6 space-y-5 rounded-radius border border-outline bg-surface p-6 dark:border-outline-dark dark:bg-surface-dark-alt">
<div class="space-y-1.5">
<label for="name" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="name", lang=lang | default(value='sk')) }}</label>
<input id="name" name="name" type="text" required value="{% if editing %}{{ category.name }}{% endif %}"
class="w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">
</div>
<div class="grid gap-5 sm:grid-cols-2">
<div class="space-y-1.5">
<label for="slug" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="slug", lang=lang | default(value='sk')) }}</label>
<input id="slug" name="slug" type="text" value="{% if editing %}{{ category.slug }}{% endif %}"
placeholder="{{ t(key='slug-auto', lang=lang | default(value='sk')) }}"
class="w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">
</div>
<div class="space-y-1.5">
<label for="position" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="position", lang=lang | default(value='sk')) }}</label>
<input id="position" name="position" type="number" value="{% if editing %}{{ category.position }}{% else %}0{% endif %}"
class="w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">
</div>
</div>
<div class="space-y-1.5">
<label for="description" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="description", lang=lang | default(value='sk')) }}</label>
<textarea id="description" name="description" rows="4"
class="w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">{% if editing and category.description %}{{ category.description }}{% endif %}</textarea>
</div>
<div class="space-y-1.5">
<label for="image" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="image", lang=lang | default(value='sk')) }}</label>
{% if editing and category.image_id %}
<img src="/images/{{ category.image_id }}" alt="" class="size-24 rounded-radius object-cover">
{% endif %}
<input id="image" name="image" type="file" accept="image/*"
class="block w-full text-sm text-on-surface file:mr-3 file:rounded-radius file:border-0 file:bg-primary file:px-3 file:py-2 file:text-sm file:font-medium file:text-on-primary dark:text-on-surface-dark dark:file:bg-primary-dark dark:file:text-on-primary-dark">
</div>
<label class="flex items-center gap-2">
<input type="checkbox" name="published" value="on" {% if editing and category.published %}checked{% endif %}
class="size-4 rounded border-outline text-primary focus:ring-primary dark:border-outline-dark">
<span class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="published", lang=lang | default(value='sk')) }}</span>
</label>
<div class="flex gap-3 pt-2">
<button type="submit"
class="inline-flex items-center justify-center rounded-radius bg-primary px-4 py-2 text-sm font-medium tracking-wide text-on-primary transition hover:opacity-75 dark:bg-primary-dark dark:text-on-primary-dark">
{{ t(key="save", lang=lang | default(value='sk')) }}
</button>
<a href="/admin/catalog/categories"
class="inline-flex items-center rounded-radius border border-outline px-4 py-2 text-sm font-medium text-on-surface transition hover:bg-surface-alt dark:border-outline-dark dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">{{ t(key="cancel", lang=lang | default(value='sk')) }}</a>
</div>
</form>
{% endblock content %}

View File

@@ -0,0 +1,101 @@
{% extends "admin/base.html" %}
{% set editing = product %}
{% block title %}{% if editing %}{{ t(key="edit-product", lang=lang | default(value='sk')) }}{% else %}{{ t(key="new-product", lang=lang | default(value='sk')) }}{% endif %}{% endblock title %}
{% block crumb %}{{ t(key="admin-products", lang=lang | default(value='sk')) }}{% endblock crumb %}
{% block content %}
<div class="flex items-center justify-between gap-3">
<h1 class="text-2xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">
{% if editing %}{{ t(key="edit-product", lang=lang | default(value='sk')) }}{% else %}{{ t(key="new-product", lang=lang | default(value='sk')) }}{% endif %}
</h1>
<a href="/admin/catalog/products"
class="inline-flex items-center rounded-radius border border-outline px-3 py-2 text-sm font-medium text-on-surface transition hover:bg-surface-alt dark:border-outline-dark dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">{{ t(key="cancel", lang=lang | default(value='sk')) }}</a>
</div>
<form method="post" enctype="multipart/form-data"
action="{% if editing %}/admin/catalog/products/{{ product.id }}{% else %}/admin/catalog/products{% endif %}"
class="mt-6 space-y-5 rounded-radius border border-outline bg-surface p-6 dark:border-outline-dark dark:bg-surface-dark-alt">
<div class="space-y-1.5">
<label for="name" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="name", lang=lang | default(value='sk')) }}</label>
<input id="name" name="name" type="text" required value="{% if editing %}{{ product.name }}{% endif %}"
class="w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">
</div>
<div class="grid gap-5 sm:grid-cols-2">
<div class="space-y-1.5">
<label for="price" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="price", lang=lang | default(value='sk')) }}</label>
<input id="price" name="price" type="text" inputmode="decimal" required value="{% if editing %}{{ product.price }}{% endif %}"
placeholder="0.00"
class="w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">
</div>
<div class="space-y-1.5">
<label for="currency" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="currency", lang=lang | default(value='sk')) }}</label>
<input id="currency" name="currency" type="text" maxlength="3" value="{% if editing %}{{ product.currency }}{% else %}EUR{% endif %}"
class="w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm uppercase text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">
</div>
</div>
<div class="grid gap-5 sm:grid-cols-2">
<div class="space-y-1.5">
<label for="stock" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="stock", lang=lang | default(value='sk')) }}</label>
<input id="stock" name="stock" type="number" min="0" value="{% if editing %}{{ product.stock }}{% else %}0{% endif %}"
class="w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">
</div>
<div class="space-y-1.5">
<label for="sku" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="sku", lang=lang | default(value='sk')) }}</label>
<input id="sku" name="sku" type="text" value="{% if editing and product.sku %}{{ product.sku }}{% endif %}"
class="w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">
</div>
</div>
<div class="space-y-1.5">
<label for="category_id" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="category", lang=lang | default(value='sk')) }}</label>
<select id="category_id" name="category_id"
class="w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">
<option value="">{{ t(key="no-category", lang=lang | default(value='sk')) }}</option>
{% for category in categories %}
<option value="{{ category.id }}" {% if editing and product.category_id == category.id %}selected{% endif %}>{{ category.name }}</option>
{% endfor %}
</select>
</div>
<div class="space-y-1.5">
<label for="slug" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="slug", lang=lang | default(value='sk')) }}</label>
<input id="slug" name="slug" type="text" value="{% if editing %}{{ product.slug }}{% endif %}"
placeholder="{{ t(key='slug-auto', lang=lang | default(value='sk')) }}"
class="w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">
</div>
<div class="space-y-1.5">
<label for="description" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="description", lang=lang | default(value='sk')) }}</label>
<textarea id="description" name="description" rows="5"
class="w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">{% if editing and product.description %}{{ product.description }}{% endif %}</textarea>
</div>
<div class="space-y-1.5">
<label for="image" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="image", lang=lang | default(value='sk')) }}</label>
{% if editing and product.image %}
<img src="/images/{{ product.image }}" alt="" class="size-24 rounded-radius object-cover">
{% endif %}
<input id="image" name="image" type="file" accept="image/*"
class="block w-full text-sm text-on-surface file:mr-3 file:rounded-radius file:border-0 file:bg-primary file:px-3 file:py-2 file:text-sm file:font-medium file:text-on-primary dark:text-on-surface-dark dark:file:bg-primary-dark dark:file:text-on-primary-dark">
</div>
<label class="flex items-center gap-2">
<input type="checkbox" name="published" value="on" {% if editing and product.published %}checked{% endif %}
class="size-4 rounded border-outline text-primary focus:ring-primary dark:border-outline-dark">
<span class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="published", lang=lang | default(value='sk')) }}</span>
</label>
<div class="flex gap-3 pt-2">
<button type="submit"
class="inline-flex items-center justify-center rounded-radius bg-primary px-4 py-2 text-sm font-medium tracking-wide text-on-primary transition hover:opacity-75 dark:bg-primary-dark dark:text-on-primary-dark">
{{ t(key="save", lang=lang | default(value='sk')) }}
</button>
<a href="/admin/catalog/products"
class="inline-flex items-center rounded-radius border border-outline px-4 py-2 text-sm font-medium text-on-surface transition hover:bg-surface-alt dark:border-outline-dark dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">{{ t(key="cancel", lang=lang | default(value='sk')) }}</a>
</div>
</form>
{% endblock content %}

View File

@@ -0,0 +1,81 @@
{% extends "admin/base.html" %}
{% block title %}{{ t(key="admin-products", lang=lang | default(value='sk')) }}{% endblock title %}
{% block crumb %}{{ t(key="admin-products", lang=lang | default(value='sk')) }}{% endblock crumb %}
{% block content %}
<div class="flex flex-wrap items-end justify-between gap-3">
<div>
<h1 class="text-2xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="admin-products", lang=lang | default(value='sk')) }}</h1>
<p class="text-sm text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="admin-products-desc", lang=lang | default(value='sk')) }}</p>
</div>
<a href="/admin/catalog/products/new"
class="inline-flex items-center justify-center rounded-radius bg-primary px-4 py-2 text-sm font-medium tracking-wide text-on-primary transition hover:opacity-75 dark:bg-primary-dark dark:text-on-primary-dark">
{{ t(key="new-product", lang=lang | default(value='sk')) }}
</a>
</div>
<div class="mt-6 overflow-hidden rounded-radius border border-outline dark:border-outline-dark">
{% if products | length > 0 %}
<table class="w-full text-left text-sm">
<thead class="border-b border-outline bg-surface-alt text-xs uppercase tracking-wide text-on-surface/70 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark/70">
<tr>
<th class="px-4 py-3 font-semibold">{{ t(key="product", lang=lang | default(value='sk')) }}</th>
<th class="px-4 py-3 font-semibold">{{ t(key="price", lang=lang | default(value='sk')) }}</th>
<th class="px-4 py-3 font-semibold">{{ t(key="stock", lang=lang | default(value='sk')) }}</th>
<th class="px-4 py-3 font-semibold">{{ t(key="status", lang=lang | default(value='sk')) }}</th>
<th class="px-4 py-3 text-right font-semibold">{{ t(key="actions", lang=lang | default(value='sk')) }}</th>
</tr>
</thead>
<tbody class="divide-y divide-outline dark:divide-outline-dark">
{% for product in products %}
<tr class="hover:bg-surface-alt dark:hover:bg-surface-dark-alt">
<td class="px-4 py-3">
<div class="flex items-center gap-3">
{% if product.image %}
<img src="/images/{{ product.image }}" alt="" class="size-10 rounded-radius object-cover">
{% else %}
<div class="size-10 rounded-radius bg-surface-alt dark:bg-surface-dark-alt"></div>
{% endif %}
<div>
<div class="font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ product.name }}</div>
{% if product.category_name %}<div class="text-xs text-on-surface/60 dark:text-on-surface-dark/60">{{ product.category_name }}</div>{% endif %}
</div>
</div>
</td>
<td class="px-4 py-3 tabular-nums">{{ product.price }} {{ product.currency }}</td>
<td class="px-4 py-3 tabular-nums">{{ product.stock }}</td>
<td class="px-4 py-3">
{% if product.published %}
<span class="inline-flex rounded-full bg-success/15 px-2 py-0.5 text-xs font-medium text-success">{{ t(key="published", lang=lang | default(value='sk')) }}</span>
{% else %}
<span class="inline-flex rounded-full bg-surface-alt px-2 py-0.5 text-xs font-medium text-on-surface/70 dark:bg-surface-dark-alt dark:text-on-surface-dark/70">{{ t(key="draft", lang=lang | default(value='sk')) }}</span>
{% endif %}
</td>
<td class="px-4 py-3">
<div class="flex flex-wrap justify-end gap-2">
<a href="/admin/catalog/products/{{ product.id }}/edit"
class="inline-flex items-center rounded-radius border border-outline px-3 py-1.5 text-xs font-medium text-on-surface transition hover:bg-surface-alt dark:border-outline-dark dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">{{ t(key="edit", lang=lang | default(value='sk')) }}</a>
<a href="/shop/{{ product.slug }}"
class="inline-flex items-center rounded-radius border border-outline px-3 py-1.5 text-xs font-medium text-on-surface transition hover:bg-surface-alt dark:border-outline-dark dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">{{ t(key="view", lang=lang | default(value='sk')) }}</a>
<form method="post" action="/admin/catalog/products/{{ product.id }}/delete"
onsubmit="return confirm('{{ t(key="confirm-delete", lang=lang | default(value='sk')) }}')">
<button type="submit" class="inline-flex items-center rounded-radius border border-outline px-3 py-1.5 text-xs font-medium text-danger transition hover:bg-danger/10 dark:border-outline-dark">{{ t(key="delete", lang=lang | default(value='sk')) }}</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="flex flex-col items-center gap-3 px-4 py-16 text-center">
<p class="text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="admin-no-products", lang=lang | default(value='sk')) }}</p>
<a href="/admin/catalog/products/new"
class="inline-flex items-center justify-center rounded-radius bg-primary px-4 py-2 text-sm font-medium text-on-primary transition hover:opacity-75 dark:bg-primary-dark dark:text-on-primary-dark">
{{ t(key="new-product", lang=lang | default(value='sk')) }}
</a>
</div>
{% endif %}
</div>
{% endblock content %}

View File

@@ -1,60 +1,29 @@
{% extends "admin/base.html" %}
{% block title %}{{ t(key="admin-title", lang=lang | default(value='sk')) }}{% endblock title %}
{% block crumb %}dashboard{% endblock crumb %}
{% block crumb %}{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}{% endblock crumb %}
{% block content %}
<header class="term-cmd">
<div>
<h1 class="term-title">{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}</h1>
<p class="term-sub">{{ t(key="admin-session", lang=lang | default(value='sk')) }}: {{ admin.email }}</p>
</div>
<div class="term-cmd-actions">
<a href="/" class="btn btn-outline btn-sm">[ {{ t(key="view-site", lang=lang | default(value='sk')) }} ]</a>
</div>
<header class="space-y-1">
<h1 class="text-2xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}</h1>
<p class="text-sm text-on-surface/70 dark:text-on-surface-dark/70">{{ admin.email }}</p>
</header>
<div class="term-grid">
<article class="card">
<div class="term-head">
<span class="term-head-name">/admin/blog</span>
<span class="term-head-meta term-tag">{{ t(key="manage", lang=lang | default(value='sk')) }}</span>
</div>
<div class="card-body">
<h2 class="card-title text-base">{{ t(key="blog-title", lang=lang | default(value='sk')) }}</h2>
<p class="text-sm opacity-70">{{ t(key="admin-blog-desc", lang=lang | default(value='sk')) }}</p>
<div class="pt-2">
<a href="/admin/blog/articles" class="btn btn-primary btn-sm">[ {{ t(key="blog-manage", lang=lang | default(value='sk')) }} → ]</a>
</div>
</div>
</article>
<article class="card">
<div class="term-head">
<span class="term-head-name">/admin/about</span>
<span class="term-head-meta term-tag is-blue">{{ t(key="single", lang=lang | default(value='sk')) }}</span>
</div>
<div class="card-body">
<h2 class="card-title text-base">{{ t(key="about-sub", lang=lang | default(value='sk')) }}</h2>
<p class="text-sm opacity-70">{{ t(key="admin-about-desc", lang=lang | default(value='sk')) }}</p>
<div class="pt-2">
<a href="/admin/about" class="btn btn-primary btn-sm">[ {{ t(key="edit", lang=lang | default(value='sk')) }} → ]</a>
</div>
</div>
</article>
<article class="card">
<div class="term-head">
<span class="term-head-name">/admin/audio</span>
<span class="term-head-meta term-tag is-purple">{{ t(key="album", lang=lang | default(value='sk')) }}</span>
</div>
<div class="card-body">
<h2 class="card-title text-base">{{ t(key="audio-title", lang=lang | default(value='sk')) }}</h2>
<p class="text-sm opacity-70">{{ t(key="admin-audio-desc", lang=lang | default(value='sk')) }}</p>
<div class="pt-2">
<a href="/admin/audio/albums" class="btn btn-primary btn-sm">[ {{ t(key="manage", lang=lang | default(value='sk')) }} → ]</a>
</div>
</div>
</article>
<div class="mt-6 grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
<a href="/admin/catalog/products"
class="flex flex-col gap-2 rounded-radius border border-outline bg-surface p-5 transition hover:border-primary dark:border-outline-dark dark:bg-surface-dark-alt dark:hover:border-primary-dark">
<h2 class="font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="admin-products", lang=lang | default(value='sk')) }}</h2>
<p class="text-sm text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="admin-products-desc", lang=lang | default(value='sk')) }}</p>
</a>
<a href="/admin/catalog/categories"
class="flex flex-col gap-2 rounded-radius border border-outline bg-surface p-5 transition hover:border-primary dark:border-outline-dark dark:bg-surface-dark-alt dark:hover:border-primary-dark">
<h2 class="font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="admin-categories", lang=lang | default(value='sk')) }}</h2>
<p class="text-sm text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="admin-categories-desc", lang=lang | default(value='sk')) }}</p>
</a>
<a href="/admin/orders"
class="flex flex-col gap-2 rounded-radius border border-outline bg-surface p-5 transition hover:border-primary dark:border-outline-dark dark:bg-surface-dark-alt dark:hover:border-primary-dark">
<h2 class="font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="admin-orders", lang=lang | default(value='sk')) }}</h2>
<p class="text-sm text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="admin-orders", lang=lang | default(value='sk')) }}</p>
</a>
</div>
{% endblock content %}

View File

@@ -0,0 +1,41 @@
{% extends "admin/base.html" %}
{% block title %}{{ t(key="admin-orders", lang=lang | default(value='sk')) }}{% endblock title %}
{% block crumb %}{{ t(key="admin-orders", lang=lang | default(value='sk')) }}{% endblock crumb %}
{% block content %}
<h1 class="text-2xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="admin-orders", lang=lang | default(value='sk')) }}</h1>
<div class="mt-6 overflow-hidden rounded-radius border border-outline dark:border-outline-dark">
{% if orders | length > 0 %}
<table class="w-full text-left text-sm">
<thead class="border-b border-outline bg-surface-alt text-xs uppercase tracking-wide text-on-surface/70 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark/70">
<tr>
<th class="px-4 py-3 font-semibold">{{ t(key="order-number", lang=lang | default(value='sk')) }}</th>
<th class="px-4 py-3 font-semibold">{{ t(key="order-customer", lang=lang | default(value='sk')) }}</th>
<th class="px-4 py-3 font-semibold">{{ t(key="order-status", lang=lang | default(value='sk')) }}</th>
<th class="px-4 py-3 text-right font-semibold">{{ t(key="order-total", lang=lang | default(value='sk')) }}</th>
<th class="px-4 py-3"></th>
</tr>
</thead>
<tbody class="divide-y divide-outline dark:divide-outline-dark">
{% for order in orders %}
<tr class="hover:bg-surface-alt dark:hover:bg-surface-dark-alt">
<td class="px-4 py-3 font-mono font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ order.order_number }}</td>
<td class="px-4 py-3">{{ order.email }}</td>
<td class="px-4 py-3">
<span class="inline-flex rounded-full bg-surface-alt px-2 py-0.5 text-xs font-medium text-on-surface/80 dark:bg-surface-dark-alt dark:text-on-surface-dark/80">{{ t(key="order-status-" ~ order.status, lang=lang | default(value='sk')) }}</span>
</td>
<td class="px-4 py-3 text-right tabular-nums">{{ order.total }} {{ order.currency }}</td>
<td class="px-4 py-3 text-right">
<a href="/admin/orders/{{ order.id }}" class="inline-flex items-center rounded-radius border border-outline px-3 py-1.5 text-xs font-medium text-on-surface transition hover:bg-surface-alt dark:border-outline-dark dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">{{ t(key="view", lang=lang | default(value='sk')) }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="px-4 py-16 text-center text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="admin-no-orders", lang=lang | default(value='sk')) }}</div>
{% endif %}
</div>
{% endblock content %}

View File

@@ -0,0 +1,73 @@
{% extends "admin/base.html" %}
{% block title %}{{ order.order_number }}{% endblock title %}
{% block crumb %}{{ t(key="admin-orders", lang=lang | default(value='sk')) }}{% endblock crumb %}
{% block content %}
<div class="flex flex-wrap items-center justify-between gap-3">
<h1 class="font-mono text-2xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ order.order_number }}</h1>
<a href="/admin/orders" class="inline-flex items-center rounded-radius border border-outline px-3 py-2 text-sm font-medium text-on-surface transition hover:bg-surface-alt dark:border-outline-dark dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">{{ t(key="admin-orders", lang=lang | default(value='sk')) }}</a>
</div>
<div class="mt-6 grid gap-6 lg:grid-cols-3">
<div class="space-y-6 lg:col-span-2">
<div class="overflow-hidden rounded-radius border border-outline dark:border-outline-dark">
<table class="w-full text-left text-sm">
<thead class="border-b border-outline bg-surface-alt text-xs uppercase tracking-wide text-on-surface/70 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark/70">
<tr>
<th class="px-4 py-3 font-semibold">{{ t(key="product", lang=lang | default(value='sk')) }}</th>
<th class="px-4 py-3 font-semibold">{{ t(key="quantity", lang=lang | default(value='sk')) }}</th>
<th class="px-4 py-3 text-right font-semibold">{{ t(key="order-total", lang=lang | default(value='sk')) }}</th>
</tr>
</thead>
<tbody class="divide-y divide-outline dark:divide-outline-dark">
{% for item in items %}
<tr>
<td class="px-4 py-3">{{ item.product_name }}</td>
<td class="px-4 py-3 tabular-nums">{{ item.quantity }}</td>
<td class="px-4 py-3 text-right tabular-nums">{{ item.line_total }} {{ order.currency }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot class="border-t border-outline dark:border-outline-dark">
<tr>
<td colspan="2" class="px-4 py-3 text-right font-semibold">{{ t(key="order-total", lang=lang | default(value='sk')) }}</td>
<td class="px-4 py-3 text-right font-bold tabular-nums text-primary dark:text-primary-dark">{{ order.total }} {{ order.currency }}</td>
</tr>
</tfoot>
</table>
</div>
</div>
<aside class="space-y-6">
<div class="space-y-3 rounded-radius border border-outline bg-surface p-5 text-sm dark:border-outline-dark dark:bg-surface-dark-alt">
<div>
<p class="text-xs uppercase tracking-wide text-on-surface/60 dark:text-on-surface-dark/60">{{ t(key="order-customer", lang=lang | default(value='sk')) }}</p>
<p class="font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ order.customer_name }}</p>
<p class="text-on-surface/80 dark:text-on-surface-dark/80">{{ order.email }}</p>
</div>
<div>
<p class="text-xs uppercase tracking-wide text-on-surface/60 dark:text-on-surface-dark/60">{{ t(key="checkout-shipping", lang=lang | default(value='sk')) }}</p>
<p class="text-on-surface/80 dark:text-on-surface-dark/80">{{ order.address }}<br>{{ order.zip }} {{ order.city }}<br>{{ order.country }}</p>
</div>
{% if order.note %}
<div>
<p class="text-xs uppercase tracking-wide text-on-surface/60 dark:text-on-surface-dark/60">{{ t(key="checkout-note", lang=lang | default(value='sk')) }}</p>
<p class="text-on-surface/80 dark:text-on-surface-dark/80">{{ order.note }}</p>
</div>
{% endif %}
</div>
<form method="post" action="/admin/orders/{{ order.id }}/status" class="space-y-3 rounded-radius border border-outline bg-surface p-5 dark:border-outline-dark dark:bg-surface-dark-alt">
<label for="status" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="order-status", lang=lang | default(value='sk')) }}</label>
<select id="status" name="status"
class="w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">
{% for status in statuses %}
<option value="{{ status }}" {% if order.status == status %}selected{% endif %}>{{ t(key="order-status-" ~ status, lang=lang | default(value='sk')) }}</option>
{% endfor %}
</select>
<button type="submit" class="inline-flex w-full items-center justify-center rounded-radius bg-primary px-4 py-2 text-sm font-medium text-on-primary transition hover:opacity-75 dark:bg-primary-dark dark:text-on-primary-dark">{{ t(key="order-update-status", lang=lang | default(value='sk')) }}</button>
</form>
</aside>
</div>
{% endblock content %}