# --- Navigation items ---
def billing_nav_items():
"""Top NavBar links β hx-boost on Layout handles HTMX automatically."""
return [
A("Dashboard", href="/billing"),
A("Checkout", href="/billing/checkout"),
A("Status", href="/billing/status"),
]
def billing_sidebar_items():
"""Sidebar links with icons β hx-boost on Layout handles HTMX automatically."""
return [
A(Icon("dashboard"), Span("Dashboard"), href="/billing", cls="nav-link"),
A(Icon("shopping_cart"), Span("Checkout"), href="/billing/checkout", cls="nav-link"),
A(Icon("receipt_long"), Span("Status"), href="/billing/status", cls="nav-link"),
]
# Highlights the active sidebar link after each HTMX navigation
active_nav_script = Script("""
function updateActiveNav() {
document.querySelectorAll('.nav-link').forEach(link => {
link.classList.toggle('active', link.getAttribute('href') === window.location.pathname);
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', updateActiveNav);
} else {
updateActiveNav();
}
document.body.addEventListener('htmx:afterSettle', updateActiveNav);
""")
# --- App shell: TrialBanner sits above the full Layout ---
def get_billing_layout(content, days_remaining=5):
banner = TrialBanner(
days_remaining=days_remaining,
end_date_text="Trial ends February 26, 2026",
cta_text="Upgrade Now",
cta_href="/billing/checkout",
)
return Div(
banner,
Layout(
content,
sidebar_links=billing_sidebar_items(),
nav_bar=NavBar(*billing_nav_items(), brand=H3("BillingDemo"), sticky=True),
),
active_nav_script,
)
# --- Page content builders ---
def billing_dashboard_content():
"""Main billing dashboard: status card + checkout side-by-side."""
return Div(
Grid(
GridCell(
BillingStatusCard(
status="trialing",
plan_label="Pro Plan",
current_period_end="February 26, 2026",
manage_url="/billing/status",
),
span="s12 m6",
),
GridCell(
CheckoutCard(
monthly_price=19.99,
yearly_price=199.99,
plan_id="pro",
trial_days=5,
cta_text="Subscribe Now",
form_action="/billing/checkout",
),
span="s12 m6",
),
),
cls="medium-space",
)
def billing_checkout_content():
"""Dedicated checkout page with a single centered CheckoutCard."""
return Grid(
GridCell(span="s0 m2 l3"),
GridCell(
H3("Complete Your Purchase"),
CheckoutCard(
monthly_price=9.99,
yearly_price=99.99,
plan_id="pro",
trial_days=14,
hidden_fields={"coupon": "WELCOME10"},
fine_print="Cancel anytime. No questions asked.",
form_action="/billing/checkout",
),
span="s12 m8 l6",
),
GridCell(span="s0 m2 l3"),
)
def billing_status_content():
"""All five subscription states for visual QA."""
statuses = [
("active", "Pro Plan", "March 15, 2026"),
("trialing", "Pro Plan", "March 1, 2026"),
("past_due", "Pro Plan", "February 15, 2026"),
("canceled", "Pro Plan", ""),
("checkout_pending", "", ""),
]
cards = [
BillingStatusCard(
status=s, plan_label=label, current_period_end=end,
manage_url="/billing" if s in ("active", "past_due") else "",
)
for s, label, end in statuses
]
return Div(
H3("All Subscription States"),
Div(*cards, cls="medium-space"),
cls="medium-space",
)
# --- Routes ---
@rt("/billing")
def get(req):
content = billing_dashboard_content()
if "HX-Request" in req.headers: return content
return get_billing_layout(content)
@rt("/billing/checkout")
def get(req):
content = billing_checkout_content()
if "HX-Request" in req.headers: return content
return get_billing_layout(content)
@rt("/billing/status")
def get(req):
content = billing_status_content()
if "HX-Request" in req.headers: return content
return get_billing_layout(content, days_remaining=2) # warning tone on status page
# In-notebook preview
preview(get_billing_layout(billing_dashboard_content()))