import React, { useEffect, useMemo, useState } from 'react'; import { StyleSheet, Switch, Text, View } from 'react-native'; import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import { AccountSelector, Button, Card, Input, ScreenShell } from '../../../components'; import { useAppSelector } from '../../../app/hooks'; import { selectBeneficiaryById } from '../store/beneficiariesSlice'; import { useTransferForm } from '../hooks/useTransferForm'; import { TransferAmountFields } from '../components/TransferAmountFields'; import { FeatureKey } from '../../../constants/featureKeys'; import { useFeature } from '../../../features'; import { isValidAccountNumber, isValidIfsc } from '../../../utils/validation'; import { useTheme, fontFamily } from '../../../theme'; import type { PaymentsStackParamList } from '../../../navigation/types'; type Props = NativeStackScreenProps; /** Rails this screen can offer, narrowest description first. */ const RAILS = [ { id: 'IMPS' as const, feature: FeatureKey.PAYMENTS_TRANSFER_IMPS, label: 'IMPS', hint: 'Instant, 24×7' }, { id: 'NEFT' as const, feature: FeatureKey.PAYMENTS_TRANSFER_NEFT, label: 'NEFT', hint: 'Half-hourly batches' }, { id: 'RTGS' as const, feature: FeatureKey.PAYMENTS_TRANSFER_RTGS, label: 'RTGS', hint: 'High value, same day' }, ]; /** Below this an RTGS transfer is refused by the network, not by us. */ const RTGS_MINIMUM = 200000; /** * Send to an account number and IFSC. * * The account number is asked for twice. It is the one field on this screen * with no check digit and no lookup behind it — an IFSC is structured and a * name comes back from the bank, but a mistyped account number is a valid * account number belonging to someone else. */ export default function TransferToAccountScreen({ navigation, route }: Props) { const theme = useTheme(); const prefillId = route.params?.beneficiaryId; const beneficiary = useAppSelector(selectBeneficiaryById(prefillId ?? '')); const impsEnabled = useFeature(FeatureKey.PAYMENTS_TRANSFER_IMPS); const neftEnabled = useFeature(FeatureKey.PAYMENTS_TRANSFER_NEFT); const rtgsEnabled = useFeature(FeatureKey.PAYMENTS_TRANSFER_RTGS); const availableRails = useMemo( () => RAILS.filter((rail) => rail.id === 'IMPS' ? impsEnabled : rail.id === 'NEFT' ? neftEnabled : rtgsEnabled, ), [impsEnabled, neftEnabled, rtgsEnabled], ); const { accounts, sourceAccountNumber, setSourceAccountNumber, amount, handleAmountChange, parsedAmount, amountError, amountValid, remarks, setRemarks, } = useTransferForm(); const [name, setName] = useState(''); const [accountNumber, setAccountNumber] = useState(''); const [confirmAccountNumber, setConfirmAccountNumber] = useState(''); const [ifsc, setIfsc] = useState(''); const [rail, setRail] = useState<'IMPS' | 'NEFT' | 'RTGS'>(availableRails[0]?.id ?? 'IMPS'); const [saveAsBeneficiary, setSaveAsBeneficiary] = useState(false); // Keep the choice on a rail that is actually offered — a switch flipped in // remote config must not leave a disabled rail selected underneath. useEffect(() => { const fallback = availableRails[0]; if (fallback && !availableRails.some((entry) => entry.id === rail)) { setRail(fallback.id); } }, [availableRails, rail]); useEffect(() => { if (!beneficiary) { return; } setName(beneficiary.name); setIfsc(beneficiary.ifsc ?? ''); // A saved payee is stored masked, so there is no number to prefill. The // customer confirms it once and it is theirs to keep. setSaveAsBeneficiary(false); }, [beneficiary]); const accountError = accountNumber.length > 0 && !isValidAccountNumber(accountNumber) ? 'Enter a 9-18 digit account number' : undefined; const confirmError = confirmAccountNumber.length > 0 && confirmAccountNumber !== accountNumber ? 'Both entries must match' : undefined; const ifscError = ifsc.length > 0 && !isValidIfsc(ifsc) ? 'Enter a valid IFSC code' : undefined; /** The only rail on offer, when there is exactly one. */ const soleRail = availableRails.length === 1 ? availableRails[0] : undefined; const railError = rail === 'RTGS' && amountValid && parsedAmount < RTGS_MINIMUM ? `RTGS requires at least ₹${RTGS_MINIMUM.toLocaleString('en-IN')}` : undefined; const canContinue = Boolean(sourceAccountNumber) && name.trim().length > 0 && isValidAccountNumber(accountNumber) && confirmAccountNumber === accountNumber && isValidIfsc(ifsc) && amountValid && !railError; const handleContinue = () => { if (!canContinue || !sourceAccountNumber) { return; } navigation.navigate('PaymentConfirmation', { transferType: rail, amount: parsedAmount, remarks: remarks || undefined, sourceAccountNumber, beneficiaryId: prefillId, saveRecipient: saveAsBeneficiary, recipient: { name: name.trim(), // Only the last four ever reach the receipt or the payee list. accountMasked: `XXXX ${accountNumber.slice(-4)}`, accountNumber, ifsc: ifsc.toUpperCase(), }, }); }; return (