Files
Sales-and-Marketing/address_book/conference_mail_merge/templates/index.html
T
beantownbabe b745d6f15d Unify contact sheet, add reconnect message template, and scale the contacts UI
conference_contacts.xlsx is now the single working file behind both the dash's
Download button and the mail-merge tool -- populated with all 139 SPIE 2026
presenters (not just ones with emails already found), so Peter can fill in
emails as he meets people at the conference. Also adds a second message
template (reconnect_message) alongside the existing wavegate_focus one, and
a search/scroll UI for the contacts list now that it's no longer a handful
of rows.
2026-08-16 09:48:12 +02:00

90 lines
4.2 KiB
HTML

{% extends "base.html" %}
{% block content %}
<h1>TXproducts Mail Merge</h1>
<p class="sub">Conference follow-ups &mdash; check who gets a message, pick a template, generate or send.</p>
{% if error %}<div class="warning">{{ error }}</div>{% endif %}
<form method="post">
<div class="card">
<h2>Contacts <span class="sub" style="font-weight:400;">({{ contacts|length }} total)</span></h2>
<div style="margin-bottom:10px;">
<button type="button" onclick="document.querySelectorAll('.contact-check').forEach(c=>c.checked=true)">Select All</button>
<button type="button" class="secondary" onclick="document.querySelectorAll('.contact-check').forEach(c=>c.checked=false)">Select None</button>
<a href="/" style="margin-left:8px; font-size:0.85rem;">Refresh from Excel</a>
</div>
<input type="text" id="contact-search" placeholder="Search by name..." style="margin-bottom:10px; max-width:300px;">
<div class="contact-list-scroll">
{% set ns = namespace(found=false) %}
{% for c in contacts %}
{% set is_preselected = preselect_email and c.email|lower == preselect_email %}
{% if is_preselected %}{% set ns.found = true %}{% endif %}
<div class="contact-row {% if c.sent %}disabled{% endif %}" data-name="{{ (c.first ~ ' ' ~ c.last)|lower }}" {% if is_preselected %}id="preselected-contact" style="background:#eef4fd;"{% endif %}>
<input type="checkbox" class="contact-check" name="contacts" value="{{ c.email }}" {% if c.sent %}disabled{% endif %} {% if is_preselected %}checked{% endif %}>
<span>{{ c.first }} {{ c.last }} &lt;{{ c.email }}&gt;</span>
{% if c.conference %}<span class="meta">&mdash; {{ c.conference }}</span>{% endif %}
{% if c.draft_opened %}<span class="meta">(draft opened {{ c.draft_opened }})</span>{% endif %}
{% if c.sent %}<span class="meta">(already marked as sent)</span>{% endif %}
</div>
{% else %}
<p class="sub">No contacts in the sheet yet.</p>
{% endfor %}
</div>
{% if preselect_email and not ns.found %}
<p class="warning" style="margin-top:10px;">No contact with email {{ preselect_email }} found in the sheet yet &mdash; add them first.</p>
{% endif %}
</div>
<div class="card">
<h2>Message template</h2>
<label>Standard message:
<select name="template">
{% for t in templates %}
<option value="{{ t }}">{{ t }}</option>
{% endfor %}
</select>
</label>
<h2 style="margin-top:18px;">Attachments</h2>
{% for a in attachments %}
<label style="display:inline-flex; align-items:center; gap:6px; margin-right:16px;">
<input type="checkbox" name="attachments" value="{{ a.path }}" {% if a.exists %}checked{% else %}disabled{% endif %}>
{{ a.name }}{% if not a.exists %} (not found){% endif %}
</label>
{% endfor %}
<h2 style="margin-top:18px;">From address</h2>
<input type="text" name="from_address" value="{{ default_from }}">
</div>
<button type="submit" formaction="/open_in_mail">Open in Mail.app</button>
<button type="submit" formaction="/generate">Generate Drafts</button>
<button type="submit" formaction="/preview">Preview &amp; Send Batch</button>
<button type="button" class="secondary" onclick="fetch('/open_folder', {method:'POST'})">Open Drafts Folder</button>
</form>
<script>
// Prevent double-clicks/double-submits from firing the same action twice (e.g. writing
// two draft files or opening two Mail.app windows for one click).
document.querySelector("form").addEventListener("submit", function (e) {
if (e.target.dataset.submitted) {
e.preventDefault();
return;
}
e.target.dataset.submitted = "true";
document.querySelectorAll("form button[type=submit]").forEach(function (btn) {
btn.disabled = true;
});
});
var preselected = document.getElementById("preselected-contact");
if (preselected) { preselected.scrollIntoView({ block: "center" }); }
document.getElementById("contact-search").addEventListener("input", function (e) {
var q = e.target.value.trim().toLowerCase();
document.querySelectorAll(".contact-row[data-name]").forEach(function (row) {
row.classList.toggle("no-match", q !== "" && row.dataset.name.indexOf(q) === -1);
});
});
</script>
{% endblock %}