Fix form keyboard issues and sticky footers in agent screens

This commit is contained in:
Abin Toppo
2026-05-26 16:34:05 +05:30
commit 9ac5e95d52
410 changed files with 86650 additions and 0 deletions
@@ -0,0 +1,248 @@
import React, { useEffect, useMemo } from 'react';
import { ScrollView, StyleSheet, Text, View } from 'react-native';
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
import { useAppDispatch, useAppSelector } from '../../../app/hooks';
import { Button, Card, Input, ScreenShell } from '../../../components';
import type { InsuranceStackParamList } from '../../../navigation/types';
import { useTheme } from '../../../theme';
import { formatCompactCurrency, formatCurrency } from '../../../utils/formatters';
import { InsuranceCategoryChip } from '../components/InsuranceCategoryChip';
import { InsuranceRangeSlider } from '../components/InsuranceRangeSlider';
import { useEstimateInsurancePremiumQuery } from '../services/insuranceApi';
import { insuranceActions } from '../store/insuranceSlice';
import { selectInsuranceCalculator } from '../store/insuranceSelectors';
import { INSURANCE_CATEGORIES } from '../types';
type Props = NativeStackScreenProps<InsuranceStackParamList, 'InsuranceCalculator'>;
export default function InsuranceCalculatorScreen({ navigation, route }: Props) {
const dispatch = useAppDispatch();
const theme = useTheme();
const calculator = useAppSelector(selectInsuranceCalculator);
const effectiveCategory = calculator.category;
useEffect(() => {
if (route.params?.category) {
dispatch(
insuranceActions.updateInsuranceCalculator({
field: 'category',
value: route.params.category,
}),
);
}
}, [dispatch, route.params?.category]);
const estimateQuery = useEstimateInsurancePremiumQuery({
...calculator,
category: effectiveCategory,
});
const estimate = estimateQuery.data;
const coverageValue = useMemo(
() => Number(calculator.coverageAmount || 0),
[calculator.coverageAmount],
);
return (
<ScreenShell title="Calculator" subtitle="Estimate premiums by age, cover, and tenure">
<ScrollView contentContainerStyle={styles.scroll} showsVerticalScrollIndicator={false} keyboardShouldPersistTaps="handled">
<Card style={styles.sectionCard}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>
Policy category
</Text>
<View style={styles.categoryWrap}>
{INSURANCE_CATEGORIES.map((category) => (
<InsuranceCategoryChip
key={category}
category={category}
selected={effectiveCategory === category}
onPress={() =>
dispatch(
insuranceActions.updateInsuranceCalculator({
field: 'category',
value: category,
}),
)
}
/>
))}
</View>
</Card>
<Card style={styles.sectionCard}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>
Adjust your assumptions
</Text>
<InsuranceRangeSlider
label="Age"
min={18}
max={70}
step={1}
value={Number(calculator.age || 18)}
onValueChange={(value) =>
dispatch(
insuranceActions.updateInsuranceCalculator({
field: 'age',
value: String(value),
}),
)
}
/>
<InsuranceRangeSlider
label="Coverage"
min={100000}
max={20000000}
step={100000}
value={coverageValue || 100000}
formatter={(value) => formatCompactCurrency(value)}
onValueChange={(value) =>
dispatch(
insuranceActions.updateInsuranceCalculator({
field: 'coverageAmount',
value: String(value),
}),
)
}
/>
<InsuranceRangeSlider
label="Tenure"
min={1}
max={30}
step={1}
value={Number(calculator.tenureYears || 1)}
suffix=" yr"
onValueChange={(value) =>
dispatch(
insuranceActions.updateInsuranceCalculator({
field: 'tenureYears',
value: String(value),
}),
)
}
/>
</Card>
<Card style={styles.sectionCard}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>
Manual inputs
</Text>
<Input
label="Age"
keyboardType="number-pad"
value={calculator.age}
onChangeText={(value) =>
dispatch(
insuranceActions.updateInsuranceCalculator({ field: 'age', value }),
)
}
/>
<Input
label="Coverage Amount"
keyboardType="number-pad"
value={calculator.coverageAmount}
onChangeText={(value) =>
dispatch(
insuranceActions.updateInsuranceCalculator({
field: 'coverageAmount',
value,
}),
)
}
/>
<Input
label="Tenure Years"
keyboardType="number-pad"
value={calculator.tenureYears}
onChangeText={(value) =>
dispatch(
insuranceActions.updateInsuranceCalculator({
field: 'tenureYears',
value,
}),
)
}
/>
</Card>
<Card style={styles.resultCard}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>
Estimated premium
</Text>
<View style={styles.resultGrid}>
<View style={[styles.resultMetric, { backgroundColor: theme.colors.surfaceMuted }]}>
<Text style={[styles.resultLabel, { color: theme.colors.textSecondary }]}>
Monthly
</Text>
<Text style={[styles.resultValue, { color: theme.colors.textPrimary }]}>
{estimate ? formatCurrency(estimate.monthlyPremium) : '--'}
</Text>
</View>
<View style={[styles.resultMetric, { backgroundColor: theme.colors.surfaceMuted }]}>
<Text style={[styles.resultLabel, { color: theme.colors.textSecondary }]}>
Annual
</Text>
<Text style={[styles.resultValue, { color: theme.colors.textPrimary }]}>
{estimate ? formatCurrency(estimate.annualPremium) : '--'}
</Text>
</View>
</View>
<Text style={[styles.recommendation, { color: theme.colors.textSecondary }]}>
Recommended protection: {estimate ? formatCompactCurrency(estimate.recommendedCover) : '--'}
</Text>
<Button
title="Buy This Cover"
onPress={() =>
navigation.navigate('BuyInsurance', { category: effectiveCategory })
}
/>
</Card>
</ScrollView>
</ScreenShell>
);
}
const styles = StyleSheet.create({
scroll: {
gap: 16,
paddingBottom: 28,
},
sectionCard: {
gap: 14,
},
sectionTitle: {
fontSize: 16,
fontWeight: '800',
},
categoryWrap: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 10,
},
resultCard: {
gap: 14,
},
resultGrid: {
flexDirection: 'row',
gap: 12,
},
resultMetric: {
flex: 1,
borderRadius: 16,
padding: 14,
gap: 4,
},
resultLabel: {
fontSize: 11,
fontWeight: '700',
textTransform: 'uppercase',
letterSpacing: 0.5,
},
resultValue: {
fontSize: 18,
fontWeight: '900',
},
recommendation: {
fontSize: 12,
lineHeight: 18,
},
});