eshop
This commit is contained in:
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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')) }} · {{ tracks | length }} {{ t(key="songs-title", lang=lang | default(value='sk')) }} ·
|
||||
{% 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 %}—{% 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 %}
|
||||
@@ -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 %}
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
65
assets/views/admin/catalog/categories.html
Normal file
65
assets/views/admin/catalog/categories.html
Normal 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 %}
|
||||
70
assets/views/admin/catalog/category_form.html
Normal file
70
assets/views/admin/catalog/category_form.html
Normal 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 %}
|
||||
101
assets/views/admin/catalog/product_form.html
Normal file
101
assets/views/admin/catalog/product_form.html
Normal 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 %}
|
||||
81
assets/views/admin/catalog/products.html
Normal file
81
assets/views/admin/catalog/products.html
Normal 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 %}
|
||||
@@ -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 %}
|
||||
|
||||
41
assets/views/admin/orders/index.html
Normal file
41
assets/views/admin/orders/index.html
Normal 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 %}
|
||||
73
assets/views/admin/orders/show.html
Normal file
73
assets/views/admin/orders/show.html
Normal 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 %}
|
||||
@@ -1,140 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ album.title }}{% endblock title %}
|
||||
{% block crumb %}audio/{{ album.slug }}{% endblock crumb %}
|
||||
|
||||
{% block content %}
|
||||
{% if logged_in_admin %}
|
||||
<header class="term-cmd">
|
||||
<div>
|
||||
<h1 class="term-title">{{ album.title }}</h1>
|
||||
{% if album.artist %}
|
||||
<p class="term-sub">// {{ t(key="album-by", lang=lang | default(value='sk')) }} {{ album.artist }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="term-cmd-actions">
|
||||
{% if tracks | length > 0 %}
|
||||
<button type="button" class="uw-play-album btn btn-primary btn-sm"
|
||||
data-tracks-from="#uw-album-tracks">{{ t(key="album-play-full", lang=lang | default(value='sk')) }}</button>
|
||||
{% endif %}
|
||||
<a href="/audio/albums" class="btn btn-outline btn-sm">[ {{ t(key="cd-up", lang=lang | default(value='sk')) }} ]</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% if album.cover_image_id %}
|
||||
<div class="card mb-6">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/audio/{{ album.slug }}/cover.png</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<img src="/images/{{ album.cover_image_id }}" alt="">
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if album.description %}
|
||||
<div class="card mb-6">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/audio/{{ album.slug }}/notes.txt</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="term-prose whitespace-pre-line">{{ album.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<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-green">{{ tracks | length }} tracks</span>
|
||||
</div>
|
||||
<div class="card-body" id="uw-album-tracks">
|
||||
{% if tracks | length > 0 %}
|
||||
<div class="term-track-bar">
|
||||
<button type="button" class="uw-play-album btn btn-primary btn-sm"
|
||||
data-tracks-from="#uw-album-tracks">{{ t(key="album-play-full", lang=lang | default(value='sk')) }}</button>
|
||||
<span class="term-track-name t-dim">// {{ t(key="album-queue-all", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
{% for track in tracks %}
|
||||
<div class="term-track">
|
||||
<button type="button" class="uw-play btn btn-primary btn-sm"
|
||||
data-src="/audio/tracks/{{ track.id }}/stream" data-title="{{ track.title }}">▶ play</button>
|
||||
<span class="term-track-name">
|
||||
<span class="t-dim">{% if track.track_number %}{{ track.track_number }}{% else %}-{% endif %}</span>
|
||||
<span class="t-green">▸</span> {{ track.title }}
|
||||
</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p class="term-empty-cmd">{{ t(key="album-no-tracks", lang=lang | default(value='sk')) }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<header class="term-cmd">
|
||||
<div>
|
||||
<h1 class="term-title">{{ album.title }}</h1>
|
||||
{% if album.artist %}
|
||||
<p class="term-sub">{{ t(key="album-by", lang=lang | default(value='sk')) }} {{ album.artist }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="term-cmd-actions">
|
||||
{% if tracks | length > 0 %}
|
||||
<button type="button" class="uw-play-album btn btn-primary btn-sm"
|
||||
data-tracks-from="#uw-album-tracks">{{ t(key="album-play-full", lang=lang | default(value='sk')) }}</button>
|
||||
{% endif %}
|
||||
<a href="/audio/albums" class="btn btn-outline btn-sm">[ {{ t(key="cd-up", lang=lang | default(value='sk')) }} ]</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% if album.cover_image_id %}
|
||||
<div class="card mb-6">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/audio/{{ album.slug }}/cover.png</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<img src="/images/{{ album.cover_image_id }}" alt="">
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if album.description %}
|
||||
<div class="card mb-6">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/audio/{{ album.slug }}/notes.txt</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="term-prose whitespace-pre-line">{{ album.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<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-green">{{ tracks | length }} tracks</span>
|
||||
</div>
|
||||
<div class="card-body" id="uw-album-tracks">
|
||||
{% if tracks | length > 0 %}
|
||||
<div class="term-track-bar">
|
||||
<button type="button" class="uw-play-album btn btn-primary btn-sm"
|
||||
data-tracks-from="#uw-album-tracks">{{ t(key="album-play-full", lang=lang | default(value='sk')) }}</button>
|
||||
<span class="term-track-name t-dim">// {{ t(key="album-queue-all", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
{% for track in tracks %}
|
||||
<div class="term-track">
|
||||
<button type="button" class="uw-play btn btn-primary btn-sm"
|
||||
data-src="/audio/tracks/{{ track.id }}/stream" data-title="{{ track.title }}">{{ t(key="audio-play", lang=lang | default(value='sk')) }}</button>
|
||||
<span class="term-track-name">
|
||||
<span class="t-dim">{% if track.track_number %}{{ track.track_number }}{% else %}-{% endif %}</span>
|
||||
<span class="t-green">▸</span> {{ track.title }}
|
||||
</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p class="term-empty-cmd">{{ t(key="album-no-tracks", lang=lang | default(value='sk')) }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
@@ -1,96 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ t(key="audio-title", lang=lang | default(value='sk')) }}{% endblock title %}
|
||||
{% block crumb %}audio{% endblock crumb %}
|
||||
|
||||
{% block content %}
|
||||
{% if logged_in_admin %}
|
||||
<header class="term-cmd">
|
||||
<div>
|
||||
<h1 class="term-title">{{ t(key="audio-title", lang=lang | default(value='sk')) }}</h1>
|
||||
<p class="term-sub">// {{ albums | length }} {{ t(key="audio-sub", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
<div class="term-cmd-actions">
|
||||
<a href="/audio/tracks" class="btn btn-outline btn-sm">[ {{ t(key="audio-all-songs", lang=lang | default(value='sk')) }} ]</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% if albums | length > 0 %}
|
||||
<div class="term-grid">
|
||||
{% for album in albums %}
|
||||
<article class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/audio/{{ album.slug }}/</span>
|
||||
<span class="term-head-meta term-tag is-purple">{{ t(key="album", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if album.cover_image_id %}
|
||||
<img src="/images/{{ album.cover_image_id }}" alt="" class="mb-3">
|
||||
{% endif %}
|
||||
<h2 class="card-title text-base">{{ album.title }}</h2>
|
||||
{% if album.artist %}
|
||||
<p class="text-sm t-aqua">{{ album.artist }}</p>
|
||||
{% endif %}
|
||||
{% if album.description %}
|
||||
<p class="term-prose text-sm opacity-80">{{ album.description }}</p>
|
||||
{% endif %}
|
||||
<div class="flex flex-wrap gap-2 pt-2">
|
||||
<button type="button" class="uw-play-album-remote btn btn-primary btn-sm"
|
||||
data-album-tracks-url="/audio/albums/{{ album.slug }}/tracks">{{ t(key="audio-play", lang=lang | default(value='sk')) }}</button>
|
||||
<a href="/audio/albums/{{ album.slug }}" class="btn btn-outline btn-sm">{{ t(key="audio-open", lang=lang | default(value='sk')) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="term-empty">
|
||||
<p class="font-medium">{{ t(key="audio-no-albums", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<header class="term-cmd">
|
||||
<div>
|
||||
<h1 class="term-title">{{ t(key="audio-title", lang=lang | default(value='sk')) }}</h1>
|
||||
<p class="term-sub">{{ albums | length }} {{ t(key="audio-sub", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
<div class="term-cmd-actions">
|
||||
<a href="/audio/tracks" class="btn btn-outline btn-sm">[ {{ t(key="audio-all-songs", lang=lang | default(value='sk')) }} ]</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% if albums | length > 0 %}
|
||||
<div class="term-grid">
|
||||
{% for album in albums %}
|
||||
<article class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/audio/{{ album.slug }}/</span>
|
||||
<span class="term-head-meta term-tag is-purple">{{ t(key="album", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if album.cover_image_id %}
|
||||
<img src="/images/{{ album.cover_image_id }}" alt="" class="mb-3">
|
||||
{% endif %}
|
||||
<h2 class="card-title text-base">{{ album.title }}</h2>
|
||||
{% if album.artist %}
|
||||
<p class="text-sm t-aqua">{{ album.artist }}</p>
|
||||
{% endif %}
|
||||
{% if album.description %}
|
||||
<p class="term-prose text-sm opacity-80">{{ album.description }}</p>
|
||||
{% endif %}
|
||||
<div class="flex flex-wrap gap-2 pt-2">
|
||||
<button type="button" class="uw-play-album-remote btn btn-primary btn-sm"
|
||||
data-album-tracks-url="/audio/albums/{{ album.slug }}/tracks">{{ t(key="audio-play", lang=lang | default(value='sk')) }}</button>
|
||||
<a href="/audio/albums/{{ album.slug }}" class="btn btn-outline btn-sm">{{ t(key="audio-open", lang=lang | default(value='sk')) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="term-empty">
|
||||
<p class="font-medium">{{ t(key="audio-no-albums", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
@@ -1,76 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ t(key="songs-title", lang=lang | default(value='sk')) }}{% endblock title %}
|
||||
{% block crumb %}audio/tracks{% endblock crumb %}
|
||||
|
||||
{% block content %}
|
||||
{% if logged_in_admin %}
|
||||
<header class="term-cmd">
|
||||
<div>
|
||||
<h1 class="term-title">{{ t(key="songs-title", lang=lang | default(value='sk')) }}</h1>
|
||||
<p class="term-sub">// {{ tracks | length }} {{ t(key="songs-sub", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
<div class="term-cmd-actions">
|
||||
{% if tracks | length > 0 %}
|
||||
<button type="button" class="uw-play-album btn btn-primary btn-sm"
|
||||
data-tracks-from="#uw-songs-list">{{ t(key="songs-play-all", lang=lang | default(value='sk')) }}</button>
|
||||
{% endif %}
|
||||
<a href="/audio/albums" class="btn btn-outline btn-sm">[ {{ t(key="songs-albums", lang=lang | default(value='sk')) }} ]</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/audio/playlist.m3u</span>
|
||||
<span class="term-head-meta term-tag is-green">{{ tracks | length }} tracks</span>
|
||||
</div>
|
||||
<div class="card-body" id="uw-songs-list">
|
||||
{% if tracks | length > 0 %}
|
||||
{% for track in tracks %}
|
||||
<div class="term-track">
|
||||
<button type="button" class="uw-play btn btn-primary btn-sm"
|
||||
data-src="/audio/tracks/{{ track.id }}/stream" data-title="{{ track.title }}">{{ t(key="audio-play", lang=lang | default(value='sk')) }}</button>
|
||||
<span class="term-track-name"><span class="t-green">▸</span> {{ track.title }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p class="term-empty-cmd">{{ t(key="songs-no-tracks", lang=lang | default(value='sk')) }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<header class="term-cmd">
|
||||
<div>
|
||||
<h1 class="term-title">{{ t(key="songs-title", lang=lang | default(value='sk')) }}</h1>
|
||||
<p class="term-sub">{{ tracks | length }} {{ t(key="songs-sub", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
<div class="term-cmd-actions">
|
||||
{% if tracks | length > 0 %}
|
||||
<button type="button" class="uw-play-album btn btn-primary btn-sm"
|
||||
data-tracks-from="#uw-songs-list">{{ t(key="songs-play-all", lang=lang | default(value='sk')) }}</button>
|
||||
{% endif %}
|
||||
<a href="/audio/albums" class="btn btn-outline btn-sm">[ {{ t(key="songs-albums", lang=lang | default(value='sk')) }} ]</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/audio/playlist.m3u</span>
|
||||
<span class="term-head-meta term-tag is-green">{{ tracks | length }} tracks</span>
|
||||
</div>
|
||||
<div class="card-body" id="uw-songs-list">
|
||||
{% if tracks | length > 0 %}
|
||||
{% for track in tracks %}
|
||||
<div class="term-track">
|
||||
<button type="button" class="uw-play btn btn-primary btn-sm"
|
||||
data-src="/audio/tracks/{{ track.id }}/stream" data-title="{{ track.title }}">{{ t(key="audio-play", lang=lang | default(value='sk')) }}</button>
|
||||
<span class="term-track-name">{{ track.title }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p class="term-empty-cmd">{{ t(key="songs-no-tracks", lang=lang | default(value='sk')) }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
@@ -57,10 +57,7 @@
|
||||
<!-- desktop links -->
|
||||
<ul class="ml-2 hidden items-center gap-1 md:flex">
|
||||
<li><a href="/" data-nav="/" 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="nav-home", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/blog" data-nav="/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="nav-blog", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/audio/albums" data-nav="/audio/albums" 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="nav-audio", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/audio/tracks" data-nav="/audio/tracks" 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="nav-songs", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/about" data-nav="/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="nav-about", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/shop" data-nav="/shop" 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="nav-shop", lang=lang | default(value='sk')) }}</a></li>
|
||||
{% if logged_in_admin %}
|
||||
<li><a href="/admin/dashboard" hx-boost="false" data-nav="/admin" class="rounded-radius px-3 py-1.5 text-sm font-medium text-warning transition hover:bg-surface-alt dark:hover:bg-surface-dark-alt">{{ t(key="admin-title", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li>
|
||||
@@ -73,8 +70,21 @@
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
<!-- right side: settings + mobile toggle -->
|
||||
<!-- right side: cart + settings + mobile toggle -->
|
||||
<div class="ml-auto flex items-center gap-1">
|
||||
<!-- cart with live item-count badge read from the `cart` cookie -->
|
||||
<a href="/cart" data-nav="/cart"
|
||||
x-data="{ count: 0 }"
|
||||
x-init="count = (function(){ var m = document.cookie.split('; ').find(function(c){return c.indexOf('cart=')===0}); if(!m) return 0; var v = decodeURIComponent(m.split('=')[1]||''); if(!v) return 0; return v.split(',').reduce(function(s,e){ return s + (parseInt(e.split(':')[1])||0) }, 0) })()"
|
||||
aria-label="{{ t(key='cart-title', lang=lang | default(value='sk')) }}"
|
||||
title="{{ t(key='cart-title', lang=lang | default(value='sk')) }}"
|
||||
class="relative 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="M2.25 3h1.386c.51 0 .955.343 1.087.835l.383 1.437M7.5 14.25a3 3 0 0 0-3 3h15.75m-12.75-3h11.218c1.121-2.3 2.1-4.684 2.924-7.138a60.114 60.114 0 0 0-16.536-1.84M7.5 14.25 5.106 5.272M6 20.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Zm12.75 0a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z" />
|
||||
</svg>
|
||||
<span x-show="count > 0" x-cloak x-text="count"
|
||||
class="absolute -right-1 -top-1 inline-flex min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-semibold leading-4 text-on-primary dark:bg-primary-dark dark:text-on-primary-dark"></span>
|
||||
</a>
|
||||
<!-- settings (language + theme) dropdown -->
|
||||
<div x-data="{ open: false }" @keydown.escape="open = false" class="relative">
|
||||
<button type="button" @click="open = !open" :aria-expanded="open"
|
||||
@@ -144,10 +154,7 @@
|
||||
<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="/" 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="nav-home", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/blog" 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="nav-blog", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/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="nav-audio", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/audio/tracks" 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="nav-songs", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/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="nav-about", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/shop" 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="nav-shop", lang=lang | default(value='sk')) }}</a></li>
|
||||
{% if logged_in_admin %}
|
||||
<li><a href="/admin/dashboard" hx-boost="false" class="block rounded-radius px-3 py-2 text-sm font-medium text-warning hover:bg-surface-alt dark:hover:bg-surface-dark">{{ t(key="admin-title", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li>
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ t(key="blog-title", lang=lang | default(value='sk')) }}{% endblock title %}
|
||||
{% block crumb %}blog{% endblock crumb %}
|
||||
|
||||
{% block content %}
|
||||
{% if logged_in_admin %}
|
||||
<header class="term-cmd">
|
||||
<div>
|
||||
<h1 class="term-title">{{ t(key="blog-title", lang=lang | default(value='sk')) }}</h1>
|
||||
<p class="term-sub">// {{ articles | length }} {{ t(key="blog-sub", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
<div class="term-cmd-actions">
|
||||
<a href="/admin/blog/articles" hx-boost="false" class="btn btn-outline btn-sm">[ {{ t(key="blog-manage", lang=lang | default(value='sk')) }} ]</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% if articles | length > 0 %}
|
||||
<div class="term-stack">
|
||||
{% for article in articles %}
|
||||
<article class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/blog/{{ article.slug }}.txt</span>
|
||||
<span class="term-head-meta term-tag">{{ t(key="post", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h2 class="card-title text-base">
|
||||
<a href="/blog/{{ article.slug }}">{{ article.title }}</a>
|
||||
</h2>
|
||||
{% if article.excerpt %}
|
||||
<p class="term-prose text-sm opacity-80">{{ article.excerpt }}</p>
|
||||
{% endif %}
|
||||
<div class="pt-2">
|
||||
<a href="/blog/{{ article.slug }}" class="btn btn-primary btn-sm">{{ t(key="blog-read", lang=lang | default(value='sk')) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="term-empty">
|
||||
<p class="font-medium">{{ t(key="blog-no-posts", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<header class="term-cmd">
|
||||
<div>
|
||||
<h1 class="term-title">{{ t(key="blog-title", lang=lang | default(value='sk')) }}</h1>
|
||||
<p class="term-sub">{{ articles | length }} {{ t(key="blog-sub", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% if articles | length > 0 %}
|
||||
<div class="term-stack">
|
||||
{% for article in articles %}
|
||||
<article class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/blog/{{ article.slug }}.txt</span>
|
||||
<span class="term-head-meta term-tag">{{ t(key="post", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h2 class="card-title text-base">
|
||||
<a href="/blog/{{ article.slug }}">{{ article.title }}</a>
|
||||
</h2>
|
||||
{% if article.excerpt %}
|
||||
<p class="term-prose text-sm opacity-80">{{ article.excerpt }}</p>
|
||||
{% endif %}
|
||||
<div class="pt-2">
|
||||
<a href="/blog/{{ article.slug }}" class="btn btn-primary btn-sm">{{ t(key="blog-read", lang=lang | default(value='sk')) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="term-empty">
|
||||
<p class="font-medium">{{ t(key="blog-no-posts", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
@@ -1,56 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ article.title }}{% endblock title %}
|
||||
{% block crumb %}blog/{{ article.slug }}{% endblock crumb %}
|
||||
|
||||
{% block content %}
|
||||
{% if logged_in_admin %}
|
||||
<header class="term-cmd">
|
||||
<div>
|
||||
<h1 class="term-title">{{ article.title }}</h1>
|
||||
<p class="term-sub">// {{ article.view_count }} {{ t(key="blog-views", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
<div class="term-cmd-actions">
|
||||
<a href="/blog" class="btn btn-outline btn-sm">[ {{ t(key="cd-up", lang=lang | default(value='sk')) }} ]</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/blog/{{ article.slug }}.txt</span>
|
||||
<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.excerpt %}
|
||||
<p class="term-prose t-yellow"># {{ article.excerpt }}</p>
|
||||
<div class="border-t border-base-300 pt-4"></div>
|
||||
{% endif %}
|
||||
<div class="blog-content term-prose">{{ article.content | safe }}</div>
|
||||
</div>
|
||||
</article>
|
||||
{% else %}
|
||||
<header class="term-cmd">
|
||||
<div>
|
||||
<h1 class="term-title">{{ article.title }}</h1>
|
||||
<p class="term-sub">{{ article.view_count }} {{ t(key="blog-views", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
<div class="term-cmd-actions">
|
||||
<a href="/blog" class="btn btn-outline btn-sm">[ {{ t(key="cd-up", lang=lang | default(value='sk')) }} ]</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/blog/{{ article.slug }}.txt</span>
|
||||
<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.excerpt %}
|
||||
<p class="term-prose t-yellow"># {{ article.excerpt }}</p>
|
||||
<div class="border-t border-base-300 pt-4"></div>
|
||||
{% endif %}
|
||||
<div class="blog-content term-prose">{{ article.content | safe }}</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
@@ -1,186 +1,29 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ t(key="home-title", lang=lang | default(value='sk')) }}{% endblock title %}
|
||||
{% block crumb %}{% endblock crumb %}
|
||||
{% block title %}{{ t(key="brand", lang=lang | default(value='sk')) }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
{% if logged_in_admin %}
|
||||
<header class="term-cmd">
|
||||
<div>
|
||||
<h1 class="term-title">{{ t(key="home-title", lang=lang | default(value='sk')) }}</h1>
|
||||
<p class="term-sub">// {{ t(key="home-sub", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
<div class="term-cmd-actions">
|
||||
<a href="/blog" class="btn btn-outline btn-sm">[ {{ t(key="home-all-posts", lang=lang | default(value='sk')) }} ]</a>
|
||||
</div>
|
||||
</header>
|
||||
<div class="space-y-12">
|
||||
<!-- hero -->
|
||||
<section class="rounded-radius border border-outline bg-surface-alt px-6 py-12 text-center dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
<h1 class="text-4xl font-bold tracking-tight text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="shop-title", lang=lang | default(value='sk')) }}</h1>
|
||||
<p class="mx-auto mt-3 max-w-xl text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="shop-subtitle", lang=lang | default(value='sk')) }}</p>
|
||||
<a href="/shop" class="mt-6 inline-flex items-center justify-center rounded-radius bg-primary px-6 py-2.5 text-sm font-medium tracking-wide text-on-primary transition hover:opacity-75 dark:bg-primary-dark dark:text-on-primary-dark">{{ t(key="nav-shop", lang=lang | default(value='sk')) }}</a>
|
||||
</section>
|
||||
|
||||
{% if featured_track or featured_album %}
|
||||
<section class="mb-8">
|
||||
<p class="term-cmd-line mb-6"><span class="t-dim"># </span>{{ t(key="home-picks", lang=lang | default(value='sk')) }}</p>
|
||||
<div class="term-grid">
|
||||
{% if featured_track %}
|
||||
<article class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/audio/tracks/{{ featured_track.slug }}</span>
|
||||
<span class="term-head-meta term-tag is-green">{{ t(key="song", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="term-track">
|
||||
<button type="button" class="uw-play btn btn-primary btn-sm"
|
||||
data-src="/audio/tracks/{{ featured_track.id }}/stream" data-title="{{ featured_track.title }}">{{ t(key="audio-play", lang=lang | default(value='sk')) }}</button>
|
||||
<span class="term-track-name"><span class="t-green">▸</span> {{ featured_track.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
{% if featured_album %}
|
||||
<article class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/audio/{{ featured_album.slug }}/</span>
|
||||
<span class="term-head-meta term-tag is-purple">{{ t(key="album", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if featured_album.cover_image_id %}
|
||||
<img src="/images/{{ featured_album.cover_image_id }}" alt="" class="mb-3">
|
||||
{% endif %}
|
||||
<h2 class="card-title text-base">{{ featured_album.title }}</h2>
|
||||
{% if featured_album.artist %}
|
||||
<p class="text-sm t-aqua">{{ featured_album.artist }}</p>
|
||||
{% endif %}
|
||||
{% if featured_album.description %}
|
||||
<p class="term-prose text-sm opacity-80">{{ featured_album.description }}</p>
|
||||
{% endif %}
|
||||
<div class="flex flex-wrap gap-2 pt-2">
|
||||
<button type="button" class="uw-play-album-remote btn btn-primary btn-sm"
|
||||
data-album-tracks-url="/audio/albums/{{ featured_album.slug }}/tracks">{{ t(key="audio-play", lang=lang | default(value='sk')) }}</button>
|
||||
<a href="/audio/albums/{{ featured_album.slug }}" class="btn btn-outline btn-sm">{{ t(key="audio-open", lang=lang | default(value='sk')) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<section>
|
||||
<p class="term-cmd-line mb-6"><span class="t-dim"># </span>{{ t(key="home-recent", lang=lang | default(value='sk')) }} <span class="t-dim">({{ articles | length }})</span></p>
|
||||
{% if articles | length > 0 %}
|
||||
<div class="term-stack">
|
||||
{% for article in articles %}
|
||||
<article class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/blog/{{ article.slug }}.txt</span>
|
||||
<span class="term-head-meta term-tag">{{ t(key="post", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h2 class="card-title text-base">
|
||||
<a href="/blog/{{ article.slug }}">{{ article.title }}</a>
|
||||
</h2>
|
||||
{% if article.excerpt %}
|
||||
<p class="term-prose text-sm opacity-80">{{ article.excerpt }}</p>
|
||||
{% endif %}
|
||||
<div class="pt-2">
|
||||
<a href="/blog/{{ article.slug }}" class="btn btn-primary btn-sm">{{ t(key="blog-read", lang=lang | default(value='sk')) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<!-- featured products -->
|
||||
{% if products | length > 0 %}
|
||||
<section class="space-y-5">
|
||||
<div class="flex items-end justify-between">
|
||||
<h2 class="text-2xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="shop-title", lang=lang | default(value='sk')) }}</h2>
|
||||
<a href="/shop" class="text-sm font-medium text-primary dark:text-primary-dark">{{ t(key="cart-continue", lang=lang | default(value='sk')) }} →</a>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-5 sm:grid-cols-3 lg:grid-cols-4">
|
||||
{% for product in products %}
|
||||
{% include "shop/_card.html" %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="term-empty">
|
||||
<p class="font-medium">{{ t(key="home-no-posts", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% else %}
|
||||
<header class="term-cmd">
|
||||
<div>
|
||||
<h1 class="term-title">{{ t(key="home-title", lang=lang | default(value='sk')) }}</h1>
|
||||
<p class="term-sub">{{ t(key="home-sub", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
<div class="term-cmd-actions">
|
||||
<a href="/blog" class="btn btn-outline btn-sm">{{ t(key="home-all-posts", lang=lang | default(value='sk')) }}</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% if featured_track or featured_album %}
|
||||
<section class="mb-8">
|
||||
<p class="term-cmd-line mb-6"><span class="t-dim"># </span>{{ t(key="home-picks", lang=lang | default(value='sk')) }}</p>
|
||||
<div class="term-grid">
|
||||
{% if featured_track %}
|
||||
<article class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/audio/tracks/{{ featured_track.slug }}</span>
|
||||
<span class="term-head-meta term-tag is-green">{{ t(key="song", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="term-track">
|
||||
<button type="button" class="uw-play btn btn-primary btn-sm"
|
||||
data-src="/audio/tracks/{{ featured_track.id }}/stream" data-title="{{ featured_track.title }}">{{ t(key="audio-play", lang=lang | default(value='sk')) }}</button>
|
||||
<span class="term-track-name"><span class="t-green">▸</span> {{ featured_track.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
{% if featured_album %}
|
||||
<article class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/audio/{{ featured_album.slug }}/</span>
|
||||
<span class="term-head-meta term-tag is-purple">{{ t(key="album", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if featured_album.cover_image_id %}
|
||||
<img src="/images/{{ featured_album.cover_image_id }}" alt="" class="mb-3">
|
||||
{% endif %}
|
||||
<h2 class="card-title text-base">{{ featured_album.title }}</h2>
|
||||
{% if featured_album.artist %}
|
||||
<p class="text-sm t-aqua">{{ featured_album.artist }}</p>
|
||||
{% endif %}
|
||||
{% if featured_album.description %}
|
||||
<p class="term-prose text-sm opacity-80">{{ featured_album.description }}</p>
|
||||
{% endif %}
|
||||
<div class="flex flex-wrap gap-2 pt-2">
|
||||
<button type="button" class="uw-play-album-remote btn btn-primary btn-sm"
|
||||
data-album-tracks-url="/audio/albums/{{ featured_album.slug }}/tracks">{{ t(key="audio-play", lang=lang | default(value='sk')) }}</button>
|
||||
<a href="/audio/albums/{{ featured_album.slug }}" class="btn btn-outline btn-sm">{{ t(key="audio-open", lang=lang | default(value='sk')) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<section>
|
||||
<p class="term-cmd-line mb-6"><span class="t-dim"># </span>{{ t(key="home-recent", lang=lang | default(value='sk')) }} <span class="t-dim">({{ articles | length }})</span></p>
|
||||
{% if articles | length > 0 %}
|
||||
<div class="term-stack">
|
||||
{% for article in articles %}
|
||||
<article class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/blog/{{ article.slug }}.txt</span>
|
||||
<span class="term-head-meta term-tag">{{ t(key="post", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h2 class="card-title text-base">
|
||||
<a href="/blog/{{ article.slug }}">{{ article.title }}</a>
|
||||
</h2>
|
||||
{% if article.excerpt %}
|
||||
<p class="text-sm opacity-80">{{ article.excerpt }}</p>
|
||||
{% endif %}
|
||||
<div class="pt-2">
|
||||
<a href="/blog/{{ article.slug }}" class="btn btn-primary btn-sm">{{ t(key="blog-read", lang=lang | default(value='sk')) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="term-empty">
|
||||
<p class="font-medium">{{ t(key="home-no-posts", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ page.title }}{% endblock title %}
|
||||
{% block crumb %}about{% endblock crumb %}
|
||||
|
||||
{% block content %}
|
||||
{% if logged_in_admin %}
|
||||
<header class="term-cmd">
|
||||
<div>
|
||||
<h1 class="term-title">{{ page.title }}</h1>
|
||||
<p class="term-sub">// {{ t(key="about-sub", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
<div class="term-cmd-actions">
|
||||
<a href="/admin/about" hx-boost="false" class="btn btn-outline btn-sm">[ {{ t(key="edit", lang=lang | default(value='sk')) }} ]</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/about.txt</span>
|
||||
<span class="term-head-meta term-tag is-blue">{{ t(key="readonly", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="term-prose whitespace-pre-line">{{ page.content }}</div>
|
||||
</div>
|
||||
</article>
|
||||
{% else %}
|
||||
<header class="term-cmd">
|
||||
<div>
|
||||
<h1 class="term-title">{{ page.title }}</h1>
|
||||
<p class="term-sub">{{ t(key="about-sub", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">~/about.txt</span>
|
||||
<span class="term-head-meta term-tag is-blue">{{ t(key="readonly", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="term-prose whitespace-pre-line">{{ page.content }}</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
12
assets/views/shop/_card.html
Normal file
12
assets/views/shop/_card.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<a href="/shop/{{ product.slug }}"
|
||||
class="group flex flex-col overflow-hidden rounded-radius border border-outline bg-surface transition hover:border-primary dark:border-outline-dark dark:bg-surface-dark-alt dark:hover:border-primary-dark">
|
||||
<div class="aspect-square overflow-hidden bg-surface-alt dark:bg-surface-dark">
|
||||
{% if product.image %}
|
||||
<img src="/images/{{ product.image }}" alt="{{ product.name }}" class="size-full object-cover transition group-hover:scale-105">
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col gap-1 p-4">
|
||||
<h3 class="font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ product.name }}</h3>
|
||||
<p class="mt-auto pt-2 font-semibold text-primary dark:text-primary-dark">{{ product.price }} {{ product.currency }}</p>
|
||||
</div>
|
||||
</a>
|
||||
67
assets/views/shop/cart.html
Normal file
67
assets/views/shop/cart.html
Normal file
@@ -0,0 +1,67 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ t(key="cart-title", lang=lang | default(value='sk')) }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="space-y-6">
|
||||
<h1 class="text-3xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="cart-title", lang=lang | default(value='sk')) }}</h1>
|
||||
|
||||
{% if items | length > 0 %}
|
||||
<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="price", 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="cart-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 item in items %}
|
||||
<tr>
|
||||
<td class="px-4 py-3">
|
||||
<a href="/shop/{{ item.slug }}" class="font-medium text-on-surface-strong hover:text-primary dark:text-on-surface-dark-strong dark:hover:text-primary-dark">{{ item.name }}</a>
|
||||
</td>
|
||||
<td class="px-4 py-3 tabular-nums">{{ item.price }} {{ item.currency }}</td>
|
||||
<td class="px-4 py-3">
|
||||
<form method="post" action="/cart/update" hx-boost="false" class="flex items-center gap-2">
|
||||
<input type="hidden" name="product_id" value="{{ item.id }}">
|
||||
<input type="number" name="quantity" min="0" max="{{ item.stock }}" value="{{ item.quantity }}"
|
||||
class="w-20 rounded-radius border border-outline bg-surface px-2 py-1 text-sm text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">
|
||||
<button type="submit" class="rounded-radius border border-outline px-2 py-1 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="cart-update", lang=lang | default(value='sk')) }}</button>
|
||||
</form>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-right font-medium tabular-nums">{{ item.line_total }} {{ item.currency }}</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<form method="post" action="/cart/remove" hx-boost="false">
|
||||
<input type="hidden" name="product_id" value="{{ item.id }}">
|
||||
<button type="submit" class="text-xs font-medium text-danger hover:underline">{{ t(key="cart-remove", lang=lang | default(value='sk')) }}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot class="border-t border-outline dark:border-outline-dark">
|
||||
<tr>
|
||||
<td colspan="3" class="px-4 py-3 text-right font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="cart-total", lang=lang | default(value='sk')) }}</td>
|
||||
<td class="px-4 py-3 text-right text-lg font-bold tabular-nums text-primary dark:text-primary-dark">{{ total }} {{ currency }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap justify-between gap-3">
|
||||
<a href="/shop" 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="cart-continue", lang=lang | default(value='sk')) }}</a>
|
||||
<a href="/checkout" class="inline-flex items-center justify-center rounded-radius bg-primary px-5 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="cart-checkout", lang=lang | default(value='sk')) }}</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="rounded-radius border border-outline px-6 py-16 text-center dark:border-outline-dark">
|
||||
<p class="text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="cart-empty", lang=lang | default(value='sk')) }}</p>
|
||||
<a href="/shop" class="mt-4 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="cart-continue", lang=lang | default(value='sk')) }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
29
assets/views/shop/category.html
Normal file
29
assets/views/shop/category.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ category.name }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="space-y-8">
|
||||
<header class="space-y-2">
|
||||
<nav class="text-sm text-on-surface/60 dark:text-on-surface-dark/60">
|
||||
<a href="/shop" class="hover:text-primary dark:hover:text-primary-dark">{{ t(key="nav-shop", lang=lang | default(value='sk')) }}</a>
|
||||
<span class="px-1">/</span>
|
||||
<span>{{ category.name }}</span>
|
||||
</nav>
|
||||
<h1 class="text-3xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ category.name }}</h1>
|
||||
{% if category.description %}<p class="text-on-surface/70 dark:text-on-surface-dark/70">{{ category.description }}</p>{% endif %}
|
||||
</header>
|
||||
|
||||
{% if products | length > 0 %}
|
||||
<div class="grid grid-cols-2 gap-5 sm:grid-cols-3 lg:grid-cols-4">
|
||||
{% for product in products %}
|
||||
{% include "shop/_card.html" %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="rounded-radius border border-outline px-6 py-16 text-center text-on-surface/70 dark:border-outline-dark dark:text-on-surface-dark/70">
|
||||
{{ t(key="shop-empty", lang=lang | default(value='sk')) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
77
assets/views/shop/checkout.html
Normal file
77
assets/views/shop/checkout.html
Normal file
@@ -0,0 +1,77 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ t(key="checkout-title", lang=lang | default(value='sk')) }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="text-3xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-title", lang=lang | default(value='sk')) }}</h1>
|
||||
|
||||
<div class="mt-6 grid gap-8 lg:grid-cols-3">
|
||||
<form method="post" action="/checkout" hx-boost="false" class="space-y-6 lg:col-span-2">
|
||||
<fieldset class="space-y-4 rounded-radius border border-outline bg-surface p-6 dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
<legend class="px-1 text-sm font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-contact", lang=lang | default(value='sk')) }}</legend>
|
||||
<div class="space-y-1.5">
|
||||
<label for="email" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-email", lang=lang | default(value='sk')) }}</label>
|
||||
<input id="email" name="email" type="email" required
|
||||
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="customer_name" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-name", lang=lang | default(value='sk')) }}</label>
|
||||
<input id="customer_name" name="customer_name" type="text" required
|
||||
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>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="space-y-4 rounded-radius border border-outline bg-surface p-6 dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
<legend class="px-1 text-sm font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-shipping", lang=lang | default(value='sk')) }}</legend>
|
||||
<div class="space-y-1.5">
|
||||
<label for="address" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-address", lang=lang | default(value='sk')) }}</label>
|
||||
<input id="address" name="address" type="text" required
|
||||
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-4 sm:grid-cols-3">
|
||||
<div class="space-y-1.5">
|
||||
<label for="city" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-city", lang=lang | default(value='sk')) }}</label>
|
||||
<input id="city" name="city" type="text" required
|
||||
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="zip" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-zip", lang=lang | default(value='sk')) }}</label>
|
||||
<input id="zip" name="zip" type="text" required
|
||||
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="country" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-country", lang=lang | default(value='sk')) }}</label>
|
||||
<input id="country" name="country" type="text" required
|
||||
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="note" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-note", lang=lang | default(value='sk')) }}</label>
|
||||
<textarea id="note" name="note" rows="3"
|
||||
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"></textarea>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<button type="submit"
|
||||
class="inline-flex items-center justify-center rounded-radius bg-primary px-6 py-2.5 text-sm font-medium tracking-wide text-on-primary transition hover:opacity-75 dark:bg-primary-dark dark:text-on-primary-dark">
|
||||
{{ t(key="checkout-place-order", lang=lang | default(value='sk')) }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<aside class="h-fit space-y-4 rounded-radius border border-outline bg-surface p-6 dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
<h2 class="text-sm font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-summary", lang=lang | default(value='sk')) }}</h2>
|
||||
<ul class="space-y-2 text-sm">
|
||||
{% for item in items %}
|
||||
<li class="flex justify-between gap-2">
|
||||
<span class="text-on-surface/80 dark:text-on-surface-dark/80">{{ item.name }} × {{ item.quantity }}</span>
|
||||
<span class="tabular-nums">{{ item.line_total }} {{ item.currency }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="flex justify-between border-t border-outline pt-3 text-base font-bold dark:border-outline-dark">
|
||||
<span>{{ t(key="cart-total", lang=lang | default(value='sk')) }}</span>
|
||||
<span class="tabular-nums text-primary dark:text-primary-dark">{{ total }} {{ currency }}</span>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
33
assets/views/shop/index.html
Normal file
33
assets/views/shop/index.html
Normal file
@@ -0,0 +1,33 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ t(key="nav-shop", lang=lang | default(value='sk')) }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="space-y-8">
|
||||
<header class="space-y-2">
|
||||
<h1 class="text-3xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="shop-title", lang=lang | default(value='sk')) }}</h1>
|
||||
<p class="text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="shop-subtitle", lang=lang | default(value='sk')) }}</p>
|
||||
</header>
|
||||
|
||||
{% if categories | length > 0 %}
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{% for category in categories %}
|
||||
<a href="/category/{{ category.slug }}"
|
||||
class="rounded-full border border-outline px-3 py-1 text-sm font-medium text-on-surface transition hover:border-primary hover:text-primary dark:border-outline-dark dark:text-on-surface-dark dark:hover:border-primary-dark dark:hover:text-primary-dark">{{ category.name }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if products | length > 0 %}
|
||||
<div class="grid grid-cols-2 gap-5 sm:grid-cols-3 lg:grid-cols-4">
|
||||
{% for product in products %}
|
||||
{% include "shop/_card.html" %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="rounded-radius border border-outline px-6 py-16 text-center text-on-surface/70 dark:border-outline-dark dark:text-on-surface-dark/70">
|
||||
{{ t(key="shop-empty", lang=lang | default(value='sk')) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
38
assets/views/shop/order_confirmed.html
Normal file
38
assets/views/shop/order_confirmed.html
Normal file
@@ -0,0 +1,38 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ t(key="order-confirmed-title", lang=lang | default(value='sk')) }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="mx-auto max-w-2xl space-y-6 text-center">
|
||||
<div class="mx-auto flex size-14 items-center justify-center rounded-full bg-success/15 text-success">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-7">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="order-confirmed-title", lang=lang | default(value='sk')) }}</h1>
|
||||
<p class="mt-1 text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="order-confirmed-sub", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
|
||||
<div class="rounded-radius border border-outline bg-surface p-6 text-left dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
<div class="flex flex-wrap justify-between gap-2 border-b border-outline pb-3 dark:border-outline-dark">
|
||||
<span class="text-sm text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="order-number", lang=lang | default(value='sk')) }}</span>
|
||||
<span class="font-mono font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ order.order_number }}</span>
|
||||
</div>
|
||||
<ul class="space-y-2 py-3 text-sm">
|
||||
{% for item in items %}
|
||||
<li class="flex justify-between gap-2">
|
||||
<span class="text-on-surface/80 dark:text-on-surface-dark/80">{{ item.product_name }} × {{ item.quantity }}</span>
|
||||
<span class="tabular-nums">{{ item.line_total }} {{ order.currency }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="flex justify-between border-t border-outline pt-3 font-bold dark:border-outline-dark">
|
||||
<span>{{ t(key="order-total", lang=lang | default(value='sk')) }}</span>
|
||||
<span class="tabular-nums text-primary dark:text-primary-dark">{{ order.total }} {{ order.currency }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="/shop" class="inline-flex items-center justify-center rounded-radius border border-outline px-5 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="cart-continue", lang=lang | default(value='sk')) }}</a>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
60
assets/views/shop/show.html
Normal file
60
assets/views/shop/show.html
Normal file
@@ -0,0 +1,60 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ product.name }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="grid gap-10 lg:grid-cols-2">
|
||||
<!-- gallery -->
|
||||
<div x-data="{ active: 0 }" class="space-y-4">
|
||||
<div class="aspect-square overflow-hidden rounded-radius border border-outline bg-surface-alt dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
{% if images | length > 0 %}
|
||||
{% for image in images %}
|
||||
<img x-show="active === {{ loop.index0 }}" src="/images/{{ image }}" alt="{{ product.name }}" class="size-full object-cover">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if images | length > 1 %}
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{% for image in images %}
|
||||
<button type="button" @click="active = {{ loop.index0 }}"
|
||||
:class="active === {{ loop.index0 }} ? 'border-primary dark:border-primary-dark' : 'border-outline dark:border-outline-dark'"
|
||||
class="size-16 overflow-hidden rounded-radius border">
|
||||
<img src="/images/{{ image }}" alt="" class="size-full object-cover">
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- details -->
|
||||
<div class="space-y-6">
|
||||
{% if category %}
|
||||
<a href="/category/{{ category.slug }}" class="text-sm font-medium text-primary dark:text-primary-dark">{{ category.name }}</a>
|
||||
{% endif %}
|
||||
<h1 class="text-3xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ product.name }}</h1>
|
||||
<p class="text-2xl font-semibold text-primary dark:text-primary-dark">{{ product.price }} {{ product.currency }}</p>
|
||||
|
||||
{% if product.description %}
|
||||
<div class="whitespace-pre-line leading-relaxed text-on-surface/80 dark:text-on-surface-dark/80">{{ product.description }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if product.stock > 0 %}
|
||||
<form method="post" action="/cart/add" hx-boost="false" class="flex flex-wrap items-end gap-3">
|
||||
<input type="hidden" name="product_id" value="{{ product.id }}">
|
||||
<div class="space-y-1.5">
|
||||
<label for="quantity" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="quantity", lang=lang | default(value='sk')) }}</label>
|
||||
<input id="quantity" name="quantity" type="number" min="1" max="{{ product.stock }}" value="1"
|
||||
class="w-24 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>
|
||||
<button type="submit"
|
||||
class="inline-flex items-center justify-center rounded-radius bg-primary px-5 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="add-to-cart", lang=lang | default(value='sk')) }}
|
||||
</button>
|
||||
</form>
|
||||
<p class="text-sm text-on-surface/60 dark:text-on-surface-dark/60">{{ t(key="in-stock", lang=lang | default(value='sk')) }}: {{ product.stock }}</p>
|
||||
{% else %}
|
||||
<p class="inline-flex rounded-radius bg-danger/10 px-3 py-2 text-sm font-medium text-danger">{{ t(key="out-of-stock", lang=lang | default(value='sk')) }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
Reference in New Issue
Block a user