Files
web_django_template/site/shop/templates/shop/detail.html
T
westfarn f3e34f5099 Add shopper accounts, reviews, tracking, and seed_demo (#10)
## 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
2026-09-21 13:29:36 -07:00

62 lines
2.4 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}{{ product.name }} · {{ SITE_NAME }}{% endblock %}
{% block content %}
<section class="section section-lg">
<div class="container">
<p><a href="{% url 'shop:list' %}">← Shop</a></p>
<h1>{{ product.name }}</h1>
<p class="muted">{{ product.price }} {{ product.currency|upper }} · {{ product.sku }}</p>
<div>{{ product.description|linebreaks }}</div>
{% if available is not None %}
<p>{{ available }} in stock</p>
{% endif %}
<form method="post" action="{% url 'shop:cart_add' product.slug %}">
{% csrf_token %}
<label>Qty <input name="quantity" type="number" min="1" value="1"></label>
<button class="button button-primary" type="submit">Add to cart</button>
</form>
<h2>Reviews</h2>
{% if review_count %}
<p>{{ review_avg|floatformat:1 }} / 5 · {{ review_count }} review{{ review_count|pluralize }}</p>
{% else %}
<p>No reviews yet.</p>
{% endif %}
{% if can_review %}
<form method="post" action="{% url 'shop:review' product.slug %}">
{% csrf_token %}
<p>
<label for="review-rating">Rating</label>
<select id="review-rating" name="rating" required>
<option value="">Choose 1–5</option>
<option value="5">5 — Excellent</option>
<option value="4">4 — Good</option>
<option value="3">3 — Okay</option>
<option value="2">2 — Fair</option>
<option value="1">1 — Poor</option>
</select>
</p>
<p><label>Title (optional) <input name="title" maxlength="120"></label></p>
<p><label>Review (optional) <textarea name="body" rows="4"></textarea></label></p>
<button class="button button-primary" type="submit">Submit review</button>
</form>
{% elif already_reviewed %}
<p>You already reviewed this product.</p>
{% elif user.is_authenticated %}
<p>Buy this product to leave a review.</p>
{% else %}
<p><a href="{% url 'account:login' %}?next={{ request.path }}">Sign in</a> after a purchase to leave a review.</p>
{% endif %}
<ul>
{% for review in reviews %}
<li>
<strong>{{ review.rating }}/5</strong>
{% if review.title %} · {{ review.title }}{% endif %}
<span> — {{ review.user.first_name|default:review.user.email }}</span>
{% if review.body %}<p>{{ review.body|linebreaks }}</p>{% endif %}
</li>
{% endfor %}
</ul>
</div>
</section>
{% endblock %}