Update CBS Mobile App codebase
This commit is contained in:
@@ -5,7 +5,7 @@ import Ionicons from '@react-native-vector-icons/ionicons';
|
||||
import Animated, { FadeInRight, FadeOutLeft } from 'react-native-reanimated';
|
||||
|
||||
import { Input, Button, DatePicker, KeyboardAwareScrollView } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import { TAB_BAR_HEIGHT } from '../../../utils/layoutConstants';
|
||||
import type { AgentLoansStackParamList } from '../navigation/agentNavigationTypes';
|
||||
import { useAccountOpeningForm } from '../hooks/useAccountOpeningForm';
|
||||
@@ -33,7 +33,7 @@ const StepIndicator = ({ steps, currentStepIndex }: { steps: any[], currentStepI
|
||||
{i < currentStepIndex ? (
|
||||
<Ionicons name="checkmark" size={14} color={theme.colors.white} />
|
||||
) : (
|
||||
<Text style={{ color: isPassed ? theme.colors.white : theme.colors.textMuted, fontSize: 12, fontWeight: '700' }}>{i + 1}</Text>
|
||||
<Text style={{ color: isPassed ? theme.colors.white : theme.colors.textMuted, fontSize: 12, fontFamily: fontFamily.bold }}>{i + 1}</Text>
|
||||
)}
|
||||
</View>
|
||||
{i < steps.length - 1 && <View style={[styles.stepLine, { backgroundColor: i < currentStepIndex ? theme.colors.primary : theme.colors.cardBorder }]} />}
|
||||
@@ -48,7 +48,7 @@ const StepIndicator = ({ steps, currentStepIndex }: { steps: any[], currentStepI
|
||||
style={[
|
||||
styles.stepLabel,
|
||||
{ color: i <= currentStepIndex ? theme.colors.primary : theme.colors.textMuted },
|
||||
i === currentStepIndex && { fontWeight: '700' },
|
||||
i === currentStepIndex && { fontFamily: fontFamily.bold },
|
||||
]}
|
||||
>
|
||||
{s.label}
|
||||
@@ -231,7 +231,7 @@ export default function AccountOpeningScreen({ navigation }: { navigation: Nativ
|
||||
value={draft.initialDeposit}
|
||||
onChangeText={v => updateField('initialDeposit', v)}
|
||||
keyboardType="number-pad"
|
||||
leftAccessory={<Text style={{ fontSize: 16, fontWeight: '700', color: theme.colors.textPrimary }}>₹</Text>}
|
||||
leftAccessory={<Text style={{ fontSize: 16, fontFamily: fontFamily.bold, color: theme.colors.textPrimary }}>₹</Text>}
|
||||
/>
|
||||
{(draft.accountType === 'RD' || draft.accountType === 'DDS') && (
|
||||
<Input
|
||||
@@ -240,7 +240,7 @@ export default function AccountOpeningScreen({ navigation }: { navigation: Nativ
|
||||
value={draft.monthlyInstallment}
|
||||
onChangeText={v => updateField('monthlyInstallment', v)}
|
||||
keyboardType="number-pad"
|
||||
leftAccessory={<Text style={{ fontSize: 16, fontWeight: '700', color: theme.colors.textPrimary }}>₹</Text>}
|
||||
leftAccessory={<Text style={{ fontSize: 16, fontFamily: fontFamily.bold, color: theme.colors.textPrimary }}>₹</Text>}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
@@ -295,7 +295,7 @@ export default function AccountOpeningScreen({ navigation }: { navigation: Nativ
|
||||
|
||||
<View style={styles.reviewRow}>
|
||||
<Text style={[styles.reviewLabel, { color: theme.colors.textMuted }]}>Initial Deposit</Text>
|
||||
<Text style={[styles.reviewValue, { color: theme.colors.success, fontWeight: '800', fontSize: 16 }]}>
|
||||
<Text style={[styles.reviewValue, { color: theme.colors.success, fontFamily: fontFamily.bold, fontSize: 16 }]}>
|
||||
{formatAgentCurrency(Number(draft.initialDeposit))}
|
||||
</Text>
|
||||
</View>
|
||||
@@ -361,7 +361,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
localHeaderTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
// ── Step indicator ─────────────────────────
|
||||
@@ -401,7 +401,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
stepLabel: {
|
||||
fontSize: 10,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.3,
|
||||
width: 54,
|
||||
@@ -418,7 +418,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
stepTitle: {
|
||||
fontSize: 20,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
marginBottom: 4,
|
||||
},
|
||||
stepSubtitle: {
|
||||
@@ -448,7 +448,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
typeLabel: {
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
marginBottom: 4,
|
||||
},
|
||||
typeDesc: {
|
||||
@@ -477,7 +477,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
sectionDividerText: {
|
||||
fontSize: 12,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
paddingHorizontal: 12,
|
||||
@@ -502,7 +502,7 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
fontSize: 13,
|
||||
lineHeight: 18,
|
||||
fontWeight: '500',
|
||||
fontFamily: fontFamily.medium,
|
||||
},
|
||||
|
||||
// ── Review card ───────────────────────────
|
||||
@@ -519,11 +519,11 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
reviewLabel: {
|
||||
fontSize: 13,
|
||||
fontWeight: '500',
|
||||
fontFamily: fontFamily.medium,
|
||||
},
|
||||
reviewValue: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
textAlign: 'right',
|
||||
flexShrink: 1,
|
||||
marginLeft: 12,
|
||||
|
||||
@@ -5,12 +5,14 @@ import Ionicons from '@react-native-vector-icons/ionicons';
|
||||
import Animated, { FadeInUp } from 'react-native-reanimated';
|
||||
|
||||
import { ScreenShell, Input, Button, KeyboardAwareScrollView } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import { TAB_BAR_HEIGHT } from '../../../utils/layoutConstants';
|
||||
import { useAppSelector } from '../../../app/hooks';
|
||||
import { useToast } from '../../../hooks/useToast';
|
||||
import { selectCustomerById } from '../store/agentSelectors';
|
||||
import type { AgentCustomersStackParamList } from '../navigation/agentNavigationTypes';
|
||||
import { useCustomerForm } from '../hooks/useCustomerForm';
|
||||
import { useLazyGetCustomerByClientIdQuery } from '../services/agentApi';
|
||||
|
||||
type Props = NativeStackScreenProps<AgentCustomersStackParamList, 'AddEditCustomer'>;
|
||||
|
||||
@@ -18,7 +20,7 @@ const FormSection = ({ title, icon, children }: { title: string, icon: string, c
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Animated.View entering={FadeInUp} style={[styles.section, { backgroundColor: theme.colors.surface, borderColor: theme.colors.cardBorder }]}>
|
||||
<View style={styles.sectionHeader}>
|
||||
<View style={[styles.sectionHeader, { borderBottomColor: theme.colors.cardBorder }]}>
|
||||
<View style={[styles.sectionIcon, { backgroundColor: `${theme.colors.primary}15` }]}>
|
||||
<Ionicons name={icon as any} size={18} color={theme.colors.primary} />
|
||||
</View>
|
||||
@@ -39,7 +41,9 @@ export default function AddEditCustomerScreen({ route, navigation }: Props) {
|
||||
customerId ? selectCustomerById(customerId)(state) : null
|
||||
);
|
||||
|
||||
const { data, errors, updateField, submit, isSubmitting } = useCustomerForm(existingCustomer || undefined);
|
||||
const { data, errors, updateField, prefill, submit, isSubmitting } = useCustomerForm(existingCustomer || undefined);
|
||||
const toast = useToast();
|
||||
const [lookupClient, { isFetching: isLookingUp }] = useLazyGetCustomerByClientIdQuery();
|
||||
|
||||
const isEdit = !!existingCustomer;
|
||||
|
||||
@@ -50,6 +54,31 @@ export default function AddEditCustomerScreen({ route, navigation }: Props) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleFetchClient = async () => {
|
||||
const clientId = data.clientId.trim();
|
||||
if (!clientId) {
|
||||
toast('Enter a Client ID first', 'error');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const client = await lookupClient(clientId).unwrap();
|
||||
prefill({
|
||||
name: client.name,
|
||||
phone: client.phone,
|
||||
email: client.email,
|
||||
address: client.address,
|
||||
city: client.city,
|
||||
state: client.state,
|
||||
pincode: client.pincode,
|
||||
aadhaarNumber: client.aadhaarNumber,
|
||||
panNumber: client.panNumber,
|
||||
});
|
||||
toast('Details loaded', 'success', `${client.name}'s profile has been auto-filled.`);
|
||||
} catch {
|
||||
toast('Client not found', 'error', 'No client matches this Client ID.');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
@@ -57,6 +86,27 @@ export default function AddEditCustomerScreen({ route, navigation }: Props) {
|
||||
<View style={styles.flexContainer}>
|
||||
<KeyboardAwareScrollView contentContainerStyle={styles.scrollContent} style={styles.flex}>
|
||||
|
||||
<FormSection title="Client ID" icon="card-outline">
|
||||
<Text style={[styles.helperText, { color: theme.colors.textSecondary }]}>
|
||||
Have an existing Client ID? Enter it to auto-fill the customer's details.
|
||||
</Text>
|
||||
<Input
|
||||
label="Client ID (Optional)"
|
||||
placeholder="e.g. CL-100245"
|
||||
value={data.clientId}
|
||||
onChangeText={(v) => updateField('clientId', v)}
|
||||
autoCapitalize="characters"
|
||||
error={errors.clientId}
|
||||
/>
|
||||
<Button
|
||||
title="Fetch Details"
|
||||
variant="secondary"
|
||||
onPress={handleFetchClient}
|
||||
loading={isLookingUp}
|
||||
leftAccessory={<Ionicons name="search-outline" size={18} color={theme.colors.textPrimary} />}
|
||||
/>
|
||||
</FormSection>
|
||||
|
||||
<FormSection title="Personal Information" icon="person-outline">
|
||||
<Input
|
||||
label="Full Name"
|
||||
@@ -73,7 +123,7 @@ export default function AddEditCustomerScreen({ route, navigation }: Props) {
|
||||
keyboardType="number-pad"
|
||||
maxLength={10}
|
||||
error={errors.phone}
|
||||
leftAccessory={<Text style={{ color: theme.colors.textPrimary, fontWeight: 'bold' }}>+91</Text>}
|
||||
leftAccessory={<Text style={{ color: theme.colors.textPrimary, fontFamily: fontFamily.bold }}>+91</Text>}
|
||||
/>
|
||||
<Input
|
||||
label="Email Address (Optional)"
|
||||
@@ -183,7 +233,6 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
padding: 16,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: 'rgba(0,0,0,0.05)',
|
||||
},
|
||||
sectionIcon: {
|
||||
width: 32,
|
||||
@@ -195,13 +244,17 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 15,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
sectionContent: {
|
||||
padding: 16,
|
||||
paddingBottom: 16,
|
||||
gap: 16,
|
||||
},
|
||||
helperText: {
|
||||
fontSize: 13,
|
||||
lineHeight: 18,
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@ import Ionicons from '@react-native-vector-icons/ionicons';
|
||||
import Animated, { FadeIn } from 'react-native-reanimated';
|
||||
|
||||
import { ScreenShell } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import { useAppSelector, useAppDispatch } from '../../../app/hooks';
|
||||
import type { AgentTabParamList, AgentDashboardStackParamList } from '../navigation/agentNavigationTypes';
|
||||
import { useAgentDashboard } from '../hooks/useAgentDashboard';
|
||||
@@ -166,7 +166,7 @@ export default function AgentDashboardScreen({ navigation }: { navigation: Agent
|
||||
title="Today's Collection"
|
||||
value={formatAgentCurrency(stats.todayCollection)}
|
||||
icon="wallet-outline"
|
||||
gradient={['#2563EB', '#3B82F6']}
|
||||
gradient={['#2563EB', theme.colors.info]}
|
||||
onPress={goToCollections}
|
||||
/>
|
||||
<View style={{ width: 12 }} />
|
||||
@@ -175,7 +175,7 @@ export default function AgentDashboardScreen({ navigation }: { navigation: Agent
|
||||
title="This Week"
|
||||
value={formatAgentCurrency(stats.weeklyCollection)}
|
||||
icon="trending-up-outline"
|
||||
gradient={['#059669', '#10B981']}
|
||||
gradient={[theme.colors.success, theme.colors.success]}
|
||||
onPress={goToCollectionHistory}
|
||||
/>
|
||||
</View>
|
||||
@@ -196,7 +196,7 @@ export default function AgentDashboardScreen({ navigation }: { navigation: Agent
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={styles.chartDivider} />
|
||||
<View style={[styles.chartDivider, { backgroundColor: theme.colors.cardBorder }]} />
|
||||
|
||||
<Text style={[styles.chartSubtitle, { color: theme.colors.textMuted }]}>Last 7 Days Trend</Text>
|
||||
<AgentMiniChart
|
||||
@@ -253,12 +253,12 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
greeting: {
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
fontFamily: fontFamily.medium,
|
||||
marginBottom: 4,
|
||||
},
|
||||
name: {
|
||||
fontSize: 24,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
letterSpacing: -0.5,
|
||||
},
|
||||
avatarFrame: {
|
||||
@@ -292,22 +292,21 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
chartTitle: {
|
||||
fontSize: 12,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
marginBottom: 4,
|
||||
},
|
||||
chartValue: {
|
||||
fontSize: 22,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
letterSpacing: -0.5,
|
||||
},
|
||||
chartDivider: {
|
||||
height: 1,
|
||||
backgroundColor: 'rgba(0,0,0,0.05)',
|
||||
marginVertical: 16,
|
||||
},
|
||||
chartSubtitle: {
|
||||
fontSize: 11,
|
||||
fontWeight: '500',
|
||||
fontFamily: fontFamily.medium,
|
||||
marginBottom: 12,
|
||||
},
|
||||
footerSpacer: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { ScreenShell, Card } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import { useAppDispatch } from '../../../app/hooks';
|
||||
import { logoutSession } from '../../auth/store/authThunk';
|
||||
import { Button } from '../../../components/Button';
|
||||
@@ -48,7 +48,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
title: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: 16,
|
||||
|
||||
@@ -5,7 +5,7 @@ import Ionicons from '@react-native-vector-icons/ionicons';
|
||||
import Animated, { FadeIn, FadeInRight, FadeInLeft } from 'react-native-reanimated';
|
||||
|
||||
import { ScreenShell } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import type { AgentLoansStackParamList } from '../navigation/agentNavigationTypes';
|
||||
|
||||
import LoanRequestListScreen from './LoanRequestListScreen';
|
||||
@@ -94,7 +94,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: 22,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
letterSpacing: -0.5,
|
||||
},
|
||||
tabContainer: {
|
||||
@@ -115,7 +115,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
tabLabel: {
|
||||
fontSize: 12,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
contentArea: {
|
||||
flex: 1,
|
||||
|
||||
@@ -7,7 +7,7 @@ import Ionicons from '@react-native-vector-icons/ionicons';
|
||||
import Animated, { FadeInUp } from 'react-native-reanimated';
|
||||
|
||||
import { ScreenShell, Button } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import { TAB_BAR_HEIGHT } from '../../../utils/layoutConstants';
|
||||
import { useAppDispatch, useAppSelector } from '../../../app/hooks';
|
||||
import type { AgentTabParamList, AgentProfileStackParamList } from '../navigation/agentNavigationTypes';
|
||||
@@ -25,8 +25,8 @@ export default function AgentProfileScreen({ navigation }: { navigation: Navigat
|
||||
|
||||
const handleLogout = () => {
|
||||
Alert.alert(
|
||||
'Log Out',
|
||||
'Are you sure you want to log out of your agent account?',
|
||||
'Log out of this device?',
|
||||
'Signing out clears everything this app has stored on this phone, not just your session.',
|
||||
[
|
||||
{ text: 'Cancel', style: 'cancel' },
|
||||
{
|
||||
@@ -153,11 +153,11 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
avatarText: {
|
||||
fontSize: 32,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
name: {
|
||||
fontSize: 20,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
marginBottom: 4,
|
||||
},
|
||||
role: {
|
||||
@@ -179,11 +179,11 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
badgeText: {
|
||||
fontSize: 12,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
marginBottom: 8,
|
||||
@@ -214,7 +214,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
settingsLabel: {
|
||||
fontSize: 15,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
},
|
||||
divider: {
|
||||
height: 1,
|
||||
|
||||
@@ -1,100 +1,157 @@
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet, ScrollView } from 'react-native';
|
||||
import { View, Text, StyleSheet, Share } from 'react-native';
|
||||
import { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import Ionicons from '@react-native-vector-icons/ionicons';
|
||||
import Animated, { FadeInUp } from 'react-native-reanimated';
|
||||
|
||||
import { ScreenShell, Button } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useToast } from '../../../hooks/useToast';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import type { AgentDashboardStackParamList } from '../navigation/agentNavigationTypes';
|
||||
|
||||
type Props = NativeStackScreenProps<AgentDashboardStackParamList, 'AgentRoute'>;
|
||||
|
||||
export default function AgentRouteScreen({ navigation }: Props) {
|
||||
type Stop = {
|
||||
id: string;
|
||||
name: string;
|
||||
area: string;
|
||||
time: string;
|
||||
status: 'done' | 'next' | 'pending';
|
||||
};
|
||||
|
||||
const STOPS: Stop[] = [
|
||||
{ id: '1', name: 'Ramesh Kumar', area: 'MG Road, Shop 14', time: '9:30 AM', status: 'done' },
|
||||
{ id: '2', name: 'Sunita Devi', area: 'Gandhi Nagar, H-22', time: '10:15 AM', status: 'done' },
|
||||
{ id: '3', name: 'Imran Shaikh', area: 'Market Lane, 7B', time: '11:00 AM', status: 'next' },
|
||||
{ id: '4', name: 'Lakshmi Bai', area: 'Station Road, 41', time: '11:45 AM', status: 'pending' },
|
||||
{ id: '5', name: 'Vikram Patel', area: 'Old Town, 9', time: '12:30 PM', status: 'pending' },
|
||||
];
|
||||
|
||||
export default function AgentRouteScreen(_props: Props) {
|
||||
const theme = useTheme();
|
||||
const toast = useToast();
|
||||
|
||||
const statusColor = (status: Stop['status']) =>
|
||||
status === 'done' ? theme.colors.success : status === 'next' ? theme.colors.primary : theme.colors.textMuted;
|
||||
|
||||
const handleShare = async () => {
|
||||
const summary = STOPS.map((stop, index) => `${index + 1}. ${stop.name} — ${stop.area} (${stop.time})`).join('\n');
|
||||
try {
|
||||
await Share.share({
|
||||
title: "Today's collection route",
|
||||
message: `Today's collection route (${STOPS.length} stops):\n\n${summary}`,
|
||||
});
|
||||
} catch {
|
||||
toast('Unable to share', 'error', 'Could not open the share sheet. Please try again.');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ScreenShell title="My Route" scrollable={false}>
|
||||
<View style={styles.container}>
|
||||
<View style={styles.mapPlaceholder}>
|
||||
<Ionicons name="map-outline" size={64} color={theme.colors.textMuted} />
|
||||
<Text style={[styles.mapText, { color: theme.colors.textMuted }]}>Map Integration Coming Soon</Text>
|
||||
<ScreenShell title="My Route" subtitle="Today's collection route" contentContainerStyle={styles.content}>
|
||||
{/* Map preview */}
|
||||
<View style={[styles.mapPlaceholder, { borderColor: theme.colors.cardBorder, backgroundColor: theme.colors.surfaceMuted }]}>
|
||||
<Ionicons name="map-outline" size={48} color={theme.colors.primary} />
|
||||
<Text style={[styles.mapText, { color: theme.colors.textSecondary }]}>Route overview · 5 stops · 8.4 km</Text>
|
||||
<View style={styles.pinRow}>
|
||||
{STOPS.map((stop) => (
|
||||
<View key={stop.id} style={[styles.pin, { backgroundColor: statusColor(stop.status) }]} />
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={[styles.infoCard, { backgroundColor: theme.colors.surface, borderColor: theme.colors.cardBorder }]}>
|
||||
<Text style={[styles.infoTitle, { color: theme.colors.textPrimary }]}>Route Summary</Text>
|
||||
<View style={[styles.divider, { backgroundColor: theme.colors.cardBorder }]} />
|
||||
|
||||
<View style={styles.statRow}>
|
||||
<Text style={[styles.statLabel, { color: theme.colors.textMuted }]}>Total Stops</Text>
|
||||
<Text style={[styles.statValue, { color: theme.colors.textPrimary }]}>12</Text>
|
||||
</View>
|
||||
<View style={styles.statRow}>
|
||||
<Text style={[styles.statLabel, { color: theme.colors.textMuted }]}>Completed</Text>
|
||||
<Text style={[styles.statValue, { color: theme.colors.success }]}>5</Text>
|
||||
</View>
|
||||
<View style={styles.statRow}>
|
||||
<Text style={[styles.statLabel, { color: theme.colors.textMuted }]}>Remaining</Text>
|
||||
<Text style={[styles.statValue, { color: theme.colors.warning }]}>7</Text>
|
||||
</View>
|
||||
{/* Summary */}
|
||||
<View style={[styles.infoCard, { backgroundColor: theme.colors.surface, borderColor: theme.colors.cardBorder }]}>
|
||||
<View style={styles.statCol}>
|
||||
<Text style={[styles.statValue, { color: theme.colors.textPrimary }]}>12</Text>
|
||||
<Text style={[styles.statLabel, { color: theme.colors.textMuted }]}>Total stops</Text>
|
||||
</View>
|
||||
<View style={[styles.statDivider, { backgroundColor: theme.colors.cardBorder }]} />
|
||||
<View style={styles.statCol}>
|
||||
<Text style={[styles.statValue, { color: theme.colors.success }]}>5</Text>
|
||||
<Text style={[styles.statLabel, { color: theme.colors.textMuted }]}>Completed</Text>
|
||||
</View>
|
||||
<View style={[styles.statDivider, { backgroundColor: theme.colors.cardBorder }]} />
|
||||
<View style={styles.statCol}>
|
||||
<Text style={[styles.statValue, { color: theme.colors.warning }]}>7</Text>
|
||||
<Text style={[styles.statLabel, { color: theme.colors.textMuted }]}>Remaining</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<Button
|
||||
title="Start Navigation"
|
||||
onPress={() => {}}
|
||||
style={{ marginTop: 'auto', marginBottom: 24 }}
|
||||
{/* Stops list */}
|
||||
<Text style={[styles.sectionTitle, { color: theme.colors.textPrimary }]}>Stops</Text>
|
||||
<View style={[styles.stopsCard, { backgroundColor: theme.colors.surface, borderColor: theme.colors.cardBorder }]}>
|
||||
{STOPS.map((stop, index) => (
|
||||
<View key={stop.id} style={[styles.stopRow, index !== STOPS.length - 1 && { borderBottomWidth: 1, borderBottomColor: theme.colors.cardBorder }]}>
|
||||
<View style={[styles.stopDot, { backgroundColor: `${statusColor(stop.status)}20` }]}>
|
||||
<Ionicons
|
||||
name={stop.status === 'done' ? 'checkmark' : 'location'}
|
||||
size={14}
|
||||
color={statusColor(stop.status)}
|
||||
/>
|
||||
</View>
|
||||
<View style={{ flex: 1 }}>
|
||||
<Text style={[styles.stopName, { color: theme.colors.textPrimary }]}>{stop.name}</Text>
|
||||
<Text style={[styles.stopArea, { color: theme.colors.textSecondary }]}>{stop.area}</Text>
|
||||
</View>
|
||||
<Text style={[styles.stopTime, { color: theme.colors.textMuted }]}>{stop.time}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
{/* Actions */}
|
||||
<View style={styles.actionRow}>
|
||||
<Button
|
||||
title="Start Navigation"
|
||||
fullWidth={false}
|
||||
style={styles.actionBtn}
|
||||
onPress={() => toast('Opening maps', 'info', 'Launching turn-by-turn navigation for your route.')}
|
||||
leftAccessory={<Ionicons name="navigate-outline" size={18} color={theme.colors.white} />}
|
||||
/>
|
||||
<Button
|
||||
title="Share Route"
|
||||
variant="secondary"
|
||||
fullWidth={false}
|
||||
style={styles.actionBtn}
|
||||
onPress={handleShare}
|
||||
leftAccessory={<Ionicons name="share-social-outline" size={18} color={theme.colors.textPrimary} />}
|
||||
/>
|
||||
</View>
|
||||
<View style={{ height: 32 }} />
|
||||
</ScreenShell>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
padding: 16,
|
||||
},
|
||||
content: { gap: 16 },
|
||||
mapPlaceholder: {
|
||||
flex: 1,
|
||||
backgroundColor: 'rgba(0,0,0,0.02)',
|
||||
height: 160,
|
||||
borderRadius: 16,
|
||||
borderWidth: 1,
|
||||
borderStyle: 'dashed',
|
||||
borderColor: 'rgba(0,0,0,0.1)',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: 24,
|
||||
},
|
||||
mapText: {
|
||||
marginTop: 16,
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
gap: 10,
|
||||
},
|
||||
mapText: { fontSize: 14, fontFamily: fontFamily.semibold },
|
||||
pinRow: { flexDirection: 'row', gap: 8 },
|
||||
pin: { width: 10, height: 10, borderRadius: 5 },
|
||||
infoCard: {
|
||||
flexDirection: 'row',
|
||||
borderRadius: 16,
|
||||
borderWidth: 1,
|
||||
padding: 16,
|
||||
alignItems: 'center',
|
||||
},
|
||||
infoTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
marginBottom: 12,
|
||||
},
|
||||
divider: {
|
||||
height: 1,
|
||||
width: '100%',
|
||||
marginBottom: 12,
|
||||
},
|
||||
statRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
paddingVertical: 8,
|
||||
},
|
||||
statLabel: {
|
||||
fontSize: 14,
|
||||
},
|
||||
statValue: {
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
},
|
||||
statCol: { flex: 1, alignItems: 'center', gap: 4 },
|
||||
statDivider: { width: 1, height: 32 },
|
||||
statValue: { fontSize: 20, fontFamily: fontFamily.bold },
|
||||
statLabel: { fontSize: 12 },
|
||||
sectionTitle: { fontSize: 16, fontFamily: fontFamily.bold, marginLeft: 4 },
|
||||
stopsCard: { borderRadius: 16, borderWidth: 1, paddingHorizontal: 16 },
|
||||
stopRow: { flexDirection: 'row', alignItems: 'center', gap: 12, paddingVertical: 14 },
|
||||
stopDot: { width: 30, height: 30, borderRadius: 15, alignItems: 'center', justifyContent: 'center' },
|
||||
stopName: { fontSize: 14, fontFamily: fontFamily.bold },
|
||||
stopArea: { fontSize: 12, fontFamily: fontFamily.medium },
|
||||
stopTime: { fontSize: 12, fontFamily: fontFamily.semibold },
|
||||
actionRow: { flexDirection: 'row', gap: 12 },
|
||||
actionBtn: { flex: 1 },
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import Animated, { FadeInUp } from 'react-native-reanimated';
|
||||
|
||||
import { ScreenShell } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import { useAppDispatch, useAppSelector } from '../../../app/hooks';
|
||||
import type { AgentCollectionsStackParamList } from '../navigation/agentNavigationTypes';
|
||||
import { useGetCollectionHistoryQuery } from '../services/agentApi';
|
||||
@@ -93,12 +93,12 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
summaryLabel: {
|
||||
fontSize: 12,
|
||||
fontWeight: '500',
|
||||
fontFamily: fontFamily.medium,
|
||||
marginBottom: 4,
|
||||
},
|
||||
summaryValue: {
|
||||
fontSize: 20,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
vDivider: {
|
||||
width: 1,
|
||||
|
||||
@@ -5,7 +5,7 @@ import Ionicons from '@react-native-vector-icons/ionicons';
|
||||
import Animated, { FadeIn, SlideInUp } from 'react-native-reanimated';
|
||||
|
||||
import { ScreenShell, Button } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import type { AgentCollectionsStackParamList } from '../navigation/agentNavigationTypes';
|
||||
import { formatFullCurrency, formatDateTime } from '../utils/agentFormatters';
|
||||
|
||||
@@ -36,7 +36,7 @@ export default function CollectionReceiptScreen({ route, navigation }: Props) {
|
||||
styles.infoValue,
|
||||
{
|
||||
color: strong ? theme.colors.textPrimary : theme.colors.textSecondary,
|
||||
fontWeight: strong ? '700' : '500'
|
||||
fontFamily: strong ? fontFamily.bold : fontFamily.medium
|
||||
}
|
||||
]}>
|
||||
{value}
|
||||
@@ -130,12 +130,12 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
successTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
marginBottom: 8,
|
||||
},
|
||||
successAmount: {
|
||||
fontSize: 32,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
dividerContainer: {
|
||||
flexDirection: 'row',
|
||||
@@ -199,6 +199,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
actionBtnText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import { View, StyleSheet, FlatList, RefreshControl, Text } from 'react-native';
|
||||
import Animated, { FadeInUp } from 'react-native-reanimated';
|
||||
import Ionicons from '@react-native-vector-icons/ionicons';
|
||||
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import { TAB_BAR_HEIGHT } from '../../../utils/layoutConstants';
|
||||
import { useAppDispatch, useAppSelector } from '../../../app/hooks';
|
||||
import { useGetCommissionSummaryQuery } from '../services/agentApi';
|
||||
@@ -132,13 +132,13 @@ const styles = StyleSheet.create({
|
||||
totalLabel: {
|
||||
color: 'rgba(255,255,255,0.8)',
|
||||
fontSize: 13,
|
||||
fontWeight: '500',
|
||||
fontFamily: fontFamily.medium,
|
||||
marginBottom: 4,
|
||||
},
|
||||
totalValue: {
|
||||
color: '#fff',
|
||||
fontSize: 28,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
letterSpacing: -0.5,
|
||||
},
|
||||
iconCircle: {
|
||||
@@ -161,11 +161,11 @@ const styles = StyleSheet.create({
|
||||
lastPaidValue: {
|
||||
color: '#fff',
|
||||
fontSize: 14,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
historyTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
marginBottom: 12,
|
||||
},
|
||||
historyCard: {
|
||||
@@ -181,7 +181,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
historyMonth: {
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
statusBadge: {
|
||||
paddingHorizontal: 8,
|
||||
@@ -190,7 +190,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
statusText: {
|
||||
fontSize: 10,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
divider: {
|
||||
height: 1,
|
||||
@@ -210,6 +210,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
value: {
|
||||
fontSize: 15,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import Ionicons from '@react-native-vector-icons/ionicons';
|
||||
import Animated, { FadeInRight, FadeOutLeft, FadeIn } from 'react-native-reanimated';
|
||||
|
||||
import { ScreenShell, Input, Button, KeyboardAwareScrollView } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import { TAB_BAR_HEIGHT } from '../../../utils/layoutConstants';
|
||||
import type { AgentLoansStackParamList } from '../navigation/agentNavigationTypes';
|
||||
import { useLoanRequestForm } from '../hooks/useLoanRequestForm';
|
||||
@@ -46,7 +46,7 @@ const StepIndicator = ({ step }: { step: number }) => {
|
||||
<Text style={{
|
||||
color: isActive ? theme.colors.white : theme.colors.textMuted,
|
||||
fontSize: 11,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
}}>{s.id}</Text>
|
||||
)}
|
||||
</View>
|
||||
@@ -54,7 +54,7 @@ const StepIndicator = ({ step }: { step: number }) => {
|
||||
styles.stepLabel,
|
||||
{
|
||||
color: isActive ? theme.colors.primary : isCompleted ? theme.colors.success : theme.colors.textMuted,
|
||||
fontWeight: isActive ? '700' : '500',
|
||||
fontFamily: isActive ? fontFamily.bold : fontFamily.medium,
|
||||
},
|
||||
]}>{s.label}</Text>
|
||||
</View>
|
||||
@@ -103,6 +103,12 @@ export default function CreateLoanRequestScreen({ route, navigation }: Props) {
|
||||
const [step, setStep] = useState(1);
|
||||
|
||||
const selectedCategory = LOAN_CATEGORIES.find(c => c.id === data.category);
|
||||
// The chosen type's categorical pair — a legible tone and a measured tint —
|
||||
// for whichever theme is live. Falls back to the brand tint before a type is
|
||||
// picked, so the banner is never drawn with an undefined colour.
|
||||
const selectedTone = selectedCategory
|
||||
? theme.colors.category[selectedCategory.hue]
|
||||
: { tone: theme.colors.primary, surface: theme.colors.primarySurface };
|
||||
|
||||
const handleNext = () => {
|
||||
if (step === 1 && !data.category) return;
|
||||
@@ -185,29 +191,40 @@ export default function CreateLoanRequestScreen({ route, navigation }: Props) {
|
||||
<View style={styles.categoryList}>
|
||||
{LOAN_CATEGORIES.map(cat => {
|
||||
const isSelected = data.category === cat.id;
|
||||
// The hue resolves to a tone and a measured tint here,
|
||||
// where the theme exists. The tint replaces the old
|
||||
// `${hex}12` / `${hex}20` alpha suffixes, which composited
|
||||
// the raw hue over the card and so worked on white only.
|
||||
const { tone, surface } = theme.colors.category[cat.hue];
|
||||
return (
|
||||
<Pressable
|
||||
key={cat.id}
|
||||
style={[
|
||||
styles.catCard,
|
||||
{
|
||||
backgroundColor: isSelected ? `${cat.color}12` : theme.colors.surfaceMuted,
|
||||
borderColor: isSelected ? cat.color : 'transparent',
|
||||
backgroundColor: isSelected ? surface : theme.colors.surfaceMuted,
|
||||
borderColor: isSelected ? tone : 'transparent',
|
||||
borderWidth: isSelected ? 1.5 : 1,
|
||||
},
|
||||
]}
|
||||
onPress={() => updateField('category', cat.id as any)}
|
||||
>
|
||||
<View style={[styles.catIcon, { backgroundColor: `${cat.color}20` }]}>
|
||||
<Ionicons name={cat.icon as any} size={22} color={cat.color} />
|
||||
<View style={[styles.catIcon, { backgroundColor: surface }]}>
|
||||
<Ionicons name={cat.icon as any} size={22} color={tone} />
|
||||
</View>
|
||||
<Text style={[
|
||||
styles.catLabel,
|
||||
{ color: isSelected ? cat.color : theme.colors.textPrimary },
|
||||
{ color: isSelected ? tone : theme.colors.textPrimary },
|
||||
]}>{cat.label}</Text>
|
||||
{isSelected && (
|
||||
<View style={[styles.catCheck, { backgroundColor: cat.color }]}>
|
||||
<Ionicons name="checkmark" size={12} color={theme.colors.white} />
|
||||
<View style={[styles.catCheck, { backgroundColor: tone }]}>
|
||||
{/* On a saturated fill, so this takes the on-fill
|
||||
tone — white in light mode, dark ink in dark. */}
|
||||
<Ionicons
|
||||
name="checkmark"
|
||||
size={12}
|
||||
color={theme.colors.textOnAccentFill}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</Pressable>
|
||||
@@ -234,7 +251,7 @@ export default function CreateLoanRequestScreen({ route, navigation }: Props) {
|
||||
onChangeText={v => updateField('customerPhone', v)}
|
||||
keyboardType="number-pad"
|
||||
maxLength={10}
|
||||
leftAccessory={<Text style={{ color: theme.colors.textMuted, fontWeight: '600', fontSize: 13 }}>+91</Text>}
|
||||
leftAccessory={<Text style={{ color: theme.colors.textMuted, fontFamily: fontFamily.semibold, fontSize: 13 }}>+91</Text>}
|
||||
/>
|
||||
</SectionCard>
|
||||
</Animated.View>
|
||||
@@ -244,12 +261,18 @@ export default function CreateLoanRequestScreen({ route, navigation }: Props) {
|
||||
{step === 2 && (
|
||||
<Animated.View entering={FadeInRight} exiting={FadeOutLeft}>
|
||||
{selectedCategory && (
|
||||
<Animated.View entering={FadeIn} style={[styles.selectedBanner, { backgroundColor: `${selectedCategory.color}12`, borderColor: `${selectedCategory.color}30` }]}>
|
||||
<View style={[styles.bannerIcon, { backgroundColor: `${selectedCategory.color}20` }]}>
|
||||
<Ionicons name={selectedCategory.icon as any} size={20} color={selectedCategory.color} />
|
||||
<Animated.View
|
||||
entering={FadeIn}
|
||||
style={[
|
||||
styles.selectedBanner,
|
||||
{ backgroundColor: selectedTone.surface, borderColor: selectedTone.tone },
|
||||
]}
|
||||
>
|
||||
<View style={[styles.bannerIcon, { backgroundColor: selectedTone.surface }]}>
|
||||
<Ionicons name={selectedCategory.icon as any} size={20} color={selectedTone.tone} />
|
||||
</View>
|
||||
<View style={{ flex: 1 }}>
|
||||
<Text style={[styles.bannerTitle, { color: selectedCategory.color }]}>{selectedCategory.label}</Text>
|
||||
<Text style={[styles.bannerTitle, { color: selectedTone.tone }]}>{selectedCategory.label}</Text>
|
||||
<Text style={[styles.bannerSub, { color: theme.colors.textMuted }]}>for {data.customerName}</Text>
|
||||
</View>
|
||||
</Animated.View>
|
||||
@@ -263,7 +286,7 @@ export default function CreateLoanRequestScreen({ route, navigation }: Props) {
|
||||
onChangeText={v => updateField('amount', v)}
|
||||
keyboardType="number-pad"
|
||||
error={errors.amount}
|
||||
leftAccessory={<Text style={{ fontSize: 16, fontWeight: '800', color: theme.colors.primary }}>₹</Text>}
|
||||
leftAccessory={<Text style={{ fontSize: 16, fontFamily: fontFamily.bold, color: theme.colors.primary }}>₹</Text>}
|
||||
/>
|
||||
<View style={styles.row}>
|
||||
<View style={styles.col}>
|
||||
@@ -274,7 +297,7 @@ export default function CreateLoanRequestScreen({ route, navigation }: Props) {
|
||||
onChangeText={v => updateField('tenureMonths', v)}
|
||||
keyboardType="number-pad"
|
||||
error={errors.tenureMonths}
|
||||
rightAccessory={<Text style={{ fontSize: 12, color: theme.colors.textMuted, fontWeight: '600' }}>months</Text>}
|
||||
rightAccessory={<Text style={{ fontSize: 12, color: theme.colors.textMuted, fontFamily: fontFamily.semibold }}>months</Text>}
|
||||
/>
|
||||
</View>
|
||||
<View style={{ width: 12 }} />
|
||||
@@ -286,7 +309,7 @@ export default function CreateLoanRequestScreen({ route, navigation }: Props) {
|
||||
onChangeText={v => updateField('monthlyIncome', v)}
|
||||
keyboardType="number-pad"
|
||||
error={errors.monthlyIncome}
|
||||
leftAccessory={<Text style={{ fontSize: 14, fontWeight: '700', color: theme.colors.textMuted }}>₹</Text>}
|
||||
leftAccessory={<Text style={{ fontSize: 14, fontFamily: fontFamily.bold, color: theme.colors.textMuted }}>₹</Text>}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
@@ -311,9 +334,11 @@ export default function CreateLoanRequestScreen({ route, navigation }: Props) {
|
||||
<View style={[styles.reviewCard, { backgroundColor: theme.colors.surface, borderColor: theme.colors.cardBorder }]}>
|
||||
{/* Header badge */}
|
||||
{selectedCategory && (
|
||||
<View style={[styles.reviewBadge, { backgroundColor: `${selectedCategory.color}12` }]}>
|
||||
<Ionicons name={selectedCategory.icon as any} size={18} color={selectedCategory.color} />
|
||||
<Text style={[styles.reviewBadgeText, { color: selectedCategory.color }]}>{selectedCategory.label}</Text>
|
||||
<View style={[styles.reviewBadge, { backgroundColor: selectedTone.surface }]}>
|
||||
<Ionicons name={selectedCategory.icon as any} size={18} color={selectedTone.tone} />
|
||||
<Text style={[styles.reviewBadgeText, { color: selectedTone.tone }]}>
|
||||
{selectedCategory.label}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
@@ -416,7 +441,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 17,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
marginBottom: 2,
|
||||
},
|
||||
sectionSubtitle: {
|
||||
@@ -453,7 +478,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
catLabel: {
|
||||
fontSize: 11,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
textAlign: 'center',
|
||||
lineHeight: 14,
|
||||
},
|
||||
@@ -486,7 +511,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
bannerTitle: {
|
||||
fontSize: 14,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
bannerSub: {
|
||||
fontSize: 12,
|
||||
@@ -503,7 +528,7 @@ const styles = StyleSheet.create({
|
||||
errorText: {
|
||||
fontSize: 12,
|
||||
marginTop: 6,
|
||||
fontWeight: '500',
|
||||
fontFamily: fontFamily.medium,
|
||||
},
|
||||
|
||||
// ── Review Card ───────────────────────────
|
||||
@@ -524,7 +549,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
reviewBadgeText: {
|
||||
fontSize: 13,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
reviewDivider: {
|
||||
height: 1,
|
||||
@@ -538,12 +563,12 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
reviewLabel: {
|
||||
fontSize: 13,
|
||||
fontWeight: '500',
|
||||
fontFamily: fontFamily.medium,
|
||||
minWidth: 80,
|
||||
},
|
||||
reviewValue: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
textAlign: 'right',
|
||||
flexShrink: 1,
|
||||
marginLeft: 16,
|
||||
@@ -560,7 +585,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
docsTitle: {
|
||||
fontSize: 13,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
marginBottom: 3,
|
||||
},
|
||||
docsText: {
|
||||
@@ -597,7 +622,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
progressText: {
|
||||
fontSize: 11,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
minWidth: 60,
|
||||
},
|
||||
progressTrack: {
|
||||
|
||||
@@ -5,7 +5,7 @@ import Ionicons from '@react-native-vector-icons/ionicons';
|
||||
import Animated, { FadeInUp } from 'react-native-reanimated';
|
||||
|
||||
import { ScreenShell, Button } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import { useAppSelector } from '../../../app/hooks';
|
||||
import { selectCustomerById } from '../store/agentSelectors';
|
||||
import { useDeleteAgentCustomerMutation } from '../services/agentApi';
|
||||
@@ -13,6 +13,8 @@ import type { AgentCustomersStackParamList } from '../navigation/agentNavigation
|
||||
import { formatAgentCurrency, formatDate, maskAadhaar, maskPhone } from '../utils/agentFormatters';
|
||||
|
||||
import { AgentSectionHeader } from '../components/AgentSectionHeader';
|
||||
import { FeatureKey } from '../../../constants/featureKeys';
|
||||
import { FeatureGate, useFeatureAction } from '../../../features';
|
||||
|
||||
type Props = NativeStackScreenProps<AgentCustomersStackParamList, 'CustomerDetail'>;
|
||||
|
||||
@@ -55,6 +57,14 @@ export default function CustomerDetailScreen({ route, navigation }: Props) {
|
||||
);
|
||||
};
|
||||
|
||||
// Second line of defence: even if some path reached this handler while
|
||||
// `agent.customers.delete` is off, it returns before the confirm dialog and
|
||||
// before any delete request is issued.
|
||||
const handleDeleteCustomer = useFeatureAction(
|
||||
FeatureKey.AGENT_CUSTOMERS_DELETE,
|
||||
handleDelete,
|
||||
);
|
||||
|
||||
const InfoRow = ({ label, value, icon, masked = false }: { label: string, value: string, icon?: string, masked?: boolean }) => (
|
||||
<View style={styles.infoRow}>
|
||||
<View style={styles.infoLabelArea}>
|
||||
@@ -105,12 +115,14 @@ export default function CustomerDetailScreen({ route, navigation }: Props) {
|
||||
<Text style={[styles.iconLabel, { color: theme.colors.success }]}>SMS</Text>
|
||||
</Pressable>
|
||||
|
||||
<Pressable style={styles.iconBtn} onPress={() => navigation.navigate('AddEditCustomer', { customerId: customer.id })}>
|
||||
<View style={[styles.iconFrame, { backgroundColor: `${theme.colors.primary}15` }]}>
|
||||
<Ionicons name="create" size={20} color={theme.colors.primary} />
|
||||
</View>
|
||||
<Text style={[styles.iconLabel, { color: theme.colors.primary }]}>Edit</Text>
|
||||
</Pressable>
|
||||
<FeatureGate feature={FeatureKey.AGENT_CUSTOMERS_EDIT}>
|
||||
<Pressable style={styles.iconBtn} onPress={() => navigation.navigate('AddEditCustomer', { customerId: customer.id })}>
|
||||
<View style={[styles.iconFrame, { backgroundColor: `${theme.colors.primary}15` }]}>
|
||||
<Ionicons name="create" size={20} color={theme.colors.primary} />
|
||||
</View>
|
||||
<Text style={[styles.iconLabel, { color: theme.colors.primary }]}>Edit</Text>
|
||||
</Pressable>
|
||||
</FeatureGate>
|
||||
</View>
|
||||
</Animated.View>
|
||||
|
||||
@@ -170,16 +182,18 @@ export default function CustomerDetailScreen({ route, navigation }: Props) {
|
||||
</Animated.View>
|
||||
|
||||
{/* Delete Area */}
|
||||
<View style={styles.deleteArea}>
|
||||
<Button
|
||||
title="Remove Customer"
|
||||
variant="ghost"
|
||||
loading={isDeleting}
|
||||
onPress={handleDelete}
|
||||
leftAccessory={<Ionicons name="trash-outline" size={18} color={theme.colors.danger} />}
|
||||
style={{ borderColor: theme.colors.danger, borderWidth: 1 }}
|
||||
/>
|
||||
</View>
|
||||
<FeatureGate feature={FeatureKey.AGENT_CUSTOMERS_DELETE}>
|
||||
<View style={styles.deleteArea}>
|
||||
<Button
|
||||
title="Remove Customer"
|
||||
variant="ghost"
|
||||
loading={isDeleting}
|
||||
onPress={handleDeleteCustomer}
|
||||
leftAccessory={<Ionicons name="trash-outline" size={18} color={theme.colors.danger} />}
|
||||
style={{ borderColor: theme.colors.danger, borderWidth: 1 }}
|
||||
/>
|
||||
</View>
|
||||
</FeatureGate>
|
||||
|
||||
<View style={styles.footerSpacer} />
|
||||
</ScrollView>
|
||||
@@ -209,14 +223,14 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
avatarBigText: {
|
||||
fontSize: 32,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
headerInfo: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
headerName: {
|
||||
fontSize: 22,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
marginBottom: 4,
|
||||
},
|
||||
headerPhone: {
|
||||
@@ -230,7 +244,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
statusText: {
|
||||
fontSize: 10,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
actionButtonsRow: {
|
||||
flexDirection: 'row',
|
||||
@@ -250,7 +264,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
iconLabel: {
|
||||
fontSize: 12,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
},
|
||||
section: {
|
||||
marginBottom: 24,
|
||||
@@ -267,11 +281,11 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
accType: {
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
accNumber: {
|
||||
fontSize: 14,
|
||||
fontFamily: 'monospace',
|
||||
fontFamily: fontFamily.mono,
|
||||
},
|
||||
divider: {
|
||||
height: 1,
|
||||
@@ -292,7 +306,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
finValue: {
|
||||
fontSize: 18,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
vDivider: {
|
||||
width: 1,
|
||||
@@ -314,7 +328,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
infoValue: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
flex: 1,
|
||||
textAlign: 'right',
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@ import Ionicons from '@react-native-vector-icons/ionicons';
|
||||
import Animated, { FadeInUp } from 'react-native-reanimated';
|
||||
|
||||
import { ScreenShell } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import { useAppSelector } from '../../../app/hooks';
|
||||
import { selectCustomerById } from '../store/agentSelectors';
|
||||
import type { AgentCustomersStackParamList } from '../navigation/agentNavigationTypes';
|
||||
@@ -73,7 +73,7 @@ export default function CustomerInstallmentHistoryScreen({ route, navigation }:
|
||||
|
||||
return (
|
||||
<ScreenShell title="Installment History" scrollable={false}>
|
||||
<View style={styles.header}>
|
||||
<View style={[styles.header, { borderBottomColor: theme.colors.cardBorder }]}>
|
||||
<Text style={[styles.customerName, { color: theme.colors.textPrimary }]}>{customer.name}</Text>
|
||||
<Text style={[styles.accNumber, { color: theme.colors.textMuted }]}>
|
||||
{customer.accounts[0]?.type} A/C: {customer.accounts[0]?.accountNumber}
|
||||
@@ -96,16 +96,15 @@ const styles = StyleSheet.create({
|
||||
padding: 16,
|
||||
paddingTop: 8,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: 'rgba(0,0,0,0.05)',
|
||||
},
|
||||
customerName: {
|
||||
fontSize: 18,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
marginBottom: 4,
|
||||
},
|
||||
accNumber: {
|
||||
fontSize: 14,
|
||||
fontFamily: 'monospace',
|
||||
fontFamily: fontFamily.mono,
|
||||
},
|
||||
listContent: {
|
||||
padding: 16,
|
||||
@@ -133,7 +132,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
date: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
marginBottom: 2,
|
||||
},
|
||||
receipt: {
|
||||
@@ -144,11 +143,11 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
amount: {
|
||||
fontSize: 15,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
marginBottom: 2,
|
||||
},
|
||||
status: {
|
||||
fontSize: 10,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -4,10 +4,10 @@ import { CompositeNavigationProp } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { BottomTabNavigationProp } from '@react-navigation/bottom-tabs';
|
||||
import Ionicons from '@react-native-vector-icons/ionicons';
|
||||
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
||||
import Animated, { FadeIn } from 'react-native-reanimated';
|
||||
|
||||
import { ScreenShell } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import { TAB_BAR_HEIGHT } from '../../../utils/layoutConstants';
|
||||
import { useAppDispatch, useAppSelector } from '../../../app/hooks';
|
||||
import type { AgentTabParamList, AgentCustomersStackParamList } from '../navigation/agentNavigationTypes';
|
||||
@@ -20,6 +20,8 @@ import type { CustomerFilter } from '../types/agentTypes';
|
||||
import { CustomerCard } from '../components/CustomerCard';
|
||||
import { AgentSkeletonLoader } from '../components/AgentSkeletonLoader';
|
||||
import { AgentEmptyState } from '../components/AgentEmptyState';
|
||||
import { FeatureKey } from '../../../constants/featureKeys';
|
||||
import { FeatureGate } from '../../../features';
|
||||
|
||||
type NavigationProp = CompositeNavigationProp<
|
||||
NativeStackNavigationProp<AgentCustomersStackParamList, 'CustomerList'>,
|
||||
@@ -143,8 +145,12 @@ export default function CustomerListScreen({ navigation }: { navigation: Navigat
|
||||
onAction={searchQuery ? () => handleSearch('') : undefined}
|
||||
/>
|
||||
)}
|
||||
// No `exiting` here: FlatList unmounts cells for virtualisation as well
|
||||
// as for deletion, and an exit animation makes Reanimated hold the
|
||||
// outgoing native view while Fabric is recycling that cell — which
|
||||
// crashes with "addViewAt: the specified child already has a parent".
|
||||
renderItem={({ item, index }) => (
|
||||
<Animated.View entering={FadeIn.delay(Math.min(index * 50, 500))} exiting={FadeOut}>
|
||||
<Animated.View entering={FadeIn.delay(Math.min(index * 50, 500))}>
|
||||
<CustomerCard
|
||||
customer={item}
|
||||
onPress={() => navigation.navigate('CustomerDetail', { customerId: item.id })}
|
||||
@@ -156,12 +162,14 @@ export default function CustomerListScreen({ navigation }: { navigation: Navigat
|
||||
</View>
|
||||
|
||||
{/* FAB to add new customer */}
|
||||
<Pressable
|
||||
style={[styles.fab, { backgroundColor: theme.colors.primary }]}
|
||||
onPress={() => navigation.navigate('AddEditCustomer', {})}
|
||||
>
|
||||
<Ionicons name="add" size={24} color={theme.colors.white} />
|
||||
</Pressable>
|
||||
<FeatureGate feature={FeatureKey.AGENT_CUSTOMERS_CREATE}>
|
||||
<Pressable
|
||||
style={[styles.fab, { backgroundColor: theme.colors.primary }]}
|
||||
onPress={() => navigation.navigate('AddEditCustomer', {})}
|
||||
>
|
||||
<Ionicons name="add" size={24} color={theme.colors.white} />
|
||||
</Pressable>
|
||||
</FeatureGate>
|
||||
</ScreenShell>
|
||||
);
|
||||
}
|
||||
@@ -205,7 +213,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
filterText: {
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
},
|
||||
listArea: {
|
||||
flex: 1,
|
||||
|
||||
@@ -8,7 +8,7 @@ import Animated, { FadeIn, SlideInDown } from 'react-native-reanimated';
|
||||
import BottomSheet, { BottomSheetView } from '@gorhom/bottom-sheet';
|
||||
|
||||
import { ScreenShell, Button, Input } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import { TAB_BAR_HEIGHT } from '../../../utils/layoutConstants';
|
||||
import { useAppDispatch, useAppSelector } from '../../../app/hooks';
|
||||
import type { AgentTabParamList, AgentCollectionsStackParamList } from '../navigation/agentNavigationTypes';
|
||||
@@ -64,7 +64,7 @@ const CollectionForm = ({ selectedEntry, navigation, bottomSheetRef }: { selecte
|
||||
onChangeText={setAmount}
|
||||
keyboardType="number-pad"
|
||||
error={formError || undefined}
|
||||
leftAccessory={<Text style={{ fontSize: 18, fontWeight: '700', color: theme.colors.textPrimary }}>₹</Text>}
|
||||
leftAccessory={<Text style={{ fontSize: 18, fontFamily: fontFamily.bold, color: theme.colors.textPrimary }}>₹</Text>}
|
||||
/>
|
||||
|
||||
<Text style={[styles.modeLabel, { color: theme.colors.textMuted }]}>Payment Mode</Text>
|
||||
@@ -314,13 +314,13 @@ const styles = StyleSheet.create({
|
||||
summaryLabel: {
|
||||
color: 'rgba(255,255,255,0.8)',
|
||||
fontSize: 12,
|
||||
fontWeight: '500',
|
||||
fontFamily: fontFamily.medium,
|
||||
marginBottom: 4,
|
||||
},
|
||||
summaryValue: {
|
||||
color: '#fff',
|
||||
fontSize: 24,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
letterSpacing: -0.5,
|
||||
},
|
||||
progressTrack: {
|
||||
@@ -346,7 +346,7 @@ const styles = StyleSheet.create({
|
||||
offlineText: {
|
||||
color: '#fff',
|
||||
fontSize: 12,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
},
|
||||
listArea: {
|
||||
flex: 1,
|
||||
@@ -361,11 +361,11 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
listTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
historyLink: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
},
|
||||
filterRow: {
|
||||
flexDirection: 'row',
|
||||
@@ -381,7 +381,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
filterText: {
|
||||
fontSize: 12,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
},
|
||||
listContent: {
|
||||
padding: 16,
|
||||
@@ -411,11 +411,11 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
avatarText: {
|
||||
fontSize: 18,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
entryName: {
|
||||
fontSize: 15,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
marginBottom: 2,
|
||||
},
|
||||
entryMeta: {
|
||||
@@ -433,11 +433,11 @@ const styles = StyleSheet.create({
|
||||
collectBtnText: {
|
||||
color: '#fff',
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
},
|
||||
entryAmount: {
|
||||
fontSize: 15,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
statusBadge: {
|
||||
paddingHorizontal: 8,
|
||||
@@ -446,7 +446,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
statusText: {
|
||||
fontSize: 10,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
sheetContainer: {
|
||||
flex: 1,
|
||||
@@ -457,7 +457,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
sheetTitle: {
|
||||
fontSize: 20,
|
||||
fontWeight: '800',
|
||||
fontFamily: fontFamily.bold,
|
||||
marginBottom: 24,
|
||||
},
|
||||
sheetCustomerInfo: {
|
||||
@@ -465,7 +465,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
sheetCustomerName: {
|
||||
fontSize: 18,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
marginBottom: 4,
|
||||
},
|
||||
sheetCustomerMeta: {
|
||||
@@ -473,7 +473,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
modeLabel: {
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
marginBottom: 12,
|
||||
marginTop: 16,
|
||||
},
|
||||
@@ -493,6 +493,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
modeText: {
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import Ionicons from '@react-native-vector-icons/ionicons';
|
||||
import Animated, { SlideInRight } from 'react-native-reanimated';
|
||||
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import { TAB_BAR_HEIGHT } from '../../../utils/layoutConstants';
|
||||
import { useAppDispatch, useAppSelector } from '../../../app/hooks';
|
||||
import type { AgentLoansStackParamList } from '../navigation/agentNavigationTypes';
|
||||
@@ -114,7 +114,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
title: {
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
newBtn: {
|
||||
flexDirection: 'row',
|
||||
@@ -126,7 +126,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
newBtnText: {
|
||||
fontSize: 12,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
listContent: {
|
||||
padding: 16,
|
||||
@@ -148,7 +148,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
customerName: {
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
marginBottom: 2,
|
||||
},
|
||||
category: {
|
||||
@@ -161,7 +161,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
statusText: {
|
||||
fontSize: 10,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
divider: {
|
||||
height: 1,
|
||||
@@ -178,6 +178,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
value: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
fontFamily: fontFamily.semibold,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import Ionicons from '@react-native-vector-icons/ionicons';
|
||||
import Animated, { FadeInUp } from 'react-native-reanimated';
|
||||
|
||||
import { ScreenShell } from '../../../components';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { useTheme, fontFamily } from '../../../theme';
|
||||
import { useAppDispatch, useAppSelector } from '../../../app/hooks';
|
||||
import type { AgentProfileStackParamList } from '../navigation/agentNavigationTypes';
|
||||
import { useGetSmsLogsQuery } from '../services/agentApi';
|
||||
@@ -106,7 +106,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
phone: {
|
||||
fontSize: 14,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
headerRight: {
|
||||
flexDirection: 'row',
|
||||
@@ -115,7 +115,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
statusText: {
|
||||
fontSize: 10,
|
||||
fontWeight: '700',
|
||||
fontFamily: fontFamily.bold,
|
||||
},
|
||||
divider: {
|
||||
height: 1,
|
||||
|
||||
Reference in New Issue
Block a user