feat: implement Pages as Spaces Phase 1
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 45s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 45s
- Add PageConfig model with feature flags (calendar, market) - Auto-create PageConfig on Ghost page sync - Add create_page() for Ghost /pages/ API endpoint - Add /new-page/ route for creating pages - Add ?type=pages blog filter with Posts|Pages tab toggle - Add list_pages() to DBClient with PageConfig eager loading - Add PUT /<slug>/admin/features/ route for feature toggles - Add feature badges (calendar, market) on page cards - Add features panel to page admin dashboard - Update shared_lib submodule with PageConfig model Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,17 +22,91 @@ def register():
|
||||
@require_admin
|
||||
async def admin(slug: str):
|
||||
from suma_browser.app.utils.htmx import is_htmx_request
|
||||
from models.page_config import PageConfig
|
||||
from sqlalchemy import select as sa_select
|
||||
|
||||
# Load features for page admin
|
||||
post = (g.post_data or {}).get("post", {})
|
||||
features = {}
|
||||
if post.get("is_page"):
|
||||
pc = (await g.s.execute(
|
||||
sa_select(PageConfig).where(PageConfig.post_id == post["id"])
|
||||
)).scalar_one_or_none()
|
||||
if pc:
|
||||
features = pc.features or {}
|
||||
|
||||
ctx = {"features": features}
|
||||
|
||||
# Determine which template to use based on request type
|
||||
if not is_htmx_request():
|
||||
# Normal browser request: full page with layout
|
||||
html = await render_template("_types/post/admin/index.html")
|
||||
html = await render_template("_types/post/admin/index.html", **ctx)
|
||||
else:
|
||||
# HTMX request: main panel + OOB elements
|
||||
html = await render_template("_types/post/admin/_oob_elements.html")
|
||||
html = await render_template("_types/post/admin/_oob_elements.html", **ctx)
|
||||
|
||||
return await make_response(html)
|
||||
|
||||
@bp.put("/features/")
|
||||
@require_admin
|
||||
async def update_features(slug: str):
|
||||
"""Update PageConfig.features for a page."""
|
||||
from models.page_config import PageConfig
|
||||
from models.ghost_content import Post
|
||||
from sqlalchemy import select as sa_select
|
||||
from quart import jsonify
|
||||
import json
|
||||
|
||||
post = g.post_data.get("post")
|
||||
if not post or not post.get("is_page"):
|
||||
return jsonify({"error": "This is not a page."}), 400
|
||||
|
||||
post_id = post["id"]
|
||||
|
||||
# Load PageConfig
|
||||
pc = (await g.s.execute(
|
||||
sa_select(PageConfig).where(PageConfig.post_id == post_id)
|
||||
)).scalar_one_or_none()
|
||||
if pc is None:
|
||||
return jsonify({"error": "PageConfig not found for this page."}), 404
|
||||
|
||||
# Parse request body
|
||||
body = await request.get_json()
|
||||
if body is None:
|
||||
# Fall back to form data
|
||||
form = await request.form
|
||||
body = {}
|
||||
for key in ("calendar", "market"):
|
||||
val = form.get(key)
|
||||
if val is not None:
|
||||
body[key] = val in ("true", "1", "on")
|
||||
|
||||
if not isinstance(body, dict):
|
||||
return jsonify({"error": "Expected JSON object with feature flags."}), 400
|
||||
|
||||
# Merge features
|
||||
features = dict(pc.features or {})
|
||||
for key, val in body.items():
|
||||
if isinstance(val, bool):
|
||||
features[key] = val
|
||||
elif val in ("true", "1", "on"):
|
||||
features[key] = True
|
||||
elif val in ("false", "0", "off", None):
|
||||
features[key] = False
|
||||
|
||||
pc.features = features
|
||||
from sqlalchemy.orm.attributes import flag_modified
|
||||
flag_modified(pc, "features")
|
||||
await g.s.flush()
|
||||
|
||||
# Return updated features panel
|
||||
html = await render_template(
|
||||
"_types/post/admin/_features_panel.html",
|
||||
features=features,
|
||||
post=post,
|
||||
)
|
||||
return await make_response(html)
|
||||
|
||||
@bp.get("/data/")
|
||||
@require_admin
|
||||
async def data(slug: str):
|
||||
|
||||
Reference in New Issue
Block a user