feat(footer): help popover shows license state + Activate link

- Bump version to 3.0 (src/__init__.py).
- Switch support address to support@unalogix.com.
- Help popover now includes a License section that reads
  ``src.license.current_state()``:
  * When activated + valid: name + expiry date + days remaining.
  * Otherwise: "Not activated" + an ``Activate now →`` link
    pointing at ``./activate``.
  License-state queries are wrapped so a corrupted license file
  can't take the footer down — it falls through to the inactive
  branch.
- Popover HTML is now built in Python (so the license branch
  lives in one place) and passed to the JS as a single string.

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

View File

@@ -5,4 +5,4 @@ the Windows Inno Setup script, the macOS DMG packaging script, and
the AppImage build. Bump here on release and CI propagates it.
"""
__version__ = "1.0.0"
__version__ = "3.0"

View File

@@ -565,12 +565,58 @@ def render_sticky_footer() -> None:
help_version = _html.escape(
_t("footer.help_version").format(version=__version__)
)
support_email = "support@datatools.app"
help_support = _html.escape(
_t("footer.help_support").format(email=support_email)
support_email = "support@unalogix.com"
help_support_text = _t("footer.help_support").format(email=support_email)
help_support_html = _html.escape(help_support_text).replace(
_html.escape(support_email),
f'<a href="mailto:{_html.escape(support_email)}">'
f'{_html.escape(support_email)}</a>',
)
license_label = _html.escape(_t("footer.help_license_label"))
help_dismiss = _html.escape(_t("footer.help_dismiss"))
# License section — read state and branch on activated/valid. The
# query is wrapped because a corrupted license file MUST NOT stop
# the footer from rendering; in that case we fall back to the
# "ask to activate" branch.
try:
from src.license import current_state as _license_state
state = _license_state()
except Exception:
state = None
if state is not None and state.activated and state.valid:
active_line = _t("footer.help_license_active").format(
name=state.name or state.email or "",
)
expires_line = _t("footer.help_license_expires").format(
date=(state.expires_at or "")[:10],
days=state.days_remaining,
)
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>'
)
else:
inactive_line = _html.escape(_t("footer.help_license_inactive"))
activate_link = _html.escape(_t("footer.help_activate_link"))
license_html = (
f'<div class="dt-help-row"><span class="dt-help-key">'
f'{license_label}:</span> {inactive_line}</div>'
f'<div class="dt-help-row">'
f'<a href="./activate" target="_self">{activate_link}</a></div>'
)
popover_html = (
f'<div class="dt-help-title">{help_title}</div>'
f'<div class="dt-help-row">{help_version}</div>'
f'{license_html}'
f'<div class="dt-help-row">{help_support_html}</div>'
f'<button type="button" class="dt-help-dismiss">{help_dismiss}</button>'
)
st.markdown(
"""
<style>
@@ -646,6 +692,15 @@ def render_sticky_footer() -> None:
margin: 0.15rem 0 !important;
line-height: 1.4 !important;
}
#datatools-help-popover .dt-help-row.dt-help-sub {
color: rgb(90, 95, 110) !important;
font-size: 12px !important;
margin-left: 0.65rem !important;
}
#datatools-help-popover .dt-help-key {
color: rgb(90, 95, 110) !important;
font-weight: 500 !important;
}
#datatools-help-popover .dt-help-row a {
color: rgb(0, 102, 204) !important;
text-decoration: none !important;
@@ -678,11 +733,7 @@ def render_sticky_footer() -> None:
var labels = {_json.dumps({
"close": close_label,
"help": help_label,
"help_title": help_title,
"help_version": help_version,
"help_support": help_support,
"help_dismiss": help_dismiss,
"support_email": support_email,
"popover_html": popover_html,
})};
function build(doc) {{
var prev = doc.getElementById('datatools-sticky-footer');
@@ -710,14 +761,7 @@ def render_sticky_footer() -> None:
var pop = doc.createElement('div');
pop.id = 'datatools-help-popover';
pop.hidden = true;
pop.innerHTML =
'<div class="dt-help-title">' + labels.help_title + '</div>' +
'<div class="dt-help-row">' + labels.help_version + '</div>' +
'<div class="dt-help-row">' + labels.help_support.replace(
labels.support_email,
'<a href="mailto:' + labels.support_email + '">' + labels.support_email + '</a>'
) + '</div>' +
'<button type="button" class="dt-help-dismiss">' + labels.help_dismiss + '</button>';
pop.innerHTML = labels.popover_html;
helpBtn.addEventListener('click', function (e) {{
e.preventDefault();

View File

@@ -178,6 +178,11 @@
"help_title": "DataTools",
"help_version": "Version {version}",
"help_support": "Support: {email}",
"help_license_label": "License",
"help_license_inactive": "Not activated",
"help_license_active": "{name}",
"help_license_expires": "Expires {date} ({days} days)",
"help_activate_link": "Activate now →",
"help_dismiss": "Close"
}
}

View File

@@ -178,6 +178,11 @@
"help_title": "DataTools",
"help_version": "Versión {version}",
"help_support": "Soporte: {email}",
"help_license_label": "Licencia",
"help_license_inactive": "No activada",
"help_license_active": "{name}",
"help_license_expires": "Caduca {date} ({days} días)",
"help_activate_link": "Activar ahora →",
"help_dismiss": "Cerrar"
}
}