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 { AuthProvider } from './contexts/AuthContext';
|
||||||
import { SSEProvider } from './contexts/SSEContext';
|
import { SSEProvider } from './contexts/SSEContext';
|
||||||
import { SubscriptionEventsProvider } from './contexts/SubscriptionEventsContext';
|
import { SubscriptionEventsProvider } from './contexts/SubscriptionEventsContext';
|
||||||
|
import { TourProvider } from './contexts/TourContext';
|
||||||
import GlobalSubscriptionHandler from './components/auth/GlobalSubscriptionHandler';
|
import GlobalSubscriptionHandler from './components/auth/GlobalSubscriptionHandler';
|
||||||
import { CookieBanner } from './components/ui/CookieConsent';
|
import { CookieBanner } from './components/ui/CookieConsent';
|
||||||
|
import { Tour } from './components/ui/Tour';
|
||||||
import i18n from './i18n';
|
import i18n from './i18n';
|
||||||
|
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
@@ -65,7 +67,10 @@ function App() {
|
|||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<SSEProvider>
|
<SSEProvider>
|
||||||
<SubscriptionEventsProvider>
|
<SubscriptionEventsProvider>
|
||||||
<AppContent />
|
<TourProvider>
|
||||||
|
<AppContent />
|
||||||
|
<Tour />
|
||||||
|
</TourProvider>
|
||||||
</SubscriptionEventsProvider>
|
</SubscriptionEventsProvider>
|
||||||
</SSEProvider>
|
</SSEProvider>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import {
|
|||||||
DataSourceChoiceStep,
|
DataSourceChoiceStep,
|
||||||
RegisterTenantStep,
|
RegisterTenantStep,
|
||||||
UploadSalesDataStep,
|
UploadSalesDataStep,
|
||||||
|
ProductCategorizationStep,
|
||||||
|
InitialStockEntryStep,
|
||||||
ProductionProcessesStep,
|
ProductionProcessesStep,
|
||||||
MLTrainingStep,
|
MLTrainingStep,
|
||||||
CompletionStep
|
CompletionStep
|
||||||
@@ -89,6 +91,22 @@ const OnboardingWizardContent: React.FC = () => {
|
|||||||
isConditional: true,
|
isConditional: true,
|
||||||
condition: (ctx) => ctx.state.dataSource === 'ai-assisted',
|
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
|
// Phase 2b: Core Data Entry
|
||||||
{
|
{
|
||||||
id: 'suppliers-setup',
|
id: 'suppliers-setup',
|
||||||
@@ -322,6 +340,14 @@ const OnboardingWizardContent: React.FC = () => {
|
|||||||
wizardContext.updateAISuggestions(data.aiSuggestions);
|
wizardContext.updateAISuggestions(data.aiSuggestions);
|
||||||
wizardContext.setAIAnalysisComplete(true);
|
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') {
|
if (currentStep.id === 'inventory-setup') {
|
||||||
wizardContext.markStepComplete('inventoryCompleted');
|
wizardContext.markStepComplete('inventoryCompleted');
|
||||||
}
|
}
|
||||||
@@ -381,6 +407,12 @@ const OnboardingWizardContent: React.FC = () => {
|
|||||||
if (currentStep.id === 'data-source-choice' && data?.dataSource) {
|
if (currentStep.id === 'data-source-choice' && data?.dataSource) {
|
||||||
wizardContext.updateDataSource(data.dataSource as 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
|
// Show loading state
|
||||||
|
|||||||
@@ -23,8 +23,12 @@ export interface WizardState {
|
|||||||
uploadedFileSize?: number;
|
uploadedFileSize?: number;
|
||||||
aiSuggestions: AISuggestion[];
|
aiSuggestions: AISuggestion[];
|
||||||
aiAnalysisComplete: boolean;
|
aiAnalysisComplete: boolean;
|
||||||
|
categorizedProducts?: any[]; // Products with type classification
|
||||||
|
productsWithStock?: any[]; // Products with initial stock levels
|
||||||
|
|
||||||
// Setup Progress
|
// Setup Progress
|
||||||
|
categorizationCompleted: boolean;
|
||||||
|
stockEntryCompleted: boolean;
|
||||||
suppliersCompleted: boolean;
|
suppliersCompleted: boolean;
|
||||||
inventoryCompleted: boolean;
|
inventoryCompleted: boolean;
|
||||||
recipesCompleted: boolean;
|
recipesCompleted: boolean;
|
||||||
@@ -47,6 +51,8 @@ export interface WizardContextValue {
|
|||||||
updateDataSource: (source: DataSource) => void;
|
updateDataSource: (source: DataSource) => void;
|
||||||
updateAISuggestions: (suggestions: AISuggestion[]) => void;
|
updateAISuggestions: (suggestions: AISuggestion[]) => void;
|
||||||
setAIAnalysisComplete: (complete: boolean) => void;
|
setAIAnalysisComplete: (complete: boolean) => void;
|
||||||
|
updateCategorizedProducts: (products: any[]) => void;
|
||||||
|
updateProductsWithStock: (products: any[]) => void;
|
||||||
markStepComplete: (step: keyof WizardState) => void;
|
markStepComplete: (step: keyof WizardState) => void;
|
||||||
getVisibleSteps: () => string[];
|
getVisibleSteps: () => string[];
|
||||||
shouldShowStep: (stepId: string) => boolean;
|
shouldShowStep: (stepId: string) => boolean;
|
||||||
@@ -58,6 +64,10 @@ const initialState: WizardState = {
|
|||||||
dataSource: null,
|
dataSource: null,
|
||||||
aiSuggestions: [],
|
aiSuggestions: [],
|
||||||
aiAnalysisComplete: false,
|
aiAnalysisComplete: false,
|
||||||
|
categorizedProducts: undefined,
|
||||||
|
productsWithStock: undefined,
|
||||||
|
categorizationCompleted: false,
|
||||||
|
stockEntryCompleted: false,
|
||||||
suppliersCompleted: false,
|
suppliersCompleted: false,
|
||||||
inventoryCompleted: false,
|
inventoryCompleted: false,
|
||||||
recipesCompleted: false,
|
recipesCompleted: false,
|
||||||
@@ -121,6 +131,14 @@ export const WizardProvider: React.FC<WizardProviderProps> = ({
|
|||||||
setState(prev => ({ ...prev, aiAnalysisComplete: complete }));
|
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) => {
|
const markStepComplete = (step: keyof WizardState) => {
|
||||||
setState(prev => ({ ...prev, [step]: true }));
|
setState(prev => ({ ...prev, [step]: true }));
|
||||||
};
|
};
|
||||||
@@ -154,6 +172,11 @@ export const WizardProvider: React.FC<WizardProviderProps> = ({
|
|||||||
|
|
||||||
if (state.aiAnalysisComplete) {
|
if (state.aiAnalysisComplete) {
|
||||||
steps.push('review-suggestions');
|
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,
|
updateDataSource,
|
||||||
updateAISuggestions,
|
updateAISuggestions,
|
||||||
setAIAnalysisComplete,
|
setAIAnalysisComplete,
|
||||||
|
updateCategorizedProducts,
|
||||||
|
updateProductsWithStock,
|
||||||
markStepComplete,
|
markStepComplete,
|
||||||
getVisibleSteps,
|
getVisibleSteps,
|
||||||
shouldShowStep,
|
shouldShowStep,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { Button } from '../../ui';
|
|||||||
import { Badge } from '../../ui';
|
import { Badge } from '../../ui';
|
||||||
import { Tooltip } from '../../ui';
|
import { Tooltip } from '../../ui';
|
||||||
import { Avatar } from '../../ui';
|
import { Avatar } from '../../ui';
|
||||||
|
import { TourButton } from '../../ui/Tour/TourButton';
|
||||||
import {
|
import {
|
||||||
LayoutDashboard,
|
LayoutDashboard,
|
||||||
Package,
|
Package,
|
||||||
@@ -811,6 +812,13 @@ export const Sidebar = forwardRef<SidebarRef, SidebarProps>(({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Tour Button */}
|
||||||
|
{!isCollapsed && (
|
||||||
|
<div className="px-4 pb-2">
|
||||||
|
<TourButton variant="button" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Navigation */}
|
{/* Navigation */}
|
||||||
<nav className={clsx('flex-1 overflow-y-auto overflow-x-hidden', isCollapsed ? 'px-1 py-4' : 'p-4')}>
|
<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')}>
|
<ul className={clsx(isCollapsed ? 'space-y-1 flex flex-col items-center' : 'space-y-2')}>
|
||||||
|
|||||||
Reference in New Issue
Block a user