initial commit of gitara site
This commit is contained in:
36
assets/views/admin/about.html
Normal file
36
assets/views/admin/about.html
Normal file
@@ -0,0 +1,36 @@
|
||||
{% 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 %}
|
||||
81
assets/views/admin/audio/albums.html
Normal file
81
assets/views/admin/audio/albums.html
Normal file
@@ -0,0 +1,81 @@
|
||||
{% 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 %}
|
||||
93
assets/views/admin/audio/new_album.html
Normal file
93
assets/views/admin/audio/new_album.html
Normal file
@@ -0,0 +1,93 @@
|
||||
{% 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 %}
|
||||
99
assets/views/admin/audio/songs.html
Normal file
99
assets/views/admin/audio/songs.html
Normal file
@@ -0,0 +1,99 @@
|
||||
{% 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 %}
|
||||
123
assets/views/admin/audio/tracks.html
Normal file
123
assets/views/admin/audio/tracks.html
Normal file
@@ -0,0 +1,123 @@
|
||||
{% 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 %}
|
||||
76
assets/views/admin/audio/upload_track.html
Normal file
76
assets/views/admin/audio/upload_track.html
Normal file
@@ -0,0 +1,76 @@
|
||||
{% 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 %}
|
||||
154
assets/views/admin/base.html
Normal file
154
assets/views/admin/base.html
Normal file
@@ -0,0 +1,154 @@
|
||||
<!doctype html>
|
||||
<html lang="{{ lang | default(value='sk') }}" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}{{ t(key="admin-title", lang=lang | default(value='sk')) }}{% endblock title %}</title>
|
||||
<meta name="description" content="{% block meta_description %}{{ t(key="meta-description", lang=lang | default(value='sk')) }}{% endblock meta_description %}">
|
||||
<link rel="icon" type="image/x-icon" href="/static/favicon/favicon.ico">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon/favicon-16x16.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/static/favicon/apple-touch-icon.png">
|
||||
<link rel="manifest" href="/static/favicon/site.webmanifest">
|
||||
<script>
|
||||
function applyTheme(t) {
|
||||
var dark = t === 'dark'
|
||||
|| (t === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
document.documentElement.setAttribute('data-theme', dark ? 'dark' : 'light');
|
||||
}
|
||||
function highlightTheme(t) {
|
||||
document.querySelectorAll('[data-theme-opt]').forEach(function (b) {
|
||||
var on = b.getAttribute('data-theme-opt') === t;
|
||||
b.classList.toggle('active', on);
|
||||
var chk = b.querySelector('.opt-check');
|
||||
if (chk) chk.classList.toggle('hidden', !on);
|
||||
});
|
||||
}
|
||||
function setTheme(t) {
|
||||
localStorage.setItem('theme', t);
|
||||
applyTheme(t);
|
||||
highlightTheme(t);
|
||||
}
|
||||
applyTheme(localStorage.getItem('theme') || 'dark');
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function () {
|
||||
if ((localStorage.getItem('theme') || 'dark') === 'system') applyTheme('system');
|
||||
});
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
highlightTheme(localStorage.getItem('theme') || 'dark');
|
||||
var path = location.pathname;
|
||||
document.querySelectorAll('.term-navlinks a[data-nav]').forEach(function (a) {
|
||||
var h = a.getAttribute('data-nav');
|
||||
if (h === path || (h !== '/' && path.indexOf(h) === 0)) a.classList.add('is-active');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<link href="/static/css/app.css?v=2026-05-20b" rel="stylesheet" type="text/css">
|
||||
{% block head %}{% endblock head %}
|
||||
<link href="/static/css/theme.css?v=2026-05-20b" rel="stylesheet" type="text/css">
|
||||
<script src="/static/vendor/htmx/htmx-1.9.12.min.js"></script>
|
||||
<style>
|
||||
@media (min-width: 768px) {
|
||||
.nav-menu { flex-direction: row; }
|
||||
}
|
||||
#nav-backdrop { display: none; }
|
||||
@media (max-width: 767px) {
|
||||
#nav-backdrop {
|
||||
display: block;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 40;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.15s ease, visibility 0s linear 0.2s;
|
||||
}
|
||||
.term-titlebar:has(.dropdown:focus-within) ~ #nav-backdrop {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transition: opacity 0.15s ease, visibility 0s;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="flex min-h-screen flex-col bg-base-100 text-base-content antialiased">
|
||||
<header class="term-titlebar">
|
||||
<nav class="term-nav">
|
||||
<a href="/admin/dashboard" class="term-brand">{{ t(key="admin-title", lang=lang | default(value='sk')) }}</a>
|
||||
<ul class="nav-menu term-navlinks menu menu-sm hidden items-center md:flex">
|
||||
<li><a href="/admin/dashboard" data-nav="/admin/dashboard">{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/admin/blog/articles" data-nav="/admin/blog">{{ t(key="admin-blog", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/admin/audio/albums" data-nav="/admin/audio">{{ t(key="admin-audio", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/admin/about" data-nav="/admin/about">{{ t(key="admin-about", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/" class="t-blue">{{ t(key="admin-exit", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li>
|
||||
<form method="post" action="/admin/logout">
|
||||
<button type="submit" class="t-red w-full">{{ t(key="logout", lang=lang | default(value='sk')) }}</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="term-nav-right">
|
||||
<div class="dropdown dropdown-end md:hidden">
|
||||
<div tabindex="0" role="button" class="btn btn-ghost btn-sm btn-circle" aria-label="{{ t(key='menu', lang=lang | default(value='sk')) }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="h-5 w-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
||||
</svg>
|
||||
</div>
|
||||
<ul tabindex="0"
|
||||
class="menu dropdown-content z-50 mt-3 w-52 border border-base-300 bg-base-200 p-2 shadow-lg">
|
||||
<li><a href="/admin/dashboard">{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/admin/blog/articles">{{ t(key="admin-blog", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/admin/audio/albums">{{ t(key="admin-audio", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/admin/about">{{ t(key="admin-about", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/" class="t-blue">{{ t(key="admin-exit", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li>
|
||||
<form method="post" action="/admin/logout">
|
||||
<button type="submit" class="t-red w-full">{{ t(key="logout", lang=lang | default(value='sk')) }}</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="dropdown dropdown-end">
|
||||
<div tabindex="0" role="button" class="btn btn-ghost btn-sm btn-circle" aria-label="{{ t(key='settings', lang=lang | default(value='sk')) }}" title="{{ t(key='settings', lang=lang | default(value='sk')) }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="h-5 w-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>
|
||||
</div>
|
||||
<form method="post" action="/lang" hx-boost="false">
|
||||
<ul tabindex="0" class="menu dropdown-content z-50 mt-3 w-56 border border-base-300 bg-base-200 p-2 shadow-lg">
|
||||
<li class="menu-title">{{ t(key="settings-language", lang=lang | default(value='sk')) }}</li>
|
||||
<li>
|
||||
<button type="submit" name="lang" value="en" class="{% if lang | default(value='sk') == 'en' %}active{% endif %}">
|
||||
{{ t(key="language-en", lang=lang | default(value='sk')) }}
|
||||
{% if lang | default(value='sk') == 'en' %}
|
||||
<span class="ml-auto">✓</span>
|
||||
{% endif %}
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button type="submit" name="lang" value="sk" class="{% if lang | default(value='sk') == 'sk' %}active{% endif %}">
|
||||
{{ t(key="language-sk", lang=lang | default(value='sk')) }}
|
||||
{% if lang | default(value='sk') == 'sk' %}
|
||||
<span class="ml-auto">✓</span>
|
||||
{% endif %}
|
||||
</button>
|
||||
</li>
|
||||
<li class="menu-title">{{ t(key="settings-theme", lang=lang | default(value='sk')) }}</li>
|
||||
<li><button type="button" data-theme-opt="system" onclick="setTheme('system')">{{ t(key="theme-system", lang=lang | default(value='sk')) }} <span class="opt-check ml-auto hidden">✓</span></button></li>
|
||||
<li><button type="button" data-theme-opt="light" onclick="setTheme('light')">{{ t(key="theme-light", lang=lang | default(value='sk')) }} <span class="opt-check ml-auto hidden">✓</span></button></li>
|
||||
<li><button type="button" data-theme-opt="dark" onclick="setTheme('dark')">{{ t(key="theme-dark", lang=lang | default(value='sk')) }} <span class="opt-check ml-auto hidden">✓</span></button></li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<div id="nav-backdrop" aria-hidden="true"></div>
|
||||
<main class="term-main">
|
||||
{% block content %}{% endblock content %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
63
assets/views/admin/blog/edit.html
Normal file
63
assets/views/admin/blog/edit.html
Normal file
@@ -0,0 +1,63 @@
|
||||
{% extends "admin/base.html" %}
|
||||
|
||||
{% block title %}{{ t(key="edit-article", lang=lang | default(value='sk')) }}{% endblock title %}
|
||||
{% block head %}
|
||||
<link href="/static/vendor/quill/quill.snow.css" rel="stylesheet" type="text/css">
|
||||
{% 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>
|
||||
<script src="/static/vendor/quill/quill.js"></script>
|
||||
<script src="/static/js/blog-editor.js"></script>
|
||||
{% endblock content %}
|
||||
63
assets/views/admin/blog/index.html
Normal file
63
assets/views/admin/blog/index.html
Normal file
@@ -0,0 +1,63 @@
|
||||
{% 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 %}
|
||||
64
assets/views/admin/blog/new.html
Normal file
64
assets/views/admin/blog/new.html
Normal file
@@ -0,0 +1,64 @@
|
||||
{% extends "admin/base.html" %}
|
||||
|
||||
{% block title %}{{ t(key="new-article", lang=lang | default(value='sk')) }}{% endblock title %}
|
||||
{% block head %}
|
||||
<link href="/static/vendor/quill/quill.snow.css" rel="stylesheet" type="text/css">
|
||||
{% 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>
|
||||
<script src="/static/vendor/quill/quill.js"></script>
|
||||
<script src="/static/js/blog-editor.js"></script>
|
||||
{% endblock content %}
|
||||
60
assets/views/admin/index.html
Normal file
60
assets/views/admin/index.html
Normal file
@@ -0,0 +1,60 @@
|
||||
{% extends "admin/base.html" %}
|
||||
|
||||
{% block title %}{{ t(key="admin-title", lang=lang | default(value='sk')) }}{% endblock title %}
|
||||
{% block crumb %}dashboard{% 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>
|
||||
|
||||
<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>
|
||||
{% endblock content %}
|
||||
34
assets/views/admin/login.html
Normal file
34
assets/views/admin/login.html
Normal file
@@ -0,0 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ t(key="login-title", lang=lang | default(value='sk')) }}{% endblock title %}
|
||||
{% block crumb %}admin/login{% endblock crumb %}
|
||||
|
||||
{% block content %}
|
||||
<div class="mx-auto mt-8 max-w-sm">
|
||||
<div class="card">
|
||||
<div class="term-head">
|
||||
<span class="term-head-name">{{ t(key="nav-admin", lang=lang | default(value='sk')) }}</span>
|
||||
<span class="term-head-meta term-tag is-red">{{ t(key="auth", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h1 class="term-title">{{ t(key="login-auth", lang=lang | default(value='sk')) }}</h1>
|
||||
{% if error %}
|
||||
<div class="alert alert-error mt-2">
|
||||
<span>✗ {{ t(key="login-error", lang=lang | default(value='sk')) }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<form method="post" action="/admin/login" hx-boost="false" class="space-y-2">
|
||||
<div class="form-control">
|
||||
<label class="label"><span class="label-text t-green">{{ t(key="login-email", lang=lang | default(value='sk')) }}:</span></label>
|
||||
<input type="email" name="email" required autofocus class="input input-bordered w-full">
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label"><span class="label-text t-green">{{ t(key="login-password", lang=lang | default(value='sk')) }}:</span></label>
|
||||
<input type="password" name="password" required class="input input-bordered w-full">
|
||||
</div>
|
||||
<button class="btn btn-primary mt-2 w-full">[ {{ t(key="login-auth", lang=lang | default(value='sk')) }} ]</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
Reference in New Issue
Block a user