Implement 5 UX enhancements for ingredient management
This commit implements the requested enhancements for the ingredient quick-add system and batch management: **1. Duplicate Detection** - Real-time Levenshtein distance-based similarity checking - Shows warning with top 3 similar ingredients (70%+ similarity) - Prevents accidental duplicate creation - Location: QuickAddIngredientModal.tsx **2. Smart Category Suggestions** - Auto-populates category based on ingredient name patterns - Supports Spanish and English ingredient names - Shows visual indicator when category is AI-suggested - Pattern matching for: Baking, Dairy, Fruits, Vegetables, Meat, Seafood, Spices - Location: ingredientHelpers.ts **3. Quick Templates** - 10 pre-configured common bakery ingredients - One-click template application - Templates include: Flour, Butter, Sugar, Eggs, Yeast, Milk, Chocolate, Vanilla, Salt, Cream - Each template has sensible defaults (shelf life, refrigeration requirements) - Location: QuickAddIngredientModal.tsx **4. Batch Creation Mode** - BatchAddIngredientsModal component for adding multiple ingredients at once - Table-based interface for efficient data entry - "Load from Templates" quick action - Duplicate detection within batch - Partial success handling (some ingredients succeed, some fail) - Location: BatchAddIngredientsModal.tsx - Integration: UploadSalesDataStep.tsx (2 buttons: "Add One" / "Add Multiple") **5. Dashboard Alert for Incomplete Ingredients** - IncompleteIngredientsAlert component on dashboard - Queries ingredients with needs_review metadata flag - Shows count badge and first 5 incomplete ingredients - "Complete Information" button links to inventory page - Only shows when incomplete ingredients exist - Location: IncompleteIngredientsAlert.tsx - Integration: DashboardPage.tsx **New Files Created:** - ingredientHelpers.ts - Utilities for duplicate detection, smart suggestions, templates - BatchAddIngredientsModal.tsx - Batch ingredient creation component - IncompleteIngredientsAlert.tsx - Dashboard alert component **Files Modified:** - QuickAddIngredientModal.tsx - Added duplicate detection, smart suggestions, templates - UploadSalesDataStep.tsx - Integrated batch creation modal - DashboardPage.tsx - Added incomplete ingredients alert **Technical Highlights:** - Levenshtein distance algorithm for fuzzy name matching - Pattern-based category suggestions (supports 100+ ingredient patterns) - Metadata tracking (needs_review, created_context) - Real-time validation and error handling - Responsive UI with animations - Consistent with existing design system All features built and tested successfully. Build time: 21.29s
This commit is contained in:
@@ -7,6 +7,7 @@ import { useValidateImportFile, useImportSalesData } from '../../../../api/hooks
|
||||
import type { ImportValidationResponse } from '../../../../api/types/dataImport';
|
||||
import type { ProductSuggestionResponse } from '../../../../api/types/inventory';
|
||||
import { useAuth } from '../../../../contexts/AuthContext';
|
||||
import { BatchAddIngredientsModal } from '../../inventory/BatchAddIngredientsModal';
|
||||
|
||||
interface UploadSalesDataStepProps {
|
||||
onNext: () => void;
|
||||
@@ -64,6 +65,7 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
|
||||
// Form state for adding/editing
|
||||
const [isAdding, setIsAdding] = useState(false);
|
||||
const [editingId, setEditingId] = useState<string | null>(null);
|
||||
const [showBatchModal, setShowBatchModal] = useState(false);
|
||||
const [formData, setFormData] = useState<InventoryItemForm>({
|
||||
id: '',
|
||||
name: '',
|
||||
@@ -720,20 +722,36 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
|
||||
</form>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsAdding(true)}
|
||||
className="w-full p-4 border-2 border-dashed border-[var(--border-secondary)] rounded-lg hover:border-[var(--color-primary)] hover:bg-[var(--bg-secondary)] transition-colors group"
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2 text-[var(--text-secondary)] group-hover:text-[var(--color-primary)]">
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||
</svg>
|
||||
<span className="font-medium">
|
||||
Agregar Ingrediente Manualmente
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsAdding(true)}
|
||||
className="p-4 border-2 border-dashed border-[var(--border-secondary)] rounded-lg hover:border-[var(--color-primary)] hover:bg-[var(--bg-secondary)] transition-colors group"
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2 text-[var(--text-secondary)] group-hover:text-[var(--color-primary)]">
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||
</svg>
|
||||
<span className="font-medium">
|
||||
Agregar Uno
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowBatchModal(true)}
|
||||
className="p-4 border-2 border-dashed border-[var(--color-primary)]/30 rounded-lg hover:border-[var(--color-primary)] hover:bg-[var(--color-primary)]/5 transition-colors group"
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2 text-[var(--color-primary)]">
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
<span className="font-medium">
|
||||
Agregar Varios
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
@@ -787,6 +805,35 @@ export const UploadSalesDataStep: React.FC<UploadSalesDataStepProps> = ({
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Batch Add Modal */}
|
||||
<BatchAddIngredientsModal
|
||||
isOpen={showBatchModal}
|
||||
onClose={() => setShowBatchModal(false)}
|
||||
onCreated={(ingredients) => {
|
||||
// Add all created ingredients to the list
|
||||
const newItems: InventoryItemForm[] = ingredients.map(ing => ({
|
||||
id: ing.id,
|
||||
name: ing.name,
|
||||
product_type: ing.product_type,
|
||||
category: ing.category,
|
||||
unit_of_measure: ing.unit_of_measure,
|
||||
stock_quantity: 0,
|
||||
cost_per_unit: ing.average_cost || 0,
|
||||
estimated_shelf_life_days: ing.shelf_life_days || 30,
|
||||
requires_refrigeration: ing.requires_refrigeration || false,
|
||||
requires_freezing: ing.requires_freezing || false,
|
||||
is_seasonal: ing.is_seasonal || false,
|
||||
low_stock_threshold: ing.low_stock_threshold || 0,
|
||||
reorder_point: ing.reorder_point || 0,
|
||||
notes: ing.notes || '',
|
||||
isSuggested: false,
|
||||
}));
|
||||
setInventoryItems([...inventoryItems, ...newItems]);
|
||||
setShowBatchModal(false);
|
||||
}}
|
||||
tenantId={currentTenant?.id || ''}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user