diff --git a/.env.example b/.env.example index 1af219d..e7b917c 100644 --- a/.env.example +++ b/.env.example @@ -8,7 +8,9 @@ AMAZON_CREATORS_CREDENTIAL_SECRET=your-credential-secret AMAZON_CREATORS_CREDENTIAL_VERSION=3.1 AMAZON_CREATORS_PARTNER_TAG=iu0e3-20 AMAZON_CREATORS_MARKETPLACE=www.amazon.com -VITE_FLASH_CATALOG_URL= +# Assortment source of truth (amazon-flash-catalog Worker base URL). +# Defaults to https://amazon-flash-catalog.tech-bf6.workers.dev when unset. +VITE_FLASH_CATALOG_URL=https://amazon-flash-catalog.tech-bf6.workers.dev # Conbal content balloons. The site key is public by design and may be # overridden for preview environments. diff --git a/package.json b/package.json index 4ec5700..ab02be7 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "import:bsr": "node scripts/bsr/import-bsr.mjs && node scripts/bsr/fill-quota.mjs", "fill:quota": "node scripts/bsr/fill-quota.mjs", "refresh:weekly": "npm run import:bsr && npm run build && wrangler deploy", - "deploy": "npm run build && wrangler deploy", - "deploy:flash": "VITE_FLASH_CATALOG_URL=${VITE_FLASH_CATALOG_URL:-https://amazon-flash-catalog.tech-bf6.workers.dev} npm run build && wrangler deploy", + "deploy": "VITE_FLASH_CATALOG_URL=${VITE_FLASH_CATALOG_URL:-https://amazon-flash-catalog.tech-bf6.workers.dev} npm run build && wrangler deploy", + "deploy:flash": "npm run deploy", "library:sync": "node scripts/sync-from-library.mjs", "library:sync:dry": "node scripts/sync-from-library.mjs --dry-run" }, diff --git a/public/sitemap.xml b/public/sitemap.xml index d6f11d9..ddd34b9 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -2,1291 +2,1291 @@ https://ibamboo.com/ - 2026-08-01 + 2026-08-02 daily 1.0 https://ibamboo.com/shop - 2026-08-01 + 2026-08-02 daily 0.95 https://ibamboo.com/shop?limited=1 - 2026-08-01 + 2026-08-02 daily 0.9 https://ibamboo.com/quiz - 2026-08-01 + 2026-08-02 weekly 0.85 https://ibamboo.com/why - 2026-08-01 + 2026-08-02 monthly 0.7 https://ibamboo.com/shop?cat=kitchen - 2026-08-01 + 2026-08-02 daily 0.85 https://ibamboo.com/shop?cat=cutting-boards - 2026-08-01 + 2026-08-02 daily 0.85 https://ibamboo.com/shop?cat=dining - 2026-08-01 + 2026-08-02 daily 0.85 https://ibamboo.com/shop?cat=bath - 2026-08-01 + 2026-08-02 daily 0.85 https://ibamboo.com/shop?cat=organization - 2026-08-01 + 2026-08-02 daily 0.85 https://ibamboo.com/shop?cat=desk - 2026-08-01 + 2026-08-02 daily 0.85 https://ibamboo.com/shop?cat=outdoor - 2026-08-01 + 2026-08-02 daily 0.85 https://ibamboo.com/shop?cat=baby - 2026-08-01 + 2026-08-02 daily 0.85 https://ibamboo.com/vibe/craft - 2026-08-01 + 2026-08-02 weekly 0.8 https://ibamboo.com/vibe/ritual - 2026-08-01 + 2026-08-02 weekly 0.8 https://ibamboo.com/vibe/focus - 2026-08-01 + 2026-08-02 weekly 0.8 https://ibamboo.com/vibe/host - 2026-08-01 + 2026-08-02 weekly 0.8 https://ibamboo.com/vibe/nest - 2026-08-01 + 2026-08-02 weekly 0.8 https://ibamboo.com/vibe/patio - 2026-08-01 + 2026-08-02 weekly 0.8 https://ibamboo.com/product/astercook-deep-carbonized-bamboo-cutting-boards-for-kitchen-with-wood-storage-st - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/royal-craft-wood-wooden-cutting-boards-for-kitchen-meal-prep-serving-non-toxic-b - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/niagara-sleep-solution-ultra-soft-queen-size-mattress-topper-rayon-derived-from- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/keechee-bamboo-cutting-board-durable-wood-cutting-boards-for-kitchen-with-deep-j - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bedlore-queen-mattress-topper-viscose-derived-from-bamboo-thick-pillow-top-mattr - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/zeawec-3-piece-set-deep-carbonized-bamboo-cutting-board-for-kitchen-crack-resist - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/riveira-dark-bamboo-wooden-spoons-for-cooking-6-piece-apartment-essentials-wood- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/greener-chef-luxury-xl-bamboo-cutting-board-set-of-3-with-vertical-grain-strengt - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/greener-chef-luxury-bamboo-cutting-board-set-of-3-with-glue-free-surface-technol - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/3-packs-bamboo-cutting-board-set-with-non-slip-feet-and-over-sink-nice-solid-thi - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/freshware-bamboo-cutting-board-set-of-3-wooden-cutting-boards-for-kitchen-knife- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/kitsure-bamboo-cutting-board-for-chopping-serving-large-natural-multi-sizes-wood - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/fibogollo-bamboo-cutting-boards-for-kitchen-set-of-3-chopping-boards-with-juice- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/chef-studio-bamboo-salad-hands-wooden-salad-serving-utensils-natural-wood-salad- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/kikcoin-17-11-bamboo-cutting-boards-for-kitchen-knife-friendly-food-grade-wooden - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/edelhaus-carbonized-bamboo-cutting-boards-cutting-board-set-of-3-heat-treated-fo - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-cutting-board-set-of-3-with-stand-carbonized-bamboo-juice-groove-carboniz - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/greener-chef-luxury-wood-cutting-board-3-piece-set-with-vertical-grain-strength- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/zarler-bamboo-cutting-boards-for-kitchen-wood-cutting-board-with-juice-grooves-a - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/farberware-bamboo-cutting-board-with-clear-locking-lid-11x14-inch-charcuterie-bo - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-cutting-board-for-kitchen-3-piece-set-with-juice-grooves-side-handles-non - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/hiware-extra-large-18-x-12-bamboo-cutting-board-for-kitchen-heavy-duty-wood-cutt - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-cutting-board-set-with-stand-3pcs-double-sided-chopping-boards-for-kitche - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/keechee-dishwasher-safe-bamboo-cutting-board-lightweight-durable-wood-cutting-bo - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/cooler-kitchen-bamboo-wood-cutting-board-set-with-6-color-coded-flexible-plastic - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-salad-tossers-set-of-2-natural-wooden-salad-servers-with-thumb-hook-ergon - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/3-pcs-wood-cutting-board-set-with-innovative-stand-durable-double-sided-bamboo-c - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/smirly-bamboo-cutting-boards-for-kitchen-wood-cutting-board-for-meal-prep-servin - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/3-pack-heavy-duty-bamboo-cutting-board-set-non-toxic-wooden-cutting-board-for-ki - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/3-piece-bamboo-cutting-board-set-cutting-boards-for-kitchen-hampton-distribution - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-cutting-board-for-kitchen-3-piece-wooden-cutting-board-set-with-juice-gro - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/romguar-craft-3-tier-bamboo-shoe-rack-for-closet-free-standing-wood-shoe-shelf-s - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-cutting-board-for-kitchen-18-large-wood-charcuterie-cheese-board-wooden-c - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/acethrills-dad-birthday-gift-for-dad-gifts-from-daughter-son-gifts-for-dads-who- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/homaxy-extra-large-organic-bamboo-cutting-boards-for-kitchen-17-5-x12-wooden-xl- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/3-piece-carbonized-bamboo-cutting-boards-groove-handles-stand-15-12-9-carbonized - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/dujen-2-pieces-bamboo-toast-tongs-10-2-inches-long-wood-toaster-tongs-with-anti- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/gfware-toothbrush-holders-for-bathrooms-5-slots-bamboo-toothbrush-holder-kids-el - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/personalized-cutting-board-custom-bamboo-engraved-charcuterie-boards-best-presen - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/besilord-silverware-organizer-kitchen-drawer-organizer-utensil-organizer-bamboo- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/farberware-bamboo-cutting-board-with-clear-locking-lid-11-x-14-inch-build-a-boar - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/2-pack-small-bamboo-wood-cutting-board-for-kitchen-8-6-x-6-mini-wooden-charcuter - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/hefild-7-pcs-small-wooden-spoons-5-8-inch-honey-spoons-for-tea-hardwood-wooden-h - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/67pcs-baby-shower-prizes-charcuterie-boards-bulk-for-guests-bamboo-cheese-board- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/wooden-laptop-stand-vertical-laptop-for-13-18-6in-bamboo-stand-for-desk-laptop-s - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/nuvograin-laptop-stand-wooden-vertical-laptop-stand-for-desk-bamboo-laptop-stand - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/wooden-vertical-laptop-stand-for-desk-bamboo-laptop-holder-compatible-with-macbo - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-laptop-stand-for-desk-ergonomic-space-saving-lightweight-stand-for-laptop - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/wkwker-bamboo-laptop-desk-with-folding-legs-tilting-surface-workstation-table-ad - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-wooden-laptop-stand-portable-wood-laptop-stand-for-desk-tablet-with-carry - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-monitor-stand-riser-4-adjustable-height-easy-assembly-laptop-stand-with-a - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/riveira-bamboo-cooking-spoons-6-piece-wooden-cooking-utensils-set-with-wooden-sp - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/best-non-toxic-wooden-spatula-for-cooking-utensils-teak-wood-wok-spatulas-for-ki - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/best-non-toxic-wooden-spatula-for-cooking-utensils-set-wood-spurtle-flat-spatula - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/royal-craft-wood-wooden-cutting-boards-for-kitchen-meal-prep-and-serving-bamboo- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/5-pcs-scalloped-cutting-board-wood-bulk-15-7-x-11-inch-mini-charcuterie-boards-b - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/smirly-charcuterie-large-bamboo-cheese-board-set-for-serving-house-warming-gifts - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/smirly-charcuterie-boards-set-large-cheese-board-set-natural-bamboo-charcuterie- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/smirly-charcuterie-board-set-large-bamboo-cheese-board-for-serving-house-warming - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/smirly-charcuterie-boards-set-large-bamboo-cheese-board-set-house-warming-gifts- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/eco-soul-6-inch-disposable-palm-leaf-bamboo-plates-50-pack-square-premium-natura - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/eco-soul-7and10-inch-disposable-palm-leaf-bamboo-bundle-plates-100-pack-premium- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/eco-soul-6-inch-disposable-palm-leaf-bamboo-plates-400-pack-square-premium-natur - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/vivago-bamboo-toothbrushes-10-pack-bpa-free-soft-bristles-toothbrushes-eco-72v3y - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/vivago-bamboo-toothbrushes-medium-bristles-10-pack-bpa-free-medium-bristles-toot - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/vivago-bamboo-toothbrushes-10-pack-natural-toothbrushes-plant-based-soft-bristle - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/navona-30-pcs-bamboo-toothbrushes-pack-bpa-free-medium-soft-bristles-toothbrushe - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/genkent-bamboo-toothbrushes-12-pack-natural-toothbrushes-bpa-free-soft-bristles- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/amazerbath-soap-holder-soap-dish-bamboo-soap-dish-2-pack-wood-bar-soap-holder-fo - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/vaefae-6-pack-adjustable-bamboo-drawer-dividers-expandable-drawer-organizer-for- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/vaefae-premium-bamboo-drawer-organizer-expandable-silverware-drawer-organizer-cu - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/vaefae-in-drawer-knife-block-bamboo-knife-drawer-organizer-insert-kitchen-knife- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/maplegrace-bamboo-drawer-organizer-set-individual-wood-storage-organizers-5-pcs- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/pipishell-bamboo-expandable-silverware-drawer-organizer-adjustable-kitchen-utens - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-dual-monitor-stand-riser-natural-2-tier-desk-monitor-riser-for-2-monitors - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-monitor-stand-riser-with-drawer-ergonomic-computer-riser-for-desk-with-ca - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/dual-monitor-stand-riser-with-drawer-for-desk-laptop-desk-shelf-with-adjustable- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/flexispot-solid-bamboo-standing-desk-55-34-x-28-34-real-bamboo-desktop-with-natu - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/flexispot-e6-bamboo-3-stage-dual-motor-electric-standing-desk-55-x-28-sit-stand- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/mumtop-bamboo-wind-chimes-deep-tone-wooden-wind-chime-outdoor-garden-decor-natur - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/virekm-bamboo-wind-chimes-memorial-sympathy-wood-wind-chimes-wooden-windchimes-f - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/suction-plates-bowls-set-for-baby-toddler-non-toxic-bamboo-feeding-set-with-sili - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/let-39-s-make-bamboo-suction-plates-bowls-set-for-baby-toddler-divided-platter-f - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-bath-mat-foldable-wooden-bathmat-24-x-16-inches-accessories-for-bathroom- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-bath-mat-foldable-wood-bathmat-34-x-18-inches-modern-farmhouse-decor-and- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/mind-reader-bath-mat-shower-mat-bathtub-mat-bathroom-accessory-wood-rayon-from-b - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/qtlcohd-2-pcs-bamboo-bath-mat-wood-shower-bathroom-mat-16-x-24-inch-non-slip-bat - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/cariloha-bamboo-viscose-bath-towels-plush-quick-dry-towel-turkish-cotton-bamboo- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bathtub-tray-caddy-bath-tray-bath-tub-tray-caddy-bamboo-bath-table-tray-for-tub- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/sen-yi-bao-luxury-bamboo-bathtub-tray-wooden-expandable-caddy-with-book-tablet-w - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bathtub-tray-caddy-bath-tray-bath-tub-table-tray-caddy-3mdgq2 - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/unibambou-bamboo-shower-bench-inside-shower-stool-with-storage-shelf-spa-bath-ch - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/serenelife-bamboo-bath-and-shower-mat-non-slip-floor-mat-with-extra-grips-slatte - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/back-scrubber-for-shower-17in-bamboo-with-curved-long-handle-stiff-natural-boar- - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-shower-bench-for-inside-shower-to-sit-on-waterproof-shower-stool-for-shav - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-corner-shower-stool-2-tier-waterproof-shower-stool-for-inside-shower-shav - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/artisan-cooking-utensil-set - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/slim-spatula-trio - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/everyday-spoon-collection - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/serving-tongs - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/classic-rice-paddle - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/honey-spiral-dipper - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/soft-cheese-spreader-set - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/herb-mortar-and-pestle - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/matcha-scoop - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/reusable-chopstick-set - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/party-skewer-bundle - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/coffee-and-tea-scoop - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/gelato-scoop - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/pasta-server - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/locking-kitchen-tongs - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/wok-turner - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/soup-ladle - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bakers-rolling-pin - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/universal-knife-block-insert - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/stir-fry-spoon - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/grand-prep-board - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/nesting-board-set - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/end-grain-butcher-block - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/charcuterie-board - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/lazy-susan-turntable - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/handled-serving-tray - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bread-board-with-crumb-well - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/sushi-rolling-mat - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/pizza-peel - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/carving-board-with-well - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/steak-plank-pair - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/over-sink-prep-board - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/paddle-serving-board - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/thick-chopping-block - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/charcuterie-host-set - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/dinner-plate-set-of-four - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bowl-set-of-four - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/family-salad-bowl - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-handle-flatware - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/drinking-cup-set - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/ramen-bowl-kit - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/condiment-dish-set - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/serving-spoon-and-fork - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/napkin-ring-set - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/woven-placemat-set - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/coaster-set-with-caddy - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/trivet-pair - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/egg-cup-pair - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/butter-dish - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/picnic-plate-stack - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/soft-bristle-toothbrush-set - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-cotton-swabs - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/slatted-soap-dish - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/toothbrush-stand - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/vanity-tray - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/long-handle-bath-brush - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/paddle-hairbrush - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/safety-razor-handle - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/toilet-brush-caddy - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/corner-shower-shelf - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/expandable-drawer-organizer - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/desk-drawer-dividers - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/lidded-storage-boxes - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/jewelry-case - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/watch-display-stand - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/entry-key-tray - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/remote-caddy - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/two-tier-spice-rack - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/paper-towel-stand - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/counter-wine-rack - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/magazine-stand - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/closet-shelf-dividers - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/roll-top-bread-box - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/compact-waste-bin - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/cable-management-box - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/monitor-stand-riser - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/laptop-stand - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/desk-organizer-caddy - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/keyboard-wrist-rest - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/phone-dock - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/tablet-stand - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/pen-cup - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/business-card-holder - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/mouse-pad-with-wrist-support - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/headphone-stand - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/patio-serving-tray - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/garden-plant-labels - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bamboo-wind-chime - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/torch-stake-set - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/picnic-carrier - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/folding-side-table - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/bird-feeder - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/cutlery-travel-roll - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/suction-baby-plate - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/baby-spoon-trio - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/kids-dinnerware-set - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/baby-hairbrush - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/teething-rattle - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/kids-toothbrush-pair - 2026-08-01 + 2026-08-02 weekly 0.75 https://ibamboo.com/product/snack-bowl-set - 2026-08-01 + 2026-08-02 weekly 0.75 diff --git a/src/data/catalog.ts b/src/data/catalog.ts index 5e893a7..4eda6cc 100644 --- a/src/data/catalog.ts +++ b/src/data/catalog.ts @@ -1,5 +1,7 @@ /** - * iBamboo catalog — curated house + weekly Amazon BSR limited drop. + * iBamboo catalog helpers. + * Live assortment SoT is Flash Catalog (see useFlashCatalog). + * Static merge below is emergency fallback when flash is unreachable. */ export type { Category, Product, ProductSpec } from './types' export { products as curatedProducts } from './products' @@ -177,7 +179,7 @@ export function filterProducts( /** Include ASIN-less pads (default false — prevents house-edit walls) */ includePads?: boolean }, - /** Optional live catalog (flash + static). Defaults to static shopProducts. */ + /** Active assortment pool (prefer flash SoT). Defaults to static emergency. */ pool?: Product[], ) { let list = opts.includePads @@ -225,9 +227,13 @@ export function bsrLeaders(limit = 12, pool: Product[] = shopProducts): Product[ .slice(0, limit) } -/** Same category + collection first, then same category. */ -export function similarProducts(product: Product, limit = 4): Product[] { - const rest = shopProducts.filter((p) => p.id !== product.id) +/** Same category + collection first, then same category. Pass live pool when available. */ +export function similarProducts( + product: Product, + limit = 4, + pool: Product[] = shopProducts, +): Product[] { + const rest = pool.filter((p) => p.id !== product.id) const sameCollection = rest.filter( (p) => p.collection === product.collection && p.category === product.category, @@ -240,7 +246,11 @@ export function similarProducts(product: Product, limit = 4): Product[] { return [...sameCollection, ...sameCategory].slice(0, limit) } -export function youMayAlsoLike(product: Product, limit = 4): Product[] { +export function youMayAlsoLike( + product: Product, + limit = 4, + pool: Product[] = shopProducts, +): Product[] { const adjacent: Record = { kitchen: ['cutting-boards', 'dining', 'organization'], 'cutting-boards': ['kitchen', 'dining', 'organization'], @@ -256,7 +266,7 @@ export function youMayAlsoLike(product: Product, limit = 4): Product[] { const lo = (product.priceHint || 20) * 0.45 const hi = (product.priceHint || 20) * 2.4 - const scored = shopProducts + const scored = pool .filter((p) => p.id !== product.id) .map((p) => { let score = 0 @@ -291,13 +301,20 @@ export function formatRating(n?: number) { return n.toFixed(1) } -export function limitedTimeCopy(pool: Product[] = shopProducts) { +export function limitedTimeCopy( + pool: Product[] = shopProducts, + meta?: { weekOf?: string; generatedAt?: string }, +) { + const limitedCount = limitedProducts(pool).length + // When flash is SoT, "this week" = whole published drop if nothing is flagged limited + const count = limitedCount > 0 ? limitedCount : pool.length return { headline: bsrMarketing.headline, subhead: bsrMarketing.subhead, - weekOf: bsrWeekOf || null, + weekOf: meta?.weekOf || bsrWeekOf || null, expiresAt: bsrExpiresAt || null, - count: limitedProducts(pool).length, + generatedAt: meta?.generatedAt || null, + count, } } diff --git a/src/hooks/useFlashCatalog.ts b/src/hooks/useFlashCatalog.ts index 896a49f..0e9e30c 100644 --- a/src/hooks/useFlashCatalog.ts +++ b/src/hooks/useFlashCatalog.ts @@ -4,17 +4,16 @@ import { withProductMedia } from '../data/productMedia' import type { Product } from '../data/types' import { fetchFlashCatalog, + flashCatalogBaseUrl, mapFlashProducts, type FlashCatalogPayload, } from '../lib/flashCatalog' -function mergeFlashOverStatic(flash: Product[]): Product[] { +function finalizePool(list: Product[]): Product[] { const seenAsin = new Set() const seenSlug = new Set() const out: Product[] = [] - - // Flash limited drop first, then full static catalog (BSR + curated) - for (const p of [...flash, ...shopProducts]) { + for (const p of list) { if (!isMerchandisableProduct(p)) continue if (p.asin && seenAsin.has(p.asin)) continue if (p.asin) seenAsin.add(p.asin) @@ -28,35 +27,57 @@ function mergeFlashOverStatic(flash: Product[]): Product[] { } export type FlashCatalogState = { - /** Full shop list with live flash products preferred */ + /** + * Active shop assortment. + * When source === 'flash', this is flash-only (control plane SoT). + * When source === 'static', emergency fallback (static BSR/curated). + */ products: Product[] flashOnly: Product[] payload: FlashCatalogPayload | null loading: boolean source: 'flash' | 'static' + error?: string generatedAt?: string weekOf?: string + catalogBaseUrl: string } /** - * Prefer live flash catalog for limited-time merchandising. - * Falls back to static BSR-generated catalog when URL unset or fetch fails. + * Load site assortment from Flash Catalog (source of truth). + * Static catalog is emergency fallback only when flash is empty/unreachable. */ export function useFlashCatalog(siteId = 'ibamboo'): FlashCatalogState { + const catalogBaseUrl = flashCatalogBaseUrl() const [payload, setPayload] = useState(null) - const [loading, setLoading] = useState(Boolean(import.meta.env.VITE_FLASH_CATALOG_URL)) + const [loading, setLoading] = useState(Boolean(catalogBaseUrl)) + const [error, setError] = useState() useEffect(() => { let cancelled = false - const base = import.meta.env.VITE_FLASH_CATALOG_URL + const base = flashCatalogBaseUrl() if (!base) { setLoading(false) + setError('Flash catalog URL not configured') return } setLoading(true) + setError(undefined) fetchFlashCatalog(siteId) .then((data) => { - if (!cancelled) setPayload(data) + if (cancelled) return + if (!data) { + setPayload(null) + setError('Flash catalog unreachable or empty') + return + } + setPayload(data) + setError(undefined) + }) + .catch((e) => { + if (cancelled) return + setPayload(null) + setError(e instanceof Error ? e.message : 'Flash catalog fetch failed') }) .finally(() => { if (!cancelled) setLoading(false) @@ -67,26 +88,31 @@ export function useFlashCatalog(siteId = 'ibamboo'): FlashCatalogState { }, [siteId]) return useMemo(() => { - const flashOnly = mapFlashProducts(payload) + const flashOnly = finalizePool(mapFlashProducts(payload)) if (flashOnly.length > 0) { return { - products: mergeFlashOverStatic(flashOnly), + // Assortment SoT: flash products only — do not merge static shelves + products: flashOnly, flashOnly, payload, loading, source: 'flash' as const, + error: undefined, generatedAt: payload?.generatedAt, weekOf: payload?.weekOf, + catalogBaseUrl, } } return { - products: shopProducts, + products: finalizePool(shopProducts), flashOnly: [], payload: null, loading, source: 'static' as const, + error: error || (loading ? undefined : 'Using static emergency catalog'), weekOf: undefined, generatedAt: undefined, + catalogBaseUrl, } - }, [payload, loading]) + }, [payload, loading, error, catalogBaseUrl]) } diff --git a/src/lib/flashCatalog.ts b/src/lib/flashCatalog.ts index 9c3d3dc..71e0c8a 100644 --- a/src/lib/flashCatalog.ts +++ b/src/lib/flashCatalog.ts @@ -1,6 +1,6 @@ /** - * Live flash drop from amazon-flash-catalog Worker (R2-backed JSON). - * Falls back silently so the static BSR catalog still works offline. + * Site assortment from amazon-flash-catalog (R2-backed JSON). + * Flash is the assortment source of truth when healthy; static catalog is emergency fallback only. */ import type { Category, Product } from '../data/types' @@ -33,6 +33,10 @@ export type FlashProductDto = { blurb?: string } +/** Production default — always on so deploy cannot forget the control plane. */ +export const DEFAULT_FLASH_CATALOG_BASE = + 'https://amazon-flash-catalog.tech-bf6.workers.dev' + const CATEGORIES: Category[] = [ 'kitchen', 'cutting-boards', @@ -64,6 +68,21 @@ function hueFromAsin(asin: string): number { return h } +/** Short house name from Amazon title (never dump the full listing string). */ +export function houseNameFromTitle(title: string): string { + let t = title.trim() + // Prefer clause before pipe / em-dash / en-dash separators common on Amazon + t = t.split(/\s*[|–—]\s*/)[0] || t + // Drop trailing marketing piles after comma if still huge + if (t.length > 72) { + const comma = t.indexOf(',') + if (comma > 24 && comma < 72) t = t.slice(0, comma) + } + t = t.replace(/\s+/g, ' ').trim() + if (t.length > 64) t = `${t.slice(0, 61).replace(/\s+\S*$/, '')}…` + return t || title.slice(0, 48) +} + /** Skip non-bamboo / placeholder titles client-side (defense in depth). */ export function isQualityFlashTitle(title: string | undefined): boolean { if (!title || title.trim().length < 12) return false @@ -71,56 +90,90 @@ export function isQualityFlashTitle(title: string | undefined): boolean { return /\bbamboo\b/i.test(title) } -/** Map flash DTO → iBamboo Product (limited-time shelf). */ +/** + * Accept only well-formed Amazon list CDN paths. + * Rejects truncated hashes (e.g. …/I/71eJfNw) that 404 in the wild. + */ +export function isUsableFlashImage(url: string | undefined): boolean { + if (!url || !/^https:\/\//i.test(url)) return false + if (!/\/images\/I\//i.test(url)) return false + if (/\/images\/P\//i.test(url)) return false + // Image id is typically 10–15+ chars before size token + const m = url.match(/\/images\/I\/([A-Za-z0-9_+%-]+)/i) + if (!m || m[1].length < 10) return false + // Prefer paths that already have a size token or full filename + if (!/\._[A-Z0-9_,+%-]+\./i.test(url) && !/\.(jpe?g|png|webp)(\?|$)/i.test(url)) { + return false + } + return true +} + +function collectionFor(cat: Category): string { + switch (cat) { + case 'cutting-boards': + return 'Boards' + case 'desk': + return 'Workspace' + case 'organization': + return 'Organize' + case 'dining': + return 'Tabletop' + case 'bath': + return 'Bath' + case 'baby': + return 'Little Ones' + case 'outdoor': + return 'Outdoor' + default: + return cat.charAt(0).toUpperCase() + cat.slice(1) + } +} + +/** Map flash DTO → iBamboo Product (full assortment item). */ export function flashToProduct(p: FlashProductDto): Product { const asin = p.asin.toUpperCase() - const name = (p.title || '').trim() + const rawTitle = (p.title || '').trim() + const name = houseNameFromTitle(rawTitle) const cat = asCategory(p.siteCategory) - // Only keep reliable listing CDN paths; empty → ProductCard uses unique monogram chain - const images = - p.image && /\/images\/I\//i.test(p.image) && !/\/images\/P\//i.test(p.image) - ? [p.image] - : [] + const images = isUsableFlashImage(p.image) ? [p.image as string] : [] + const limited = Boolean(p.limitedTime) const tagline = p.blurb?.split('—')[0]?.trim() || - 'Limited-time bamboo pick from this week’s flash drop.' + `Bamboo for the ${cat.replace(/-/g, ' ')} — discover on iBamboo, buy on Amazon.` const description = p.blurb || `${name} — a bamboo piece curated for the ${cat.replace(/-/g, ' ')} room.` + // Stable slug: house name + ASIN tail so title refreshes don't thrash URLs + const baseSlug = slugify(name) || asin.toLowerCase() + const slug = `${baseSlug}-${asin.slice(-4).toLowerCase()}` + return { id: `flash-${asin}`, - slug: slugify(name) || asin.toLowerCase(), + slug, name, tagline: tagline.slice(0, 140), description, category: cat, - collection: - cat === 'cutting-boards' - ? 'Boards' - : cat === 'desk' - ? 'Workspace' - : cat === 'organization' - ? 'Organize' - : cat === 'dining' - ? 'Tabletop' - : cat.charAt(0).toUpperCase() + cat.slice(1), + collection: collectionFor(cat), material: 'Bamboo', - features: ['Bamboo focus', 'Limited-time flash'], + features: limited + ? ['Bamboo focus', 'Rotating assortment'] + : ['Bamboo focus', 'Amazon fulfilled'], specs: [], priceHint: p.price && p.price > 0 ? p.price : 24, asin, - searchKeywords: `${name} bamboo`, + searchKeywords: `${rawTitle} bamboo`, images, rating: p.rating, reviewCount: p.reviewCount, hue: hueFromAsin(asin), - limitedTime: true, + limitedTime: limited, weekOf: p.weekOf, bsrRank: p.bsrRank, bsrCategory: p.sourceCategoryId, bsrCategoryId: p.sourceCategoryId, source: p.enriched ? 'amazon-bsr' : 'amazon-search', - badge: 'Flash', + badge: limited ? 'This week' : undefined, } } @@ -128,7 +181,7 @@ export function flashCatalogBaseUrl(): string { const fromEnv = (import.meta.env.VITE_FLASH_CATALOG_URL as string | undefined) ?.trim() .replace(/\/$/, '') - return fromEnv || '' + return fromEnv || DEFAULT_FLASH_CATALOG_BASE } export async function fetchFlashCatalog( diff --git a/src/pages/Admin.tsx b/src/pages/Admin.tsx index 50fe7d8..c1f2006 100644 --- a/src/pages/Admin.tsx +++ b/src/pages/Admin.tsx @@ -619,12 +619,26 @@ export function Admin() {

Status:{' '} - {flash.ok ? 'OK' : 'Error'} + {flash.ok ? 'OK — assortment controller reachable' : 'Error'}

+ {!flash.ok && flash.error ? ( +

+ {String(flash.error)} + {flash.status != null ? ` · HTTP ${String(flash.status)}` : ''} +

+ ) : null} +

+ Probe URL: {String(flash.catalogUrl || config.flash.catalogUrl || '—')} +

Products: {String(flash.productCount ?? '—')}

Week of: {String(flash.weekOf || '—')}

Generated: {String(flash.generatedAt || '—')}

+

+ Flash Catalog owns assortment (cron, targeting, filters). This site + only presents the published catalog — change merch in Flash admin, + then refresh there. +

{Array.isArray(flash.sample) && flash.sample.length > 0 ? (
    {(flash.sample as { asin?: string; title?: string; siteCategory?: string }[]).map( @@ -645,7 +659,7 @@ export function Admin() {

    Loading flash status…

    )}