Fix form keyboard issues and sticky footers in agent screens
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
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 },
|
||||
});
|
||||
Reference in New Issue
Block a user