Instrument webhooks, fix compose layout, and ship social connect.
Deploy Beta / unit-tests (push) Successful in 10s
Deploy Beta / docker (push) Successful in 15s
Deploy Beta / deploy-beta (push) Successful in 1m40s

Add Grafana-friendly webhook request logging, Quill overflow fix, New Contact flow, in-place postcard campaign compose, and functional social account connect + composer.
This commit is contained in:
2026-08-10 05:15:49 -05:00
parent b3a6ee0cd0
commit 58258f2875
10 changed files with 778 additions and 311 deletions
@@ -0,0 +1,100 @@
{% extends "portal_base.html" %}
{% load static %}
{% block title %}New contact · Portal{% endblock %}
{% block topbar_title %}New contact{% endblock %}
{% block extra_head %}
<link rel="stylesheet" href="{% static 'css/address-autocomplete.css' %}">
{% endblock %}
{% block portal_content %}
<form method="post">
{% csrf_token %}
<div class="split">
<div class="panel">
<div class="panel-h"><h2>Profile</h2></div>
<div class="panel-b form-grid">
<div class="form-grid cols-2">
<div class="field">
<label for="id_first_name">First name</label>
<input id="id_first_name" name="first_name" required value="{{ form.first_name }}">
</div>
<div class="field">
<label for="id_last_name">Last name</label>
<input id="id_last_name" name="last_name" value="{{ form.last_name }}">
</div>
</div>
<div class="form-grid cols-2">
<div class="field">
<label for="id_email">Email</label>
<input id="id_email" name="email" type="email" required value="{{ form.email }}">
</div>
<div class="field">
<label for="id_phone">Phone</label>
<input id="id_phone" name="phone" value="{{ form.phone }}">
</div>
</div>
<div data-address-autocomplete data-suggest-url="{% url 'address_suggest' %}">
<div class="field address-ac-wrap">
<label>Street address</label>
<input name="address_line1" data-ac="line1" value="{{ form.address_line1 }}" autocomplete="off">
</div>
<div class="field">
<label>Apt / suite</label>
<input name="address_line2" data-ac="line2" value="{{ form.address_line2 }}" autocomplete="address-line2">
</div>
<div class="form-grid cols-2">
<div class="field">
<label>City</label>
<input name="address_city" data-ac="city" value="{{ form.address_city }}" autocomplete="address-level2">
</div>
<div class="field">
<label>State</label>
<input name="address_state" data-ac="state" value="{{ form.address_state }}" autocomplete="address-level1" maxlength="32">
</div>
</div>
<div class="form-grid cols-2">
<div class="field">
<label>ZIP</label>
<input name="address_zip" data-ac="zip" value="{{ form.address_zip }}" autocomplete="postal-code" maxlength="20">
</div>
<div class="field">
<label>Country</label>
<input name="address_country" data-ac="country" value="{{ form.address_country|default:'US' }}" autocomplete="country" maxlength="2">
</div>
</div>
</div>
<div class="field">
<label for="id_notes">Notes</label>
<textarea id="id_notes" name="notes">{{ form.notes }}</textarea>
</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<button class="btn btn-primary btn-sm" type="submit">Add to mailing list</button>
<a class="btn btn-ghost btn-sm" href="{% url 'contacts:list' %}">Cancel</a>
</div>
</div>
</div>
<div class="panel">
<div class="panel-h"><h2>Consent</h2></div>
<div class="panel-b">
<div class="field">
<label class="check-row"><input type="checkbox" name="consent_email" value="1" {% if form.consent_email %}checked{% endif %}> Email marketing</label>
</div>
<div class="field">
<label class="check-row"><input type="checkbox" name="consent_sms" value="1" {% if form.consent_sms %}checked{% endif %}> SMS updates</label>
</div>
<div class="field">
<label class="check-row"><input type="checkbox" name="consent_postcard" value="1" {% if form.consent_postcard %}checked{% endif %}> Postcard mailings</label>
</div>
<p class="hint-block" style="margin-top:16px">
Matches existing contacts by email, phone, or address when possible.
Postcard defaults on when an address is saved.
</p>
</div>
</div>
</div>
</form>
{% endblock %}
{% block extra_js %}
<script src="{% static 'js/address-autocomplete.js' %}"></script>
{% endblock %}