Skip to content

Inventory Flow

This page explains how the inventory feature works end-to-end: screens, components, hooks, and the Supabase tables they touch.


Screens & key components

Inventory screen (/inventory, /inventory/new, /inventory/edit/:id)

File: src/pages/Inventory.tsx

Responsibilities:

  • List and manage all inventory items for the current company/tenant.
  • Support separate views for Active vs Archived items.
  • Provide search, filter, sort, and low-stock highlighting.
  • Handle creating, editing, duplicating, archiving, and unarchiving items.

Key pieces:

  • Tabs (Active / Archived)

    • Backed by a boolean flag passed to fetchInventoryItems(isActive).
    • archiveInventoryItems(ids) and unarchiveInventoryItems(ids) toggle archival state in bulk.
  • Search and filters

    • filters.search filters items by:
    • SKU
    • Product name
    • Description
    • Category
    • Price-like values (if search string is numeric, matches against retail/wholesale/cost as strings).
    • filters.stockLevel supports:
    • out_of_stock – stock = 0.
    • low_stock – stock > 0 and ≤ low_stock_threshold.
    • in_stock – stock > low_stock_threshold.
    • filters.sortBy controls sorting by name, SKU, price, wholesale price, cost, or available stock.
  • Low stock badge

    • Uses getLowStockItems() from the inventory hook.
    • Displays a Low Stock button with a Badge showing the count.
    • Clicking toggles the low-stock filter.
  • Inventory table

    • File: src/components/inventory/InventoryTable.tsx
    • Displays items with columns for SKU, product name, category, pricing, and stock.
    • Supports row selection for bulk archive/unarchive.
    • Integrates sorting callbacks (onSort) and selection state.
  • Inventory form

    • File: src/components/inventory/InventoryForm.tsx
    • Used for both create and update flows.
    • Receives item and onSubmit props (wired to createInventoryItem / updateInventoryItem).
    • Handles core fields: SKU, name, category, description, prices, stock, stock thresholds, vendor, etc.
  • Product edit modal

    • File: src/components/inventory/ProductEditModal.tsx
    • Used when clicking a row or variation.
    • Loads full item (via fetchInventoryItem) including variations.
    • Allows editing variations, photos, and advanced settings while viewing context.
  • Keyboard shortcuts

    • Ctrl+N – open create item form.
    • Ctrl+F – focus search input.

Hooks & operations

Inventory hooks are organized via a facade:

File: src/hooks/useInventory.ts

  • Combines domain-specific hooks:
    • useInventoryShared – shared loading/error state.
    • useInventoryItems – CRUD for items.
    • useInventoryPhotos – photos for items.
    • useVariationAttributes / useVariationAttributeValues – attribute definitions and values.
    • useProductVariations – variant rows.
    • useVariationPhotos – variant-level photos.

The combined interface exposes:

  • Items

    • fetchInventoryItems(isActive: boolean) – fetch list of items (active vs archived).
    • fetchInventoryItem(id) – fetch a single item with details/variations.
    • createInventoryItem(input) – create new item.
    • updateInventoryItem(input) – update existing item.
    • deleteInventoryItem(id) – hard-delete item.
    • getLowStockItems() – used for low-stock badge.
    • archiveInventoryItems(ids) / unarchiveInventoryItems(ids) – bulk archive toggles.
  • Variations & attributes

    • CRUD for variation attributes and values.
    • createProductVariation, updateProductVariation, deleteProductVariation, bulkCreateProductVariations, bulkUpdateProductVariations.
  • Photos

    • uploadInventoryPhoto, deleteInventoryPhoto, setPrimaryPhoto.
    • uploadVariationPhoto, assignPhotoToVariation, deleteVariationPhoto, setVariationPrimaryPhoto, reorderVariationPhotos.

These hooks ultimately call Supabase via shared utilities in integrations/supabase.


Tables touched

The exact schema is defined in migrations, but the key tables are:

  • inventory_items

    • Core product metadata for each SKUs.
    • Fields (from migrations and type usage):
    • id – primary key.
    • tenant_id / company_id – multi-tenant isolation.
    • sku, product_name, category, subcategory.
    • item_description.
    • retail_price, wholesale_price, cost_of_goods.
    • current_stock_qty – stock-level for base product.
    • track_quantity – whether stock should be decremented.
    • low_stock_threshold – used for low-stock checks.
    • track_variant_inventory – whether stock is derived from variants.
    • Archive flag / timestamps (e.g. archived_at).
  • Variant-related tables (names inferred from migrations and hooks):

    • product_variations – per-variant rows linked to inventory_items.
    • variation_attributes – defines variation dimensions (e.g. size, color).
    • variation_attribute_values – possible values per attribute.
    • variation_photos – mapping photos to specific variants.
  • Payment-related tables

    • pay_link_line_items references inventory_items(id) for pay links:
    inventory_item_id UUID REFERENCES public.inventory_items(id) ON DELETE SET NULL
    
    • order_line_items.item_name is populated from inventory product names for invoices and order displays.

Authoritative schemas

For exact column definitions and relationships, see the Supabase migrations referenced in Supabase Architecture and the API/Data → Supabase Tables page.


CRUD & behavior

Create

  • Triggered via Create Item button on the inventory screen.
  • Opens InventoryForm with no item.
  • On submit:
    • Calls createInventoryItem(input).
    • Refreshes inventory list via loadItems().
    • If route was /inventory/new, navigates to /inventory/edit/:id for the new item.

Read

  • List view uses fetchInventoryItems(isActive).
    • Filtered and sorted in Inventory.tsx based on filters.
  • Detail view uses fetchInventoryItem(id) when:
    • Navigating to /inventory/edit/:id.
    • Opening the ProductEditModal from a row click.

Update

  • Editing base item:
    • If editingItem is set, InventoryForm calls updateInventoryItem({ id, ...data }).
  • Editing variations:
    • ProductEditModal calls updateInventoryItem(itemId, data) and variation-specific hooks.
    • Variations can be added, updated, or removed via the variation-specific hooks.
  • Updating photos:
    • Uses uploadInventoryPhoto / uploadVariationPhoto and related helpers.

Delete

  • Deleting items:
    • deleteInventoryItem(id) from row actions.
    • After deletion, reloads inventory and low-stock count.
  • Deleting variations:
    • deleteProductVariation(variationId) and then reloads inventory and low-stock count.

Archive / Unarchive

  • Archive flow:
    • User selects rows and clicks Archive.
    • archiveInventoryItems(Array.from(selectedItems)) is called.
    • On success, selection is cleared and inventory reloaded.
  • Unarchive flow:
    • Same pattern via unarchiveInventoryItems from the Archived tab.
  • Archived items:
    • Hidden from main selection lists.
    • Still referenced in historical orders, invoices, and pay links.

Search & filter behavior

Search logic in Inventory.tsx:

  • Treats filters.search as a free-text query.
  • Matches against:
    • Base fields: sku, product_name, item_description, category.
    • Prices (if the search string is numeric).
    • Variation fields:
    • Combined SKU: item.sku + variation.sku_suffix.
    • Variation display_name.
    • Joined attribute values (e.g. size/color combinations).

Stock-level filter:

  • Uses computed stock per item:
    • If track_variant_inventory is true and item has variations:
    • Stock = sum of all variation.current_stock_qty values.
    • Otherwise: stock = current_stock_qty.
  • Interprets low_stock_threshold per item.

Sorting:

  • filters.sortBy values map to UI columns:
    • alphabetical → product name.
    • sku_asc / sku_desc → SKU.
    • price_asc / price_desc → min/max retail price (considering variations).
    • wholesale_price_*, cost_* → min/max values including variations.
    • stock_* → computed stock per item.

Flow diagram

flowchart LR
  U[User] --> INV[/Inventory screen/]
  INV --> HOOK[useInventory hook]
  HOOK --> DB[(Supabase: inventory_items & variations)]
  INV --> FILTERS[Search & filters]
  FILTERS --> INV

  click INV "../architecture/frontend/" "Inventory page and components"
  click HOOK "../architecture/frontend/" "Frontend hooks & data fetching"
  click DB "../architecture/supabase/" "Supabase schema (inventory tables)"

Narrative:

  1. User navigates to /inventory and sees the Inventory screen.
  2. The screen calls useInventory to fetch items from Supabase.
  3. The result set is filtered and sorted client-side by search, stock level, and sort options.
  4. CRUD operations call back into useInventory hooks, which in turn persist changes to Supabase.
  5. Related flows (orders, pay links, payments) reference inventory items by inventory_item_id and item_name.