feat: add Products form to admin

This commit is contained in:
2026-05-18 17:07:15 +02:00
parent 3b46a18b29
commit 6471b98ec2
9 changed files with 407 additions and 3 deletions

View File

@@ -0,0 +1,45 @@
{% extends "wagtailadmin/base.html" %}
{% load static i18n %}
{% block titletag %}
{% trans "Product" %} {% if product %} — {{ product.name }}{% endif %}
{% endblock titletag %}
{% block content %}
{% include "wagtailadmin/shared/header.html" with title="Products" icon="tag" %}
<div style="padding: 0 3em;">
<a href="{% url 'admin_purchase_dashboard' %}" class="button button-secondary">&larr; {% trans "Back" %}</a>
<h1>{% if product %}{% trans "Edit product" %}{% else %}{% trans "Add product" %}{% endif %}</h1>
{% if error %}
<div class="message error">{{ error }}</div>
{% endif %}
<form action="" method="post">
{% csrf_token %}
<label for="id_name">{% trans "Name" %}</label>
<input id="id_name" name="name" type="text" value="{{ product.name|default_if_none:'' }}" required>
<label for="id_description">{% trans "Description" %}</label>
<textarea id="id_description" name="description">{{ product.description|default_if_none:'' }}</textarea>
<label for="id_price">{% trans "Price (cents)" %}</label>
<input id="id_price" name="price_cents" type="number" value="{{ product.price_cents|default_if_none:'' }}" required>
<label for="id_currency">{% trans "Currency" %}</label>
<input id="id_currency" name="currency" type="text" value="{{ product.currency|default_if_none:'usd' }}">
<p>
{% trans "Stripe product" %}: {{ product.stripe_product_id|default:"—" }}<br>
{% trans "Stripe price" %}: {{ product.stripe_price_id|default:"—" }}
</p>
<div style="margin-top:1em;">
<button type="submit" class="button button-primary">{% trans "Save" %}</button>
{% if product %}
<button type="submit" name="action" value="delete" class="button button-secondary" style="margin-left:0.5em">{% trans "Delete" %}</button>
{% endif %}
</div>
</form>
</div>
{% endblock content %}

View File

@@ -0,0 +1,23 @@
{% extends "wagtailadmin/base.html" %}
{% load static i18n %}
{% block titletag %}
{% trans "Products" %}
{% endblock titletag %}
{% block content %}
{% include "wagtailadmin/shared/header.html" with title="Products" icon="tag" %}
<h1>{% trans "Purchasable Products" %}</h1>
<a href="{% url 'admin_purchase_add' %}" class="button button-primary">{% trans "Add product" %}</a>
<ul>
{% for p in products %}
<li>
<a href="{% url 'admin_purchase' p.id %}">
{{ p.name }} — {{ p.price_cents|floatformat:-2 }} {{ p.currency|upper }}
</a>
</li>
{% empty %}
<li>{% trans "No products found." %}</li>
{% endfor %}
</ul>
{% endblock content %}

View File

@@ -1,10 +1,12 @@
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.shortcuts import redirect, render
from django.shortcuts import redirect, render, get_object_or_404
from home.models import ChatMessage
from purchase.models import PurchasableProduct
from django.views.decorators.http import require_http_methods
# Chat admin + user views (restored)
@login_required
def admin_chat_dashboard(request):
chats = ChatMessage.get_all_user_senders()
@@ -41,3 +43,60 @@ def user_chat_send(request, user_id):
if request.user.is_staff:
return redirect("admin_chat", user_id=user_id)
return redirect("user_chat")
# PurchasableProduct admin views
@login_required
def admin_purchase_dashboard(request):
products = PurchasableProduct.objects.all().order_by("-created_at")
return render(
request, "purchase/admin/admin_purchase_dashboard.html", {"products": products}
)
@login_required
@require_http_methods(["GET", "POST"])
def admin_purchase(request, product_id=None):
product = None
if product_id:
product = get_object_or_404(PurchasableProduct, id=product_id)
if request.method == "POST":
# Handle create/update/delete actions
action = request.POST.get("action")
name = request.POST.get("name", "").strip()
description = request.POST.get("description", "").strip()
price = request.POST.get("price_cents")
currency = request.POST.get("currency", "pln").strip()
if action == "delete" and product:
product.delete()
return redirect("admin_purchase_dashboard")
# Create or update
if not name or not price:
# Simple validation: require name and price
error = "Name and price are required"
return render(
request,
"purchase/admin/admin_purchase.html",
{"product": product, "error": error},
)
price_cents = int(price)
if product:
product.name = name
product.description = description
product.price_cents = price_cents
product.currency = currency
product.save()
else:
PurchasableProduct.objects.create(
name=name,
description=description,
price_cents=price_cents,
currency=currency,
)
return redirect("admin_purchase_dashboard")
return render(request, "purchase/admin/admin_purchase.html", {"product": product})

View File

@@ -47,7 +47,7 @@ def register_code_block_feature(features):
@hooks.register("register_admin_urls")
def register_admin_chat_dashboard_url():
def register_admin_urls():
return [
path("chat/", views.admin_chat_dashboard, name="admin_chat_dashboard"),
path(
@@ -55,9 +55,25 @@ def register_admin_chat_dashboard_url():
views.admin_chat,
name="admin_chat",
),
path("purchase/", views.admin_purchase_dashboard, name="admin_purchase_dashboard"),
path("purchase/add/", views.admin_purchase, name="admin_purchase_add"),
path(
"purchase/<int:product_id>/",
views.admin_purchase,
name="admin_purchase",
),
]
@hooks.register("register_admin_menu_item")
def register_admin_chat_menu_item():
return MenuItem("Chat", reverse("admin_chat_dashboard"), icon_name="mail")
@hooks.register("register_admin_menu_item")
def register_admin_purchase_menu_item():
return MenuItem("Products", reverse("admin_purchase_dashboard"), icon_name="tag")