{% extends "base.html" %} {% load humanize %} {% block title %}{{ page_title }}{% endblock %} {% block content %} {% comment %} /erp/imports/crm/// — the review queue (phase5-importer.md 4.6-4.9): confidence ascending, chips with counts, side-by-side rows, keyboard, bulk actions, claims, the progress header and every empty state of 4.7. Without JS every action is a plain form POST. {% endcomment %}

Review {{ plural|lower }}

{{ job.source }} · {{ job.get_status_display }} · coverage {{ coverage.coverage }}{% if coverage.reason_text %} ({{ coverage.reason_text }}){% endif %}

Job overview

{% if not header.total %} {% if coverage.exact %}

The source has no {{ plural|lower }}.

{% else %}
{{ plural }}: coverage {{ coverage.coverage }} — the export could not read {% if coverage.coverage == "unknown" %}them{% else %}all of them{% endif %}{% if coverage.reason_text %} ({{ coverage.reason_text }}){% endif %}. 0 rows here does not mean the source has none.
{% endif %} {% else %}

Progress

{% include "erp/crm_import/_queue_header.html" %}
{% if not header.remaining and not filters.narrowed %}

All {{ header.total|intcomma }} {{ plural|lower }} are decided. {{ header.approved|intcomma }} approved, {{ header.skipped|intcomma }} skipped{% if header.failed %}, {{ header.failed|intcomma }} failed{% endif %}.

{% if can_run %}
{% if header.approved_pending %}
{% csrf_token %}
{% endif %} {% if header.failed %}
{% csrf_token %}
{% endif %}
{% endif %}
{% endif %}

Rows {{ view_count|intcomma }} in this view · lowest confidence first{% if filters.order == "desc" %} (flipped: highest first){% endif %}

{% if filters.decision %}{% endif %} {% if filters.band %}{% endif %} {% if filters.flag %}{% endif %} {% if filters.mine %}{% endif %}
{% if filters.narrowed %}{% endif %}
{% for group in chips %}
{{ group.title }} {% for chip in group.items %} {{ chip.label }}: {{ chip.count|intcomma }} left {% endfor %}
{% endfor %}
{% if frozen %}

This job is {{ job.get_status_display|lower }}: its rows cannot be decided now.

{% elif not can_apply %}

You can look at this queue. Deciding, claiming and bulk actions need “Can apply and undo CRM imports”.

{% endif %} {% if not ai_on %}

AI suggestions are off for this job, so there is no AI column. They are optional (suggestions only, never a decision) and are turned on per job with the ai_match option (manage.py crm_import stage --ai-hints, or manage.py crm_import ai --job {{ job.pk }} --enable); they would go through the llmhub purpose crm_match_propose{% if ai_route %} (route {{ ai_route }}){% else %}, which this deployment has not configured yet{% endif %}. The model sees comparisons (same family name, same e-mail domain, …), never names or contact details.

{% elif ai_blocked %}

AI suggestions are on for this job but cannot run: {{ ai_reason|default:"the llmhub purpose crm_match_propose has no route" }}.

{% endif %} {% if can_apply %}
{% csrf_token %}
All unflagged rows at ≥ {{ threshold|floatformat:2 }} in this view: never a flagged row, a domain-only link, a conflict or a row someone else claimed.
{% if ai_ready %}
{{ ai_pending|intcomma }}{% if ai_pending_more %}+{% endif %} undecided row{{ ai_pending|pluralize }} in this view without a suggestion; one call each, at most {{ ai_web_limit }} per click (more: manage.py crm_import ai --job {{ job.pk }}). Budget: {{ ai_budget_left|intcomma }} of {{ ai_budget|intcomma }} calls left. Route {{ ai_route }}. Suggestions never decide a row.
{% endif %}
{% if header.mine %}{% endif %} A claim keeps others off your rows for {{ claim_ttl_minutes }} minutes.
{% endif %} {% if can_run and failed_codes %}
{% csrf_token %}
{% endif %}
Keyboard shortcuts
  • j / ↓ next row, k / ↑ previous row, Enter open or close the row
  • a approve, r reject (create new instead of the link; skip otherwise), c create new, s or n skip (N/A)
  • 1–5 pick that candidate, x select the row for a bulk action
  • After a decision the next undecided row opens.
{% if items %}
    {% for item in items %}{% include "erp/crm_import/_queue_row.html" %}{% endfor %}
{% elif filters.narrowed %}

No rows match these filters. Clear filters

{% endif %}
{% endif %} {% endblock %} {% block javascripts %} {% endblock %}