Files
CBS-Mobile-App/src/modules/loan/screens/LoanDashboardScreen.tsx
T

255 lines
9.9 KiB
TypeScript

import React from 'react';
import { RefreshControl, ScrollView, StyleSheet, Text, View } from 'react-native';
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
import Ionicons from '@react-native-vector-icons/ionicons';
import type { LoanStackParamList } from '../../../navigation/types';
import { Button, Card, EmptyState, Loader, ScreenShell } from '../../../components';
import { useTheme } from '../../../theme';
import { formatCurrency, formatDate, formatCompactCurrency } from '../../../utils/formatters';
import { useLoanDashboard } from '../hooks/useLoanDashboard';
import { LoanSummaryHero } from '../components/LoanSummaryHero';
import { LoanAccountCard } from '../components/LoanAccountCard';
import { LoanFilterChip } from '../components/LoanFilterChip';
import { LoanNotificationItem } from '../components/LoanNotificationItem';
import { useGetLoanNotificationsQuery } from '../services/loanApi';
type Props = NativeStackScreenProps<LoanStackParamList, 'LoanDashboard'>;
export default function LoanDashboardScreen({ navigation }: Props) {
const theme = useTheme();
const {
activeLoans,
applications,
totalOutstanding,
nextEmi,
summary,
unreadNotifications,
isLoading,
isError,
refreshing,
onRefresh,
} = useLoanDashboard();
// Mock notifications for demonstration on dashboard
const { data: notifications } = useGetLoanNotificationsQuery();
const recentNotifications = (notifications ?? []).slice(0, 2);
return (
<ScreenShell title="Loan Management" subtitle="Manage active loans and apply for new ones" scrollable={false}>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.scrollContent}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
tintColor={theme.colors.primary}
colors={[theme.colors.primary]}
/>
}
>
{/* Hero Summary */}
<LoanSummaryHero totalOutstanding={totalOutstanding} nextEmi={nextEmi} summary={summary} />
{/* Notifications Section */}
{recentNotifications.length > 0 && (
<View style={styles.loanSection}>
<View style={styles.sectionHeaderRow}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>What's new</Text>
{unreadNotifications > 0 && (
<View style={[styles.unreadBadge, { backgroundColor: theme.colors.primary }]}>
<Text style={styles.unreadCount}>{unreadNotifications}</Text>
</View>
)}
</View>
<Card style={styles.notificationCard}>
{recentNotifications.map((notif, index) => (
<View key={notif.id}>
<LoanNotificationItem notification={notif} />
{index < recentNotifications.length - 1 && (
<View style={[styles.divider, { backgroundColor: theme.colors.cardBorder }]} />
)}
</View>
))}
</Card>
</View>
)}
{/* Quick Actions */}
<Card style={styles.actionCard}>
<View style={styles.sectionHeader}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>Quick actions</Text>
<Text style={[styles.sectionSubtitle, { color: theme.colors.textSecondary }]}>
Apply for a new loan, calculate EMI, or manage payments
</Text>
</View>
<View style={styles.actionGrid}>
<View style={styles.actionRow}>
<View style={styles.actionCol}>
<Button
title="Apply Loan"
onPress={() => navigation.navigate('LoanTypesListing')}
leftAccessory={<Ionicons name="add-circle-outline" size={18} color={theme.colors.white} />}
/>
</View>
<View style={styles.actionCol}>
<Button
title="EMI Calculator"
variant="secondary"
onPress={() => navigation.navigate('LoanCalculator', {})}
leftAccessory={<Ionicons name="calculator-outline" size={18} color={theme.colors.textPrimary} />}
/>
</View>
</View>
<View style={styles.actionRow}>
<View style={styles.actionCol}>
<Button
title="Track Application"
variant="secondary"
onPress={() => navigation.navigate('LoanTracking')}
leftAccessory={<Ionicons name="locate-outline" size={18} color={theme.colors.textPrimary} />}
/>
</View>
<View style={styles.actionCol}>
<Button
title="Payment History"
variant="secondary"
onPress={() => navigation.navigate('LoanHistory')}
leftAccessory={<Ionicons name="time-outline" size={18} color={theme.colors.textPrimary} />}
/>
</View>
</View>
<Button
title="Documents"
variant="secondary"
onPress={() => navigation.navigate('LoanDocuments', {})}
leftAccessory={<Ionicons name="document-text-outline" size={18} color={theme.colors.textPrimary} />}
/>
</View>
</Card>
{/* Active Loans */}
<View style={styles.loanSection}>
<View style={styles.sectionHeaderRow}>
<View style={styles.sectionHeaderCopy}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>Active loans</Text>
<Text style={[styles.sectionSubtitle, { color: theme.colors.textSecondary }]}>
{activeLoans.length} loan{activeLoans.length !== 1 ? 's' : ''} in your portfolio
</Text>
</View>
</View>
{isLoading && !activeLoans.length ? (
<Card><Loader /></Card>
) : null}
{isError && !activeLoans.length ? (
<EmptyState
title="Unable to load loans"
description="Please pull to refresh or try again in a moment."
actionLabel="Refresh"
onAction={onRefresh}
/>
) : null}
{!isLoading && !isError && !activeLoans.length ? (
<EmptyState
title="No active loans"
description="Apply for a loan to finance your dreams with competitive interest rates."
actionLabel="Apply for Loan"
onAction={() => navigation.navigate('LoanTypesListing')}
/>
) : null}
{activeLoans.map((loan) => (
<LoanAccountCard
key={loan.id}
loan={loan}
onPress={() => navigation.navigate('ActiveLoanDetails', { loanId: loan.id })}
/>
))}
</View>
{/* Recent Applications */}
{applications.length > 0 && (
<View style={styles.loanSection}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>Recent applications</Text>
{applications.map((app) => (
<Card key={app.id} style={styles.applicationCard}>
<View style={styles.appHeader}>
<View style={styles.appHeaderLeft}>
<Text style={[styles.appType, { color: theme.colors.textPrimary }]}>{app.type}</Text>
<Text style={[styles.appAmount, { color: theme.colors.textSecondary }]}>
{formatCurrency(app.amount)} · {formatDate(app.appliedDate)}
</Text>
</View>
<View
style={[
styles.appStatusBadge,
{
backgroundColor: app.status === 'Approved' ? '#10B98114' :
app.status === 'Rejected' ? '#EF444414' : `${theme.colors.primary}14`,
},
]}
>
<Text
style={[
styles.appStatusText,
{
color: app.status === 'Approved' ? '#10B981' :
app.status === 'Rejected' ? '#EF4444' : theme.colors.primary,
},
]}
>
{app.status}
</Text>
</View>
</View>
</Card>
))}
</View>
)}
<View style={styles.bottomSpacer} />
</ScrollView>
</ScreenShell>
);
}
const styles = StyleSheet.create({
scrollContent: { gap: 18, paddingBottom: 24 },
actionCard: { gap: 16 },
sectionHeader: { gap: 4 },
sectionTitle: { fontSize: 18, fontWeight: '800' },
sectionSubtitle: { fontSize: 13, lineHeight: 18 },
actionGrid: { gap: 12 },
actionRow: { flexDirection: 'row', gap: 12 },
actionCol: { flex: 1 },
loanSection: { gap: 14 },
sectionHeaderRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
sectionHeaderCopy: { flex: 1, gap: 4 },
applicationCard: { gap: 8 },
unreadBadge: {
paddingHorizontal: 8,
paddingVertical: 2,
borderRadius: 8,
marginLeft: 8,
},
unreadCount: {
color: '#FFF',
fontSize: 12,
fontWeight: '800',
},
notificationCard: { padding: 4, gap: 0 },
divider: { height: 1, marginHorizontal: 12 },
appHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
appHeaderLeft: { gap: 2 },
appType: { fontSize: 15, fontWeight: '700' },
appAmount: { fontSize: 12, fontWeight: '500' },
appStatusBadge: { paddingHorizontal: 10, paddingVertical: 5, borderRadius: 999 },
appStatusText: { fontSize: 12, fontWeight: '700' },
bottomSpacer: { height: 60 },
});