Development

This commit is contained in:
Matthew Grotke 2026-05-26 00:07:35 -04:00
parent 1c1bddade3
commit 537de935e9
2 changed files with 19 additions and 10 deletions

View file

@ -270,7 +270,8 @@ def _timing_status_msg(entry_ts, action_label):
def _build_timing_msg(entry_ts, action_label='Configuration saved'): def _build_timing_msg(entry_ts, action_label='Configuration saved'):
if not _apply_changes_immediately(): if not _apply_changes_immediately():
return f'{action_label}. Visit Actions page to apply your changes.' from markupsafe import Markup
return Markup(f'{action_label}. Visit <strong>Actions</strong> page to apply your changes.')
return _timing_status_msg(entry_ts, action_label) return _timing_status_msg(entry_ts, action_label)

View file

@ -1619,8 +1619,9 @@ def _load_datasource(spec):
def render_layout(view_id, content_html, tokens): def render_layout(view_id, content_html, tokens):
css = _load_css() css = _load_css()
level = _client_level() level = _client_level()
has_pending_alert = not _apply_changes_immediately() and bool(get_dashboard_pending())
titlebar_html = f'<div class="titlebar"><span class="titlebar-brand">{WEB_APP_DISPLAY_NAME}</span></div>' titlebar_html = f'<div class="titlebar"><span class="titlebar-brand">{WEB_APP_DISPLAY_NAME}</span></div>'
navbar_html = _render_navbar(view_id, level, tokens) navbar_html = _render_navbar(view_id, level, tokens, pending_alert=has_pending_alert)
footer_html = f'<footer class="footer">{WEB_APP_DISPLAY_NAME}</footer>' footer_html = f'<footer class="footer">{WEB_APP_DISPLAY_NAME}</footer>'
page_hash = config_hash() page_hash = config_hash()
@ -1692,7 +1693,7 @@ def render_layout(view_id, content_html, tokens):
fix_suffix = (f'Fix will be applied {timing}.' if timing fix_suffix = (f'Fix will be applied {timing}.' if timing
else 'Fix pending. The processing service is not running.') else 'Fix pending. The processing service is not running.')
else: else:
fix_suffix = 'Fix pending. Visit Actions page ASAP to apply fix.' fix_suffix = 'Fix pending. Visit <strong>Actions</strong> page ASAP to apply fix.'
for sev, items in grouped.items(): for sev, items in grouped.items():
if not items: if not items:
continue continue
@ -1701,7 +1702,7 @@ def render_layout(view_id, content_html, tokens):
+ ''.join(f'<li>{d}</li>' for d in items) + ''.join(f'<li>{d}</li>' for d in items)
+ '</ul>') + '</ul>')
uuid_attr = f' data-health-uuid="{e(fix_uuid)}"' if _apply_changes_immediately() else '' uuid_attr = f' data-health-uuid="{e(fix_uuid)}"' if _apply_changes_immediately() else ''
fix_html = (f'<div style="margin-top:0.5em"{uuid_attr}>{e(fix_suffix)}</div>' fix_html = (f'<div style="margin-top:0.5em"{uuid_attr}>{fix_suffix}</div>'
if fix_suffix else '') if fix_suffix else '')
content = ('<div style="width:100%">' content = ('<div style="width:100%">'
'<div style="font-weight:600;margin-bottom:0.25em">Health check - problems found:</div>' '<div style="font-weight:600;margin-bottom:0.25em">Health check - problems found:</div>'
@ -1711,6 +1712,12 @@ def render_layout(view_id, content_html, tokens):
except Exception: except Exception:
pass pass
pending_bar = ''
if has_pending_alert:
pending_bar = ('<div class="info-bar info-bar-warning">'
'You have actions pending. Please visit <strong>Actions</strong> page.'
'</div>\n')
return (f'<!DOCTYPE html>\n<html lang="en">\n<head>\n' return (f'<!DOCTYPE html>\n<html lang="en">\n<head>\n'
f' <meta charset="UTF-8"/>\n' f' <meta charset="UTF-8"/>\n'
f' <meta name="viewport" content="width=device-width, initial-scale=1.0"/>\n' f' <meta name="viewport" content="width=device-width, initial-scale=1.0"/>\n'
@ -1719,14 +1726,14 @@ def render_layout(view_id, content_html, tokens):
f'</head>\n<body>\n' f'</head>\n<body>\n'
f'{titlebar_html}\n' f'{titlebar_html}\n'
f'{navbar_html}\n' f'{navbar_html}\n'
f'<main class="main-content">\n{problem_bars}{other_bars}{content_html}\n</main>\n' f'<main class="main-content">\n{pending_bar}{problem_bars}{other_bars}{content_html}\n</main>\n'
f'{footer_html}\n' f'{footer_html}\n'
f'<script>var CONFIG_HASH="{page_hash}";var LAN_IFACE="{lan_iface}";var VPN_VLAN_COUNT={vpn_count};var EXISTING_VLAN_IDS={existing_ids};var EXISTING_VLAN_NAMES={existing_names};var EXISTING_VLAN_INTERFACES={existing_interfaces};var APPLY_UUID={json.dumps(my_uuid)};</script>\n' f'<script>var CONFIG_HASH="{page_hash}";var LAN_IFACE="{lan_iface}";var VPN_VLAN_COUNT={vpn_count};var EXISTING_VLAN_IDS={existing_ids};var EXISTING_VLAN_NAMES={existing_names};var EXISTING_VLAN_INTERFACES={existing_interfaces};var APPLY_UUID={json.dumps(my_uuid)};</script>\n'
f'<script>{_inline_js()}</script>\n' f'<script>{_inline_js()}</script>\n'
f'</body>\n</html>') f'</body>\n</html>')
def _render_navbar(active_view, level, tokens): def _render_navbar(active_view, level, tokens, pending_alert=False):
navbar_data = _load_json(f'{DATA_DIR}/navbar_content.json') navbar_data = _load_json(f'{DATA_DIR}/navbar_content.json')
left, right = [], [] left, right = [], []
for item in navbar_data.get('items', []): for item in navbar_data.get('items', []):
@ -1734,7 +1741,7 @@ def _render_navbar(active_view, level, tokens):
align = item.get('align', 'left') align = item.get('align', 'left')
if not _passes(req, level): if not _passes(req, level):
continue continue
frag = _render_nav_item(item, active_view, level, in_dropdown=False, inherited_req=req) frag = _render_nav_item(item, active_view, level, in_dropdown=False, inherited_req=req, pending_alert=pending_alert)
(right if align == 'right' else left).append(frag) (right if align == 'right' else left).append(frag)
return (f'<nav class="nav-bar">' return (f'<nav class="nav-bar">'
@ -1743,7 +1750,7 @@ def _render_navbar(active_view, level, tokens):
f'</nav>') f'</nav>')
def _render_nav_item(item, active_view, level, in_dropdown=False, inherited_req=None): def _render_nav_item(item, active_view, level, in_dropdown=False, inherited_req=None, pending_alert=False):
req = item.get('client_requirement', inherited_req) req = item.get('client_requirement', inherited_req)
t = item.get('type', '') t = item.get('type', '')
@ -1752,7 +1759,8 @@ def _render_nav_item(item, active_view, level, in_dropdown=False, inherited_req=
map_to = item.get('map_to', '') map_to = item.get('map_to', '')
action = item.get('action', '') action = item.get('action', '')
is_active = ' active' if map_to and map_to == active_view else '' is_active = ' active' if map_to and map_to == active_view else ''
cls = f'dropdown-item{is_active}' if in_dropdown else f'nav-item{is_active}' pending = ' nav-item-pending' if pending_alert and map_to == 'view_actions' else ''
cls = f'dropdown-item{is_active}' if in_dropdown else f'nav-item{is_active}{pending}'
if action: if action:
return (f'<form method="post" action="/action/{e(action)}" class="form-inline">' return (f'<form method="post" action="/action/{e(action)}" class="form-inline">'
f'<button type="submit" class="{cls}">{label}</button></form>') f'<button type="submit" class="{cls}">{label}</button></form>')
@ -1770,7 +1778,7 @@ def _render_nav_item(item, active_view, level, in_dropdown=False, inherited_req=
child_req = child.get('client_requirement', req) child_req = child.get('client_requirement', req)
if not _passes(child_req, level): if not _passes(child_req, level):
continue continue
children += _render_nav_item(child, active_view, level, in_dropdown=True, inherited_req=req) children += _render_nav_item(child, active_view, level, in_dropdown=True, inherited_req=req, pending_alert=pending_alert)
if not children: if not children:
return '' return ''
return (f'<div class="nav-menu">' return (f'<div class="nav-menu">'