feat: add markets and payments management pages
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 45s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 45s
- New markets blueprint at /<slug>/markets/ with create/delete - New payments blueprint at /<slug>/payments/ with SumUp config - Register both in events app with context processor for markets - Remove PageConfig feature flag check from calendar creation (feature toggles replaced by direct management pages) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
25
templates/_types/markets/_main_panel.html
Normal file
25
templates/_types/markets/_main_panel.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<section class="p-4">
|
||||
{% if has_access('markets.create_market') %}
|
||||
<div id="market-create-errors" class="mt-2 text-sm text-red-600"></div>
|
||||
|
||||
<form
|
||||
class="mt-4 flex gap-2 items-end"
|
||||
hx-post="{{ url_for('markets.create_market') }}"
|
||||
hx-target="#markets-list"
|
||||
hx-select="#markets-list"
|
||||
hx-swap="outerHTML"
|
||||
hx-on::before-request="document.querySelector('#market-create-errors').textContent='';"
|
||||
hx-on::response-error="document.querySelector('#market-create-errors').innerHTML = event.detail.xhr.responseText;"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="flex-1">
|
||||
<label class="block text-sm text-gray-600">Name</label>
|
||||
<input name="name" type="text" required class="w-full border rounded px-3 py-2" placeholder="e.g. Farm Shop, Bakery" />
|
||||
</div>
|
||||
<button type="submit" class="border rounded px-3 py-2">Add market</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<div id="markets-list" class="mt-6">
|
||||
{% include "_types/markets/_markets_list.html" %}
|
||||
</div>
|
||||
</section>
|
||||
37
templates/_types/markets/_markets_list.html
Normal file
37
templates/_types/markets/_markets_list.html
Normal file
@@ -0,0 +1,37 @@
|
||||
{% for m in markets %}
|
||||
<div class="mt-6 border rounded-lg p-4">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
|
||||
{% set market_href = market_url('/' + post.slug + '/' + m.slug + '/') %}
|
||||
<a
|
||||
class="flex items-baseline gap-3"
|
||||
href="{{ market_href }}"
|
||||
>
|
||||
<h3 class="font-semibold">{{ m.name }}</h3>
|
||||
<h4 class="text-gray-500">/{{ m.slug }}/</h4>
|
||||
</a>
|
||||
|
||||
<button
|
||||
class="text-sm border rounded px-3 py-1 hover:bg-red-50 hover:border-red-400"
|
||||
data-confirm
|
||||
data-confirm-title="Delete market?"
|
||||
data-confirm-text="Products will be hidden (soft delete)"
|
||||
data-confirm-icon="warning"
|
||||
data-confirm-confirm-text="Yes, delete it"
|
||||
data-confirm-cancel-text="Cancel"
|
||||
data-confirm-event="confirmed"
|
||||
hx-delete="{{ url_for('markets.delete_market', market_slug=m.slug) }}"
|
||||
hx-trigger="confirmed"
|
||||
hx-target="#markets-list"
|
||||
hx-select="#markets-list"
|
||||
hx-swap="outerHTML"
|
||||
hx-headers='{"X-CSRFToken":"{{ csrf_token() }}"}'
|
||||
>
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-gray-500 mt-4">No markets yet. Create one above.</p>
|
||||
{% endfor %}
|
||||
2
templates/_types/markets/_nav.html
Normal file
2
templates/_types/markets/_nav.html
Normal file
@@ -0,0 +1,2 @@
|
||||
{% from 'macros/admin_nav.html' import placeholder_nav %}
|
||||
{{ placeholder_nav() }}
|
||||
19
templates/_types/markets/_oob_elements.html
Normal file
19
templates/_types/markets/_oob_elements.html
Normal file
@@ -0,0 +1,19 @@
|
||||
{% extends 'oob_elements.html' %}
|
||||
|
||||
{% from '_types/root/_oob_menu.html' import mobile_menu with context %}
|
||||
|
||||
{% block oobs %}
|
||||
{% from '_types/root/_n/macros.html' import oob_header with context %}
|
||||
{{oob_header('post-admin-header-child', 'markets-header-child', '_types/markets/header/_header.html')}}
|
||||
|
||||
{% from '_types/post/admin/header/_header.html' import header_row with context %}
|
||||
{{ header_row(oob=True) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block mobile_menu %}
|
||||
{% include '_types/markets/_nav.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include "_types/markets/_main_panel.html" %}
|
||||
{% endblock %}
|
||||
14
templates/_types/markets/header/_header.html
Normal file
14
templates/_types/markets/header/_header.html
Normal file
@@ -0,0 +1,14 @@
|
||||
{% import 'macros/links.html' as links %}
|
||||
{% macro header_row(oob=False) %}
|
||||
{% call links.menu_row(id='markets-row', oob=oob) %}
|
||||
{% call links.link(url_for('markets.home'), hx_select_search) %}
|
||||
<i class="fa fa-shopping-bag" aria-hidden="true"></i>
|
||||
<div>
|
||||
Markets
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% call links.desktop_nav() %}
|
||||
{% include '_types/markets/_nav.html' %}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
{% endmacro %}
|
||||
21
templates/_types/markets/index.html
Normal file
21
templates/_types/markets/index.html
Normal file
@@ -0,0 +1,21 @@
|
||||
{% extends '_types/root/_index.html' %}
|
||||
|
||||
{% block meta %}{% endblock %}
|
||||
|
||||
{% block root_header_child %}
|
||||
{% from '_types/root/_n/macros.html' import index_row with context %}
|
||||
{% call index_row('post-header-child', '_types/post/header/_header.html') %}
|
||||
{% call index_row('markets-header-child', '_types/markets/header/_header.html') %}
|
||||
{% block markets_header_child %}
|
||||
{% endblock %}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
{% endblock %}
|
||||
|
||||
{% block _main_mobile_menu %}
|
||||
{% include '_types/markets/_nav.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include '_types/markets/_main_panel.html' %}
|
||||
{% endblock %}
|
||||
70
templates/_types/payments/_main_panel.html
Normal file
70
templates/_types/payments/_main_panel.html
Normal file
@@ -0,0 +1,70 @@
|
||||
<section class="p-4 max-w-lg mx-auto">
|
||||
<div id="payments-panel" class="space-y-4 p-4 bg-white rounded-lg border border-stone-200">
|
||||
<h3 class="text-lg font-semibold text-stone-800">
|
||||
<i class="fa fa-credit-card text-purple-600 mr-1"></i>
|
||||
SumUp Payment
|
||||
</h3>
|
||||
<p class="text-xs text-stone-400">
|
||||
Configure per-page SumUp credentials. Leave blank to use the global merchant account.
|
||||
</p>
|
||||
|
||||
<form
|
||||
hx-put="{{ url_for('payments.update_sumup') }}"
|
||||
hx-target="#payments-panel"
|
||||
hx-swap="outerHTML"
|
||||
hx-select="#payments-panel"
|
||||
class="space-y-3"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-stone-600 mb-1">Merchant Code</label>
|
||||
<input
|
||||
type="text"
|
||||
name="merchant_code"
|
||||
value="{{ sumup_merchant_code }}"
|
||||
placeholder="e.g. ME4J6100"
|
||||
class="w-full px-3 py-1.5 text-sm border border-stone-300 rounded focus:ring-purple-500 focus:border-purple-500"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-stone-600 mb-1">API Key</label>
|
||||
<input
|
||||
type="password"
|
||||
name="api_key"
|
||||
value=""
|
||||
placeholder="{{ '--------' if sumup_configured else 'sup_sk_...' }}"
|
||||
class="w-full px-3 py-1.5 text-sm border border-stone-300 rounded focus:ring-purple-500 focus:border-purple-500"
|
||||
>
|
||||
{% if sumup_configured %}
|
||||
<p class="text-xs text-stone-400 mt-0.5">Key is set. Leave blank to keep current key.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-stone-600 mb-1">Checkout Reference Prefix</label>
|
||||
<input
|
||||
type="text"
|
||||
name="checkout_prefix"
|
||||
value="{{ sumup_checkout_prefix }}"
|
||||
placeholder="e.g. ROSE-"
|
||||
class="w-full px-3 py-1.5 text-sm border border-stone-300 rounded focus:ring-purple-500 focus:border-purple-500"
|
||||
>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="px-4 py-1.5 text-sm font-medium text-white bg-purple-600 rounded hover:bg-purple-700 focus:ring-2 focus:ring-purple-500"
|
||||
>
|
||||
Save SumUp Settings
|
||||
</button>
|
||||
|
||||
{% if sumup_configured %}
|
||||
<span class="ml-2 text-xs text-green-600">
|
||||
<i class="fa fa-check-circle"></i> Connected
|
||||
</span>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
2
templates/_types/payments/_nav.html
Normal file
2
templates/_types/payments/_nav.html
Normal file
@@ -0,0 +1,2 @@
|
||||
{% from 'macros/admin_nav.html' import placeholder_nav %}
|
||||
{{ placeholder_nav() }}
|
||||
19
templates/_types/payments/_oob_elements.html
Normal file
19
templates/_types/payments/_oob_elements.html
Normal file
@@ -0,0 +1,19 @@
|
||||
{% extends 'oob_elements.html' %}
|
||||
|
||||
{% from '_types/root/_oob_menu.html' import mobile_menu with context %}
|
||||
|
||||
{% block oobs %}
|
||||
{% from '_types/root/_n/macros.html' import oob_header with context %}
|
||||
{{oob_header('post-admin-header-child', 'payments-header-child', '_types/payments/header/_header.html')}}
|
||||
|
||||
{% from '_types/post/admin/header/_header.html' import header_row with context %}
|
||||
{{ header_row(oob=True) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block mobile_menu %}
|
||||
{% include '_types/payments/_nav.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include "_types/payments/_main_panel.html" %}
|
||||
{% endblock %}
|
||||
14
templates/_types/payments/header/_header.html
Normal file
14
templates/_types/payments/header/_header.html
Normal file
@@ -0,0 +1,14 @@
|
||||
{% import 'macros/links.html' as links %}
|
||||
{% macro header_row(oob=False) %}
|
||||
{% call links.menu_row(id='payments-row', oob=oob) %}
|
||||
{% call links.link(url_for('payments.home'), hx_select_search) %}
|
||||
<i class="fa fa-credit-card" aria-hidden="true"></i>
|
||||
<div>
|
||||
Payments
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% call links.desktop_nav() %}
|
||||
{% include '_types/payments/_nav.html' %}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
{% endmacro %}
|
||||
21
templates/_types/payments/index.html
Normal file
21
templates/_types/payments/index.html
Normal file
@@ -0,0 +1,21 @@
|
||||
{% extends '_types/root/_index.html' %}
|
||||
|
||||
{% block meta %}{% endblock %}
|
||||
|
||||
{% block root_header_child %}
|
||||
{% from '_types/root/_n/macros.html' import index_row with context %}
|
||||
{% call index_row('post-header-child', '_types/post/header/_header.html') %}
|
||||
{% call index_row('payments-header-child', '_types/payments/header/_header.html') %}
|
||||
{% block payments_header_child %}
|
||||
{% endblock %}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
{% endblock %}
|
||||
|
||||
{% block _main_mobile_menu %}
|
||||
{% include '_types/payments/_nav.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include '_types/payments/_main_panel.html' %}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user