Migrate all apps to defpage declarative page routes
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m41s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m41s
Replace Python GET page handlers with declarative defpage definitions in .sx files across all 8 apps (sx docs, orders, account, market, cart, federation, events, blog). Each app now has sxc/pages/ with setup functions, layout registrations, page helpers, and .sx defpage declarations. Core infrastructure: add g I/O primitive, PageDef support for auth/layout/ data/content/filter/aside/menu slots, post_author auth level, and custom layout registration. Remove ~1400 lines of render_*_page/render_*_oob boilerplate. Update all endpoint references in routes, sx_components, and templates to defpage_* naming. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,10 @@ from shared.sx.helpers import (
|
||||
search_mobile_sx,
|
||||
search_desktop_sx,
|
||||
full_page_sx,
|
||||
mobile_menu_sx,
|
||||
mobile_root_nav_sx,
|
||||
post_mobile_nav_sx,
|
||||
post_admin_mobile_nav_sx,
|
||||
)
|
||||
|
||||
# Load blog service .sx component definitions + handler definitions
|
||||
@@ -76,6 +80,15 @@ def _post_admin_header_sx(ctx: dict, *, oob: bool = False, selected: str = "") -
|
||||
return post_admin_header_sx(ctx, slug, oob=oob, selected=selected)
|
||||
|
||||
|
||||
def _post_admin_mobile_menu(ctx: dict, selected: str = "") -> str:
|
||||
"""Full mobile menu for any post admin page (admin + post + root)."""
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return mobile_menu_sx(
|
||||
post_admin_mobile_nav_sx(ctx, slug, selected),
|
||||
post_mobile_nav_sx(ctx),
|
||||
mobile_root_nav_sx(ctx),
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Settings header (root-header-child -> root-settings-header-child)
|
||||
@@ -85,7 +98,7 @@ def _settings_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Settings header row with admin icon and nav links (sx)."""
|
||||
from quart import url_for as qurl
|
||||
|
||||
settings_href = qurl("settings.home")
|
||||
settings_href = qurl("settings.defpage_settings_home")
|
||||
label_sx = sx_call("blog-admin-label")
|
||||
nav_sx = _settings_nav_sx(ctx)
|
||||
|
||||
@@ -107,10 +120,10 @@ def _settings_nav_sx(ctx: dict) -> str:
|
||||
parts = []
|
||||
|
||||
for endpoint, icon, label in [
|
||||
("menu_items.list_menu_items", "bars", "Menu Items"),
|
||||
("snippets.list_snippets", "puzzle-piece", "Snippets"),
|
||||
("blog.tag_groups_admin.index", "tags", "Tag Groups"),
|
||||
("settings.cache", "refresh", "Cache"),
|
||||
("menu_items.defpage_menu_items_page", "bars", "Menu Items"),
|
||||
("snippets.defpage_snippets_page", "puzzle-piece", "Snippets"),
|
||||
("blog.tag_groups_admin.defpage_tag_groups_page", "tags", "Tag Groups"),
|
||||
("settings.defpage_cache_page", "refresh", "Cache"),
|
||||
]:
|
||||
href = qurl(endpoint)
|
||||
parts.append(sx_call("nav-link",
|
||||
@@ -679,7 +692,7 @@ def _post_main_panel_sx(ctx: dict) -> str:
|
||||
if post.get("status") == "draft":
|
||||
edit_sx = ""
|
||||
if is_admin or (user and post.get("user_id") == getattr(user, "id", None)):
|
||||
edit_href = qurl("blog.post.admin.edit", slug=slug)
|
||||
edit_href = qurl("blog.post.admin.defpage_post_edit", slug=slug)
|
||||
edit_sx = sx_call("blog-detail-edit-link",
|
||||
href=edit_href, hx_select=hx_select,
|
||||
)
|
||||
@@ -951,7 +964,7 @@ def _tag_groups_main_panel_sx(ctx: dict) -> str:
|
||||
g_colour = getattr(group, "colour", None) if hasattr(group, "colour") else group.get("colour")
|
||||
g_sort = getattr(group, "sort_order", 0) if hasattr(group, "sort_order") else group.get("sort_order", 0)
|
||||
|
||||
edit_href = qurl("blog.tag_groups_admin.edit", id=g_id)
|
||||
edit_href = qurl("blog.tag_groups_admin.defpage_tag_group_edit", id=g_id)
|
||||
|
||||
if g_fi:
|
||||
icon = sx_call("blog-tag-group-icon-image", src=g_fi, name=g_name)
|
||||
@@ -1053,7 +1066,7 @@ async def render_home_page(ctx: dict) -> str:
|
||||
header_rows = "(<> " + root_hdr + " " + post_hdr + ")"
|
||||
content = _home_main_panel_sx(ctx)
|
||||
meta = _post_meta_sx(ctx)
|
||||
menu = ctx.get("nav_sx", "") or ""
|
||||
menu = mobile_menu_sx(post_mobile_nav_sx(ctx), mobile_root_nav_sx(ctx))
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content,
|
||||
meta=meta, menu=menu)
|
||||
|
||||
@@ -1088,9 +1101,8 @@ async def render_blog_oob(ctx: dict) -> str:
|
||||
content = _blog_main_panel_sx(ctx)
|
||||
aside = _blog_aside_sx(ctx)
|
||||
filter_sx = _blog_filter_sx(ctx)
|
||||
nav = ctx.get("nav_sx", "") or ""
|
||||
return oob_page_sx(oobs=header_oob, content=content, aside=aside,
|
||||
filter=filter_sx, menu=nav)
|
||||
filter=filter_sx)
|
||||
|
||||
|
||||
async def render_blog_cards(ctx: dict) -> str:
|
||||
@@ -1304,15 +1316,6 @@ async def render_new_post_page(ctx: dict) -> str:
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content)
|
||||
|
||||
|
||||
async def render_new_post_oob(ctx: dict) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
blog_hdr = _blog_header_sx(ctx)
|
||||
rows = "(<> " + root_hdr + " " + blog_hdr + ")"
|
||||
header_oob = _oob_header_sx("root-header-child", "blog-header-child", rows)
|
||||
content = ctx.get("editor_html", "")
|
||||
return oob_page_sx(oobs=header_oob, content=content)
|
||||
|
||||
|
||||
# ---- Post detail ----
|
||||
|
||||
async def render_post_page(ctx: dict) -> str:
|
||||
@@ -1321,7 +1324,7 @@ async def render_post_page(ctx: dict) -> str:
|
||||
header_rows = "(<> " + root_hdr + " " + post_hdr + ")"
|
||||
content = _post_main_panel_sx(ctx)
|
||||
meta = _post_meta_sx(ctx)
|
||||
menu = ctx.get("nav_sx", "") or ""
|
||||
menu = mobile_menu_sx(post_mobile_nav_sx(ctx), mobile_root_nav_sx(ctx))
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content,
|
||||
meta=meta, menu=menu)
|
||||
|
||||
@@ -1332,35 +1335,14 @@ async def render_post_oob(ctx: dict) -> str:
|
||||
rows = "(<> " + root_hdr + " " + post_hdr + ")"
|
||||
post_oob = _oob_header_sx("root-header-child", "post-header-child", rows)
|
||||
content = _post_main_panel_sx(ctx)
|
||||
menu = ctx.get("nav_sx", "") or ""
|
||||
menu = mobile_menu_sx(post_mobile_nav_sx(ctx), mobile_root_nav_sx(ctx))
|
||||
oobs = post_oob
|
||||
return oob_page_sx(oobs=oobs, content=content, menu=menu)
|
||||
|
||||
|
||||
# ---- Post admin ----
|
||||
|
||||
async def render_post_admin_page(ctx: dict) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
post_hdr = _post_header_sx(ctx)
|
||||
admin_hdr = _post_admin_header_sx(ctx)
|
||||
header_rows = "(<> " + root_hdr + " " + post_hdr + " " + admin_hdr + ")"
|
||||
content = _post_admin_main_panel_sx(ctx)
|
||||
menu = ctx.get("nav_sx", "") or ""
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content,
|
||||
menu=menu)
|
||||
|
||||
|
||||
async def render_post_admin_oob(ctx: dict) -> str:
|
||||
post_hdr_oob = _post_header_sx(ctx, oob=True)
|
||||
admin_oob = _oob_header_sx("post-header-child", "post-admin-header-child",
|
||||
_post_admin_header_sx(ctx))
|
||||
content = _post_admin_main_panel_sx(ctx)
|
||||
menu = ctx.get("nav_sx", "") or ""
|
||||
oobs = "(<> " + post_hdr_oob + " " + admin_oob + ")"
|
||||
return oob_page_sx(oobs=oobs, content=content, menu=menu)
|
||||
|
||||
|
||||
# ---- Post data ----
|
||||
# ===========================================================================
|
||||
|
||||
def _post_data_content_sx(ctx: dict) -> str:
|
||||
"""Build post data inspector panel natively (replaces _types/post_data/_main_panel.html)."""
|
||||
@@ -1478,22 +1460,7 @@ def _post_data_content_sx(ctx: dict) -> str:
|
||||
return _raw_html_sx(html)
|
||||
|
||||
|
||||
async def render_post_data_page(ctx: dict) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
post_hdr = _post_header_sx(ctx)
|
||||
admin_hdr = _post_admin_header_sx(ctx, selected="data")
|
||||
header_rows = "(<> " + root_hdr + " " + post_hdr + " " + admin_hdr + ")"
|
||||
content = _post_data_content_sx(ctx)
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content)
|
||||
|
||||
|
||||
async def render_post_data_oob(ctx: dict) -> str:
|
||||
admin_hdr_oob = _post_admin_header_sx(ctx, oob=True, selected="data")
|
||||
content = _post_data_content_sx(ctx)
|
||||
return oob_page_sx(oobs=admin_hdr_oob, content=content)
|
||||
|
||||
|
||||
# ---- Post preview ----
|
||||
# ===========================================================================
|
||||
|
||||
def _preview_main_panel_sx(ctx: dict) -> str:
|
||||
"""Build the preview panel with 4 expandable sections."""
|
||||
@@ -1540,22 +1507,7 @@ def _preview_main_panel_sx(ctx: dict) -> str:
|
||||
return sx_call("blog-preview-panel", sections=SxExpr(f"(<> {inner})"))
|
||||
|
||||
|
||||
async def render_post_preview_page(ctx: dict) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
post_hdr = _post_header_sx(ctx)
|
||||
admin_hdr = _post_admin_header_sx(ctx, selected="preview")
|
||||
header_rows = "(<> " + root_hdr + " " + post_hdr + " " + admin_hdr + ")"
|
||||
content = _preview_main_panel_sx(ctx)
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content)
|
||||
|
||||
|
||||
async def render_post_preview_oob(ctx: dict) -> str:
|
||||
admin_hdr_oob = _post_admin_header_sx(ctx, oob=True, selected="preview")
|
||||
content = _preview_main_panel_sx(ctx)
|
||||
return oob_page_sx(oobs=admin_hdr_oob, content=content)
|
||||
|
||||
|
||||
# ---- Post entries ----
|
||||
# ===========================================================================
|
||||
|
||||
def _post_entries_content_sx(ctx: dict) -> str:
|
||||
"""Build post entries panel natively (replaces _types/post_entries/_main_panel.html)."""
|
||||
@@ -1613,21 +1565,6 @@ def _post_entries_content_sx(ctx: dict) -> str:
|
||||
)
|
||||
|
||||
|
||||
async def render_post_entries_page(ctx: dict) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
post_hdr = _post_header_sx(ctx)
|
||||
admin_hdr = _post_admin_header_sx(ctx, selected="entries")
|
||||
header_rows = "(<> " + root_hdr + " " + post_hdr + " " + admin_hdr + ")"
|
||||
content = _post_entries_content_sx(ctx)
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content)
|
||||
|
||||
|
||||
async def render_post_entries_oob(ctx: dict) -> str:
|
||||
admin_hdr_oob = _post_admin_header_sx(ctx, oob=True, selected="entries")
|
||||
content = _post_entries_content_sx(ctx)
|
||||
return oob_page_sx(oobs=admin_hdr_oob, content=content)
|
||||
|
||||
|
||||
# ---- Calendar view (for entries browser) ----
|
||||
|
||||
def render_calendar_view(
|
||||
@@ -2045,22 +1982,7 @@ def _post_edit_content_sx(ctx: dict) -> str:
|
||||
return _raw_html_sx("".join(parts))
|
||||
|
||||
|
||||
async def render_post_edit_page(ctx: dict) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
post_hdr = _post_header_sx(ctx)
|
||||
admin_hdr = _post_admin_header_sx(ctx, selected="edit")
|
||||
header_rows = "(<> " + root_hdr + " " + post_hdr + " " + admin_hdr + ")"
|
||||
content = _post_edit_content_sx(ctx)
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content)
|
||||
|
||||
|
||||
async def render_post_edit_oob(ctx: dict) -> str:
|
||||
admin_hdr_oob = _post_admin_header_sx(ctx, oob=True, selected="edit")
|
||||
content = _post_edit_content_sx(ctx)
|
||||
return oob_page_sx(oobs=admin_hdr_oob, content=content)
|
||||
|
||||
|
||||
# ---- Post settings ----
|
||||
# ===========================================================================
|
||||
|
||||
def _post_settings_content_sx(ctx: dict) -> str:
|
||||
"""Build settings form natively (replaces _types/post_settings/_main_panel.html)."""
|
||||
@@ -2195,189 +2117,17 @@ def _post_settings_content_sx(ctx: dict) -> str:
|
||||
return _raw_html_sx(html)
|
||||
|
||||
|
||||
async def render_post_settings_page(ctx: dict) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
post_hdr = _post_header_sx(ctx)
|
||||
admin_hdr = _post_admin_header_sx(ctx, selected="settings")
|
||||
header_rows = "(<> " + root_hdr + " " + post_hdr + " " + admin_hdr + ")"
|
||||
content = _post_settings_content_sx(ctx)
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content)
|
||||
# ===========================================================================
|
||||
|
||||
# ===========================================================================
|
||||
|
||||
async def render_post_settings_oob(ctx: dict) -> str:
|
||||
admin_hdr_oob = _post_admin_header_sx(ctx, oob=True, selected="settings")
|
||||
content = _post_settings_content_sx(ctx)
|
||||
return oob_page_sx(oobs=admin_hdr_oob, content=content)
|
||||
# ===========================================================================
|
||||
|
||||
# ===========================================================================
|
||||
|
||||
# ---- Settings home ----
|
||||
|
||||
async def render_settings_page(ctx: dict) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
settings_hdr = _settings_header_sx(ctx)
|
||||
header_rows = "(<> " + root_hdr + " " + settings_hdr + ")"
|
||||
content = _settings_main_panel_sx(ctx)
|
||||
menu = _settings_nav_sx(ctx)
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content,
|
||||
menu=menu)
|
||||
|
||||
|
||||
async def render_settings_oob(ctx: dict) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
settings_hdr = _settings_header_sx(ctx)
|
||||
rows = "(<> " + root_hdr + " " + settings_hdr + ")"
|
||||
header_oob = _oob_header_sx("root-header-child", "root-settings-header-child", rows)
|
||||
content = _settings_main_panel_sx(ctx)
|
||||
menu = _settings_nav_sx(ctx)
|
||||
return oob_page_sx(oobs=header_oob, content=content, menu=menu)
|
||||
|
||||
|
||||
# ---- Cache ----
|
||||
|
||||
async def render_cache_page(ctx: dict) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
settings_hdr = _settings_header_sx(ctx)
|
||||
from quart import url_for as qurl
|
||||
cache_hdr = _sub_settings_header_sx(
|
||||
"cache-row", "cache-header-child",
|
||||
qurl("settings.cache"), "refresh", "Cache", ctx,
|
||||
)
|
||||
header_rows = "(<> " + root_hdr + " " + settings_hdr + " " + cache_hdr + ")"
|
||||
content = _cache_main_panel_sx(ctx)
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content)
|
||||
|
||||
|
||||
async def render_cache_oob(ctx: dict) -> str:
|
||||
settings_hdr_oob = _settings_header_sx(ctx, oob=True)
|
||||
from quart import url_for as qurl
|
||||
cache_hdr = _sub_settings_header_sx(
|
||||
"cache-row", "cache-header-child",
|
||||
qurl("settings.cache"), "refresh", "Cache", ctx,
|
||||
)
|
||||
cache_oob = _oob_header_sx("root-settings-header-child", "cache-header-child",
|
||||
cache_hdr)
|
||||
content = _cache_main_panel_sx(ctx)
|
||||
oobs = "(<> " + settings_hdr_oob + " " + cache_oob + ")"
|
||||
return oob_page_sx(oobs=oobs, content=content)
|
||||
|
||||
|
||||
# ---- Snippets ----
|
||||
|
||||
async def render_snippets_page(ctx: dict) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
settings_hdr = _settings_header_sx(ctx)
|
||||
from quart import url_for as qurl
|
||||
snippets_hdr = _sub_settings_header_sx(
|
||||
"snippets-row", "snippets-header-child",
|
||||
qurl("snippets.list_snippets"), "puzzle-piece", "Snippets", ctx,
|
||||
)
|
||||
header_rows = "(<> " + root_hdr + " " + settings_hdr + " " + snippets_hdr + ")"
|
||||
content = _snippets_main_panel_sx(ctx)
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content)
|
||||
|
||||
|
||||
async def render_snippets_oob(ctx: dict) -> str:
|
||||
settings_hdr_oob = _settings_header_sx(ctx, oob=True)
|
||||
from quart import url_for as qurl
|
||||
snippets_hdr = _sub_settings_header_sx(
|
||||
"snippets-row", "snippets-header-child",
|
||||
qurl("snippets.list_snippets"), "puzzle-piece", "Snippets", ctx,
|
||||
)
|
||||
snippets_oob = _oob_header_sx("root-settings-header-child", "snippets-header-child",
|
||||
snippets_hdr)
|
||||
content = _snippets_main_panel_sx(ctx)
|
||||
oobs = "(<> " + settings_hdr_oob + " " + snippets_oob + ")"
|
||||
return oob_page_sx(oobs=oobs, content=content)
|
||||
|
||||
|
||||
# ---- Menu items ----
|
||||
|
||||
async def render_menu_items_page(ctx: dict) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
settings_hdr = _settings_header_sx(ctx)
|
||||
from quart import url_for as qurl
|
||||
mi_hdr = _sub_settings_header_sx(
|
||||
"menu_items-row", "menu_items-header-child",
|
||||
qurl("menu_items.list_menu_items"), "bars", "Menu Items", ctx,
|
||||
)
|
||||
header_rows = "(<> " + root_hdr + " " + settings_hdr + " " + mi_hdr + ")"
|
||||
content = _menu_items_main_panel_sx(ctx)
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content)
|
||||
|
||||
|
||||
async def render_menu_items_oob(ctx: dict) -> str:
|
||||
settings_hdr_oob = _settings_header_sx(ctx, oob=True)
|
||||
from quart import url_for as qurl
|
||||
mi_hdr = _sub_settings_header_sx(
|
||||
"menu_items-row", "menu_items-header-child",
|
||||
qurl("menu_items.list_menu_items"), "bars", "Menu Items", ctx,
|
||||
)
|
||||
mi_oob = _oob_header_sx("root-settings-header-child", "menu_items-header-child",
|
||||
mi_hdr)
|
||||
content = _menu_items_main_panel_sx(ctx)
|
||||
oobs = "(<> " + settings_hdr_oob + " " + mi_oob + ")"
|
||||
return oob_page_sx(oobs=oobs, content=content)
|
||||
|
||||
|
||||
# ---- Tag groups ----
|
||||
|
||||
async def render_tag_groups_page(ctx: dict) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
settings_hdr = _settings_header_sx(ctx)
|
||||
from quart import url_for as qurl
|
||||
tg_hdr = _sub_settings_header_sx(
|
||||
"tag-groups-row", "tag-groups-header-child",
|
||||
qurl("blog.tag_groups_admin.index"), "tags", "Tag Groups", ctx,
|
||||
)
|
||||
header_rows = "(<> " + root_hdr + " " + settings_hdr + " " + tg_hdr + ")"
|
||||
content = _tag_groups_main_panel_sx(ctx)
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content)
|
||||
|
||||
|
||||
async def render_tag_groups_oob(ctx: dict) -> str:
|
||||
settings_hdr_oob = _settings_header_sx(ctx, oob=True)
|
||||
from quart import url_for as qurl
|
||||
tg_hdr = _sub_settings_header_sx(
|
||||
"tag-groups-row", "tag-groups-header-child",
|
||||
qurl("blog.tag_groups_admin.index"), "tags", "Tag Groups", ctx,
|
||||
)
|
||||
tg_oob = _oob_header_sx("root-settings-header-child", "tag-groups-header-child",
|
||||
tg_hdr)
|
||||
content = _tag_groups_main_panel_sx(ctx)
|
||||
oobs = "(<> " + settings_hdr_oob + " " + tg_oob + ")"
|
||||
return oob_page_sx(oobs=oobs, content=content)
|
||||
|
||||
|
||||
# ---- Tag group edit ----
|
||||
|
||||
async def render_tag_group_edit_page(ctx: dict) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
settings_hdr = _settings_header_sx(ctx)
|
||||
from quart import url_for as qurl
|
||||
g_id = (ctx.get("group") or {}).get("id") or getattr(ctx.get("group"), "id", None)
|
||||
tg_hdr = _sub_settings_header_sx(
|
||||
"tag-groups-row", "tag-groups-header-child",
|
||||
qurl("blog.tag_groups_admin.edit", id=g_id), "tags", "Tag Groups", ctx,
|
||||
)
|
||||
header_rows = "(<> " + root_hdr + " " + settings_hdr + " " + tg_hdr + ")"
|
||||
content = _tag_groups_edit_main_panel_sx(ctx)
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content)
|
||||
|
||||
|
||||
async def render_tag_group_edit_oob(ctx: dict) -> str:
|
||||
settings_hdr_oob = _settings_header_sx(ctx, oob=True)
|
||||
from quart import url_for as qurl
|
||||
g_id = (ctx.get("group") or {}).get("id") or getattr(ctx.get("group"), "id", None)
|
||||
tg_hdr = _sub_settings_header_sx(
|
||||
"tag-groups-row", "tag-groups-header-child",
|
||||
qurl("blog.tag_groups_admin.edit", id=g_id), "tags", "Tag Groups", ctx,
|
||||
)
|
||||
tg_oob = _oob_header_sx("root-settings-header-child", "tag-groups-header-child",
|
||||
tg_hdr)
|
||||
content = _tag_groups_edit_main_panel_sx(ctx)
|
||||
oobs = "(<> " + settings_hdr_oob + " " + tg_oob + ")"
|
||||
return oob_page_sx(oobs=oobs, content=content)
|
||||
# ===========================================================================
|
||||
|
||||
# ===========================================================================
|
||||
|
||||
# ===========================================================================
|
||||
# PUBLIC API — HTMX fragment renderers for POST/PUT/DELETE handlers
|
||||
|
||||
Reference in New Issue
Block a user