Add Positive Pay, statement guide v1.0, onboarding, and switch every feature on

- Positive Pay on the 12 Oct /pps contract (account number, not account ID):
  OTP enrollment, cheque submit, saved cheques, detail and stop payment.
  Full flow passed on the phone against mobile-banking-backend.lincpay.
- Statement & Mini Statement guide v1.0 (14 Oct): mini statement JSON on
  Home and the Statement screen; dated PDF export with the password rule.
- Onboarding slides and splash screen, Inter font, shared Icon and
  TypewriterText components, nearby branches via OpenStreetMap.
- Every feature switched on; the 14 Oct switch set is kept in
  featureConfig.stage-2026-10-14.ts. Loans run on demo data until the
  backend has /loans routes.
- npm run android wraps run-android for shells spawned from VS Code.
- OpenProject work packages updated to 14-10-2026 status, with screenshots
  of Positive Pay, statement, loans and cards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Abin Toppo
2026-10-14 18:18:40 +05:30
co-authored by Claude Opus 5.5
parent 5fbd13ba12
commit f960a119e8
510 changed files with 20268 additions and 6483 deletions
+36 -36
View File
@@ -7,7 +7,7 @@ import {
View,
} from 'react-native';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import Ionicons from '@react-native-vector-icons/ionicons';
import { Icon } from '../../../components/Icon';
import LinearGradient from 'react-native-linear-gradient';
import { BottomSheet, Button, Card, Input, ScreenShell } from '../../../components';
@@ -16,7 +16,7 @@ import type { HomeStackParamList } from '../../../navigation/types';
import { useTheme, fontFamily } from '../../../theme';
type Props = NativeStackScreenProps<HomeStackParamList, 'ReKyc'>;
type IconName = React.ComponentProps<typeof Ionicons>['name'];
type IconName = React.ComponentProps<typeof Icon>['name'];
type ChangeField = 'name' | 'mobile' | 'email' | 'address';
type ChangeOption = {
@@ -102,10 +102,10 @@ export default function ReKycScreen({ route, navigation }: Props) {
<View
style={[
styles.emptyIcon,
{ backgroundColor: `${theme.colors.danger}12` },
{ backgroundColor: theme.tint(theme.colors.danger, 0.07) },
]}
>
<Ionicons name="alert-circle-outline" size={28} color={theme.colors.danger} />
<Icon name="alert-circle-outline" size={28} color={theme.colors.danger} />
</View>
<Text style={[styles.emptyTitle, { color: theme.colors.textPrimary }]}>
Account not found
@@ -212,7 +212,7 @@ export default function ReKycScreen({ route, navigation }: Props) {
contentContainerStyle={styles.contentContainer}
>
<LinearGradient
colors={['#F4F9FF', '#E8F2FE', '#F8FBFF']}
colors={[theme.colors.surface, theme.colors.primarySurface, theme.colors.surface]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={[styles.heroCard, { borderColor: theme.colors.cardBorder }]}
@@ -221,10 +221,10 @@ export default function ReKycScreen({ route, navigation }: Props) {
<View
style={[
styles.heroIcon,
{ backgroundColor: `${theme.colors.primary}12` },
{ backgroundColor: theme.tint(theme.colors.primary, 0.07) },
]}
>
<Ionicons name="shield-checkmark-outline" size={20} color={theme.colors.primary} />
<Icon name="shield-checkmark-outline" size={20} color={theme.colors.primary} />
</View>
<View style={styles.heroCopy}>
<Text style={[styles.heroTitle, { color: theme.colors.textPrimary }]}>
@@ -242,7 +242,7 @@ export default function ReKycScreen({ route, navigation }: Props) {
<View
style={[
styles.stepIndex,
{ backgroundColor: `${theme.colors.primary}12` },
{ backgroundColor: theme.tint(theme.colors.primary, 0.07) },
]}
>
<Text style={[styles.stepIndexText, { color: theme.colors.primary }]}>
@@ -260,10 +260,10 @@ export default function ReKycScreen({ route, navigation }: Props) {
<View
style={[
styles.sectionIcon,
{ backgroundColor: `${theme.colors.info}12` },
{ backgroundColor: theme.tint(theme.colors.info, 0.07) },
]}
>
<Ionicons name="card-outline" size={18} color={theme.colors.info} />
<Icon name="card-outline" size={18} color={theme.colors.info} />
</View>
<View style={styles.sectionCopy}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>
@@ -282,7 +282,7 @@ export default function ReKycScreen({ route, navigation }: Props) {
keyboardType="number-pad"
maxLength={14}
placeholder="1234 5678 9012"
leftAccessory={<Ionicons name="finger-print-outline" size={18} color={theme.colors.textMuted} />}
leftAccessory={<Icon name="finger-print-outline" size={18} color={theme.colors.textMuted} />}
/>
</Card>
@@ -291,10 +291,10 @@ export default function ReKycScreen({ route, navigation }: Props) {
<View
style={[
styles.sectionIcon,
{ backgroundColor: `${theme.colors.warning}12` },
{ backgroundColor: theme.tint(theme.colors.warning, 0.07) },
]}
>
<Ionicons name="create-outline" size={18} color={theme.colors.warning} />
<Icon name="create-outline" size={18} color={theme.colors.warning} />
</View>
<View style={styles.sectionCopy}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>
@@ -317,12 +317,12 @@ export default function ReKycScreen({ route, navigation }: Props) {
style={[
styles.changeChip,
{
backgroundColor: isSelected ? `${theme.colors.primary}12` : theme.colors.surfaceMuted,
borderColor: isSelected ? `${theme.colors.primary}45` : theme.colors.cardBorder,
backgroundColor: isSelected ? theme.tint(theme.colors.primary, 0.07) : theme.colors.surfaceMuted,
borderColor: isSelected ? theme.tint(theme.colors.primary, 0.27) : theme.colors.cardBorder,
},
]}
>
<Ionicons
<Icon
name={option.icon as any}
size={16}
color={isSelected ? theme.colors.primary : theme.colors.textSecondary}
@@ -348,7 +348,7 @@ export default function ReKycScreen({ route, navigation }: Props) {
{ backgroundColor: theme.colors.surfaceMuted, borderColor: theme.colors.cardBorder },
]}
>
<Ionicons
<Icon
name={consentGiven ? 'checkmark-circle' : 'ellipse-outline'}
size={22}
color={consentGiven ? theme.colors.success : theme.colors.textMuted}
@@ -404,8 +404,8 @@ export default function ReKycScreen({ route, navigation }: Props) {
{/* Document upload */}
<Card style={styles.sectionCard}>
<View style={styles.sectionHeader}>
<View style={[styles.sectionIcon, { backgroundColor: `${theme.colors.success}12` }]}>
<Ionicons name="cloud-upload-outline" size={18} color={theme.colors.success} />
<View style={[styles.sectionIcon, { backgroundColor: theme.tint(theme.colors.success, 0.07) }]}>
<Icon name="cloud-upload-outline" size={18} color={theme.colors.success} />
</View>
<View style={styles.sectionCopy}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>Upload documents</Text>
@@ -424,13 +424,13 @@ export default function ReKycScreen({ route, navigation }: Props) {
style={[
styles.uploadRow,
{
backgroundColor: isUploaded ? `${theme.colors.success}10` : theme.colors.surfaceMuted,
borderColor: isUploaded ? `${theme.colors.success}45` : theme.colors.cardBorder,
backgroundColor: isUploaded ? theme.tint(theme.colors.success, 0.06) : theme.colors.surfaceMuted,
borderColor: isUploaded ? theme.tint(theme.colors.success, 0.27) : theme.colors.cardBorder,
},
]}
>
<View style={[styles.uploadIcon, { backgroundColor: theme.colors.surface }]}>
<Ionicons name={doc.icon} size={18} color={theme.colors.primary} />
<Icon name={doc.icon} size={18} color={theme.colors.primary} />
</View>
<View style={styles.uploadCopy}>
<Text style={[styles.uploadLabel, { color: theme.colors.textPrimary }]}>{doc.label}</Text>
@@ -438,7 +438,7 @@ export default function ReKycScreen({ route, navigation }: Props) {
{isUploaded ? 'Uploaded · tap to remove' : doc.hint}
</Text>
</View>
<Ionicons
<Icon
name={isUploaded ? 'checkmark-circle' : 'add-circle-outline'}
size={22}
color={isUploaded ? theme.colors.success : theme.colors.primary}
@@ -451,8 +451,8 @@ export default function ReKycScreen({ route, navigation }: Props) {
{/* Latest photograph */}
<Card style={styles.sectionCard}>
<View style={styles.sectionHeader}>
<View style={[styles.sectionIcon, { backgroundColor: `${theme.colors.info}12` }]}>
<Ionicons name="camera-outline" size={18} color={theme.colors.info} />
<View style={[styles.sectionIcon, { backgroundColor: theme.tint(theme.colors.info, 0.07) }]}>
<Icon name="camera-outline" size={18} color={theme.colors.info} />
</View>
<View style={styles.sectionCopy}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>Latest photograph</Text>
@@ -467,12 +467,12 @@ export default function ReKycScreen({ route, navigation }: Props) {
style={[
styles.photoTile,
{
backgroundColor: photoUploaded ? `${theme.colors.success}10` : theme.colors.surfaceMuted,
borderColor: photoUploaded ? `${theme.colors.success}45` : theme.colors.cardBorder,
backgroundColor: photoUploaded ? theme.tint(theme.colors.success, 0.06) : theme.colors.surfaceMuted,
borderColor: photoUploaded ? theme.tint(theme.colors.success, 0.27) : theme.colors.cardBorder,
},
]}
>
<Ionicons
<Icon
name={photoUploaded ? 'checkmark-circle' : 'person-circle-outline'}
size={48}
color={photoUploaded ? theme.colors.success : theme.colors.textMuted}
@@ -486,8 +486,8 @@ export default function ReKycScreen({ route, navigation }: Props) {
{/* Self declaration */}
<Card style={styles.sectionCard}>
<View style={styles.sectionHeader}>
<View style={[styles.sectionIcon, { backgroundColor: `${theme.colors.warning}12` }]}>
<Ionicons name="document-text-outline" size={18} color={theme.colors.warning} />
<View style={[styles.sectionIcon, { backgroundColor: theme.tint(theme.colors.warning, 0.07) }]}>
<Icon name="document-text-outline" size={18} color={theme.colors.warning} />
</View>
<View style={styles.sectionCopy}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>Self declaration</Text>
@@ -508,7 +508,7 @@ export default function ReKycScreen({ route, navigation }: Props) {
{ backgroundColor: theme.colors.surfaceMuted, borderColor: theme.colors.cardBorder },
]}
>
<Ionicons
<Icon
name={accepted ? 'checkmark-circle' : 'ellipse-outline'}
size={22}
color={accepted ? theme.colors.success : theme.colors.textMuted}
@@ -522,8 +522,8 @@ export default function ReKycScreen({ route, navigation }: Props) {
{/* Video KYC option */}
<Card style={styles.sectionCard}>
<View style={styles.sectionHeader}>
<View style={[styles.sectionIcon, { backgroundColor: `${theme.colors.primary}12` }]}>
<Ionicons name="videocam-outline" size={18} color={theme.colors.primary} />
<View style={[styles.sectionIcon, { backgroundColor: theme.tint(theme.colors.primary, 0.07) }]}>
<Icon name="videocam-outline" size={18} color={theme.colors.primary} />
</View>
<View style={styles.sectionCopy}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>Prefer a video call?</Text>
@@ -536,13 +536,13 @@ export default function ReKycScreen({ route, navigation }: Props) {
title="Start Video KYC"
variant="secondary"
onPress={() => account && navigation.navigate('VideoKyc', { accountNumber: account.accountNumber })}
leftAccessory={<Ionicons name="videocam-outline" size={18} color={theme.colors.textPrimary} />}
leftAccessory={<Icon name="videocam-outline" size={18} color={theme.colors.textPrimary} />}
/>
</Card>
<Card style={styles.footerCard}>
<View style={styles.footerHeader}>
<Ionicons name="time-outline" size={18} color={theme.colors.primary} />
<Icon name="time-outline" size={18} color={theme.colors.primary} />
<Text style={[styles.footerTitle, { color: theme.colors.textPrimary }]}>
Processing note
</Text>
@@ -553,7 +553,7 @@ export default function ReKycScreen({ route, navigation }: Props) {
<Button
title="Submit re-KYC request"
onPress={handleSubmit}
leftAccessory={<Ionicons name="paper-plane-outline" size={18} color={theme.colors.white} />}
leftAccessory={<Icon name="paper-plane-outline" size={18} color={theme.colors.white} />}
/>
</Card>
<View style={{ height: 60 }} />