Add avatar color, initials structure, and admin nav dropdown
Lint / Python (flake8) (push) Failing after 1m7s
Lint / JS (eslint) (push) Successful in 10s
Security / Python Security (bandit) (push) Failing after 1m31s
Test / Python Tests (pytest) (push) Successful in 1m8s
Lint / Notify on failure (push) Successful in 2s
Lint / Deploy (push) Has been skipped

- app.py: avatar_color Jinja filter using deterministic hash → lt-avatar--orange/green/purple
- base.html: proper lt-avatar--sm with lt-avatar-initials span and color class; multi-word initials support
- base.html: admin users get lt-nav-dropdown for Suppressions; non-admins see flat link; mobile drawer hides Suppressions for non-admins

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-18 23:54:01 -04:00
parent 293edd674e
commit c1c3905179
2 changed files with 39 additions and 2 deletions
+6
View File
@@ -27,6 +27,12 @@ logger = logging.getLogger('gandalf.web')
app = Flask(__name__) app = Flask(__name__)
_AVATAR_COLORS = ['lt-avatar--orange', 'lt-avatar--green', 'lt-avatar--purple', '']
@app.template_filter('avatar_color')
def avatar_color_filter(name: str) -> str:
return _AVATAR_COLORS[abs(hash(name)) % len(_AVATAR_COLORS)]
_cfg = None _cfg = None
+33 -2
View File
@@ -42,9 +42,11 @@
<a href="{{ url_for('inspector') }}" <a href="{{ url_for('inspector') }}"
class="lt-nav-drawer-link{% if request.endpoint == 'inspector' %} active{% endif %}" class="lt-nav-drawer-link{% if request.endpoint == 'inspector' %} active{% endif %}"
{% if request.endpoint == 'inspector' %}aria-current="page"{% endif %}>Inspector</a> {% if request.endpoint == 'inspector' %}aria-current="page"{% endif %}>Inspector</a>
{% if user.groups and 'admin' in user.groups %}
<a href="{{ url_for('suppressions_page') }}" <a href="{{ url_for('suppressions_page') }}"
class="lt-nav-drawer-link{% if request.endpoint == 'suppressions_page' %} active{% endif %}" class="lt-nav-drawer-link{% if request.endpoint == 'suppressions_page' %} active{% endif %}"
{% if request.endpoint == 'suppressions_page' %}aria-current="page"{% endif %}>Suppressions</a> {% if request.endpoint == 'suppressions_page' %}aria-current="page"{% endif %}>Suppressions</a>
{% endif %}
</nav> </nav>
</div> </div>
<div id="lt-nav-overlay" class="lt-nav-drawer-overlay"></div> <div id="lt-nav-overlay" class="lt-nav-drawer-overlay"></div>
@@ -93,20 +95,49 @@
{% if request.endpoint == 'inspector' %}aria-current="page"{% endif %}> {% if request.endpoint == 'inspector' %}aria-current="page"{% endif %}>
Inspector Inspector
</a> </a>
{% if user.groups and 'admin' in user.groups %}
<div class="lt-nav-dropdown" data-action="toggle-nav-dropdown">
<a href="#"
class="lt-nav-link{% if request.endpoint == 'suppressions_page' %} active{% endif %}"
role="button"
aria-haspopup="true"
aria-expanded="false"
aria-controls="lt-admin-dropdown-menu">
Admin &#x25BE;
</a>
<ul class="lt-nav-dropdown-menu"
id="lt-admin-dropdown-menu"
role="menu"
aria-label="Admin menu">
<li role="none">
<a href="{{ url_for('suppressions_page') }}" role="menuitem"
class="{% if request.endpoint == 'suppressions_page' %}active{% endif %}">
Suppressions
</a>
</li>
</ul>
</div>
{% else %}
<a href="{{ url_for('suppressions_page') }}" <a href="{{ url_for('suppressions_page') }}"
class="lt-nav-link{% if request.endpoint == 'suppressions_page' %} active{% endif %}" class="lt-nav-link{% if request.endpoint == 'suppressions_page' %} active{% endif %}"
{% if request.endpoint == 'suppressions_page' %}aria-current="page"{% endif %}> {% if request.endpoint == 'suppressions_page' %}aria-current="page"{% endif %}>
Suppressions Suppressions
</a> </a>
{% endif %}
</nav> </nav>
</div> </div>
<div class="lt-header-right"> <div class="lt-header-right">
{% set _uname = user.name or user.username %} {% set _uname = user.name or user.username %}
<div class="lt-avatar" title="{{ _uname }}" aria-label="{{ _uname }}">{{ _uname[0] | upper }}</div> {% set _words = _uname.split() %}
{% set _initials = (_words[0][0] ~ (_words[1][0] if _words|length > 1 else ''))|upper %}
<div class="lt-avatar lt-avatar--sm {{ _uname | avatar_color }}"
aria-hidden="true" title="{{ _uname }}">
<span class="lt-avatar-initials">{{ _initials }}</span>
</div>
<span class="lt-header-user">{{ _uname }}</span> <span class="lt-header-user">{{ _uname }}</span>
{% if user.groups and 'admin' in user.groups %} {% if user.groups and 'admin' in user.groups %}
<span class="lt-badge lt-badge-admin">admin</span> <span class="lt-badge lt-badge-admin" aria-label="Administrator">ADMIN</span>
{% endif %} {% endif %}
<button type="button" class="lt-theme-btn" id="lt-theme-btn" <button type="button" class="lt-theme-btn" id="lt-theme-btn"
aria-label="Toggle theme" title="Toggle light/dark mode">&#x2600;</button> aria-label="Toggle theme" title="Toggle light/dark mode">&#x2600;</button>