{% load common_extras humanize %} {% comment %} The broadcast table. One renderer for the toolbar's fetch and for every mutation's refresh event. The status chip is `status-pill status-pill-{{ broadcast.status }}`: the five classes exist in styles.css, named for the stored values, so this template never branches on which one a row is in. The counters come off the row's stored `stats` (SPEC §5), not from a per-row aggregate — a list of fifty broadcasts must not be fifty GROUP BYs. The detail page is where the live figures are computed, and it writes them back here. {% endcomment %} {% if broadcasts %} {% comment %} The delivery bar is the stored counters drawn as one stacked track (HANDOFF §3, Broadcasts): green for sent, blue for still going out, red for failed, each as a share of everyone queued. Skipped recipients — the compliance filter's exclusions — are not in the bar; the detail page itemises them. {% endcomment %}
{% for broadcast in broadcasts %} {% with stats=broadcast.stats %} {% endwith %} {% endfor %}
Broadcast Status Delivery Audience When
{{ broadcast.name }}

{% include "partials/_platform_icon.html" with platform=broadcast.channel_connection.platform size="sm" %} {{ broadcast.channel_connection.display_name }}

{{ broadcast.get_status_display }} {% if stats.queued %} {% widthratio stats.sent stats.queued 100 as sent_pct %} {% widthratio stats.pending stats.queued 100 as pending_pct %} {% widthratio stats.failed stats.queued 100 as failed_pct %}

{{ stats.sent|default:0|intcomma }} of {{ stats.queued|intcomma }}{% if stats.skipped %} · {{ stats.skipped|intcomma }} skipped{% endif %}

{% else %}

—

{% endif %}
{% if stats.queued %}{{ stats.queued|intcomma }}{% else %}—{% endif %} {% if broadcast.status == "sending" %}Now {% elif broadcast.finished_at %}{{ broadcast.finished_at|naturalday:"j M" }} {% elif broadcast.scheduled_at %}{{ broadcast.scheduled_at|naturalday:"j M"|capfirst }} {{ broadcast.scheduled_at|time:"H:i" }} {% else %}Not sent{% endif %}
{% elif q or filters.status or filters.channel %} {% include "components/empty_state.html" with icon="search" title="No broadcasts match" body="Try a different word, or clear the filters." action_label="Clear filters" action_click="clearFilters()" %} {% else %} {% include "components/empty_state.html" with icon="broadcasts" title="No broadcasts yet" body="One message to many contacts. Start one with New broadcast." %} {% endif %} {% if truncated %} {% comment %} A silent cap reads as "the older ones were deleted". Saying so costs one line and one extra row fetched, and the search and status filters narrow the whole set rather than this page, so they are the way to reach what is not here. {% endcomment %}

Showing the {{ page_size }} most recent. Search or filter by status to find an older one.

{% endif %}