Remove manual path and add inventory lots UI to AI-assisted onboarding
## Architectural Changes **1. Remove Manual Entry Path** - Deleted data-source-choice step (DataSourceChoiceStep) - Removed manual inventory-setup step (InventorySetupStep) - Removed all manual path conditions from wizard flow - Set dataSource to 'ai-assisted' by default in WizardContext Files modified: - frontend/src/components/domain/onboarding/UnifiedOnboardingWizard.tsx:11-28,61-162 - frontend/src/components/domain/onboarding/context/WizardContext.tsx:64 **2. Add Inventory Lots UI to AI Inventory Step** Added full stock lot management with expiration tracking to UploadSalesDataStep: **Features Added:** - Inline stock lot entry form after each AI-suggested ingredient - Multi-lot support - add multiple lots per ingredient with different expiration dates - Fields: quantity*, expiration date, supplier, batch/lot number - Visual list of added lots with expiration dates - Delete individual lots before completing - Smart validation with expiration date warnings - FIFO help text - Auto-select supplier if only one exists **Technical Implementation:** - Added useAddStock and useSuppliers hooks (lines 5,7,102-103) - Added stock state management (lines 106-114) - Stock handler functions (lines 336-428): - handleAddStockClick - Opens stock form - handleCancelStock - Closes and resets form - validateStockForm - Validates quantity and expiration - handleSaveStockLot - Saves to local state, supports "Add Another Lot" - handleDeleteStockLot - Removes from list - Modified handleNext to create stock lots after ingredients (lines 490-533) - Added stock lots UI section in ingredient rendering (lines 679-830) **UI Flow:** 1. User uploads sales data 2. AI suggests ingredients 3. User reviews/edits ingredients 4. **NEW**: User can optionally add stock lots with expiration dates 5. Click "Next" creates both ingredients AND stock lots 6. FIFO tracking enabled from day one **Benefits:** - Addresses JTBD: waste prevention, expiration tracking from onboarding - Progressive disclosure - optional but encouraged - Maintains simplicity of AI-assisted path - Enables inventory best practices from the start Files modified: - frontend/src/components/domain/onboarding/steps/UploadSalesDataStep.tsx:1-12,90-114,335-533,679-830 **Build Status:** ✓ Successful in 20.78s
This commit is contained in:
@@ -10,7 +10,6 @@ import { useTenantInitializer } from '../../../stores/useTenantInitializer';
|
||||
import { WizardProvider, useWizardContext, BakeryType, DataSource } from './context';
|
||||
import {
|
||||
BakeryTypeSelectionStep,
|
||||
DataSourceChoiceStep,
|
||||
RegisterTenantStep,
|
||||
UploadSalesDataStep,
|
||||
ProductCategorizationStep,
|
||||
@@ -22,7 +21,6 @@ import {
|
||||
// Import setup wizard steps
|
||||
import {
|
||||
SuppliersSetupStep,
|
||||
InventorySetupStep,
|
||||
RecipesSetupStep,
|
||||
QualitySetupStep,
|
||||
TeamSetupStep,
|
||||
@@ -66,14 +64,6 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
description: t('onboarding:steps.bakery_type.description', 'Selecciona tu tipo de negocio'),
|
||||
component: BakeryTypeSelectionStep,
|
||||
},
|
||||
{
|
||||
id: 'data-source-choice',
|
||||
title: t('onboarding:steps.data_source.title', 'Método de Configuración'),
|
||||
description: t('onboarding:steps.data_source.description', 'Elige cómo configurar'),
|
||||
component: DataSourceChoiceStep,
|
||||
isConditional: true,
|
||||
condition: (ctx) => ctx.state.bakeryType !== null,
|
||||
},
|
||||
// Phase 2: Core Setup
|
||||
{
|
||||
id: 'setup',
|
||||
@@ -81,16 +71,16 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
description: t('onboarding:steps.setup.description', 'Información básica'),
|
||||
component: RegisterTenantStep,
|
||||
isConditional: true,
|
||||
condition: (ctx) => ctx.state.dataSource !== null,
|
||||
condition: (ctx) => ctx.state.bakeryType !== null,
|
||||
},
|
||||
// Phase 2a: AI-Assisted Path
|
||||
// Phase 2a: AI-Assisted Path (ONLY PATH NOW)
|
||||
{
|
||||
id: 'smart-inventory-setup',
|
||||
title: t('onboarding:steps.smart_inventory.title', 'Subir Datos de Ventas'),
|
||||
description: t('onboarding:steps.smart_inventory.description', 'Configuración con IA'),
|
||||
component: UploadSalesDataStep,
|
||||
isConditional: true,
|
||||
condition: (ctx) => ctx.state.dataSource === 'ai-assisted',
|
||||
condition: (ctx) => ctx.tenantId !== null,
|
||||
},
|
||||
{
|
||||
id: 'product-categorization',
|
||||
@@ -98,7 +88,7 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
description: t('onboarding:steps.categorization.description', 'Clasifica ingredientes vs productos'),
|
||||
component: ProductCategorizationStep,
|
||||
isConditional: true,
|
||||
condition: (ctx) => ctx.state.dataSource === 'ai-assisted' && ctx.state.aiAnalysisComplete,
|
||||
condition: (ctx) => ctx.state.aiAnalysisComplete,
|
||||
},
|
||||
{
|
||||
id: 'initial-stock-entry',
|
||||
@@ -106,19 +96,7 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
description: t('onboarding:steps.stock.description', 'Cantidades iniciales'),
|
||||
component: InitialStockEntryStep,
|
||||
isConditional: true,
|
||||
condition: (ctx) => ctx.state.dataSource === 'ai-assisted' && ctx.state.categorizationCompleted,
|
||||
},
|
||||
// Phase 2b: Core Data Entry (Manual Path)
|
||||
// IMPORTANT: Inventory must come BEFORE suppliers so suppliers can associate products
|
||||
{
|
||||
id: 'inventory-setup',
|
||||
title: t('onboarding:steps.inventory.title', 'Inventario'),
|
||||
description: t('onboarding:steps.inventory.description', 'Productos e ingredientes'),
|
||||
component: InventorySetupStep,
|
||||
isConditional: true,
|
||||
condition: (ctx) =>
|
||||
// Only show for manual path (AI path creates inventory earlier)
|
||||
ctx.state.dataSource === 'manual',
|
||||
condition: (ctx) => ctx.state.categorizationCompleted,
|
||||
},
|
||||
{
|
||||
id: 'suppliers-setup',
|
||||
@@ -126,10 +104,7 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
description: t('onboarding:steps.suppliers.description', 'Configura tus proveedores'),
|
||||
component: SuppliersSetupStep,
|
||||
isConditional: true,
|
||||
condition: (ctx) =>
|
||||
// Show after inventory exists (either from AI or manual path)
|
||||
(ctx.state.dataSource === 'ai-assisted' && ctx.state.stockEntryCompleted) ||
|
||||
(ctx.state.dataSource === 'manual' && ctx.state.inventoryCompleted),
|
||||
condition: (ctx) => ctx.state.stockEntryCompleted,
|
||||
},
|
||||
{
|
||||
id: 'recipes-setup',
|
||||
@@ -156,7 +131,7 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
description: t('onboarding:steps.quality.description', 'Estándares de calidad'),
|
||||
component: QualitySetupStep,
|
||||
isConditional: true,
|
||||
condition: (ctx) => ctx.state.dataSource !== null,
|
||||
condition: (ctx) => ctx.tenantId !== null,
|
||||
},
|
||||
{
|
||||
id: 'team-setup',
|
||||
@@ -164,7 +139,7 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
description: t('onboarding:steps.team.description', 'Miembros del equipo'),
|
||||
component: TeamSetupStep,
|
||||
isConditional: true,
|
||||
condition: (ctx) => ctx.state.dataSource !== null,
|
||||
condition: (ctx) => ctx.tenantId !== null,
|
||||
},
|
||||
// Phase 4: ML & Finalization
|
||||
{
|
||||
@@ -173,7 +148,7 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
description: t('onboarding:steps.ml_training.description', 'Modelo personalizado'),
|
||||
component: MLTrainingStep,
|
||||
isConditional: true,
|
||||
condition: (ctx) => ctx.state.inventoryCompleted || ctx.state.aiAnalysisComplete,
|
||||
condition: (ctx) => ctx.state.aiAnalysisComplete,
|
||||
},
|
||||
{
|
||||
id: 'setup-review',
|
||||
@@ -181,7 +156,7 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
description: t('onboarding:steps.review.description', 'Confirma tu configuración'),
|
||||
component: ReviewSetupStep,
|
||||
isConditional: true,
|
||||
condition: (ctx) => ctx.state.dataSource !== null,
|
||||
condition: (ctx) => ctx.tenantId !== null,
|
||||
},
|
||||
{
|
||||
id: 'completion',
|
||||
|
||||
Reference in New Issue
Block a user