Integrate categorization & stock steps with Tour system
- Updated WizardContext with new state management: - Added categorizedProducts and productsWithStock state - Added categorizationCompleted and stockEntryCompleted flags - Implemented updateCategorizedProducts() and updateProductsWithStock() methods - Updated getVisibleSteps() to include new steps in AI-assisted path - Integrated ProductCategorizationStep and InitialStockEntryStep into UnifiedOnboardingWizard: - Added conditional rendering based on AI analysis completion - Wired up state management for both steps - Added intermediate update handlers - Integrated Tour system at app level: - Added TourProvider to App.tsx context hierarchy - Added Tour component for rendering active tours - Added TourButton to Sidebar navigation - Tour button visible when sidebar is expanded This completes the Phase 6.5 integration and sets up the guided tour infrastructure.
This commit is contained in:
@@ -11,8 +11,10 @@ import { ThemeProvider } from './contexts/ThemeContext';
|
||||
import { AuthProvider } from './contexts/AuthContext';
|
||||
import { SSEProvider } from './contexts/SSEContext';
|
||||
import { SubscriptionEventsProvider } from './contexts/SubscriptionEventsContext';
|
||||
import { TourProvider } from './contexts/TourContext';
|
||||
import GlobalSubscriptionHandler from './components/auth/GlobalSubscriptionHandler';
|
||||
import { CookieBanner } from './components/ui/CookieConsent';
|
||||
import { Tour } from './components/ui/Tour';
|
||||
import i18n from './i18n';
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
@@ -65,7 +67,10 @@ function App() {
|
||||
<AuthProvider>
|
||||
<SSEProvider>
|
||||
<SubscriptionEventsProvider>
|
||||
<AppContent />
|
||||
<TourProvider>
|
||||
<AppContent />
|
||||
<Tour />
|
||||
</TourProvider>
|
||||
</SubscriptionEventsProvider>
|
||||
</SSEProvider>
|
||||
</AuthProvider>
|
||||
|
||||
@@ -13,6 +13,8 @@ import {
|
||||
DataSourceChoiceStep,
|
||||
RegisterTenantStep,
|
||||
UploadSalesDataStep,
|
||||
ProductCategorizationStep,
|
||||
InitialStockEntryStep,
|
||||
ProductionProcessesStep,
|
||||
MLTrainingStep,
|
||||
CompletionStep
|
||||
@@ -89,6 +91,22 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
isConditional: true,
|
||||
condition: (ctx) => ctx.state.dataSource === 'ai-assisted',
|
||||
},
|
||||
{
|
||||
id: 'product-categorization',
|
||||
title: t('onboarding:steps.categorization.title', 'Categorizar Productos'),
|
||||
description: t('onboarding:steps.categorization.description', 'Clasifica ingredientes vs productos'),
|
||||
component: ProductCategorizationStep,
|
||||
isConditional: true,
|
||||
condition: (ctx) => ctx.state.dataSource === 'ai-assisted' && ctx.state.aiAnalysisComplete,
|
||||
},
|
||||
{
|
||||
id: 'initial-stock-entry',
|
||||
title: t('onboarding:steps.stock.title', 'Niveles de Stock'),
|
||||
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
|
||||
{
|
||||
id: 'suppliers-setup',
|
||||
@@ -322,6 +340,14 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
wizardContext.updateAISuggestions(data.aiSuggestions);
|
||||
wizardContext.setAIAnalysisComplete(true);
|
||||
}
|
||||
if (currentStep.id === 'product-categorization' && data?.categorizedProducts) {
|
||||
wizardContext.updateCategorizedProducts(data.categorizedProducts);
|
||||
wizardContext.markStepComplete('categorizationCompleted');
|
||||
}
|
||||
if (currentStep.id === 'initial-stock-entry' && data?.productsWithStock) {
|
||||
wizardContext.updateProductsWithStock(data.productsWithStock);
|
||||
wizardContext.markStepComplete('stockEntryCompleted');
|
||||
}
|
||||
if (currentStep.id === 'inventory-setup') {
|
||||
wizardContext.markStepComplete('inventoryCompleted');
|
||||
}
|
||||
@@ -381,6 +407,12 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
if (currentStep.id === 'data-source-choice' && data?.dataSource) {
|
||||
wizardContext.updateDataSource(data.dataSource as DataSource);
|
||||
}
|
||||
if (currentStep.id === 'product-categorization' && data?.categorizedProducts) {
|
||||
wizardContext.updateCategorizedProducts(data.categorizedProducts);
|
||||
}
|
||||
if (currentStep.id === 'initial-stock-entry' && data?.productsWithStock) {
|
||||
wizardContext.updateProductsWithStock(data.productsWithStock);
|
||||
}
|
||||
};
|
||||
|
||||
// Show loading state
|
||||
|
||||
@@ -23,8 +23,12 @@ export interface WizardState {
|
||||
uploadedFileSize?: number;
|
||||
aiSuggestions: AISuggestion[];
|
||||
aiAnalysisComplete: boolean;
|
||||
categorizedProducts?: any[]; // Products with type classification
|
||||
productsWithStock?: any[]; // Products with initial stock levels
|
||||
|
||||
// Setup Progress
|
||||
categorizationCompleted: boolean;
|
||||
stockEntryCompleted: boolean;
|
||||
suppliersCompleted: boolean;
|
||||
inventoryCompleted: boolean;
|
||||
recipesCompleted: boolean;
|
||||
@@ -47,6 +51,8 @@ export interface WizardContextValue {
|
||||
updateDataSource: (source: DataSource) => void;
|
||||
updateAISuggestions: (suggestions: AISuggestion[]) => void;
|
||||
setAIAnalysisComplete: (complete: boolean) => void;
|
||||
updateCategorizedProducts: (products: any[]) => void;
|
||||
updateProductsWithStock: (products: any[]) => void;
|
||||
markStepComplete: (step: keyof WizardState) => void;
|
||||
getVisibleSteps: () => string[];
|
||||
shouldShowStep: (stepId: string) => boolean;
|
||||
@@ -58,6 +64,10 @@ const initialState: WizardState = {
|
||||
dataSource: null,
|
||||
aiSuggestions: [],
|
||||
aiAnalysisComplete: false,
|
||||
categorizedProducts: undefined,
|
||||
productsWithStock: undefined,
|
||||
categorizationCompleted: false,
|
||||
stockEntryCompleted: false,
|
||||
suppliersCompleted: false,
|
||||
inventoryCompleted: false,
|
||||
recipesCompleted: false,
|
||||
@@ -121,6 +131,14 @@ export const WizardProvider: React.FC<WizardProviderProps> = ({
|
||||
setState(prev => ({ ...prev, aiAnalysisComplete: complete }));
|
||||
};
|
||||
|
||||
const updateCategorizedProducts = (products: any[]) => {
|
||||
setState(prev => ({ ...prev, categorizedProducts: products }));
|
||||
};
|
||||
|
||||
const updateProductsWithStock = (products: any[]) => {
|
||||
setState(prev => ({ ...prev, productsWithStock: products }));
|
||||
};
|
||||
|
||||
const markStepComplete = (step: keyof WizardState) => {
|
||||
setState(prev => ({ ...prev, [step]: true }));
|
||||
};
|
||||
@@ -154,6 +172,11 @@ export const WizardProvider: React.FC<WizardProviderProps> = ({
|
||||
|
||||
if (state.aiAnalysisComplete) {
|
||||
steps.push('review-suggestions');
|
||||
steps.push('product-categorization');
|
||||
}
|
||||
|
||||
if (state.categorizationCompleted) {
|
||||
steps.push('initial-stock-entry');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,6 +233,8 @@ export const WizardProvider: React.FC<WizardProviderProps> = ({
|
||||
updateDataSource,
|
||||
updateAISuggestions,
|
||||
setAIAnalysisComplete,
|
||||
updateCategorizedProducts,
|
||||
updateProductsWithStock,
|
||||
markStepComplete,
|
||||
getVisibleSteps,
|
||||
shouldShowStep,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Button } from '../../ui';
|
||||
import { Badge } from '../../ui';
|
||||
import { Tooltip } from '../../ui';
|
||||
import { Avatar } from '../../ui';
|
||||
import { TourButton } from '../../ui/Tour/TourButton';
|
||||
import {
|
||||
LayoutDashboard,
|
||||
Package,
|
||||
@@ -811,6 +812,13 @@ export const Sidebar = forwardRef<SidebarRef, SidebarProps>(({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tour Button */}
|
||||
{!isCollapsed && (
|
||||
<div className="px-4 pb-2">
|
||||
<TourButton variant="button" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className={clsx('flex-1 overflow-y-auto overflow-x-hidden', isCollapsed ? 'px-1 py-4' : 'p-4')}>
|
||||
<ul className={clsx(isCollapsed ? 'space-y-1 flex flex-col items-center' : 'space-y-2')}>
|
||||
|
||||
Reference in New Issue
Block a user