Garage Door OS Docs
Price Book

Price Book Components During Creation

Add inventory components while creating price book items — required vs optional parts of bundled services.
8 min read

Price Book Inventory Components During Creation

Summary

Problem: Previously, when creating a new price book item, users had to save the item first before they could add inventory components. This required a two-step process: create the item, then go back to add components.
Solution: Users can now select inventory components while creating a price book item. All components are saved together when the price book item is created, streamlining the workflow.

Changes Made

Frontend (apps/web/src/pages/priceBook/PriceBook.tsx)

1. Added New Imports

import { InventoryItemSelector, type SelectedInventoryItem } from '../../components/InventoryItemSelector'

2. Added State Management

const [pendingComponents, setPendingComponents] = useState<SelectedInventoryItem[]>([])
const [showComponentSelector, setShowComponentSelector] = useState(false)
  • pendingComponents: Stores temporarily selected inventory items before the price book item is created
  • showComponentSelector: Controls the visibility of the inventory item selector modal

3. Updated Event Handlers

handleAdd: Resets pending components when opening the create modal
const handleAdd = () => {
  setEditingItem(null)
  setPendingComponents([])  // Clear pending components
  // ... rest of the code
}
handleModalCancel: Clears pending components when closing the modal
const handleModalCancel = () => {
  // ... existing code
  setPendingComponents([])  // Clear pending components
}
handleModalOk: Saves pending components after creating the price book item
if (editingItem) {
  // Update existing item
} else {
  const result = await createPriceBookItem(formData).unwrap()
  const newPriceBookItemId = result.data?.id
  
  // If there are pending components, create them now
  if (newPriceBookItemId && pendingComponents.length > 0) {
    for (const component of pendingComponents) {
      await apiClient.post(`/api/price-book/${newPriceBookItemId}/components`, {
        inventoryItemId: component.id,
        quantity: component.quantity,
        unitCost: component.unitCost,
        isOptional: component.isOptional || false,
        description: component.description,
        sortOrder: successCount
      })
    }
  }
}

4. Updated "Inventory Components" Tab

Before: Showed a message "Components will be available after saving"
After: Shows a full interface for adding and managing components:
  • Alert explaining components can be added before saving
  • "Add Components" button to open inventory selector
  • Table showing selected components with:
    • Item name and SKU
    • Editable quantity (inline editing)
    • Unit cost
    • Total cost calculation
    • Optional/Required tag
    • Delete button to remove individual components
  • Empty state when no components are added

5. Added Inventory Item Selector Modal

<InventoryItemSelector
  visible={showComponentSelector}
  onClose={() => setShowComponentSelector(false)}
  onSelect={(selectedItems) => {
    // Merge with existing pending components, avoiding duplicates
    const existingIds = new Set(pendingComponents.map(c => c.id))
    const newComponents = selectedItems.filter(item => !existingIds.has(item.id))
    setPendingComponents([...pendingComponents, ...newComponents])
    setShowComponentSelector(false)
  }}
  selectedItemIds={pendingComponents.map(c => c.id)}
  title="Add Inventory Components"
  showQuantityInput={true}
  showCostOverride={true}
  showOptionalFlag={true}
  multiSelect={true}
/>

User Experience

New Workflow (Streamlined)

  1. Click "Add Item" to create a new price book item
  2. Fill in "Basic Information" tab (name, price, etc.)
  3. Switch to "Inventory Components" tab
  4. Click "Add Components" button
  5. Select inventory items from the modal
    • Set quantities
    • Override costs if needed
    • Mark as optional/required
  6. Review selected components in the table
    • Edit quantities inline
    • Remove unwanted components
  7. Click "OK" to save
    • ✨ Price book item AND all components are saved together!

Old Workflow (Required Two Steps)

  1. Create price book item
  2. Save it
  3. Reopen the item
  4. Go to Inventory Components tab
  5. Add components
  6. Save again

Features

Component Management During Creation

  • Add Multiple Components: Select multiple inventory items at once
  • Set Quantities: Define quantity for each component
  • Cost Override: Override default inventory cost if needed
  • Optional/Required: Mark components as optional or required
  • Edit Before Saving: Modify quantities or remove components before creating the item
  • Visual Feedback: See total cost calculations in real-time
  • Duplicate Prevention: Can't add the same inventory item twice

Table Features

  • Inline Quantity Editing: Change quantities directly in the table
  • Cost Calculation: Automatic total cost = unit cost × quantity
  • Remove Components: Delete button for each component
  • Component Counter: Shows count of selected components

Success Messages

  • If components are added: "Price book item created with X component(s)!"
  • If no components: "Price book item created successfully"

API Integration

Create Price Book Item

POST /api/price-book
{
  name: string
  itemType: string
  unitPrice: number
  // ... other fields
}

Add Components (After Creation)

POST /api/price-book/:priceBookItemId/components
{
  inventoryItemId: string
  quantity: number
  unitCost?: number
  isOptional: boolean
  description?: string
  sortOrder: number
}
Note: Components are added sequentially after the price book item is created. If any component fails, others will still be added (graceful degradation).

Benefits

  1. ⚡ Faster Workflow: Save time by not having to reopen items to add components
  2. 📦 Single Transaction: All data saved together
  3. ✨ Better UX: More intuitive - configure everything before saving
  4. 🔄 Consistent with Other Forms: Matches pattern used in job creation, estimates, etc.
  5. 🛡️ Data Integrity: Components are added immediately after item creation
  6. 👀 Preview: See exactly what components will be added before committing

Edge Cases Handled

  • No inventory items: Tab is disabled if no inventory exists
  • Duplicate prevention: Can't add the same item twice
  • Modal cancellation: Pending components are cleared if modal is cancelled
  • Component creation failure: Each component creation is wrapped in try-catch
  • Editing existing items: Still uses the original PriceBookComponents component

Files Modified

  1. apps/web/src/pages/priceBook/PriceBook.tsx
    • Added imports for InventoryItemSelector
    • Added state for pending components
    • Updated event handlers
    • Completely redesigned "Inventory Components" tab for new items
    • Added inventory selector modal

Testing Checklist

  • Create a new price book item without components (works as before)
  • Create a new price book item with components
  • Add multiple components at once
  • Edit component quantities before saving
  • Remove a component before saving
  • Cancel the modal and verify components are cleared
  • Verify components are saved with correct data
  • Edit an existing price book item (should work as before)
  • Try adding duplicate components (should be prevented)
  • Verify success messages display correctly
  • Check that tab is disabled when no inventory items exist

Future Enhancements

  • Add visual cost summary in the components tab (like in the PriceBookComponents view)
  • Allow bulk editing of component quantities
  • Drag-and-drop reordering of components before saving
  • Import components from another price book item
  • Template support for common component combinations

Created: October 18, 2025
Status: ✅ Complete - Ready for Testing