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:
Claude
2025-11-06 19:55:42 +00:00
parent 2059c79fa6
commit 623d378faf
7 changed files with 74 additions and 50 deletions

View File

@@ -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>
);

View File

@@ -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>
);
};

View File

@@ -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>
);
};

View File

@@ -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>
);
};

View File

@@ -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>
);
};

View File

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

View File

@@ -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>
);
};