Replace ticket qty input with +/- buttons, show sold/basket counts
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 57s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 57s
- Entry page shows tickets sold count, remaining, and "in basket" count - Replace numeric input + Buy button with add-to-basket / +/- controls - New POST /tickets/adjust/ route creates/cancels tickets to target count - Keep buy form active after adding (no confirmation replacement) - New service functions: get_sold_ticket_count, get_user_reserved_count, cancel_latest_reserved_ticket Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
4
templates/_types/tickets/_adjust_response.html
Normal file
4
templates/_types/tickets/_adjust_response.html
Normal file
@@ -0,0 +1,4 @@
|
||||
{# Response for ticket adjust — buy form + OOB cart-mini update #}
|
||||
{% from '_types/cart/_mini.html' import mini %}
|
||||
{{ mini(oob='true') }}
|
||||
{% include '_types/tickets/_buy_form.html' %}
|
||||
@@ -3,14 +3,31 @@
|
||||
<div id="ticket-buy-{{ entry.id }}" class="rounded-xl border border-stone-200 bg-white p-4">
|
||||
<h3 class="text-sm font-semibold text-stone-700 mb-3">
|
||||
<i class="fa fa-ticket mr-1" aria-hidden="true"></i>
|
||||
Buy Tickets
|
||||
Tickets
|
||||
</h3>
|
||||
|
||||
{# Sold / remaining info #}
|
||||
<div class="flex items-center gap-3 mb-3 text-xs text-stone-500">
|
||||
{% if ticket_sold_count is defined and ticket_sold_count %}
|
||||
<span>{{ ticket_sold_count }} sold</span>
|
||||
{% endif %}
|
||||
{% if ticket_remaining is not none %}
|
||||
<span>{{ ticket_remaining }} remaining</span>
|
||||
{% endif %}
|
||||
{% if user_ticket_count is defined and user_ticket_count %}
|
||||
<span class="text-emerald-600 font-medium">
|
||||
<i class="fa fa-shopping-cart text-[0.6rem]" aria-hidden="true"></i>
|
||||
{{ user_ticket_count }} in basket
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if entry.ticket_types %}
|
||||
{# Multiple ticket types #}
|
||||
<div class="space-y-2 mb-4">
|
||||
<div class="space-y-2">
|
||||
{% for tt in entry.ticket_types %}
|
||||
{% if tt.deleted_at is none %}
|
||||
{% set type_count = user_ticket_counts_by_type.get(tt.id, 0) if user_ticket_counts_by_type is defined else 0 %}
|
||||
<div class="flex items-center justify-between p-3 rounded-lg bg-stone-50 border border-stone-100">
|
||||
<div>
|
||||
<div class="font-medium text-sm">{{ tt.name }}</div>
|
||||
@@ -18,34 +35,83 @@
|
||||
£{{ '%.2f'|format(tt.cost) }}
|
||||
</div>
|
||||
</div>
|
||||
<form
|
||||
hx-post="{{ url_for('tickets.buy_tickets') }}"
|
||||
hx-target="#ticket-buy-{{ entry.id }}"
|
||||
hx-swap="outerHTML"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="hidden" name="entry_id" value="{{ entry.id }}" />
|
||||
<input type="hidden" name="ticket_type_id" value="{{ tt.id }}" />
|
||||
<input
|
||||
type="number"
|
||||
name="quantity"
|
||||
value="1"
|
||||
min="1"
|
||||
max="10"
|
||||
class="w-16 px-2 py-1 text-sm border rounded text-center"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
class="px-3 py-1 bg-emerald-600 text-white text-sm rounded hover:bg-emerald-700 transition"
|
||||
|
||||
{% if type_count == 0 %}
|
||||
{# Add to basket button #}
|
||||
<form
|
||||
hx-post="{{ url_for('tickets.adjust_quantity') }}"
|
||||
hx-target="#ticket-buy-{{ entry.id }}"
|
||||
hx-swap="outerHTML"
|
||||
class="flex items-center"
|
||||
>
|
||||
Buy
|
||||
</button>
|
||||
</form>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="hidden" name="entry_id" value="{{ entry.id }}" />
|
||||
<input type="hidden" name="ticket_type_id" value="{{ tt.id }}" />
|
||||
<input type="hidden" name="count" value="1" />
|
||||
<button
|
||||
type="submit"
|
||||
class="relative inline-flex items-center justify-center text-sm font-medium text-stone-500 hover:bg-emerald-50 rounded p-1"
|
||||
>
|
||||
<i class="fa fa-cart-plus text-2xl" aria-hidden="true"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
{# +/- controls #}
|
||||
<div class="flex items-center gap-2">
|
||||
<form
|
||||
hx-post="{{ url_for('tickets.adjust_quantity') }}"
|
||||
hx-target="#ticket-buy-{{ entry.id }}"
|
||||
hx-swap="outerHTML"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="hidden" name="entry_id" value="{{ entry.id }}" />
|
||||
<input type="hidden" name="ticket_type_id" value="{{ tt.id }}" />
|
||||
<input type="hidden" name="count" value="{{ type_count - 1 }}" />
|
||||
<button
|
||||
type="submit"
|
||||
class="inline-flex items-center justify-center w-8 h-8 text-sm font-medium rounded-full border border-emerald-600 text-emerald-700 hover:bg-emerald-50 text-xl"
|
||||
>
|
||||
-
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<a
|
||||
class="relative inline-flex items-center justify-center text-emerald-700"
|
||||
href="{{ url_for('tickets.my_tickets') }}"
|
||||
>
|
||||
<span class="relative inline-flex items-center justify-center">
|
||||
<i class="fa-solid fa-shopping-cart text-2xl" aria-hidden="true"></i>
|
||||
<span class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none">
|
||||
<span class="flex items-center justify-center bg-black text-white rounded-full w-4 h-4 text-xs font-bold">
|
||||
{{ type_count }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<form
|
||||
hx-post="{{ url_for('tickets.adjust_quantity') }}"
|
||||
hx-target="#ticket-buy-{{ entry.id }}"
|
||||
hx-swap="outerHTML"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="hidden" name="entry_id" value="{{ entry.id }}" />
|
||||
<input type="hidden" name="ticket_type_id" value="{{ tt.id }}" />
|
||||
<input type="hidden" name="count" value="{{ type_count + 1 }}" />
|
||||
<button
|
||||
type="submit"
|
||||
class="inline-flex items-center justify-center w-8 h-8 text-sm font-medium rounded-full border border-emerald-600 text-emerald-700 hover:bg-emerald-50 text-xl"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
{# Simple ticket (single price) #}
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
@@ -55,38 +121,80 @@
|
||||
</span>
|
||||
<span class="text-sm text-stone-500 ml-2">per ticket</span>
|
||||
</div>
|
||||
{% if ticket_remaining is not none %}
|
||||
<span class="text-xs text-stone-500">
|
||||
{{ ticket_remaining }} remaining
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<form
|
||||
hx-post="{{ url_for('tickets.buy_tickets') }}"
|
||||
hx-target="#ticket-buy-{{ entry.id }}"
|
||||
hx-swap="outerHTML"
|
||||
class="flex items-center gap-3"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="hidden" name="entry_id" value="{{ entry.id }}" />
|
||||
<label class="text-sm text-stone-600">Qty:</label>
|
||||
<input
|
||||
type="number"
|
||||
name="quantity"
|
||||
value="1"
|
||||
min="1"
|
||||
max="10"
|
||||
class="w-16 px-2 py-1 text-sm border rounded text-center"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
class="px-4 py-2 bg-emerald-600 text-white text-sm rounded-lg hover:bg-emerald-700 transition font-medium"
|
||||
{% set qty = user_ticket_count if user_ticket_count is defined else 0 %}
|
||||
|
||||
{% if qty == 0 %}
|
||||
{# Add to basket button #}
|
||||
<form
|
||||
hx-post="{{ url_for('tickets.adjust_quantity') }}"
|
||||
hx-target="#ticket-buy-{{ entry.id }}"
|
||||
hx-swap="outerHTML"
|
||||
class="flex items-center"
|
||||
>
|
||||
<i class="fa fa-ticket mr-1" aria-hidden="true"></i>
|
||||
Buy Tickets
|
||||
</button>
|
||||
</form>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="hidden" name="entry_id" value="{{ entry.id }}" />
|
||||
<input type="hidden" name="count" value="1" />
|
||||
<button
|
||||
type="submit"
|
||||
class="relative inline-flex items-center justify-center text-sm font-medium text-stone-500 hover:bg-emerald-50 rounded p-1"
|
||||
>
|
||||
<span class="relative inline-flex items-center justify-center">
|
||||
<i class="fa fa-cart-plus text-4xl" aria-hidden="true"></i>
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
{# +/- controls #}
|
||||
<div class="flex items-center gap-2">
|
||||
<form
|
||||
hx-post="{{ url_for('tickets.adjust_quantity') }}"
|
||||
hx-target="#ticket-buy-{{ entry.id }}"
|
||||
hx-swap="outerHTML"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="hidden" name="entry_id" value="{{ entry.id }}" />
|
||||
<input type="hidden" name="count" value="{{ qty - 1 }}" />
|
||||
<button
|
||||
type="submit"
|
||||
class="inline-flex items-center justify-center w-8 h-8 text-sm font-medium rounded-full border border-emerald-600 text-emerald-700 hover:bg-emerald-50 text-xl"
|
||||
>
|
||||
-
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<a
|
||||
class="relative inline-flex items-center justify-center text-emerald-700"
|
||||
href="{{ url_for('tickets.my_tickets') }}"
|
||||
>
|
||||
<span class="relative inline-flex items-center justify-center">
|
||||
<i class="fa-solid fa-shopping-cart text-2xl" aria-hidden="true"></i>
|
||||
<span class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none">
|
||||
<span class="flex items-center justify-center bg-black text-white rounded-full w-4 h-4 text-xs font-bold">
|
||||
{{ qty }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<form
|
||||
hx-post="{{ url_for('tickets.adjust_quantity') }}"
|
||||
hx-target="#ticket-buy-{{ entry.id }}"
|
||||
hx-swap="outerHTML"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="hidden" name="entry_id" value="{{ entry.id }}" />
|
||||
<input type="hidden" name="count" value="{{ qty + 1 }}" />
|
||||
<button
|
||||
type="submit"
|
||||
class="inline-flex items-center justify-center w-8 h-8 text-sm font-medium rounded-full border border-emerald-600 text-emerald-700 hover:bg-emerald-50 text-xl"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% elif entry.ticket_price is not none %}
|
||||
|
||||
Reference in New Issue
Block a user