Files
WooNooW/project_info__1.md
Dwindi Ramadhana 396ca25be4 feat: Page Editor v1.0 - canonical schema, SSR parity, and migration
Major improvements to WooNooW Page Editor system:

Schema & Architecture:
- Canonical section schema with unified sectionSchema.ts
- Normalized feature-grid to use items (not features)
- Standardized default values across all section types
- Schema versioning with automatic migration on read

Backend (PHP):
- Enhanced PlaceholderRenderer with typed output contracts
- Added fallback behavior for empty/invalid dynamic sources
- Added caching support for post data resolution
- New SchemaMigration class for backward compatibility
- New Features class for feature flags
- Enhanced PageSSR with full style support
- Removed controller-level special-casing for related_posts

Frontend (Admin SPA):
- Updated CanvasRenderer with schema-aware transformation
- Enhanced InspectorPanel with canonical schema metadata
- Added new section renderers

Frontend (Customer SPA):
- New section components: BentoCategoryGrid, MarqueeBanner, ProductCarousel, ShoppableImage
- Updated FeatureGridSection for items prop contract

Testing:
- Add PHP tests: SchemaMigrationTest, PlaceholderRendererTest, PageSSRTest
- Add TypeScript tests: schema-integration, feature-grid-regression
- Add parity tests for React vs SSR content matching
- Add CI script: check-schema-drift.mjs
- Add VERIFICATION_CHECKLIST.md

Documentation:
- RELEASE_NOTES-v1.0.md with full release notes
- docs/PAGE_EDITOR_SECTION_SCHEMA_V1.md
- docs/PAGE_EDITOR_SSR_COVERAGE_AUDIT.md
2026-05-30 13:02:08 +07:00

32 KiB
Raw Permalink Blame History

WooNooW Page Editor (Page Editor feature) — Comprehensive Trace & Consistency Audit

Summary

WooNooW implements “Page Editor” as a section-based layout system with two render paths: (1) an interactive React canvas in admin-spa and (2) a front-end storefront render via customer-spa DynamicPage section components (and a bot-safe SSR fallback for SEO). The admin editor stores page/template section JSON in WordPress (page: post meta _wn_page_structure; CPT templates: wn_template_{cpt} option). The key engineering goal—WYSIWYG consistency between what the editor shows and what the frontend renders—is currently only partially achieved due to mismatched prop flattening/dynamic placeholder handling and incomplete SSR coverage.


Architecture

Primary pattern

JSON schema + adapter rendering:

  • Admin editor maintains a sections[] JSON tree (Zustand store)
  • The canvas maps section.type → customer-spa React section components
  • Frontend render uses those same React components at runtime
  • Bot render uses PHP PageSSR + PlaceholderRenderer to turn the same JSON into HTML

Major subsystems

  1. Admin editor UI (admin-spa)
    • Appearance > Pages editor (sidebar + canvas + inspector)
    • CanvasRenderer adapts section JSON into props for React section components
  2. WordPress REST API (includes/Api/PagesController.php)
    • Fetch/save page structures + CPT templates
    • Preview endpoints for editor iframe-like previews (even though the v4 brief is canvas-only)
  3. Front-end storefront renderer (customer-spa)
    • DynamicPage sections like HeroSection, FeatureGridSection, etc.
  4. Bot/server rendering (includes/Frontend/PageSSR.php + PlaceholderRenderer.php + TemplateOverride.php)
    • Detect bots → serve minimal SSR HTML
    • Resolve dynamic placeholders to post data

Technology stack

  • Backend: PHP 8.2+, WordPress REST API, WooCommerce hooks, Yoast/RankMath compatibility, transient caching
  • Frontend: React 18 + TypeScript, Zustand, React Query, dnd-kit, UI primitives
  • Data: JSON structures persisted into WordPress post meta / options

Execution start (runtime entry points)

  • Admin editor: admin-spa/src/routes/Appearance/Pages/index.tsx
  • Front-end page display: mediated by TemplateOverride.php (SPA wrapper + redirect + bot SSR)
  • Bot SSR path:
    • TemplateOverride::maybe_serve_ssr_for_bots()TemplateOverride::serve_ssr_content()PageSSR::render()PageSSR::render_section() → section-specific render methods

Directory Structure (meaningful subset)

project-root/
├── admin-spa/
│   └── src/routes/Appearance/Pages/
│       ├── index.tsx                          — page editor orchestrator (data fetching + save)
│       ├── components/
│       │   ├── CanvasRenderer.tsx           — canvas composition + dnd + mapping to section renderers
│       │   ├── CanvasSection.tsx            — selection/hover wrapper (partially inspected)
│       │   ├── InspectorPanel.tsx           — inspector UI (fields/styles/background)
│       │   ├── InspectorField.tsx / InspectorRepeater.tsx — field editors + repeaters
│       │   └── section-renderers/            — older editor-specific renderers (hero etc.)
│       └── store/usePageEditorStore.ts      — Zustand store + persistence payload
├── customer-spa/
│   └── src/pages/DynamicPage/sections/
│       ├── HeroSection.tsx
│       ├── FeatureGridSection.tsx
│       └── (other section components)
├── includes/
│   ├── Api/PagesController.php              — REST API for pages/templates/preview
│   ├── Frontend/PageSSR.php                — bot SSR HTML generator from section JSON
│   ├── Frontend/PlaceholderRenderer.php  — dynamic source resolution from post data
│   └── Frontend/TemplateOverride.php      — SPA routing + bot SSR integration
└── templates/
    └── spa-full-page.php / spa-wrapper.php — SPA mount templates

Key Abstractions

1) Page Editor JSON model (Section + SectionProp)

  • Where:
    • admin-spa/src/routes/Appearance/Pages/store/usePageEditorStore.ts
  • Responsibility: Defines the shape of editable layout:
    • Section includes type, layoutVariant, colorScheme, styles, elementStyles, and props
    • props is a map of { [fieldName]: { type: 'static'|'dynamic', value?, source? } }
  • Lifecycle:
    • Created client-side when user adds a section
    • Updated live as inspector changes
    • Persisted on save via REST
  • Used by:
    • CanvasRenderer adapter to customer-spa components
    • PHP SSR and placeholder renderer

2) Zustand store: usePageEditorStore

  • File: admin-spa/src/routes/Appearance/Pages/store/usePageEditorStore.ts
  • Responsibility:
    • Holds currentPage, sections, selection/hover/device mode, unsaved-change flag
    • Implements add/delete/duplicate/reorder/update actions
    • Implements SPA landing page API calls
    • Implements savePage() that POSTs sections to REST endpoints
  • Interface (selected):
    • addSection(type, index?)
    • updateSectionProp(sectionId, propName, value: SectionProp)
    • updateSectionStyles(sectionId, styles)
    • updateElementStyles(sectionId, fieldName, styles)
    • savePage()
  • Important behavior:
    • setSections() marks hasUnsavedChanges=true (and save clears it)
  • Used by:
    • admin-spa/src/routes/Appearance/Pages/index.tsx

3) Canvas renderer: CanvasRenderer

  • File: admin-spa/src/routes/Appearance/Pages/components/CanvasRenderer.tsx
  • Responsibility:
    • Drag-and-drop reorder of sections (dnd-kit)
    • Maps section.type → customer-spa section components
    • Wraps customer component with withSectionWrapper() to adapt JSON props
  • Key behavior:
    • flattenSectionProps() transforms:
      • static {type,value} → value
      • dynamic {type,source} → string marker like "[source]" (NOT actual placeholder resolution)
  • Used by:
    • admin-spa/src/routes/Appearance/Pages/index.tsx
  • Consistency risk:
    • Editor canvas does not resolve dynamic placeholders to actual post data. It passes markers, while frontend runtime likely resolves using fetched post data.
  • File: admin-spa/src/routes/Appearance/Pages/components/InspectorPanel.tsx
  • Responsibility:
    • When a section is selected:
      • shows “Content” tab (fields)
      • shows “Design” tab (section background + spacing + element-level style overrides)
      • special repeaters for feature-grid, bento-category-grid, shoppable-image
    • When no section selected:
      • page/template info, SPA landing toggle, container width radio, delete actions
  • Key behavior:
    • Uses SECTION_FIELDS definitions per section.type
    • For dynamic field support, InspectorPanel passes supportsDynamic={field.dynamic && isTemplate} to InspectorField
  • Consistency risk:
    • Styles and elementStyles keys must match what customer-spa section components read; mismatches cause divergence between editor and SSR/render output.

5) Frontend render components: customer-spa DynamicPage sections

  • Files:
    • customer-spa/src/pages/DynamicPage/sections/HeroSection.tsx
    • customer-spa/src/pages/DynamicPage/sections/FeatureGridSection.tsx
  • Responsibility:
    • Render real UI using props: layout, colorScheme, styles, elementStyles
    • Use helper getSectionBackground(styles) (in HeroSection)
  • Key behavior:
    • The components assume props are already in “resolved” form (e.g., title is plain string, image is a URL, items are concrete objects).
  • Consistency implication:
    • Admin editor passes flattenSectionProps() values; dynamic placeholders are not resolved → UI may show placeholder markers rather than real values.

6) REST API: includes/Api/PagesController.php

  • File: includes/Api/PagesController.php
  • Responsibility:
    • Provide structures to admin editor and accept saves
    • Store page structures in post meta (_wn_page_structure) and templates in wp_options (wn_template_{cpt})
  • Key endpoints:
    • GET /woonoow/v1/pages (list pages + CPT templates)
    • GET /woonoow/v1/pages/{slug} (page structure with SEO + container width)
    • POST /woonoow/v1/pages/{slug} (save page sections)
    • GET /woonoow/v1/templates/{cpt}
    • POST /woonoow/v1/templates/{cpt}
    • POST /preview/page/{slug} and /preview/template/{cpt}
    • GET /woonoow/v1/content/{type}/{slug} (return post data + template + rendered sections)
  • Important behavior:
    • get_content_with_template() calls PageSSR::resolve_props() to resolve section props for the frontend render payload.
    • Contains a special pre-resolution for related_posts dynamic arrays.

7) Bot SSR: PageSSR + PlaceholderRenderer

  • Files:
    • includes/Frontend/PageSSR.php
    • includes/Frontend/PlaceholderRenderer.php
    • wired by includes/Frontend/TemplateOverride.php
  • Responsibility:
    • Convert stored JSON + post data into static HTML for bots
    • Replace dynamic placeholders at SSR time
  • Key limitations (observed):
    • PageSSR includes explicit renderers for hero/content/image_text/feature_grid/cta/contact_form; other section types may fall back to render_generic() which only prints string props.
    • render_content() uses apply_filters('the_content'), meaning SSR content is likely different from Reacts rendering if React expects different sanitization or structured HTML.

8) Routing + SSR integration: TemplateOverride

  • File: includes/Frontend/TemplateOverride.php
  • Responsibility:
    • Redirect WooCommerce pages to SPA routes
    • Serve SPA template (spa-full-page.php) for full SPA mode
    • Detect bots and serve SSR via maybe_serve_ssr_for_bots()
  • Key invariant:
    • SSR runs only when is_bot() matches known patterns and the target has _wn_page_structure or wn_template_{post_type} sections.

Data Flow (concrete tracing)

A) Editor → persisted structure

  1. User opens editor: admin-spa/src/routes/Appearance/Pages/index.tsx
  2. Editor fetches list: api.get('/pages') (REST) → PagesController::get_pages()
  3. User selects a page/template:
    • api.get('/pages/{slug}') or api.get('/templates/{cpt}')
  4. Editor loads pageData.structure.sections and sets Zustand sections
  5. User edits:
    • inspector modifies sections[n].props[fieldName] and styles/elementStyles
  6. On Save:
    • saveMutation POSTs { sections } to /pages/{slug} or /templates/{cpt}
    • PagesController::save_page() saves into _wn_page_structure
    • PagesController::save_template() saves into wn_template_{cpt}

B) Editor canvas: JSON → React component props

  1. CanvasRenderer maps section.type to a customer-spa renderer
  2. flattenSectionProps() converts each prop:
    • static: passes value directly
    • dynamic: passes placeholder marker string like [source]
  3. The customer-spa component renders based on expected prop types (string URLs, arrays, etc.)

This is the main “WYSIWYG gap”: dynamic placeholders in the editor are not resolved with a selected post context.

C) Frontend runtime render (human + bot) — key split

  1. TemplateOverride decides whether to serve SPA or SSR.
  2. Humans (non-bots):
    • SPA template renders React app; dynamic page fetch uses PagesController::get_content_with_template() (not fully traced here, but confirmed by the endpoint implementation).
  3. Bots:
    • maybe_serve_ssr_for_bots() calls serve_ssr_content()
  4. PagesController::get_content_with_template():
    • fetches post data via PlaceholderRenderer::build_post_data()
    • resolves each sections props via PageSSR::resolve_props() (with special pre-resolution for related_posts)
    • returns rendered.sections payload

D) Bot SSR: JSON → HTML

  1. TemplateOverride::serve_ssr_content():
    • gets _wn_page_structure or wn_template_{cpt}
    • calls PageSSR::render($sections, $post_data)
  2. PageSSR::render_section():
    • resolves props again with resolve_props()
    • calls render_hero, render_feature_grid, etc.
  3. PlaceholderRenderer::get_value() converts sources to actual values.

Non-Obvious Behaviors & Design Decisions (and what that means)

1) Editor currently does not truly “resolve dynamic placeholders”

  • Admin canvas uses flattenSectionProps() and converts dynamic props to markers like "[source]".
  • Frontend runtime resolves placeholders using PHP PageSSR::resolve_props() inside PagesController::get_content_with_template().

Why it matters: a template section that uses dynamic sources (post title, featured image, related posts) will show “[post_title]”-style strings in the editor canvas, but will show real values on the storefront.

2) Two parallel “renderers” exist: customer-spa React vs PageSSR PHP

  • React sections are full fidelity for humans.
  • PHP PageSSR covers only some section types explicitly.

Why it matters:

  • Even for bots, different section types may fallback to generic HTML and lose styling/content structure.
  • That produces another “editor vs rendered” mismatch, because the editor is React-rendered (not SSR).

3) Section schema keys are implicitly coupled to both sides

  • Example: customer HeroSection expects styles.paddingTop/paddingBottom and styles.contentWidth, elementStyles per element name.
  • PHP PageSSR::render_hero() expects section_styles keys like backgroundType, paddingTop, etc.

Risk: any rename in editor/styling keys breaks one render path silently.

  • get_content_with_template() does a manual resolution when it detects dynamic prop related_posts.
  • This suggests the generic PageSSR::resolve_props() doesnt produce the required array shape without precomputation.

Implication: other complex dynamic sources might also need special handling but are not generalized.

5) Duplicate section deep clone via JSON stringify

  • duplicateSection uses JSON.parse(JSON.stringify(section)).

Implication: any non-serializable structures in the section (e.g. functions) would be lost; current SectionProp model is JSON-safe, but this is a design constraint.


Section-by-Section Trace (whats implemented + the gap)

Below are the sections we directly observed in code paths:

Hero section

  • Editor:
    • InspectorPanel defines hero fields: title, subtitle, image, cta_text, cta_url (title/subtitle/image can be dynamic: true).
    • DEFAULT_SECTION_PROPS.hero in Zustand initializes static values.
  • Canvas:
    • CanvasRenderer maps type='hero'customer-spa HeroSection
    • withSectionWrapper flattens section.props:
      • dynamic title becomes marker string
  • Frontend runtime:
    • customer-spa HeroSection expects resolved title/subtitle/image strings
  • Bot SSR:
    • PageSSR::render_hero() renders title/subtitle/image/cta with inline style support and background/overlay/spacing.

Gap: dynamic placeholders arent resolved in the editor canvas, so WYSIWYG breaks for Hero dynamic fields.

Feature Grid section

  • Editor:
    • InspectorPanel includes:
      • SECTION_FIELDS for feature-grid: only heading (static)
      • repeaters for features inside “Feature Grid Repeater”
    • Zustand defaults include features as {type:'static', value:''} (note: this is inconsistent with repeater expecting arrays—possible defect).
  • Canvas:
    • CanvasRenderer maps feature-gridcustomer-spa FeatureGridSection
    • flattenSectionProps() will transform:
      • static features'' or array depending on stored shape
      • dynamic features"[source]" marker (but the editor repeater may prevent dynamic)
  • Frontend runtime:
    • customer-spa FeatureGridSection uses items = items.length>0 ? items : features
    • It supports “post-card” mode by detecting item.url.
  • Bot SSR:
    • PageSSR::render_feature_grid() renders items = $props['items'] ?? []; it does not include a features fallback.
    • Also it uses get_icon_svg() only for known Lucide icon names; unknown icons degrade.

Gaps / defects:

  1. Schema inconsistency between editor and SSR:
    • React component uses items and features props
    • PHP SSR renders only items
  2. Zustand default for feature-grid.features is '' not [], which likely makes the repeater misbehave and/or yields empty rendering on save.
  3. Editor likely supports dynamic features only via special logic, but the inspector repeater has logic for featuresProp.type === 'dynamic'—we did not verify the dynamic editing path end-to-end.

Image + Text section

  • Editor:
    • InspectorPanel defines image-text fields: title, text (textarea), image, cta_text, cta_url
  • Canvas:
    • maps image-textcustomer-spa ImageTextSection
  • Bot SSR:
    • PageSSR has render_image_text() but it relies on helper render_universal_row() and expects specific prop shapes (string or props[...] objects).

Gap:

  • If editor stores nested {type,value} shapes, React HOC wrapper flattens them to plain strings; SSR expects resolved strings after resolve_props(). Thats fine for bots because SSR uses resolve_props. But editor WYSIWYG may still be placeholder-markers if any fields are dynamic.

CTA Banner section

  • Editor: cta-banner fields
  • Canvas: mapped to customer-spa CTABannerSection
  • Bot SSR: render_cta_banner() exists Gap:
  • Same dynamic placeholder mismatch in editor (markers vs resolved values).

Contact Form section

  • Editor: contact-form includes webhook_url and redirect_url
  • Canvas: mapped to customer-spa ContactFormSection
  • Bot SSR: renders a <form method="post"> with inputs, but does not implement functional submit behavior (explicit comment).

Gap:

  • For bots, SSR shows structure; for humans, React likely does AJAX submission and redirects.
  • This is an acceptable SEO compromise, but it contributes to “not exactly whats rendered” for bots.
  • We saw CanvasRenderer includes mapping for these section types to customer-spa components.
  • We did not inspect corresponding PHP PageSSR::render_* methods for all of them.

Gap (likely):

  • If PHP SSR lacks renderers, bots will get render_generic() and only string props will be output, losing complex nested UI.

Non-Obvious Risks / Defects / Opportunities

A) WYSIWYG inconsistency for dynamic templates

Defect class: “editor uses placeholder markers instead of resolved values”

  • Evidence:
    • CanvasRenderer.flattenSectionProps() renders dynamic as "[source]".
    • PageSSR::resolve_props() resolves dynamic values on the server for bots and on the API response for frontend runtime.

Opportunity:

  • Improve editor canvas by introducing preview context:
    • for templates: allow selecting a sample CPT item in the editor, then resolve placeholders client-side (or call a preview endpoint) so the canvas matches the actual frontend.

B) SSR coverage likely incomplete for newer section types

Defect class: fallback generic output

  • Evidence:
    • PageSSR has explicit renderers for hero/content/image_text/feature_grid/cta/contact_form.
    • Other types fall back to render_generic() which only prints string props.

Opportunity:

  • Add render_{type} implementations or a generic JSON-to-HTML renderer that can handle structured items/features/hotspots arrays.
  • Ensure SSR HTML structure and CSS classes match the React implementation classes (or at least maintain content parity).

C) Prop/schema mismatch between editor, React, and SSR

Examples:

  • Feature Grid: React uses items and features, SSR uses $props['items']
  • Editor defaults: feature-grid.features default is value: '' not []

Opportunity:

  • Centralize a single section schema definition (types + default values + prop names) used by:
    • Zustand defaults
    • Inspector UI
    • Canvas flattening
    • customer-spa props
    • PHP SSR expectations

D) Dynamic array sources have special handling

  • Evidence:
    • PagesController::get_content_with_template() pre-resolves dynamic related_posts producing arrays before PageSSR::resolve_props()

Opportunity:

  • Refactor dynamic sources resolution into PlaceholderRenderer::get_value() for arrays, not special-casing in controller.
  • Otherwise, other sources might require similar special-casing and accumulate technical debt.

E) Security/sanitization differences between React and SSR

  • SSR uses esc_html, esc_url, and inline styles.
  • Editor allows RTE in content section; SSR uses apply_filters('the_content') and outputs wrapped HTML.

Opportunity:

  • Audit that the React section uses the same sanitized HTML pipeline as SSR. Otherwise, “what you see” differs (especially for shortcodes, autop, and sanitized tags).

Competitive Assessment (Woocommerce builder vs Shopify / Fluent Cart)

Where WooNooW can be competitive

  • No data migration: keeps WooCommerce as the engine.
  • Section templates for CPT items: closer to “theme editor + content rendering” than traditional Woo page builders.
  • SPA + SSR hybrid: offers better UX than classic PHP templating alone, while preserving SEO via SSR for bots.

Where gaps likely limit competitiveness

  • True WYSIWYG for dynamic content:
    • Shopifys theme editor shows real content (or at least realistic previews)
    • WooNooW currently may show placeholders in the editor for dynamic templates.
  • Template rendering consistency:
    • If SSR fallback is generic for many section types, bot-indexed pages may differ in richness compared to React runtime.
  • Depth of section catalog:
    • Competitive parity likely requires many robust layout blocks and repeaters with tested SSR support.

Compared to Fluent Cart / plugin builders

  • Fluent Cart focuses on cart/checkout UX via Woo hooks.
  • WooNooWs editor aims at storefront page/landing UX across page and CPT templates.
  • The big differentiator is the unified section JSON model; but to beat SaaS (Shopify) the preview/resolution pipeline must be consistent.

Module Reference (one-liners)

File Purpose
admin-spa/src/routes/Appearance/Pages/index.tsx Orchestrates editor, fetch/save, dialogs, layout columns
admin-spa/src/routes/Appearance/Pages/components/CanvasRenderer.tsx Canvas rendering, dnd reorder, maps section types to customer-spa components
admin-spa/src/routes/Appearance/Pages/store/usePageEditorStore.ts Section JSON state + actions + persistence payload
admin-spa/src/routes/Appearance/Pages/components/InspectorPanel.tsx Inspector UI for fields and design/background styles
includes/Api/PagesController.php REST endpoints for structures, templates, content-with-template render payload
includes/Frontend/PlaceholderRenderer.php Resolves dynamic placeholders into post data values
includes/Frontend/PageSSR.php Bot SSR HTML renderer from section JSON
includes/Frontend/TemplateOverride.php SPA redirects + bot SSR routing

Suggested Reading Order (fast onboarding)

  1. admin-spa/src/routes/Appearance/Pages/index.tsx
    Start here to see how the editor loads/saves and how the page is assembled.
  2. admin-spa/src/routes/Appearance/Pages/components/CanvasRenderer.tsx
    Understand how JSON is adapted into customer-spa props (and where WYSIWYG breaks).
  3. admin-spa/src/routes/Appearance/Pages/components/InspectorPanel.tsx
    Identify exactly which props/styles the inspector can write.
  4. customer-spa/src/pages/DynamicPage/sections/HeroSection.tsx
    See what “resolved” props look like in React.
  5. includes/Api/PagesController.phpget_content_with_template()
    This shows the server-side resolution that React runtime likely relies on.
  6. includes/Frontend/PageSSR.php + includes/Frontend/PlaceholderRenderer.php
    Understand bot rendering limitations and placeholder resolution behavior.

TODO / Development Guideline Material (derived next steps)

Based on the trace, the “next development guideline material” should focus on enforcing one canonical section schema and a preview resolution contract across editor, React runtime, and SSR.

Concrete guideline items:

  1. Define a canonical section prop schema and prop names (including items vs features for feature-grid).
  2. Implement a template-preview context in the editor for dynamic sources.
  3. Ensure PageSSR has renderers (or at least structured HTML parity) for every editor-supported section type.
  4. Add automated tests that:
    • save a known section JSON in a fixture
    • render via React and via SSR
    • diff for content parity (especially for dynamic placeholders).

Execution Summary (English)

This document identifies one core issue: WYSIWYG inconsistency between the admin editor preview and frontend/SSR output.

Highest-impact implementation priorities:

  1. Align one section schema contract (props, field names, default values) across editor, React runtime, and PHP SSR.
  2. Add template preview context so dynamic fields render real sample data instead of [source] markers in the editor.
  3. Complete SSR support for all section types currently available in the editor canvas.
  4. Add output parity tests (React vs SSR) to prevent regressions.

Expected outcome after these 4 priorities:

  • a more trustworthy WYSIWYG editing experience,
  • better SEO consistency for bot-rendered pages,
  • safer cross-module maintenance (admin-spa, customer-spa, PHP backend).

Implementation Checklist / Tasklist

Workstream A — Canonical Section Schema

  • Create a single schema source for section types, prop names, and default values.
  • Fix feature-grid default shape (features should be array-based where required, not '').
  • Normalize feature-grid naming (items vs features) and document final contract.
  • Refactor editor defaults (usePageEditorStore) to use canonical schema.
  • Refactor inspector field definitions (InspectorPanel) to use canonical schema metadata.
  • Refactor canvas prop flattening to rely on schema-aware transformation rules.
  • Update customer section component prop types to the same contract.
  • Update PHP SSR renderers to read the same prop names and shapes.
  • Add migration handling for legacy saved JSON structures.

Workstream B — Dynamic Preview Consistency in Editor

  • Add a template preview context selector (pick sample post/product/CPT item).
  • Add API support (or reuse existing preview endpoint) to resolve dynamic props for selected context.
  • Render resolved dynamic values in admin canvas preview while preserving source metadata in saved JSON.
  • Handle unresolved/missing dynamic source states with explicit placeholder UI (not raw [source] string).
  • Add loading/error states for dynamic resolution in editor preview.
  • Ensure repeaters with dynamic arrays (related_posts, etc.) resolve to proper object arrays.

Workstream C — SSR Coverage & Parity

  • Audit all editor-supported section types against PageSSR renderer coverage.
  • Implement missing SSR renderers for unsupported types (bento-category-grid, product-carousel, shoppable-image, marquee-banner, etc.).
  • Ensure each SSR renderer supports section styles/background/spacing parity where applicable.
  • Ensure SSR supports nested/repeater data structures beyond plain strings.
  • Reduce dependence on render_generic() for production-facing section types.
  • Validate sanitizer/escaping parity between SSR and React rendering expectations.

Workstream D — Dynamic Source Resolution Architecture

  • Remove controller-level special-casing for related_posts where possible.
  • Move complex dynamic resolution rules into a shared resolver path (PlaceholderRenderer + resolver contract).
  • Define typed output contracts for scalar dynamic values vs array/object dynamic values.
  • Add explicit fallback behavior when a dynamic source returns empty/invalid data.
  • Cache dynamic resolution carefully to avoid stale or mismatched preview data.

Workstream E — Testing & Quality Gates

  • Add fixture-based tests for section JSON save/load integrity.
  • Add frontend tests for section render correctness with static and dynamic props.
  • Add PHP tests for PageSSR::resolve_props() and section HTML render output.
  • Add parity tests comparing React-rendered content snapshots vs SSR content snapshots.
  • Add regression tests for feature-grid (items/features), dynamic repeater arrays, and style keys.
  • Add CI checks that fail on schema drift between TS schema and PHP renderer expectations.

Workstream F — Rollout & Backward Compatibility

  • Add versioning for section schema payloads (e.g., schemaVersion).
  • Provide migration script/runtime migration for old saved structures.
  • Roll out behind feature flag for dynamic preview if needed.
  • Verify no breaking behavior on existing live pages/templates before full rollout.
  • Prepare release notes and internal support notes for content/admin teams.

Suggested Delivery Sequence

  1. Workstream A (schema contract)
  2. Workstream B (editor dynamic preview)
  3. Workstream C + D (SSR and resolver architecture)
  4. Workstream E (test parity hardening)
  5. Workstream F (safe rollout)

Sprint Board (Now / Next / Later)

Legend:

  • Owner: FE-Admin (admin-spa), FE-Storefront (customer-spa), BE-WP (PHP/WordPress), QA (test/validation), Tech Lead (cross-module coordination)
  • Effort: S (0.5-1 day), M (2-3 days), L (4-6 days)

Now (Current Sprint)

  • Define canonical section schema contract and publish v1 spec doc.
    • Owner: Tech Lead
    • Effort: M
  • Fix feature-grid contract (items vs features) and align defaults ([] instead of empty string where needed).
    • Owner: FE-Admin + FE-Storefront + BE-WP
    • Effort: M
  • Refactor usePageEditorStore and InspectorPanel to consume canonical schema metadata.
    • Owner: FE-Admin
    • Effort: L
  • Update CanvasRenderer transformation to be schema-aware (remove implicit brittle mapping).
    • Owner: FE-Admin
    • Effort: M
  • Audit PageSSR coverage against all section types currently available in editor canvas.
    • Owner: BE-WP
    • Effort: S
  • Add test fixtures for section JSON save/load integrity and baseline regression fixtures.
    • Owner: QA + BE-WP
    • Effort: M

Next (Following Sprint)

  • Implement template preview context selector (sample post/product/CPT picker).
    • Owner: FE-Admin
    • Effort: M
  • Add/extend API endpoint to resolve dynamic props for selected preview context.
    • Owner: BE-WP
    • Effort: M
  • Render resolved dynamic values in canvas preview while preserving saved source metadata.
    • Owner: FE-Admin
    • Effort: M
  • Implement missing SSR renderers for unsupported sections (bento-category-grid, product-carousel, shoppable-image, marquee-banner, etc.).
    • Owner: BE-WP
    • Effort: L
  • Normalize dynamic array resolution architecture (reduce/remove controller-level related_posts special casing).
    • Owner: BE-WP
    • Effort: M
  • Add parity tests: React snapshots vs SSR snapshots for key templates.
    • Owner: QA + FE-Storefront + BE-WP
    • Effort: L

Later (Hardening / Release Readiness)

  • Add schema versioning (schemaVersion) and backward-compat migration strategy.
    • Owner: BE-WP + Tech Lead
    • Effort: M
  • Implement runtime migration path for legacy saved page/template structures.
    • Owner: BE-WP
    • Effort: M
  • Add CI guardrails for schema drift between TS and PHP contracts.
    • Owner: QA + Tech Lead
    • Effort: M
  • Validate sanitization parity and security handling between React and SSR outputs.
    • Owner: BE-WP + FE-Storefront
    • Effort: M
  • Run staged rollout with feature flag for dynamic preview and monitor real-store templates.
    • Owner: Tech Lead + QA
    • Effort: M
  • Publish release notes + internal support playbook for content/admin teams.
    • Owner: Tech Lead
    • Effort: S

Suggested Team Capacity Split

  • FE-Admin: 40%
  • BE-WP: 35%
  • FE-Storefront: 15%
  • QA: 10%

Definition of Done (Per Work Item)

  • Contract/spec updated (if schema-affecting change).
  • Implementation completed with tests.
  • Backward compatibility validated with legacy fixture coverage.
  • Manual verification done on at least: hero, feature-grid, one dynamic template, one repeater-heavy section.