Merge branch 'worktree-sx-layout-conversion' into macros
# Conflicts: # blog/sxc/pages/layouts.py # cart/sxc/pages/layouts.py # events/sxc/pages/helpers.py # events/sxc/pages/layouts.py # market/sxc/pages/layouts.py # sx/sxc/pages/layouts.py
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
;; Blog layout defcomps — fully self-contained via IO primitives.
|
||||
;; Registered via register_sx_layout in __init__.py.
|
||||
|
||||
;; --- Blog header (invisible row for blog-header-child swap target) ---
|
||||
|
||||
@@ -7,28 +8,161 @@
|
||||
:link-label-content (div)
|
||||
:child-id "blog-header-child" :oob oob))
|
||||
|
||||
;; --- Blog layout (root + blog header) ---
|
||||
;; --- Auto-fetching settings header macro ---
|
||||
|
||||
(defmacro ~blog-settings-header-auto (oob)
|
||||
(quasiquote
|
||||
(~menu-row-sx :id "root-settings-row" :level 1
|
||||
:link-href (url-for "settings.defpage_settings_home")
|
||||
:link-label-content (~blog-admin-label)
|
||||
:nav (~blog-settings-nav)
|
||||
:child-id "root-settings-header-child"
|
||||
:oob (unquote oob))))
|
||||
|
||||
;; --- Auto-fetching sub-settings header macro ---
|
||||
|
||||
(defmacro ~blog-sub-settings-header-auto (row-id child-id endpoint icon label oob)
|
||||
(quasiquote
|
||||
(~menu-row-sx :id (unquote row-id) :level 2
|
||||
:link-href (url-for (unquote endpoint))
|
||||
:link-label-content (~blog-sub-settings-label
|
||||
:icon (str "fa fa-" (unquote icon))
|
||||
:label (unquote label))
|
||||
:child-id (unquote child-id)
|
||||
:oob (unquote oob))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Blog layout (root + blog header)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~blog-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~blog-header)))
|
||||
|
||||
;; --- Settings layout (root + settings header) ---
|
||||
(defcomp ~blog-layout-oob ()
|
||||
(<> (~blog-header :oob true)
|
||||
(~clear-oob-div :id "blog-header-child")
|
||||
(~root-header-auto true)))
|
||||
|
||||
(defcomp ~settings-layout-full (&key settings-header)
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Settings layout (root + settings header)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~blog-settings-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
settings-header))
|
||||
(~blog-settings-header-auto)))
|
||||
|
||||
;; --- Sub-settings layout (root + settings + sub row) ---
|
||||
(defcomp ~blog-settings-layout-oob ()
|
||||
(<> (~blog-settings-header-auto true)
|
||||
(~clear-oob-div :id "root-settings-header-child")
|
||||
(~root-header-auto true)))
|
||||
|
||||
(defcomp ~sub-settings-layout-full (&key settings-header sub-header)
|
||||
(defcomp ~blog-settings-layout-mobile ()
|
||||
(~blog-settings-nav))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Cache layout (root + settings + cache sub-header)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~blog-cache-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
settings-header sub-header))
|
||||
(~blog-settings-header-auto)
|
||||
(~blog-sub-settings-header-auto
|
||||
"cache-row" "cache-header-child"
|
||||
"settings.defpage_cache_page" "refresh" "Cache")))
|
||||
|
||||
(defcomp ~sub-settings-layout-oob (&key settings-header-oob sub-header-oob)
|
||||
(<> settings-header-oob sub-header-oob))
|
||||
(defcomp ~blog-cache-layout-oob ()
|
||||
(<> (~blog-sub-settings-header-auto
|
||||
"cache-row" "cache-header-child"
|
||||
"settings.defpage_cache_page" "refresh" "Cache" true)
|
||||
(~clear-oob-div :id "cache-header-child")
|
||||
(~blog-settings-header-auto true)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; --- Settings nav links — uses (select-colours) IO primitive ---
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Snippets layout (root + settings + snippets sub-header)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~blog-snippets-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~blog-settings-header-auto)
|
||||
(~blog-sub-settings-header-auto
|
||||
"snippets-row" "snippets-header-child"
|
||||
"snippets.defpage_snippets_page" "puzzle-piece" "Snippets")))
|
||||
|
||||
(defcomp ~blog-snippets-layout-oob ()
|
||||
(<> (~blog-sub-settings-header-auto
|
||||
"snippets-row" "snippets-header-child"
|
||||
"snippets.defpage_snippets_page" "puzzle-piece" "Snippets" true)
|
||||
(~clear-oob-div :id "snippets-header-child")
|
||||
(~blog-settings-header-auto true)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Menu Items layout (root + settings + menu-items sub-header)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~blog-menu-items-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~blog-settings-header-auto)
|
||||
(~blog-sub-settings-header-auto
|
||||
"menu_items-row" "menu_items-header-child"
|
||||
"menu_items.defpage_menu_items_page" "bars" "Menu Items")))
|
||||
|
||||
(defcomp ~blog-menu-items-layout-oob ()
|
||||
(<> (~blog-sub-settings-header-auto
|
||||
"menu_items-row" "menu_items-header-child"
|
||||
"menu_items.defpage_menu_items_page" "bars" "Menu Items" true)
|
||||
(~clear-oob-div :id "menu_items-header-child")
|
||||
(~blog-settings-header-auto true)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Tag Groups layout (root + settings + tag-groups sub-header)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~blog-tag-groups-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~blog-settings-header-auto)
|
||||
(~blog-sub-settings-header-auto
|
||||
"tag-groups-row" "tag-groups-header-child"
|
||||
"blog.tag_groups_admin.defpage_tag_groups_page" "tags" "Tag Groups")))
|
||||
|
||||
(defcomp ~blog-tag-groups-layout-oob ()
|
||||
(<> (~blog-sub-settings-header-auto
|
||||
"tag-groups-row" "tag-groups-header-child"
|
||||
"blog.tag_groups_admin.defpage_tag_groups_page" "tags" "Tag Groups" true)
|
||||
(~clear-oob-div :id "tag-groups-header-child")
|
||||
(~blog-settings-header-auto true)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Tag Group Edit layout (root + settings + tag-groups sub-header with id)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~blog-tag-group-edit-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~blog-settings-header-auto)
|
||||
(~menu-row-sx :id "tag-groups-row" :level 2
|
||||
:link-href (url-for "blog.tag_groups_admin.defpage_tag_group_edit"
|
||||
:id (request-view-args "id"))
|
||||
:link-label-content (~blog-sub-settings-label
|
||||
:icon "fa fa-tags" :label "Tag Groups")
|
||||
:child-id "tag-groups-header-child")))
|
||||
|
||||
(defcomp ~blog-tag-group-edit-layout-oob ()
|
||||
(<> (~menu-row-sx :id "tag-groups-row" :level 2
|
||||
:link-href (url-for "blog.tag_groups_admin.defpage_tag_group_edit"
|
||||
:id (request-view-args "id"))
|
||||
:link-label-content (~blog-sub-settings-label
|
||||
:icon "fa fa-tags" :label "Tag Groups")
|
||||
:child-id "tag-groups-header-child"
|
||||
:oob true)
|
||||
(~clear-oob-div :id "tag-groups-header-child")
|
||||
(~blog-settings-header-auto true)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; --- Settings nav links — uses IO primitives ---
|
||||
|
||||
(defcomp ~blog-settings-nav ()
|
||||
(let* ((sc (select-colours))
|
||||
|
||||
@@ -1,196 +1,19 @@
|
||||
"""Blog layout functions for defpage rendering."""
|
||||
"""Blog layout registration — all layouts delegate to .sx defcomps."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Header helpers (moved from sx_components — thin sx_call wrappers)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _settings_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
from shared.sx.helpers import sx_call
|
||||
from quart import url_for as qurl
|
||||
|
||||
settings_href = qurl("settings.defpage_settings_home")
|
||||
label_sx = sx_call("blog-admin-label")
|
||||
nav_sx = _settings_nav_sx(ctx)
|
||||
|
||||
return sx_call("menu-row-sx",
|
||||
id="root-settings-row", level=1,
|
||||
link_href=settings_href,
|
||||
link_label_content=label_sx,
|
||||
nav=nav_sx or None,
|
||||
child_id="root-settings-header-child", oob=oob)
|
||||
|
||||
|
||||
def _settings_nav_sx(ctx: dict) -> str:
|
||||
from shared.sx.helpers import sx_call
|
||||
return sx_call("blog-settings-nav")
|
||||
|
||||
|
||||
def _sub_settings_header_sx(row_id: str, child_id: str, href: str,
|
||||
icon: str, label: str, ctx: dict,
|
||||
*, oob: bool = False, nav_sx: str = "") -> str:
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
label_sx = sx_call("blog-sub-settings-label",
|
||||
icon=f"fa fa-{icon}", label=label)
|
||||
return sx_call("menu-row-sx",
|
||||
id=row_id, level=2,
|
||||
link_href=href,
|
||||
link_label_content=label_sx,
|
||||
nav=SxExpr(nav_sx) if nav_sx else None,
|
||||
child_id=child_id, oob=oob)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Layouts
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _register_blog_layouts() -> None:
|
||||
from shared.sx.layouts import register_custom_layout
|
||||
register_custom_layout("blog", _blog_full, _blog_oob)
|
||||
register_custom_layout("blog-settings", _settings_full, _settings_oob,
|
||||
mobile_fn=_settings_mobile)
|
||||
register_custom_layout("blog-cache", _cache_full, _cache_oob)
|
||||
register_custom_layout("blog-snippets", _snippets_full, _snippets_oob)
|
||||
register_custom_layout("blog-menu-items", _menu_items_full, _menu_items_oob)
|
||||
register_custom_layout("blog-tag-groups", _tag_groups_full, _tag_groups_oob)
|
||||
register_custom_layout("blog-tag-group-edit",
|
||||
_tag_group_edit_full, _tag_group_edit_oob)
|
||||
|
||||
|
||||
# --- Blog layout (root + blog header) ---
|
||||
|
||||
async def _blog_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
return await render_to_sx_with_env("blog-layout-full", {})
|
||||
|
||||
|
||||
async def _blog_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
rows = await render_to_sx_with_env("blog-layout-full", {})
|
||||
return await oob_header_sx("root-header-child", "blog-header-child", rows)
|
||||
|
||||
|
||||
# --- Settings layout (root + settings header) ---
|
||||
|
||||
async def _settings_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
return await render_to_sx_with_env("settings-layout-full", {},
|
||||
settings_header=_settings_header_sx(ctx))
|
||||
|
||||
|
||||
async def _settings_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
rows = await render_to_sx_with_env("settings-layout-full", {},
|
||||
settings_header=_settings_header_sx(ctx))
|
||||
return await oob_header_sx("root-header-child", "root-settings-header-child", rows)
|
||||
|
||||
|
||||
def _settings_mobile(ctx: dict, **kw: Any) -> str:
|
||||
return _settings_nav_sx(ctx)
|
||||
|
||||
|
||||
# --- Sub-settings helpers ---
|
||||
|
||||
async def _sub_settings_full(ctx: dict, row_id: str, child_id: str,
|
||||
endpoint: str, icon: str, label: str) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
from quart import url_for as qurl
|
||||
return await render_to_sx_with_env("sub-settings-layout-full", {},
|
||||
settings_header=_settings_header_sx(ctx),
|
||||
sub_header=_sub_settings_header_sx(
|
||||
row_id, child_id, qurl(endpoint), icon, label, ctx))
|
||||
|
||||
|
||||
async def _sub_settings_oob(ctx: dict, row_id: str, child_id: str,
|
||||
endpoint: str, icon: str, label: str) -> str:
|
||||
from shared.sx.helpers import oob_header_sx, sx_call
|
||||
from quart import url_for as qurl
|
||||
settings_hdr_oob = _settings_header_sx(ctx, oob=True)
|
||||
sub_hdr = _sub_settings_header_sx(
|
||||
row_id, child_id, qurl(endpoint), icon, label, ctx)
|
||||
sub_oob = await oob_header_sx("root-settings-header-child", child_id, sub_hdr)
|
||||
return sx_call("sub-settings-layout-oob",
|
||||
settings_header_oob=settings_hdr_oob,
|
||||
sub_header_oob=sub_oob)
|
||||
|
||||
|
||||
# --- Cache ---
|
||||
|
||||
async def _cache_full(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_full(ctx, "cache-row", "cache-header-child",
|
||||
"defpage_cache_page", "refresh", "Cache")
|
||||
|
||||
|
||||
async def _cache_oob(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_oob(ctx, "cache-row", "cache-header-child",
|
||||
"defpage_cache_page", "refresh", "Cache")
|
||||
|
||||
|
||||
# --- Snippets ---
|
||||
|
||||
async def _snippets_full(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_full(ctx, "snippets-row", "snippets-header-child",
|
||||
"defpage_snippets_page", "puzzle-piece", "Snippets")
|
||||
|
||||
|
||||
async def _snippets_oob(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_oob(ctx, "snippets-row", "snippets-header-child",
|
||||
"defpage_snippets_page", "puzzle-piece", "Snippets")
|
||||
|
||||
|
||||
# --- Menu Items ---
|
||||
|
||||
async def _menu_items_full(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_full(ctx, "menu_items-row", "menu_items-header-child",
|
||||
"defpage_menu_items_page", "bars", "Menu Items")
|
||||
|
||||
|
||||
async def _menu_items_oob(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_oob(ctx, "menu_items-row", "menu_items-header-child",
|
||||
"defpage_menu_items_page", "bars", "Menu Items")
|
||||
|
||||
|
||||
# --- Tag Groups ---
|
||||
|
||||
async def _tag_groups_full(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_full(ctx, "tag-groups-row", "tag-groups-header-child",
|
||||
"defpage_tag_groups_page", "tags", "Tag Groups")
|
||||
|
||||
|
||||
async def _tag_groups_oob(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_oob(ctx, "tag-groups-row", "tag-groups-header-child",
|
||||
"defpage_tag_groups_page", "tags", "Tag Groups")
|
||||
|
||||
|
||||
# --- Tag Group Edit ---
|
||||
|
||||
async def _tag_group_edit_full(ctx: dict, **kw: Any) -> str:
|
||||
from quart import request, url_for as qurl
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
g_id = (request.view_args or {}).get("id")
|
||||
return await render_to_sx_with_env("sub-settings-layout-full", {},
|
||||
settings_header=_settings_header_sx(ctx),
|
||||
sub_header=_sub_settings_header_sx(
|
||||
"tag-groups-row", "tag-groups-header-child",
|
||||
qurl("defpage_tag_group_edit", id=g_id),
|
||||
"tags", "Tag Groups", ctx))
|
||||
|
||||
|
||||
async def _tag_group_edit_oob(ctx: dict, **kw: Any) -> str:
|
||||
from quart import request, url_for as qurl
|
||||
from shared.sx.helpers import oob_header_sx, sx_call
|
||||
g_id = (request.view_args or {}).get("id")
|
||||
settings_hdr_oob = _settings_header_sx(ctx, oob=True)
|
||||
sub_hdr = _sub_settings_header_sx(
|
||||
"tag-groups-row", "tag-groups-header-child",
|
||||
qurl("defpage_tag_group_edit", id=g_id),
|
||||
"tags", "Tag Groups", ctx)
|
||||
sub_oob = await oob_header_sx("root-settings-header-child", "tag-groups-header-child", sub_hdr)
|
||||
return sx_call("sub-settings-layout-oob",
|
||||
settings_header_oob=settings_hdr_oob,
|
||||
sub_header_oob=sub_oob)
|
||||
from shared.sx.layouts import register_sx_layout
|
||||
register_sx_layout("blog", "blog-layout-full", "blog-layout-oob")
|
||||
register_sx_layout("blog-settings", "blog-settings-layout-full",
|
||||
"blog-settings-layout-oob", "blog-settings-layout-mobile")
|
||||
register_sx_layout("blog-cache", "blog-cache-layout-full",
|
||||
"blog-cache-layout-oob")
|
||||
register_sx_layout("blog-snippets", "blog-snippets-layout-full",
|
||||
"blog-snippets-layout-oob")
|
||||
register_sx_layout("blog-menu-items", "blog-menu-items-layout-full",
|
||||
"blog-menu-items-layout-oob")
|
||||
register_sx_layout("blog-tag-groups", "blog-tag-groups-layout-full",
|
||||
"blog-tag-groups-layout-oob")
|
||||
register_sx_layout("blog-tag-group-edit", "blog-tag-group-edit-layout-full",
|
||||
"blog-tag-group-edit-layout-oob")
|
||||
|
||||
@@ -172,6 +172,45 @@ class CartPageService:
|
||||
"summary": summary,
|
||||
}
|
||||
|
||||
async def admin_data(self, session, **kw):
|
||||
"""Populate post context for cart-admin layout headers."""
|
||||
from quart import g
|
||||
from shared.infrastructure.fragments import fetch_fragments
|
||||
|
||||
post = g.page_post
|
||||
slug = post.slug if post else ""
|
||||
post_id = post.id if post else None
|
||||
|
||||
# Fetch container_nav for post header
|
||||
container_nav = ""
|
||||
if post_id:
|
||||
nav_params = {
|
||||
"container_type": "page",
|
||||
"container_id": str(post_id),
|
||||
"post_slug": slug,
|
||||
}
|
||||
events_nav, market_nav = await fetch_fragments([
|
||||
("events", "container-nav", nav_params),
|
||||
("market", "container-nav", nav_params),
|
||||
], required=False)
|
||||
container_nav = events_nav + market_nav
|
||||
|
||||
return {
|
||||
"post": {
|
||||
"id": post_id,
|
||||
"slug": slug,
|
||||
"title": (post.title if post else "")[:160],
|
||||
"feature_image": getattr(post, "feature_image", None),
|
||||
},
|
||||
"container_nav": container_nav,
|
||||
}
|
||||
|
||||
async def payments_admin_data(self, session, **kw):
|
||||
"""Admin data + payments data combined for cart-payments page."""
|
||||
admin = await self.admin_data(session)
|
||||
payments = await self.payments_data(session)
|
||||
return {**admin, **payments}
|
||||
|
||||
async def payments_data(self, session, **kw):
|
||||
from shared.sx.page import get_template_context
|
||||
|
||||
|
||||
@@ -1,25 +1,78 @@
|
||||
;; Cart layout defcomps — fully self-contained via IO primitives.
|
||||
;; Registered via register_sx_layout in __init__.py.
|
||||
|
||||
;; --- cart-page layout: root + cart row + page-cart row ---
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Auto-fetching cart page header macros
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~cart-page-layout-full (&key cart-row page-cart-row)
|
||||
(defmacro ~cart-page-header-auto (oob)
|
||||
"Cart page header: cart-row + page-cart-row using (cart-page-ctx)."
|
||||
(quasiquote
|
||||
(let ((__cpctx (cart-page-ctx)))
|
||||
(<>
|
||||
(~menu-row-sx :id "cart-row" :level 1 :colour "sky"
|
||||
:link-href (get __cpctx "cart-url")
|
||||
:link-label "cart" :icon "fa fa-shopping-cart"
|
||||
:child-id "cart-header-child")
|
||||
(~header-child-sx :id "cart-header-child"
|
||||
:inner (~menu-row-sx :id "page-cart-row" :level 2 :colour "sky"
|
||||
:link-href (get __cpctx "page-cart-url")
|
||||
:link-label-content (~cart-page-label
|
||||
:feature-image (get __cpctx "feature-image")
|
||||
:title (get __cpctx "title"))
|
||||
:nav (~cart-all-carts-link :href (get __cpctx "cart-url"))
|
||||
:oob (unquote oob)))))))
|
||||
|
||||
(defmacro ~cart-page-header-oob ()
|
||||
"Cart page OOB: individual oob rows."
|
||||
(quasiquote
|
||||
(let ((__cpctx (cart-page-ctx)))
|
||||
(<>
|
||||
(~menu-row-sx :id "page-cart-row" :level 2 :colour "sky"
|
||||
:link-href (get __cpctx "page-cart-url")
|
||||
:link-label-content (~cart-page-label
|
||||
:feature-image (get __cpctx "feature-image")
|
||||
:title (get __cpctx "title"))
|
||||
:nav (~cart-all-carts-link :href (get __cpctx "cart-url"))
|
||||
:oob true)
|
||||
(~menu-row-sx :id "cart-row" :level 1 :colour "sky"
|
||||
:link-href (get __cpctx "cart-url")
|
||||
:link-label "cart" :icon "fa fa-shopping-cart"
|
||||
:child-id "cart-header-child"
|
||||
:oob true)))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; cart-page layout: root + cart row + page-cart row
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~cart-page-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx
|
||||
:inner (<> cart-row
|
||||
(~header-child-sx :id "cart-header-child" :inner page-cart-row)))))
|
||||
:inner (~cart-page-header-auto))))
|
||||
|
||||
(defcomp ~cart-page-layout-oob (&key root-header-oob cart-row-oob page-cart-row)
|
||||
(<> (~oob-header-sx :parent-id "cart-header-child" :row page-cart-row)
|
||||
cart-row-oob
|
||||
root-header-oob))
|
||||
(defcomp ~cart-page-layout-oob ()
|
||||
(<> (~cart-page-header-oob)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; --- cart-admin layout: root + post header + admin header ---
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; cart-admin layout: root + post header + admin header
|
||||
;; Uses (post-header-ctx) — requires :data handler to populate g._defpage_ctx
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~cart-admin-layout-full (&key post-header admin-header)
|
||||
(defcomp ~cart-admin-layout-full (&key selected)
|
||||
(<> (~root-header-auto)
|
||||
post-header admin-header))
|
||||
(~header-child-sx
|
||||
:inner (~post-header-auto nil))))
|
||||
|
||||
;; --- orders-within-cart: root + auth-simple + orders ---
|
||||
(defcomp ~cart-admin-layout-oob (&key selected)
|
||||
(<> (~post-header-auto true)
|
||||
(~oob-header-sx :parent-id "post-header-child"
|
||||
:row (~post-admin-header-auto nil selected))
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; orders-within-cart: root + auth-simple + orders
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~cart-orders-layout-full (&key list-url)
|
||||
(<> (~root-header-auto)
|
||||
@@ -35,7 +88,9 @@
|
||||
:row (~orders-header-row :list-url list-url))
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; --- order-detail-within-cart: root + auth-simple + orders + order ---
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; order-detail-within-cart: root + auth-simple + orders + order
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~cart-order-detail-layout-full (&key list-url detail-url order-label)
|
||||
(<> (~root-header-auto)
|
||||
|
||||
@@ -32,12 +32,13 @@
|
||||
:path "/<page_slug>/admin/"
|
||||
:auth :admin
|
||||
:layout :cart-admin
|
||||
:data (service "cart-page" "admin-data")
|
||||
:content (~cart-admin-content))
|
||||
|
||||
(defpage cart-payments
|
||||
:path "/<page_slug>/admin/payments/"
|
||||
:auth :admin
|
||||
:layout (:cart-admin :selected "payments")
|
||||
:data (service "cart-page" "payments-data")
|
||||
:data (service "cart-page" "payments-admin-data")
|
||||
:content (~cart-payments-content
|
||||
:page-config page-config))
|
||||
|
||||
@@ -1,133 +1,8 @@
|
||||
"""Cart layout registration and header builders."""
|
||||
"""Cart layout registration — all layouts delegate to .sx defcomps."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
def _register_cart_layouts() -> None:
|
||||
from shared.sx.layouts import register_custom_layout
|
||||
register_custom_layout("cart-page", _cart_page_full, _cart_page_oob)
|
||||
register_custom_layout("cart-admin", _cart_admin_full, _cart_admin_oob)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Header helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _ensure_post_ctx(ctx: dict, page_post: Any) -> dict:
|
||||
"""Ensure ctx has a 'post' dict from page_post DTO."""
|
||||
if ctx.get("post") or not page_post:
|
||||
return ctx
|
||||
return {**ctx, "post": {
|
||||
"id": getattr(page_post, "id", None),
|
||||
"slug": getattr(page_post, "slug", ""),
|
||||
"title": getattr(page_post, "title", ""),
|
||||
"feature_image": getattr(page_post, "feature_image", None),
|
||||
}}
|
||||
|
||||
|
||||
async def _ensure_container_nav(ctx: dict) -> dict:
|
||||
"""Fetch container_nav if not already present."""
|
||||
if ctx.get("container_nav"):
|
||||
return ctx
|
||||
post = ctx.get("post") or {}
|
||||
post_id = post.get("id")
|
||||
if not post_id:
|
||||
return ctx
|
||||
slug = post.get("slug", "")
|
||||
from shared.infrastructure.fragments import fetch_fragments
|
||||
nav_params = {
|
||||
"container_type": "page",
|
||||
"container_id": str(post_id),
|
||||
"post_slug": slug,
|
||||
}
|
||||
events_nav, market_nav = await fetch_fragments([
|
||||
("events", "container-nav", nav_params),
|
||||
("market", "container-nav", nav_params),
|
||||
], required=False)
|
||||
return {**ctx, "container_nav": events_nav + market_nav}
|
||||
|
||||
|
||||
async def _post_header_sx(ctx: dict, page_post: Any, *, oob: bool = False) -> str:
|
||||
from shared.sx.helpers import post_header_sx as _shared_post_header_sx
|
||||
ctx = _ensure_post_ctx(ctx, page_post)
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
return await _shared_post_header_sx(ctx, oob=oob)
|
||||
|
||||
|
||||
def _cart_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
from shared.sx.helpers import sx_call, call_url
|
||||
return sx_call(
|
||||
"menu-row-sx",
|
||||
id="cart-row", level=1, colour="sky",
|
||||
link_href=call_url(ctx, "cart_url", "/"),
|
||||
link_label="cart", icon="fa fa-shopping-cart",
|
||||
child_id="cart-header-child", oob=oob,
|
||||
)
|
||||
|
||||
|
||||
def _page_cart_header_sx(ctx: dict, page_post: Any, *, oob: bool = False) -> str:
|
||||
from shared.sx.helpers import sx_call, call_url
|
||||
slug = page_post.slug if page_post else ""
|
||||
title = ((page_post.title if page_post else None) or "")[:160]
|
||||
label_sx = sx_call("cart-page-label",
|
||||
feature_image=page_post.feature_image if page_post else None,
|
||||
title=title)
|
||||
nav_sx = sx_call("cart-all-carts-link", href=call_url(ctx, "cart_url", "/"))
|
||||
return sx_call(
|
||||
"menu-row-sx",
|
||||
id="page-cart-row", level=2, colour="sky",
|
||||
link_href=call_url(ctx, "cart_url", f"/{slug}/"),
|
||||
link_label_content=label_sx,
|
||||
nav=nav_sx, oob=oob,
|
||||
)
|
||||
|
||||
|
||||
async def _cart_page_admin_header_sx(ctx: dict, page_post: Any, *, oob: bool = False,
|
||||
selected: str = "") -> str:
|
||||
from shared.sx.helpers import post_admin_header_sx
|
||||
slug = page_post.slug if page_post else ""
|
||||
ctx = _ensure_post_ctx(ctx, page_post)
|
||||
return await post_admin_header_sx(ctx, slug, oob=oob, selected=selected)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Layout functions
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _cart_page_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
page_post = ctx.get("page_post")
|
||||
env = {}
|
||||
return await render_to_sx_with_env("cart-page-layout-full", env,
|
||||
cart_row=_cart_header_sx(ctx),
|
||||
page_cart_row=_page_cart_header_sx(ctx, page_post),
|
||||
)
|
||||
|
||||
|
||||
async def _cart_page_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, root_header_sx
|
||||
page_post = ctx.get("page_post")
|
||||
env = {}
|
||||
return await render_to_sx_with_env("cart-page-layout-oob", env,
|
||||
root_header_oob=await root_header_sx(ctx, oob=True),
|
||||
cart_row_oob=_cart_header_sx(ctx, oob=True),
|
||||
page_cart_row=_page_cart_header_sx(ctx, page_post),
|
||||
)
|
||||
|
||||
|
||||
async def _cart_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
page_post = ctx.get("page_post")
|
||||
selected = kw.get("selected", "")
|
||||
env = {}
|
||||
return await render_to_sx_with_env("cart-admin-layout-full", env,
|
||||
post_header=await _post_header_sx(ctx, page_post),
|
||||
admin_header=await _cart_page_admin_header_sx(ctx, page_post, selected=selected),
|
||||
)
|
||||
|
||||
|
||||
async def _cart_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
page_post = ctx.get("page_post")
|
||||
selected = kw.get("selected", "")
|
||||
return await _cart_page_admin_header_sx(ctx, page_post, oob=True, selected=selected)
|
||||
from shared.sx.layouts import register_sx_layout
|
||||
register_sx_layout("cart-page", "cart-page-layout-full", "cart-page-layout-oob")
|
||||
register_sx_layout("cart-admin", "cart-admin-layout-full", "cart-admin-layout-oob")
|
||||
|
||||
@@ -1,96 +1,405 @@
|
||||
;; Events layout defcomps — root header via ~root-header-auto,
|
||||
;; events-specific headers passed as &key params.
|
||||
;; Events layout defcomps — fully self-contained via IO primitives.
|
||||
;; Registered via register_sx_layout in helpers.py.
|
||||
|
||||
;; --- Calendar admin layout: root + post + child(admin + cal + cal-admin) ---
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Auto-fetching header macros — calendar, day, entry, slot, tickets
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~events-cal-admin-layout-full (&key post-header admin-header
|
||||
calendar-header calendar-admin-header)
|
||||
(defmacro ~events-calendar-header-auto (oob)
|
||||
"Calendar header row using (events-calendar-ctx)."
|
||||
(quasiquote
|
||||
(let ((__cal (events-calendar-ctx))
|
||||
(__sc (select-colours)))
|
||||
(when (get __cal "slug")
|
||||
(~menu-row-sx :id "calendar-row" :level 3
|
||||
:link-href (url-for "calendar.get"
|
||||
:calendar-slug (get __cal "slug"))
|
||||
:link-label-content (~events-calendar-label
|
||||
:name (get __cal "name")
|
||||
:description (get __cal "description"))
|
||||
:nav (<>
|
||||
(~nav-link :href (url-for "defpage_slots_listing"
|
||||
:calendar-slug (get __cal "slug"))
|
||||
:icon "fa fa-clock" :label "Slots"
|
||||
:select-colours __sc)
|
||||
(let ((__rights (app-rights)))
|
||||
(when (get __rights "admin")
|
||||
(~nav-link :href (url-for "defpage_calendar_admin"
|
||||
:calendar-slug (get __cal "slug"))
|
||||
:icon "fa fa-cog"
|
||||
:select-colours __sc))))
|
||||
:child-id "calendar-header-child"
|
||||
:oob (unquote oob))))))
|
||||
|
||||
(defmacro ~events-calendar-admin-header-auto (oob)
|
||||
"Calendar admin header row."
|
||||
(quasiquote
|
||||
(let ((__cal (events-calendar-ctx))
|
||||
(__sc (select-colours)))
|
||||
(when (get __cal "slug")
|
||||
(~menu-row-sx :id "calendar-admin-row" :level 4
|
||||
:link-label "admin" :icon "fa fa-cog"
|
||||
:nav (<>
|
||||
(~nav-link :href (url-for "defpage_slots_listing"
|
||||
:calendar-slug (get __cal "slug"))
|
||||
:label "slots" :select-colours __sc)
|
||||
(~nav-link :href (url-for "calendar.admin.calendar_description_edit"
|
||||
:calendar-slug (get __cal "slug"))
|
||||
:label "description" :select-colours __sc))
|
||||
:child-id "calendar-admin-header-child"
|
||||
:oob (unquote oob))))))
|
||||
|
||||
(defmacro ~events-day-header-auto (oob)
|
||||
"Day header row using (events-day-ctx)."
|
||||
(quasiquote
|
||||
(let ((__day (events-day-ctx))
|
||||
(__cal (events-calendar-ctx)))
|
||||
(when (get __day "date-str")
|
||||
(~menu-row-sx :id "day-row" :level 4
|
||||
:link-href (url-for "calendar.day.show_day"
|
||||
:calendar-slug (get __cal "slug")
|
||||
:year (get __day "year")
|
||||
:month (get __day "month")
|
||||
:day (get __day "day"))
|
||||
:link-label-content (~events-day-label
|
||||
:date-str (get __day "date-str"))
|
||||
:nav (get __day "nav")
|
||||
:child-id "day-header-child"
|
||||
:oob (unquote oob))))))
|
||||
|
||||
(defmacro ~events-day-admin-header-auto (oob)
|
||||
"Day admin header row."
|
||||
(quasiquote
|
||||
(let ((__day (events-day-ctx))
|
||||
(__cal (events-calendar-ctx)))
|
||||
(when (get __day "date-str")
|
||||
(~menu-row-sx :id "day-admin-row" :level 5
|
||||
:link-href (url-for "defpage_day_admin"
|
||||
:calendar-slug (get __cal "slug")
|
||||
:year (get __day "year")
|
||||
:month (get __day "month")
|
||||
:day (get __day "day"))
|
||||
:link-label "admin" :icon "fa fa-cog"
|
||||
:child-id "day-admin-header-child"
|
||||
:oob (unquote oob))))))
|
||||
|
||||
(defmacro ~events-entry-header-auto (oob)
|
||||
"Entry header row using (events-entry-ctx)."
|
||||
(quasiquote
|
||||
(let ((__ectx (events-entry-ctx)))
|
||||
(when (get __ectx "id")
|
||||
(~menu-row-sx :id "entry-row" :level 5
|
||||
:link-href (get __ectx "link-href")
|
||||
:link-label-content (~events-entry-label
|
||||
:entry-id (get __ectx "id")
|
||||
:title (~events-entry-title :name (get __ectx "name"))
|
||||
:times (~events-entry-times :time-str (get __ectx "time-str")))
|
||||
:nav (get __ectx "nav")
|
||||
:child-id "entry-header-child"
|
||||
:oob (unquote oob))))))
|
||||
|
||||
(defmacro ~events-entry-admin-header-auto (oob)
|
||||
"Entry admin header row."
|
||||
(quasiquote
|
||||
(let ((__ectx (events-entry-ctx)))
|
||||
(when (get __ectx "id")
|
||||
(~menu-row-sx :id "entry-admin-row" :level 6
|
||||
:link-href (get __ectx "admin-href")
|
||||
:link-label "admin" :icon "fa fa-cog"
|
||||
:nav (when (get __ectx "is-admin")
|
||||
(~nav-link :href (get __ectx "ticket-types-href")
|
||||
:label "ticket_types"
|
||||
:select-colours (get __ectx "select-colours")))
|
||||
:child-id "entry-admin-header-child"
|
||||
:oob (unquote oob))))))
|
||||
|
||||
(defmacro ~events-slot-header-auto (oob)
|
||||
"Slot detail header row using (events-slot-ctx)."
|
||||
(quasiquote
|
||||
(let ((__slot (events-slot-ctx)))
|
||||
(when (get __slot "name")
|
||||
(~menu-row-sx :id "slot-row" :level 5
|
||||
:link-label-content (~events-slot-label
|
||||
:name (get __slot "name")
|
||||
:description (get __slot "description"))
|
||||
:child-id "slot-header-child"
|
||||
:oob (unquote oob))))))
|
||||
|
||||
(defmacro ~events-ticket-types-header-auto (oob)
|
||||
"Ticket types header row."
|
||||
(quasiquote
|
||||
(let ((__ectx (events-entry-ctx))
|
||||
(__cal (events-calendar-ctx)))
|
||||
(when (get __ectx "id")
|
||||
(~menu-row-sx :id "ticket_types-row" :level 7
|
||||
:link-href (get __ectx "ticket-types-href")
|
||||
:link-label-content (<>
|
||||
(i :class "fa fa-ticket")
|
||||
(div :class "shrink-0" "ticket types"))
|
||||
:nav (~events-admin-placeholder-nav)
|
||||
:child-id "ticket_type-header-child"
|
||||
:oob (unquote oob))))))
|
||||
|
||||
(defmacro ~events-ticket-type-header-auto (oob)
|
||||
"Single ticket type header row using (events-ticket-type-ctx)."
|
||||
(quasiquote
|
||||
(let ((__tt (events-ticket-type-ctx)))
|
||||
(when (get __tt "id")
|
||||
(~menu-row-sx :id "ticket_type-row" :level 8
|
||||
:link-href (get __tt "link-href")
|
||||
:link-label-content (div :class "flex flex-col md:flex-row md:gap-2 items-center"
|
||||
(div :class "flex flex-row items-center gap-2"
|
||||
(i :class "fa fa-ticket")
|
||||
(div :class "shrink-0" (get __tt "name"))))
|
||||
:nav (~events-admin-placeholder-nav)
|
||||
:child-id "ticket_type-header-child-inner"
|
||||
:oob (unquote oob))))))
|
||||
|
||||
(defmacro ~events-markets-header-auto (oob)
|
||||
"Markets section header row."
|
||||
(quasiquote
|
||||
(~menu-row-sx :id "markets-row" :level 3
|
||||
:link-href (url-for "defpage_events_markets")
|
||||
:link-label-content (~events-markets-label)
|
||||
:child-id "markets-header-child"
|
||||
:oob (unquote oob))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; OOB clear helpers — clear deeper header rows not present at this level
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~events-clear-oob-cal-admin ()
|
||||
"Clear OOB divs for cal-admin level (keeps down to calendar-admin)."
|
||||
(<>
|
||||
(~clear-oob-div :id "entry-admin-row")
|
||||
(~clear-oob-div :id "entry-admin-header-child")
|
||||
(~clear-oob-div :id "entry-row")
|
||||
(~clear-oob-div :id "entry-header-child")
|
||||
(~clear-oob-div :id "day-admin-row")
|
||||
(~clear-oob-div :id "day-admin-header-child")
|
||||
(~clear-oob-div :id "day-row")
|
||||
(~clear-oob-div :id "day-header-child")
|
||||
(~clear-oob-div :id "calendars-row")
|
||||
(~clear-oob-div :id "calendars-header-child")))
|
||||
|
||||
(defcomp ~events-clear-oob-slot ()
|
||||
"Clear OOB divs for slot level."
|
||||
(<>
|
||||
(~clear-oob-div :id "entry-admin-row")
|
||||
(~clear-oob-div :id "entry-admin-header-child")
|
||||
(~clear-oob-div :id "entry-row")
|
||||
(~clear-oob-div :id "entry-header-child")
|
||||
(~clear-oob-div :id "day-admin-row")
|
||||
(~clear-oob-div :id "day-admin-header-child")
|
||||
(~clear-oob-div :id "day-row")
|
||||
(~clear-oob-div :id "day-header-child")
|
||||
(~clear-oob-div :id "calendars-row")
|
||||
(~clear-oob-div :id "calendars-header-child")))
|
||||
|
||||
(defcomp ~events-clear-oob-day-admin ()
|
||||
"Clear OOB divs for day-admin level."
|
||||
(<>
|
||||
(~clear-oob-div :id "entry-admin-row")
|
||||
(~clear-oob-div :id "entry-admin-header-child")
|
||||
(~clear-oob-div :id "entry-row")
|
||||
(~clear-oob-div :id "entry-header-child")
|
||||
(~clear-oob-div :id "calendars-row")
|
||||
(~clear-oob-div :id "calendars-header-child")))
|
||||
|
||||
(defcomp ~events-clear-oob-entry ()
|
||||
"Clear OOB divs for entry level (public, no admin rows)."
|
||||
(<>
|
||||
(~clear-oob-div :id "entry-admin-row")
|
||||
(~clear-oob-div :id "entry-admin-header-child")
|
||||
(~clear-oob-div :id "day-admin-row")
|
||||
(~clear-oob-div :id "day-admin-header-child")
|
||||
(~clear-oob-div :id "calendar-admin-row")
|
||||
(~clear-oob-div :id "calendar-admin-header-child")
|
||||
(~clear-oob-div :id "calendars-row")
|
||||
(~clear-oob-div :id "calendars-header-child")
|
||||
(~clear-oob-div :id "post-admin-row")
|
||||
(~clear-oob-div :id "post-admin-header-child")))
|
||||
|
||||
(defcomp ~events-clear-oob-entry-admin ()
|
||||
"Clear OOB divs for entry-admin level."
|
||||
(<>
|
||||
(~clear-oob-div :id "calendars-row")
|
||||
(~clear-oob-div :id "calendars-header-child")))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Calendar admin layout: root + post + child(post-admin + cal + cal-admin)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~events-cal-admin-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
post-header
|
||||
(~header-child-sx :inner (<> admin-header calendar-header calendar-admin-header))))
|
||||
(~header-child-sx
|
||||
:inner (<> (~post-header-auto nil)
|
||||
(~post-admin-header-auto nil "calendars")
|
||||
(~events-calendar-header-auto nil)
|
||||
(~events-calendar-admin-header-auto nil)))))
|
||||
|
||||
(defcomp ~events-cal-admin-layout-oob (&key admin-oob cal-oob cal-admin-oob-wrap clear-oob)
|
||||
(<> admin-oob cal-oob cal-admin-oob-wrap clear-oob))
|
||||
(defcomp ~events-cal-admin-layout-oob ()
|
||||
(<> (~post-admin-header-auto true "calendars")
|
||||
(~events-calendar-header-auto true)
|
||||
(~oob-header-sx :parent-id "calendar-header-child"
|
||||
:row (~events-calendar-admin-header-auto nil))
|
||||
(~events-clear-oob-cal-admin)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; --- Slots layout: same full as cal-admin ---
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Slots layout: same full as cal-admin
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~events-slots-layout-oob (&key admin-oob cal-admin-oob clear-oob)
|
||||
(<> admin-oob cal-admin-oob clear-oob))
|
||||
|
||||
;; --- Slot detail layout: root + post + child(admin + cal + cal-admin + slot) ---
|
||||
|
||||
(defcomp ~events-slot-layout-full (&key post-header admin-header
|
||||
calendar-header calendar-admin-header slot-header)
|
||||
(defcomp ~events-slots-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
post-header
|
||||
(~header-child-sx :inner (<> admin-header calendar-header calendar-admin-header slot-header))))
|
||||
(~header-child-sx
|
||||
:inner (<> (~post-header-auto nil)
|
||||
(~post-admin-header-auto nil "calendars")
|
||||
(~events-calendar-header-auto nil)
|
||||
(~events-calendar-admin-header-auto nil)))))
|
||||
|
||||
(defcomp ~events-slot-layout-oob (&key admin-oob cal-admin-oob slot-oob-wrap clear-oob)
|
||||
(<> admin-oob cal-admin-oob slot-oob-wrap clear-oob))
|
||||
(defcomp ~events-slots-layout-oob ()
|
||||
(<> (~post-admin-header-auto true "calendars")
|
||||
(~events-calendar-admin-header-auto true)
|
||||
(~events-clear-oob-cal-admin)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; --- Day admin layout: root + post + child(admin + cal + day + day-admin) ---
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Slot detail layout: root + post + child(admin + cal + cal-admin + slot)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~events-day-admin-layout-full (&key post-header admin-header
|
||||
calendar-header day-header day-admin-header)
|
||||
(defcomp ~events-slot-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
post-header
|
||||
(~header-child-sx :inner (<> admin-header calendar-header day-header day-admin-header))))
|
||||
(~header-child-sx
|
||||
:inner (<> (~post-header-auto nil)
|
||||
(~post-admin-header-auto nil "calendars")
|
||||
(~events-calendar-header-auto nil)
|
||||
(~events-calendar-admin-header-auto nil)
|
||||
(~events-slot-header-auto nil)))))
|
||||
|
||||
(defcomp ~events-day-admin-layout-oob (&key admin-oob cal-oob day-admin-oob-wrap clear-oob)
|
||||
(<> admin-oob cal-oob day-admin-oob-wrap clear-oob))
|
||||
(defcomp ~events-slot-layout-oob ()
|
||||
(<> (~post-admin-header-auto true "calendars")
|
||||
(~events-calendar-admin-header-auto true)
|
||||
(~oob-header-sx :parent-id "calendar-admin-header-child"
|
||||
:row (~events-slot-header-auto nil))
|
||||
(~events-clear-oob-slot)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; --- Entry layout: root + child(post + cal + day + entry) ---
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Day admin layout: root + post + child(admin + cal + day + day-admin)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~events-entry-layout-full (&key post-header calendar-header day-header entry-header)
|
||||
(defcomp ~events-day-admin-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx :inner (<> post-header calendar-header day-header entry-header))))
|
||||
(~header-child-sx
|
||||
:inner (<> (~post-header-auto nil)
|
||||
(~post-admin-header-auto nil "calendars")
|
||||
(~events-calendar-header-auto nil)
|
||||
(~events-day-header-auto nil)
|
||||
(~events-day-admin-header-auto nil)))))
|
||||
|
||||
(defcomp ~events-entry-layout-oob (&key day-oob entry-oob-wrap clear-oob)
|
||||
(<> day-oob entry-oob-wrap clear-oob))
|
||||
(defcomp ~events-day-admin-layout-oob ()
|
||||
(<> (~post-admin-header-auto true "calendars")
|
||||
(~events-calendar-header-auto true)
|
||||
(~oob-header-sx :parent-id "day-header-child"
|
||||
:row (~events-day-admin-header-auto nil))
|
||||
(~events-clear-oob-day-admin)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; --- Entry admin layout: root + post + child(admin + cal + day + entry + entry-admin) ---
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Entry layout: root + child(post + cal + day + entry) — public, no admin
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~events-entry-admin-layout-full (&key post-header admin-header
|
||||
calendar-header day-header
|
||||
entry-header entry-admin-header)
|
||||
(defcomp ~events-entry-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
post-header
|
||||
(~header-child-sx :inner (<> admin-header calendar-header day-header
|
||||
entry-header entry-admin-header))))
|
||||
(~header-child-sx
|
||||
:inner (<> (~post-header-auto nil)
|
||||
(~events-calendar-header-auto nil)
|
||||
(~events-day-header-auto nil)
|
||||
(~events-entry-header-auto nil)))))
|
||||
|
||||
(defcomp ~events-entry-admin-layout-oob (&key admin-oob entry-oob entry-admin-oob-wrap clear-oob)
|
||||
(<> admin-oob entry-oob entry-admin-oob-wrap clear-oob))
|
||||
(defcomp ~events-entry-layout-oob ()
|
||||
(<> (~events-day-header-auto true)
|
||||
(~oob-header-sx :parent-id "day-header-child"
|
||||
:row (~events-entry-header-auto nil))
|
||||
(~events-clear-oob-entry)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; --- Ticket types layout: root + child(post + cal + day + entry + entry-admin + ticket-types) ---
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Entry admin layout: root + post + child(admin + cal + day + entry + entry-admin)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~events-ticket-types-layout-full (&key post-header calendar-header day-header
|
||||
entry-header entry-admin-header
|
||||
ticket-types-header)
|
||||
(defcomp ~events-entry-admin-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx :inner (<> post-header calendar-header day-header
|
||||
entry-header entry-admin-header ticket-types-header))))
|
||||
(~header-child-sx
|
||||
:inner (<> (~post-header-auto nil)
|
||||
(~post-admin-header-auto nil "calendars")
|
||||
(~events-calendar-header-auto nil)
|
||||
(~events-day-header-auto nil)
|
||||
(~events-entry-header-auto nil)
|
||||
(~events-entry-admin-header-auto nil)))))
|
||||
|
||||
(defcomp ~events-ticket-types-layout-oob (&key entry-admin-oob ticket-types-oob-wrap)
|
||||
(<> entry-admin-oob ticket-types-oob-wrap))
|
||||
(defcomp ~events-entry-admin-layout-oob ()
|
||||
(<> (~post-admin-header-auto true "calendars")
|
||||
(~events-entry-header-auto true)
|
||||
(~oob-header-sx :parent-id "entry-header-child"
|
||||
:row (~events-entry-admin-header-auto nil))
|
||||
(~events-clear-oob-entry-admin)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; --- Ticket type detail layout: root + child(post + cal + day + entry + entry-admin + types + type) ---
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Ticket types layout: root + child(post + cal + day + entry + entry-admin + ticket-types)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~events-ticket-type-layout-full (&key post-header calendar-header day-header
|
||||
entry-header entry-admin-header
|
||||
ticket-types-header ticket-type-header)
|
||||
(defcomp ~events-ticket-types-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx :inner (<> post-header calendar-header day-header
|
||||
entry-header entry-admin-header
|
||||
ticket-types-header ticket-type-header))))
|
||||
(~header-child-sx
|
||||
:inner (<> (~post-header-auto nil)
|
||||
(~events-calendar-header-auto nil)
|
||||
(~events-day-header-auto nil)
|
||||
(~events-entry-header-auto nil)
|
||||
(~events-entry-admin-header-auto nil)
|
||||
(~events-ticket-types-header-auto nil)))))
|
||||
|
||||
(defcomp ~events-ticket-type-layout-oob (&key ticket-types-oob ticket-type-oob-wrap)
|
||||
(<> ticket-types-oob ticket-type-oob-wrap))
|
||||
(defcomp ~events-ticket-types-layout-oob ()
|
||||
(<> (~events-entry-admin-header-auto true)
|
||||
(~oob-header-sx :parent-id "entry-admin-header-child"
|
||||
:row (~events-ticket-types-header-auto nil))
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; --- Markets layout: root + child(post + markets) ---
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Ticket type layout: all headers down to ticket-type
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~events-markets-layout-full (&key post-header markets-header)
|
||||
(defcomp ~events-ticket-type-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx :inner (<> post-header markets-header))))
|
||||
(~header-child-sx
|
||||
:inner (<> (~post-header-auto nil)
|
||||
(~events-calendar-header-auto nil)
|
||||
(~events-day-header-auto nil)
|
||||
(~events-entry-header-auto nil)
|
||||
(~events-entry-admin-header-auto nil)
|
||||
(~events-ticket-types-header-auto nil)
|
||||
(~events-ticket-type-header-auto nil)))))
|
||||
|
||||
(defcomp ~events-markets-layout-oob (&key post-oob markets-oob-wrap)
|
||||
(<> post-oob markets-oob-wrap))
|
||||
(defcomp ~events-ticket-type-layout-oob ()
|
||||
(<> (~events-ticket-types-header-auto true)
|
||||
(~oob-header-sx :parent-id "ticket_types-header-child"
|
||||
:row (~events-ticket-type-header-auto nil))
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Markets layout: root + child(post + markets)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~events-markets-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx
|
||||
:inner (<> (~post-header-auto nil)
|
||||
(~events-markets-header-auto nil)))))
|
||||
|
||||
(defcomp ~events-markets-layout-oob ()
|
||||
(<> (~post-header-auto true)
|
||||
(~oob-header-sx :parent-id "post-header-child"
|
||||
:row (~events-markets-header-auto nil))
|
||||
(~root-header-auto true)))
|
||||
|
||||
@@ -5,30 +5,22 @@ from typing import Any
|
||||
|
||||
from shared.sx.helpers import sx_call
|
||||
|
||||
from .utils import _clear_deeper_oob, _ensure_container_nav
|
||||
from .calendar import (
|
||||
_post_header_sx, _calendar_header_sx,
|
||||
_calendar_admin_header_sx, _day_header_sx,
|
||||
_day_admin_header_sx, _markets_header_sx,
|
||||
_calendars_main_panel_sx,
|
||||
_calendar_admin_main_panel_html,
|
||||
_day_admin_main_panel_html,
|
||||
_markets_main_panel_html,
|
||||
)
|
||||
from .entries import (
|
||||
_entry_header_html, _entry_main_panel_html,
|
||||
_entry_main_panel_html,
|
||||
_entry_nav_html,
|
||||
_entry_admin_header_html, _entry_admin_main_panel_html,
|
||||
_entry_admin_main_panel_html,
|
||||
)
|
||||
from .tickets import (
|
||||
_tickets_main_panel_html, _ticket_detail_panel_html,
|
||||
_ticket_admin_main_panel_html,
|
||||
_ticket_types_header_html, _ticket_type_header_html,
|
||||
render_ticket_type_main_panel, render_ticket_types_table,
|
||||
)
|
||||
from .slots import (
|
||||
_slot_header_html, render_slot_main_panel, render_slots_table,
|
||||
)
|
||||
from .slots import render_slot_main_panel, render_slots_table
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -43,6 +35,40 @@ def _add_to_defpage_ctx(**kwargs: Any) -> None:
|
||||
g._defpage_ctx.update(kwargs)
|
||||
|
||||
|
||||
def _ensure_post_defpage_ctx() -> None:
|
||||
"""Copy g.post_data["post"] into g._defpage_ctx for layout IO primitives."""
|
||||
from quart import g
|
||||
post_data = getattr(g, "post_data", None)
|
||||
if post_data and post_data.get("post"):
|
||||
_add_to_defpage_ctx(post=post_data["post"])
|
||||
|
||||
|
||||
async def _ensure_container_nav_defpage_ctx() -> None:
|
||||
"""Fetch container_nav and add to g._defpage_ctx for layout IO primitives."""
|
||||
from quart import g
|
||||
dctx = getattr(g, "_defpage_ctx", None) or {}
|
||||
if dctx.get("container_nav"):
|
||||
return
|
||||
post = dctx.get("post") or {}
|
||||
post_id = post.get("id")
|
||||
slug = post.get("slug", "")
|
||||
if not post_id:
|
||||
return
|
||||
from shared.infrastructure.fragments import fetch_fragments
|
||||
current_cal = getattr(g, "calendar_slug", "") or ""
|
||||
nav_params = {
|
||||
"container_type": "page",
|
||||
"container_id": str(post_id),
|
||||
"post_slug": slug,
|
||||
"current_calendar": current_cal,
|
||||
}
|
||||
events_nav, market_nav = await fetch_fragments([
|
||||
("events", "container-nav", nav_params),
|
||||
("market", "container-nav", nav_params),
|
||||
], required=False)
|
||||
_add_to_defpage_ctx(container_nav=events_nav + market_nav)
|
||||
|
||||
|
||||
async def _ensure_calendar(calendar_slug: str | None) -> None:
|
||||
"""Load calendar into g.calendar if not already present."""
|
||||
from quart import g, abort
|
||||
@@ -63,6 +89,7 @@ async def _ensure_calendar(calendar_slug: str | None) -> None:
|
||||
g.calendar = cal
|
||||
g.calendar_slug = calendar_slug
|
||||
_add_to_defpage_ctx(calendar=cal)
|
||||
_ensure_post_defpage_ctx()
|
||||
|
||||
|
||||
async def _ensure_entry(entry_id: int | None) -> None:
|
||||
@@ -209,265 +236,29 @@ async def _ensure_day_data(year: int, month: int, day: int) -> None:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Layouts
|
||||
# Layouts — all layouts delegate to .sx defcomps via register_sx_layout
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _register_events_layouts() -> None:
|
||||
from shared.sx.layouts import register_custom_layout
|
||||
register_custom_layout("events-calendar-admin", _cal_admin_full, _cal_admin_oob)
|
||||
register_custom_layout("events-slots", _slots_full, _slots_oob)
|
||||
register_custom_layout("events-slot", _slot_full, _slot_oob)
|
||||
register_custom_layout("events-day-admin", _day_admin_full, _day_admin_oob)
|
||||
register_custom_layout("events-entry", _entry_full, _entry_oob)
|
||||
register_custom_layout("events-entry-admin", _entry_admin_full, _entry_admin_oob)
|
||||
register_custom_layout("events-ticket-types", _ticket_types_full, _ticket_types_oob)
|
||||
register_custom_layout("events-ticket-type", _ticket_type_full, _ticket_type_oob)
|
||||
register_custom_layout("events-markets", _markets_full, _markets_oob)
|
||||
|
||||
|
||||
# --- Calendar admin layout (root + post + child(post-admin + calendar + cal-admin)) ---
|
||||
|
||||
async def _cal_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-cal-admin-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
admin_header=await post_admin_header_sx(ctx, slug, selected="calendars"),
|
||||
calendar_header=_calendar_header_sx(ctx),
|
||||
calendar_admin_header=_calendar_admin_header_sx(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _cal_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-cal-admin-layout-oob", {},
|
||||
admin_oob=await post_admin_header_sx(ctx, slug, oob=True, selected="calendars"),
|
||||
cal_oob=_calendar_header_sx(ctx, oob=True),
|
||||
cal_admin_oob_wrap=await oob_header_sx("calendar-header-child",
|
||||
"calendar-admin-header-child", _calendar_admin_header_sx(ctx)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"calendar-admin-row", "calendar-admin-header-child")),
|
||||
)
|
||||
|
||||
|
||||
# --- Slots layout (same full as cal-admin but different OOB) ---
|
||||
|
||||
async def _slots_full(ctx: dict, **kw: Any) -> str:
|
||||
return await _cal_admin_full({**ctx, "is_admin_section": True}, **kw)
|
||||
|
||||
|
||||
async def _slots_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
ctx = await _ensure_container_nav({**ctx, "is_admin_section": True})
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-slots-layout-oob", {},
|
||||
admin_oob=await post_admin_header_sx(ctx, slug, oob=True, selected="calendars"),
|
||||
cal_admin_oob=_calendar_admin_header_sx(ctx, oob=True),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"calendar-admin-row", "calendar-admin-header-child")),
|
||||
)
|
||||
|
||||
|
||||
# --- Slot detail layout (extends cal-admin with slot header) ---
|
||||
|
||||
async def _slot_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx
|
||||
ctx = await _ensure_container_nav({**ctx, "is_admin_section": True})
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-slot-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
admin_header=await post_admin_header_sx(ctx, slug, selected="calendars"),
|
||||
calendar_header=_calendar_header_sx(ctx),
|
||||
calendar_admin_header=_calendar_admin_header_sx(ctx),
|
||||
slot_header=_slot_header_html(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _slot_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
ctx = await _ensure_container_nav({**ctx, "is_admin_section": True})
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-slot-layout-oob", {},
|
||||
admin_oob=await post_admin_header_sx(ctx, slug, oob=True, selected="calendars"),
|
||||
cal_admin_oob=_calendar_admin_header_sx(ctx, oob=True),
|
||||
slot_oob_wrap=await oob_header_sx("calendar-admin-header-child",
|
||||
"slot-header-child", _slot_header_html(ctx)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"calendar-admin-row", "calendar-admin-header-child",
|
||||
"slot-row", "slot-header-child")),
|
||||
)
|
||||
|
||||
|
||||
# --- Day admin layout (root + post + post-admin + child(cal + day + day-admin)) ---
|
||||
|
||||
async def _day_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-day-admin-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
admin_header=await post_admin_header_sx(ctx, slug, selected="calendars"),
|
||||
calendar_header=_calendar_header_sx(ctx),
|
||||
day_header=_day_header_sx(ctx),
|
||||
day_admin_header=_day_admin_header_sx(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _day_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-day-admin-layout-oob", {},
|
||||
admin_oob=await post_admin_header_sx(ctx, slug, oob=True, selected="calendars"),
|
||||
cal_oob=_calendar_header_sx(ctx, oob=True),
|
||||
day_admin_oob_wrap=await oob_header_sx("day-header-child",
|
||||
"day-admin-header-child", _day_admin_header_sx(ctx)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"day-row", "day-header-child",
|
||||
"day-admin-row", "day-admin-header-child")),
|
||||
)
|
||||
|
||||
|
||||
# --- Entry layout (root + child(post + cal + day + entry), + menu) ---
|
||||
|
||||
async def _entry_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
return await render_to_sx_with_env("events-entry-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
calendar_header=_calendar_header_sx(ctx),
|
||||
day_header=_day_header_sx(ctx),
|
||||
entry_header=_entry_header_html(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _entry_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-entry-layout-oob", {},
|
||||
day_oob=_day_header_sx(ctx, oob=True),
|
||||
entry_oob_wrap=await oob_header_sx("day-header-child",
|
||||
"entry-header-child", _entry_header_html(ctx)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"day-row", "day-header-child",
|
||||
"entry-row", "entry-header-child")),
|
||||
)
|
||||
|
||||
|
||||
# --- Entry admin layout (root + post + child(post-admin + cal + day + entry + entry-admin), + menu) ---
|
||||
|
||||
async def _entry_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-entry-admin-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
admin_header=await post_admin_header_sx(ctx, slug, selected="calendars"),
|
||||
calendar_header=_calendar_header_sx(ctx),
|
||||
day_header=_day_header_sx(ctx),
|
||||
entry_header=_entry_header_html(ctx),
|
||||
entry_admin_header=_entry_admin_header_html(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _entry_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-entry-admin-layout-oob", {},
|
||||
admin_oob=await post_admin_header_sx(ctx, slug, oob=True, selected="calendars"),
|
||||
entry_oob=_entry_header_html(ctx, oob=True),
|
||||
entry_admin_oob_wrap=await oob_header_sx("entry-header-child",
|
||||
"entry-admin-header-child", _entry_admin_header_html(ctx)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"day-row", "day-header-child",
|
||||
"entry-row", "entry-header-child",
|
||||
"entry-admin-row", "entry-admin-header-child")),
|
||||
)
|
||||
|
||||
|
||||
# --- Ticket types layout (extends entry admin with ticket-types header, + menu) ---
|
||||
|
||||
async def _ticket_types_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
return await render_to_sx_with_env("events-ticket-types-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
calendar_header=_calendar_header_sx(ctx),
|
||||
day_header=_day_header_sx(ctx),
|
||||
entry_header=_entry_header_html(ctx),
|
||||
entry_admin_header=_entry_admin_header_html(ctx),
|
||||
ticket_types_header=_ticket_types_header_html(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _ticket_types_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
return await render_to_sx_with_env("events-ticket-types-layout-oob", {},
|
||||
entry_admin_oob=_entry_admin_header_html(ctx, oob=True),
|
||||
ticket_types_oob_wrap=await oob_header_sx("entry-admin-header-child",
|
||||
"ticket_types-header-child", _ticket_types_header_html(ctx)),
|
||||
)
|
||||
|
||||
|
||||
# --- Ticket type detail layout (extends ticket types with ticket-type header, + menu) ---
|
||||
|
||||
async def _ticket_type_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
return await render_to_sx_with_env("events-ticket-type-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
calendar_header=_calendar_header_sx(ctx),
|
||||
day_header=_day_header_sx(ctx),
|
||||
entry_header=_entry_header_html(ctx),
|
||||
entry_admin_header=_entry_admin_header_html(ctx),
|
||||
ticket_types_header=_ticket_types_header_html(ctx),
|
||||
ticket_type_header=_ticket_type_header_html(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _ticket_type_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
return await render_to_sx_with_env("events-ticket-type-layout-oob", {},
|
||||
ticket_types_oob=_ticket_types_header_html(ctx, oob=True),
|
||||
ticket_type_oob_wrap=await oob_header_sx("ticket_types-header-child",
|
||||
"ticket_type-header-child", _ticket_type_header_html(ctx)),
|
||||
)
|
||||
|
||||
|
||||
# --- Markets layout (root + child(post + markets)) ---
|
||||
|
||||
async def _markets_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
return await render_to_sx_with_env("events-markets-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
markets_header=_markets_header_sx(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _markets_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
return await render_to_sx_with_env("events-markets-layout-oob", {},
|
||||
post_oob=await _post_header_sx(ctx, oob=True),
|
||||
markets_oob_wrap=await oob_header_sx("post-header-child",
|
||||
"markets-header-child", _markets_header_sx(ctx)),
|
||||
)
|
||||
from shared.sx.layouts import register_sx_layout
|
||||
register_sx_layout("events-calendar-admin",
|
||||
"events-cal-admin-layout-full", "events-cal-admin-layout-oob")
|
||||
register_sx_layout("events-slots",
|
||||
"events-slots-layout-full", "events-slots-layout-oob")
|
||||
register_sx_layout("events-slot",
|
||||
"events-slot-layout-full", "events-slot-layout-oob")
|
||||
register_sx_layout("events-day-admin",
|
||||
"events-day-admin-layout-full", "events-day-admin-layout-oob")
|
||||
register_sx_layout("events-entry",
|
||||
"events-entry-layout-full", "events-entry-layout-oob")
|
||||
register_sx_layout("events-entry-admin",
|
||||
"events-entry-admin-layout-full", "events-entry-admin-layout-oob")
|
||||
register_sx_layout("events-ticket-types",
|
||||
"events-ticket-types-layout-full", "events-ticket-types-layout-oob")
|
||||
register_sx_layout("events-ticket-type",
|
||||
"events-ticket-type-layout-full", "events-ticket-type-layout-oob")
|
||||
register_sx_layout("events-markets",
|
||||
"events-markets-layout-full", "events-markets-layout-oob")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -497,6 +288,7 @@ def _register_events_helpers() -> None:
|
||||
|
||||
async def _h_calendar_admin_content(calendar_slug=None, **kw):
|
||||
await _ensure_calendar(calendar_slug)
|
||||
await _ensure_container_nav_defpage_ctx()
|
||||
from shared.sx.page import get_template_context
|
||||
ctx = await get_template_context()
|
||||
return _calendar_admin_main_panel_html(ctx)
|
||||
@@ -504,6 +296,7 @@ async def _h_calendar_admin_content(calendar_slug=None, **kw):
|
||||
|
||||
async def _h_day_admin_content(calendar_slug=None, year=None, month=None, day=None, **kw):
|
||||
await _ensure_calendar(calendar_slug)
|
||||
await _ensure_container_nav_defpage_ctx()
|
||||
if year is not None:
|
||||
await _ensure_day_data(int(year), int(month), int(day))
|
||||
return _day_admin_main_panel_html({})
|
||||
@@ -512,6 +305,7 @@ async def _h_day_admin_content(calendar_slug=None, year=None, month=None, day=No
|
||||
async def _h_slots_content(calendar_slug=None, **kw):
|
||||
from quart import g
|
||||
await _ensure_calendar(calendar_slug)
|
||||
await _ensure_container_nav_defpage_ctx()
|
||||
calendar = getattr(g, "calendar", None)
|
||||
from bp.slots.services.slots import list_slots as svc_list_slots
|
||||
slots = await svc_list_slots(g.s, calendar.id) if calendar else []
|
||||
@@ -522,6 +316,7 @@ async def _h_slots_content(calendar_slug=None, **kw):
|
||||
async def _h_slot_content(calendar_slug=None, slot_id=None, **kw):
|
||||
from quart import g, abort
|
||||
await _ensure_calendar(calendar_slug)
|
||||
await _ensure_container_nav_defpage_ctx()
|
||||
from bp.slot.services.slot import get_slot as svc_get_slot
|
||||
slot = await svc_get_slot(g.s, slot_id) if slot_id else None
|
||||
if not slot:
|
||||
@@ -550,6 +345,7 @@ async def _h_entry_menu(calendar_slug=None, entry_id=None, **kw):
|
||||
|
||||
async def _h_entry_admin_content(calendar_slug=None, entry_id=None, **kw):
|
||||
await _ensure_calendar(calendar_slug)
|
||||
await _ensure_container_nav_defpage_ctx()
|
||||
await _ensure_entry_context(entry_id)
|
||||
from shared.sx.page import get_template_context
|
||||
ctx = await get_template_context()
|
||||
@@ -666,6 +462,7 @@ async def _h_ticket_admin_content(**kw):
|
||||
|
||||
|
||||
async def _h_markets_content(**kw):
|
||||
_ensure_post_defpage_ctx()
|
||||
from shared.sx.page import get_template_context
|
||||
ctx = await get_template_context()
|
||||
return _markets_main_panel_html(ctx)
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
"""Layout registration + header builders."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
from .utils import _clear_deeper_oob, _ensure_container_nav
|
||||
from .calendar import (
|
||||
_post_header_sx, _calendar_header_sx, _calendar_admin_header_sx,
|
||||
_day_header_sx, _day_admin_header_sx, _markets_header_sx,
|
||||
)
|
||||
from .entries import _entry_header_html, _entry_admin_header_html
|
||||
from .slots import _slot_header_html
|
||||
from .tickets import _ticket_types_header_html, _ticket_type_header_html
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Layouts
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _register_events_layouts() -> None:
|
||||
from shared.sx.layouts import register_custom_layout
|
||||
register_custom_layout("events-calendar-admin", _cal_admin_full, _cal_admin_oob)
|
||||
register_custom_layout("events-slots", _slots_full, _slots_oob)
|
||||
register_custom_layout("events-slot", _slot_full, _slot_oob)
|
||||
register_custom_layout("events-day-admin", _day_admin_full, _day_admin_oob)
|
||||
register_custom_layout("events-entry", _entry_full, _entry_oob)
|
||||
register_custom_layout("events-entry-admin", _entry_admin_full, _entry_admin_oob)
|
||||
register_custom_layout("events-ticket-types", _ticket_types_full, _ticket_types_oob)
|
||||
register_custom_layout("events-ticket-type", _ticket_type_full, _ticket_type_oob)
|
||||
register_custom_layout("events-markets", _markets_full, _markets_oob)
|
||||
|
||||
|
||||
# --- Calendar admin layout (root + post + child(post-admin + calendar + cal-admin)) ---
|
||||
|
||||
async def _cal_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-cal-admin-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
admin_header=await post_admin_header_sx(ctx, slug, selected="calendars"),
|
||||
calendar_header=_calendar_header_sx(ctx),
|
||||
calendar_admin_header=_calendar_admin_header_sx(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _cal_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-cal-admin-layout-oob", {},
|
||||
admin_oob=await post_admin_header_sx(ctx, slug, oob=True, selected="calendars"),
|
||||
cal_oob=_calendar_header_sx(ctx, oob=True),
|
||||
cal_admin_oob_wrap=await oob_header_sx("calendar-header-child",
|
||||
"calendar-admin-header-child", _calendar_admin_header_sx(ctx)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"calendar-admin-row", "calendar-admin-header-child")),
|
||||
)
|
||||
|
||||
|
||||
# --- Slots layout (same full as cal-admin but different OOB) ---
|
||||
|
||||
async def _slots_full(ctx: dict, **kw: Any) -> str:
|
||||
return await _cal_admin_full({**ctx, "is_admin_section": True}, **kw)
|
||||
|
||||
|
||||
async def _slots_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
ctx = await _ensure_container_nav({**ctx, "is_admin_section": True})
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-slots-layout-oob", {},
|
||||
admin_oob=await post_admin_header_sx(ctx, slug, oob=True, selected="calendars"),
|
||||
cal_admin_oob=_calendar_admin_header_sx(ctx, oob=True),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"calendar-admin-row", "calendar-admin-header-child")),
|
||||
)
|
||||
|
||||
|
||||
# --- Slot detail layout (extends cal-admin with slot header) ---
|
||||
|
||||
async def _slot_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx
|
||||
ctx = await _ensure_container_nav({**ctx, "is_admin_section": True})
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-slot-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
admin_header=await post_admin_header_sx(ctx, slug, selected="calendars"),
|
||||
calendar_header=_calendar_header_sx(ctx),
|
||||
calendar_admin_header=_calendar_admin_header_sx(ctx),
|
||||
slot_header=_slot_header_html(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _slot_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
ctx = await _ensure_container_nav({**ctx, "is_admin_section": True})
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-slot-layout-oob", {},
|
||||
admin_oob=await post_admin_header_sx(ctx, slug, oob=True, selected="calendars"),
|
||||
cal_admin_oob=_calendar_admin_header_sx(ctx, oob=True),
|
||||
slot_oob_wrap=await oob_header_sx("calendar-admin-header-child",
|
||||
"slot-header-child", _slot_header_html(ctx)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"calendar-admin-row", "calendar-admin-header-child",
|
||||
"slot-row", "slot-header-child")),
|
||||
)
|
||||
|
||||
|
||||
# --- Day admin layout (root + post + post-admin + child(cal + day + day-admin)) ---
|
||||
|
||||
async def _day_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-day-admin-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
admin_header=await post_admin_header_sx(ctx, slug, selected="calendars"),
|
||||
calendar_header=_calendar_header_sx(ctx),
|
||||
day_header=_day_header_sx(ctx),
|
||||
day_admin_header=_day_admin_header_sx(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _day_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-day-admin-layout-oob", {},
|
||||
admin_oob=await post_admin_header_sx(ctx, slug, oob=True, selected="calendars"),
|
||||
cal_oob=_calendar_header_sx(ctx, oob=True),
|
||||
day_admin_oob_wrap=await oob_header_sx("day-header-child",
|
||||
"day-admin-header-child", _day_admin_header_sx(ctx)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"day-row", "day-header-child",
|
||||
"day-admin-row", "day-admin-header-child")),
|
||||
)
|
||||
|
||||
|
||||
# --- Entry layout (root + child(post + cal + day + entry), + menu) ---
|
||||
|
||||
async def _entry_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
return await render_to_sx_with_env("events-entry-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
calendar_header=_calendar_header_sx(ctx),
|
||||
day_header=_day_header_sx(ctx),
|
||||
entry_header=_entry_header_html(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _entry_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-entry-layout-oob", {},
|
||||
day_oob=_day_header_sx(ctx, oob=True),
|
||||
entry_oob_wrap=await oob_header_sx("day-header-child",
|
||||
"entry-header-child", _entry_header_html(ctx)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"day-row", "day-header-child",
|
||||
"entry-row", "entry-header-child")),
|
||||
)
|
||||
|
||||
|
||||
# --- Entry admin layout (root + post + child(post-admin + cal + day + entry + entry-admin), + menu) ---
|
||||
|
||||
async def _entry_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-entry-admin-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
admin_header=await post_admin_header_sx(ctx, slug, selected="calendars"),
|
||||
calendar_header=_calendar_header_sx(ctx),
|
||||
day_header=_day_header_sx(ctx),
|
||||
entry_header=_entry_header_html(ctx),
|
||||
entry_admin_header=_entry_admin_header_html(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _entry_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, post_admin_header_sx, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-entry-admin-layout-oob", {},
|
||||
admin_oob=await post_admin_header_sx(ctx, slug, oob=True, selected="calendars"),
|
||||
entry_oob=_entry_header_html(ctx, oob=True),
|
||||
entry_admin_oob_wrap=await oob_header_sx("entry-header-child",
|
||||
"entry-admin-header-child", _entry_admin_header_html(ctx)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"day-row", "day-header-child",
|
||||
"entry-row", "entry-header-child",
|
||||
"entry-admin-row", "entry-admin-header-child")),
|
||||
)
|
||||
|
||||
|
||||
# --- Ticket types layout (extends entry admin with ticket-types header, + menu) ---
|
||||
|
||||
async def _ticket_types_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
return await render_to_sx_with_env("events-ticket-types-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
calendar_header=_calendar_header_sx(ctx),
|
||||
day_header=_day_header_sx(ctx),
|
||||
entry_header=_entry_header_html(ctx),
|
||||
entry_admin_header=_entry_admin_header_html(ctx),
|
||||
ticket_types_header=_ticket_types_header_html(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _ticket_types_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
return await render_to_sx_with_env("events-ticket-types-layout-oob", {},
|
||||
entry_admin_oob=_entry_admin_header_html(ctx, oob=True),
|
||||
ticket_types_oob_wrap=await oob_header_sx("entry-admin-header-child",
|
||||
"ticket_types-header-child", _ticket_types_header_html(ctx)),
|
||||
)
|
||||
|
||||
|
||||
# --- Ticket type detail layout (extends ticket types with ticket-type header, + menu) ---
|
||||
|
||||
async def _ticket_type_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
return await render_to_sx_with_env("events-ticket-type-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
calendar_header=_calendar_header_sx(ctx),
|
||||
day_header=_day_header_sx(ctx),
|
||||
entry_header=_entry_header_html(ctx),
|
||||
entry_admin_header=_entry_admin_header_html(ctx),
|
||||
ticket_types_header=_ticket_types_header_html(ctx),
|
||||
ticket_type_header=_ticket_type_header_html(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _ticket_type_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
return await render_to_sx_with_env("events-ticket-type-layout-oob", {},
|
||||
ticket_types_oob=_ticket_types_header_html(ctx, oob=True),
|
||||
ticket_type_oob_wrap=await oob_header_sx("ticket_types-header-child",
|
||||
"ticket_type-header-child", _ticket_type_header_html(ctx)),
|
||||
)
|
||||
|
||||
|
||||
# --- Markets layout (root + child(post + markets)) ---
|
||||
|
||||
async def _markets_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
return await render_to_sx_with_env("events-markets-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
markets_header=_markets_header_sx(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _markets_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
return await render_to_sx_with_env("events-markets-layout-oob", {},
|
||||
post_oob=await _post_header_sx(ctx, oob=True),
|
||||
markets_oob_wrap=await oob_header_sx("post-header-child",
|
||||
"markets-header-child", _markets_header_sx(ctx)),
|
||||
)
|
||||
@@ -1,17 +1,17 @@
|
||||
;; Federation layout defcomps — read ctx values from env free variables.
|
||||
;; `actor` is injected into env by the layout registration in __init__.py.
|
||||
;; Federation layout defcomps — fully self-contained via IO primitives.
|
||||
;; Registered via register_sx_layout("social", ...) in __init__.py.
|
||||
|
||||
;; Full page: root header + social header in header-child
|
||||
(defcomp ~social-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx
|
||||
:inner (~federation-social-header
|
||||
:nav (~federation-social-nav :actor actor)))))
|
||||
:nav (~federation-social-nav :actor (federation-actor-ctx))))))
|
||||
|
||||
;; OOB (HTMX): social header oob + root header oob
|
||||
(defcomp ~social-layout-oob ()
|
||||
(<> (~oob-header-sx
|
||||
:parent-id "root-header-child"
|
||||
:row (~federation-social-header
|
||||
:nav (~federation-social-nav :actor actor)))
|
||||
:nav (~federation-social-nav :actor (federation-actor-ctx))))
|
||||
(~root-header-auto true)))
|
||||
|
||||
@@ -15,6 +15,5 @@ def _load_federation_page_files() -> None:
|
||||
|
||||
|
||||
def _register_federation_layouts() -> None:
|
||||
from shared.sx.layouts import register_custom_layout
|
||||
from .utils import _social_full, _social_oob
|
||||
register_custom_layout("social", _social_full, _social_oob)
|
||||
from shared.sx.layouts import register_sx_layout
|
||||
register_sx_layout("social", "social-layout-full", "social-layout-oob")
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
"""Federation page utilities — serializers, actor helpers, social page builder."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
def _serialize_actor(actor) -> dict | None:
|
||||
"""Serialize an actor profile to a dict for sx defcomps."""
|
||||
@@ -24,7 +22,7 @@ def _serialize_remote_actor(a) -> dict:
|
||||
|
||||
async def _social_page(ctx: dict, actor, *, content: str,
|
||||
title: str = "Rose Ash", meta_html: str = "") -> str:
|
||||
"""Build a full social page with social header."""
|
||||
"""Build a full social page with social header (non-defpage routes)."""
|
||||
from shared.sx.helpers import render_to_sx_with_env, full_page_sx
|
||||
from markupsafe import escape
|
||||
|
||||
@@ -47,22 +45,3 @@ def _require_actor():
|
||||
if not actor:
|
||||
abort(403, "You need to choose a federation username first")
|
||||
return actor
|
||||
|
||||
|
||||
def _actor_data(ctx: dict) -> dict | None:
|
||||
actor = ctx.get("actor")
|
||||
if not actor:
|
||||
return None
|
||||
return _serialize_actor(actor)
|
||||
|
||||
|
||||
async def _social_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
env = {"actor": kw.get("actor") or _actor_data(ctx)}
|
||||
return await render_to_sx_with_env("social-layout-full", env)
|
||||
|
||||
|
||||
async def _social_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
env = {"actor": kw.get("actor") or _actor_data(ctx)}
|
||||
return await render_to_sx_with_env("social-layout-oob", env)
|
||||
|
||||
@@ -228,6 +228,11 @@ def create_app() -> "Quart":
|
||||
("market", "container-nav", nav_params),
|
||||
], required=False)
|
||||
ctx["container_nav"] = events_nav + market_nav
|
||||
# Populate g._defpage_ctx for layout IO primitives
|
||||
if not hasattr(g, '_defpage_ctx'):
|
||||
g._defpage_ctx = {}
|
||||
g._defpage_ctx.setdefault("post", post_data.get("post"))
|
||||
g._defpage_ctx.setdefault("container_nav", ctx["container_nav"])
|
||||
return ctx
|
||||
|
||||
# --- oEmbed endpoint ---
|
||||
|
||||
@@ -1,42 +1,105 @@
|
||||
;; Market layout defcomps — root header via ~root-header-auto,
|
||||
;; market-specific headers passed as &key params.
|
||||
;; Market layout defcomps — fully self-contained via IO primitives.
|
||||
;; Registered via register_sx_layout in layouts.py.
|
||||
|
||||
;; --- Browse layout: root + post header + market header ---
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Auto-fetching market header macro
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~market-browse-layout-full (&key post-header market-header)
|
||||
(defmacro ~market-header-auto (oob)
|
||||
"Market header row using (market-header-ctx)."
|
||||
(quasiquote
|
||||
(let ((__mctx (market-header-ctx)))
|
||||
(~menu-row-sx :id "market-row" :level 2
|
||||
:link-href (get __mctx "link-href")
|
||||
:link-label-content (~market-shop-label
|
||||
:title (get __mctx "market-title")
|
||||
:top-slug (get __mctx "top-slug")
|
||||
:sub-div (get __mctx "sub-slug"))
|
||||
:nav (get __mctx "desktop-nav")
|
||||
:child-id "market-header-child"
|
||||
:oob (unquote oob)))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; OOB clear helpers
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~market-clear-oob ()
|
||||
"Clear OOB divs for browse level."
|
||||
(<>
|
||||
(~clear-oob-div :id "product-admin-row")
|
||||
(~clear-oob-div :id "product-admin-header-child")
|
||||
(~clear-oob-div :id "product-row")
|
||||
(~clear-oob-div :id "product-header-child")
|
||||
(~clear-oob-div :id "market-admin-row")
|
||||
(~clear-oob-div :id "market-admin-header-child")
|
||||
(~clear-oob-div :id "post-admin-row")
|
||||
(~clear-oob-div :id "post-admin-header-child")))
|
||||
|
||||
(defcomp ~market-clear-oob-admin ()
|
||||
"Clear OOB divs for admin level."
|
||||
(<>
|
||||
(~clear-oob-div :id "product-admin-row")
|
||||
(~clear-oob-div :id "product-admin-header-child")
|
||||
(~clear-oob-div :id "product-row")
|
||||
(~clear-oob-div :id "product-header-child")))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Browse layout: root + post + market (self-contained)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~market-browse-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx :inner (<> post-header market-header))))
|
||||
(~header-child-sx
|
||||
:inner (<> (~post-header-auto nil)
|
||||
(~market-header-auto nil)))))
|
||||
|
||||
(defcomp ~market-browse-layout-oob (&key oob-header post-header-oob clear-oob)
|
||||
(<> oob-header post-header-oob clear-oob))
|
||||
(defcomp ~market-browse-layout-oob ()
|
||||
(<> (~post-header-auto true)
|
||||
(~oob-header-sx :parent-id "post-header-child"
|
||||
:row (~market-header-auto nil))
|
||||
(~market-clear-oob)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; --- Product layout: root + post + market + product ---
|
||||
(defcomp ~market-browse-layout-mobile ()
|
||||
(let ((__mctx (market-header-ctx)))
|
||||
(get __mctx "mobile-nav")))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Market admin layout: root + post + market + post-admin (self-contained)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~market-admin-layout-full (&key selected)
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx
|
||||
:inner (<> (~post-header-auto nil)
|
||||
(~market-header-auto nil)
|
||||
(~post-admin-header-auto nil selected)))))
|
||||
|
||||
(defcomp ~market-admin-layout-oob (&key selected)
|
||||
(<> (~market-header-auto true)
|
||||
(~oob-header-sx :parent-id "market-header-child"
|
||||
:row (~post-admin-header-auto nil selected))
|
||||
(~market-clear-oob-admin)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Parameterized defcomps — used by renders.py (non-defpage routes)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
;; Product layout: root + post + market + product
|
||||
(defcomp ~market-product-layout-full (&key post-header market-header product-header)
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx :inner (<> post-header market-header product-header))))
|
||||
|
||||
;; --- Product admin layout: root + post + market + product + admin ---
|
||||
|
||||
;; Product admin layout: root + post + market + product + admin
|
||||
(defcomp ~market-product-admin-layout-full (&key post-header market-header product-header admin-header)
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx :inner (<> post-header market-header product-header admin-header))))
|
||||
|
||||
;; --- Market admin layout: root + post + market + market-admin ---
|
||||
|
||||
(defcomp ~market-admin-layout-full (&key post-header market-header admin-header)
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx :inner (<> post-header market-header admin-header))))
|
||||
|
||||
(defcomp ~market-admin-layout-oob (&key market-header-oob admin-oob-header clear-oob)
|
||||
(<> market-header-oob admin-oob-header clear-oob))
|
||||
|
||||
;; --- OOB wrappers ---
|
||||
|
||||
;; OOB wrappers
|
||||
(defcomp ~market-oob-wrap (&key parts)
|
||||
(<> parts))
|
||||
|
||||
;; --- Content wrappers ---
|
||||
|
||||
;; Content wrappers
|
||||
(defcomp ~market-content-padded (&key content)
|
||||
(<> content (div :class "pb-8")))
|
||||
|
||||
@@ -4,15 +4,9 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from shared.sx.parser import SxExpr
|
||||
from shared.sx.helpers import (
|
||||
sx_call,
|
||||
post_header_sx as _post_header_sx,
|
||||
post_admin_header_sx,
|
||||
oob_header_sx as _oob_header_sx,
|
||||
header_child_sx,
|
||||
)
|
||||
from shared.sx.helpers import sx_call
|
||||
|
||||
from .utils import _set_prices, _price_str, _clear_deeper_oob
|
||||
from .utils import _set_prices, _price_str
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -272,62 +266,14 @@ def _product_admin_header_sx(ctx: dict, d: dict, *, oob: bool = False) -> str:
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Market admin header
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _market_admin_header_sx(ctx: dict, *, oob: bool = False, selected: str = "") -> str:
|
||||
"""Build market admin header row -- delegates to shared helper."""
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await post_admin_header_sx(ctx, slug, oob=oob, selected=selected)
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# Layout registration
|
||||
# Layout registration — all layouts delegate to .sx defcomps
|
||||
# ===========================================================================
|
||||
|
||||
def _register_market_layouts() -> None:
|
||||
from shared.sx.layouts import register_custom_layout
|
||||
register_custom_layout("market", _market_full, _market_oob, _market_mobile)
|
||||
register_custom_layout("market-admin", _market_admin_full, _market_admin_oob)
|
||||
|
||||
|
||||
async def _market_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
return await render_to_sx_with_env("market-browse-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
market_header=_market_header_sx(ctx))
|
||||
|
||||
|
||||
async def _market_oob(ctx: dict, **kw: Any) -> str:
|
||||
oob_hdr = await _oob_header_sx("post-header-child", "market-header-child",
|
||||
_market_header_sx(ctx))
|
||||
return sx_call("market-browse-layout-oob",
|
||||
oob_header=oob_hdr,
|
||||
post_header_oob=await _post_header_sx(ctx, oob=True),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"market-row", "market-header-child")))
|
||||
|
||||
|
||||
def _market_mobile(ctx: dict, **kw: Any) -> str:
|
||||
return _mobile_nav_panel_sx(ctx)
|
||||
|
||||
|
||||
async def _market_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
selected = kw.get("selected", "")
|
||||
return await render_to_sx_with_env("market-admin-layout-full", {},
|
||||
post_header=await _post_header_sx(ctx),
|
||||
market_header=_market_header_sx(ctx),
|
||||
admin_header=await _market_admin_header_sx(ctx, selected=selected))
|
||||
|
||||
|
||||
async def _market_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
selected = kw.get("selected", "")
|
||||
return sx_call("market-admin-layout-oob",
|
||||
market_header_oob=_market_header_sx(ctx, oob=True),
|
||||
admin_oob_header=await _oob_header_sx("market-header-child", "market-admin-header-child",
|
||||
await _market_admin_header_sx(ctx, selected=selected)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"market-row", "market-header-child",
|
||||
"market-admin-row", "market-admin-header-child")))
|
||||
from shared.sx.layouts import register_sx_layout
|
||||
register_sx_layout("market",
|
||||
"market-browse-layout-full", "market-browse-layout-oob",
|
||||
"market-browse-layout-mobile")
|
||||
register_sx_layout("market-admin",
|
||||
"market-admin-layout-full", "market-admin-layout-oob")
|
||||
|
||||
@@ -50,6 +50,15 @@ IO_PRIMITIVES: frozenset[str] = frozenset({
|
||||
"select-colours",
|
||||
"account-nav-ctx",
|
||||
"app-rights",
|
||||
"federation-actor-ctx",
|
||||
"request-view-args",
|
||||
"cart-page-ctx",
|
||||
"events-calendar-ctx",
|
||||
"events-day-ctx",
|
||||
"events-entry-ctx",
|
||||
"events-slot-ctx",
|
||||
"events-ticket-type-ctx",
|
||||
"market-header-ctx",
|
||||
})
|
||||
|
||||
|
||||
@@ -557,6 +566,371 @@ async def _io_post_header_ctx(
|
||||
return result
|
||||
|
||||
|
||||
async def _io_cart_page_ctx(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> dict[str, Any]:
|
||||
"""``(cart-page-ctx)`` → dict with cart page header values.
|
||||
|
||||
Reads ``g.page_post`` (set by cart's before_request) and returns
|
||||
slug, title, feature-image, and cart-url for the page cart header.
|
||||
"""
|
||||
from quart import g
|
||||
from .types import NIL
|
||||
from shared.infrastructure.urls import app_url
|
||||
|
||||
page_post = getattr(g, "page_post", None)
|
||||
if not page_post:
|
||||
return {"slug": "", "title": "", "feature-image": NIL, "cart-url": "/"}
|
||||
|
||||
slug = getattr(page_post, "slug", "") or ""
|
||||
title = (getattr(page_post, "title", "") or "")[:160]
|
||||
feature_image = getattr(page_post, "feature_image", None) or NIL
|
||||
|
||||
return {
|
||||
"slug": slug,
|
||||
"title": title,
|
||||
"feature-image": feature_image,
|
||||
"page-cart-url": app_url("cart", f"/{slug}/"),
|
||||
"cart-url": app_url("cart", "/"),
|
||||
}
|
||||
|
||||
|
||||
async def _io_federation_actor_ctx(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> dict[str, Any] | None:
|
||||
"""``(federation-actor-ctx)`` → serialized actor dict or None.
|
||||
|
||||
Reads ``g._social_actor`` (set by federation social blueprint's
|
||||
before_request hook) and serializes to a dict for .sx components.
|
||||
"""
|
||||
from quart import g
|
||||
actor = getattr(g, "_social_actor", None)
|
||||
if not actor:
|
||||
return None
|
||||
return {
|
||||
"id": actor.id,
|
||||
"preferred_username": actor.preferred_username,
|
||||
"display_name": getattr(actor, "display_name", None),
|
||||
"icon_url": getattr(actor, "icon_url", None),
|
||||
"actor_url": getattr(actor, "actor_url", ""),
|
||||
}
|
||||
|
||||
|
||||
async def _io_request_view_args(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> Any:
|
||||
"""``(request-view-args "key")`` → request.view_args[key]."""
|
||||
if not args:
|
||||
raise ValueError("request-view-args requires a key")
|
||||
from quart import request
|
||||
key = str(args[0])
|
||||
return (request.view_args or {}).get(key)
|
||||
|
||||
|
||||
async def _io_events_calendar_ctx(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> dict[str, Any]:
|
||||
"""``(events-calendar-ctx)`` → dict with events calendar header values.
|
||||
|
||||
Reads ``g.calendar`` or ``g._defpage_ctx["calendar"]`` and returns
|
||||
slug, name, description for the calendar header row.
|
||||
"""
|
||||
from quart import g
|
||||
cal = getattr(g, "calendar", None)
|
||||
if not cal:
|
||||
dctx = getattr(g, "_defpage_ctx", None) or {}
|
||||
cal = dctx.get("calendar")
|
||||
if not cal:
|
||||
return {"slug": ""}
|
||||
return {
|
||||
"slug": getattr(cal, "slug", "") or "",
|
||||
"name": getattr(cal, "name", "") or "",
|
||||
"description": getattr(cal, "description", "") or "",
|
||||
}
|
||||
|
||||
|
||||
async def _io_events_day_ctx(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> dict[str, Any]:
|
||||
"""``(events-day-ctx)`` → dict with events day header values.
|
||||
|
||||
Reads ``g.day_date``, ``g.calendar``, confirmed entries from
|
||||
``g._defpage_ctx``. Pre-builds the confirmed entries nav as SxExpr.
|
||||
"""
|
||||
from quart import g, url_for
|
||||
from .types import NIL
|
||||
from .parser import SxExpr
|
||||
|
||||
dctx = getattr(g, "_defpage_ctx", None) or {}
|
||||
cal = getattr(g, "calendar", None) or dctx.get("calendar")
|
||||
day_date = dctx.get("day_date") or getattr(g, "day_date", None)
|
||||
if not cal or not day_date:
|
||||
return {"date-str": ""}
|
||||
|
||||
cal_slug = getattr(cal, "slug", "") or ""
|
||||
|
||||
# Build confirmed entries nav
|
||||
confirmed = dctx.get("confirmed_entries") or []
|
||||
rights = getattr(g, "rights", None) or {}
|
||||
is_admin = (
|
||||
rights.get("admin", False)
|
||||
if isinstance(rights, dict)
|
||||
else getattr(rights, "admin", False)
|
||||
)
|
||||
|
||||
from .helpers import sx_call
|
||||
nav_parts: list[str] = []
|
||||
if confirmed:
|
||||
entry_links = []
|
||||
for entry in confirmed:
|
||||
href = url_for(
|
||||
"calendar.day.calendar_entries.calendar_entry.get",
|
||||
calendar_slug=cal_slug,
|
||||
year=day_date.year, month=day_date.month, day=day_date.day,
|
||||
entry_id=entry.id,
|
||||
)
|
||||
start = entry.start_at.strftime("%H:%M") if entry.start_at else ""
|
||||
end = (
|
||||
f" \u2013 {entry.end_at.strftime('%H:%M')}"
|
||||
if entry.end_at else ""
|
||||
)
|
||||
entry_links.append(sx_call(
|
||||
"events-day-entry-link",
|
||||
href=href, name=entry.name, time_str=f"{start}{end}",
|
||||
))
|
||||
inner = "".join(entry_links)
|
||||
nav_parts.append(sx_call(
|
||||
"events-day-entries-nav", inner=SxExpr(inner),
|
||||
))
|
||||
|
||||
if is_admin and day_date:
|
||||
admin_href = url_for(
|
||||
"defpage_day_admin", calendar_slug=cal_slug,
|
||||
year=day_date.year, month=day_date.month, day=day_date.day,
|
||||
)
|
||||
nav_parts.append(sx_call("nav-link", href=admin_href, icon="fa fa-cog"))
|
||||
|
||||
return {
|
||||
"date-str": day_date.strftime("%A %d %B %Y"),
|
||||
"year": day_date.year,
|
||||
"month": day_date.month,
|
||||
"day": day_date.day,
|
||||
"nav": SxExpr("".join(nav_parts)) if nav_parts else NIL,
|
||||
}
|
||||
|
||||
|
||||
async def _io_events_entry_ctx(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> dict[str, Any]:
|
||||
"""``(events-entry-ctx)`` → dict with events entry header values.
|
||||
|
||||
Reads ``g.entry``, ``g.calendar``, and entry_posts from
|
||||
``g._defpage_ctx``. Pre-builds entry nav (posts + admin link) as SxExpr.
|
||||
"""
|
||||
from quart import g, url_for
|
||||
from .types import NIL
|
||||
from .parser import SxExpr
|
||||
|
||||
dctx = getattr(g, "_defpage_ctx", None) or {}
|
||||
cal = getattr(g, "calendar", None) or dctx.get("calendar")
|
||||
entry = getattr(g, "entry", None) or dctx.get("entry")
|
||||
if not cal or not entry:
|
||||
return {"id": ""}
|
||||
|
||||
cal_slug = getattr(cal, "slug", "") or ""
|
||||
day = dctx.get("day")
|
||||
month = dctx.get("month")
|
||||
year = dctx.get("year")
|
||||
|
||||
# Times
|
||||
start = entry.start_at
|
||||
end = entry.end_at
|
||||
time_str = ""
|
||||
if start:
|
||||
time_str = start.strftime("%H:%M")
|
||||
if end:
|
||||
time_str += f" \u2192 {end.strftime('%H:%M')}"
|
||||
|
||||
link_href = url_for(
|
||||
"calendar.day.calendar_entries.calendar_entry.get",
|
||||
calendar_slug=cal_slug,
|
||||
year=year, month=month, day=day, entry_id=entry.id,
|
||||
)
|
||||
|
||||
# Build nav: associated posts + admin link
|
||||
entry_posts = dctx.get("entry_posts") or []
|
||||
rights = getattr(g, "rights", None) or {}
|
||||
is_admin = (
|
||||
rights.get("admin", False)
|
||||
if isinstance(rights, dict)
|
||||
else getattr(rights, "admin", False)
|
||||
)
|
||||
|
||||
from .helpers import sx_call
|
||||
from shared.infrastructure.urls import app_url
|
||||
|
||||
nav_parts: list[str] = []
|
||||
if entry_posts:
|
||||
post_links = ""
|
||||
for ep in entry_posts:
|
||||
ep_slug = getattr(ep, "slug", "")
|
||||
ep_title = getattr(ep, "title", "")
|
||||
feat = getattr(ep, "feature_image", None)
|
||||
href = app_url("blog", f"/{ep_slug}/")
|
||||
if feat:
|
||||
img_html = sx_call("events-post-img", src=feat, alt=ep_title)
|
||||
else:
|
||||
img_html = sx_call("events-post-img-placeholder")
|
||||
post_links += sx_call(
|
||||
"events-entry-nav-post-link",
|
||||
href=href, img=SxExpr(img_html), title=ep_title,
|
||||
)
|
||||
nav_parts.append(
|
||||
sx_call("events-entry-posts-nav-oob", items=SxExpr(post_links))
|
||||
.replace(' :hx-swap-oob "true"', '')
|
||||
)
|
||||
|
||||
if is_admin:
|
||||
admin_url = url_for(
|
||||
"calendar.day.calendar_entries.calendar_entry.admin.admin",
|
||||
calendar_slug=cal_slug,
|
||||
day=day, month=month, year=year, entry_id=entry.id,
|
||||
)
|
||||
nav_parts.append(sx_call("events-entry-admin-link", href=admin_url))
|
||||
|
||||
# Entry admin nav (ticket_types link)
|
||||
admin_href = url_for(
|
||||
"calendar.day.calendar_entries.calendar_entry.admin.admin",
|
||||
calendar_slug=cal_slug,
|
||||
day=day, month=month, year=year, entry_id=entry.id,
|
||||
) if is_admin else ""
|
||||
|
||||
ticket_types_href = url_for(
|
||||
"calendar.day.calendar_entries.calendar_entry.ticket_types.get",
|
||||
calendar_slug=cal_slug, entry_id=entry.id,
|
||||
year=year, month=month, day=day,
|
||||
)
|
||||
|
||||
from quart import current_app
|
||||
select_colours = current_app.jinja_env.globals.get("select_colours", "")
|
||||
|
||||
return {
|
||||
"id": str(entry.id),
|
||||
"name": entry.name or "",
|
||||
"time-str": time_str,
|
||||
"link-href": link_href,
|
||||
"nav": SxExpr("".join(nav_parts)) if nav_parts else NIL,
|
||||
"admin-href": admin_href,
|
||||
"ticket-types-href": ticket_types_href,
|
||||
"is-admin": is_admin,
|
||||
"select-colours": select_colours,
|
||||
}
|
||||
|
||||
|
||||
async def _io_events_slot_ctx(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> dict[str, Any]:
|
||||
"""``(events-slot-ctx)`` → dict with events slot header values."""
|
||||
from quart import g
|
||||
dctx = getattr(g, "_defpage_ctx", None) or {}
|
||||
slot = getattr(g, "slot", None) or dctx.get("slot")
|
||||
if not slot:
|
||||
return {"name": ""}
|
||||
return {
|
||||
"name": getattr(slot, "name", "") or "",
|
||||
"description": getattr(slot, "description", "") or "",
|
||||
}
|
||||
|
||||
|
||||
async def _io_events_ticket_type_ctx(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> dict[str, Any]:
|
||||
"""``(events-ticket-type-ctx)`` → dict with ticket type header values."""
|
||||
from quart import g, url_for
|
||||
|
||||
dctx = getattr(g, "_defpage_ctx", None) or {}
|
||||
cal = getattr(g, "calendar", None) or dctx.get("calendar")
|
||||
entry = getattr(g, "entry", None) or dctx.get("entry")
|
||||
ticket_type = getattr(g, "ticket_type", None) or dctx.get("ticket_type")
|
||||
if not cal or not entry or not ticket_type:
|
||||
return {"id": ""}
|
||||
|
||||
cal_slug = getattr(cal, "slug", "") or ""
|
||||
day = dctx.get("day")
|
||||
month = dctx.get("month")
|
||||
year = dctx.get("year")
|
||||
|
||||
link_href = url_for(
|
||||
"calendar.day.calendar_entries.calendar_entry.ticket_types.ticket_type.get",
|
||||
calendar_slug=cal_slug, year=year, month=month, day=day,
|
||||
entry_id=entry.id, ticket_type_id=ticket_type.id,
|
||||
)
|
||||
|
||||
return {
|
||||
"id": str(ticket_type.id),
|
||||
"name": getattr(ticket_type, "name", "") or "",
|
||||
"link-href": link_href,
|
||||
}
|
||||
|
||||
|
||||
async def _io_market_header_ctx(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> dict[str, Any]:
|
||||
"""``(market-header-ctx)`` → dict with market header values.
|
||||
|
||||
Pre-builds desktop-nav and mobile-nav as SxExpr strings using
|
||||
the existing Python helper functions in sxc.pages.layouts.
|
||||
"""
|
||||
from quart import g, url_for
|
||||
from shared.config import config as get_config
|
||||
from .parser import SxExpr
|
||||
|
||||
cfg = get_config()
|
||||
market_title = cfg.get("market_title", "")
|
||||
link_href = url_for("defpage_market_home")
|
||||
|
||||
# Get categories if market is loaded
|
||||
market = getattr(g, "market", None)
|
||||
categories = {}
|
||||
if market:
|
||||
from bp.browse.services.nav import get_nav
|
||||
nav_data = await get_nav(g.s, market_id=market.id)
|
||||
categories = nav_data.get("cats", {})
|
||||
|
||||
# Build minimal ctx for existing helper functions
|
||||
select_colours = getattr(g, "select_colours", "")
|
||||
if not select_colours:
|
||||
from quart import current_app
|
||||
select_colours = current_app.jinja_env.globals.get("select_colours", "")
|
||||
rights = getattr(g, "rights", None) or {}
|
||||
|
||||
mini_ctx: dict[str, Any] = {
|
||||
"market_title": market_title,
|
||||
"top_slug": "",
|
||||
"sub_slug": "",
|
||||
"categories": categories,
|
||||
"qs": "",
|
||||
"hx_select_search": "#main-panel",
|
||||
"select_colours": select_colours,
|
||||
"rights": rights,
|
||||
"category_label": "",
|
||||
}
|
||||
|
||||
# Pre-build nav using existing helper functions (lazy import from market service)
|
||||
from sxc.pages.layouts import _desktop_category_nav_sx, _mobile_nav_panel_sx
|
||||
desktop_nav = _desktop_category_nav_sx(mini_ctx, categories, "", "#main-panel")
|
||||
mobile_nav = _mobile_nav_panel_sx(mini_ctx)
|
||||
|
||||
return {
|
||||
"market-title": market_title,
|
||||
"link-href": link_href,
|
||||
"top-slug": "",
|
||||
"sub-slug": "",
|
||||
"desktop-nav": SxExpr(desktop_nav) if desktop_nav else "",
|
||||
"mobile-nav": SxExpr(mobile_nav) if mobile_nav else "",
|
||||
}
|
||||
|
||||
|
||||
_IO_HANDLERS: dict[str, Any] = {
|
||||
"frag": _io_frag,
|
||||
"query": _io_query,
|
||||
@@ -578,4 +952,13 @@ _IO_HANDLERS: dict[str, Any] = {
|
||||
"select-colours": _io_select_colours,
|
||||
"account-nav-ctx": _io_account_nav_ctx,
|
||||
"app-rights": _io_app_rights,
|
||||
"federation-actor-ctx": _io_federation_actor_ctx,
|
||||
"request-view-args": _io_request_view_args,
|
||||
"cart-page-ctx": _io_cart_page_ctx,
|
||||
"events-calendar-ctx": _io_events_calendar_ctx,
|
||||
"events-day-ctx": _io_events_day_ctx,
|
||||
"events-entry-ctx": _io_events_entry_ctx,
|
||||
"events-slot-ctx": _io_events_slot_ctx,
|
||||
"events-ticket-type-ctx": _io_events_ticket_type_ctx,
|
||||
"market-header-ctx": _io_market_header_ctx,
|
||||
}
|
||||
|
||||
@@ -1,17 +1,92 @@
|
||||
;; SX docs layout defcomps — root header via ~root-header-auto,
|
||||
;; sx-specific headers passed as &key params.
|
||||
;; SX docs layout defcomps — fully self-contained via IO primitives.
|
||||
;; Registered via register_sx_layout in __init__.py.
|
||||
|
||||
;; --- SX home layout: root + sx menu row ---
|
||||
;; --- Main nav defcomp: static nav items from MAIN_NAV ---
|
||||
;; @css aria-selected:bg-violet-200 aria-selected:text-violet-900
|
||||
|
||||
(defcomp ~sx-layout-full (&key sx-row)
|
||||
(defcomp ~sx-main-nav (&key section)
|
||||
(let* ((sc "aria-selected:bg-violet-200 aria-selected:text-violet-900")
|
||||
(items (list
|
||||
(dict :label "Docs" :href "/docs/introduction")
|
||||
(dict :label "Reference" :href "/reference/")
|
||||
(dict :label "Protocols" :href "/protocols/wire-format")
|
||||
(dict :label "Examples" :href "/examples/click-to-load")
|
||||
(dict :label "Essays" :href "/essays/sx-sucks"))))
|
||||
(<> (map (lambda (item)
|
||||
(~nav-link
|
||||
:href (get item "href")
|
||||
:label (get item "label")
|
||||
:is-selected (when (= (get item "label") section) "true")
|
||||
:select-colours sc))
|
||||
items))))
|
||||
|
||||
;; --- SX header row ---
|
||||
|
||||
(defcomp ~sx-header-row (&key nav child oob)
|
||||
(~menu-row-sx :id "sx-row" :level 1 :colour "violet"
|
||||
:link-href "/" :link-label "sx"
|
||||
:link-label-content (~sx-docs-label)
|
||||
:nav nav
|
||||
:child-id "sx-header-child"
|
||||
:child child
|
||||
:oob oob))
|
||||
|
||||
;; --- Sub-row for section pages ---
|
||||
|
||||
(defcomp ~sx-sub-row (&key sub-label sub-href sub-nav selected oob)
|
||||
(~menu-row-sx :id "sx-sub-row" :level 2 :colour "violet"
|
||||
:link-href sub-href :link-label sub-label
|
||||
:selected selected
|
||||
:nav sub-nav
|
||||
:oob oob))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; SX home layout (root + sx header)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~sx-layout-full (&key section)
|
||||
(<> (~root-header-auto)
|
||||
sx-row))
|
||||
(~sx-header-row :nav (~sx-main-nav :section section))))
|
||||
|
||||
(defcomp ~sx-layout-oob (&key root-header sx-row)
|
||||
(<> root-header sx-row))
|
||||
(defcomp ~sx-layout-oob (&key section)
|
||||
(<> (~sx-header-row
|
||||
:nav (~sx-main-nav :section section)
|
||||
:oob true)
|
||||
(~clear-oob-div :id "sx-header-child")
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; --- SX section layout: root + sx row (with child sub-row) ---
|
||||
(defcomp ~sx-layout-mobile (&key section)
|
||||
(<> (~mobile-menu-section
|
||||
:label "sx" :href "/" :level 1 :colour "violet"
|
||||
:items (~sx-main-nav :section section))
|
||||
(~root-mobile-auto)))
|
||||
|
||||
(defcomp ~sx-section-layout-full (&key sx-row)
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; SX section layout (root + sx header + sub-row)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~sx-section-layout-full (&key section sub-label sub-href sub-nav selected)
|
||||
(<> (~root-header-auto)
|
||||
sx-row))
|
||||
(~sx-header-row
|
||||
:nav (~sx-main-nav :section section)
|
||||
:child (~sx-sub-row :sub-label sub-label :sub-href sub-href
|
||||
:sub-nav sub-nav :selected selected))))
|
||||
|
||||
(defcomp ~sx-section-layout-oob (&key section sub-label sub-href sub-nav selected)
|
||||
(<> (~sx-sub-row :sub-label sub-label :sub-href sub-href
|
||||
:sub-nav sub-nav :selected selected :oob true)
|
||||
(~sx-header-row
|
||||
:nav (~sx-main-nav :section section)
|
||||
:oob true)
|
||||
(~root-header-auto true)))
|
||||
|
||||
(defcomp ~sx-section-layout-mobile (&key section sub-label sub-href sub-nav)
|
||||
(<>
|
||||
(when sub-nav
|
||||
(~mobile-menu-section
|
||||
:label (or sub-label section) :href sub-href :level 2 :colour "violet"
|
||||
:items sub-nav))
|
||||
(~mobile-menu-section
|
||||
:label "sx" :href "/" :level 1 :colour "violet"
|
||||
:items (~sx-main-nav :section section))
|
||||
(~root-mobile-auto)))
|
||||
|
||||
@@ -1,108 +1,11 @@
|
||||
"""Layout registration and header/mobile functions for sx docs."""
|
||||
"""SX docs layout registration — all layouts delegate to .sx defcomps."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .utils import _main_nav_sx, _sx_header_sx, _sub_row_sx
|
||||
|
||||
def _register_sx_layouts() -> None:
|
||||
"""Register the sx docs layout presets."""
|
||||
from shared.sx.layouts import register_custom_layout
|
||||
from shared.sx.layouts import register_sx_layout
|
||||
|
||||
register_custom_layout("sx", _sx_full_headers, _sx_oob_headers, _sx_mobile)
|
||||
register_custom_layout("sx-section", _sx_section_full_headers, _sx_section_oob_headers, _sx_section_mobile)
|
||||
|
||||
|
||||
async def _sx_full_headers(ctx: dict, **kw: Any) -> str:
|
||||
"""Full headers for sx home page: root + sx menu row."""
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
|
||||
main_nav = _main_nav_sx(kw.get("section"))
|
||||
sx_row = _sx_header_sx(main_nav)
|
||||
return await render_to_sx_with_env("sx-layout-full", {},
|
||||
sx_row=sx_row)
|
||||
|
||||
|
||||
async def _sx_oob_headers(ctx: dict, **kw: Any) -> str:
|
||||
"""OOB headers for sx home page."""
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
|
||||
main_nav = _main_nav_sx(kw.get("section"))
|
||||
sx_row = _sx_header_sx(main_nav)
|
||||
rows = await render_to_sx_with_env("sx-layout-full", {},
|
||||
sx_row=sx_row)
|
||||
return await oob_header_sx("root-header-child", "sx-header-child", rows)
|
||||
|
||||
|
||||
async def _sx_section_full_headers(ctx: dict, **kw: Any) -> str:
|
||||
"""Full headers for sx section pages: root + sx row + sub row."""
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
|
||||
section = kw.get("section", "")
|
||||
sub_label = kw.get("sub_label", section)
|
||||
sub_href = kw.get("sub_href", "/")
|
||||
sub_nav = kw.get("sub_nav", "")
|
||||
selected = kw.get("selected", "")
|
||||
|
||||
main_nav = _main_nav_sx(section)
|
||||
sub_row = _sub_row_sx(sub_label, sub_href, sub_nav, selected)
|
||||
sx_row = _sx_header_sx(main_nav, child=sub_row)
|
||||
return await render_to_sx_with_env("sx-section-layout-full", {},
|
||||
sx_row=sx_row)
|
||||
|
||||
|
||||
async def _sx_section_oob_headers(ctx: dict, **kw: Any) -> str:
|
||||
"""OOB headers for sx section pages."""
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
|
||||
section = kw.get("section", "")
|
||||
sub_label = kw.get("sub_label", section)
|
||||
sub_href = kw.get("sub_href", "/")
|
||||
sub_nav = kw.get("sub_nav", "")
|
||||
selected = kw.get("selected", "")
|
||||
|
||||
main_nav = _main_nav_sx(section)
|
||||
sub_row = _sub_row_sx(sub_label, sub_href, sub_nav, selected)
|
||||
sx_row = _sx_header_sx(main_nav, child=sub_row)
|
||||
rows = await render_to_sx_with_env("sx-section-layout-full", {},
|
||||
sx_row=sx_row)
|
||||
return await oob_header_sx("root-header-child", "sx-header-child", rows)
|
||||
|
||||
|
||||
async def _sx_mobile(ctx: dict, **kw: Any) -> str:
|
||||
"""Mobile menu for sx home page: main nav + root."""
|
||||
from shared.sx.helpers import (
|
||||
mobile_menu_sx, mobile_root_nav_sx, sx_call, SxExpr,
|
||||
)
|
||||
|
||||
main_nav = _main_nav_sx(kw.get("section"))
|
||||
return mobile_menu_sx(
|
||||
sx_call("mobile-menu-section",
|
||||
label="sx", href="/", level=1, colour="violet",
|
||||
items=SxExpr(main_nav)),
|
||||
await mobile_root_nav_sx(ctx),
|
||||
)
|
||||
|
||||
|
||||
async def _sx_section_mobile(ctx: dict, **kw: Any) -> str:
|
||||
"""Mobile menu for sx section pages: sub nav + main nav + root."""
|
||||
from shared.sx.helpers import (
|
||||
mobile_menu_sx, mobile_root_nav_sx, sx_call, SxExpr,
|
||||
)
|
||||
|
||||
section = kw.get("section", "")
|
||||
sub_label = kw.get("sub_label", section)
|
||||
sub_href = kw.get("sub_href", "/")
|
||||
sub_nav = kw.get("sub_nav", "")
|
||||
main_nav = _main_nav_sx(section)
|
||||
|
||||
parts = []
|
||||
if sub_nav:
|
||||
parts.append(sx_call("mobile-menu-section",
|
||||
label=sub_label, href=sub_href, level=2, colour="violet",
|
||||
items=SxExpr(sub_nav)))
|
||||
parts.append(sx_call("mobile-menu-section",
|
||||
label="sx", href="/", level=1, colour="violet",
|
||||
items=SxExpr(main_nav)))
|
||||
parts.append(await mobile_root_nav_sx(ctx))
|
||||
return mobile_menu_sx(*parts)
|
||||
register_sx_layout("sx", "sx-layout-full", "sx-layout-oob", "sx-layout-mobile")
|
||||
register_sx_layout("sx-section", "sx-section-layout-full",
|
||||
"sx-section-layout-oob", "sx-section-layout-mobile")
|
||||
|
||||
Reference in New Issue
Block a user