feat: implement earnings overview and bank account management screens
This commit is contained in:
parent
525ed45b23
commit
2148f4e0c3
@ -36,23 +36,33 @@ import { useAppDispatch, useAppSelector } from '@store';
|
|||||||
// updatedAt: '2026-07-21T07:29:42.582Z',
|
// updatedAt: '2026-07-21T07:29:42.582Z',
|
||||||
// },
|
// },
|
||||||
// ];
|
// ];
|
||||||
export const BankDetailsScreen: React.FC = () => {
|
export const BankDetailsScreen = () => {
|
||||||
const { colors } = useAppTheme();
|
const { colors } = useAppTheme();
|
||||||
const styles = getStyles(colors);
|
const styles = getStyles(colors);
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<NativeStackNavigationProp<AppStackParamList>>();
|
useNavigation<NativeStackNavigationProp<AppStackParamList>>();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { bankAccounts, isLoading, error } = useAppSelector(
|
|
||||||
state => state.addBankDetails,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Replace this state with Redux selector when wiring real API
|
// Replace this state with Redux selector when wiring real API
|
||||||
// const [accounts] = useState<BankAccount[]>(bankAccounts);
|
// const [accounts] = useState<BankAccount[]>(bankAccounts);
|
||||||
const [refreshing, setRefreshing] = useState(false);
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
|
// console.log('-------');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchBankAccounts());
|
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 = () => {
|
const handleRefresh = () => {
|
||||||
setRefreshing(true);
|
setRefreshing(true);
|
||||||
@ -100,7 +110,7 @@ export const BankDetailsScreen: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Empty State */}
|
{/* Empty State */}
|
||||||
{bankAccounts?.length === 0 ? (
|
{!accountsList || accountsList.length === 0 ? (
|
||||||
<View style={styles.emptyContainer}>
|
<View style={styles.emptyContainer}>
|
||||||
<View style={styles.emptyIconCircle}>
|
<View style={styles.emptyIconCircle}>
|
||||||
<WalletIcon size={36} color={colors.primary} />
|
<WalletIcon size={36} color={colors.primary} />
|
||||||
@ -118,22 +128,22 @@ export const BankDetailsScreen: React.FC = () => {
|
|||||||
<Text style={styles.sectionTitle}>Your Accounts</Text>
|
<Text style={styles.sectionTitle}>Your Accounts</Text>
|
||||||
<View style={styles.countBadge}>
|
<View style={styles.countBadge}>
|
||||||
<Text style={styles.countBadgeText}>
|
<Text style={styles.countBadgeText}>
|
||||||
{bankAccounts?.length}
|
{accountsList.length}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Account Cards */}
|
{/* Account Cards */}
|
||||||
{bankAccounts?.map(account => (
|
{accountsList.map((account: any) => (
|
||||||
<View
|
<View
|
||||||
key={account.id}
|
key={account?.id}
|
||||||
style={[
|
style={[
|
||||||
styles.accountCard,
|
styles.accountCard,
|
||||||
account.isDefault ? styles.defaultCard : null,
|
account?.isDefault ? styles.defaultCard : null,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{/* Top color strip for default account */}
|
{/* Top color strip for default account */}
|
||||||
{account.isDefault && <View style={styles.cardTopStrip} />}
|
{account?.isDefault && <View style={styles.cardTopStrip} />}
|
||||||
|
|
||||||
<View style={styles.cardBody}>
|
<View style={styles.cardBody}>
|
||||||
{/* Card Header Row */}
|
{/* Card Header Row */}
|
||||||
@ -144,36 +154,36 @@ export const BankDetailsScreen: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
<View style={styles.cardTitleGroup}>
|
<View style={styles.cardTitleGroup}>
|
||||||
<Text style={styles.bankName} numberOfLines={1}>
|
<Text style={styles.bankName} numberOfLines={1}>
|
||||||
{account.bankName}
|
{account?.bankName}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={styles.holderName} numberOfLines={1}>
|
<Text style={styles.holderName} numberOfLines={1}>
|
||||||
{account.accountHolderName}
|
{account?.accountHolderName}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Status Badges */}
|
{/* Status Badges */}
|
||||||
<View style={styles.badgeRow}>
|
<View style={styles.badgeRow}>
|
||||||
{account.isDefault && (
|
{account?.isDefault && (
|
||||||
<View style={styles.defaultBadge}>
|
<View style={styles.defaultBadge}>
|
||||||
<Text style={styles.defaultBadgeText}>DEFAULT</Text>
|
<Text style={styles.defaultBadgeText}>DEFAULT</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
<View
|
<View
|
||||||
style={
|
style={
|
||||||
account.isVerified
|
account?.isVerified
|
||||||
? styles.verifiedBadge
|
? styles.verifiedBadge
|
||||||
: styles.unverifiedBadge
|
: styles.unverifiedBadge
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
style={
|
style={
|
||||||
account.isVerified
|
account?.isVerified
|
||||||
? styles.verifiedBadgeText
|
? styles.verifiedBadgeText
|
||||||
: styles.unverifiedBadgeText
|
: styles.unverifiedBadgeText
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{account.isVerified ? 'VERIFIED' : 'PENDING'}
|
{account?.isVerified ? 'VERIFIED' : 'PENDING'}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@ -186,22 +196,22 @@ export const BankDetailsScreen: React.FC = () => {
|
|||||||
<View style={styles.fieldItem}>
|
<View style={styles.fieldItem}>
|
||||||
<Text style={styles.fieldLabel}>Account Number</Text>
|
<Text style={styles.fieldLabel}>Account Number</Text>
|
||||||
<Text style={styles.fieldValueMasked}>
|
<Text style={styles.fieldValueMasked}>
|
||||||
{maskAccountNumber(account.accountNumber)}
|
{maskAccountNumber(account?.accountNumber)}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.fieldItem}>
|
<View style={styles.fieldItem}>
|
||||||
<Text style={styles.fieldLabel}>IFSC Code</Text>
|
<Text style={styles.fieldLabel}>IFSC Code</Text>
|
||||||
<Text style={styles.fieldValue}>
|
<Text style={styles.fieldValue}>
|
||||||
{account.ifscCode || '—'}
|
{account?.ifscCode || '—'}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{account.branchName ? (
|
{account?.branchName ? (
|
||||||
<View style={styles.fieldItem}>
|
<View style={styles.fieldItem}>
|
||||||
<Text style={styles.fieldLabel}>Branch</Text>
|
<Text style={styles.fieldLabel}>Branch</Text>
|
||||||
<Text style={styles.fieldValue} numberOfLines={1}>
|
<Text style={styles.fieldValue} numberOfLines={1}>
|
||||||
{account.branchName}
|
{account?.branchName}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
@ -209,7 +219,7 @@ export const BankDetailsScreen: React.FC = () => {
|
|||||||
<View style={styles.fieldItem}>
|
<View style={styles.fieldItem}>
|
||||||
<Text style={styles.fieldLabel}>Owner Type</Text>
|
<Text style={styles.fieldLabel}>Owner Type</Text>
|
||||||
<Text style={styles.fieldValue}>
|
<Text style={styles.fieldValue}>
|
||||||
{account.ownerType || '—'}
|
{account?.ownerType || '—'}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@ -219,9 +229,9 @@ export const BankDetailsScreen: React.FC = () => {
|
|||||||
{/* Card Footer */}
|
{/* Card Footer */}
|
||||||
<View style={styles.cardFooter}>
|
<View style={styles.cardFooter}>
|
||||||
<Text style={styles.addedAtText}>
|
<Text style={styles.addedAtText}>
|
||||||
Added {formatDate(account.createdAt)}
|
Added {formatDate(account?.createdAt)}
|
||||||
</Text>
|
</Text>
|
||||||
{account.isVerified && (
|
{account?.isVerified && (
|
||||||
<CheckCircleIcon size={16} color={colors.success} />
|
<CheckCircleIcon size={16} color={colors.success} />
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@ -1,77 +1,95 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { View, Text, ScrollView } from 'react-native';
|
import { View, Text, ScrollView } from 'react-native';
|
||||||
import { useAppTheme } from '@theme';
|
import { useAppTheme } from '@theme';
|
||||||
import { WalletIcon } from '@icons';
|
import { WalletIcon } from '@icons';
|
||||||
import { useAppSelector } from '@store';
|
import { useAppDispatch, useAppSelector } from '@store';
|
||||||
import { formatCurrency } from '@utils/formatters';
|
import { formatCurrency, formatDate } from '@utils';
|
||||||
import { getStyles } from './earningsScreen.styles';
|
import { getStyles } from './earningsScreen.styles';
|
||||||
|
import { getCompletedDeliveries } from '../jobsScreen';
|
||||||
|
import { useFocusEffect } from '@react-navigation/native';
|
||||||
|
|
||||||
export const EarningsScreen: React.FC = () => {
|
export const EarningsScreen: React.FC = () => {
|
||||||
const { colors } = useAppTheme();
|
const { colors } = useAppTheme();
|
||||||
const styles = getStyles(colors);
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
const { todayEarnings, completedCount, onlineMinutes } = useAppSelector(
|
// const { todayEarnings, completedCount, onlineMinutes } = useAppSelector(
|
||||||
state => state.earnings,
|
// state => state.earnings,
|
||||||
);
|
// );
|
||||||
const { jobHistory } = useAppSelector(state => state.job);
|
|
||||||
|
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
|
// Default mock jobs history if none completed yet
|
||||||
const defaultHistory: {
|
// const defaultHistory: {
|
||||||
orderId: string;
|
// orderId: string;
|
||||||
time: string;
|
// time: string;
|
||||||
amount: string;
|
// amount: string;
|
||||||
paymentType: 'online' | 'cod';
|
// paymentType: 'online' | 'cod';
|
||||||
}[] = [
|
// }[] = [
|
||||||
{
|
// {
|
||||||
orderId: '#ORD125487',
|
// orderId: '#ORD125487',
|
||||||
time: '12:35 PM',
|
// time: '12:35 PM',
|
||||||
amount: '78.0',
|
// amount: '78.0',
|
||||||
paymentType: 'online',
|
// paymentType: 'online',
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
orderId: '#ORD125422',
|
// orderId: '#ORD125422',
|
||||||
time: '11:15 AM',
|
// time: '11:15 AM',
|
||||||
amount: '65.0',
|
// amount: '65.0',
|
||||||
paymentType: 'cod',
|
// paymentType: 'cod',
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
orderId: '#ORD125389',
|
// orderId: '#ORD125389',
|
||||||
time: '09:40 AM',
|
// time: '09:40 AM',
|
||||||
amount: '120.0',
|
// amount: '120.0',
|
||||||
paymentType: 'online',
|
// paymentType: 'online',
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
orderId: '#ORD125310',
|
// orderId: '#ORD125310',
|
||||||
time: 'Yesterday',
|
// time: 'Yesterday',
|
||||||
amount: '85.0',
|
// amount: '85.0',
|
||||||
paymentType: 'online',
|
// paymentType: 'online',
|
||||||
},
|
// },
|
||||||
];
|
// ];
|
||||||
|
|
||||||
const displayHistory =
|
const totalAmount = completedDeliveries?.reduce((acc, item) => {
|
||||||
jobHistory.length > 0
|
return acc + Number(item?.order?.deliveryFee);
|
||||||
? jobHistory
|
}, 0);
|
||||||
.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 formatOnlineTime = (mins: number) => {
|
// console.log('totalAmount', totalAmount)
|
||||||
const hours = Math.floor(mins / 60);
|
|
||||||
const remainingMins = mins % 60;
|
useFocusEffect(useCallback(() => {
|
||||||
return `${hours}h ${
|
dispatch(getCompletedDeliveries());
|
||||||
remainingMins < 10 ? `0${remainingMins}` : remainingMins
|
}, [dispatch]))
|
||||||
}m`;
|
// 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 (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
@ -85,18 +103,18 @@ export const EarningsScreen: React.FC = () => {
|
|||||||
|
|
||||||
{/* Total Earnings Card */}
|
{/* Total Earnings Card */}
|
||||||
<View style={styles.summaryCard}>
|
<View style={styles.summaryCard}>
|
||||||
<Text style={styles.summaryTitle}>Today's Earnings</Text>
|
<Text style={styles.summaryTitle}>Total Earnings</Text>
|
||||||
<Text style={styles.totalAmount}>
|
<Text style={styles.totalAmount}>
|
||||||
{formatCurrency(todayEarnings)}
|
{formatCurrency(totalAmount || 0)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View style={styles.statsRow}>
|
{/* <View style={styles.statsRow}>
|
||||||
<View style={styles.statBox}>
|
<View style={styles.statBox}>
|
||||||
<Text style={styles.statVal}>{completedCount}</Text>
|
<Text style={styles.statVal}>{completedDeliveries?.length}</Text>
|
||||||
<Text style={styles.statLabel}>Deliveries</Text>
|
<Text style={styles.statLabel}>Deliveries</Text>
|
||||||
</View>
|
</View> */}
|
||||||
|
|
||||||
<View style={styles.statDivider} />
|
{/* <View style={styles.statDivider} />
|
||||||
|
|
||||||
<View style={styles.statBox}>
|
<View style={styles.statBox}>
|
||||||
<Text style={styles.statVal}>
|
<Text style={styles.statVal}>
|
||||||
@ -110,15 +128,15 @@ export const EarningsScreen: React.FC = () => {
|
|||||||
<View style={styles.statBox}>
|
<View style={styles.statBox}>
|
||||||
<Text style={styles.statVal}>₹78.50</Text>
|
<Text style={styles.statVal}>₹78.50</Text>
|
||||||
<Text style={styles.statLabel}>Avg / Order</Text>
|
<Text style={styles.statLabel}>Avg / Order</Text>
|
||||||
</View>
|
</View>*/}
|
||||||
</View>
|
{/* </View> */}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Recent Deliveries List */}
|
{/* Recent Deliveries List */}
|
||||||
<View style={styles.listSection}>
|
<View style={styles.listSection}>
|
||||||
<Text style={styles.sectionTitle}>Recent Deliveries</Text>
|
<Text style={styles.sectionTitle}>Recent Deliveries</Text>
|
||||||
|
|
||||||
{displayHistory.map((item, index) => (
|
{completedDeliveries?.map((item, index) => (
|
||||||
<View key={index} style={styles.earningItem}>
|
<View key={index} style={styles.earningItem}>
|
||||||
<View style={styles.itemLeft}>
|
<View style={styles.itemLeft}>
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
@ -126,17 +144,17 @@ export const EarningsScreen: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
<View>
|
<View>
|
||||||
<Text style={styles.orderIdText}>{item.orderId}</Text>
|
<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>
|
</View>
|
||||||
|
|
||||||
<View style={styles.itemRight}>
|
<View style={styles.itemRight}>
|
||||||
<Text style={styles.itemAmount}>
|
<Text style={styles.itemAmount}>
|
||||||
+{formatCurrency(Number(item.amount))}
|
+{formatCurrency(Number(item?.order?.deliveryFee))}
|
||||||
</Text>
|
</Text>
|
||||||
<View style={styles.paymentBadge}>
|
<View style={styles.paymentBadge}>
|
||||||
<Text style={styles.paymentText}>
|
<Text style={styles.paymentText}>
|
||||||
{item.paymentType.toUpperCase()}
|
{/* {item?.order?.pa.toUpperCase()} */}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user