diff --git a/.gitignore b/.gitignore index b5bbd8e..e34a511 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ nextcloud_export/ *.env address_book/conference_mail_merge/conference_contacts.xlsx address_book/conference_mail_merge/output_drafts/ +~$*.xlsx __pycache__/ *.pyc .Rhistory diff --git a/address_book/conference_mail_merge/SPIE_2026_Presenter_Contacts.xlsx b/address_book/conference_mail_merge/SPIE_2026_Presenter_Contacts.xlsx deleted file mode 100644 index 73959e8..0000000 Binary files a/address_book/conference_mail_merge/SPIE_2026_Presenter_Contacts.xlsx and /dev/null differ diff --git a/address_book/conference_mail_merge/message_templates/reconnect_message.txt b/address_book/conference_mail_merge/message_templates/reconnect_message.txt new file mode 100644 index 0000000..ba49df5 --- /dev/null +++ b/address_book/conference_mail_merge/message_templates/reconnect_message.txt @@ -0,0 +1,17 @@ +SUBJECT: Following up on TXServices & Wavegate + +Hi {{First Name}}, + +It was a pleasure meeting you at {{Conference}} on {{Date Met}}. + +I really enjoyed your session on {{Topic}}, I found it insightful and gave me several new perspectives. + +I'd love to stay in touch, as I believe there are opportunities where our expertise could complement each other and create value for both of us. + +I've attached a short overview of TXproducts, including our services and our flagship solution, WaveGate. + +If you're open to it, I'd be happy to schedule a 20–30 minute call in the coming weeks to explore potential collaboration. + +Best regards, + +Mckinley diff --git a/address_book/conference_mail_merge/message_templates/wavegate_focus.txt b/address_book/conference_mail_merge/message_templates/wavegate_focus.txt index f75ff8e..b6d3cf9 100644 --- a/address_book/conference_mail_merge/message_templates/wavegate_focus.txt +++ b/address_book/conference_mail_merge/message_templates/wavegate_focus.txt @@ -1,13 +1,14 @@ -SUBJECT: Following up on {{Topic}} +SUBJECT: Following up on TXServices & Wavegate Hi {{First Name}}, -It was great meeting you at {{Conference}}. Following up on our conversation -about {{Topic}} on {{Date Met}}. +It was great meeting you at {{Conference}} on {{Date Met}}. -Attached is more detail on how it speeds up beamline experiments. +Your session about {{Topic}} was very interesting, and I am following up +on the info material we talked about regarding TXproducts' services and +flagship product WaveGate. -Happy to set up a short call if useful. +Would love to dive deeper — let's find a time to talk. Best regards, diff --git a/address_book/conference_mail_merge/rollout_presentation.html b/address_book/conference_mail_merge/rollout_presentation.html index 04f6c48..9a689cb 100644 --- a/address_book/conference_mail_merge/rollout_presentation.html +++ b/address_book/conference_mail_merge/rollout_presentation.html @@ -365,38 +365,44 @@ a { color: var(--accent-strong); }

What the mail actually looks like

-

A real draft generated from conference_contacts.xlsx, using the wavegate_focus template — this is exactly what sits in output_drafts/ today, unedited.

+

A real draft generated from conference_contacts.xlsx, using the reconnect_message template — one of two templates now available in the tool's dropdown (the other, wavegate_focus, stays available too).

-
The template behind it — message_templates/wavegate_focus.txt
-
SUBJECT: Following up on {{Topic}}
+    
The template behind it — message_templates/reconnect_message.txt
+
SUBJECT: Following up on TXServices & Wavegate
 
 Hi {{First Name}},
 
-It was great meeting you at {{Conference}}. Following up on our conversation
-about {{Topic}} on {{Date Met}}.
+It was a pleasure meeting you at {{Conference}} on {{Date Met}}.
 
-Attached is more detail on how it speeds up beamline experiments.
+I really enjoyed your session on {{Topic}}, I found it insightful and gave me several new perspectives.
 
-Happy to set up a short call if useful.
+I'd love to stay in touch, as I believe there are opportunities where our expertise could complement each other and create value for both of us.
+
+I've attached a short overview of TXproducts, including our services and our flagship solution, WaveGate.
+
+If you're open to it, I'd be happy to schedule a 20–30 minute call in the coming weeks to explore potential collaboration.
 
 Best regards,
 
diff --git a/address_book/conference_mail_merge/templates/base.html b/address_book/conference_mail_merge/templates/base.html
index 75a9802..5bc0c1c 100644
--- a/address_book/conference_mail_merge/templates/base.html
+++ b/address_book/conference_mail_merge/templates/base.html
@@ -44,6 +44,8 @@
   .contact-row:last-child { border-bottom: none; }
   .contact-row .meta { color: #5b6a78; font-size: 0.85rem; }
   .contact-row.disabled { color: #b6bfc8; }
+  .contact-row.no-match { display: none; }
+  .contact-list-scroll { max-height: 420px; overflow-y: auto; padding-right: 4px; }
   select, input[type=text] {
     padding: 7px 9px;
     border: 1px solid #d3dbe4;
diff --git a/address_book/conference_mail_merge/templates/index.html b/address_book/conference_mail_merge/templates/index.html
index 9d373e2..f801d33 100644
--- a/address_book/conference_mail_merge/templates/index.html
+++ b/address_book/conference_mail_merge/templates/index.html
@@ -7,15 +7,20 @@
 
 
-

Contacts

+

Contacts ({{ contacts|length }} total)

Refresh from Excel
+ +
+ {% 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 %} +
+ {{ c.first }} {{ c.last }} <{{ c.email }}> {% if c.conference %}— {{ c.conference }}{% endif %} {% if c.draft_opened %}(draft opened {{ c.draft_opened }}){% endif %} @@ -24,6 +29,10 @@ {% else %}

No contacts in the sheet yet.

{% endfor %} +
+ {% if preselect_email and not ns.found %} +

No contact with email {{ preselect_email }} found in the sheet yet — add them first.

+ {% endif %}
@@ -66,5 +75,15 @@ 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); + }); + }); {% endblock %} diff --git a/address_book/conference_mail_merge/webapp.py b/address_book/conference_mail_merge/webapp.py index 5bef562..b29a5ef 100644 --- a/address_book/conference_mail_merge/webapp.py +++ b/address_book/conference_mail_merge/webapp.py @@ -95,6 +95,7 @@ def index(): attachments=get_attachment_options(), default_from=mm.DEFAULT_FROM, error=request.args.get("error"), + preselect_email=(request.args.get("email") or "").strip().lower(), ) @@ -195,59 +196,6 @@ def open_in_mail(): return render_template("results.html", lines=lines, warnings=warnings) -@app.route("/quick_open") -def quick_open(): - """One-click entry point from the Sales & Marketing dash's presenter cards -- opens a - real Mail.app draft for a single contact by email, using the default template and - attachments. Skips the checkbox/template-picker UI entirely.""" - email = request.args.get("email", "").strip() - template_name = request.args.get("template") or "wavegate_focus.txt" - - if not email: - return render_template("results.html", lines=[], warnings=["No email provided."]) - - try: - full_df = pd.read_excel(mm.CONTACTS_FILE, sheet_name="Contacts") - except Exception as exc: - return render_template("results.html", lines=[], warnings=[f"Could not read contact sheet: {exc}"]) - - if "Draft Opened" not in full_df.columns: - full_df["Draft Opened"] = "" - - match_mask = full_df["Email"].astype(str).str.strip().str.lower() == email.lower() - if not match_mask.any(): - return render_template("results.html", lines=[], - warnings=[f"No contact with email {email} found in conference_contacts.xlsx yet -- add them there first."]) - - try: - rows = rows_for_selection([email], template_name) - except ValueError as exc: - return render_template("results.html", lines=[], warnings=[str(exc)]) - - default_attachments = [str(p) for p in DEFAULT_ATTACHMENTS if p.exists()] - - lines = [] - warnings = [] - opened_any = False - for row_dict, subject, body in rows: - if "[[MISSING:" in subject or "[[MISSING:" in body: - warnings.append(f"Missing field(s) for {row_dict['Email']} -- check before sending.") - try: - mm.open_in_mail_app(subject, body, row_dict["Email"], default_attachments, mm.DEFAULT_FROM) - lines.append(f"Opened in Mail.app: {row_dict['First Name']} {row_dict['Last Name']} <{row_dict['Email']}>") - timestamp = datetime.now().strftime("%d.%m.%y %H:%M") - full_df.loc[match_mask, "Draft Opened"] = timestamp - opened_any = True - mm.log_to_dash_outreach(row_dict["Email"], f"Draft opened in Mail.app ({template_name}), {timestamp}") - except RuntimeError as exc: - warnings.append(f"Could not open Mail.app draft for {row_dict['Email']}: {exc}") - - if opened_any: - full_df.to_excel(mm.CONTACTS_FILE, index=False, sheet_name="Contacts") - - return render_template("results.html", lines=lines, warnings=warnings) - - @app.route("/preview", methods=["POST"]) def preview(): selected = request.form.getlist("contacts") diff --git a/index.html b/index.html index 771a2fe..95cfc03 100644 --- a/index.html +++ b/index.html @@ -807,7 +807,7 @@ - Download Contact Sheet ↓ + Download Contact Sheet ↓ Open Mail Merge Tool ↗
@@ -3828,6 +3828,14 @@ { "date": "15.08.26", "note": "Draft opened in Mail.app (wavegate_focus.txt), 15.08.26 21:24" + }, + { + "date": "16.08.26", + "note": "Draft opened in Mail.app (wavegate_focus.txt), 16.08.26 06:10" + }, + { + "date": "16.08.26", + "note": "Draft opened in Mail.app (reconnect_message.txt), 16.08.26 08:44" } ], "notes": "", @@ -4686,12 +4694,12 @@ }).join("") + "
"; } - // Hands off to the real Mail Merge tool (localhost:8505), which does the actual template - // merge, attachments, and Mail.app draft creation server-side — the tool must be running. + // Hands off to the real Mail Merge tool (localhost:8505) with this contact pre-checked, + // so the template/attachments/from-address stay a deliberate choice, not an auto-pick. function buildComposeEmailButton(p) { if (!p.email) { return ""; } - var href = "http://localhost:8505/quick_open?email=" + encodeURIComponent(p.email); - return "Compose Email"; + var href = "http://localhost:8505/?email=" + encodeURIComponent(p.email); + return "Compose Email"; } function renderConferences() {