few changes
This commit is contained in:
@@ -4,7 +4,7 @@ import { Ionicons } from '@expo/vector-icons';
|
||||
import { COLORS } from '../constants/colors';
|
||||
|
||||
import HomeScreen from '../screens/Main/HomeScreen';
|
||||
import MenuScreen from '../screens/Main/MenuScreen';
|
||||
import MenuNavigator from './MenuNavigator';
|
||||
import ProfileScreen from '../screens/Main/ProfileScreen';
|
||||
|
||||
const Tab = createBottomTabNavigator();
|
||||
@@ -56,7 +56,7 @@ const MainNavigator = () => {
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Menu"
|
||||
component={MenuScreen}
|
||||
component={MenuNavigator}
|
||||
options={{
|
||||
tabBarLabel: 'Hyzmatlar',
|
||||
}}
|
||||
|
||||
23
src/navigation/MenuNavigator.js
Normal file
23
src/navigation/MenuNavigator.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import MenuScreen from '../screens/Main/MenuScreen';
|
||||
import LoanRemainingOrdersScreen from '../screens/Loan/LoanRemainingOrdersScreen';
|
||||
import CreateLoanRemainingOrderScreen from '../screens/Loan/CreateLoanRemainingOrderScreen';
|
||||
import LoanPaidOffLetterOrdersScreen from '../screens/Loan/LoanPaidOffLetterOrdersScreen';
|
||||
import CreateLoanPaidOffLetterOrderScreen from '../screens/Loan/CreateLoanPaidOffLetterOrderScreen';
|
||||
import LoanPaidOffLetterOrderDetailsScreen from '../screens/Loan/LoanPaidOffLetterOrderDetailsScreen';
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
|
||||
const MenuNavigator = () => (
|
||||
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
||||
<Stack.Screen name="MenuHome" component={MenuScreen} />
|
||||
<Stack.Screen name="LoanRemainingOrders" component={LoanRemainingOrdersScreen} />
|
||||
<Stack.Screen name="CreateLoanRemainingOrder" component={CreateLoanRemainingOrderScreen} />
|
||||
<Stack.Screen name="LoanPaidOffLetterOrders" component={LoanPaidOffLetterOrdersScreen} />
|
||||
<Stack.Screen name="CreateLoanPaidOffLetterOrder" component={CreateLoanPaidOffLetterOrderScreen} />
|
||||
<Stack.Screen name="LoanPaidOffLetterOrderDetails" component={LoanPaidOffLetterOrderDetailsScreen} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
|
||||
export default MenuNavigator;
|
||||
93
src/screens/Loan/CreateLoanRemainingOrderScreen.js
Normal file
93
src/screens/Loan/CreateLoanRemainingOrderScreen.js
Normal file
@@ -0,0 +1,93 @@
|
||||
import React, { useState } from 'react';
|
||||
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, SafeAreaView } from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import apiService from '../../services/apiService';
|
||||
import { COLORS } from '../../constants/colors';
|
||||
import Input from '../../components/Input';
|
||||
import { StatusBar } from 'expo-status-bar';
|
||||
|
||||
const CreateLoanRemainingOrderScreen = () => {
|
||||
const navigation = useNavigation();
|
||||
const [accountNumber, setAccountNumber] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (accountNumber.trim().length === 0) {
|
||||
Alert.alert('Error', 'Account number is required');
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const res = await apiService.createLoanRemainingOrder(accountNumber.trim());
|
||||
setLoading(false);
|
||||
if (res.success) {
|
||||
Alert.alert('Success', res.message || 'Order created successfully', [
|
||||
{ text: 'OK', onPress: () => navigation.goBack() },
|
||||
]);
|
||||
} else {
|
||||
Alert.alert('Error', res.error || 'Could not create order');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<StatusBar style="dark" />
|
||||
<TouchableOpacity style={styles.backBtn} onPress={() => navigation.goBack()}>
|
||||
<Ionicons name="arrow-back" size={24} color={COLORS.textPrimary} />
|
||||
</TouchableOpacity>
|
||||
|
||||
<Text style={styles.title}>Täze sargyt</Text>
|
||||
|
||||
<Input
|
||||
label="Account number"
|
||||
placeholder="1420..."
|
||||
value={accountNumber}
|
||||
onChangeText={setAccountNumber}
|
||||
keyboardType="numeric"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
returnKeyType="done"
|
||||
/>
|
||||
|
||||
<TouchableOpacity style={styles.submitBtn} onPress={handleSubmit} disabled={loading}>
|
||||
{loading ? (
|
||||
<ActivityIndicator color={COLORS.white} />
|
||||
) : (
|
||||
<Text style={styles.submitText}>Ýatda sakla</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: COLORS.backgroundSecondary,
|
||||
paddingHorizontal: 24,
|
||||
paddingTop: 40,
|
||||
},
|
||||
backBtn: {
|
||||
marginBottom: 24,
|
||||
},
|
||||
title: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: COLORS.textPrimary,
|
||||
marginBottom: 24,
|
||||
},
|
||||
submitBtn: {
|
||||
marginTop: 32,
|
||||
backgroundColor: COLORS.primary,
|
||||
paddingVertical: 16,
|
||||
borderRadius: 8,
|
||||
alignItems: 'center',
|
||||
},
|
||||
submitText: {
|
||||
color: COLORS.white,
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
},
|
||||
});
|
||||
|
||||
export default CreateLoanRemainingOrderScreen;
|
||||
144
src/screens/Loan/LoanRemainingOrderDetailsScreen.js
Normal file
144
src/screens/Loan/LoanRemainingOrderDetailsScreen.js
Normal file
@@ -0,0 +1,144 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert } from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||
import apiService from '../../services/apiService';
|
||||
import { COLORS } from '../../constants/colors';
|
||||
|
||||
const LoanRemainingOrderDetailsScreen = () => {
|
||||
const navigation = useNavigation();
|
||||
const route = useRoute();
|
||||
const { orderId } = route.params || {};
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [order, setOrder] = useState(null);
|
||||
|
||||
const fetchDetails = async () => {
|
||||
setLoading(true);
|
||||
const res = await apiService.getLoanRemainingOrder(orderId);
|
||||
if (res.success) {
|
||||
setOrder(res.data);
|
||||
} else {
|
||||
Alert.alert('Error', res.error || 'Could not fetch details');
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchDetails();
|
||||
}, []);
|
||||
|
||||
const handleDelete = () => {
|
||||
Alert.alert('Confirm', 'Are you sure you want to delete this order?', [
|
||||
{ text: 'Cancel', style: 'cancel' },
|
||||
{ text: 'Delete', style: 'destructive', onPress: deleteOrder },
|
||||
]);
|
||||
};
|
||||
|
||||
const deleteOrder = async () => {
|
||||
const res = await apiService.deleteLoanRemainingOrder(orderId);
|
||||
if (res.success) {
|
||||
Alert.alert('Deleted', res.message || 'Order deleted', [
|
||||
{ text: 'OK', onPress: () => navigation.goBack() },
|
||||
]);
|
||||
} else {
|
||||
Alert.alert('Error', res.error || 'Could not delete');
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<View style={styles.centered}>
|
||||
<ActivityIndicator size="large" color={COLORS.primary} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
if (!order) {
|
||||
return (
|
||||
<View style={styles.centered}>
|
||||
<Text>No data</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity style={styles.backBtn} onPress={() => navigation.goBack()}>
|
||||
<Ionicons name="close" size={28} color={COLORS.textPrimary} />
|
||||
</TouchableOpacity>
|
||||
|
||||
<Text style={styles.title}>Galyndy detallary</Text>
|
||||
|
||||
<View style={styles.detailCard}>
|
||||
{Object.entries(order).map(([key, value]) => (
|
||||
<View key={key} style={styles.detailRow}>
|
||||
<Text style={styles.detailKey}>{key}</Text>
|
||||
<Text style={styles.detailValue}>{String(value)}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
<TouchableOpacity style={styles.deleteBtn} onPress={handleDelete}>
|
||||
<Text style={styles.deleteText}>Poz</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: COLORS.backgroundSecondary,
|
||||
paddingHorizontal: 24,
|
||||
paddingTop: 40,
|
||||
},
|
||||
centered: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
backBtn: {
|
||||
alignSelf: 'flex-end',
|
||||
marginBottom: 16,
|
||||
},
|
||||
title: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: COLORS.textPrimary,
|
||||
marginBottom: 24,
|
||||
},
|
||||
detailCard: {
|
||||
backgroundColor: COLORS.white,
|
||||
borderRadius: 12,
|
||||
padding: 20,
|
||||
marginBottom: 32,
|
||||
},
|
||||
detailRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 12,
|
||||
},
|
||||
detailKey: {
|
||||
fontWeight: '600',
|
||||
color: COLORS.textSecondary,
|
||||
},
|
||||
detailValue: {
|
||||
color: COLORS.textPrimary,
|
||||
maxWidth: '60%',
|
||||
textAlign: 'right',
|
||||
},
|
||||
deleteBtn: {
|
||||
backgroundColor: COLORS.error,
|
||||
paddingVertical: 14,
|
||||
borderRadius: 8,
|
||||
alignItems: 'center',
|
||||
},
|
||||
deleteText: {
|
||||
color: COLORS.white,
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
},
|
||||
});
|
||||
|
||||
export default LoanRemainingOrderDetailsScreen;
|
||||
309
src/screens/Loan/LoanRemainingOrdersScreen.js
Normal file
309
src/screens/Loan/LoanRemainingOrdersScreen.js
Normal file
@@ -0,0 +1,309 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl, Modal, ScrollView, Alert, SafeAreaView, Pressable } from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useNavigation, useFocusEffect } from '@react-navigation/native';
|
||||
import apiService from '../../services/apiService';
|
||||
import { COLORS } from '../../constants/colors';
|
||||
import { StatusBar } from 'expo-status-bar';
|
||||
|
||||
const LoanRemainingOrdersScreen = () => {
|
||||
const navigation = useNavigation();
|
||||
const [orders, setOrders] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [modalData, setModalData] = useState(null);
|
||||
const [modalLoading, setModalLoading] = useState(false);
|
||||
|
||||
const fetchOrders = async () => {
|
||||
try {
|
||||
const res = await apiService.getLoanRemainingOrders();
|
||||
if (res.success) {
|
||||
setOrders(Array.isArray(res.data) ? res.data : []);
|
||||
} else {
|
||||
console.warn(res.error);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setRefreshing(false);
|
||||
}
|
||||
};
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
fetchOrders();
|
||||
}, [])
|
||||
);
|
||||
|
||||
const onRefresh = () => {
|
||||
setRefreshing(true);
|
||||
fetchOrders();
|
||||
};
|
||||
|
||||
const handleItemPress = async (item) => {
|
||||
setModalLoading(true);
|
||||
setModalVisible(true);
|
||||
|
||||
const res = await apiService.getLoanRemainingBalance(item.account_number, item.passport_serie, item.passport_id);
|
||||
if (res.success) {
|
||||
setModalData(res.data);
|
||||
} else {
|
||||
setModalData(null);
|
||||
Alert.alert('Info', res.error || 'Not found');
|
||||
setModalVisible(false);
|
||||
}
|
||||
setModalLoading(false);
|
||||
};
|
||||
|
||||
const renderItem = ({ item }) => (
|
||||
<TouchableOpacity
|
||||
style={styles.card}
|
||||
onPress={() => handleItemPress(item)}
|
||||
>
|
||||
<View style={styles.cardIconWrapper}>
|
||||
<Ionicons name="stats-chart" size={24} color={COLORS.primary} />
|
||||
</View>
|
||||
<View style={styles.cardContent}>
|
||||
<Text style={styles.cardTitle}>{item.account_number}</Text>
|
||||
</View>
|
||||
<Ionicons name="information-circle" size={20} color={COLORS.gray[400]} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<View style={styles.centered}>
|
||||
<ActivityIndicator size="large" color={COLORS.primary} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<StatusBar style="dark" />
|
||||
{/* Header */}
|
||||
<View style={styles.header}>
|
||||
<TouchableOpacity onPress={() => navigation.goBack()} style={{ paddingRight: 12 }}>
|
||||
<Ionicons name="arrow-back" size={24} color={COLORS.textPrimary} />
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.headerTitle}>Karzyň galyndysy</Text>
|
||||
</View>
|
||||
<FlatList
|
||||
data={orders}
|
||||
keyExtractor={(item) => item.id?.toString()}
|
||||
renderItem={renderItem}
|
||||
contentContainerStyle={orders.length === 0 && styles.emptyContainer}
|
||||
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
|
||||
ListEmptyComponent={<Text style={styles.emptyText}>No orders yet</Text>}
|
||||
/>
|
||||
|
||||
{/* Floating Action Button */}
|
||||
<TouchableOpacity
|
||||
style={styles.fab}
|
||||
onPress={() => navigation.navigate('CreateLoanRemainingOrder')}
|
||||
>
|
||||
<Ionicons name="add" size={28} color={COLORS.white} />
|
||||
</TouchableOpacity>
|
||||
|
||||
{/* Result Modal */}
|
||||
<Modal
|
||||
visible={modalVisible}
|
||||
transparent
|
||||
animationType="fade"
|
||||
onRequestClose={() => setModalVisible(false)}
|
||||
>
|
||||
<Pressable style={styles.modalBackdrop} onPress={() => setModalVisible(false)}>
|
||||
<Pressable style={styles.modalCard} onPress={(e) => e.stopPropagation()}>
|
||||
<TouchableOpacity style={styles.modalCloseSmall} onPress={() => setModalVisible(false)}>
|
||||
<Ionicons name="close" size={20} color={COLORS.textPrimary} />
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.modalTitle}>Netije</Text>
|
||||
{modalLoading ? (
|
||||
<View style={{ paddingVertical: 40, alignItems: 'center' }}>
|
||||
<ActivityIndicator size="large" color={COLORS.primary} />
|
||||
</View>
|
||||
) : (
|
||||
<ScrollView
|
||||
style={{ maxHeight: 320, marginTop: 8 }}
|
||||
contentContainerStyle={{ paddingBottom: 16 }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
{modalData && (() => {
|
||||
const fields = [
|
||||
{ key: 'branchName', label: 'Şahamça' },
|
||||
{ key: 'clientName', label: 'Müşderi' },
|
||||
{ key: 'docNum', label: 'Şertnama belgisi' },
|
||||
{ key: '__divider1' },
|
||||
{ key: 'docSum', label: 'Jemi karzyň möçberi' },
|
||||
{ key: 'balans', label: 'Karz boýunça jemi galyndy' },
|
||||
{ key: 'percentBalance', label: 'Hasaplama göterim (şu aý üçin)' },
|
||||
{ key: 'docMonthSum', label: 'Hasaplanan esasy bergi (şu aý üçin)' },
|
||||
{ key: 'docPayed', label: 'Jemi tölenen möçberi' },
|
||||
];
|
||||
|
||||
let renderedCount = 0;
|
||||
const rows = fields.map((f) => {
|
||||
if (f.key.startsWith('__divider')) {
|
||||
return <View key={f.key} style={styles.divider} />;
|
||||
}
|
||||
const val = modalData[f.key];
|
||||
if (val === null || val === undefined || val === '') return null;
|
||||
renderedCount++;
|
||||
return (
|
||||
<View key={f.key} style={styles.itemWrap}>
|
||||
<Text style={styles.itemLabel}>{f.label}</Text>
|
||||
<Text style={styles.itemValue}>{String(val)}</Text>
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
||||
if (renderedCount === 0) {
|
||||
return Object.entries(modalData).map(([k, v]) => (
|
||||
<View key={k} style={styles.itemWrap}>
|
||||
<Text style={styles.itemLabel}>{k}</Text>
|
||||
<Text style={styles.itemValue}>{String(v)}</Text>
|
||||
</View>
|
||||
));
|
||||
}
|
||||
return rows;
|
||||
})()}
|
||||
</ScrollView>
|
||||
)}
|
||||
</Pressable>
|
||||
</Pressable>
|
||||
</Modal>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: COLORS.backgroundSecondary,
|
||||
},
|
||||
centered: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
card: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: 20,
|
||||
borderRadius: 10,
|
||||
backgroundColor: COLORS.white,
|
||||
marginHorizontal: 24,
|
||||
marginTop: 16,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 3,
|
||||
elevation: 2,
|
||||
},
|
||||
cardIconWrapper: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 20,
|
||||
backgroundColor: COLORS.backgroundSecondary,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginRight: 16,
|
||||
},
|
||||
cardContent: {
|
||||
flex: 1,
|
||||
},
|
||||
cardTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
color: COLORS.textPrimary,
|
||||
marginBottom: 4,
|
||||
},
|
||||
cardSubtitle: {
|
||||
fontSize: 14,
|
||||
color: COLORS.textSecondary,
|
||||
},
|
||||
emptyContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
emptyText: {
|
||||
fontSize: 16,
|
||||
color: COLORS.textSecondary,
|
||||
},
|
||||
fab: {
|
||||
position: 'absolute',
|
||||
right: 24,
|
||||
bottom: 40,
|
||||
width: 56,
|
||||
height: 56,
|
||||
borderRadius: 28,
|
||||
backgroundColor: COLORS.primary,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 4,
|
||||
elevation: 4,
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: 16,
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: COLORS.textPrimary,
|
||||
marginLeft: 12,
|
||||
},
|
||||
modalBackdrop: {
|
||||
flex: 1,
|
||||
backgroundColor: 'rgba(0,0,0,0.4)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 24,
|
||||
},
|
||||
modalCard: {
|
||||
width: '100%',
|
||||
backgroundColor: COLORS.white,
|
||||
borderRadius: 12,
|
||||
padding: 20,
|
||||
elevation: 5,
|
||||
},
|
||||
modalCloseSmall: {
|
||||
position: 'absolute',
|
||||
top: 8,
|
||||
right: 8,
|
||||
},
|
||||
modalTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: COLORS.textPrimary,
|
||||
marginBottom: 16,
|
||||
textAlign: 'center',
|
||||
},
|
||||
divider: {
|
||||
height: 1,
|
||||
backgroundColor: COLORS.gray[200],
|
||||
marginVertical: 12,
|
||||
},
|
||||
itemWrap: {
|
||||
marginBottom: 16,
|
||||
},
|
||||
itemLabel: {
|
||||
fontSize: 15,
|
||||
color: COLORS.textSecondary,
|
||||
marginBottom: 4,
|
||||
},
|
||||
itemValue: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
color: COLORS.textPrimary,
|
||||
},
|
||||
});
|
||||
|
||||
export default LoanRemainingOrdersScreen;
|
||||
@@ -101,6 +101,7 @@ const styles = StyleSheet.create({
|
||||
paddingTop: 16,
|
||||
paddingBottom: 24,
|
||||
backgroundColor: COLORS.white,
|
||||
marginBottom: 16,
|
||||
},
|
||||
greeting: {
|
||||
fontSize: 16,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
@@ -12,6 +13,8 @@ import { Ionicons } from '@expo/vector-icons';
|
||||
import { COLORS } from '../../constants/colors';
|
||||
|
||||
const MenuScreen = () => {
|
||||
const navigation = useNavigation();
|
||||
|
||||
const menuSections = [
|
||||
{
|
||||
title: 'Karz',
|
||||
@@ -41,8 +44,13 @@ const MenuScreen = () => {
|
||||
];
|
||||
|
||||
const handleMenuItemPress = (item) => {
|
||||
console.log('Menu item pressed:', item.title);
|
||||
// Handle navigation or action here
|
||||
if (item.id === 2) {
|
||||
navigation.navigate('LoanRemainingOrders');
|
||||
} else if (item.id === 3) {
|
||||
navigation.navigate('LoanPaidOffLetterOrders');
|
||||
} else {
|
||||
console.log('Menu item pressed:', item.title);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -93,6 +93,127 @@ class ApiService {
|
||||
return authService.unblockCard(cardId);
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Loan Remaining Order (Karzyň galyndysy)
|
||||
// ================================
|
||||
|
||||
async getLoanRemainingOrders() {
|
||||
try {
|
||||
const data = await authService.getLoanRemainingOrders();
|
||||
return { success: true, data };
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async createLoanRemainingOrder(accountNumber) {
|
||||
try {
|
||||
const response = await authService.createLoanRemainingOrder(accountNumber);
|
||||
return { success: true, message: response.message };
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async getLoanRemainingOrder(orderId) {
|
||||
try {
|
||||
const data = await authService.getLoanRemainingOrder(orderId);
|
||||
return { success: true, data };
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async updateLoanRemainingOrder(orderId, accountNumber) {
|
||||
try {
|
||||
const response = await authService.updateLoanRemainingOrder(orderId, accountNumber);
|
||||
return { success: true, message: response.message };
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async deleteLoanRemainingOrder(orderId) {
|
||||
try {
|
||||
const response = await authService.deleteLoanRemainingOrder(orderId);
|
||||
return { success: true, message: response.message };
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// Quick loan remaining check
|
||||
async getLoanRemainingBalance(accountNumber, passportSerie = null, passportId = null) {
|
||||
try {
|
||||
let raw = await authService.getLoanRemainingBalance(accountNumber, passportSerie, passportId);
|
||||
|
||||
// Some endpoints return JSON-string (e.g. "{\"branchName\":...}") instead of object
|
||||
if (typeof raw === 'string') {
|
||||
try {
|
||||
raw = JSON.parse(raw);
|
||||
} catch (_) {
|
||||
// leave as-is
|
||||
}
|
||||
}
|
||||
|
||||
if (raw && raw.errCode && raw.errCode > 0) {
|
||||
return { success: false, error: raw.message || 'Not found', data: raw };
|
||||
}
|
||||
return { success: true, data: raw };
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Loan Paid-Off Letter Orders
|
||||
// ================================
|
||||
|
||||
async getLoanPaidOffLetterOrders() {
|
||||
try {
|
||||
const data = await authService.getLoanPaidOffLetterOrders();
|
||||
return { success: true, data };
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async createLoanPaidOffLetterOrder(payload) {
|
||||
try {
|
||||
const response = await authService.createLoanPaidOffLetterOrder(payload);
|
||||
return { success: true, message: response.message };
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async getLoanPaidOffLetterOrder(orderId) {
|
||||
try {
|
||||
const data = await authService.getLoanPaidOffLetterOrder(orderId);
|
||||
return { success: true, data };
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async updateLoanPaidOffLetterOrder(orderId, payload) {
|
||||
try {
|
||||
const response = await authService.updateLoanPaidOffLetterOrder(orderId, payload);
|
||||
return { success: true, message: response.message };
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async deleteLoanPaidOffLetterOrder(orderId) {
|
||||
try {
|
||||
const response = await authService.deleteLoanPaidOffLetterOrder(orderId);
|
||||
return { success: true, message: response.message };
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// Utility methods for common operations
|
||||
async getUserDashboardData() {
|
||||
try {
|
||||
|
||||
@@ -164,6 +164,114 @@ class AuthService {
|
||||
async deleteAccount() {
|
||||
return this.makeRequest('/user/delete-account', null, true, 'DELETE');
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Loan Remaining Order (Karzyň galyndysy)
|
||||
// ================================
|
||||
|
||||
// Helper to read cached user data (for passport details)
|
||||
async getStoredUser() {
|
||||
try {
|
||||
const raw = await AsyncStorage.getItem('user_data');
|
||||
return raw ? JSON.parse(raw) : null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// LIST orders
|
||||
async getLoanRemainingOrders() {
|
||||
return this.makeRequest('/loan-remaining-order', null, true, 'GET');
|
||||
}
|
||||
|
||||
// CREATE order (requires only account number – passport details are fetched from user profile)
|
||||
async createLoanRemainingOrder(accountNumber) {
|
||||
const user = await this.getStoredUser();
|
||||
if (!user?.passport_serie || !user?.passport_id) {
|
||||
throw new Error('Passport details are missing from profile');
|
||||
}
|
||||
const payload = {
|
||||
passport_serie: user.passport_serie,
|
||||
passport_id: user.passport_id,
|
||||
account_number: accountNumber,
|
||||
};
|
||||
return this.makeRequest('/loan-remaining-order', payload, true, 'POST');
|
||||
}
|
||||
|
||||
// SHOW order details
|
||||
async getLoanRemainingOrder(orderId) {
|
||||
return this.makeRequest(`/loan-remaining-order/${orderId}`, null, true, 'GET');
|
||||
}
|
||||
|
||||
// UPDATE order (only account number can change; passport details stay the same)
|
||||
async updateLoanRemainingOrder(orderId, accountNumber) {
|
||||
const user = await this.getStoredUser();
|
||||
if (!user?.passport_serie || !user?.passport_id) {
|
||||
throw new Error('Passport details are missing from profile');
|
||||
}
|
||||
const payload = {
|
||||
passport_serie: user.passport_serie,
|
||||
passport_id: user.passport_id,
|
||||
account_number: accountNumber,
|
||||
};
|
||||
return this.makeRequest(`/loan-remaining-order/${orderId}`, payload, true, 'POST');
|
||||
}
|
||||
|
||||
// DELETE order
|
||||
async deleteLoanRemainingOrder(orderId) {
|
||||
return this.makeRequest(`/loan-remaining-order/${orderId}`, null, true, 'DELETE');
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Loan remaining quick check (returns remaining balance info)
|
||||
// ================================
|
||||
|
||||
async getLoanRemainingBalance(accountNumber, passportSerie = null, passportId = null) {
|
||||
let serie = passportSerie;
|
||||
let pid = passportId;
|
||||
|
||||
if (!serie || !pid) {
|
||||
const user = await this.getStoredUser();
|
||||
serie = user?.passport_serie;
|
||||
pid = user?.passport_id;
|
||||
}
|
||||
|
||||
if (!serie || !pid) {
|
||||
throw new Error('Passport details are missing');
|
||||
}
|
||||
|
||||
const query = `passport_serie=${encodeURIComponent(serie)}&passport_id=${encodeURIComponent(pid)}&account_number=${encodeURIComponent(accountNumber)}`;
|
||||
return this.makeRequest(`/loan-remaining?${query}`, null, true, 'GET');
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Loan Paid-Off Letter Orders
|
||||
// ================================
|
||||
|
||||
// LIST
|
||||
async getLoanPaidOffLetterOrders() {
|
||||
return this.makeRequest('/loan-paid-off-letter-orders', null, true, 'GET');
|
||||
}
|
||||
|
||||
// CREATE
|
||||
async createLoanPaidOffLetterOrder(data) {
|
||||
return this.makeRequest('/loan-paid-off-letter-orders', data, true, 'POST');
|
||||
}
|
||||
|
||||
// SHOW
|
||||
async getLoanPaidOffLetterOrder(orderId) {
|
||||
return this.makeRequest(`/loan-paid-off-letter-orders/${orderId}`, null, true, 'GET');
|
||||
}
|
||||
|
||||
// UPDATE
|
||||
async updateLoanPaidOffLetterOrder(orderId, data) {
|
||||
return this.makeRequest(`/loan-paid-off-letter-orders/${orderId}`, data, true, 'POST');
|
||||
}
|
||||
|
||||
// DELETE
|
||||
async deleteLoanPaidOffLetterOrder(orderId) {
|
||||
return this.makeRequest(`/loan-paid-off-letter-orders/${orderId}`, null, true, 'DELETE');
|
||||
}
|
||||
}
|
||||
|
||||
export default new AuthService();
|
||||
Reference in New Issue
Block a user