All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m19s
Documents remaining 24 render_template() calls across events, blog, and orders services with phased conversion strategy. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
187 lines
9.2 KiB
Markdown
187 lines
9.2 KiB
Markdown
# Replace Remaining Jinja Templates with S-Expressions
|
|
|
|
## Context
|
|
|
|
The Rose Ash codebase is migrating from Jinja2 HTML templates to the sx s-expression component system. Most routes are already converted. **24 `render_template()` calls remain** across 3 services (events, blog, orders). Email templates (magic_link) stay as Jinja — they render server-side for email delivery, not browser rendering.
|
|
|
|
---
|
|
|
|
## Phase 1: Events Route Wiring (16 calls → 0)
|
|
|
|
The sx render functions **already exist** in `events/sx/sx_components.py`. The routes just haven't been updated to call them.
|
|
|
|
### 1a. `events/bp/slot/routes.py` — 3 calls
|
|
|
|
- `get()`: Replace `render_template("_types/slot/index.html")` / `_oob_elements.html` with `render_slot_page(tctx)` / `render_slot_oob(tctx)` using standard HTMX branch pattern
|
|
- `get_edit()`: Replace `render_template("_types/slot/_edit.html")` with `render_slot_edit_form(slot, g.calendar)` → `sx_response()`
|
|
|
|
### 1b. `events/bp/slots/routes.py` — 2 calls
|
|
|
|
- `add_form()`: Replace with `render_slot_add_form(g.calendar)` → `sx_response()`
|
|
- `add_button()`: Replace with `render_slot_add_button(g.calendar)` → `sx_response()`
|
|
|
|
### 1c. `events/bp/ticket_types/routes.py` — 4 calls
|
|
|
|
- `get()`: Replace full page / OOB with `render_ticket_types_page(tctx)` / `render_ticket_types_oob(tctx)`
|
|
- `add_form()`: Replace with sx render function → `sx_response()`
|
|
- `add_button()`: Replace with sx render function → `sx_response()`
|
|
|
|
### 1d. `events/bp/ticket_type/routes.py` — 3 calls
|
|
|
|
- `get()`: Replace full page / OOB with `render_ticket_type_page(tctx)` / `render_ticket_type_oob(tctx)`
|
|
- `get_edit()`: Replace with `render_ticket_type_edit_form(...)` → `sx_response()`
|
|
|
|
### 1e. `events/bp/calendar_entries/routes.py` — 2 calls
|
|
|
|
- `add_form()`: Replace with `render_entry_add_form(g.calendar, day, month, year, day_slots)` → `sx_response()`
|
|
- `add_button()`: Replace with `render_entry_add_button(g.calendar, day, month, year)` → `sx_response()` (need to check if this exists, create if not)
|
|
|
|
### 1f. `events/bp/calendar_entry/routes.py` — 2 calls
|
|
|
|
- `get_edit()`: Replace with `render_entry_edit_form(entry, g.calendar, day, month, year, day_slots)` → `sx_response()`
|
|
- `search_posts()`: Replace with `render_post_search_results(search_posts, query, page, total_pages, ...)` → `sx_response()`
|
|
|
|
### 1g. `events/bp/calendar_entry/admin/routes.py` — 2 calls
|
|
|
|
- `admin()`: Replace with `render_entry_admin_page(tctx)` / `render_entry_admin_oob(tctx)` using standard HTMX branch pattern
|
|
|
|
### Verification
|
|
|
|
- `./dev.sh events` — starts without errors
|
|
- Navigate to each admin page: slot detail/edit, slots add, ticket types list/add, ticket type detail/edit, calendar entry add/edit, entry admin
|
|
- Verify HTMX interactions: form add/cancel/save, OOB header swaps
|
|
|
|
---
|
|
|
|
## Phase 2: Orders Checkout Return (2 calls → 0)
|
|
|
|
### 2a. Create `orders/sx/checkout.sx`
|
|
|
|
Define `~checkout-return-content` component covering: status heading, order summary (reuse existing `~order-summary-card` etc.), line items, calendar entries, ticket items, status-dependent messaging.
|
|
|
|
### 2b. Add render function in `orders/sx/sx_components.py`
|
|
|
|
- `async def render_checkout_return_page(ctx, order, status, calendar_entries, order_tickets)` — builds header rows + content, wraps in `full_page_sx()`
|
|
- No OOB variant needed (direct-navigation-only page)
|
|
|
|
### 2c. Update `orders/bp/checkout/routes.py`
|
|
|
|
Replace both `render_template("_types/cart/checkout_return.html", ...)` calls with `render_checkout_return_page()`.
|
|
|
|
### Verification
|
|
|
|
- Test checkout flow: paid, failed, missing order states
|
|
- Verify order items and calendar entries display correctly
|
|
|
|
---
|
|
|
|
## Phase 3: Blog Menu Items (3 calls → 0)
|
|
|
|
### 3a. Create `blog/sx/menu_items.sx`
|
|
|
|
- `~menu-item-form` — add/edit form with page search input, selected page display, hidden inputs
|
|
- `~page-search-results` — search result list with infinite scroll sentinel
|
|
- `~page-search-item` — individual page result row
|
|
|
|
### 3b. Add render functions in `blog/sx/sx_components.py`
|
|
|
|
- `def render_menu_item_form(menu_item=None) -> str`
|
|
- `def render_page_search_results(pages, query, page, has_more) -> str`
|
|
|
|
### 3c. Update `blog/bp/menu_items/routes.py`
|
|
|
|
Replace all 3 `render_template()` calls with sx render functions → `sx_response()`.
|
|
|
|
### Verification
|
|
|
|
- Settings > Menu Items > Add/Edit — form renders, page search works, save succeeds
|
|
- Search with pagination (infinite scroll) works
|
|
|
|
---
|
|
|
|
## Phase 4: Blog Post Admin Panels (5 calls → 0)
|
|
|
|
These currently use a hybrid pattern: Jinja renders HTML, route passes it as `data_html`/`entries_html`/etc., and `_raw_html_sx()` wraps it in `(raw! "...")`. Converting means building content natively in sx instead.
|
|
|
|
### 4a. Post Data panel — `_types/post_data/_main_panel.html`
|
|
|
|
**Approach:** Build `_post_data_content_sx(ctx)` in `sx_components.py` using Python code to walk ORM model columns/relationships (cleaner than Jinja for metadata introspection). Add data inspector components to `blog/sx/admin.sx`.
|
|
|
|
**Modify:** `blog/bp/post/admin/routes.py` `data()` — remove `render_template()`, pass context directly. `blog/sx/sx_components.py` — replace `_raw_html_sx(ctx.get("data_html"))` with native `_post_data_content_sx(ctx)`.
|
|
|
|
### 4b. Post Entries panel — `_types/post_entries/_main_panel.html`
|
|
|
|
**Approach:** Build `_post_entries_content_sx(ctx)` showing associated entries + calendar browsers with lazy-loaded calendar views.
|
|
|
|
**Modify:** Same pattern — remove `render_template()` from `entries()` route, build content in sx.
|
|
|
|
### 4c. Calendar View — `_types/post/admin/_calendar_view.html`
|
|
|
|
**Approach:** Build `render_calendar_view_sx(calendar, year, month, ...)` producing month grid with entry toggle buttons. The HTMX attributes (`sx-post`, `sx-trigger`) translate directly to sx keyword args.
|
|
|
|
**Modify:** `blog/bp/post/admin/routes.py` `calendar_view()` — return `sx_response()` instead of `render_template()`.
|
|
|
|
### 4d. Post Settings panel — `_types/post_settings/_main_panel.html`
|
|
|
|
**Approach:** Build settings form with collapsible `<details>` sections (General, Tags, Feature Image, SEO, Social, Advanced). Jinja macros (`field_label`, `text_input`, etc.) become reusable `(defcomp ...)` components. Add to `blog/sx/settings.sx`.
|
|
|
|
**Modify:** Same pattern as 4a — replace `_raw_html_sx(ctx.get("settings_html"))`.
|
|
|
|
### 4e. Post Edit panel — `_types/post_edit/_main_panel.html` (352 lines, WYSIWYG)
|
|
|
|
**Approach:** Feature image upload/preview, title/slug editing, Lexical editor container, newsletter checkboxes, save footer. The tightly-coupled JavaScript (Lexical init, image upload, slug generation) stays as `(script ...)` or `(raw! "<script>...")` blocks. Structural HTML converts to sx components in `blog/sx/editor.sx`.
|
|
|
|
**Modify:** Same pattern — replace `_raw_html_sx(ctx.get("edit_html"))`.
|
|
|
|
### Verification
|
|
|
|
- Post admin tabs: data, entries, settings, edit — all render correctly
|
|
- Calendar view lazy-loads inside entries panel
|
|
- Settings form saves and preserves values
|
|
- WYSIWYG editor: feature image add/remove, Lexical loads, newsletters, save/publish
|
|
|
|
---
|
|
|
|
## Implementation Order
|
|
|
|
| # | Scope | Calls | Effort | New files |
|
|
|---|-------|-------|--------|-----------|
|
|
| 1 | Events route wiring | 16 | Low — render functions exist | None |
|
|
| 2 | Orders checkout | 2 | Medium — new components | `orders/sx/checkout.sx` |
|
|
| 3 | Blog menu items | 3 | Medium — new components + JS | `blog/sx/menu_items.sx` |
|
|
| 4a | Blog post data | 1 | Medium — ORM introspection | — |
|
|
| 4b | Blog post entries | 1 | Medium | — |
|
|
| 4c | Blog calendar view | 1 | Medium | — |
|
|
| 4d | Blog post settings | 1 | Medium-high — large form | — |
|
|
| 4e | Blog post edit | 1 | High — WYSIWYG editor | — |
|
|
|
|
## Files Modified
|
|
|
|
| File | Change |
|
|
|------|--------|
|
|
| `events/bp/slot/routes.py` | Wire to existing sx render functions |
|
|
| `events/bp/slots/routes.py` | Wire to existing sx render functions |
|
|
| `events/bp/ticket_types/routes.py` | Wire to existing sx render functions |
|
|
| `events/bp/ticket_type/routes.py` | Wire to existing sx render functions |
|
|
| `events/bp/calendar_entries/routes.py` | Wire to existing sx render functions |
|
|
| `events/bp/calendar_entry/routes.py` | Wire to existing sx render functions |
|
|
| `events/bp/calendar_entry/admin/routes.py` | Wire to existing sx render functions |
|
|
| `orders/sx/checkout.sx` | **NEW** — checkout return components |
|
|
| `orders/sx/sx_components.py` | Add `render_checkout_return_page()` |
|
|
| `orders/bp/checkout/routes.py` | Use sx render function |
|
|
| `blog/sx/menu_items.sx` | **NEW** — menu item form components |
|
|
| `blog/sx/sx_components.py` | Add menu item + post admin render functions |
|
|
| `blog/sx/admin.sx` | Add data/entries/calendar components |
|
|
| `blog/sx/settings.sx` | Add settings form components |
|
|
| `blog/sx/editor.sx` | Add WYSIWYG editor components |
|
|
| `blog/bp/menu_items/routes.py` | Use sx render functions |
|
|
| `blog/bp/post/admin/routes.py` | Remove all 5 `render_template()` calls |
|
|
|
|
## Final Verification
|
|
|
|
1. `python3 -m pytest shared/sx/tests/ -v` — all sx tests pass
|
|
2. `./dev.sh events` — events starts, all admin pages work
|
|
3. `./dev.sh blog` — blog starts, all post admin tabs work
|
|
4. `./dev.sh orders` — orders starts, checkout return renders
|
|
5. Zero `render_template()` calls remaining (except email templates)
|