Architect navigation buttons correctly: move from wizard-level to step-level
Fixed the navigation architecture to follow proper onboarding patterns: **ARCHITECTURE CHANGE:** - REMOVED: External navigation footer from UnifiedOnboardingWizard (Back + Continue buttons at wizard level) - ADDED: Internal Continue buttons inside each setup wizard step component **WHY THIS MATTERS:** 1. Onboarding should NEVER show Back buttons (users cannot go back) 2. Each step should be self-contained with its own Continue button 3. Setup wizard steps are reused in both contexts: - SetupWizard (/app/setup): Uses external StepNavigation component - UnifiedOnboardingWizard: Steps now render their own buttons **CHANGES MADE:** 1. UnifiedOnboardingWizard.tsx: - Removed navigation footer (lines 548-588) - Now passes canContinue prop to steps - Steps are responsible for their own navigation 2. All setup wizard steps updated: - QualitySetupStep: Added onComplete, canContinue props + Continue button - SuppliersSetupStep: Modified existing button to call onComplete - InventorySetupStep: Added onComplete, canContinue props + Continue button - RecipesSetupStep: Added canContinue prop + Continue button - TeamSetupStep: Added onComplete, canContinue props + Continue button - ReviewSetupStep: Added onComplete, canContinue props + Continue button 3. Continue button pattern: - Only renders when onComplete prop exists (onboarding context) - Disabled based on canContinue prop from parent - Styled consistently across all steps - Positioned at bottom with border-top separator **RESULT:** - Clean separation: onboarding steps have internal buttons, no external navigation - No Back button in onboarding (as required) - Setup wizard still works with external StepNavigation - Consistent UX across all steps
This commit is contained in:
@@ -542,50 +542,9 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
onUpdate={handleStepUpdate}
|
||||
isFirstStep={currentStepIndex === 0}
|
||||
isLastStep={currentStepIndex === VISIBLE_STEPS.length - 1}
|
||||
canContinue={canContinue}
|
||||
/>
|
||||
</CardBody>
|
||||
|
||||
{/* Navigation Footer - Only for setup wizard steps that don't render their own buttons */}
|
||||
{['suppliers-setup', 'inventory-setup', 'recipes-setup', 'quality-setup', 'team-setup', 'setup-review'].includes(currentStep.id) && (
|
||||
<div className="border-t border-[var(--border-primary)] px-6 py-4 bg-[var(--bg-secondary)]/30">
|
||||
<div className="flex flex-col sm:flex-row items-center justify-between gap-3">
|
||||
{/* Left side - Back button */}
|
||||
<div className="w-full sm:w-auto">
|
||||
{currentStepIndex > 0 && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => setCurrentStepIndex(currentStepIndex - 1)}
|
||||
disabled={markStepCompleted.isPending}
|
||||
className="w-full sm:w-auto"
|
||||
>
|
||||
← {t('onboarding:wizard.navigation.back', 'Atrás')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Right side - Continue button */}
|
||||
<div className="w-full sm:w-auto">
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={handleStepComplete}
|
||||
disabled={!canContinue || markStepCompleted.isPending}
|
||||
className="w-full sm:w-auto min-w-[200px]"
|
||||
>
|
||||
{markStepCompleted.isPending ? (
|
||||
<span className="flex items-center gap-2">
|
||||
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||
{t('common:saving', 'Guardando...')}
|
||||
</span>
|
||||
) : currentStepIndex === VISIBLE_STEPS.length - 1 ? (
|
||||
t('onboarding:wizard.navigation.finish', 'Finalizar →')
|
||||
) : (
|
||||
t('onboarding:wizard.navigation.next', 'Continuar →')
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@ import { UnitOfMeasure, IngredientCategory } from '../../../../api/types/invento
|
||||
import type { IngredientCreate, IngredientUpdate } from '../../../../api/types/inventory';
|
||||
import { ESSENTIAL_INGREDIENTS, COMMON_INGREDIENTS, PACKAGING_ITEMS, type IngredientTemplate, templateToIngredientCreate } from '../data/ingredientTemplates';
|
||||
|
||||
export const InventorySetupStep: React.FC<SetupStepProps> = ({ onUpdate }) => {
|
||||
export const InventorySetupStep: React.FC<SetupStepProps> = ({ onUpdate, onComplete, canContinue }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Get tenant ID
|
||||
@@ -669,6 +669,19 @@ export const InventorySetupStep: React.FC<SetupStepProps> = ({ onUpdate }) => {
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Continue button - only shown when used in onboarding context */}
|
||||
{onComplete && (
|
||||
<div className="flex justify-end mt-6 pt-6 border-t border-[var(--border-secondary)]">
|
||||
<button
|
||||
onClick={onComplete}
|
||||
disabled={canContinue === false}
|
||||
className="px-6 py-3 bg-[var(--color-primary)] text-white rounded-lg hover:bg-[var(--color-primary-dark)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium"
|
||||
>
|
||||
{t('setup_wizard:navigation.continue', 'Continue →')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useAuthUser } from '../../../../stores/auth.store';
|
||||
import { QualityCheckType, ProcessStage } from '../../../../api/types/qualityTemplates';
|
||||
import type { QualityCheckTemplateCreate } from '../../../../api/types/qualityTemplates';
|
||||
|
||||
export const QualitySetupStep: React.FC<SetupStepProps> = ({ onUpdate }) => {
|
||||
export const QualitySetupStep: React.FC<SetupStepProps> = ({ onUpdate, onComplete, canContinue }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Get tenant ID and user
|
||||
@@ -416,6 +416,19 @@ export const QualitySetupStep: React.FC<SetupStepProps> = ({ onUpdate }) => {
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Continue button - only shown when used in onboarding context */}
|
||||
{onComplete && (
|
||||
<div className="flex justify-end mt-6 pt-6 border-t border-[var(--border-secondary)]">
|
||||
<button
|
||||
onClick={onComplete}
|
||||
disabled={canContinue === false}
|
||||
className="px-6 py-3 bg-[var(--color-primary)] text-white rounded-lg hover:bg-[var(--color-primary-dark)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium"
|
||||
>
|
||||
{t('setup_wizard:navigation.continue', 'Continue →')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ interface RecipeIngredientForm {
|
||||
ingredient_order: number;
|
||||
}
|
||||
|
||||
export const RecipesSetupStep: React.FC<SetupStepProps> = ({ onUpdate, onComplete }) => {
|
||||
export const RecipesSetupStep: React.FC<SetupStepProps> = ({ onUpdate, onComplete, canContinue }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Get tenant ID
|
||||
@@ -793,6 +793,19 @@ export const RecipesSetupStep: React.FC<SetupStepProps> = ({ onUpdate, onComplet
|
||||
tenantId={tenantId}
|
||||
context="recipe"
|
||||
/>
|
||||
|
||||
{/* Continue button - only shown when used in onboarding context */}
|
||||
{onComplete && (
|
||||
<div className="flex justify-end mt-6 pt-6 border-[var(--border-secondary)]">
|
||||
<button
|
||||
onClick={onComplete}
|
||||
disabled={canContinue === false}
|
||||
className="px-6 py-3 bg-[var(--color-primary)] text-white rounded-lg hover:bg-[var(--color-primary-dark)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium"
|
||||
>
|
||||
{t('setup_wizard:navigation.continue', 'Continue →')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useQualityTemplates } from '../../../../api/hooks/qualityTemplates';
|
||||
import { useCurrentTenant } from '../../../../stores/tenant.store';
|
||||
import { useAuthUser } from '../../../../stores/auth.store';
|
||||
|
||||
export const ReviewSetupStep: React.FC<SetupStepProps> = ({ onUpdate }) => {
|
||||
export const ReviewSetupStep: React.FC<SetupStepProps> = ({ onUpdate, onComplete, canContinue }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Get tenant ID
|
||||
@@ -306,6 +306,19 @@ export const ReviewSetupStep: React.FC<SetupStepProps> = ({ onUpdate }) => {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Continue button - only shown when used in onboarding context */}
|
||||
{onComplete && (
|
||||
<div className="flex justify-end mt-6 pt-6 border-t border-[var(--border-secondary)]">
|
||||
<button
|
||||
onClick={onComplete}
|
||||
disabled={canContinue === false}
|
||||
className="px-6 py-3 bg-[var(--color-primary)] text-white rounded-lg hover:bg-[var(--color-primary-dark)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium"
|
||||
>
|
||||
{t('setup_wizard:navigation.continue', 'Continue →')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@ export const SuppliersSetupStep: React.FC<SetupStepProps> = ({
|
||||
onComplete,
|
||||
onSkip,
|
||||
onUpdate,
|
||||
canContinue,
|
||||
isFirstStep,
|
||||
isLastStep
|
||||
}) => {
|
||||
@@ -471,12 +472,11 @@ export const SuppliersSetupStep: React.FC<SetupStepProps> = ({
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={isLastStep ? () => onComplete?.() : onNext}
|
||||
onClick={() => onComplete?.()}
|
||||
disabled={!canContinue}
|
||||
className="px-6 py-2 bg-[var(--color-primary)] text-white rounded-lg hover:bg-[var(--color-primary-dark)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium flex items-center gap-2"
|
||||
>
|
||||
{isLastStep ? t('common:complete', 'Complete') : t('common:next', 'Next')}
|
||||
→
|
||||
{t('setup_wizard:navigation.continue', 'Continue →')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ interface TeamMember {
|
||||
role: string;
|
||||
}
|
||||
|
||||
export const TeamSetupStep: React.FC<SetupStepProps> = ({ onUpdate }) => {
|
||||
export const TeamSetupStep: React.FC<SetupStepProps> = ({ onUpdate, onComplete, canContinue }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Local state for team members (will be sent to backend when API is available)
|
||||
@@ -310,6 +310,19 @@ export const TeamSetupStep: React.FC<SetupStepProps> = ({ onUpdate }) => {
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Continue button - only shown when used in onboarding context */}
|
||||
{onComplete && (
|
||||
<div className="flex justify-end mt-6 pt-6 border-t border-[var(--border-secondary)]">
|
||||
<button
|
||||
onClick={onComplete}
|
||||
disabled={canContinue === false}
|
||||
className="px-6 py-3 bg-[var(--color-primary)] text-white rounded-lg hover:bg-[var(--color-primary-dark)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium"
|
||||
>
|
||||
{t('setup_wizard:navigation.continue', 'Continue →')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user