fix(nav,footer): hide Activate from sidebar, surface it in Help popover

- Collapse the Account section: Activate now lives in the same
  hidden sidebar section as Close (single ``_hidden`` group). Both
  pages stay registered with ``st.navigation`` so /activate and
  /close remain URL-routable for the Help-popover / Close-button
  links — only the sidebar entries + their section header are
  hidden via CSS.
- Help popover always exposes a license-management link now:
  ``Activate now →`` when the license is inactive, ``Manage
  license →`` when it is active and valid. Both point at
  ``./activate``.
- Extend the sidebar-hide CSS to also match ``a[href$="/activate"]``
  and the section that contains it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-18 15:39:14 +00:00
parent 9e8b4b2ca9
commit d840230e48
4 changed files with 30 additions and 19 deletions

View File

@@ -88,17 +88,20 @@ def _build_navigation() -> dict[str, list]:
default=True,
url_path="home",
)
# Activate + Close are both reached from the sticky-footer Help
# popover (Activate / Manage-license link and Close button). They
# are registered with ``st.navigation`` so ``/activate`` and
# ``/close`` remain URL-routable — pages not listed in the dict
# 404 even by direct hit — but their sidebar entries are hidden
# via CSS in ``hide_streamlit_chrome``. We group them under a
# single section header that is also hidden by that same CSS rule
# so no orphan "Account" / "Close" label is left in the sidebar.
activate = st.Page(
"pages/_Activate.py",
title=_t("nav.activate_title") or "Activate",
icon="🔑",
url_path="activate",
)
# Close is registered so it remains URL-routable (/close) for the
# sticky-footer Close button. It is hidden from the sidebar via CSS
# in ``hide_streamlit_chrome`` rather than being unregistered —
# ``st.navigation`` requires every routable page to be listed; pages
# not in the dict 404 even by direct URL hit.
close = st.Page(
"pages/99_Close.py",
title=_t("nav.close_title") or "Close",
@@ -106,15 +109,13 @@ def _build_navigation() -> dict[str, list]:
url_path="close",
)
account_header = _t("nav.section_account") or "Account"
close_header = _t("nav.section_close") or "Close"
return {
"": [home],
section_label("cleaners"): by_section["cleaners"],
section_label("transformations"): by_section["transformations"],
section_label("automations"): by_section["automations"],
account_header: [activate],
close_header: [close],
# Hidden section — see comment above + CSS in hide_streamlit_chrome.
"_hidden": [activate, close],
}

View File

@@ -60,20 +60,25 @@ header[data-testid="stHeader"] {
footer {
display: none !important;
}
/* Hide the "Close" entry from the sidebar nav — the page is kept
registered so the sticky-footer Close link can still route to
/close, but the sidebar entry is redundant and risks mis-clicks. */
/* Hide the Activate + Close entries from the sidebar nav — both
pages stay registered (so /activate and /close remain
URL-routable) but are reached from the sticky-footer Help
popover instead of the sidebar. */
[data-testid="stSidebarNav"] a[href$="/activate"],
[data-testid="stSidebarNav"] a[href$="/activate/"],
[data-testid="stSidebarNav"] a[href$="/close"],
[data-testid="stSidebarNav"] a[href$="/close/"] {
display: none !important;
}
/* Hide the entire "Close" section so its header label doesn't
orphan above the hidden link. Streamlit tags each nav section
with ``data-testid="stNavSection"``; the :has() selector picks
only the one containing the close link. Modern browsers (Chrome
105+, Safari 15.4+, Firefox 121+) all support it; older
browsers fall back to showing the empty header, which is
visually harmless. */
/* Hide the section header that wraps those entries so no orphan
label is left above the hidden links. Streamlit tags each nav
section with ``data-testid="stNavSection"``; the :has() selector
picks only the one(s) containing those links. Modern browsers
(Chrome 105+, Safari 15.4+, Firefox 121+) all support :has();
older browsers fall back to showing the section header, which
is visually harmless. */
[data-testid="stSidebarNav"] [data-testid="stNavSection"]:has(a[href$="/activate"]),
[data-testid="stSidebarNav"] [data-testid="stNavSection"]:has(a[href$="/activate/"]),
[data-testid="stSidebarNav"] [data-testid="stNavSection"]:has(a[href$="/close"]),
[data-testid="stSidebarNav"] [data-testid="stNavSection"]:has(a[href$="/close/"]) {
display: none !important;
@@ -593,11 +598,14 @@ def render_sticky_footer() -> None:
date=(state.expires_at or "")[:10],
days=state.days_remaining,
)
manage_link = _html.escape(_t("footer.help_manage_link"))
license_html = (
f'<div class="dt-help-row"><span class="dt-help-key">'
f'{license_label}:</span> {_html.escape(active_line)}</div>'
f'<div class="dt-help-row dt-help-sub">'
f'{_html.escape(expires_line)}</div>'
f'<div class="dt-help-row">'
f'<a href="./activate" target="_self">{manage_link}</a></div>'
)
else:
inactive_line = _html.escape(_t("footer.help_license_inactive"))