card pins ready
This commit is contained in:
@@ -18,6 +18,9 @@ import CardBalanceOrderDetailsScreen from '../screens/Card/CardBalanceOrderDetai
|
|||||||
import CardRequisiteOrdersScreen from '../screens/Card/CardRequisiteOrdersScreen';
|
import CardRequisiteOrdersScreen from '../screens/Card/CardRequisiteOrdersScreen';
|
||||||
import CreateCardRequisiteOrderScreen from '../screens/Card/CreateCardRequisiteOrderScreen';
|
import CreateCardRequisiteOrderScreen from '../screens/Card/CreateCardRequisiteOrderScreen';
|
||||||
import CardRequisiteOrderDetailsScreen from '../screens/Card/CardRequisiteOrderDetailsScreen';
|
import CardRequisiteOrderDetailsScreen from '../screens/Card/CardRequisiteOrderDetailsScreen';
|
||||||
|
import CardPinOrdersScreen from '../screens/Card/CardPinOrdersScreen';
|
||||||
|
import CreateCardPinOrderScreen from '../screens/Card/CreateCardPinOrderScreen';
|
||||||
|
import CardPinOrderDetailsScreen from '../screens/Card/CardPinOrderDetailsScreen';
|
||||||
|
|
||||||
const Stack = createStackNavigator();
|
const Stack = createStackNavigator();
|
||||||
|
|
||||||
@@ -41,6 +44,9 @@ const MenuNavigator = () => (
|
|||||||
<Stack.Screen name="CardRequisiteOrders" component={CardRequisiteOrdersScreen} />
|
<Stack.Screen name="CardRequisiteOrders" component={CardRequisiteOrdersScreen} />
|
||||||
<Stack.Screen name="CreateCardRequisiteOrder" component={CreateCardRequisiteOrderScreen} />
|
<Stack.Screen name="CreateCardRequisiteOrder" component={CreateCardRequisiteOrderScreen} />
|
||||||
<Stack.Screen name="CardRequisiteOrderDetails" component={CardRequisiteOrderDetailsScreen} />
|
<Stack.Screen name="CardRequisiteOrderDetails" component={CardRequisiteOrderDetailsScreen} />
|
||||||
|
<Stack.Screen name="CardPinOrders" component={CardPinOrdersScreen} />
|
||||||
|
<Stack.Screen name="CreateCardPinOrder" component={CreateCardPinOrderScreen} />
|
||||||
|
<Stack.Screen name="CardPinOrderDetails" component={CardPinOrderDetailsScreen} />
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
145
src/screens/Card/CardPinOrderDetailsScreen.js
Normal file
145
src/screens/Card/CardPinOrderDetailsScreen.js
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, Alert, ScrollView, SafeAreaView, Image, Linking } from 'react-native';
|
||||||
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
|
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||||
|
import { StatusBar } from 'expo-status-bar';
|
||||||
|
import apiService from '../../services/apiService';
|
||||||
|
import { COLORS } from '../../constants/colors';
|
||||||
|
import { useBaseEnums } from '../../contexts/BaseEnumsContext';
|
||||||
|
|
||||||
|
const DetailRow = ({ label, value, showBorder=true }) => (
|
||||||
|
<View style={[styles.detailRow, showBorder && styles.detailRowBorder]}>
|
||||||
|
<Text style={styles.detailKey}>{label}</Text>
|
||||||
|
<Text style={styles.detailValue}>{String(value)}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
const CardPinOrderDetailsScreen = () => {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
const { params } = useRoute();
|
||||||
|
const orderId = params?.orderId;
|
||||||
|
const { getLabel, getBranches } = useBaseEnums();
|
||||||
|
|
||||||
|
const [loading,setLoading]=useState(true);
|
||||||
|
const [order,setOrder]=useState(null);
|
||||||
|
const [branchName,setBranchName]=useState('');
|
||||||
|
|
||||||
|
const fetchDetails=async()=>{
|
||||||
|
setLoading(true);
|
||||||
|
const res = await apiService.getCardPinOrder(orderId);
|
||||||
|
if(res.success){
|
||||||
|
setOrder(res.data);
|
||||||
|
if(res.data?.region && res.data?.branch_id){
|
||||||
|
(async()=>{ try{ const brs = await getBranches(res.data.region); const found=brs.find(b=>String(b.id)===String(res.data.branch_id)); if(found) setBranchName(found.name);}catch(e){} })();
|
||||||
|
}
|
||||||
|
} else { Alert.alert('Ýalňyşlyk', res.error || 'Ýalňyşlyk'); }
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
useEffect(()=>{fetchDetails();},[]);
|
||||||
|
|
||||||
|
const handleDelete=()=>{ Alert.alert('Tassykla','Pozmakçy my?',[{text:'Goýbolsun',style:'cancel'},{text:'Poz',style:'destructive',onPress:async()=>{
|
||||||
|
const r=await apiService.deleteCardPinOrder(orderId);
|
||||||
|
if(r.success){ Alert.alert('Pozuldy', r.message || 'Pozuldy', [{text:'OK',onPress:()=>navigation.goBack()}]); } else { Alert.alert('Ýalňyşlyk', r.error || 'Ýalňyşlyk'); }
|
||||||
|
}}]); };
|
||||||
|
|
||||||
|
if(loading){ return <View style={styles.centered}><ActivityIndicator size="large" color={COLORS.primary}/></View>; }
|
||||||
|
if(!order){ return <View style={styles.centered}><Text>Maglumat ýok</Text></View>; }
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={styles.container}>
|
||||||
|
<StatusBar style="dark" />
|
||||||
|
<TouchableOpacity style={styles.backBtn} onPress={()=>navigation.goBack()}>
|
||||||
|
<Ionicons name="close" size={28} color={COLORS.textPrimary}/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<ScrollView contentContainerStyle={{paddingBottom:120,paddingHorizontal:24}}>
|
||||||
|
<Text style={styles.title}>Kart pin bukjalar</Text>
|
||||||
|
|
||||||
|
{/* Card info */}
|
||||||
|
<View style={styles.detailCard}>
|
||||||
|
<DetailRow label="ID" value={order.id} />
|
||||||
|
{order.card_type_id && <DetailRow label="Görnüşi" value={getLabel('card_types', order.card_type_id)} />}
|
||||||
|
{order.card_number && <DetailRow label="Kart belgisi" value={order.card_number} showBorder={false} />}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Location */}
|
||||||
|
{(order.region || branchName) && (
|
||||||
|
<>
|
||||||
|
<Text style={styles.sectionTitle}>Lokasiýa</Text>
|
||||||
|
<View style={styles.detailCard}>
|
||||||
|
{order.region && <DetailRow label="Welaýat" value={getLabel('regions', order.region)} />}
|
||||||
|
{(branchName || order.branch_id) && <DetailRow label="Şahamça" value={branchName || order.branch_id} showBorder={false} />}
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Personal */}
|
||||||
|
<Text style={styles.sectionTitle}>Şahsy maglumatlar</Text>
|
||||||
|
<View style={styles.detailCard}>
|
||||||
|
{order.customer_name && <DetailRow label="Ady" value={order.customer_name} />}
|
||||||
|
{order.customer_surname && <DetailRow label="Familiýasy" value={order.customer_surname} />}
|
||||||
|
{order.customer_patronic_name && <DetailRow label="Atasynyň ady" value={order.customer_patronic_name} />}
|
||||||
|
{order.born_at && <DetailRow label="Doglan güni" value={order.born_at} />}
|
||||||
|
{order.phone && <DetailRow label="Telefon" value={`+993 ${order.phone}`} showBorder={false} />}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Passport */}
|
||||||
|
<Text style={styles.sectionTitle}>Pasport</Text>
|
||||||
|
<View style={styles.detailCard}>
|
||||||
|
{(order.passport_serie || order.passport_id) && <DetailRow label="Seriýa/Belgi" value={`${order.passport_serie || ''} ${order.passport_id || ''}`.trim()} showBorder={false} />}
|
||||||
|
<View style={styles.imageGrid}>
|
||||||
|
{order.passport_one && (
|
||||||
|
<TouchableOpacity onPress={()=>Linking.openURL(order.passport_one)} style={styles.imageWrapper}>
|
||||||
|
<Image source={{uri:order.passport_one}} style={styles.passportImage}/>
|
||||||
|
<Text style={styles.imageLabel}>1-nji sah.</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
{order.passport_two && (
|
||||||
|
<TouchableOpacity onPress={()=>Linking.openURL(order.passport_two)} style={styles.imageWrapper}>
|
||||||
|
<Image source={{uri:order.passport_two}} style={styles.passportImage}/>
|
||||||
|
<Text style={styles.imageLabel}>2-3 sah.</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
{order.passport_three && (
|
||||||
|
<TouchableOpacity onPress={()=>Linking.openURL(order.passport_three)} style={styles.imageWrapper}>
|
||||||
|
<Image source={{uri:order.passport_three}} style={styles.passportImage}/>
|
||||||
|
<Text style={styles.imageLabel}>8-9 sah.</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
{order.passport_four && (
|
||||||
|
<TouchableOpacity onPress={()=>Linking.openURL(order.passport_four)} style={styles.imageWrapper}>
|
||||||
|
<Image source={{uri:order.passport_four}} style={styles.passportImage}/>
|
||||||
|
<Text style={styles.imageLabel}>32 sah.</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<TouchableOpacity style={styles.deleteBtn} onPress={handleDelete}>
|
||||||
|
<Text style={styles.deleteText}>Poz</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</ScrollView>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container:{flex:1,backgroundColor:COLORS.backgroundSecondary,paddingTop:40},
|
||||||
|
centered:{flex:1,justifyContent:'center',alignItems:'center'},
|
||||||
|
backBtn:{alignSelf:'flex-end',marginRight:24,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',paddingVertical:12},
|
||||||
|
detailRowBorder:{borderBottomWidth:1,borderBottomColor:COLORS.border},
|
||||||
|
detailKey:{fontWeight:'600',color:COLORS.textSecondary},
|
||||||
|
detailValue:{color:COLORS.textPrimary},
|
||||||
|
sectionTitle:{fontWeight:'700',fontSize:18,marginTop:24,marginBottom:12,color:COLORS.textPrimary},
|
||||||
|
imageGrid:{flexDirection:'row',flexWrap:'wrap',justifyContent:'space-between',marginTop:12},
|
||||||
|
imageWrapper:{width:'48%',marginBottom:16},
|
||||||
|
passportImage:{width:'100%',aspectRatio:4/3,borderRadius:8,backgroundColor:'#f0f0f0'},
|
||||||
|
imageLabel:{marginTop:4,fontSize:12,color:COLORS.textSecondary,textAlign:'center'},
|
||||||
|
deleteBtn:{backgroundColor:COLORS.error,paddingVertical:16,borderRadius:8,alignItems:'center',marginTop:16},
|
||||||
|
deleteText:{color:COLORS.white,fontSize:16,fontWeight:'600'},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default CardPinOrderDetailsScreen;
|
||||||
123
src/screens/Card/CardPinOrdersScreen.js
Normal file
123
src/screens/Card/CardPinOrdersScreen.js
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import React, { useState, useCallback } from 'react';
|
||||||
|
import { View, Text, StyleSheet, FlatList, ActivityIndicator, TouchableOpacity, RefreshControl, SafeAreaView } from 'react-native';
|
||||||
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
|
import { useNavigation, useFocusEffect } from '@react-navigation/native';
|
||||||
|
import { StatusBar } from 'expo-status-bar';
|
||||||
|
import apiService from '../../services/apiService';
|
||||||
|
import { COLORS } from '../../constants/colors';
|
||||||
|
import { useBaseEnums } from '../../contexts/BaseEnumsContext';
|
||||||
|
|
||||||
|
const CARD_BG = '#F1F9F1';
|
||||||
|
const CIRCLE_BG = '#A2E4A4';
|
||||||
|
|
||||||
|
const CardPinOrdersScreen = () => {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
const [orders, setOrders] = useState([]);
|
||||||
|
const { getLabel } = useBaseEnums();
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
|
|
||||||
|
const fetchOrders = async () => {
|
||||||
|
try {
|
||||||
|
const res = await apiService.getCardPinOrders();
|
||||||
|
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 = (item) => {
|
||||||
|
navigation.navigate('CardPinOrderDetails', { orderId: item.id });
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderItem = ({ item }) => {
|
||||||
|
const masked = item.card_number ? `${item.card_number.slice(0,6)}******${item.card_number.slice(-4)}` : '';
|
||||||
|
const created = item.created_at ? new Date(item.created_at).toLocaleDateString() : '';
|
||||||
|
const typeLabel = getLabel('card_types', item.card_type_id);
|
||||||
|
const passportLine = item.passport_serie && item.passport_id ? `${item.passport_serie} ${item.passport_id}` : '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity style={styles.card} onPress={() => handleItemPress(item)}>
|
||||||
|
<View style={styles.circle}>
|
||||||
|
<Text style={styles.circleText}>{item.id}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.cardContent}>
|
||||||
|
<Text style={styles.cardNumber}>{masked}</Text>
|
||||||
|
{typeLabel && <Text style={styles.typeText}>{typeLabel}</Text>}
|
||||||
|
{passportLine !== '' && <Text style={styles.passportText}>{passportLine}</Text>}
|
||||||
|
<Text style={styles.dateText}>{created}</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<View style={styles.centered}>
|
||||||
|
<ActivityIndicator size="large" color={COLORS.primary} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={styles.container}>
|
||||||
|
<StatusBar style="dark" />
|
||||||
|
<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}>Kart pin bukjalar</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}>Heniz sargyt ýok</Text>}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TouchableOpacity style={styles.fab} onPress={() => navigation.navigate('CreateCardPinOrder')}>
|
||||||
|
<Ionicons name="add" size={28} color={COLORS.white} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: { flex: 1, backgroundColor: COLORS.backgroundSecondary },
|
||||||
|
centered: { flex: 1, alignItems: 'center', justifyContent: 'center' },
|
||||||
|
header: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 24, paddingVertical: 16 },
|
||||||
|
headerTitle: { fontSize: 20, fontWeight: 'bold', color: COLORS.textPrimary },
|
||||||
|
card: { flexDirection: 'row', backgroundColor: CARD_BG, marginHorizontal: 24, marginTop: 16, borderRadius: 12, padding: 16, alignItems: 'center' },
|
||||||
|
circle: { width: 40, height: 40, borderRadius: 20, backgroundColor: CIRCLE_BG, alignItems: 'center', justifyContent: 'center', marginRight: 16 },
|
||||||
|
circleText: { color: COLORS.white, fontWeight: '600' },
|
||||||
|
cardContent: { flex: 1 },
|
||||||
|
cardNumber: { fontWeight: '700', color: COLORS.textPrimary, marginBottom: 4 },
|
||||||
|
dateText: { color: COLORS.textSecondary, fontSize: 12 },
|
||||||
|
emptyContainer: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
||||||
|
emptyText: { fontSize: 16, color: COLORS.textSecondary },
|
||||||
|
fab: { position: 'absolute', bottom: 32, right: 32, backgroundColor: COLORS.primary, width: 56, height: 56, borderRadius: 28, alignItems: 'center', justifyContent: 'center', elevation: 4 },
|
||||||
|
typeText:{color:COLORS.textSecondary,fontSize:14},
|
||||||
|
passportText:{color:COLORS.textSecondary,fontSize:12},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default CardPinOrdersScreen;
|
||||||
149
src/screens/Card/CreateCardPinOrderScreen.js
Normal file
149
src/screens/Card/CreateCardPinOrderScreen.js
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, SafeAreaView, ScrollView } from 'react-native';
|
||||||
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
import { StatusBar } from 'expo-status-bar';
|
||||||
|
import apiService from '../../services/apiService';
|
||||||
|
import { COLORS } from '../../constants/colors';
|
||||||
|
import Input from '../../components/Input';
|
||||||
|
import SelectInput from '../../components/SelectInput';
|
||||||
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
|
import DateInput from '../../components/DateInput';
|
||||||
|
import ImageInput from '../../components/ImageInput';
|
||||||
|
import { useBaseEnums } from '../../contexts/BaseEnumsContext';
|
||||||
|
|
||||||
|
const PASSPORT_SERIES = ['I-AS','I-MR','II-MR','I-AH','II-AH','I-LB','II-LB','I-BN','II-BN','I-DZ','II-DZ'];
|
||||||
|
|
||||||
|
const formatCardNumber = (val) => {
|
||||||
|
const digits = String(val).replace(/[^0-9]/g,'').slice(0,16);
|
||||||
|
return (digits.match(/.{1,4}/g)||[]).join(' ');
|
||||||
|
};
|
||||||
|
|
||||||
|
const CreateCardPinOrderScreen = () => {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
const { user } = useAuth();
|
||||||
|
const { getOptions, getBranches } = useBaseEnums();
|
||||||
|
|
||||||
|
const [passportSerie,setPassportSerie]=useState('');
|
||||||
|
const [passportId,setPassportId]=useState('');
|
||||||
|
const [cardNumber,setCardNumber]=useState('');
|
||||||
|
const [cardType,setCardType]=useState('');
|
||||||
|
const [region,setRegion]=useState('');
|
||||||
|
const [branchId,setBranchId]=useState('');
|
||||||
|
const [customerName,setCustomerName]=useState('');
|
||||||
|
const [customerSurname,setCustomerSurname]=useState('');
|
||||||
|
const [customerPatro,setCustomerPatro]=useState('');
|
||||||
|
const [bornAt,setBornAt]=useState('');
|
||||||
|
const [phone,setPhone]=useState('');
|
||||||
|
const [passportOne,setPassportOne]=useState(null);
|
||||||
|
const [passportTwo,setPassportTwo]=useState(null);
|
||||||
|
const [passportThree,setPassportThree]=useState(null);
|
||||||
|
const [passportFour,setPassportFour]=useState(null);
|
||||||
|
const [branchOptions,setBranchOptions]=useState([]);
|
||||||
|
const [loading,setLoading]=useState(false);
|
||||||
|
|
||||||
|
const cardTypeOptions = getOptions('card_types');
|
||||||
|
const regionOptions = getOptions('regions');
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
if(user){
|
||||||
|
if(user.passport_serie) setPassportSerie(user.passport_serie);
|
||||||
|
if(user.passport_id) setPassportId(String(user.passport_id));
|
||||||
|
if(user.card_number) setCardNumber(formatCardNumber(user.card_number));
|
||||||
|
if(user.region) setRegion(user.region);
|
||||||
|
if(user.phone) setPhone(String(user.phone).slice(-8));
|
||||||
|
if(user.name){ const parts=user.name.split(' '); setCustomerName(parts[0]); setCustomerSurname(parts[1]||''); }
|
||||||
|
}
|
||||||
|
},[user]);
|
||||||
|
|
||||||
|
useEffect(()=>{(async()=>{
|
||||||
|
if(region){ const b = await getBranches(region); setBranchOptions(b.map(br=>({label:br.name,value:br.id})));} else {setBranchOptions([]);} })();},[region]);
|
||||||
|
|
||||||
|
const handleCardNumberChange = (v)=> setCardNumber(formatCardNumber(v));
|
||||||
|
|
||||||
|
const handleSubmit = async()=>{
|
||||||
|
if(!cardType||!cardNumber.trim()||!region||!branchId||!customerName||!customerSurname||!bornAt||!phone||!passportSerie||!passportId.trim()||!passportOne||!passportTwo||!passportThree||!passportFour){
|
||||||
|
Alert.alert('Ýalňyşlyk','Ähli zerur meýdançalar doldurylmaly'); return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = new FormData();
|
||||||
|
payload.append('card_type_id', cardType);
|
||||||
|
payload.append('card_number', cardNumber.replace(/\s+/g,''));
|
||||||
|
payload.append('region', region);
|
||||||
|
payload.append('branch_id', branchId);
|
||||||
|
payload.append('customer_name', customerName);
|
||||||
|
payload.append('customer_surname', customerSurname);
|
||||||
|
if(customerPatro) payload.append('customer_patronic_name', customerPatro);
|
||||||
|
payload.append('born_at', bornAt);
|
||||||
|
payload.append('phone', parseInt(phone));
|
||||||
|
payload.append('passport_serie', passportSerie);
|
||||||
|
payload.append('passport_id', passportId.trim());
|
||||||
|
payload.append('passport_one', { uri: passportOne, name: 'p1.jpg', type:'image/jpeg'});
|
||||||
|
payload.append('passport_two', { uri: passportTwo, name: 'p2.jpg', type:'image/jpeg'});
|
||||||
|
payload.append('passport_three', { uri: passportThree, name: 'p3.jpg', type:'image/jpeg'});
|
||||||
|
payload.append('passport_four', { uri: passportFour, name: 'p4.jpg', type:'image/jpeg'});
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
const res = await apiService.createCardPinOrder(payload);
|
||||||
|
setLoading(false);
|
||||||
|
if(res.success){
|
||||||
|
Alert.alert('Üstünlik', res.message || 'Döredildi', [{ text:'OK', onPress:()=>navigation.goBack() }]);
|
||||||
|
} else {
|
||||||
|
Alert.alert('Ýalňyşlyk', res.error || 'Ýalňyşlyk ýüze çykdy');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={styles.container}>
|
||||||
|
<StatusBar style="dark" />
|
||||||
|
<ScrollView contentContainerStyle={{paddingHorizontal:24,paddingTop:40,paddingBottom:40}} showsVerticalScrollIndicator={false}>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<SelectInput label="*Görnüşi" value={cardType} onValueChange={setCardType} options={cardTypeOptions} placeholder="Saýla" />
|
||||||
|
<Input label="*Kart belgisi" placeholder="9934 6121 0000 0243" value={cardNumber} onChangeText={handleCardNumberChange} keyboardType="numeric" maxLength={19} autoCapitalize="none" autoCorrect={false}/>
|
||||||
|
|
||||||
|
{/* Location */}
|
||||||
|
<Text style={styles.sectionTitle}>Lokasiýa</Text>
|
||||||
|
<SelectInput label="*Welaýat" value={region} onValueChange={setRegion} options={regionOptions} placeholder="Saýla" />
|
||||||
|
<SelectInput label="*Şahamça" value={branchId} onValueChange={setBranchId} options={branchOptions.map(b=>({label:b.label,value:b.value}))} placeholder="Saýla" />
|
||||||
|
|
||||||
|
{/* Personal */}
|
||||||
|
<Text style={styles.sectionTitle}>Şahsy maglumatlar</Text>
|
||||||
|
<Input label="*Ady" value={customerName} onChangeText={setCustomerName} />
|
||||||
|
<Input label="*Familiýasy" value={customerSurname} onChangeText={setCustomerSurname} />
|
||||||
|
<Input label="Atasynyň ady" value={customerPatro} onChangeText={setCustomerPatro} />
|
||||||
|
<DateInput label="*Doglan güni" value={bornAt} onChange={setBornAt} />
|
||||||
|
<Input label="*Telefon" value={phone} onChangeText={setPhone} keyboardType="numeric" maxLength={8}/>
|
||||||
|
|
||||||
|
{/* Passport */}
|
||||||
|
<Text style={styles.sectionTitle}>Pasport</Text>
|
||||||
|
<SelectInput label="*Passport seriýasy" value={passportSerie} onValueChange={setPassportSerie} options={PASSPORT_SERIES.map(v=>({label:v,value:v}))} placeholder="Saýla" />
|
||||||
|
<Input label="*Passport belgisi" value={passportId} onChangeText={setPassportId} keyboardType="numeric" />
|
||||||
|
<View style={{flexDirection:'row',flexWrap:'wrap',justifyContent:'space-between'}}>
|
||||||
|
<ImageInput label="Pasport (sahypa 1) *" image={passportOne} onChange={setPassportOne} />
|
||||||
|
<ImageInput label="Pasport (2-3 sah.) *" image={passportTwo} onChange={setPassportTwo} />
|
||||||
|
<ImageInput label="Pasport (8-9 sah.) *" image={passportThree} onChange={setPassportThree} />
|
||||||
|
<ImageInput label="Pasport (32 sah.) *" image={passportFour} onChange={setPassportFour} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<TouchableOpacity style={styles.submitBtn} onPress={handleSubmit} disabled={loading}>
|
||||||
|
{loading ? <ActivityIndicator color={COLORS.white}/> : <Text style={styles.submitText}>Ýatda sakla</Text>}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</ScrollView>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container:{flex:1,backgroundColor:COLORS.backgroundSecondary},
|
||||||
|
backBtn:{marginBottom:24},
|
||||||
|
title:{fontSize:24,fontWeight:'bold',color:COLORS.textPrimary,marginBottom:24},
|
||||||
|
sectionTitle:{fontWeight:'700',fontSize:18,marginTop:24,marginBottom:12,color:COLORS.textPrimary},
|
||||||
|
submitBtn:{marginTop:32,backgroundColor:COLORS.primary,paddingVertical:16,borderRadius:8,alignItems:'center'},
|
||||||
|
submitText:{color:COLORS.white,fontSize:16,fontWeight:'600'},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default CreateCardPinOrderScreen;
|
||||||
@@ -188,6 +188,11 @@ const CreateLoanOrderScreen = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (passportId.length != 6) {
|
||||||
|
Alert.alert('Ýalňyşlyk', 'Passport nomeri 6 san bolmaly');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const rawCardNumber = cardNumber.replace(/[^0-9]/g, '');
|
const rawCardNumber = cardNumber.replace(/[^0-9]/g, '');
|
||||||
if (rawCardNumber.length !== 16) {
|
if (rawCardNumber.length !== 16) {
|
||||||
Alert.alert('Ýalňyşlyk', 'Kart belgisi dogry dolduryň (16 sany rakam)');
|
Alert.alert('Ýalňyşlyk', 'Kart belgisi dogry dolduryň (16 sany rakam)');
|
||||||
@@ -345,7 +350,9 @@ const CreateLoanOrderScreen = () => {
|
|||||||
<Input label={req('Häzirki ýaşaýyş ýeri')} placeholder="Kemine 100/200" value={realAddress} onChangeText={setRealAddress} error={submitted && !realAddress} />
|
<Input label={req('Häzirki ýaşaýyş ýeri')} placeholder="Kemine 100/200" value={realAddress} onChangeText={setRealAddress} error={submitted && !realAddress} />
|
||||||
|
|
||||||
<SelectInput label={req('Passport seriýasy')} value={passportSerie} options={passportSeriesOptions} onValueChange={setPassportSerie} placeholder="Saýla" error={submitted && !passportSerie} />
|
<SelectInput label={req('Passport seriýasy')} value={passportSerie} options={passportSeriesOptions} onValueChange={setPassportSerie} placeholder="Saýla" error={submitted && !passportSerie} />
|
||||||
<Input label={req('Passport nomeri')} placeholder="100999" value={passportId} onChangeText={setPassportId} keyboardType="numeric" error={submitted && !passportId} />
|
|
||||||
|
<Input label={req('Passport nomeri')} placeholder="100999" value={passportId} onChangeText={setPassportId} keyboardType="numeric" error={submitted && !passportId} maxLength={6} />
|
||||||
|
|
||||||
<DateInput label={req('Passport berlen senesi')} value={passportGivenAt} onChange={setPassportGivenAt} error={submitted && !passportGivenAt} />
|
<DateInput label={req('Passport berlen senesi')} value={passportGivenAt} onChange={setPassportGivenAt} error={submitted && !passportGivenAt} />
|
||||||
<Input label={req('Kim tarapyndan berildi')} placeholder="Ashgabat polisiýasy tarapyndan" value={passportGivenBy} onChangeText={setPassportGivenBy} error={submitted && !passportGivenBy} />
|
<Input label={req('Kim tarapyndan berildi')} placeholder="Ashgabat polisiýasy tarapyndan" value={passportGivenBy} onChangeText={setPassportGivenBy} error={submitted && !passportGivenBy} />
|
||||||
<Input label={req('Doglan ýeri (passport)')} placeholder="Ashgabat" value={bornPlace} onChangeText={setBornPlace} error={submitted && !bornPlace} />
|
<Input label={req('Doglan ýeri (passport)')} placeholder="Ashgabat" value={bornPlace} onChangeText={setBornPlace} error={submitted && !bornPlace} />
|
||||||
@@ -391,7 +398,7 @@ const CreateLoanOrderScreen = () => {
|
|||||||
|
|
||||||
{/* Guarantor passport */}
|
{/* Guarantor passport */}
|
||||||
<SelectInput label={req('Pasport seriýasy')} value={guarantorPassportSerie} options={passportSeriesOptions} onValueChange={setGuarantorPassportSerie} placeholder="Saýla" error={submitted && !guarantorPassportSerie} />
|
<SelectInput label={req('Pasport seriýasy')} value={guarantorPassportSerie} options={passportSeriesOptions} onValueChange={setGuarantorPassportSerie} placeholder="Saýla" error={submitted && !guarantorPassportSerie} />
|
||||||
<Input label={req('Pasport belgisi')} value={guarantorPassportId} onChangeText={setGuarantorPassportId} keyboardType="numeric" error={submitted && !guarantorPassportId} />
|
<Input label={req('Pasport belgisi')} value={guarantorPassportId} onChangeText={setGuarantorPassportId} keyboardType="numeric" error={submitted && !guarantorPassportId} maxLength={6} />
|
||||||
|
|
||||||
{needsSecondGuarantor && (
|
{needsSecondGuarantor && (
|
||||||
<>
|
<>
|
||||||
@@ -407,7 +414,7 @@ const CreateLoanOrderScreen = () => {
|
|||||||
<SelectInput label={req('Möhleti (ýyl)')} value={guarantor2CardYear} options={yearOptions} onValueChange={setGuarantor2CardYear} placeholder="Saýla" error={submitted && needsSecondGuarantor && !guarantor2CardYear} />
|
<SelectInput label={req('Möhleti (ýyl)')} value={guarantor2CardYear} options={yearOptions} onValueChange={setGuarantor2CardYear} placeholder="Saýla" error={submitted && needsSecondGuarantor && !guarantor2CardYear} />
|
||||||
|
|
||||||
<SelectInput label={req('Pasport seriýasy')} value={guarantor2PassportSerie} options={passportSeriesOptions} onValueChange={setGuarantor2PassportSerie} placeholder="Saýla" error={submitted && needsSecondGuarantor && !guarantor2PassportSerie} />
|
<SelectInput label={req('Pasport seriýasy')} value={guarantor2PassportSerie} options={passportSeriesOptions} onValueChange={setGuarantor2PassportSerie} placeholder="Saýla" error={submitted && needsSecondGuarantor && !guarantor2PassportSerie} />
|
||||||
<Input label={req('Pasport belgisi')} value={guarantor2PassportId} onChangeText={setGuarantor2PassportId} keyboardType="numeric" error={submitted && needsSecondGuarantor && !guarantor2PassportId} />
|
<Input label={req('Pasport belgisi')} value={guarantor2PassportId} onChangeText={setGuarantor2PassportId} keyboardType="numeric" error={submitted && needsSecondGuarantor && !guarantor2PassportId} maxLength={6} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ const MenuScreen = () => {
|
|||||||
navigation.navigate('CardRequisiteOrders');
|
navigation.navigate('CardRequisiteOrders');
|
||||||
} else if (item.id === 7) {
|
} else if (item.id === 7) {
|
||||||
navigation.navigate('CardBalanceOrders');
|
navigation.navigate('CardBalanceOrders');
|
||||||
|
} else if (item.id === 8) {
|
||||||
|
navigation.navigate('CardPinOrders');
|
||||||
} else {
|
} else {
|
||||||
console.log('Menu item pressed:', item.title);
|
console.log('Menu item pressed:', item.title);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -484,6 +484,46 @@ class ApiService {
|
|||||||
return { success: false, error: error.message };
|
return { success: false, error: error.message };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================================
|
||||||
|
// Card Pin Orders (Kart pin bukjalar)
|
||||||
|
// ================================
|
||||||
|
|
||||||
|
async getCardPinOrders() {
|
||||||
|
try {
|
||||||
|
const data = await authService.getCardPinOrders();
|
||||||
|
return { success: true, data };
|
||||||
|
} catch (error) {
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createCardPinOrder(payload) {
|
||||||
|
try {
|
||||||
|
const response = await authService.createCardPinOrder(payload);
|
||||||
|
return { success: true, message: response.message };
|
||||||
|
} catch (error) {
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCardPinOrder(orderId) {
|
||||||
|
try {
|
||||||
|
const data = await authService.getCardPinOrder(orderId);
|
||||||
|
return { success: true, data };
|
||||||
|
} catch (error) {
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteCardPinOrder(orderId) {
|
||||||
|
try {
|
||||||
|
const response = await authService.deleteCardPinOrder(orderId);
|
||||||
|
return { success: true, message: response.message };
|
||||||
|
} catch (error) {
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new ApiService();
|
export default new ApiService();
|
||||||
@@ -534,6 +534,35 @@ class AuthService {
|
|||||||
async downloadCardRequisites(orderId) {
|
async downloadCardRequisites(orderId) {
|
||||||
return this.makeRequest(`/card-requisites-download/${orderId}`, null, true, 'GET');
|
return this.makeRequest(`/card-requisites-download/${orderId}`, null, true, 'GET');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================================
|
||||||
|
// Card Pin Orders (Kart pin bukjalar)
|
||||||
|
// ================================
|
||||||
|
|
||||||
|
// LIST
|
||||||
|
async getCardPinOrders() {
|
||||||
|
return this.makeRequest('/card-pin-order', null, true, 'GET');
|
||||||
|
}
|
||||||
|
|
||||||
|
// CREATE (multipart payload similar to Card Requisite but without month/year)
|
||||||
|
async createCardPinOrder(payload) {
|
||||||
|
return this.makeRequest('/card-pin-order', payload, true, 'POST');
|
||||||
|
}
|
||||||
|
|
||||||
|
// SHOW
|
||||||
|
async getCardPinOrder(orderId) {
|
||||||
|
return this.makeRequest(`/card-pin-order/${orderId}`, null, true, 'GET');
|
||||||
|
}
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
async updateCardPinOrder(orderId, payload) {
|
||||||
|
return this.makeRequest(`/card-pin-order/${orderId}`, payload, true, 'POST');
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
async deleteCardPinOrder(orderId) {
|
||||||
|
return this.makeRequest(`/card-pin-order/${orderId}`, null, true, 'DELETE');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new AuthService();
|
export default new AuthService();
|
||||||
Reference in New Issue
Block a user