41f133a337
Gap 2 (#25): VirtualTagsTab.razor + /virtual-tags global page — list/create/toggle virtual tags per draft generation with DataType, Script, trigger, Historize, Enabled fields. Tab wired into DraftEditor. Gap 3 (#26): ScriptedAlarmsTab.razor + /scripted-alarms global page — list/create scripted alarms with AlarmType, Severity, MessageTemplate, PredicateScript, HistorizeToAveva, Retain. SeverityBand helper shows Low/Medium/High/Critical label. Tab wired into DraftEditor. Gap 4 (#27): ScriptLogHub (SignalR IAsyncEnumerable stream) tails scripts-*.log with optional ScriptName filter; ScriptLog.razor provides Start/Stop/Clear controls plus level filter dropdown. Hub registered at /hubs/script-log in Program.cs. Nav rail gains a "Scripting" eyebrow with entries for all three pages. 19 new unit tests for ScriptLogHub parse/filter/tail helpers (Category=Unit). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
63 lines
2.8 KiB
Plaintext
63 lines
2.8 KiB
Plaintext
@inherits LayoutComponentBase
|
|
|
|
<header class="app-bar">
|
|
<span class="brand"><span class="mark">▮</span> OtOpcUa</span>
|
|
<span class="crumb">›</span>
|
|
<span class="crumb">admin console</span>
|
|
<span class="spacer"></span>
|
|
<AuthorizeView>
|
|
<Authorized>
|
|
<span class="meta">@context.User.Identity?.Name</span>
|
|
<span class="conn-pill" data-state="connected">
|
|
<span class="dot"></span><span>signed in</span>
|
|
</span>
|
|
</Authorized>
|
|
<NotAuthorized>
|
|
<span class="conn-pill" data-state="disconnected">
|
|
<span class="dot"></span><span>signed out</span>
|
|
</span>
|
|
</NotAuthorized>
|
|
</AuthorizeView>
|
|
</header>
|
|
|
|
<div class="app-shell">
|
|
<nav class="side-rail">
|
|
<div class="rail-eyebrow">Navigation</div>
|
|
<NavLink class="rail-link" href="/" Match="NavLinkMatch.All">Overview</NavLink>
|
|
<NavLink class="rail-link" href="/fleet" Match="NavLinkMatch.Prefix">Fleet status</NavLink>
|
|
<NavLink class="rail-link" href="/hosts" Match="NavLinkMatch.Prefix">Host status</NavLink>
|
|
<NavLink class="rail-link" href="/clusters" Match="NavLinkMatch.Prefix">Clusters</NavLink>
|
|
<NavLink class="rail-link" href="/reservations" Match="NavLinkMatch.Prefix">Reservations</NavLink>
|
|
<NavLink class="rail-link" href="/certificates" Match="NavLinkMatch.Prefix">Certificates</NavLink>
|
|
<NavLink class="rail-link" href="/role-grants" Match="NavLinkMatch.Prefix">Role grants</NavLink>
|
|
<div class="rail-eyebrow">Scripting</div>
|
|
<NavLink class="rail-link" href="/virtual-tags" Match="NavLinkMatch.Prefix">Virtual tags</NavLink>
|
|
<NavLink class="rail-link" href="/scripted-alarms" Match="NavLinkMatch.Prefix">Scripted alarms</NavLink>
|
|
<NavLink class="rail-link" href="/script-log" Match="NavLinkMatch.Prefix">Script log</NavLink>
|
|
|
|
<div class="rail-foot">
|
|
<AuthorizeView>
|
|
<Authorized>
|
|
<div class="rail-eyebrow">Session</div>
|
|
<a class="rail-user" href="/account">@context.User.Identity?.Name</a>
|
|
<div class="rail-roles">
|
|
@string.Join(", ", context.User.Claims
|
|
.Where(c => c.Type.EndsWith("/role")).Select(c => c.Value))
|
|
</div>
|
|
<form method="post" action="/auth/logout">
|
|
<button class="rail-btn" type="submit">Sign out</button>
|
|
</form>
|
|
</Authorized>
|
|
<NotAuthorized>
|
|
<div class="rail-eyebrow">Session</div>
|
|
<a class="rail-btn" href="/login">Sign in</a>
|
|
</NotAuthorized>
|
|
</AuthorizeView>
|
|
</div>
|
|
</nav>
|
|
|
|
<main class="page">
|
|
@Body
|
|
</main>
|
|
</div>
|