rustio-admin 0.24.0

Django Admin, but for Rust. A small, focused admin framework.
Documentation
<header class="rio-topbar" role="banner">
  {% if identity %}
  <button type="button" class="rio-sidebar-toggle" data-rio-sidebar-toggle aria-label="Toggle navigation">
    {{ icon("menu", class="rio-icon") }}
  </button>
  {% endif %}
  <a href="/admin" class="rio-topbar-brand">{{ site_header }}</a>
  {% if identity %}
  {# Global ⌘K search palette trigger. The trigger sits between the
   # brand and the nav; the modal markup lives below this header and
   # is positioned `fixed` so DOM placement doesn't affect layout.
   # JS in admin.js wires the button + ⌘K shortcut + keyboard nav. #}
  <button type="button" class="rio-search-trigger" data-rio-search-trigger
          aria-haspopup="dialog" aria-controls="rio-search-palette"
          aria-label="Search across all models">
    {{ icon("search", class="rio-search-trigger__icon") }}
    <span class="rio-search-trigger__label">Search…</span>
    <span class="rio-search-trigger__kbd" aria-hidden="true">⌘K</span>
  </button>
  {% endif %}
  <nav class="rio-topbar-nav" aria-label="Account">
    {% if identity %}
      {# Bell stays on the chrome — status indicator deserves its
       # own affordance. Everything else (MFA, sessions, password,
       # log out) folds into one account dropdown anchored on the
       # user identity. The old inline strip ran out of horizontal
       # room and visually competed with the page title. #}
      <a href="/admin/notifications" class="rio-topbar-link rio-topbar-bell"
         aria-label="{% if unread_count > 0 %}{{ unread_count }} unread notifications{% else %}Notifications{% endif %}">
        {{ icon("bell", class="rio-icon") }}
        {% if unread_count > 0 %}
        <span class="rio-topbar-bell__badge" aria-hidden="true">{% if unread_count > 99 %}99+{% else %}{{ unread_count }}{% endif %}</span>
        {% endif %}
      </a>
      <div class="rio-dropdown rio-topbar-account" data-rio-dropdown>
        <button type="button" class="rio-dropdown-toggle rio-topbar-account__toggle"
                aria-haspopup="true" aria-expanded="false"
                aria-label="Account menu for {{ identity.email }}">
          <span class="rio-topbar-account__avatar" aria-hidden="true">{{ identity.email[:1]|upper }}</span>
          <span class="rio-topbar-account__email">{{ identity.email }}</span>
          {{ icon("chevron-down", class="rio-chev") }}
        </button>
        <div class="rio-dropdown-panel rio-topbar-account__panel" role="dialog" aria-label="Account">
          <div class="rio-dropdown-section">
            <span class="rio-dropdown-label">Signed in as</span>
            <p class="rio-topbar-account__who">{{ identity.email }}</p>
          </div>
          <div class="rio-dropdown-section">
            <div class="rio-dropdown-menu">
              {# Two-factor self-service surface — same routing as
               # the previous inline link: enrolled users get the
               # regenerate/disable chooser, un-enrolled users get
               # the enrol QR-code flow. #}
              {% if identity.mfa_enabled %}
                <a href="/admin/account/mfa/regenerate-codes" class="rio-dropdown-item">Two-factor</a>
              {% else %}
                <a href="/admin/account/mfa/enroll" class="rio-dropdown-item">Enable MFA</a>
              {% endif %}
              <a href="/admin/account/sessions" class="rio-dropdown-item">Sessions</a>
              <a href="/admin/password_change" class="rio-dropdown-item">Change password</a>
            </div>
          </div>
          <div class="rio-dropdown-footer">
            <form method="post" action="/admin/logout" class="rio-topbar-logout">
              <input type="hidden" name="_csrf" value="{{ csrf_token }}">
              <button type="submit" class="rio-button rio-button--ghost">Log out</button>
            </form>
          </div>
        </div>
      </div>
    {% else %}
      <a href="/admin/login" class="rio-topbar-link">Log in</a>
    {% endif %}
  </nav>
</header>

{% if identity %}
{# Global ⌘K search palette modal. Hidden by default (aria-hidden);
 # JS toggles aria-hidden="false" on open. The dialog is centered and
 # dimmed via .rio-search-palette in components/search_palette.css.
 # Backdrop click + Esc + selecting a result all close it. Empty
 # query returns no results (server short-circuits below 2 chars). #}
<div id="rio-search-palette" class="rio-search-palette"
     role="dialog" aria-modal="true" aria-hidden="true"
     aria-label="Search" data-rio-search-palette>
  <div class="rio-search-palette__dialog" data-rio-search-palette-dialog>
    <div class="rio-search-palette__input-wrap">
      {{ icon("search", class="rio-search-palette__icon") }}
      <input type="search" class="rio-search-palette__input"
             placeholder="Search across all models…"
             autocomplete="off" spellcheck="false"
             data-rio-search-palette-input
             aria-label="Search query"
             aria-controls="rio-search-palette-results">
    </div>
    <ul id="rio-search-palette-results" class="rio-search-palette__list"
        role="listbox" data-rio-search-palette-results></ul>
    <div class="rio-search-palette__footer" aria-hidden="true">
      <span class="rio-search-palette__footer-item">
        <span class="rio-search-palette__footer-kbd"></span>
        <span class="rio-search-palette__footer-kbd"></span> navigate
      </span>
      <span class="rio-search-palette__footer-item">
        <span class="rio-search-palette__footer-kbd"></span> open
      </span>
      <span class="rio-search-palette__footer-item">
        <span class="rio-search-palette__footer-kbd">esc</span> close
      </span>
    </div>
  </div>
</div>
{% endif %}