feat: implement onboarding stack and various feature screens for OTP, bank details, and profile management.

This commit is contained in:
Tamojit Biswas 2026-07-21 18:27:32 +05:30
parent 65f685d5cf
commit 525ed45b23
7 changed files with 88 additions and 38 deletions

View File

@ -56,7 +56,9 @@ export const OtpScreen: React.FC<{ route: any }> = ({ route }) => {
await dispatch(
verifyOtp({ phone, code: otp, role: 'DELIVERY' }),
).unwrap();
navigation.navigate('OnBoarding', { screen: RouteNames.CompleteProfile });
// Navigation is handled automatically by rootNavigator's conditional
// rendering: once accessToken is set, the Auth stack unmounts and
// OnBoarding or App stack mounts based on user.status.
} catch (error) {
Alert.alert('Error', error as string);
}

View File

@ -12,10 +12,8 @@ import { WalletIcon, CheckCircleIcon, ClipboardIcon } from '@icons';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { AppStackParamList } from '@navigation/navigationTypes';
import { RouteNames } from '@utils/constants';
import { BankAccount } from '@interfaces';
import { getStyles } from './bankDetailsScreen.styles';
import { formatDate, maskAccountNumber } from '@utils';
import { formatDate, maskAccountNumber, RouteNames } from '@utils';
import { fetchBankAccounts } from '../addBankDetailsScreen';
import { useAppDispatch, useAppSelector } from '@store';

View File

@ -8,6 +8,7 @@ export interface completeProfileState {
error: string | null;
submitError: string | null;
userProfile: UserProfile | null;
alreadyNavigate: boolean;
}
const initialState: completeProfileState = {
@ -15,6 +16,7 @@ const initialState: completeProfileState = {
error: null,
submitError: null,
userProfile: null,
alreadyNavigate: false,
};
export const completeProfileReducer = createReducer(initialState, builder => {
@ -26,6 +28,7 @@ export const completeProfileReducer = createReducer(initialState, builder => {
.addCase(completeOnboard.fulfilled, (state, action) => {
state.isLoading = false;
state.userProfile = action.payload;
state.alreadyNavigate = true;
})
.addCase(completeOnboard.rejected, (state, action) => {
state.isLoading = false;

View File

@ -198,7 +198,7 @@ export const DocumentsScreen: React.FC = () => {
statusBarTranslucent
onRequestClose={() => setSelectedDoc(null)}
>
<SafeAreaView style={styles.modalContainer}>
<View style={styles.modalContainer}>
{/* Modal Header */}
<View style={styles.modalHeader}>
<View style={styles.modalHeaderInfo}>
@ -259,7 +259,7 @@ export const DocumentsScreen: React.FC = () => {
)
)}
</View>
</SafeAreaView>
</View>
</Modal>
</View>
);

View File

@ -10,35 +10,75 @@ export const EarningsScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
const { todayEarnings, completedCount, onlineMinutes } = useAppSelector((state) => state.earnings);
const { jobHistory } = useAppSelector((state) => state.job);
const { todayEarnings, completedCount, onlineMinutes } = useAppSelector(
state => state.earnings,
);
const { jobHistory } = useAppSelector(state => state.job);
// Default mock jobs history if none completed yet
const defaultHistory: { orderId: string; time: string; amount: number; paymentType: 'online' | 'cod'; }[] = [
{ orderId: '#ORD125487', time: '12:35 PM', amount: 78.0, paymentType: 'online' },
{ orderId: '#ORD125422', time: '11:15 AM', amount: 65.0, paymentType: 'cod' },
{ orderId: '#ORD125389', time: '09:40 AM', amount: 120.0, paymentType: 'online' },
{ orderId: '#ORD125310', time: 'Yesterday', amount: 85.0, paymentType: 'online' },
const defaultHistory: {
orderId: string;
time: string;
amount: string;
paymentType: 'online' | 'cod';
}[] = [
{
orderId: '#ORD125487',
time: '12:35 PM',
amount: '78.0',
paymentType: 'online',
},
{
orderId: '#ORD125422',
time: '11:15 AM',
amount: '65.0',
paymentType: 'cod',
},
{
orderId: '#ORD125389',
time: '09:40 AM',
amount: '120.0',
paymentType: 'online',
},
{
orderId: '#ORD125310',
time: 'Yesterday',
amount: '85.0',
paymentType: 'online',
},
];
const displayHistory = jobHistory.length > 0
? jobHistory.map(job => ({
const displayHistory =
jobHistory.length > 0
? jobHistory
.map(job => ({
orderId: job.orderId,
time: job.deliveredAt ? new Date(job.deliveredAt).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}) : 'Delivered',
time: job.deliveredAt
? new Date(job.deliveredAt).toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
})
: 'Delivered',
amount: job.amount,
paymentType: job.paymentType,
})).concat(defaultHistory)
}))
.concat(defaultHistory)
: defaultHistory;
const formatOnlineTime = (mins: number) => {
const hours = Math.floor(mins / 60);
const remainingMins = mins % 60;
return `${hours}h ${remainingMins < 10 ? `0${remainingMins}` : remainingMins}m`;
return `${hours}h ${
remainingMins < 10 ? `0${remainingMins}` : remainingMins
}m`;
};
return (
<View style={styles.container}>
<ScrollView contentContainerStyle={styles.scrollContainer} showsVerticalScrollIndicator={false}>
<ScrollView
contentContainerStyle={styles.scrollContainer}
showsVerticalScrollIndicator={false}
>
<View style={styles.headerContainer}>
<Text style={styles.title}>Earnings</Text>
</View>
@ -46,7 +86,9 @@ export const EarningsScreen: React.FC = () => {
{/* Total Earnings Card */}
<View style={styles.summaryCard}>
<Text style={styles.summaryTitle}>Today's Earnings</Text>
<Text style={styles.totalAmount}>{formatCurrency(todayEarnings)}</Text>
<Text style={styles.totalAmount}>
{formatCurrency(todayEarnings)}
</Text>
<View style={styles.statsRow}>
<View style={styles.statBox}>
@ -57,7 +99,9 @@ export const EarningsScreen: React.FC = () => {
<View style={styles.statDivider} />
<View style={styles.statBox}>
<Text style={styles.statVal}>{formatOnlineTime(onlineMinutes)}</Text>
<Text style={styles.statVal}>
{formatOnlineTime(onlineMinutes)}
</Text>
<Text style={styles.statLabel}>Online Time</Text>
</View>
@ -87,9 +131,13 @@ export const EarningsScreen: React.FC = () => {
</View>
<View style={styles.itemRight}>
<Text style={styles.itemAmount}>+{formatCurrency(item.amount)}</Text>
<Text style={styles.itemAmount}>
+{formatCurrency(Number(item.amount))}
</Text>
<View style={styles.paymentBadge}>
<Text style={styles.paymentText}>{item.paymentType.toUpperCase()}</Text>
<Text style={styles.paymentText}>
{item.paymentType.toUpperCase()}
</Text>
</View>
</View>
</View>

View File

@ -19,11 +19,6 @@ export const ProfileScreen: React.FC = () => {
const { profile } = useAppSelector(state => state.accountInfo);
const menuItems = [
{
id: 'personal',
title: 'Personal Info',
icon: <PersonIcon size={20} color={colors.primary} />,
},
{
id: 'vehicle',
title: 'Vehicle Info',

View File

@ -8,20 +8,24 @@ import {
KycScreen,
} from '@features/screens';
import { RouteNames } from '@utils/constants';
import { useAppSelector } from '@store';
const Stack = createNativeStackNavigator<OnBoardingParamList>();
const onBoardingStack: React.FC = () => {
const { alreadyNavigate } = useAppSelector(state => state.onboard);
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
{/* <Stack.Screen
name={RouteNames.SetLocation}
component={SetLocationScreen}
/> */}
{!alreadyNavigate && (
<Stack.Screen
name={RouteNames.CompleteProfile}
component={CompleteProfileScreen}
/>
)}
<Stack.Screen name={RouteNames.Kyc} component={KycScreen} />
<Stack.Screen
name={RouteNames.OnboardingComplete}