151 lines
5.1 KiB
TypeScript
151 lines
5.1 KiB
TypeScript
/**
|
|
* @ai-summary Mobile onboarding screen with multi-step wizard
|
|
*/
|
|
|
|
import React, { useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { MobileContainer } from '../../../shared-minimal/components/mobile/MobileContainer';
|
|
import { GlassCard } from '../../../shared-minimal/components/mobile/GlassCard';
|
|
import { useSavePreferences, useCompleteOnboarding } from '../hooks/useOnboarding';
|
|
import { PreferencesStep } from '../components/PreferencesStep';
|
|
import { AddVehicleStep } from '../components/AddVehicleStep';
|
|
import { CompleteStep } from '../components/CompleteStep';
|
|
import { OnboardingStep, OnboardingPreferences } from '../types/onboarding.types';
|
|
import { CreateVehicleRequest } from '../../vehicles/types/vehicles.types';
|
|
import { vehiclesApi } from '../../vehicles/api/vehicles.api';
|
|
import toast from 'react-hot-toast';
|
|
|
|
export const OnboardingMobileScreen: React.FC = () => {
|
|
const navigate = useNavigate();
|
|
const [currentStep, setCurrentStep] = useState<OnboardingStep>('preferences');
|
|
const savePreferences = useSavePreferences();
|
|
const completeOnboarding = useCompleteOnboarding();
|
|
const [isAddingVehicle, setIsAddingVehicle] = useState(false);
|
|
|
|
const stepNumbers: Record<OnboardingStep, number> = {
|
|
preferences: 1,
|
|
vehicle: 2,
|
|
complete: 3,
|
|
};
|
|
|
|
const handleSavePreferences = async (data: OnboardingPreferences) => {
|
|
try {
|
|
await savePreferences.mutateAsync(data);
|
|
setCurrentStep('vehicle');
|
|
} catch (error) {
|
|
// Error is handled by the mutation hook
|
|
}
|
|
};
|
|
|
|
const handleAddVehicle = async (data: CreateVehicleRequest) => {
|
|
setIsAddingVehicle(true);
|
|
try {
|
|
await vehiclesApi.create(data);
|
|
toast.success('Vehicle added successfully');
|
|
setCurrentStep('complete');
|
|
} catch (error: any) {
|
|
toast.error(error.response?.data?.error || 'Failed to add vehicle');
|
|
} finally {
|
|
setIsAddingVehicle(false);
|
|
}
|
|
};
|
|
|
|
const handleSkipVehicle = () => {
|
|
setCurrentStep('complete');
|
|
};
|
|
|
|
const handleComplete = async () => {
|
|
try {
|
|
await completeOnboarding.mutateAsync();
|
|
navigate('/garage');
|
|
} catch (error) {
|
|
// Error is handled by the mutation hook
|
|
}
|
|
};
|
|
|
|
const handleBack = () => {
|
|
if (currentStep === 'vehicle') {
|
|
setCurrentStep('preferences');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<MobileContainer>
|
|
<div className="flex-1 overflow-y-auto p-4 space-y-6">
|
|
{/* Header */}
|
|
<div className="text-center pt-4">
|
|
<h1 className="text-2xl font-bold text-slate-800 dark:text-white mb-2">Welcome to MotoVault Pro</h1>
|
|
<p className="text-slate-600 dark:text-gray-300 text-sm">Let's set up your account</p>
|
|
</div>
|
|
|
|
{/* Progress Indicator */}
|
|
<div className="flex items-center justify-between px-4">
|
|
{(['preferences', 'vehicle', 'complete'] as OnboardingStep[]).map((step, index) => (
|
|
<React.Fragment key={step}>
|
|
<div className="flex flex-col items-center">
|
|
<div
|
|
className={`w-10 h-10 rounded-full flex items-center justify-center font-semibold text-sm transition-all ${
|
|
stepNumbers[currentStep] >= stepNumbers[step]
|
|
? 'bg-primary-600 text-white dark:bg-primary-700 dark:text-white'
|
|
: 'bg-gray-200 text-gray-500 dark:bg-gray-700 dark:text-gray-400'
|
|
}`}
|
|
>
|
|
{stepNumbers[step]}
|
|
</div>
|
|
<span
|
|
className={`text-xs mt-1 font-medium ${
|
|
stepNumbers[currentStep] >= stepNumbers[step]
|
|
? 'text-primary-600 dark:text-primary-400'
|
|
: 'text-gray-500 dark:text-gray-400'
|
|
}`}
|
|
>
|
|
{step === 'preferences' && 'Setup'}
|
|
{step === 'vehicle' && 'Vehicle'}
|
|
{step === 'complete' && 'Done'}
|
|
</span>
|
|
</div>
|
|
{index < 2 && (
|
|
<div
|
|
className={`flex-1 h-1 mx-2 rounded transition-all ${
|
|
stepNumbers[currentStep] > stepNumbers[step]
|
|
? 'bg-primary-600 dark:bg-primary-700'
|
|
: 'bg-gray-200 dark:bg-gray-700'
|
|
}`}
|
|
/>
|
|
)}
|
|
</React.Fragment>
|
|
))}
|
|
</div>
|
|
|
|
{/* Step Content */}
|
|
<GlassCard padding="md">
|
|
{currentStep === 'preferences' && (
|
|
<PreferencesStep
|
|
onNext={handleSavePreferences}
|
|
loading={savePreferences.isPending}
|
|
/>
|
|
)}
|
|
|
|
{currentStep === 'vehicle' && (
|
|
<AddVehicleStep
|
|
onNext={handleSkipVehicle}
|
|
onAddVehicle={handleAddVehicle}
|
|
onBack={handleBack}
|
|
loading={isAddingVehicle}
|
|
/>
|
|
)}
|
|
|
|
{currentStep === 'complete' && (
|
|
<CompleteStep
|
|
onComplete={handleComplete}
|
|
loading={completeOnboarding.isPending}
|
|
/>
|
|
)}
|
|
</GlassCard>
|
|
</div>
|
|
</MobileContainer>
|
|
);
|
|
};
|
|
|
|
export default OnboardingMobileScreen;
|