Шапка: служебные разделы в меню пользователя, статус НРД и время друг над другом
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4YVo4UMopqT5DzicqpPjL
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package lkgateway
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Снимок шапки для визуальной проверки: страница пишется в файл, если задан BJ_SHOT_OUT.
|
||||
func TestTopbarShot(t *testing.T) {
|
||||
out := os.Getenv("BJ_SHOT_OUT")
|
||||
if out == "" {
|
||||
t.Skip("BJ_SHOT_OUT не задан")
|
||||
}
|
||||
a, err := newAdmin()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rec := httptest.NewRecorder()
|
||||
p := nowPage("Настройка", "setup")
|
||||
p.NPR = true
|
||||
render(rec, a.modulePage, modulePageData{page: p, Content: template.HTML(`<div style="padding:40px" class="muted">содержимое страницы</div>`)})
|
||||
if err := os.WriteFile(out, rec.Body.Bytes(), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ body {
|
||||
border-radius:10px; box-shadow:0 8px 30px rgba(0,0,0,.14); min-width:210px; z-index:60; padding:6px; }
|
||||
.nav .ddmenu a { display:block; padding:8px 12px; border-radius:7px; }
|
||||
.topbar-right { display:flex; align-items:center; gap:12px; flex:none; padding-left:10px; }
|
||||
@media (max-width:1200px){ .sys-status span:not(.pulse){ display:none } .sys-status{ padding:5px 8px } .topbar .clock{ display:none } }
|
||||
@media (max-width:1100px){ .sys-status span:not(.pulse){ display:none } .sys-status{ padding:5px 8px } .topbar .clock{ display:none } .user-dd .user-role{ display:none } }
|
||||
/* Строка системного статуса — состояние читается всегда */
|
||||
.sys-status { display:inline-flex; align-items:center; gap:7px; font-size:12px; font-weight:600; color:var(--text-2); padding:5px 11px 5px 9px; border:1px solid var(--border); border-radius:20px; background:var(--card-2); white-space:nowrap; }
|
||||
.sys-status .pulse { width:8px; height:8px; border-radius:50%; background:var(--ok); box-shadow:0 0 0 0 var(--ok-weak); animation:pulse 2.4s var(--ease) infinite; }
|
||||
@@ -99,7 +99,20 @@ body {
|
||||
}
|
||||
.theme-toggle svg { width:16px; height:16px; }
|
||||
.theme-toggle:hover { background:var(--border); color:var(--text); }
|
||||
.topbar .clock { font-size:12px; color:var(--muted); font-family:var(--mono); font-variant-numeric:tabular-nums; letter-spacing:-0.02em; }
|
||||
.topbar .clock { font-size:11px; color:var(--muted); font-family:var(--mono); font-variant-numeric:tabular-nums; letter-spacing:-0.02em; }
|
||||
.sys-block { display:inline-flex; flex-direction:column; align-items:center; gap:2px; line-height:1.1; }
|
||||
.sys-block .sys-status { padding:3px 9px 3px 7px; font-size:11.5px; }
|
||||
.user-dd .ddbtn.user-chip { display:inline-flex; flex-direction:column; align-items:flex-start; padding:4px 10px; border-radius:8px; margin:0; max-width:220px; }
|
||||
.user-dd .user-name { font-size:12.5px; }
|
||||
.nav .ddmenu.ddmenu-right, .user-dd .ddmenu { left:auto; right:0; min-width:190px; }
|
||||
.user-dd { position:relative; }
|
||||
.user-dd .ddmenu { display:none; position:absolute; top:100%; right:0; margin-top:8px; background:var(--card); border:1px solid var(--border); border-radius:10px; padding:6px; box-shadow:0 10px 30px rgba(0,0,0,.12); z-index:50; }
|
||||
.user-dd:hover .ddmenu, .user-dd:focus-within .ddmenu { display:block; }
|
||||
.user-dd::after { content:''; position:absolute; top:100%; left:0; right:0; height:10px; }
|
||||
.user-dd .ddmenu a { display:block; padding:8px 12px; border-radius:7px; font-size:13px; color:var(--text-2); text-decoration:none; white-space:nowrap; }
|
||||
.user-dd .ddmenu a:hover, .user-dd .ddmenu a.active { background:var(--accent-weak); color:var(--accent-strong); }
|
||||
.user-dd:hover .ddbtn, .user-dd.active .ddbtn { background:var(--accent-weak); }
|
||||
.ddsep { display:block; height:1px; background:var(--border); margin:6px 4px; }
|
||||
|
||||
/* ---- Контент ---- */
|
||||
main { padding:24px; max-width:1200px; margin:0 auto; }
|
||||
@@ -304,16 +317,25 @@ label { font-size:13px; font-weight:500; }
|
||||
<a href="/admin/handbook">Справочник НРД</a>{{end}}
|
||||
</span>
|
||||
</span>{{end}}
|
||||
{{if can "settings"}}<span class="group-label">Администратор</span>
|
||||
<a href="/admin/setup" class="{{if eq .Active "setup"}}active{{end}}">Настройка</a>
|
||||
<a href="/admin/status" class="{{if eq .Active "status"}}active{{end}}">Статус</a>{{end}}
|
||||
<a href="/admin/help" class="{{if eq .Active "help"}}active{{end}}">Справка</a>
|
||||
</nav>
|
||||
<div class="topbar-right">
|
||||
{{with curUser}}<span class="user-chip" title="{{.RoleTitle}}{{if eq .Source "ad"}} · вход через AD{{end}}"><span class="user-name">{{if .Name}}{{.Name}}{{else}}{{.Login}}{{end}}</span><span class="user-role">{{.RoleTitle}}</span></span><a class="user-logout" href="/logout" title="Выйти">Выйти</a>{{else}}{{if authOpen}}<a class="user-logout" href="/admin/setup#users" title="Пользователи не созданы — вход открыт">без входа</a>{{end}}{{end}}
|
||||
<span class="sys-status {{if .IsMockMode}}warn{{end}}" title="Состояние обмена с НРД">
|
||||
<span class="pulse"></span>{{if .IsMockMode}}{{if devOn}}Эмуляция{{else}}Нет связи с НРД{{end}}{{else}}НРД на связи{{end}}</span>
|
||||
<span class="clock">{{.Now}}</span>
|
||||
<span class="sys-block">
|
||||
<span class="sys-status {{if .IsMockMode}}warn{{end}}" title="Состояние обмена с НРД">
|
||||
<span class="pulse"></span>{{if .IsMockMode}}{{if devOn}}Эмуляция{{else}}Нет связи с НРД{{end}}{{else}}НРД на связи{{end}}</span>
|
||||
<span class="clock">{{.Now}}</span>
|
||||
</span>
|
||||
<span class="dd user-dd{{if or (eq .Active "setup") (eq .Active "status") (eq .Active "help")}} active{{end}}">
|
||||
<span class="ddbtn user-chip" title="{{with curUser}}{{.RoleTitle}}{{if eq .Source "ad"}} · вход через AD{{end}}{{else}}Пользователи не созданы — вход открыт{{end}}">
|
||||
<span class="user-name">{{with curUser}}{{if .Name}}{{.Name}}{{else}}{{.Login}}{{end}}{{else}}без входа{{end}} ▾</span>
|
||||
<span class="user-role">{{with curUser}}{{.RoleTitle}}{{else}}вход открыт{{end}}</span>
|
||||
</span>
|
||||
<span class="ddmenu ddmenu-right">
|
||||
{{if can "settings"}}<a href="/admin/setup" class="{{if eq .Active "setup"}}active{{end}}">Настройка</a>
|
||||
<a href="/admin/status" class="{{if eq .Active "status"}}active{{end}}">Статус системы</a>{{end}}
|
||||
<a href="/admin/help" class="{{if eq .Active "help"}}active{{end}}">Справка</a>
|
||||
{{with curUser}}<span class="ddsep"></span><a href="/logout">Выйти</a>{{else}}{{if authOpen}}<span class="ddsep"></span><a href="/admin/setup#users">Создать пользователей</a>{{end}}{{end}}
|
||||
</span>
|
||||
</span>
|
||||
<button class="theme-toggle" id="theme-toggle" title="Светлая/тёмная тема" aria-label="Переключить тему"></button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
Reference in New Issue
Block a user