Skip to content

Inventory Implementation Quick Reference

Quick reference guide for implementing inventory improvements in the Shaker mobile app.

#FeatureUser StoryEffortImpact
1ProductLevel Value ObjectProduct Level TrackingMediumHigh
2ProductBarcode Value ObjectBarcode ScanningLowMedium
3Ingredient EntityIngredient MappingHighHigh
4Enhanced Product Level DisplayProduct Level TrackingMediumHigh
5Quick Barcode Update FlowBarcode ScanningMediumHigh
6Shopping List → Inventory IntegrationShopping List ManagementHighHigh
7Ingredient Detail ScreenIngredient MappingMediumMedium
8Quick Update Modal ComponentProduct Level TrackingLowHigh
#FeatureUser StoryEffortImpact
9IngredientProfile for FlavorFlavor ProfilesMediumMedium
10Product-Ingredient Mapping InterfaceIngredient MappingMediumHigh
11Enhanced Shopping List EditingShopping List ManagementMediumMedium
12Inventory Search & FilterGeneral UXMediumHigh
13Product Level Indicator ComponentProduct Level TrackingLowMedium
14Ingredient Selector ComponentIngredient MappingMediumMedium
15Shopping List Item EditorShopping List ManagementLowMedium
#FeatureUser StoryEffortImpact
16Product Tags InterfaceTagging SystemMediumLow
17Batch Barcode ScanningBarcode ScanningHighMedium
18Cost History TrackingShopping List ManagementMediumLow
19Flavor Profile SystemFlavor ProfilesHighLow
20Product SubstitutionIngredient MappingHighLow
21Export/ImportData PortabilityMediumLow
shaker/app/(tabs)/inventory/
├── index.tsx # Dashboard - Add quick actions
├── view-all.tsx # Add search/filter
├── inventory-items.tsx # Add ProductLevelIndicator
├── [id].tsx # Enhanced product detail
├── add-product.tsx # Add ingredient mapping
└── ingredients/
├── index.tsx # NEW - Ingredient list
└── [id].tsx # NEW - Ingredient detail
shaker/app/(tabs)/events/
├── shopping-list.tsx # Enhance with editing
└── shopping-list-items.tsx # Add inventory integration
shaker/components/
├── ProductLevelIndicator.tsx # Visual level display
├── QuickUpdateModal.tsx # Quick level update
├── IngredientSelector.tsx # Ingredient autocomplete
└── ShoppingListItemEditor.tsx # Edit shopping items
shaker/types/
├── product.ts # Add ProductLevel, ProductBarcode
├── ingredient.ts # NEW - Ingredient types
└── shopping-list.ts # Enhance ShoppingListItem
shaker/types/product.ts
export interface ProductLevel {
currentAmount: number; // ml or oz
totalCapacity: number; // ml or oz
percentage: number; // 0-100
status: 'full' | 'high' | 'medium' | 'low' | 'empty';
lastUpdated: string; // ISO date
}
export interface ProductBarcode {
code: string;
type: 'UPC' | 'EAN' | 'QR' | 'CODE128';
scannedDate?: string;
}
export interface Product {
id: string;
name: string;
brand?: string;
type: ProductType;
volume: number;
volumeUnit: string;
currentLevel: ProductLevel; // Changed from string
barcode?: ProductBarcode; // Changed from string
ingredients: string[]; // NEW - Ingredient IDs
// ... other fields
}
shaker/types/ingredient.ts
export type IngredientType =
| 'spirit'
| 'mixer'
| 'modifier'
| 'sweetener'
| 'garnish'
| 'bitters'
| 'other';
export interface IngredientProfile {
sweet: number; // 0-10
sour: number;
bitter: number;
umami: number;
spicy: number;
herbal: number;
floral: number;
fruity: number;
}
export interface Ingredient {
id: string;
name: string;
type: IngredientType;
category?: string;
flavorProfile?: IngredientProfile;
substitutes?: string[]; // Ingredient IDs
products?: string[]; // Product IDs
createdAt: string;
updatedAt: string;
}
shaker/components/ProductLevelIndicator.tsx
import { View, Text } from 'react-native';
import { ProductLevel } from '@/types/product';
interface Props {
level: ProductLevel;
variant?: 'bar' | 'bottle' | 'circle' | 'compact';
size?: 'small' | 'medium' | 'large';
}
export function ProductLevelIndicator({ level, variant = 'bar', size = 'medium' }: Props) {
const getColor = () => {
if (level.percentage >= 75) return '#4CAF50'; // green
if (level.percentage >= 50) return '#FFC107'; // yellow
if (level.percentage >= 25) return '#FF9800'; // orange
return '#F44336'; // red
};
if (variant === 'bar') {
return (
<View style={{ width: '100%', height: 8, backgroundColor: '#E0E0E0', borderRadius: 4 }}>
<View
style={{
width: `${level.percentage}%`,
height: '100%',
backgroundColor: getColor(),
borderRadius: 4
}}
/>
</View>
);
}
// Other variants...
}
query GetProductsWithIngredients {
products {
id
name
brand
type
currentLevel {
currentAmount
totalCapacity
percentage
status
lastUpdated
}
barcode {
code
type
}
ingredients {
id
name
type
}
}
}
mutation UpdateProductLevel($productId: ID!, $level: ProductLevelInput!) {
updateProductLevel(productId: $productId, level: $level) {
id
currentLevel {
percentage
status
}
}
}
mutation MapProductToIngredient($productId: ID!, $ingredientId: ID!) {
mapProductToIngredient(productId: $productId, ingredientId: $ingredientId) {
id
ingredients {
id
name
}
}
}
  • Product can be created with ProductLevel object
  • Product can be created with ProductBarcode object
  • Ingredient can be created
  • Product can be mapped to ingredient
  • Product can be unmapped from ingredient
  • Migration from old currentLevel string works
  • ProductLevelIndicator displays correctly
  • Quick update modal works
  • Barcode scan updates level
  • Level colors match status
  • Percentage calculation is accurate
  • Can check off shopping list item
  • Checking off prompts for product selection
  • Selecting product updates inventory
  • Can add custom items to shopping list
  • Can edit quantities
  • Can edit costs
function calculateProductLevel(currentAmount: number, totalCapacity: number): ProductLevel {
const percentage = Math.round((currentAmount / totalCapacity) * 100);
let status: ProductLevel['status'];
if (percentage >= 90) status = 'full';
else if (percentage >= 60) status = 'high';
else if (percentage >= 30) status = 'medium';
else if (percentage > 0) status = 'low';
else status = 'empty';
return {
currentAmount,
totalCapacity,
percentage,
status,
lastUpdated: new Date().toISOString()
};
}
function migrateLegacyLevel(oldLevel: string, volume: number): ProductLevel {
const percentageMap: Record<string, number> = {
'full': 100,
'75': 75,
'50': 50,
'25': 25,
'empty': 0
};
const percentage = percentageMap[oldLevel] || 100;
const currentAmount = (volume * percentage) / 100;
return calculateProductLevel(currentAmount, volume);
}

Last Updated: 2025-12-30
Related: Full Analysis