Add global + page-scoped market listings with infinite scroll
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m4s
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:
33
templates/_types/all_markets/_card.html
Normal file
33
templates/_types/all_markets/_card.html
Normal 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>
|
||||
18
templates/_types/all_markets/_cards.html
Normal file
18
templates/_types/all_markets/_cards.html
Normal 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 %}
|
||||
12
templates/_types/all_markets/_main_panel.html
Normal file
12
templates/_types/all_markets/_main_panel.html
Normal 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>
|
||||
7
templates/_types/all_markets/index.html
Normal file
7
templates/_types/all_markets/index.html
Normal file
@@ -0,0 +1,7 @@
|
||||
{% extends '_types/root/_index.html' %}
|
||||
|
||||
{% block meta %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include '_types/all_markets/_main_panel.html' %}
|
||||
{% endblock %}
|
||||
13
templates/_types/page_markets/_card.html
Normal file
13
templates/_types/page_markets/_card.html
Normal 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>
|
||||
18
templates/_types/page_markets/_cards.html
Normal file
18
templates/_types/page_markets/_cards.html
Normal 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 %}
|
||||
12
templates/_types/page_markets/_main_panel.html
Normal file
12
templates/_types/page_markets/_main_panel.html
Normal 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>
|
||||
15
templates/_types/page_markets/index.html
Normal file
15
templates/_types/page_markets/index.html
Normal 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 %}
|
||||
Reference in New Issue
Block a user