- Global feature search for the user app, wired to the home search icon - Notifications screen: swipe-to-delete, vanish animation, unread badge, and Notifee-backed foreground + background (scheduled) OS notifications - Fix Add Beneficiary save (single source of truth via beneficiaries slice) - Fix Cards tab scroll freeze and bottom blank space (use ScreenShell scroll) - User-friendly toast copy across the app - Android build tuning: parallel/caching/JVM G1 args, mobile-only ABIs Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
261 lines
7.3 KiB
TypeScript
261 lines
7.3 KiB
TypeScript
import React, { useMemo } from 'react';
|
|
import { StyleSheet, Text, View } from 'react-native';
|
|
import type { CompositeScreenProps } from '@react-navigation/native';
|
|
import type { BottomTabScreenProps } from '@react-navigation/bottom-tabs';
|
|
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
|
|
import Ionicons from '@react-native-vector-icons/ionicons';
|
|
|
|
import { useAppSelector } from '../../../app/hooks';
|
|
import { Card, ScreenShell } from '../../../components';
|
|
import { useToast } from '../../../hooks/useToast';
|
|
import { ServiceGridItem } from '../../home/components/ServiceGridItem';
|
|
import { ServiceStatusPill } from '../components/ServiceStatusPill';
|
|
import { selectServiceRequests } from '../store/serviceSelectors';
|
|
import type { MainTabParamList, ServicesStackParamList } from '../../../navigation/types';
|
|
import { useTheme } from '../../../theme';
|
|
|
|
type Props = CompositeScreenProps<
|
|
NativeStackScreenProps<ServicesStackParamList, 'ServicesHub'>,
|
|
BottomTabScreenProps<MainTabParamList, 'Services'>
|
|
>;
|
|
|
|
export default function ServicesHubScreen({ navigation }: Props) {
|
|
const theme = useTheme();
|
|
const toast = useToast();
|
|
const serviceRequests = useAppSelector(selectServiceRequests);
|
|
|
|
const latestRequest = serviceRequests[0] ?? null;
|
|
const openRequestsCount = serviceRequests.filter(
|
|
(request) => request.status !== 'resolved',
|
|
).length;
|
|
|
|
const serviceOptions = useMemo(
|
|
() => [
|
|
{
|
|
id: 'branch_locator',
|
|
title: 'Find Branch & ATMs',
|
|
icon: 'map-outline',
|
|
iconColor: '#10B981',
|
|
onPress: () => navigation.navigate('BranchLocator'),
|
|
},
|
|
{
|
|
id: 'all_deposits',
|
|
title: 'Deposits',
|
|
icon: 'wallet-outline',
|
|
iconColor: '#124577',
|
|
onPress: () => navigation.navigate('DepositsOverview'),
|
|
},
|
|
{
|
|
id: 'cheque_services',
|
|
title: 'Cheque Services',
|
|
icon: 'book-outline',
|
|
iconColor: '#3B82F6',
|
|
onPress: () => navigation.navigate('ChequeServicesHub'),
|
|
},
|
|
{
|
|
id: 'certificates',
|
|
title: 'Certificates',
|
|
icon: 'document-text-outline',
|
|
iconColor: '#F59E0B',
|
|
onPress: () => navigation.navigate('CertificatesHub'),
|
|
},
|
|
{
|
|
id: 'requests',
|
|
title: 'Service Requests',
|
|
icon: 'git-pull-request-outline',
|
|
iconColor: '#EF4444',
|
|
onPress: () => navigation.navigate('ServiceRequestsHub'),
|
|
},
|
|
{
|
|
id: 'calculator',
|
|
title: 'FD/RD Calc',
|
|
icon: 'calculator-outline',
|
|
iconColor: '#6366F1',
|
|
onPress: () => navigation.navigate('DepositCalculator'),
|
|
},
|
|
{
|
|
id: 'support',
|
|
title: 'Support Chat',
|
|
icon: 'chatbubble-ellipses-outline',
|
|
iconColor: '#0EA5E9',
|
|
onPress: () =>
|
|
toast(
|
|
'Support chat coming soon',
|
|
'info',
|
|
'This feature will be available in the next update.',
|
|
),
|
|
},
|
|
{
|
|
id: 'help',
|
|
title: 'Help Center',
|
|
icon: 'help-circle-outline',
|
|
iconColor: '#8B5CF6',
|
|
onPress: () =>
|
|
toast('Help center coming soon', 'info', 'This feature will be available shortly.'),
|
|
},
|
|
{
|
|
id: 'cyber_security',
|
|
title: 'Cyber Security',
|
|
icon: 'shield-half-outline',
|
|
iconColor: '#F59E0B',
|
|
onPress: () => navigation.navigate('AwarenessList'),
|
|
},
|
|
{
|
|
id: 'protection_schemes',
|
|
title: 'Protection & Schemes',
|
|
icon: 'umbrella-outline',
|
|
iconColor: '#EC4899',
|
|
onPress: () => navigation.navigate('FinancialProtectionMain'),
|
|
},
|
|
],
|
|
[navigation, toast]
|
|
);
|
|
|
|
return (
|
|
<ScreenShell title="Service desk" subtitle="All service options in one hub">
|
|
{/* <View style={styles.sectionHeader}>
|
|
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>
|
|
Service options
|
|
</Text>
|
|
<Text style={[styles.sectionSubtitle, { color: theme.colors.textSecondary }]}>
|
|
Choose a service card to continue
|
|
</Text>
|
|
</View> */}
|
|
|
|
<View style={styles.grid}>
|
|
{serviceOptions.map((option) => (
|
|
<ServiceGridItem
|
|
key={option.id}
|
|
id={option.id}
|
|
title={option.title}
|
|
icon={option.icon}
|
|
iconColor={option.iconColor}
|
|
onPress={option.onPress}
|
|
/>
|
|
))}
|
|
</View>
|
|
|
|
<Card variant="glass" style={styles.heroCard}>
|
|
<View style={styles.heroHeader}>
|
|
<View
|
|
style={[
|
|
styles.heroIcon,
|
|
{ backgroundColor: `${theme.colors.primary}12` },
|
|
]}
|
|
>
|
|
<Ionicons name="grid-outline" size={18} color={theme.colors.primary} />
|
|
</View>
|
|
<View style={styles.heroCopy}>
|
|
<Text style={[styles.heroTitle, { color: theme.colors.textPrimary }]}>
|
|
Services hub
|
|
</Text>
|
|
<Text style={[styles.heroSubtitle, { color: theme.colors.textSecondary }]}>
|
|
Browse deposit journeys, service support actions, and request tools from one place.
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
|
|
<View style={styles.heroStats}>
|
|
<View style={[styles.heroStat, { backgroundColor: theme.colors.surfaceMuted }]}>
|
|
<Text style={[styles.heroStatLabel, { color: theme.colors.textSecondary }]}>
|
|
Open requests
|
|
</Text>
|
|
<Text style={[styles.heroStatValue, { color: theme.colors.textPrimary }]}>
|
|
{openRequestsCount}
|
|
</Text>
|
|
</View>
|
|
<View style={[styles.heroStat, { backgroundColor: theme.colors.surfaceMuted }]}>
|
|
<Text style={[styles.heroStatLabel, { color: theme.colors.textSecondary }]}>
|
|
Latest status
|
|
</Text>
|
|
{latestRequest ? (
|
|
<ServiceStatusPill status={latestRequest.status} />
|
|
) : (
|
|
<Text style={[styles.heroStatEmpty, { color: theme.colors.textMuted }]}>
|
|
No requests
|
|
</Text>
|
|
)}
|
|
</View>
|
|
</View>
|
|
</Card>
|
|
<View style={styles.bottomSpacer} />
|
|
</ScreenShell>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
heroCard: {
|
|
gap: 16,
|
|
shadowOpacity: 0.04,
|
|
shadowRadius: 14,
|
|
elevation: 2,
|
|
},
|
|
heroHeader: {
|
|
flexDirection: 'row',
|
|
gap: 12,
|
|
},
|
|
heroIcon: {
|
|
width: 42,
|
|
height: 42,
|
|
borderRadius: 14,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
heroCopy: {
|
|
flex: 1,
|
|
gap: 4,
|
|
},
|
|
heroTitle: {
|
|
fontSize: 18,
|
|
fontWeight: '800',
|
|
},
|
|
heroSubtitle: {
|
|
fontSize: 13,
|
|
lineHeight: 18,
|
|
},
|
|
heroStats: {
|
|
flexDirection: 'row',
|
|
gap: 12,
|
|
},
|
|
heroStat: {
|
|
flex: 1,
|
|
borderRadius: 18,
|
|
padding: 14,
|
|
gap: 6,
|
|
},
|
|
heroStatLabel: {
|
|
fontSize: 11,
|
|
fontWeight: '700',
|
|
textTransform: 'uppercase',
|
|
letterSpacing: 0.8,
|
|
},
|
|
heroStatValue: {
|
|
fontSize: 22,
|
|
fontWeight: '900',
|
|
letterSpacing: -0.5,
|
|
},
|
|
heroStatEmpty: {
|
|
fontSize: 13,
|
|
fontWeight: '600',
|
|
},
|
|
sectionHeader: {
|
|
gap: 4,
|
|
},
|
|
sectionTitle: {
|
|
fontSize: 18,
|
|
fontWeight: '800',
|
|
},
|
|
sectionSubtitle: {
|
|
fontSize: 13,
|
|
lineHeight: 18,
|
|
},
|
|
grid: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
justifyContent: 'space-between',
|
|
},
|
|
bottomSpacer: {
|
|
height: 60,
|
|
},
|
|
});
|