Add global + page-scoped market listings with infinite scroll
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m4s

- New all_markets blueprint at / with paginated grid and HTMX infinite scroll
- New page_markets blueprint at /<slug>/ for page-scoped market listing
- list_marketplaces service method (via shared submodule update)
- Updated slug preprocessor to handle both /<slug>/ and /<page_slug>/<market_slug>/
- Removed inline markets_listing() route (replaced by all_markets blueprint)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
giles
2026-02-22 23:34:58 +00:00
parent 555ac6a152
commit 3c87832fdf
15 changed files with 303 additions and 38 deletions

View File

@@ -0,0 +1,33 @@
{# Card for a single market in the global listing #}
{% set pi = page_info.get(market.container_id, {}) %}
{% set page_slug = pi.get('slug', '') %}
{% set page_title = pi.get('title') %}
{% if page_slug %}
{% set market_href = market_url('/' ~ page_slug ~ '/' ~ market.slug ~ '/') %}
{% else %}
{% set market_href = '' %}
{% endif %}
<article class="rounded-xl bg-white shadow-sm border border-stone-200 p-5 flex flex-col justify-between hover:border-stone-400 transition-colors">
<div>
{% if market_href %}
<a href="{{ market_href }}" class="hover:text-emerald-700">
<h2 class="text-lg font-semibold text-stone-900">{{ market.name }}</h2>
</a>
{% else %}
<h2 class="text-lg font-semibold text-stone-900">{{ market.name }}</h2>
{% endif %}
{% if market.description %}
<p class="text-sm text-stone-600 mt-1 line-clamp-2">{{ market.description }}</p>
{% endif %}
</div>
<div class="flex flex-wrap items-center gap-1.5 mt-3">
{% if page_title %}
<a href="{{ market_url('/' ~ page_slug ~ '/') }}"
class="inline-block px-2 py-0.5 rounded-full text-xs font-medium bg-amber-100 text-amber-800 hover:bg-amber-200">
{{ page_title }}
</a>
{% endif %}
</div>
</article>

View File

@@ -0,0 +1,18 @@
{% for market in markets %}
{% include "_types/all_markets/_card.html" %}
{% endfor %}
{% if has_more %}
{# Infinite scroll sentinel #}
{% set next_url = url_for('all_markets.markets_fragment', page=page + 1)|host %}
<div
id="sentinel-{{ page }}"
class="h-4 opacity-0 pointer-events-none"
hx-get="{{ next_url }}"
hx-trigger="intersect once delay:250ms"
hx-swap="outerHTML"
role="status"
aria-hidden="true"
>
<div class="text-center text-xs text-stone-400">loading...</div>
</div>
{% endif %}

View File

@@ -0,0 +1,12 @@
{# Markets grid #}
{% if markets %}
<div class="max-w-full px-3 py-3 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{% include "_types/all_markets/_cards.html" %}
</div>
{% else %}
<div class="px-3 py-12 text-center text-stone-400">
<i class="fa fa-store text-4xl mb-3" aria-hidden="true"></i>
<p class="text-lg">No markets available</p>
</div>
{% endif %}
<div class="pb-8"></div>

View File

@@ -0,0 +1,7 @@
{% extends '_types/root/_index.html' %}
{% block meta %}{% endblock %}
{% block content %}
{% include '_types/all_markets/_main_panel.html' %}
{% endblock %}

View File

@@ -0,0 +1,13 @@
{# Card for a single market in a page-scoped listing #}
{% set market_href = market_url('/' ~ post.slug ~ '/' ~ market.slug ~ '/') %}
<article class="rounded-xl bg-white shadow-sm border border-stone-200 p-5 flex flex-col justify-between hover:border-stone-400 transition-colors">
<div>
<a href="{{ market_href }}" class="hover:text-emerald-700">
<h2 class="text-lg font-semibold text-stone-900">{{ market.name }}</h2>
</a>
{% if market.description %}
<p class="text-sm text-stone-600 mt-1 line-clamp-2">{{ market.description }}</p>
{% endif %}
</div>
</article>

View File

@@ -0,0 +1,18 @@
{% for market in markets %}
{% include "_types/page_markets/_card.html" %}
{% endfor %}
{% if has_more %}
{# Infinite scroll sentinel #}
{% set next_url = url_for('page_markets.markets_fragment', page=page + 1)|host %}
<div
id="sentinel-{{ page }}"
class="h-4 opacity-0 pointer-events-none"
hx-get="{{ next_url }}"
hx-trigger="intersect once delay:250ms"
hx-swap="outerHTML"
role="status"
aria-hidden="true"
>
<div class="text-center text-xs text-stone-400">loading...</div>
</div>
{% endif %}

View File

@@ -0,0 +1,12 @@
{# Markets grid for a single page #}
{% if markets %}
<div class="max-w-full px-3 py-3 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{% include "_types/page_markets/_cards.html" %}
</div>
{% else %}
<div class="px-3 py-12 text-center text-stone-400">
<i class="fa fa-store text-4xl mb-3" aria-hidden="true"></i>
<p class="text-lg">No markets for this page</p>
</div>
{% endif %}
<div class="pb-8"></div>

View File

@@ -0,0 +1,15 @@
{% 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') %}
{% block post_header_child %}
{% endblock %}
{% endcall %}
{% endblock %}
{% block content %}
{% include '_types/page_markets/_main_panel.html' %}
{% endblock %}