{% extends "base.html" %} {% block title %}Review queue — BackupWatch{% endblock %} {% block main_class %}review2{% endblock %} {% block content %}
UNMATCHED EMAIL · TRIAGE

Review queue

{% if n_total %}
{% endif %} {% if n_suggested %}
{% endif %}
{{ n_total }} email{{ '' if n_total == 1 else 's' }} waiting · {{ n_suggested }} ready to confirm in {{ n_batches }} batch{{ '' if n_batches == 1 else 'es' }} · {{ undecided|length }} need{{ 's' if undecided|length == 1 else '' }} a decision {% if n_total %}OLDEST {{ oldest_days }} DAY{{ '' if oldest_days == 1 else 'S' }} AGO{% endif %}
{% if not n_total %}
Nothing to review. 🎉  New unmatched email lands here as it arrives.
{% endif %} {% if undecided %}
NEEDS A DECISION {{ undecided|length }} No rule and no confident guess — pick a job, create one, or ignore the sender for good
{% for row in undecided %}{% set e = row.ev %}
{{ e.status|upper }} {{ e.email_subject }} {{ e.email_from }} {{ row.ago }}
{% if row.tie %}
Tied between {%- for t in row.tie %} {%- if not loop.first %}{{ ' and' if loop.last else ',' }}{% endif %}
{%- endfor %} — the engine won’t guess. Tighten one job’s rules to break the tie.
{% endif %}
{% endfor %}
Nothing here matches that filter.
{% endif %} {% if batches %}
READY TO CONFIRM {{ n_suggested }} Grouped by the job BackupWatch would assign — confirm a whole batch at once
{% for b in batches %}
{{ b.job.client.display_name }} / {{ b.job.name }} {{ b.rows|length }} via {{ b.via }}
{% for row in b.rows %}{% endfor %}
{% for r in b.rows %}{% endfor %}
{% for row in b.rows %}{% set e = row.ev %}
{{ e.status|upper }} {{ e.email_subject }} {{ e.email_from }} {{ row.ago }}
{% endfor %}
{% endfor %}
No suggested email matches that filter.
{% endif %} {# ---- ignore rules footer card ---- #}

Ignore rules

Senders and subjects that never reach this queue See ignored email ({{ ignored_count }})
{% for r in ignore_rules %}
{{ r.field }} {{ r.operator }} “{{ r.value }}”{% if r.value2 %} AND {{ r.field2 }} {{ r.operator2 }} “{{ r.value2 }}”{% endif %}{% if r.case_sensitive %} (case){% endif %} {{ r.hit_count or 0 }} match{{ '' if (r.hit_count or 0) == 1 else 'es' }}{% if r.last_hit_at %} · last {{ r.last_hit_at|date }}{% endif %} · added {{ r.created_at|date }} by {{ r.created_by }}
{% else %}
No ignore rules yet. Use Always ignore… on any email above to stop a recurring non-backup sender or subject from ever arriving here.
{% endfor %}
{# ---- bulk action bar ---- #}
0 SELECTED
{# ---- new client & job drawer ---- #}
New client & job
CREATING FROM
Client name Email identifiers one per line — prefilled from the sender
Job name Vendor
{% for v in ['veeam', 'dropsuite', 'synology', 'other'] %} {% endfor %}
Expected schedule
Matching rules all must match — prefilled from this email
These rules decide which future emails land on this job. Clear them all and a learned rule is written from this subject instead.
{# ---- always ignore dialog ---- #}

Always ignore

AND — optional second condition

Both must match — e.g. sender contains vendor.com AND subject contains Newsletter, so real backup mail from the same sender still gets through.

Everything currently waiting that matches is ignored immediately; future matching emails never reach the queue. Keep the value specific — a too-broad rule can swallow real backup mail.

{% endblock %}