"""HTML rendering for Phase 3 Notifications and Human-Attention Console (#648).""" from __future__ import annotations from html import escape from typing import Sequence from webui.layout import render_page from webui.notifications import ( ATTENTION_HUMAN_REQUIRED, ATTENTION_OPERATOR, ATTENTION_ROUTINE, NotificationItem, NotificationSnapshot, ) def _render_attention_badge(attention_class: str) -> str: cls = "badge" if attention_class == ATTENTION_HUMAN_REQUIRED: cls += " badge-blocked" elif attention_class == ATTENTION_OPERATOR: cls += " badge-claimed" else: cls += " muted" return f'{escape(attention_class)}' def _render_notification_row(item: NotificationItem) -> str: category_label = escape(item.category.upper()) id_str = escape(item.id) title_str = escape(item.title) summary_str = escape(item.summary) att_badge = _render_attention_badge(item.attention_class) work_item_html = "—" if item.work_number and item.work_kind: kind_label = escape(item.work_kind.upper()) num_str = f"#{item.work_number}" link = item.deep_link or "#" work_item_html = f'{kind_label} {num_str}' requires_human_label = ( 'HUMAN REQUIRED' if item.requires_human else "" ) return f""" {category_label}
{id_str}
{title_str} {att_badge} {requires_human_label}
{summary_str}
{work_item_html} {escape(item.created_at[:19])} """ def _render_notifications_table(items: Sequence[NotificationItem], empty_message: str) -> str: if not items: return f'

{escape(empty_message)}

' rows = "".join(_render_notification_row(item) for item in items) return f""" {rows}
Category & ID Title & Attention Summary Work Item Time
""" def render_notifications_page( snapshot: NotificationSnapshot, *, filter_class: str = "inbox", filter_project: str | None = None, ) -> str: """Render the notifications and attention inbox page.""" title = "Notifications & Attention Inbox" err_html = "" if snapshot.fetch_error: err_html = f'

Fetch Warning: {escape(snapshot.fetch_error)}

' # Determine items to render based on filter_class if filter_class == ATTENTION_HUMAN_REQUIRED: display_items = snapshot.human_required_items active_tab_title = "Human-Required Escalations" elif filter_class == ATTENTION_OPERATOR: display_items = snapshot.operator_items active_tab_title = "Operator Inbox Items" elif filter_class == ATTENTION_ROUTINE: display_items = snapshot.routine_items active_tab_title = "Routine Workflow Transitions" elif filter_class == "all": display_items = snapshot.items active_tab_title = "All Events (including Routine)" else: # "inbox" default display_items = snapshot.inbox_items active_tab_title = "Attention Inbox (Human + Operator)" hr_cls = "badge-blocked" if snapshot.human_required_count > 0 else "muted" op_cls = "badge-claimed" if snapshot.operator_count > 0 else "muted" metrics_html = f"""
Human Required

{snapshot.human_required_count}

Critical escalation boundary

Operator Inbox

{snapshot.operator_count}

Operational items needing review

Routine Transitions

{snapshot.routine_count}

Background transitions (filtered)

""" # Filter navigation links def _tab_link(target_class: str, label: str) -> str: is_active = (filter_class == target_class) style = "font-weight:bold; border-bottom:2px solid currentColor;" if is_active else "color:#4a5568;" return f'{label}' tabs_html = f"""
{_tab_link("inbox", f"Attention Inbox ({snapshot.human_required_count + snapshot.operator_count})")} {_tab_link("human-required", f"Human Required ({snapshot.human_required_count})")} {_tab_link("operator", f"Operator ({snapshot.operator_count})")} {_tab_link("routine", f"Routine ({snapshot.routine_count})")} {_tab_link("all", f"All Events ({snapshot.total_count})")}
""" table_html = _render_notifications_table( display_items, f"No items match attention filter '{filter_class}'.", ) body = f"""

{escape(title)}

Phase 3 console surface for human-attention routing (#648). Routine workflow transitions are filtered by default to eliminate notification fatigue.

{err_html} {metrics_html} {tabs_html}

{escape(active_tab_title)}

{table_html}""" return render_page(title=title, body_html=body)