Move blog index rendering from Python to .sx composition defcomps
BlogPageService.index_data() assembles all data (cards, filters, actions) and 7 new .sx defcomps handle rendering: main content, aside, filter, actions, tag groups filter, authors filter, and sentinel. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -133,85 +133,40 @@ def register(url_prefix, title):
|
||||
@blogs_bp.get("/index/")
|
||||
async def index():
|
||||
"""Blog listing — moved from / to /index."""
|
||||
|
||||
q = decode()
|
||||
content_type = request.args.get("type", "posts")
|
||||
|
||||
if content_type == "pages":
|
||||
data = await pages_data(g.s, q.page, q.search)
|
||||
context = {
|
||||
**data,
|
||||
"content_type": "pages",
|
||||
"search": q.search,
|
||||
"selected_tags": (),
|
||||
"selected_authors": (),
|
||||
"selected_groups": (),
|
||||
"sort": None,
|
||||
"view": None,
|
||||
"drafts": None,
|
||||
"draft_count": 0,
|
||||
"tags": [],
|
||||
"authors": [],
|
||||
"tag_groups": [],
|
||||
"posts": data.get("pages", []),
|
||||
}
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import render_blog_page, render_blog_oob, render_blog_page_cards
|
||||
|
||||
tctx = await get_template_context()
|
||||
tctx.update(context)
|
||||
if not is_htmx_request():
|
||||
html = await render_blog_page(tctx)
|
||||
return await make_response(html)
|
||||
elif q.page > 1:
|
||||
sx_src = await render_blog_page_cards(tctx)
|
||||
return sx_response(sx_src)
|
||||
else:
|
||||
sx_src = await render_blog_oob(tctx)
|
||||
return sx_response(sx_src)
|
||||
|
||||
# Default: posts listing
|
||||
# Drafts filter requires login; ignore if not logged in
|
||||
show_drafts = bool(q.drafts and g.user)
|
||||
is_admin = bool((g.get("rights") or {}).get("admin"))
|
||||
drafts_user_id = None if (not show_drafts or is_admin) else g.user.id
|
||||
|
||||
# For the draft count badge: admin sees all drafts, non-admin sees own
|
||||
count_drafts_uid = None if (g.user and is_admin) else (g.user.id if g.user else False)
|
||||
|
||||
data = await posts_data(
|
||||
g.s, q.page, q.search, q.sort, q.selected_tags, q.selected_authors, q.liked,
|
||||
drafts=show_drafts, drafts_user_id=drafts_user_id,
|
||||
count_drafts_for_user_id=count_drafts_uid,
|
||||
selected_groups=q.selected_groups,
|
||||
from shared.services.registry import services
|
||||
from shared.sx.helpers import (
|
||||
render_to_sx, root_header_sx, full_page_sx, oob_page_sx,
|
||||
)
|
||||
from sx.sx_components import _blog_header_sx, _oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
context = {
|
||||
**data,
|
||||
"content_type": "posts",
|
||||
"selected_tags": q.selected_tags,
|
||||
"selected_authors": q.selected_authors,
|
||||
"selected_groups": q.selected_groups,
|
||||
"sort": q.sort,
|
||||
"search": q.search,
|
||||
"view": q.view,
|
||||
"drafts": q.drafts if show_drafts else None,
|
||||
}
|
||||
data = await services.get("blog_page").index_data(g.s)
|
||||
|
||||
# Render content, aside, and filter via .sx defcomps
|
||||
content = await render_to_sx("blog-index-main-content", **data)
|
||||
aside = await render_to_sx("blog-index-aside-content", **data)
|
||||
filter_sx = await render_to_sx("blog-index-filter-content", **data)
|
||||
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import render_blog_page, render_blog_oob, render_blog_cards
|
||||
|
||||
tctx = await get_template_context()
|
||||
tctx.update(context)
|
||||
|
||||
if not is_htmx_request():
|
||||
html = await render_blog_page(tctx)
|
||||
root_hdr = await root_header_sx(tctx)
|
||||
blog_hdr = await _blog_header_sx(tctx)
|
||||
header_rows = "(<> " + root_hdr + " " + blog_hdr + ")"
|
||||
html = await full_page_sx(tctx, header_rows=header_rows,
|
||||
content=content, aside=aside, filter=filter_sx)
|
||||
return await make_response(html)
|
||||
elif q.page > 1:
|
||||
# Sx wire format — client renders blog cards
|
||||
sx_src = await render_blog_cards(tctx)
|
||||
return sx_response(sx_src)
|
||||
elif data.get("page", 1) > 1:
|
||||
# Pagination — return just the cards
|
||||
return sx_response(content)
|
||||
else:
|
||||
sx_src = await render_blog_oob(tctx)
|
||||
root_hdr = await root_header_sx(tctx)
|
||||
blog_hdr = await _blog_header_sx(tctx)
|
||||
rows = "(<> " + root_hdr + " " + blog_hdr + ")"
|
||||
header_oob = await _oob_header_sx("root-header-child", "blog-header-child", rows)
|
||||
sx_src = await oob_page_sx(oobs=header_oob, content=content,
|
||||
aside=aside, filter=filter_sx)
|
||||
return sx_response(sx_src)
|
||||
|
||||
@blogs_bp.post("/new/")
|
||||
|
||||
@@ -171,6 +171,165 @@ class BlogPageService:
|
||||
"csrf": generate_csrf_token(),
|
||||
}
|
||||
|
||||
async def index_data(self, session, **kw):
|
||||
"""Blog index page data — posts or pages listing with filters."""
|
||||
from quart import g, request, url_for as qurl
|
||||
from bp.blog.services.posts_data import posts_data
|
||||
from bp.blog.services.pages_data import pages_data
|
||||
from bp.blog.filters.qs import decode
|
||||
from shared.utils import host_url
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
|
||||
q = decode()
|
||||
content_type = request.args.get("type", "posts")
|
||||
is_admin = bool((g.get("rights") or {}).get("admin"))
|
||||
user = getattr(g, "user", None)
|
||||
csrf = generate_csrf_token()
|
||||
|
||||
blog_url_base = host_url(qurl("blog.index")).rstrip("/index").rstrip("/")
|
||||
|
||||
if content_type == "pages":
|
||||
data = await pages_data(session, q.page, q.search)
|
||||
posts_list = data.get("pages", [])
|
||||
tag_groups_raw = []
|
||||
authors_raw = []
|
||||
draft_count = 0
|
||||
selected_tags = ()
|
||||
selected_authors = ()
|
||||
selected_groups = ()
|
||||
else:
|
||||
show_drafts = bool(q.drafts and user)
|
||||
drafts_user_id = None if (not show_drafts or is_admin) else user.id
|
||||
count_drafts_uid = None if (user and is_admin) else (user.id if user else False)
|
||||
data = await posts_data(
|
||||
session, q.page, q.search, q.sort, q.selected_tags,
|
||||
q.selected_authors, q.liked,
|
||||
drafts=show_drafts, drafts_user_id=drafts_user_id,
|
||||
count_drafts_for_user_id=count_drafts_uid,
|
||||
selected_groups=q.selected_groups,
|
||||
)
|
||||
posts_list = data.get("posts", [])
|
||||
tag_groups_raw = data.get("tag_groups", [])
|
||||
authors_raw = data.get("authors", [])
|
||||
draft_count = data.get("draft_count", 0)
|
||||
selected_tags = q.selected_tags
|
||||
selected_authors = q.selected_authors
|
||||
selected_groups = q.selected_groups
|
||||
|
||||
page_num = data.get("page", q.page)
|
||||
total_pages = data.get("total_pages", 1)
|
||||
card_widgets = data.get("card_widgets_html", {})
|
||||
|
||||
current_local_href = f"{blog_url_base}/index"
|
||||
if content_type == "pages":
|
||||
current_local_href += "?type=pages"
|
||||
hx_select = "#main-panel"
|
||||
|
||||
# Serialize posts for cards
|
||||
def _format_ts(dt):
|
||||
if not dt:
|
||||
return ""
|
||||
return dt.strftime("%-d %b %Y at %H:%M") if hasattr(dt, "strftime") else str(dt)
|
||||
|
||||
cards = []
|
||||
for p in posts_list:
|
||||
slug = p.get("slug", "")
|
||||
href = f"{blog_url_base}/{slug}/"
|
||||
status = p.get("status", "published")
|
||||
is_draft = status == "draft"
|
||||
ts = _format_ts(p.get("updated_at") if is_draft else p.get("published_at"))
|
||||
tags = []
|
||||
for t in (p.get("tags") or []):
|
||||
name = t.get("name") or getattr(t, "name", "")
|
||||
fi = t.get("feature_image") or getattr(t, "feature_image", None)
|
||||
tags.append({"name": name, "src": fi or "", "initial": name[:1] if name else ""})
|
||||
authors = []
|
||||
for a in (p.get("authors") or []):
|
||||
name = a.get("name") or getattr(a, "name", "")
|
||||
img = a.get("profile_image") or getattr(a, "profile_image", None)
|
||||
authors.append({"name": name, "image": img or ""})
|
||||
card = {
|
||||
"slug": slug, "href": href, "hx_select": hx_select,
|
||||
"title": p.get("title", ""), "feature_image": p.get("feature_image"),
|
||||
"excerpt": p.get("custom_excerpt") or p.get("excerpt", ""),
|
||||
"is_draft": is_draft,
|
||||
"publish_requested": p.get("publish_requested", False) if is_draft else False,
|
||||
"status_timestamp": ts,
|
||||
"tags": tags, "authors": authors,
|
||||
"has_like": bool(user),
|
||||
}
|
||||
if user:
|
||||
card["liked"] = p.get("is_liked", False)
|
||||
card["like_url"] = f"{blog_url_base}/{slug}/like/toggle/"
|
||||
card["csrf_token"] = csrf
|
||||
widget = card_widgets.get(str(p.get("id", "")), "")
|
||||
if widget:
|
||||
card["widget"] = widget
|
||||
# Page-specific fields
|
||||
features = p.get("features") or {}
|
||||
if content_type == "pages":
|
||||
card["has_calendar"] = features.get("calendar", False)
|
||||
card["has_market"] = features.get("market", False)
|
||||
card["pub_timestamp"] = ts
|
||||
cards.append(card)
|
||||
|
||||
# Serialize tag groups for filter
|
||||
tag_groups = []
|
||||
for grp in tag_groups_raw:
|
||||
g_slug = grp.get("slug", "") if isinstance(grp, dict) else getattr(grp, "slug", "")
|
||||
g_name = grp.get("name", "") if isinstance(grp, dict) else getattr(grp, "name", "")
|
||||
g_fi = grp.get("feature_image") if isinstance(grp, dict) else getattr(grp, "feature_image", None)
|
||||
g_colour = grp.get("colour") if isinstance(grp, dict) else getattr(grp, "colour", None)
|
||||
g_count = grp.get("post_count", 0) if isinstance(grp, dict) else getattr(grp, "post_count", 0)
|
||||
if g_count <= 0 and g_slug not in selected_groups:
|
||||
continue
|
||||
tag_groups.append({
|
||||
"slug": g_slug, "name": g_name, "feature_image": g_fi,
|
||||
"colour": g_colour, "post_count": g_count,
|
||||
"is_selected": g_slug in selected_groups,
|
||||
})
|
||||
|
||||
# Serialize authors for filter
|
||||
authors_list = []
|
||||
for a in authors_raw:
|
||||
a_slug = a.get("slug", "") if isinstance(a, dict) else getattr(a, "slug", "")
|
||||
a_name = a.get("name", "") if isinstance(a, dict) else getattr(a, "name", "")
|
||||
a_img = a.get("profile_image") if isinstance(a, dict) else getattr(a, "profile_image", None)
|
||||
a_count = a.get("published_post_count", 0) if isinstance(a, dict) else getattr(a, "published_post_count", 0)
|
||||
authors_list.append({
|
||||
"slug": a_slug, "name": a_name, "profile_image": a_img,
|
||||
"published_post_count": a_count,
|
||||
"is_selected": a_slug in selected_authors,
|
||||
})
|
||||
|
||||
# Filter summary names
|
||||
tg_summary_names = [grp["name"] for grp in tag_groups if grp["is_selected"]]
|
||||
au_summary_names = [a["name"] for a in authors_list if a["is_selected"]]
|
||||
|
||||
return {
|
||||
"content_type": content_type,
|
||||
"view": q.view,
|
||||
"cards": cards,
|
||||
"page": page_num,
|
||||
"total_pages": total_pages,
|
||||
"current_local_href": current_local_href,
|
||||
"hx_select": hx_select,
|
||||
"is_admin": is_admin,
|
||||
"has_user": bool(user),
|
||||
"draft_count": draft_count,
|
||||
"drafts": bool(q.drafts) if user else False,
|
||||
"new_post_href": f"{blog_url_base}/new/",
|
||||
"new_page_href": f"{blog_url_base}/new-page/",
|
||||
"tag_groups": tag_groups,
|
||||
"authors": authors_list,
|
||||
"is_any_group": len(selected_groups) == 0 and len(selected_tags) == 0,
|
||||
"is_any_author": len(selected_authors) == 0,
|
||||
"tg_summary": ", ".join(tg_summary_names) if tg_summary_names else "",
|
||||
"au_summary": ", ".join(au_summary_names) if au_summary_names else "",
|
||||
"blog_url_base": blog_url_base,
|
||||
"csrf": csrf,
|
||||
}
|
||||
|
||||
async def post_admin_data(self, session, *, slug=None, **kw):
|
||||
"""Post admin panel — just needs post loaded into context."""
|
||||
from quart import g
|
||||
|
||||
221
blog/sx/index.sx
221
blog/sx/index.sx
@@ -30,3 +30,224 @@
|
||||
tag-groups-filter
|
||||
authors-filter)
|
||||
(div :id "filter-summary-desktop" :hxx-swap-oob "outerHTML")))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Data-driven composition defcomps — replace Python sx_components functions
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
;; Helper: CSS class for filter item based on selection state
|
||||
(defcomp ~blog-filter-cls (&key is-on)
|
||||
;; Returns nothing — use inline (if is-on ...) instead
|
||||
nil)
|
||||
|
||||
;; Blog index main content — replaces _blog_main_panel_sx
|
||||
(defcomp ~blog-index-main-content (&key content-type view cards page total-pages
|
||||
current-local-href hx-select blog-url-base)
|
||||
(let* ((posts-href (str blog-url-base "/index"))
|
||||
(pages-href (str posts-href "?type=pages"))
|
||||
(posts-cls (if (not (= content-type "pages"))
|
||||
"bg-stone-700 text-white" "bg-stone-100 text-stone-600 hover:bg-stone-200"))
|
||||
(pages-cls (if (= content-type "pages")
|
||||
"bg-stone-700 text-white" "bg-stone-100 text-stone-600 hover:bg-stone-200")))
|
||||
(if (= content-type "pages")
|
||||
;; Pages listing
|
||||
(~blog-main-panel-pages
|
||||
:tabs (~blog-content-type-tabs
|
||||
:posts-href posts-href :pages-href pages-href
|
||||
:hx-select hx-select :posts-cls posts-cls :pages-cls pages-cls)
|
||||
:cards (<>
|
||||
(map (lambda (card)
|
||||
(~blog-page-card
|
||||
:href (get card "href") :hx-select hx-select
|
||||
:title (get card "title")
|
||||
:has-calendar (get card "has_calendar")
|
||||
:has-market (get card "has_market")
|
||||
:pub-timestamp (get card "pub_timestamp")
|
||||
:feature-image (get card "feature_image")
|
||||
:excerpt (get card "excerpt")))
|
||||
(or cards (list)))
|
||||
(if (< page total-pages)
|
||||
(~sentinel-simple
|
||||
:id (str "sentinel-" page "-d")
|
||||
:next-url (str current-local-href
|
||||
(if (contains? current-local-href "?") "&" "?")
|
||||
"page=" (+ page 1)))
|
||||
(if (not (empty? (or cards (list))))
|
||||
(~end-of-results)
|
||||
(~blog-no-pages)))))
|
||||
;; Posts listing
|
||||
(let* ((grid-cls (if (= view "tile")
|
||||
"max-w-full px-3 py-3 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4"
|
||||
"max-w-full px-3 py-3 space-y-3"))
|
||||
(list-href current-local-href)
|
||||
(tile-href (str current-local-href
|
||||
(if (contains? current-local-href "?") "&" "?") "view=tile"))
|
||||
(list-cls (if (not (= view "tile"))
|
||||
"bg-stone-200 text-stone-800"
|
||||
"text-stone-400 hover:text-stone-600"))
|
||||
(tile-cls (if (= view "tile")
|
||||
"bg-stone-200 text-stone-800"
|
||||
"text-stone-400 hover:text-stone-600")))
|
||||
(~blog-main-panel-posts
|
||||
:tabs (~blog-content-type-tabs
|
||||
:posts-href posts-href :pages-href pages-href
|
||||
:hx-select hx-select :posts-cls posts-cls :pages-cls pages-cls)
|
||||
:toggle (~view-toggle
|
||||
:list-href list-href :tile-href tile-href :hx-select hx-select
|
||||
:list-cls list-cls :tile-cls tile-cls :storage-key "blog_view"
|
||||
:list-svg (~list-svg) :tile-svg (~tile-svg))
|
||||
:grid-cls grid-cls
|
||||
:cards (<>
|
||||
(map (lambda (card)
|
||||
(if (= view "tile")
|
||||
(~blog-card-tile
|
||||
:href (get card "href") :hx-select hx-select
|
||||
:feature-image (get card "feature_image")
|
||||
:title (get card "title") :is-draft (get card "is_draft")
|
||||
:publish-requested (get card "publish_requested")
|
||||
:status-timestamp (get card "status_timestamp")
|
||||
:excerpt (get card "excerpt")
|
||||
:tags (get card "tags") :authors (get card "authors"))
|
||||
(~blog-card
|
||||
:slug (get card "slug") :href (get card "href") :hx-select hx-select
|
||||
:title (get card "title") :feature-image (get card "feature_image")
|
||||
:excerpt (get card "excerpt") :is-draft (get card "is_draft")
|
||||
:publish-requested (get card "publish_requested")
|
||||
:status-timestamp (get card "status_timestamp")
|
||||
:has-like (get card "has_like") :liked (get card "liked")
|
||||
:like-url (get card "like_url") :csrf-token (get card "csrf_token")
|
||||
:tags (get card "tags") :authors (get card "authors")
|
||||
:widget (get card "widget"))))
|
||||
(or cards (list)))
|
||||
(~blog-index-sentinel
|
||||
:page page :total-pages total-pages
|
||||
:current-local-href current-local-href)))))))
|
||||
|
||||
;; Sentinel for blog index infinite scroll
|
||||
(defcomp ~blog-index-sentinel (&key page total-pages current-local-href)
|
||||
(when (< page total-pages)
|
||||
(let* ((next-url (str current-local-href "?page=" (+ page 1))))
|
||||
(~sentinel-desktop
|
||||
:id (str "sentinel-" page "-d")
|
||||
:next-url next-url
|
||||
:hyperscript "init if not me.dataset.retryMs then set me.dataset.retryMs to 1000 end on htmx:beforeRequest(event) add .hidden to .js-neterr in me remove .hidden from .js-loading in me remove .opacity-100 from me add .opacity-0 to me set trig to null if event.detail and event.detail.triggeringEvent then set trig to event.detail.triggeringEvent end if trig and trig.type is 'intersect' set scroller to the closest .js-grid-viewport if scroller is null then halt end if scroller.scrollTop < 20 then halt end end def backoff() set ms to me.dataset.retryMs if ms > 30000 then set ms to 30000 end add .hidden to .js-loading in me remove .hidden from .js-neterr in me remove .opacity-0 from me add .opacity-100 to me wait ms ms trigger sentinel:retry set ms to ms * 2 if ms > 30000 then set ms to 30000 end set me.dataset.retryMs to ms end on htmx:sendError call backoff() on htmx:responseError call backoff() on htmx:timeout call backoff()"))))
|
||||
|
||||
;; Blog index action buttons — replaces _action_buttons_sx
|
||||
(defcomp ~blog-index-actions (&key is-admin has-user hx-select draft-count drafts
|
||||
new-post-href new-page-href current-local-href)
|
||||
(~blog-action-buttons-wrapper
|
||||
:inner (<>
|
||||
(when is-admin
|
||||
(<>
|
||||
(~blog-action-button
|
||||
:href new-post-href :hx-select hx-select
|
||||
:btn-class "px-3 py-1 rounded bg-stone-700 text-white text-sm hover:bg-stone-800 transition-colors"
|
||||
:title "New Post" :icon-class "fa fa-plus mr-1" :label " New Post")
|
||||
(~blog-action-button
|
||||
:href new-page-href :hx-select hx-select
|
||||
:btn-class "px-3 py-1 rounded bg-blue-600 text-white text-sm hover:bg-blue-700 transition-colors"
|
||||
:title "New Page" :icon-class "fa fa-plus mr-1" :label " New Page")))
|
||||
(when (and has-user (or draft-count drafts))
|
||||
(if drafts
|
||||
(~blog-drafts-button
|
||||
:href current-local-href :hx-select hx-select
|
||||
:btn-class "px-3 py-1 rounded bg-stone-700 text-white text-sm hover:bg-stone-800 transition-colors"
|
||||
:title "Hide Drafts" :label " Drafts " :draft-count (str draft-count))
|
||||
(let* ((on-href (str current-local-href
|
||||
(if (contains? current-local-href "?") "&" "?") "drafts=1")))
|
||||
(~blog-drafts-button-amber
|
||||
:href on-href :hx-select hx-select
|
||||
:btn-class "px-3 py-1 rounded bg-amber-600 text-white text-sm hover:bg-amber-700 transition-colors"
|
||||
:title "Show Drafts" :label " Drafts " :draft-count (str draft-count))))))))
|
||||
|
||||
;; Tag groups filter — replaces _tag_groups_filter_sx
|
||||
(defcomp ~blog-index-tag-groups-filter (&key tag-groups is-any-group hx-select)
|
||||
(~blog-filter-nav
|
||||
:items (<>
|
||||
(~blog-filter-any-topic
|
||||
:cls (if is-any-group
|
||||
"bg-stone-900 text-white border-stone-900"
|
||||
"bg-white text-stone-600 border-stone-300 hover:bg-stone-50")
|
||||
:hx-select hx-select)
|
||||
(map (lambda (grp)
|
||||
(let* ((is-on (get grp "is_selected"))
|
||||
(cls (if is-on
|
||||
"bg-stone-900 text-white border-stone-900"
|
||||
"bg-white text-stone-600 border-stone-300 hover:bg-stone-50"))
|
||||
(fi (get grp "feature_image"))
|
||||
(colour (get grp "colour"))
|
||||
(name (get grp "name"))
|
||||
(icon (if fi
|
||||
(~blog-filter-group-icon-image :src fi :name name)
|
||||
(~blog-filter-group-icon-color
|
||||
:style (if colour
|
||||
(str "background-color: " colour "; color: white;")
|
||||
"background-color: #e7e5e4; color: #57534e;")
|
||||
:initial (slice (or name "?") 0 1)))))
|
||||
(~blog-filter-group-li
|
||||
:cls cls :hx-get (str "?group=" (get grp "slug") "&page=1")
|
||||
:hx-select hx-select :icon icon
|
||||
:name name :count (str (get grp "post_count")))))
|
||||
(or tag-groups (list))))))
|
||||
|
||||
;; Authors filter — replaces _authors_filter_sx
|
||||
(defcomp ~blog-index-authors-filter (&key authors is-any-author hx-select)
|
||||
(~blog-filter-nav
|
||||
:items (<>
|
||||
(~blog-filter-any-author
|
||||
:cls (if is-any-author
|
||||
"bg-stone-900 text-white border-stone-900"
|
||||
"bg-white text-stone-600 border-stone-300 hover:bg-stone-50")
|
||||
:hx-select hx-select)
|
||||
(map (lambda (a)
|
||||
(let* ((is-on (get a "is_selected"))
|
||||
(cls (if is-on
|
||||
"bg-stone-900 text-white border-stone-900"
|
||||
"bg-white text-stone-600 border-stone-300 hover:bg-stone-50"))
|
||||
(img (get a "profile_image")))
|
||||
(~blog-filter-author-li
|
||||
:cls cls :hx-get (str "?author=" (get a "slug") "&page=1")
|
||||
:hx-select hx-select
|
||||
:icon (when img (~blog-filter-author-icon :src img :name (get a "name")))
|
||||
:name (get a "name")
|
||||
:count (str (get a "published_post_count")))))
|
||||
(or authors (list))))))
|
||||
|
||||
;; Blog index aside — replaces _blog_aside_sx
|
||||
(defcomp ~blog-index-aside-content (&key is-admin has-user hx-select draft-count drafts
|
||||
new-post-href new-page-href current-local-href
|
||||
tag-groups authors is-any-group is-any-author)
|
||||
(~blog-aside
|
||||
:search (~search-desktop)
|
||||
:action-buttons (~blog-index-actions
|
||||
:is-admin is-admin :has-user has-user :hx-select hx-select
|
||||
:draft-count draft-count :drafts drafts
|
||||
:new-post-href new-post-href :new-page-href new-page-href
|
||||
:current-local-href current-local-href)
|
||||
:tag-groups-filter (~blog-index-tag-groups-filter
|
||||
:tag-groups tag-groups :is-any-group is-any-group :hx-select hx-select)
|
||||
:authors-filter (~blog-index-authors-filter
|
||||
:authors authors :is-any-author is-any-author :hx-select hx-select)))
|
||||
|
||||
;; Blog index mobile filter — replaces _blog_filter_sx
|
||||
(defcomp ~blog-index-filter-content (&key is-admin has-user hx-select draft-count drafts
|
||||
new-post-href new-page-href current-local-href
|
||||
tag-groups authors is-any-group is-any-author
|
||||
tg-summary au-summary)
|
||||
(~mobile-filter
|
||||
:filter-summary (<>
|
||||
(~search-mobile)
|
||||
(when (not (= tg-summary ""))
|
||||
(~blog-filter-summary :text tg-summary))
|
||||
(when (not (= au-summary ""))
|
||||
(~blog-filter-summary :text au-summary)))
|
||||
:action-buttons (~blog-index-actions
|
||||
:is-admin is-admin :has-user has-user :hx-select hx-select
|
||||
:draft-count draft-count :drafts drafts
|
||||
:new-post-href new-post-href :new-page-href new-page-href
|
||||
:current-local-href current-local-href)
|
||||
:filter-details (<>
|
||||
(~blog-index-tag-groups-filter
|
||||
:tag-groups tag-groups :is-any-group is-any-group :hx-select hx-select)
|
||||
(~blog-index-authors-filter
|
||||
:authors authors :is-any-author is-any-author :hx-select hx-select))))
|
||||
|
||||
Reference in New Issue
Block a user