{"slug":"trade-portal","total":9,"limit":100,"offset":0,"since":null,"commits":[{"hash":"ed2c486","date":"2026-07-16 08:49:17 -0700","author":"steve@designerwallcoverings.com","subject":"Handle sample_only + buyable catalog fields; eliminate \\$null price rendering","body":"- loadCatalog() now maps buyable, sample_only, price (nullable), price_display,\n  product_url through from the refreshed shopify-catalog.json\n- computePricing() short-circuits on sample_only/null price → pricing_rule 'on-request'\n- /api/products passes buyable, sample_only, price_display, product_url to client;\n  cart_url is null for sample_only products\n- price sorts use Infinity sentinel so null-price products sort gracefully\n- cardHTML() uses price_display (pre-formatted string) everywhere — no \\$null/\\$NaN possible\n- sample_only cards: show \"Roll price on request\" + \"View on DW →\" link; NO buy button\n- buyable cards: unchanged — real price_display + qty + Buy on Shopify button\n- 250 products total: 167 buyable, 83 sample_only"},{"hash":"5f3208a","date":"2026-07-16 08:13:07 -0700","author":"Steve","subject":"shopify: refresh catalog — real roll prices, no $4.25 sample-only leak","body":""},{"hash":"a0835ac","date":"2026-07-16 08:12:05 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-16T08:12:03 (1 files) — data/shopify-catalog.json","body":""},{"hash":"e5b8824","date":"2026-07-16 07:51:19 -0700","author":"Steve","subject":"security: bind server to 127.0.0.1 so the tunnel/proxy basic-auth gate can't be bypassed on the LAN","body":""},{"hash":"7904fa5","date":"2026-07-16 07:37:14 -0700","author":"steve@designerwallcoverings.com","subject":"Add Buy on Shopify cart button, test-mode banner, trade pricing caveat","body":"- TEST-MODE banner: warns checkout is live Shopify; PayPal toggle note\n- Per-card: qty input + 'Buy on Shopify' link -> /cart/<variant_id>:<qty>\n  qty is adjustable; link updated via onclick before navigation\n- Trade users see Phase-2 checkout caveat: Shopify charges store price,\n  not on-site trade/net estimate; ?discount=CODE param placeholder noted\n- Non-Kravet trade price labeled 'est.' with inline caveat text\n- Public view: retail only, trade null, no cost exposed (verified)\n- Sort + density persist in localStorage; Color sort option added\n- Admin cards: created date+time chip unchanged (HARD RULE preserved)"},{"hash":"20df63d","date":"2026-07-16 07:37:04 -0700","author":"steve@designerwallcoverings.com","subject":"Switch product source to shopify-catalog.json; add cart permalink builder","body":"- loadCatalog() reads data/shopify-catalog.json (real DW Shopify store, read-only)\n- computePricing() driven by catalog price field (Shopify retail = MAP for Kravet)\n- buildCartUrl(): https://checkout_domain/cart/<variant_id>:<qty>[?discount=CODE]\n- /api/products returns variant_id + cart_url per product; never exposes cost\n- Non-Kravet trade = est. retail x0.75; labeled 'standard-est-25pct' (clearly est.)\n- Catalog cached in-memory after first load; no Shopify Admin token in app"},{"hash":"2eb6579","date":"2026-07-16 07:36:55 -0700","author":"steve@designerwallcoverings.com","subject":"Add real Shopify catalog data source (shopify-catalog.json, 200 DW products)","body":"Copies real DW Shopify catalog into data/shopify-catalog.json for use\nby the trade portal product grid."},{"hash":"ebc041b","date":"2026-07-15 16:59:31 -0700","author":"Steve","subject":"Phase-1 trade-portal: trade application form, login stub, MAP/net pricing, admin card grid","body":""},{"hash":"ee4bba5","date":"2026-07-15 16:52:44 -0700","author":"Steve","subject":"initial scaffold (trade-portal) via web-dev accelerator","body":""}]}