Production catalog migration, managed media, and storefront continuity
The operational catalog now lives in Supabase as drafts, product media is moving into managed Storage, and storefront navigation preserves pagination and scroll position.
July 18, 2026
This update completes the next operational step identified in the July 12 update: the KOF catalog has moved from a local import-ready snapshot into the production Supabase data model. The 1,046 general-merchandise products remain drafts, preserving a review boundary between supplier data and the live storefront. The five CBD-review products and 272 restricted or ambiguous cannabinoid products remain outside this production inventory scope.
The same pass starts moving the catalog's local image mirror into dedicated Supabase Storage, replaces the experimental storefront inventory path with a production API boundary, and improves the experience of moving through a large product catalog. Pagination, browser history, scroll restoration, loading states, and the smoke effect now work together without making the storefront feel as though it restarts whenever a shopper opens a product and returns.
Production catalog migration
The operational catalog has now been imported into the self-hosted Supabase project:
| Production record | Count | Current state |
|---|---|---|
| Operational products | 1,046 | Imported as drafts |
| Product variants | 2,069 | Linked to draft products |
| Product-media records | 2,298 | Source and fallback references retained |
| CBD-review products | 5 | Excluded from the operational import |
| Restricted-cannabinoid products | 272 | Excluded from the operational import |
Products, variants, options, supplier costs, suggested retail prices, media references, locations, stock levels, and inventory movements now have distinct production records. Supplier availability does not become KOF on-hand inventory, and imported variants do not begin tracking stock until that behavior is intentionally enabled through the management workflow.
Draft status remains the most important safeguard. The public catalog is expected to stay empty until selected products have reviewed merchandising, defensible retail prices, and an explicit publication decision. The migration therefore establishes the production data shape without silently publishing the supplier feed.
A safer, rerunnable importer
The initial apply successfully inserted some draft records before one supplier handle failed the database's slug constraint. The importer was hardened instead of deleting or manually repairing those partial records.
Supplier handles now pass through one deterministic slug normalizer before new products are created. It normalizes Unicode, lowercases text, removes apostrophes, converts other punctuation runs to single hyphens, and rejects an empty result. A preflight check also confirms that every operational slug is valid and unique before any remote write begins.
The handle that exposed the issue was:
9-clover-angled-neck-matrix-perc-water-pipe-wpe-434_It now becomes the database-safe slug:
9-clover-angled-neck-matrix-perc-water-pipe-wpe-434Reruns continue to refresh supplier facts, cost, availability, options, and media while preserving dashboard-managed slugs, product names, descriptions, publication decisions, retail prices, and tracked inventory by default. Explicit overwrite controls remain available for intentional bulk changes. This makes both a partially completed import and future supplier refreshes safe to resume without treating the database as disposable.
Product media moves to managed Storage
The local image mirror was always intended as a temporary reliability layer. A separate,
production-safe media synchronization phase now moves those files into the public
kof-product-media bucket without coupling large object uploads to catalog-row imports.
The complete preflight found:
| Media audit | Result |
|---|---|
| Operational media files | 2,298 |
| Existing local files | 2,298 |
| Missing files | 0 |
| Unique content hashes | 2,220 |
| Expected upload size | 206,975,909 bytes (about 197.4 MiB) |
File signatures, rather than supplier filename extensions, determine the stored content type. The
mirror contains 2,063 WebP files, 162 JPEG files, and 73 AVIF files; many supplier URLs ending in
.jpg actually return modern WebP or AVIF bytes.
Each object receives a deterministic path containing its supplier product ID, source image ID, and a short content hash. Existing objects are skipped, new objects are never blindly overwritten, and the matching database media row is updated only after the Storage object is confirmed. Failed or interrupted batches can be rerun, and the process never deletes local files or stale Storage objects.
The guarded media upload began with this update. The local mirror remains in place until the run is verified with zero upload and database-update failures. Runtime delivery then has three layers:
- Use the original Shopify image while it remains available.
- Fall back to the mirrored Supabase Storage URL.
- Use the temporary repo-local image only as a final fallback.
This keeps the supplier reference intact while giving KOF control of the product bytes. Storage objects are also treated as a separate backup concern because PostgreSQL backups do not include the bucket's image data.
Production storefront data boundary
The storefront API now reads its inventory through a deliberately narrow public Supabase function. The operational tables stay in the private KOF schema, while the anonymous storefront receives only the active customer-facing catalog fields it needs.
The previous wholesale seed remains available for tests and explicit local tooling, but it is no longer a silent production fallback. Missing Supabase configuration or a failed catalog request now produces a clear unavailable state and a service-unavailable response instead of displaying sample inventory as though it were live data. API health remains independent so an inventory outage can be identified without making the whole service appear offline.
This boundary also means draft products cannot leak into the storefront simply because the import completed. The API and database publication state must agree before a record becomes public.
Catalog navigation and loading continuity
The larger catalog exposed several navigation problems that were easy to miss with the earlier small inventory. The main storefront and full products page now show 12 products per page with a compact pagination control.
Pagination state lives in browser history rather than a visible query parameter. The storefront URL therefore stays clean while Back and Forward can still restore the selected page. React Router scroll restoration records the matching scroll position, so opening a product and returning brings the shopper back to the product grid position they left instead of the footer or the top of page one. The active category shelf is stored with that same history entry, so returning from a product also restores the filtered catalog rather than reverting to the full wall.
Loading behavior was adjusted around that restored state:
- A returning catalog shows a skeleton for the requested page while inventory resolves, avoiding a flash of page one.
- A successfully loaded inventory snapshot is retained across client-side route navigation, which avoids unnecessary refetches and catalog flashes.
- Product list and detail failures render explicit recovery states instead of falling back to demo data.
- Route transitions no longer briefly reveal the location splash between the catalog and product detail pages.
- Featured shelf tiles use curated, category-appropriate catalog photography. Empty categories are omitted from both the featured rail and the sidebar instead of showing dead-end filters.
- On desktop, the filter rail remains sticky within the product workspace. The existing numbered pagination sits in a storefront-level sticky surface that spans from the sidebar to the viewport edge, then releases at the product-results boundary before it can overlap the featured shelves or storefront heading.
The large smoke shader also uses a targeted lazy-mount boundary with a generous preload margin. When it is near the viewport, the existing visible loader remains clear and intentional. When a shopper returns to a middle scroll position with the effect offscreen, it can initialize in the background instead of holding the entire page transition. Once reached, its mounted state is latched for the rest of the session.
Development testing controls
Testing auth, age verification, cart, checkout, and browser-history behavior no longer requires repeated trips into browser storage tools. A private workspace package now provides a configurable, floating reset menu in Vite development only.
The KOF integration can clear its own age-verification state, sign out through the real auth
provider, reset cart and checkout state, or run selected combinations without using a broad
localStorage.clear(). It is dynamically excluded from production and can also be disabled through
development configuration.
Purpose-built development scenarios can enter the real app as a guest, member, cart, or checkout case. The one-time query is removed after entry, the scenario remains tab-scoped, and normal routes, providers, browser history, and scroll restoration continue to run. This makes movement between pages testable without replacing the behavior being tested with a simulated screen.
The separate THCA Reserve continues to use the 272-record restricted review seed during local API development. That override applies only to the THCA catalog surface; the main storefront continues to use Supabase, and the production API entry does not enable local demo inventory.
Production-facing store details
Supporting storefront details were tightened while the data path moved toward production:
- Footer actions now use the verified Western Avenue address, Google Maps directions, Waze directions, and the store phone number.
- Internal product and sign-up navigation stays inside the React Router app instead of constructing malformed production URLs.
- The public KOF URL and King of the Flavors naming are centralized with the rest of the storefront configuration.
- Main-store and THCA review inventory failures are explicit, keeping restricted review data separate from general storefront inventory.
Quality and operational status
The catalog import, media manifest, API boundary, pagination state, navigation behavior, and development resets are covered by the expanded automated checks. At the close of implementation:
- The catalog importer dry run selects all 1,046 operational products and 2,069 variants with valid, unique slugs.
- The media dry run resolves all 2,298 operational images with zero missing local files.
- 60 project tests pass with 200 assertions and no failures.
- Type checking passes across the shared package, API, storefront, THCA review app, developer menu, and documentation app.
The remaining production work is intentionally operational rather than architectural: let the guarded media upload finish, verify object and database-row counts, keep a separate backup of the Storage bucket, review draft merchandising and suggested prices, and activate only the specific products approved for the live catalog.
Live inventory audit and storefront delivery cleanup
A read-only production audit confirms the full Supabase catalog is public, identifies the remaining price, assortment, media, and merchandising review queues, and improves category navigation without changing inventory remotely.
Retail catalog, compliance review, and storefront polish
Wholesale catalog refinement, inventory-management groundwork, a restricted-product review experience, and a more coherent storefront modal system.