fix: address user feedback (delete user, editor return, UI tweaks)
This commit is contained in:
@@ -85,7 +85,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-sm text-app-textMuted">На странице:</span>
|
<span class="text-sm text-app-textMuted">На странице:</span>
|
||||||
<select name="per_page" class="select select-sm w-auto py-1" form="filter-form">
|
<select name="per_page" class="select select-sm w-auto py-1 pr-8" form="filter-form">
|
||||||
{% for n in [10,25,50,100] %}
|
{% for n in [10,25,50,100] %}
|
||||||
<option value="{{ n }}" {% if pagination.per_page == n %}selected{% endif %}>{{ n }}</option>
|
<option value="{{ n }}" {% if pagination.per_page == n %}selected{% endif %}>{{ n }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -74,7 +74,11 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<div class="mb-4" x-data="{ galleryOpen: false, activeIdx: 0, images: {{ photo_urls | tojson | forceescape }} }" @keydown.escape.window="galleryOpen = false">
|
<div class="mb-4" x-data="{ galleryOpen: false, activeIdx: 0, images: {{ photo_urls | tojson | forceescape }} }"
|
||||||
|
@keydown.escape.window="galleryOpen = false"
|
||||||
|
@keydown.left.window="if(galleryOpen && images.length > 1) activeIdx = (activeIdx === 0) ? images.length - 1 : activeIdx - 1"
|
||||||
|
@keydown.right.window="if(galleryOpen && images.length > 1) activeIdx = (activeIdx === images.length - 1) ? 0 : activeIdx + 1">
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
{% for m in p.media_items %}
|
{% for m in p.media_items %}
|
||||||
{% if m.type == "photo" and m.url %}
|
{% if m.type == "photo" and m.url %}
|
||||||
@@ -91,32 +95,30 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Alpine Modal Gallery -->
|
<!-- Alpine Modal Gallery -->
|
||||||
<template x-if="galleryOpen">
|
<div x-show="galleryOpen" style="display: none;" x-transition.opacity class="fixed inset-0 z-[100] flex items-center justify-center bg-black/90 backdrop-blur-sm p-4">
|
||||||
<div class="fixed inset-0 z-[100] flex items-center justify-center bg-black/90 backdrop-blur-sm p-4">
|
<!-- Close button -->
|
||||||
<!-- Close button -->
|
<button @click.prevent="galleryOpen = false" type="button" class="absolute top-4 right-4 text-white hover:text-app-primary transition-colors p-2">
|
||||||
<button @click.prevent="galleryOpen = false" type="button" class="absolute top-4 right-4 text-white hover:text-app-primary transition-colors p-2">
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
|
||||||
<i data-lucide="x" class="w-8 h-8"></i>
|
</button>
|
||||||
</button>
|
|
||||||
|
<!-- Previous -->
|
||||||
<!-- Previous -->
|
<button x-show="images.length > 1" @click.prevent="activeIdx = (activeIdx === 0) ? images.length - 1 : activeIdx - 1" type="button" class="absolute left-4 top-1/2 -translate-y-1/2 text-white hover:text-app-primary p-2">
|
||||||
<button x-show="images.length > 1" @click.prevent="activeIdx = (activeIdx === 0) ? images.length - 1 : activeIdx - 1" type="button" class="absolute left-4 top-1/2 -translate-y-1/2 text-white hover:text-app-primary p-2">
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7" /></svg>
|
||||||
<i data-lucide="chevron-left" class="w-10 h-10"></i>
|
</button>
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Image -->
|
<!-- Image -->
|
||||||
<img :src="images[activeIdx]" class="max-w-full max-h-[90vh] object-contain rounded-lg shadow-2xl">
|
<img :src="images[activeIdx]" class="max-w-full max-h-[90vh] object-contain rounded-lg shadow-2xl">
|
||||||
|
|
||||||
<!-- Next -->
|
<!-- Next -->
|
||||||
<button x-show="images.length > 1" @click.prevent="activeIdx = (activeIdx === images.length - 1) ? 0 : activeIdx + 1" type="button" class="absolute right-4 top-1/2 -translate-y-1/2 text-white hover:text-app-primary p-2">
|
<button x-show="images.length > 1" @click.prevent="activeIdx = (activeIdx === images.length - 1) ? 0 : activeIdx + 1" type="button" class="absolute right-4 top-1/2 -translate-y-1/2 text-white hover:text-app-primary p-2">
|
||||||
<i data-lucide="chevron-right" class="w-10 h-10"></i>
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" /></svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Counter -->
|
<!-- Counter -->
|
||||||
<div x-show="images.length > 1" class="absolute bottom-4 left-1/2 -translate-x-1/2 text-white font-mono text-sm bg-black/50 px-3 py-1 rounded-full">
|
<div x-show="images.length > 1" class="absolute bottom-4 left-1/2 -translate-x-1/2 text-white font-mono text-sm bg-black/50 px-3 py-1 rounded-full">
|
||||||
<span x-text="activeIdx + 1"></span> / <span x-text="images.length"></span>
|
<span x-text="activeIdx + 1"></span> / <span x-text="images.length"></span>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@@ -182,28 +184,38 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Actions -->
|
<!-- Actions -->
|
||||||
|
{% if p.editorial_status != 'published' %}
|
||||||
<div class="mt-5 pt-4 border-t border-app-border flex flex-wrap items-center justify-between gap-3">
|
<div class="mt-5 pt-4 border-t border-app-border flex flex-wrap items-center justify-between gap-3">
|
||||||
<!-- Delete Button -->
|
{% if p.editorial_status == 'rejected' %}
|
||||||
<div>
|
<div>
|
||||||
{% if p.editorial_status != 'rejected' %}
|
<button class="btn btn-ghost btn-sm text-app-warning hover:bg-app-warning/10 hover:text-app-warning" type="button"
|
||||||
|
hx-post="/editor/{{ p.id }}/return" hx-target="#post-container-{{ p.id }}" hx-swap="outerHTML"
|
||||||
|
hx-vals='{"csrf_token": "{{ user.csrf_token }}"}'>
|
||||||
|
<i data-lucide="corner-up-left" class="w-4 h-4"></i> Вернуть на проверку
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<!-- Delete Button -->
|
||||||
|
<div>
|
||||||
<button class="btn btn-ghost btn-sm text-app-error hover:bg-app-errorBg hover:text-app-error" type="button"
|
<button class="btn btn-ghost btn-sm text-app-error hover:bg-app-errorBg hover:text-app-error" type="button"
|
||||||
hx-post="/editor/{{ p.id }}/reject" hx-target="#post-container-{{ p.id }}" hx-swap="outerHTML"
|
hx-post="/editor/{{ p.id }}/reject" hx-target="#post-container-{{ p.id }}" hx-swap="outerHTML"
|
||||||
hx-vals='{"csrf_token": "{{ user.csrf_token }}"}'
|
hx-vals='{"csrf_token": "{{ user.csrf_token }}"}'
|
||||||
onclick="return confirm('Отклонить пост?')">
|
onclick="return confirm('Отклонить пост?')">
|
||||||
<i data-lucide="x" class="w-4 h-4"></i> В мусор
|
<i data-lucide="x" class="w-4 h-4"></i> Отклонить
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="flex gap-2">
|
||||||
<div class="flex gap-2">
|
<button type="submit" name="action" value="save" class="btn btn-surface btn-sm">
|
||||||
<button type="submit" name="action" value="save" class="btn btn-surface btn-sm">
|
<i data-lucide="save" class="w-4 h-4"></i> Сохранить как черновик
|
||||||
<i data-lucide="save" class="w-4 h-4"></i> Сохранить как черновик
|
</button>
|
||||||
</button>
|
<button type="submit" name="action" value="accept" class="btn btn-primary btn-sm">
|
||||||
<button type="submit" name="action" value="accept" class="btn btn-primary btn-sm">
|
<i data-lucide="send" class="w-4 h-4"></i> Сохранить и Опубликовать
|
||||||
<i data-lucide="send" class="w-4 h-4"></i> Сохранить и Опубликовать
|
</button>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
<input type="hidden" name="{{ item.key }}" value="{{ item.value }}">
|
<input type="hidden" name="{{ item.key }}" value="{{ item.value }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<select name="per_page" class="select select-sm w-20 py-1" onchange="this.form.submit()">
|
<select name="per_page" class="select select-sm w-20 py-1 pr-8" onchange="this.form.submit()">
|
||||||
{% for n in [25,50,100,200] %}
|
{% for n in [25,50,100,200] %}
|
||||||
<option value="{{ n }}" {% if pagination.per_page == n %}selected{% endif %}>{{ n }}</option>
|
<option value="{{ n }}" {% if pagination.per_page == n %}selected{% endif %}>{{ n }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-sm text-app-textMuted">На странице:</span>
|
<span class="text-sm text-app-textMuted">На странице:</span>
|
||||||
<select name="per_page" class="select select-sm w-auto py-1" form="filter-form" onchange="document.getElementById('filter-form').dispatchEvent(new Event('submit', {cancelable: true}))">
|
<select name="per_page" class="select select-sm w-auto py-1 pr-8" form="filter-form" onchange="document.getElementById('filter-form').dispatchEvent(new Event('submit', {cancelable: true}))">
|
||||||
{% for n in [25,50,100,200] %}
|
{% for n in [25,50,100,200] %}
|
||||||
<option value="{{ n }}" {% if pagination.per_page == n %}selected{% endif %}>{{ n }}</option>
|
<option value="{{ n }}" {% if pagination.per_page == n %}selected{% endif %}>{{ n }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -43,12 +43,20 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="px-4 py-3 text-right">
|
<td class="px-4 py-3 text-right">
|
||||||
{% if user.role == "admin" and user.id != u.id %}
|
{% if user.role == "admin" and user.id != u.id %}
|
||||||
<form method="post" action="/users/{{ u.id }}/toggle" class="m-0 inline-block">
|
<div class="flex items-center justify-end gap-2">
|
||||||
<input type="hidden" name="csrf_token" value="{{ user.csrf_token }}">
|
<form method="post" action="/users/{{ u.id }}/toggle" class="m-0 inline-block">
|
||||||
<button class="btn btn-xs {% if u.is_active %}btn-danger-outline{% else %}btn-primary-outline{% endif %}" type="submit">
|
<input type="hidden" name="csrf_token" value="{{ user.csrf_token }}">
|
||||||
{% if u.is_active %}Блокировать{% else %}Разблокировать{% endif %}
|
<button class="btn btn-xs {% if u.is_active %}btn-surface{% else %}btn-primary-outline{% endif %}" type="submit">
|
||||||
</button>
|
{% if u.is_active %}Блокировать{% else %}Разблокировать{% endif %}
|
||||||
</form>
|
</button>
|
||||||
|
</form>
|
||||||
|
<form method="post" action="/users/{{ u.id }}/delete" class="m-0 inline-block" onsubmit="return confirm('Точно удалить пользователя?')">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ user.csrf_token }}">
|
||||||
|
<button class="btn btn-xs text-app-error hover:bg-app-errorBg hover:text-app-error" type="submit">
|
||||||
|
Удалить
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
Reference in New Issue
Block a user