{# Vendored from packages/web/src/governor_web/templates/opportunities/_table.html. Audit changes: - Column-header click → plain with ?sort=...&direction=... so the table sorts via standard GET round-trip (no SSE). - Local-runtime branches kept (audit IS local) - so the Occurrences column doesn't render and the workload chip falls back to "Unclassified" when the kind is unknown. - Row click target: /opportunities/{id} (audit has no /projects/... namespace; everything is single-tenant). - State column: derived from severity bucket (audit has no solution_risk_level / recommendation_state). Same chip grammar (ui-signal-chip + ui-legend-dot). #}
{% from "components/_sort_header.html" import sort_header %} {% if opportunities and opportunities | length > 0 %} {% set _qs = pagination.query_string_without_sort_or_page %}
{# Audit always renders local_runtime_mode. The 'Savings' and 'Occurrences' columns are cloud-only and never show here. #} {{ sort_header("dataset", "Dataset", signals.sort, signals.direction, base_qs=_qs) }} {{ sort_header("table", "Object", signals.sort, signals.direction, base_qs=_qs) }} {{ sort_header("author", "Author", signals.sort, signals.direction, base_qs=_qs) }} {{ sort_header("type", "Issue", signals.sort, signals.direction, base_qs=_qs) }} {{ sort_header("workload", "Workload", signals.sort, signals.direction, base_qs=_qs) }} {{ sort_header("cost", "Query Cost", signals.sort, signals.direction, align="right", base_qs=_qs, tooltip="Cost from the most recent BigQuery job execution, dry-run estimate, or detection heuristic.") }} {{ sort_header("age", "Last Detected", signals.sort, signals.direction, align="right", base_qs=_qs) }} {# Partition / Cluster column removed - partition + cluster recommendations are a data-lineage-level signal, not a per-job one, and don't belong on a job-level listing. #} {{ sort_header("materialization", "Materialization", signals.sort, signals.direction, align="center", base_qs=_qs) }} {{ sort_header("suggestions", "Suggestions", signals.sort, signals.direction, align="center", base_qs=_qs) }} {% for opp in opportunities %} {# Each row represents ONE destination_table, aggregated across every Opportunity that fired on it. Click goes to the representative opportunity's detail page (/opportunities/{id}) - same recommendation layout users are familiar with. The detail page itself links onward to the cohort view (/jobs/{table}) for the full per-rule history if they want to dive deeper. #} {% endfor %}
{{ opp.affected_dataset }} {{ opp.affected_object }} {% if opp.author_email %} {{ opp.author_email }} {% else %} - {% endif %} {# Issue cell mirrors the Failures table's Reason chip styling (ui-signal-chip + dot) but in amber instead of rose, so detection findings read with the same visual grammar as BigQuery error reasons while the colour still signals "performance issue, not failure". Shows first issue + ``+N`` overflow indicator when the table fires more than one rule; the full list lives in the wrapper's ``title`` tooltip. #} {% set _issue_count = opp.table_issue_count or 0 %} {% set _issue_types = opp.table_issue_types or [] %} {% from "components/_signal_chip.html" import signal_chip %} {% if _issue_count == 0 %} - {% else %}
{{ signal_chip( _issue_types[0] | replace('_', ' '), color="amber", overflow=("+" ~ (_issue_count - 1)) if _issue_count > 1 else None ) }}
{% endif %}
{% if opp.query_workload_kind == 'build' %} {{ signal_chip("Build", color="brand", title="Latest job classified as build") }} {% elif opp.query_workload_kind == 'consumption' %} {{ signal_chip("Consumption", color="teal", title="Latest job classified as consumption") }} {% else %} {{ signal_chip("Unclassified", color="slate", title="No exact local run job match was found for this opportunity, so Governor does not infer a workload type") }} {% endif %} {% if opp.query_cost_value is not none %} {{ opp.query_cost_value | format_usd }} {% else %} - {% endif %} {{ opp.last_detected_at.strftime("%d/%m/%Y") if opp.last_detected_at else (opp.created_at.strftime("%d/%m/%Y") if opp.created_at else "-") }} {# Merged Materialization column (post-spec-155). Shows the strategy chip when this row IS a materialization candidate (ephemeral / incremental / table / materialized view) OR the "unused" chip when this row IS an unused destination table. Different chip colours so the two semantics stay visually distinct sharing one column: brand (blue) = strategy recommendation amber = unused (review) Empty cell when neither signal applies. #} {% set _label = opp.materialization_label %} {% if _label == "unused" %} {{ signal_chip(_label, color="amber", title="Built in the scan window but no other query reads from it. Open the row to review.") }} {% elif _label %} {{ signal_chip(_label, color="brand", title="Open the row to see the materialization recommendation.") }} {% endif %} {# Suggestions count - aggregated per table. When zero, render an empty cell rather than a "None" chip - keeps the table chrome clean and visually matches the empty-state convention used in the Issue column. #} {% set _sugg = opp.table_suggestion_count or 0 %} {% if _sugg > 0 %} {{ signal_chip(_sugg ~ " suggested", color="emerald", title=_sugg ~ " suggestion rule(s) fired across this table - open the row to see each diff") }} {% endif %}
{# The opportunities listing groups by ``affected_table`` (one row per table), so the footer noun should reflect that to avoid colliding mentally with the "Flagged Jobs" headline above (which dedupes by destination_table / query_hash). #} {% with pagination_item_noun = "affected tables" %} {% include "components/_pagination.html" %} {% endwith %} {% else %}

No opportunities match your filters

{% endif %}