feat: add admin workspace navigation
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
---
|
||||
const path = Astro.url.pathname;
|
||||
const links = [
|
||||
["/admin", "Обзор"],
|
||||
["/admin/moderation", "Уловы"],
|
||||
["/admin/external-sources", "Источники"],
|
||||
["/admin/media", "Медиа"],
|
||||
] as const;
|
||||
---
|
||||
<nav class="admin-nav" aria-label="Разделы админ-панели">
|
||||
{links.map(([href, label]) => <a class={path === href ? "active" : undefined} aria-current={path === href ? "page" : undefined} href={href}>{label}</a>)}
|
||||
</nav>
|
||||
@@ -1,10 +1,12 @@
|
||||
---
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import AdminNav from "../../components/AdminNav.astro";
|
||||
const apiUrl = import.meta.env.PUBLIC_API_URL || "http://localhost:8000";
|
||||
---
|
||||
<Layout title="Внешние источники — RF4 Spotter">
|
||||
<section class="form-hero"><div><span class="eyebrow"><b>ADMIN</b> Staging</span><h1>Внешние<br/><em>наблюдения</em></h1></div><p>Сопоставьте названия с каноническим каталогом. Опубликовать можно только запись с координатами и весом.</p></section>
|
||||
<section class="moderation-app" data-api-url={apiUrl}>
|
||||
<AdminNav />
|
||||
<form class="admin-login" autocomplete="off"><label>Административный токен<input name="token" type="password" required autocomplete="off" /></label><button data-action="primary" type="submit">Открыть очередь</button></form>
|
||||
<p class="privacy">Токен остаётся только в памяти страницы. Исходная ссылка и происхождение сохраняются при публикации.</p>
|
||||
<div class="admin-session-bar" hidden><span>Административная сессия активна</span><button data-action="secondary" type="button" data-admin-logout>Выйти</button></div>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
---
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import AdminNav from "../../components/AdminNav.astro";
|
||||
const apiUrl = import.meta.env.PUBLIC_API_URL || "http://localhost:8000";
|
||||
---
|
||||
<Layout title="Административная панель — RF4 Spotter">
|
||||
<section class="form-hero"><div><span class="eyebrow"><b>ADMIN</b> Центр управления</span><h1>Панель<br/><em>модератора</em></h1></div><p>Очереди, состояние источников и последние импорты в одном безопасном обзоре.</p></section>
|
||||
<main class="admin-dashboard" data-api-url={apiUrl}>
|
||||
<AdminNav />
|
||||
<form class="admin-login" autocomplete="off"><label>Административный токен<input name="token" type="password" required autocomplete="off" /></label><button data-action="primary" type="submit">Открыть панель</button></form>
|
||||
<p class="privacy">Токен существует только в памяти вкладки. Сессия завершится после 15 минут бездействия.</p>
|
||||
<div class="admin-session-bar" hidden><span>Административная сессия активна</span><button data-action="secondary" type="button" data-admin-logout>Выйти</button></div>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
---
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import AdminNav from "../../components/AdminNav.astro";
|
||||
const apiUrl = import.meta.env.PUBLIC_API_URL || "http://localhost:8000";
|
||||
---
|
||||
<Layout title="Проверка медиа — RF4 Spotter" noindex>
|
||||
<section class="form-hero"><div><span class="eyebrow"><b>ADMIN</b> Media review</span><h1>Проверка<br/><em>медиа</em></h1></div><p>Сравнение approved и upgrade_queued файлов перед отдельным редакционным решением.</p></section>
|
||||
<main class="moderation-app" data-api-url={apiUrl}>
|
||||
<AdminNav />
|
||||
<form class="admin-login" autocomplete="off"><label>Административный токен<input name="token" type="password" required autocomplete="off" /></label><button data-action="primary" type="submit">Открыть медиатеку</button></form>
|
||||
<p class="privacy">Публичные файлы не переключаются из этого экрана. Токен хранится только в памяти страницы.</p>
|
||||
<div class="admin-session-bar" hidden><span>Административная сессия активна</span><button data-action="secondary" type="button" data-admin-logout>Выйти</button></div>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
---
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import AdminNav from "../../components/AdminNav.astro";
|
||||
const apiUrl = import.meta.env.PUBLIC_API_URL || "http://localhost:8000";
|
||||
---
|
||||
<Layout title="Модерация уловов — RF4 Spotter">
|
||||
<section class="form-hero"><div><span class="eyebrow"><b>ADMIN</b> Очередь проверки</span><h1>Модерация<br/><em>уловов</em></h1></div><p>Проверьте данные и скриншот до того, как запись повлияет на публичную статистику.</p></section>
|
||||
<section class="moderation-app" data-api-url={apiUrl}>
|
||||
<AdminNav />
|
||||
<form class="admin-login" autocomplete="off"><label>Административный токен<input name="token" type="password" required autocomplete="off" /></label><button data-action="primary" type="submit">Открыть очередь</button></form>
|
||||
<p class="privacy">Токен хранится только в памяти страницы и не записывается в URL или localStorage.</p>
|
||||
<div class="admin-session-bar" hidden><span>Административная сессия активна</span><button data-action="secondary" type="button" data-admin-logout>Выйти</button></div>
|
||||
|
||||
@@ -59,6 +59,9 @@ footer{min-height:118px;background:var(--deep);color:#dbe4df;padding:28px max(32
|
||||
/* One semantic treatment for empty, unavailable and invalid public states. */
|
||||
.state.unavailable-state{border-style:solid;border-color:color-mix(in srgb,var(--warning) 35%,var(--border));background:color-mix(in srgb,var(--warning-soft) 35%,var(--surface))}.state.error-state{background:color-mix(in srgb,var(--danger-soft) 28%,var(--surface))}.state>a{margin-top:8px;font-weight:750;text-underline-offset:4px}
|
||||
|
||||
/* Admin navigation keeps all operational surfaces reachable by keyboard. */
|
||||
.admin-nav{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 18px}.admin-nav a{padding:9px 13px;border:1px solid var(--line);border-radius:999px;color:var(--text-muted);font-size:12px;text-decoration:none}.admin-nav a:hover,.admin-nav a.active{border-color:var(--focus);color:var(--deep);background:var(--lime)}
|
||||
|
||||
/* Action hierarchy stays semantic across public and admin surfaces. */
|
||||
[data-action]{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:42px;padding:0 18px;border:1px solid transparent;border-radius:10px;font-weight:750;text-decoration:none;transition:background-color var(--motion-fast) var(--ease-out),border-color var(--motion-fast) var(--ease-out),color var(--motion-fast) var(--ease-out),transform var(--motion-fast) var(--ease-out)}[data-action]:hover:not(:disabled){transform:translateY(-1px)}[data-action]:active:not(:disabled){transform:translateY(0)}[data-action="primary"]{background:var(--lime);color:var(--deep)}[data-action="secondary"]{border-color:var(--border);background:var(--surface-soft);color:var(--text-muted)}[data-action="inverse"]{border-color:#ffffff38;background:var(--deep);color:var(--white)}[data-action="danger"]{background:var(--danger-soft);color:var(--danger)}[data-action="quiet-danger"]{border-color:color-mix(in srgb,var(--danger) 35%,transparent);background:transparent;color:var(--danger)}[data-action="quiet"]{background:transparent;color:#d5dfdc;text-decoration:underline;text-underline-offset:4px}[data-action]:disabled{opacity:.55;cursor:not-allowed}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user