{% extends "base.html" %} {% block title %}Onboarding preview — BackupWatch{% endblock %} {% block content %} ← Onboarding workspace
Batch {{ batch.id[:8] }} · saved {{ batch.created_at|dt }} · {{ batch.created_by }}
{% if msg %}{% endif %} {% if batch.kind == 'mapping' %}Map your columns once. A blank cell preserves an existing job's value. New jobs use the displayed defaults and remain drafts.
{% elif batch.kind == 'activation' %}{{ data.action|capitalize }} preview. Tested {{ data.samples }} reports from the last {{ data.history_days }} days using client resolution and competing jobs. This sample is evidence, not proof of complete inventory coverage.
{% if data.truncated %}{% endif %}Timezone: {{ data.timezone }}. Notification destination: {{ data.recipients or 'Not configured' }}.
{% else %} {% if data.coverage %}{{ data.coverage.expected }} expected · {{ data.coverage.configured }} configured · {{ data.coverage.reporting }} have reported · {{ data.coverage.missing }} missing · {{ data.coverage.never_seen }} never seen.
These jobs are outside this batch's expected inventory. Review them before deciding whether to retire anything.
{% for j in data.unexpected %}{{ j.client }} / {{ j.name }}
{% endfor %}{{ data.message_count }} reports grouped from {{ data.source }}. {% if data.capped %}500-report limit reached; narrow the range to discover additional jobs.{% endif %} Correct unresolved client codes below. Give existing clients a code on their client page before assigning discovered jobs.
{% endif %} {% if data.ai_sweep %}{{ data.message_count }} waiting Review-queue report(s) across {{ data.client_count }} client(s) grouped.{% if data.capped %} Sample limit reached — a follow-up sweep may find more.{% endif %}
{% if data.ai_candidates %}One request per client, subject lines and sender addresses only, using your Anthropic API key and the shared hourly AI request budget. If the sweep hits the hourly limit partway through, the remaining clients are reported below the next run — nothing is lost, just not yet refined.
{{ data.ai_summary.refined|length }} client(s) refined (~${{ '%.2f'|format(data.ai_summary.cost) }}).
{% if data.ai_summary.skipped_cap %}Hourly AI limit reached. Not sent: {{ data.ai_summary.skipped_cap|join(', ') }}. Re-run the sweep later.
{% endif %} {% if data.ai_summary.skipped_error %}Couldn't refine: {{ data.ai_summary.skipped_error|join(', ') }}.
{% endif %}Saving creates drafts. Existing updates require explicit confirmation; blank values preserve settings. Imports never delete jobs missing from the file.
Download rows needing correction
| Select | Row / job | Action | Changes | Result | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| {% if not row.error and not row.result %}{% endif %} | {{ row.number }} · {{ row.input.get('client_name') or row.input.get('client_code') or 'Client needed' }} {{ row.input.get('job_name','') }}{% if row.ai %} AI{% endif %}{% if row.samples %} {{ row.report_count }} report(s)
…and {{ row.extra }} more not shown. {% endif %}{{ row.report_count }} report(s){{ row.sample }} Create job in wizard {% endif %} |
{{ row.action|default('Needs correction') }} | {% if row.get('values') %}Review values{% for key,value in row['values'].items() %}{% if not row.before or row.before.get(key) != value %}{{ key|replace('_',' ') }}: {{ row.before.get(key,'') if row.before else 'New' }} → {{ value }} {% endif %}{% endfor %}{% if row.rules %}Replacement matching rules: {{ row.rules }} {% endif %}{% if row.deadline %}Next deadline ({{ timezone }}): {{ row.deadline }} {% endif %} |
{% if row.error %}{{ row.error }}{% elif row.result %}{{ row.result }}{% else %}Awaiting selection{% endif %}
{% if batch.kind == 'import' and not row.result %}Correct this row |
||||||
| No candidate jobs in this sample. Try another date range or upload an inventory. | ||||||||||