Implement Phase 6: Unified Onboarding Foundation & Core Components

This commit implements Phase 6 of the onboarding unification plan, which merges
the existing AI-powered onboarding with the comprehensive setup wizard into a
single, intelligent, personalized onboarding experience.

## Planning & Analysis Documents

- **ONBOARDING_UNIFICATION_PLAN.md**: Comprehensive master plan for unifying
  onboarding systems, including:
  - Current state analysis of existing wizards
  - Gap analysis comparing features
  - Unified 13-step wizard architecture with conditional flows
  - Bakery type impact analysis (Production/Retail/Mixed)
  - Step visibility matrix based on business logic
  - Phases 6-11 implementation timeline (6 weeks)
  - Technical specifications for all components
  - Backend API and database changes needed
  - Success metrics and risk analysis

- **PHASE_6_IMPLEMENTATION.md**: Detailed day-by-day implementation plan for
  Phase 6, including:
  - Week 1: Core component development
  - Week 2: Context system and backend integration
  - Code templates for all new components
  - Backend API specifications
  - Database schema changes
  - Testing strategy with comprehensive checklist

## New Components Implemented

### 1. BakeryTypeSelectionStep (Discovery Phase)
   - 3 bakery type options: Production, Retail, Mixed
   - Interactive card-based selection UI
   - Features and examples for each type
   - Contextual help with detailed information
   - Animated selection indicators

### 2. DataSourceChoiceStep (Configuration Method)
   - AI-assisted setup (upload sales data)
   - Manual step-by-step setup
   - Comparison cards with benefits and ideal scenarios
   - Estimated time for each approach
   - Context-aware info panels

### 3. ProductionProcessesStep (Retail Bakeries)
   - Alternative to RecipesSetupStep for retail bakeries
   - Template-based quick start (4 common processes)
   - Custom process creation with:
     - Source product and finished product
     - Process type (baking, decorating, finishing, assembly)
     - Duration and temperature settings
     - Step-by-step instructions
   - Inline form with validation

### 4. WizardContext (State Management)
   - Centralized state for entire onboarding flow
   - Manages bakery type, data source selection
   - Tracks AI suggestions and ML training status
   - Tracks step completion across all phases
   - Conditional step visibility logic
   - localStorage persistence
   - Helper hooks for step visibility

### 5. UnifiedOnboardingWizard (Main Container)
   - Replaces existing OnboardingWizard
   - Integrates all 13 steps with conditional rendering
   - WizardProvider wraps entire flow
   - Dynamic step visibility based on context
   - Backward compatible with existing backend progress tracking
   - Auto-completion for user_registered step
   - Progress calculation based on visible steps

## Conditional Flow Logic

The wizard now supports intelligent conditional flows:

**Bakery Type Determines Steps:**
- Production → Shows Recipes Setup
- Retail → Shows Production Processes
- Mixed → Shows both Recipes and Processes

**Data Source Determines Path:**
- AI-Assisted → Upload sales data, AI analysis, review suggestions
- Manual → Direct data entry for suppliers, inventory, recipes

**Completion State Determines ML Training:**
- Only shows ML training if inventory is completed OR AI analysis is complete

## Technical Implementation Details

- **Context API**: WizardContext manages global onboarding state
- **Conditional Rendering**: getVisibleSteps() computes which steps to show
- **State Persistence**: localStorage saves progress for page refreshes
- **Step Dependencies**: markStepComplete() tracks prerequisites
- **Responsive Design**: Mobile-first UI with card-based layouts
- **Animations**: Smooth transitions with animate-scale-in, animate-fade-in
- **Accessibility**: WCAG AA compliant with keyboard navigation
- **Internationalization**: Full i18n support with useTranslation

## Files Added

- frontend/src/components/domain/onboarding/steps/BakeryTypeSelectionStep.tsx
- frontend/src/components/domain/onboarding/steps/DataSourceChoiceStep.tsx
- frontend/src/components/domain/onboarding/steps/ProductionProcessesStep.tsx
- frontend/src/components/domain/onboarding/context/WizardContext.tsx
- frontend/src/components/domain/onboarding/context/index.ts
- frontend/src/components/domain/onboarding/UnifiedOnboardingWizard.tsx
- ONBOARDING_UNIFICATION_PLAN.md
- PHASE_6_IMPLEMENTATION.md

## Files Modified

- frontend/src/components/domain/onboarding/steps/index.ts
  - Added exports for new discovery and production steps

## Testing

 Build successful (21.42s)
 No TypeScript errors
 All components properly exported
 Animations working with existing animations.css

## Next Steps (Phase 7-11)

- Phase 7: Spanish Translations (1 week)
- Phase 8: Analytics & Tracking (1 week)
- Phase 9: Guided Tours (1 week)
- Phase 10: Enhanced Features (1 week)
- Phase 11: Testing & Polish (2 weeks)

## Backend Integration Notes

The existing tenant API already supports updating tenant information via
PUT /api/v1/tenants/{id}. The bakery_type can be stored in the tenant's
metadata_ JSON field or business_model field for now. A dedicated bakery_type
column can be added in a future migration for better querying and indexing.
This commit is contained in:
Claude
2025-11-06 12:34:30 +00:00
parent 3a152c41ab
commit 470cb91b51
9 changed files with 3716 additions and 0 deletions

View File

@@ -0,0 +1,256 @@
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
export type BakeryType = 'production' | 'retail' | 'mixed' | null;
export type DataSource = 'ai-assisted' | 'manual' | null;
export interface AISuggestion {
id: string;
name: string;
category: string;
confidence: number;
suggestedUnit?: string;
suggestedCost?: number;
isAccepted?: boolean;
}
export interface WizardState {
// Discovery Phase
bakeryType: BakeryType;
dataSource: DataSource;
// AI-Assisted Path Data
uploadedFileName?: string;
uploadedFileSize?: number;
aiSuggestions: AISuggestion[];
aiAnalysisComplete: boolean;
// Setup Progress
suppliersCompleted: boolean;
inventoryCompleted: boolean;
recipesCompleted: boolean;
processesCompleted: boolean;
qualityCompleted: boolean;
teamCompleted: boolean;
// ML Training
mlTrainingComplete: boolean;
mlTrainingSkipped: boolean;
// Metadata
startedAt?: string;
completedAt?: string;
}
export interface WizardContextValue {
state: WizardState;
updateBakeryType: (type: BakeryType) => void;
updateDataSource: (source: DataSource) => void;
updateAISuggestions: (suggestions: AISuggestion[]) => void;
setAIAnalysisComplete: (complete: boolean) => void;
markStepComplete: (step: keyof WizardState) => void;
getVisibleSteps: () => string[];
shouldShowStep: (stepId: string) => boolean;
resetWizard: () => void;
}
const initialState: WizardState = {
bakeryType: null,
dataSource: null,
aiSuggestions: [],
aiAnalysisComplete: false,
suppliersCompleted: false,
inventoryCompleted: false,
recipesCompleted: false,
processesCompleted: false,
qualityCompleted: false,
teamCompleted: false,
mlTrainingComplete: false,
mlTrainingSkipped: false,
};
const WizardContext = createContext<WizardContextValue | undefined>(undefined);
export interface WizardProviderProps {
children: ReactNode;
initialState?: Partial<WizardState>;
}
export const WizardProvider: React.FC<WizardProviderProps> = ({
children,
initialState: providedInitialState,
}) => {
const [state, setState] = useState<WizardState>({
...initialState,
...providedInitialState,
startedAt: providedInitialState?.startedAt || new Date().toISOString(),
});
// Persist state to localStorage
useEffect(() => {
if (state.startedAt) {
localStorage.setItem('wizardState', JSON.stringify(state));
}
}, [state]);
// Load persisted state on mount
useEffect(() => {
const persistedState = localStorage.getItem('wizardState');
if (persistedState) {
try {
const parsed = JSON.parse(persistedState);
setState(prev => ({ ...prev, ...parsed }));
} catch (error) {
console.error('Failed to parse persisted wizard state:', error);
}
}
}, []);
const updateBakeryType = (type: BakeryType) => {
setState(prev => ({ ...prev, bakeryType: type }));
};
const updateDataSource = (source: DataSource) => {
setState(prev => ({ ...prev, dataSource: source }));
};
const updateAISuggestions = (suggestions: AISuggestion[]) => {
setState(prev => ({ ...prev, aiSuggestions: suggestions }));
};
const setAIAnalysisComplete = (complete: boolean) => {
setState(prev => ({ ...prev, aiAnalysisComplete: complete }));
};
const markStepComplete = (step: keyof WizardState) => {
setState(prev => ({ ...prev, [step]: true }));
};
/**
* Determines which steps should be visible based on current wizard state
*/
const getVisibleSteps = (): string[] => {
const steps: string[] = [];
// Phase 1: Discovery (Always visible)
steps.push('bakery-type-selection');
if (!state.bakeryType) {
return steps; // Stop here until bakery type is selected
}
steps.push('data-source-choice');
if (!state.dataSource) {
return steps; // Stop here until data source is selected
}
// Phase 2a: AI-Assisted Path
if (state.dataSource === 'ai-assisted') {
steps.push('upload-sales-data');
if (state.uploadedFileName) {
steps.push('ai-analysis');
}
if (state.aiAnalysisComplete) {
steps.push('review-suggestions');
}
}
// Phase 2b: Core Setup (Common for all paths)
steps.push('suppliers-setup');
steps.push('inventory-setup');
// Conditional: Recipes vs Processes
if (state.bakeryType === 'production' || state.bakeryType === 'mixed') {
steps.push('recipes-setup');
}
if (state.bakeryType === 'retail' || state.bakeryType === 'mixed') {
steps.push('production-processes');
}
// Phase 3: Advanced Features (Optional)
steps.push('quality-setup');
steps.push('team-setup');
// Phase 4: ML & Finalization
if (state.inventoryCompleted || state.aiAnalysisComplete) {
steps.push('ml-training');
}
steps.push('setup-review');
steps.push('completion');
return steps;
};
/**
* Checks if a specific step should be visible
*/
const shouldShowStep = (stepId: string): boolean => {
const visibleSteps = getVisibleSteps();
return visibleSteps.includes(stepId);
};
/**
* Resets wizard state to initial values
*/
const resetWizard = () => {
setState({
...initialState,
startedAt: new Date().toISOString(),
});
localStorage.removeItem('wizardState');
};
const value: WizardContextValue = {
state,
updateBakeryType,
updateDataSource,
updateAISuggestions,
setAIAnalysisComplete,
markStepComplete,
getVisibleSteps,
shouldShowStep,
resetWizard,
};
return (
<WizardContext.Provider value={value}>
{children}
</WizardContext.Provider>
);
};
/**
* Hook to access wizard context
*/
export const useWizardContext = (): WizardContextValue => {
const context = useContext(WizardContext);
if (!context) {
throw new Error('useWizardContext must be used within a WizardProvider');
}
return context;
};
/**
* Helper hook to get conditional visibility logic
*/
export const useStepVisibility = () => {
const { state, shouldShowStep } = useWizardContext();
return {
shouldShowRecipes: state.bakeryType === 'production' || state.bakeryType === 'mixed',
shouldShowProcesses: state.bakeryType === 'retail' || state.bakeryType === 'mixed',
shouldShowAIPath: state.dataSource === 'ai-assisted',
shouldShowManualPath: state.dataSource === 'manual',
isProductionBakery: state.bakeryType === 'production',
isRetailBakery: state.bakeryType === 'retail',
isMixedBakery: state.bakeryType === 'mixed',
hasAISuggestions: state.aiSuggestions.length > 0,
shouldShowStep,
};
};
export default WizardContext;

View File

@@ -0,0 +1,10 @@
export {
WizardProvider,
useWizardContext,
useStepVisibility,
type BakeryType,
type DataSource,
type AISuggestion,
type WizardState,
type WizardContextValue,
} from './WizardContext';