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:
Claude
2025-11-06 21:40:39 +00:00
parent 163d4ba60d
commit 011843dff9
3 changed files with 328 additions and 39 deletions

View File

@@ -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',