{% comment %} Recent messages plus whatever automation is running for this contact. A window on the thread, not the thread: rendering conversations is the inbox's job (issue #14) and each row links there rather than reproducing it. There is no compose box — replying is `reply_in_inbox`, which is a different permission on a different page. The pane refreshes on `contactAutomationChanged`, which the start-flow and stop-automation toasts carry, so the execution line redraws without rebuilding the field editor and the channel list beside it. Message text and platform usernames are attacker-controlled (SECURITY-BASELINE §2): escaped, never marked safe, and never used as a URL. {% endcomment %}

Activity

{# ── Automation ──────────────────────────────────────────────── #}
{% if execution %}
{{ execution.get_status_display }} {{ execution.flow.name }} v{{ execution.flow_version.version }} · since {{ execution.updated_at }}
{% if can_manage_crm %}
{% endif %} {% else %} No automation running. {% endif %}
{% if can_manage_crm %}
{% comment %} Only published flows are offered: start_flow raises for a draft, so a picker listing them would be a picker full of error messages. Draft runs are issue #12's "test on Telegram", a different affordance elsewhere. {% endcomment %} Starting a flow ends whatever this contact is already running.
{% comment %} Sequence enrolment (#22). It posts to `contacts:bulk_sequence` with a single `ids` value rather than to a route of its own: the bulk endpoint already does exactly this, and a second one would be a second permission check to keep in step. That endpoint gates on manage_crm, which is why the control is inside this block. {% endcomment %} {% if sequences %}
{% csrf_token %} Enrolling restarts the sequence at step 1.
{% else %}

No sequences yet. create one to enrol this contact.

{% endif %} {% endif %} {# ── Messages ────────────────────────────────────────────────── #}

Recent messages

{% for row in recent_messages %}
{% if row.inbound %}Received{% else %}Sent{% endif %} {{ row.created_at }} {% if row.message.internal %}Internal note{% endif %} {% comment %} `inbox:list`, not `inbox` — apps/inbox/urls.py sets app_name, so the bare name does not resolve and this partial raised NoReverseMatch for any contact with at least one message. {% endcomment %} Open in inbox
{% if row.text %}{{ row.text }}{% else %}No text{% endif %} {% if row.attachment_count %} · {{ row.attachment_count }} attachment{{ row.attachment_count|pluralize }} {% endif %}
{% empty %}

Nothing yet. Messages appear here as soon as this contact writes in.

{% endfor %}