feat: implement earnings overview and bank account management screens

This commit is contained in:
Atanu Das 2026-07-22 18:09:53 +05:30
parent 525ed45b23
commit 2148f4e0c3
2 changed files with 125 additions and 97 deletions

View File

@ -36,23 +36,33 @@ import { useAppDispatch, useAppSelector } from '@store';
// updatedAt: '2026-07-21T07:29:42.582Z',
// },
// ];
export const BankDetailsScreen: React.FC = () => {
export const BankDetailsScreen = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
const navigation =
useNavigation<NativeStackNavigationProp<AppStackParamList>>();
const dispatch = useAppDispatch();
const { bankAccounts, isLoading, error } = useAppSelector(
state => state.addBankDetails,
);
// Replace this state with Redux selector when wiring real API
// const [accounts] = useState<BankAccount[]>(bankAccounts);
const [refreshing, setRefreshing] = useState(false);
// console.log('-------');
useEffect(() => {
dispatch(fetchBankAccounts());
}, []);
// console.log('object')
}, [dispatch]);
const { bankAccounts, isLoading, error } = useAppSelector(
state => state.addBankDetails,
);
const accountsList = Array.isArray(bankAccounts)
? bankAccounts
: (bankAccounts as any)?.data || (bankAccounts as any)?.bankAccounts || [];
// console.log(accountsList);
const handleRefresh = () => {
setRefreshing(true);
@ -100,7 +110,7 @@ export const BankDetailsScreen: React.FC = () => {
</View>
{/* Empty State */}
{bankAccounts?.length === 0 ? (
{!accountsList || accountsList.length === 0 ? (
<View style={styles.emptyContainer}>
<View style={styles.emptyIconCircle}>
<WalletIcon size={36} color={colors.primary} />
@ -118,22 +128,22 @@ export const BankDetailsScreen: React.FC = () => {
<Text style={styles.sectionTitle}>Your Accounts</Text>
<View style={styles.countBadge}>
<Text style={styles.countBadgeText}>
{bankAccounts?.length}
{accountsList.length}
</Text>
</View>
</View>
{/* Account Cards */}
{bankAccounts?.map(account => (
{accountsList.map((account: any) => (
<View
key={account.id}
key={account?.id}
style={[
styles.accountCard,
account.isDefault ? styles.defaultCard : null,
account?.isDefault ? styles.defaultCard : null,
]}
>
{/* Top color strip for default account */}
{account.isDefault && <View style={styles.cardTopStrip} />}
{account?.isDefault && <View style={styles.cardTopStrip} />}
<View style={styles.cardBody}>
{/* Card Header Row */}
@ -144,36 +154,36 @@ export const BankDetailsScreen: React.FC = () => {
</View>
<View style={styles.cardTitleGroup}>
<Text style={styles.bankName} numberOfLines={1}>
{account.bankName}
{account?.bankName}
</Text>
<Text style={styles.holderName} numberOfLines={1}>
{account.accountHolderName}
{account?.accountHolderName}
</Text>
</View>
</View>
{/* Status Badges */}
<View style={styles.badgeRow}>
{account.isDefault && (
{account?.isDefault && (
<View style={styles.defaultBadge}>
<Text style={styles.defaultBadgeText}>DEFAULT</Text>
</View>
)}
<View
style={
account.isVerified
account?.isVerified
? styles.verifiedBadge
: styles.unverifiedBadge
}
>
<Text
style={
account.isVerified
account?.isVerified
? styles.verifiedBadgeText
: styles.unverifiedBadgeText
}
>
{account.isVerified ? 'VERIFIED' : 'PENDING'}
{account?.isVerified ? 'VERIFIED' : 'PENDING'}
</Text>
</View>
</View>
@ -186,22 +196,22 @@ export const BankDetailsScreen: React.FC = () => {
<View style={styles.fieldItem}>
<Text style={styles.fieldLabel}>Account Number</Text>
<Text style={styles.fieldValueMasked}>
{maskAccountNumber(account.accountNumber)}
{maskAccountNumber(account?.accountNumber)}
</Text>
</View>
<View style={styles.fieldItem}>
<Text style={styles.fieldLabel}>IFSC Code</Text>
<Text style={styles.fieldValue}>
{account.ifscCode || '—'}
{account?.ifscCode || '—'}
</Text>
</View>
{account.branchName ? (
{account?.branchName ? (
<View style={styles.fieldItem}>
<Text style={styles.fieldLabel}>Branch</Text>
<Text style={styles.fieldValue} numberOfLines={1}>
{account.branchName}
{account?.branchName}
</Text>
</View>
) : null}
@ -209,7 +219,7 @@ export const BankDetailsScreen: React.FC = () => {
<View style={styles.fieldItem}>
<Text style={styles.fieldLabel}>Owner Type</Text>
<Text style={styles.fieldValue}>
{account.ownerType || '—'}
{account?.ownerType || '—'}
</Text>
</View>
</View>
@ -219,9 +229,9 @@ export const BankDetailsScreen: React.FC = () => {
{/* Card Footer */}
<View style={styles.cardFooter}>
<Text style={styles.addedAtText}>
Added {formatDate(account.createdAt)}
Added {formatDate(account?.createdAt)}
</Text>
{account.isVerified && (
{account?.isVerified && (
<CheckCircleIcon size={16} color={colors.success} />
)}
</View>

View File

@ -1,77 +1,95 @@
import React from 'react';
import React, { useCallback } from 'react';
import { View, Text, ScrollView } from 'react-native';
import { useAppTheme } from '@theme';
import { WalletIcon } from '@icons';
import { useAppSelector } from '@store';
import { formatCurrency } from '@utils/formatters';
import { useAppDispatch, useAppSelector } from '@store';
import { formatCurrency, formatDate } from '@utils';
import { getStyles } from './earningsScreen.styles';
import { getCompletedDeliveries } from '../jobsScreen';
import { useFocusEffect } from '@react-navigation/native';
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 dispatch = useAppDispatch();
// const { jobHistory } = useAppSelector(state => state.job);
const { completedDeliveries } = useAppSelector(state => state.deliveries)
// console.log('completedDeliveries', completedDeliveries)
// Default mock jobs history if none completed yet
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 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 => ({
orderId: job.orderId,
time: job.deliveredAt
? new Date(job.deliveredAt).toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
})
: 'Delivered',
amount: job.amount,
paymentType: job.paymentType,
}))
.concat(defaultHistory)
: defaultHistory;
const totalAmount = completedDeliveries?.reduce((acc, item) => {
return acc + Number(item?.order?.deliveryFee);
}, 0);
const formatOnlineTime = (mins: number) => {
const hours = Math.floor(mins / 60);
const remainingMins = mins % 60;
return `${hours}h ${
remainingMins < 10 ? `0${remainingMins}` : remainingMins
}m`;
};
// console.log('totalAmount', totalAmount)
useFocusEffect(useCallback(() => {
dispatch(getCompletedDeliveries());
}, [dispatch]))
// const deliveryList = Array.isArray(completedDeliveries)
// ? completedDeliveries
// : (completedDeliveries as any)?.data || (completedDeliveries as any)?.bankAccounts || [];
// const displayHistory =
// deliveryList?.length > 0
// ? deliveryList
// .map((delivery: any) => ({
// orderId: delivery.orderId,
// time: delivery.deliveredAt
// ? new Date(delivery.deliveredAt).toLocaleTimeString([], {
// hour: '2-digit',
// minute: '2-digit',
// })
// : 'Delivered',
// amount: delivery?.totalAmount,
// paymentType: delivery?.paymentMethod,
// }))
// .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 (
<View style={styles.container}>
@ -85,18 +103,18 @@ export const EarningsScreen: React.FC = () => {
{/* Total Earnings Card */}
<View style={styles.summaryCard}>
<Text style={styles.summaryTitle}>Today's Earnings</Text>
<Text style={styles.summaryTitle}>Total Earnings</Text>
<Text style={styles.totalAmount}>
{formatCurrency(todayEarnings)}
{formatCurrency(totalAmount || 0)}
</Text>
<View style={styles.statsRow}>
{/* <View style={styles.statsRow}>
<View style={styles.statBox}>
<Text style={styles.statVal}>{completedCount}</Text>
<Text style={styles.statVal}>{completedDeliveries?.length}</Text>
<Text style={styles.statLabel}>Deliveries</Text>
</View>
</View> */}
<View style={styles.statDivider} />
{/* <View style={styles.statDivider} />
<View style={styles.statBox}>
<Text style={styles.statVal}>
@ -110,15 +128,15 @@ export const EarningsScreen: React.FC = () => {
<View style={styles.statBox}>
<Text style={styles.statVal}>78.50</Text>
<Text style={styles.statLabel}>Avg / Order</Text>
</View>
</View>
</View>*/}
{/* </View> */}
</View>
{/* Recent Deliveries List */}
<View style={styles.listSection}>
<Text style={styles.sectionTitle}>Recent Deliveries</Text>
{displayHistory.map((item, index) => (
{completedDeliveries?.map((item, index) => (
<View key={index} style={styles.earningItem}>
<View style={styles.itemLeft}>
<View style={styles.iconContainer}>
@ -126,17 +144,17 @@ export const EarningsScreen: React.FC = () => {
</View>
<View>
<Text style={styles.orderIdText}>{item.orderId}</Text>
<Text style={styles.timeText}>{item.time}</Text>
<Text style={styles.timeText}>{formatDate(item?.deliveredAt || '')}</Text>
</View>
</View>
<View style={styles.itemRight}>
<Text style={styles.itemAmount}>
+{formatCurrency(Number(item.amount))}
+{formatCurrency(Number(item?.order?.deliveryFee))}
</Text>
<View style={styles.paymentBadge}>
<Text style={styles.paymentText}>
{item.paymentType.toUpperCase()}
{/* {item?.order?.pa.toUpperCase()} */}
</Text>
</View>
</View>