PolyWood Price Builder

Rules Builder UI

Scope

src/app/ruleset-builder/page.tsx contains the active rules authoring workflow:

  • ruleset ID editing and validation
  • SKU search/select for preview context
  • widget-based pipeline editing (ordered step list)
  • tier pricing rule editing (T2-T5)
  • save/publish through POST /api/rulesets

src/app/rules/page.tsx is now a ruleset list page (create/edit/delete entrypoint), not the builder.

Data Loaded on Init

  • useProducts() loads /api/products for SKU and cost context.
  • Builder loads /api/widgets on init.
    • If this request fails, it falls back to DEFAULT_WIDGETS and surfaces a warning.
  • Builder also loads /api/rule-assignments on init for assignment lookup.
  • If URL has ?id=<rulesetId>, builder loads:
    • /api/rulesets?id=<rulesetId>&version=latest
    • /api/rule-assignments (parallel read for finding assigned SKUs)

Pipeline Model

  • Steps are an ordered array of RuleStep records ({ id, widgetId, config }).
  • Adding from the widget palette appends to the pipeline tail.
  • Steps are reordered with drag-and-drop in a linear list.
  • Step field controls are generated from each widget manifest.
  • Step-level output values are shown from applyPipeline preview.
  • Loaded steps that reference deprecated widget IDs (price.roundUpMultiple, price.roundDownMultiple) are filtered out in UI load path.

Tier Pricing Editor

  • T1 comes from pipeline final output for selected SKU.
  • T2-T5 each have an editable formula:
    • dependsOn tier
    • operator (addPercent or percentOf)
    • numeric value
  • Preview values and policy errors are shown inline using computeTierPrices.

Persistence Actions

  • Save ruleset: POST /api/rulesets
    • UI sends { id, steps, tierPolicy }.
    • Backend auto-creates v0001 for new IDs or publishes next version for existing IDs.
    • UI displays returned version label after save.
  • Assignment writes are not performed in builder.
    • Product assignment edits happen on src/app/page.tsx via PUT /api/rule-assignments.