Files
CBS-Mobile-App/src/modules/schemes/screens/SchemeDetailScreen.tsx
T
Abin ToppoandClaude Opus 5.5 f960a119e8 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>
2026-10-14 18:18:40 +05:30

272 lines
8.2 KiB
TypeScript

import React from 'react';
import { Share, StyleSheet, Text, View, Pressable, Linking } from 'react-native';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { Icon } from '../../../components/Icon';
import { Card, ScreenShell, Button } from '../../../components';
import { ServicesStackParamList } from '../../../navigation/types';
import { useTheme, fontFamily } from '../../../theme';
import { useSchemes } from '../hooks/useSchemes';
import { Accordion } from '../components/Accordion';
type Props = NativeStackScreenProps<ServicesStackParamList, 'SchemeDetails'>;
export default function SchemeDetailScreen({ route, navigation }: Props) {
const { schemeId } = route.params;
const theme = useTheme();
const { schemes, bookmarks, toggleBookmark } = useSchemes();
const scheme = schemes.find((s) => s.id === schemeId);
const isBookmarked = bookmarks.includes(schemeId);
if (!scheme) {
return (
<ScreenShell title="Not Found">
<View style={styles.errorContainer}>
<Text style={{ color: theme.colors.textPrimary }}>Scheme not found.</Text>
</View>
</ScreenShell>
);
}
const handleShare = async () => {
try {
await Share.share({
message: `${scheme.title}\n\n${scheme.description}\n\nLearn more on BankCBS!`,
title: scheme.title,
});
} catch (error) {
console.error('Error sharing scheme', error);
}
};
const handleApply = async () => {
if (scheme.isNew || scheme.type === 'GOVT_SCHEME') {
navigation.navigate('SchemeEnrollment', { schemeId });
} else if (scheme.applyUrl) {
const supported = await Linking.canOpenURL(scheme.applyUrl);
if (supported) {
await Linking.openURL(scheme.applyUrl);
}
}
};
const rightContent = (
<View style={styles.headerActions}>
<Pressable onPress={handleShare} style={[styles.actionBtn, { backgroundColor: theme.colors.surfaceMuted }]}>
<Icon name="share-social-outline" size={20} color={theme.colors.textPrimary} />
</Pressable>
<Pressable
onPress={() => toggleBookmark(schemeId)}
style={[styles.actionBtn, { backgroundColor: isBookmarked ? theme.tint(theme.colors.primary, 0.08) : theme.colors.surfaceMuted }]}
>
<Icon
name={isBookmarked ? 'bookmark' : 'bookmark-outline'}
size={20}
color={isBookmarked ? theme.colors.primary : theme.colors.textPrimary}
/>
</Pressable>
</View>
);
return (
<ScreenShell
title="Details"
subtitle={scheme.type.replace('_', ' ')}
rightContent={rightContent}
>
<View style={styles.container}>
<View style={styles.metaRow}>
<View style={[styles.typeBadge, { backgroundColor: theme.tint(theme.colors.primary, 0.07) }]}>
<Text style={[styles.typeText, { color: theme.colors.primary }]}>{scheme.type.replace('_', ' ')}</Text>
</View>
<View style={[styles.categoryBadge, { backgroundColor: theme.colors.surfaceMuted }]}>
<Text style={[styles.categoryText, { color: theme.colors.textSecondary }]}>{scheme.category}</Text>
</View>
</View>
<Text style={[styles.title, { color: theme.colors.textPrimary }]}>{scheme.title}</Text>
<Card variant="glass" style={styles.overviewCard}>
<Text style={[styles.description, { color: theme.colors.textSecondary }]}>
{scheme.description}
</Text>
</Card>
<View style={styles.section}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>About the Scheme</Text>
<Text style={[styles.contentText, { color: theme.colors.textPrimary }]}>
{scheme.content}
</Text>
</View>
{scheme.benefits && (
<View style={styles.section}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>Key Benefits</Text>
{scheme.benefits.map((benefit, idx) => (
<View key={idx} style={styles.listRow}>
<Icon name="checkmark-circle" size={18} color={theme.colors.success} />
<Text style={[styles.listText, { color: theme.colors.textSecondary }]}>{benefit}</Text>
</View>
))}
</View>
)}
{scheme.eligibility && (
<View style={styles.section}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>Eligibility Criteria</Text>
{scheme.eligibility.map((criterion, idx) => (
<View key={idx} style={styles.listRow}>
<Icon name="information-circle-outline" size={18} color={theme.colors.primary} />
<Text style={[styles.listText, { color: theme.colors.textSecondary }]}>{criterion}</Text>
</View>
))}
</View>
)}
{scheme.premiumInfo && (
<View style={styles.section}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>Premium & Contributions</Text>
<View style={[styles.highlightCard, { backgroundColor: theme.colors.surfaceMuted }]}>
<Text style={[styles.highlightText, { color: theme.colors.textPrimary }]}>{scheme.premiumInfo}</Text>
</View>
</View>
)}
{scheme.faqs && scheme.faqs.length > 0 && (
<View style={styles.section}>
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>Frequently Asked Questions</Text>
{scheme.faqs.map((faq, idx) => (
<Accordion key={idx} title={faq.question}>
<Text style={[styles.faqAnswer, { color: theme.colors.textSecondary }]}>
{faq.answer}
</Text>
</Accordion>
))}
</View>
)}
{scheme.applyUrl && (
<View style={styles.footerAction}>
<Button
title={scheme.type === 'GOVT_SCHEME' ? "Enroll Now" : "Apply Now"}
onPress={handleApply}
/>
<Text style={[styles.disclaimer, { color: theme.colors.textMuted }]}>
{scheme.type === 'GOVT_SCHEME'
? "* Enrolling digitally will trigger an OTP verification sent to your registered mobile."
: "* Clicking Apply Now will redirect you to the official government portal."}
</Text>
</View>
)}
</View>
</ScreenShell>
);
}
const styles = StyleSheet.create({
container: {
paddingHorizontal: 16,
paddingBottom: 40,
},
headerActions: {
flexDirection: 'row',
gap: 8,
},
actionBtn: {
width: 40,
height: 40,
borderRadius: 12,
alignItems: 'center',
justifyContent: 'center',
},
metaRow: {
flexDirection: 'row',
gap: 8,
alignItems: 'center',
marginBottom: 12,
},
typeBadge: {
paddingHorizontal: 10,
paddingVertical: 4,
borderRadius: 8,
},
typeText: {
fontSize: 11,
fontFamily: fontFamily.bold,
letterSpacing: 0.1,
},
categoryBadge: {
paddingHorizontal: 10,
paddingVertical: 4,
borderRadius: 8,
},
categoryText: {
fontSize: 11,
fontFamily: fontFamily.bold,
},
title: {
fontSize: 24,
fontFamily: fontFamily.bold,
lineHeight: 30,
marginBottom: 16,
},
overviewCard: {
padding: 16,
marginBottom: 24,
},
description: {
fontSize: 15,
lineHeight: 22,
fontFamily: fontFamily.medium,
},
section: {
marginBottom: 28,
},
sectionTitle: {
fontSize: 18,
fontFamily: fontFamily.bold,
marginBottom: 12,
},
contentText: {
fontSize: 16,
lineHeight: 24,
},
listRow: {
flexDirection: 'row',
gap: 12,
marginBottom: 10,
alignItems: 'flex-start',
},
listText: {
fontSize: 15,
lineHeight: 20,
flex: 1,
},
highlightCard: {
padding: 16,
borderRadius: 12,
},
highlightText: {
fontSize: 15,
fontFamily: fontFamily.semibold,
},
faqAnswer: {
fontSize: 14,
lineHeight: 20,
},
footerAction: {
marginTop: 10,
gap: 12,
},
disclaimer: {
fontSize: 11,
textAlign: 'center',
fontStyle: 'italic',
},
errorContainer: {
padding: 20,
alignItems: 'center',
},
});