feat: landing pages, storefront UX, and Yalidine integration hardening - #102
Merged
Conversation
…tribution, MCP tools - D1 migrations 0019 (landing_pages + images + orders.landing_page_id) and 0020 (image intrinsic dimensions for CLS-free rendering); Drizzle tables + mock-db fixtures - Management API /api/landing-pages (defineRoute, LANDING_PAGES_READ/MANAGE scopes, slug rules, delete-with-orders guard, image stack endpoints with R2-first delete + reference counting, duplicate shares R2 objects) - Public store endpoint GET /store/landing-pages/:slug (published-only, atomic deferred view counter) + order attribution via landingPageSlug (best-effort, never blocks an order) - presign folder param (products | landing) with route+handler allowlists - Dashboard: feature module (13 API fns), nav leaf, /landing-pages list + create flow + Studio (uploads, reorder, live preview, spacing sliders, publish) + A/B compare screen; i18n ar/en/fr (+ guard) - Store Settings: custom domain field (landing page share URLs prefer it) - 7 MCP tools wired into the registry with scope gating - theme core: fetchLandingPageBySlug client + placeOrder schema field + OrderForm landingPageSlug hidden input (the /lp/[slug] page itself lands with the theme UX commit)
bighadj22
force-pushed
the
feat/landing-pages-yalidine-hardening
branch
from
September 8, 2026 09:42
738514d to
6792bb8
Compare
…A, CLS-free landing pages - favicon: serve the real /favicon.ico (the old link pointed at a nonexistent favicon.svg — every page 404'd its icon) - StoreLayout marketing mode: no header/nav AND no footer on landing pages - /lp/[slug]: parallel SSR fetches (halved TTFB), multi-image preload, intrinsic width/height from the 0020 columns reserve every image's slot (zero form-jumping layout shift), soft placeholder background - sticky Order Now CTA fixed to the bottom while the form is out of view, hidden via IntersectionObserver the moment it enters the viewport; localized (orderCta), RTL-safe, anchor-based (works without JS), hidden when out of stock - wrangler deploy script passes the production COD_SERVER_URL override
… dispatch UX, webhook tooling Fixes the three production breakages found by auditing our adapter against the official docs AND the live API (272 real history events across 20 real parcels; every decision below is backed by tests, not assumptions): Carrier geo-name resolution (~25% of dispatches were rejected): - migration 0021: carrier_wilayas + carrier_communes per-carrier exact-name maps; POST /api/delivery-companies/:id/sync-geo builds them by matching our reference rows to the carrier's own lists (exact → normalized → distance-1 variant scoped to the same wilaya), reporting unmapped rows - Yalidine dispatches resolve wilaya/commune through the map (our seed spellings differ for 389/1551 communes — 'Ain Arnat' vs 'Aïn Arnat'); NOEST/ZR/EcoTrack untouched; dashboard 'Sync Delivery Zones' button Status mapper rewritten against the documented 36-status enum: - real return statuses (Retourné au vendeur, Echèc livraison, Colis abandonné, ...) now map to 'returned' — orders no longer stick at out_for_delivery forever; phantom statuses removed; transit statuses are deliberate no-ops (the old map could REGRESS a dispatched order via Ramassé/En préparation); late Tentative échouée events can't touch terminal orders; exhaustive drift-guard test fails CI on enum changes Webhook HMAC-SHA256 verification (was a TODO accepting unsigned events): - X-Yalidine-Signature over raw body, hex digest, constant-time compare, proven byte-identical to PHP hash_hmac via a Node reference-implementation test; 400 on tampered/missing signatures; fail-open + warning when no secret stored (dashboard shows the unverified state) - fixes replayed-delivery 500s (Drizzle wraps D1 errors — duplicate detection now walks the cause chain; retries previously risked the carrier auto-disabling the webhook) Dispatch UX (merchant-side stop desk, carrier-agnostic): - deliveryType override on dispatch (home ⇄ stop desk) persisted with the tracking number; wilaya-scoped desk picking (cross-wilaya fallback removed); commune desk pre-selection; explicit no-desk-in-wilaya state with switch hint; Yalidine weight field enabled per the official contract Webhook tooling: - GET /api/delivery-companies/:id/webhook/events — outcome-filtered, paginated event log (ok/ignored/unmapped/error) with joined order links - dashboard: Yalidine setup card (URL + copy, secret save, guided steps), ZR Express register/unregister + custom status-mapping editor (both were dead API code), events panel for every webhook-capable carrier - adapter: unified request() extracts the carrier's real error bodies (nested error.message + flat message + raw-body snippet) and getStopDesks no longer swallows failures (a dead credential used to report 'synced 0 desks, success') Egress workaround (see KNOWN LIMITATION in CHANGELOG): - Yalidine's Cloudflare zone blocks ALL Cloudflare-Worker-originated traffic (403 error 1106 — proven pre-auth, whole zone, fetch AND raw sockets); scripts/yalidine-egress-proxy.ts is a ready Deno Deploy relay (secret-authenticated, allowlisted to api.yalidine.app) and the adapter routes through it when proxy_base_url/proxy_secret are set in company notes — inert otherwise; .agents/skills/yalidine-integration-skill-main/ vendors the official reference docs we validated against
…n limitation, brand logo - README: +3 code-verified Delivery Engine claims (carrier zone-name sync, dispatch-time delivery-type switching, webhook event log); Yalidine Hardening + Landing Pages entries in Recently Shipped - CHANGELOG [Unreleased]: Added/Fixed for landing pages, theme UX, and the Yalidine hardening; Known Limitations section documents the Yalidine Cloudflare-Worker egress block (403 error 1106) with impact and both workarounds - replaces the banner with the CodFlow logo asset
bighadj22
force-pushed
the
feat/landing-pages-yalidine-hardening
branch
from
September 8, 2026 09:44
6792bb8 to
cbb83f2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Four clean commits, each self-consistent:
feat(landing-pages)(75 files)feat(theme01)feat(yalidine)(55 files)docsYalidine highlights
sync-geoendpoint + 3-phase matcher (exact → normalized → distance-1, wilaya-scoped). Our seed spellings differ from Yalidine's for 389/1551 communes (Ain ArnatvsAïn Arnat) — ~25% of dispatches were being rejected at the carrier. Dashboard button included; other carriers untouched.Retourné au vendeur,Echèc livraison, …) now map toreturnedinstead of leaving orders stuck atout_for_delivery; phantom statuses deleted; transit statuses are deliberate no-ops (the old map could regress dispatched orders); drift-guard test fails CI if the enum changes.hash_hmacvia a Node reference-implementation suite; 400 on tampered/missing signatures; fail-open + dashboard warning when no secret. Also fixes replayed-delivery 500s (Drizzle cause-chain duplicate detection — retries previously risked carrier-side webhook auto-disable).Yalidine's Cloudflare zone blocks all Cloudflare-Worker-originated traffic (403
error code: 1106) — proven pre-auth, whole-zone, fetch AND raw sockets, token-independent. Outbound calls (dispatch, syncs, tracking pulls) fail from the production Worker until either (a) Yalidine allowlists Workers traffic (evidence email drafted), or (b) the ready-made egress relay (cod-server/scripts/yalidine-egress-proxy.ts, Deno Deploy) is deployed and wired via company-notes keys. Inbound webhooks are unaffected — live-proven with real events (HMAC-verified, deduplicated, correctly mapped).Verification
Deployed versions already running: server
6c6fcca9, dashboard6043f414, storefrontead35ad9.