Add SetupProgressIndicator component that shows users their current position in the 4-step onboarding journey: Name → Beneficiary → Equipment → Connect. The indicator displays: - Visual progress bar with percentage fill - Step circles with icons showing completed/current/pending status - Current step label with "Step X of 4" text Integrate the indicator into all four auth screens: - enter-name.tsx (Step 1) - add-loved-one.tsx (Step 2) - purchase.tsx (Step 3) - activate.tsx (Step 4) Also add @expo/vector-icons mock to jest.setup.js for testing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
192 lines
5.1 KiB
TypeScript
192 lines
5.1 KiB
TypeScript
import React from 'react';
|
|
import { View, Text, StyleSheet } from 'react-native';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import {
|
|
AppColors,
|
|
BorderRadius,
|
|
FontSizes,
|
|
FontWeights,
|
|
Spacing,
|
|
} from '@/constants/theme';
|
|
|
|
export type SetupStep = 'name' | 'beneficiary' | 'purchase' | 'activate';
|
|
|
|
interface SetupProgressIndicatorProps {
|
|
currentStep: SetupStep;
|
|
variant?: 'default' | 'compact';
|
|
}
|
|
|
|
interface StepConfig {
|
|
key: SetupStep;
|
|
label: string;
|
|
shortLabel: string;
|
|
icon: keyof typeof Ionicons.glyphMap;
|
|
}
|
|
|
|
const STEPS: StepConfig[] = [
|
|
{ key: 'name', label: 'Your Name', shortLabel: 'Name', icon: 'person-outline' },
|
|
{ key: 'beneficiary', label: 'Add Loved One', shortLabel: 'Person', icon: 'heart-outline' },
|
|
{ key: 'purchase', label: 'Get Equipment', shortLabel: 'Equipment', icon: 'hardware-chip-outline' },
|
|
{ key: 'activate', label: 'Connect', shortLabel: 'Connect', icon: 'wifi-outline' },
|
|
];
|
|
|
|
function getStepIndex(step: SetupStep): number {
|
|
return STEPS.findIndex((s) => s.key === step);
|
|
}
|
|
|
|
export function SetupProgressIndicator({
|
|
currentStep,
|
|
variant = 'default',
|
|
}: SetupProgressIndicatorProps) {
|
|
const currentIndex = getStepIndex(currentStep);
|
|
const isCompact = variant === 'compact';
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
{/* Progress bar */}
|
|
<View style={styles.progressBarContainer}>
|
|
<View style={styles.progressBarTrack}>
|
|
<View
|
|
style={[
|
|
styles.progressBarFill,
|
|
{ width: `${((currentIndex + 1) / STEPS.length) * 100}%` },
|
|
]}
|
|
/>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Step indicators */}
|
|
<View style={styles.stepsContainer}>
|
|
{STEPS.map((step, index) => {
|
|
const isCompleted = index < currentIndex;
|
|
const isCurrent = index === currentIndex;
|
|
const isPending = index > currentIndex;
|
|
|
|
return (
|
|
<View key={step.key} style={styles.stepWrapper}>
|
|
{/* Step circle */}
|
|
<View
|
|
style={[
|
|
styles.stepCircle,
|
|
isCompleted && styles.stepCircleCompleted,
|
|
isCurrent && styles.stepCircleCurrent,
|
|
isPending && styles.stepCirclePending,
|
|
]}
|
|
>
|
|
{isCompleted ? (
|
|
<Ionicons
|
|
name="checkmark"
|
|
size={isCompact ? 12 : 14}
|
|
color={AppColors.white}
|
|
/>
|
|
) : (
|
|
<Ionicons
|
|
name={step.icon}
|
|
size={isCompact ? 12 : 14}
|
|
color={isCurrent ? AppColors.white : AppColors.textMuted}
|
|
/>
|
|
)}
|
|
</View>
|
|
|
|
{/* Step label */}
|
|
{!isCompact && (
|
|
<Text
|
|
style={[
|
|
styles.stepLabel,
|
|
isCompleted && styles.stepLabelCompleted,
|
|
isCurrent && styles.stepLabelCurrent,
|
|
isPending && styles.stepLabelPending,
|
|
]}
|
|
numberOfLines={1}
|
|
>
|
|
{step.shortLabel}
|
|
</Text>
|
|
)}
|
|
</View>
|
|
);
|
|
})}
|
|
</View>
|
|
|
|
{/* Current step text */}
|
|
<Text style={styles.currentStepText}>
|
|
Step {currentIndex + 1} of {STEPS.length}: {STEPS[currentIndex].label}
|
|
</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
paddingHorizontal: Spacing.md,
|
|
paddingVertical: Spacing.md,
|
|
backgroundColor: AppColors.surface,
|
|
borderRadius: BorderRadius.lg,
|
|
marginBottom: Spacing.lg,
|
|
},
|
|
progressBarContainer: {
|
|
marginBottom: Spacing.md,
|
|
},
|
|
progressBarTrack: {
|
|
height: 4,
|
|
backgroundColor: AppColors.borderLight,
|
|
borderRadius: BorderRadius.full,
|
|
overflow: 'hidden',
|
|
},
|
|
progressBarFill: {
|
|
height: '100%',
|
|
backgroundColor: AppColors.primary,
|
|
borderRadius: BorderRadius.full,
|
|
},
|
|
stepsContainer: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
marginBottom: Spacing.sm,
|
|
},
|
|
stepWrapper: {
|
|
alignItems: 'center',
|
|
flex: 1,
|
|
},
|
|
stepCircle: {
|
|
width: 28,
|
|
height: 28,
|
|
borderRadius: 14,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginBottom: Spacing.xs,
|
|
},
|
|
stepCircleCompleted: {
|
|
backgroundColor: AppColors.success,
|
|
},
|
|
stepCircleCurrent: {
|
|
backgroundColor: AppColors.primary,
|
|
},
|
|
stepCirclePending: {
|
|
backgroundColor: AppColors.backgroundSecondary,
|
|
borderWidth: 1,
|
|
borderColor: AppColors.border,
|
|
},
|
|
stepLabel: {
|
|
fontSize: FontSizes.xs,
|
|
textAlign: 'center',
|
|
},
|
|
stepLabelCompleted: {
|
|
color: AppColors.success,
|
|
fontWeight: FontWeights.medium,
|
|
},
|
|
stepLabelCurrent: {
|
|
color: AppColors.primary,
|
|
fontWeight: FontWeights.semibold,
|
|
},
|
|
stepLabelPending: {
|
|
color: AppColors.textMuted,
|
|
},
|
|
currentStepText: {
|
|
fontSize: FontSizes.sm,
|
|
color: AppColors.textSecondary,
|
|
textAlign: 'center',
|
|
fontWeight: FontWeights.medium,
|
|
},
|
|
});
|
|
|
|
export default SetupProgressIndicator;
|