Template
## Summary - Closes #9. Port shop-gated buyer accounts (register/login/profile, password reset, order history), purchase-gated reviews, and Stripe customer ids. Cards stay on Stripe. - Slim public contact form to email + interest + message. Shipping tracking status, scan events, paste-in numbers, and EasyPost webhook when FEATURE_SHIPPING is on. - Add template-neutral `seed_demo` (generic SKUs, demo shoppers, guest orders, tracking stubs). Prod refused. `--reset` deletes tagged demo rows after invoices. ## Test plan - [ ] `cd site && uv run python manage.py test` (173 tests passed locally) - [ ] Shop on: `/account/register`, login, profile, password reset, claim guest orders by email - [ ] Shop off: `/account/` URLs absent; Sign in / Account links hidden - [ ] Checkout as logged-in buyer stores `Order.user` and Stripe customer id; product review only after paid purchase - [ ] Shipping on: portal paste-in tracking + EasyPost webhook updates status; shipping off: orders still work without tracking UI - [ ] `seed_demo` refused in prod; `--reset` rebuilds tagged catalog/shoppers without deleting staff invoices Reviewed-on: #10
70 lines
2.7 KiB
HTML
70 lines
2.7 KiB
HTML
{% extends "portal_base.html" %}
|
|
{% block title %}{{ shipment.order.number }} · Shipping{% endblock %}
|
|
{% block topbar_title %}{{ shipment.order.number }}{% endblock %}
|
|
{% block portal_content %}
|
|
<p>{{ shipment.order.email }} · {{ shipment.get_status_display }}</p>
|
|
{% if shipment.tracking_number %}
|
|
<p>
|
|
Tracking: {{ shipment.tracking_number }}
|
|
{% if shipment.tracking_status %} · {{ shipment.get_tracking_status_display }}{% endif %}
|
|
</p>
|
|
{% if shipment.tracking_url %}<p><a href="{{ shipment.tracking_url }}" target="_blank" rel="noopener">Track package</a></p>{% endif %}
|
|
<form method="post" action="{% url 'shipping:shipment_refresh_tracking' shipment.pk %}">
|
|
{% csrf_token %}
|
|
<button class="btn btn-ghost btn-sm" type="submit">Refresh tracking</button>
|
|
</form>
|
|
{% endif %}
|
|
{% if shipment.label_url %}<p><a href="{{ shipment.label_url }}">Download label</a></p>{% endif %}
|
|
|
|
<h3>Add tracking</h3>
|
|
<p class="hint-block">Paste a number from Pirate Ship or the carrier. EasyPost looks up scan events when an API key is set.</p>
|
|
<form method="post" action="{% url 'shipping:shipment_attach_tracking' shipment.pk %}">
|
|
{% csrf_token %}
|
|
<div class="form-grid">
|
|
<div class="field">
|
|
<label for="id_tracking_number">Tracking number</label>
|
|
<input id="id_tracking_number" name="tracking_number" value="{{ shipment.tracking_number }}" required>
|
|
</div>
|
|
<div class="field">
|
|
<label for="id_carrier">Carrier</label>
|
|
<input id="id_carrier" name="carrier" value="{{ shipment.carrier }}" placeholder="USPS, UPS, FedEx">
|
|
</div>
|
|
</div>
|
|
<button class="btn btn-primary" type="submit">Save tracking</button>
|
|
</form>
|
|
|
|
{% if shipment.tracking_events %}
|
|
<h3>Scan history</h3>
|
|
<table class="table">
|
|
<thead><tr><th>When</th><th>Status</th><th>Detail</th></tr></thead>
|
|
<tbody>
|
|
{% for event in shipment.tracking_events %}
|
|
<tr>
|
|
<td>{{ event.datetime }}</td>
|
|
<td>{{ event.status }}</td>
|
|
<td>{{ event.message }}{% if event.location %} · {{ event.location }}{% endif %}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% endif %}
|
|
|
|
{% if shipment.status != 'labeled' %}
|
|
<form method="post" action="{% url 'shipping:shipment_buy' shipment.pk %}">
|
|
{% csrf_token %}
|
|
{% for rate in shipment.rates %}
|
|
<label>
|
|
<input type="radio" name="rate_id" value="{{ rate.id }}" {% if forloop.first %}checked{% endif %}>
|
|
{{ rate.carrier }} {{ rate.service }} — {{ rate.rate }} {{ rate.currency }}
|
|
</label><br>
|
|
{% empty %}
|
|
<p>No rates yet.</p>
|
|
{% endfor %}
|
|
{% if shipment.rates %}
|
|
<button class="btn btn-primary" type="submit">Buy label</button>
|
|
{% endif %}
|
|
</form>
|
|
{% endif %}
|
|
<p><a href="{% url 'shipping:shipment_list' %}">← All shipments</a></p>
|
|
{% endblock %}
|