card order ready
This commit is contained in:
17
CHANGELOG.md
17
CHANGELOG.md
@@ -0,0 +1,17 @@
|
|||||||
|
# 0.0.1
|
||||||
|
|
||||||
|
- Initial release
|
||||||
|
|
||||||
|
# 0.0.2
|
||||||
|
|
||||||
|
- Commented Visa/Master and Sber cards
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
title: 'Halkara tölegler',
|
||||||
|
items: [
|
||||||
|
{ id: 9, title: 'Visa/Master tölegleri (talyplar üçin)', icon: 'logo-usd', description: 'Visa/Master' },
|
||||||
|
{ id: 10, title: 'Sber tölegler (talyplar üçin)', icon: 'globe', description: 'Sber tölegler' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
```
|
||||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -23,7 +23,8 @@
|
|||||||
"react-native-modal-datetime-picker": "^15.0.1",
|
"react-native-modal-datetime-picker": "^15.0.1",
|
||||||
"react-native-safe-area-context": "^5.5.1",
|
"react-native-safe-area-context": "^5.5.1",
|
||||||
"react-native-screens": "^4.11.1",
|
"react-native-screens": "^4.11.1",
|
||||||
"react-native-svg": "^15.12.0"
|
"react-native-svg": "^15.12.0",
|
||||||
|
"react-native-webview": "^13.11.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.0"
|
"@babel/core": "^7.20.0"
|
||||||
@@ -6467,6 +6468,19 @@
|
|||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-native-webview": {
|
||||||
|
"version": "13.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.15.0.tgz",
|
||||||
|
"integrity": "sha512-Vzjgy8mmxa/JO6l5KZrsTC7YemSdq+qB01diA0FqjUTaWGAGwuykpJ73MDj3+mzBSlaDxAEugHzTtkUQkQEQeQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"escape-string-regexp": "^4.0.0",
|
||||||
|
"invariant": "2.2.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-native/node_modules/@react-native/virtualized-lists": {
|
"node_modules/react-native/node_modules/@react-native/virtualized-lists": {
|
||||||
"version": "0.79.5",
|
"version": "0.79.5",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.79.5.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.79.5.tgz",
|
||||||
|
|||||||
@@ -24,7 +24,8 @@
|
|||||||
"react-native-safe-area-context": "^5.5.1",
|
"react-native-safe-area-context": "^5.5.1",
|
||||||
"react-native-screens": "^4.11.1",
|
"react-native-screens": "^4.11.1",
|
||||||
"react-native-svg": "^15.12.0",
|
"react-native-svg": "^15.12.0",
|
||||||
"expo-image-picker": "~16.1.4"
|
"expo-image-picker": "~16.1.4",
|
||||||
|
"react-native-webview": "^13.11.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.0"
|
"@babel/core": "^7.20.0"
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ import CardRequisiteOrderDetailsScreen from '../screens/Card/CardRequisiteOrderD
|
|||||||
import CardPinOrdersScreen from '../screens/Card/CardPinOrdersScreen';
|
import CardPinOrdersScreen from '../screens/Card/CardPinOrdersScreen';
|
||||||
import CreateCardPinOrderScreen from '../screens/Card/CreateCardPinOrderScreen';
|
import CreateCardPinOrderScreen from '../screens/Card/CreateCardPinOrderScreen';
|
||||||
import CardPinOrderDetailsScreen from '../screens/Card/CardPinOrderDetailsScreen';
|
import CardPinOrderDetailsScreen from '../screens/Card/CardPinOrderDetailsScreen';
|
||||||
|
import CardOrdersScreen from '../screens/Card/CardOrdersScreen';
|
||||||
|
import CreateCardOrderScreen from '../screens/Card/CreateCardOrderScreen';
|
||||||
|
import CardOrderDetailsScreen from '../screens/Card/CardOrderDetailsScreen';
|
||||||
|
|
||||||
const Stack = createStackNavigator();
|
const Stack = createStackNavigator();
|
||||||
|
|
||||||
@@ -47,6 +50,9 @@ const MenuNavigator = () => (
|
|||||||
<Stack.Screen name="CardPinOrders" component={CardPinOrdersScreen} />
|
<Stack.Screen name="CardPinOrders" component={CardPinOrdersScreen} />
|
||||||
<Stack.Screen name="CreateCardPinOrder" component={CreateCardPinOrderScreen} />
|
<Stack.Screen name="CreateCardPinOrder" component={CreateCardPinOrderScreen} />
|
||||||
<Stack.Screen name="CardPinOrderDetails" component={CardPinOrderDetailsScreen} />
|
<Stack.Screen name="CardPinOrderDetails" component={CardPinOrderDetailsScreen} />
|
||||||
|
<Stack.Screen name="CardOrders" component={CardOrdersScreen} />
|
||||||
|
<Stack.Screen name="CreateCardOrder" component={CreateCardOrderScreen} />
|
||||||
|
<Stack.Screen name="CardOrderDetails" component={CardOrderDetailsScreen} />
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
133
src/screens/Card/CardOrderDetailsScreen.js
Normal file
133
src/screens/Card/CardOrderDetailsScreen.js
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { View, Text, StyleSheet, ActivityIndicator, TouchableOpacity, ScrollView, SafeAreaView, Image, Alert, 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 CardOrderDetailsScreen = ()=>{
|
||||||
|
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 fetchData = async()=>{
|
||||||
|
setLoading(true);
|
||||||
|
const res = await apiService.getCardOrder(orderId);
|
||||||
|
if(res.success){
|
||||||
|
setOrder(res.data);
|
||||||
|
if(res.data?.region && res.data?.branch_id){
|
||||||
|
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 || 'Maglumat almak bolmady'); }
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
useEffect(()=>{ fetchData(); },[]);
|
||||||
|
|
||||||
|
const handleDelete = ()=>{ Alert.alert('Tassykla','Pozmakçy my?',[{text:'Goýbolsun',style:'cancel'},{text:'Poz',style:'destructive',onPress:async()=>{
|
||||||
|
const r = await apiService.deleteCardOrder(orderId);
|
||||||
|
if(r.success){ Alert.alert('Pozuldy',r.message||'Pozuldy',[{text:'OK',onPress:()=>navigation.goBack()}]); } else { Alert.alert('Ýalňyşlyk', r.error || 'Pozup bolmady'); }
|
||||||
|
}}]); };
|
||||||
|
|
||||||
|
if(loading) return <View style={styles.center}><ActivityIndicator size="large" color={COLORS.primary}/></View>;
|
||||||
|
if(!order) return <View style={styles.center}><Text>Maglumat ýok</Text></View>;
|
||||||
|
|
||||||
|
const formattedDate = (d)=> d? new Date(d).toLocaleDateString():'';
|
||||||
|
|
||||||
|
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}>Täze kart sargyt</Text>
|
||||||
|
|
||||||
|
{/* Summary */}
|
||||||
|
<View style={styles.detailCard}>
|
||||||
|
<DetailRow label="ID" value={order.unique_id} />
|
||||||
|
<DetailRow label="Kartyň çykarylmagynyň sebäbi" value={getLabel('card_states', order.card_state_id)} />
|
||||||
|
<DetailRow label="Kart görnüşi" value={getLabel('card_types', order.card_type_id)} />
|
||||||
|
<DetailRow label="Tölenildi" value={order.paid? 'Hawa':'Ýok'} 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}>
|
||||||
|
<DetailRow label="Ady" value={order.customer_name} />
|
||||||
|
<DetailRow label="Familiýasy" value={order.customer_surname} />
|
||||||
|
{order.customer_patronic_name && <DetailRow label="Atasynyň ady" value={order.customer_patronic_name} />}
|
||||||
|
{order.old_surname && <DetailRow label="Köne familiýasy" value={order.old_surname} />}
|
||||||
|
<DetailRow label="Doglan güni" value={formattedDate(order.born_at)} />
|
||||||
|
<DetailRow label="Telefon" value={`+993 ${order.phone}`} />
|
||||||
|
{order.phone_additional && <DetailRow label="Telefon goşmaça" value={`+993 ${order.phone_additional}`}/>}
|
||||||
|
|
||||||
|
<DetailRow label="Ýazgy edilen salgyňyz" value={order.passport_address} />
|
||||||
|
<DetailRow label="Häzirki ýaşaýyş ýeri" value={order.real_address} />
|
||||||
|
<DetailRow label="Işleýän ýeriňiz we wezipäňiz" value={order.job_location} showBorder={false}/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Passport info */}
|
||||||
|
<Text style={styles.sectionTitle}>Pasport</Text>
|
||||||
|
<View style={styles.detailCard}>
|
||||||
|
<DetailRow label="Seriýa/Belgi" value={`${order.passport_serie} ${order.passport_id}`} />
|
||||||
|
<DetailRow label="Berlen senesi" value={formattedDate(order.passport_given_at)} />
|
||||||
|
<DetailRow label="Berlen ýeri" value={order.passport_given_by} />
|
||||||
|
<DetailRow label="Doglan ýeri (passport)" value={order.born_place} style={{marginBottom:12}} 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.img}/><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.img}/><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.img}/><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.img}/><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},
|
||||||
|
center:{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},
|
||||||
|
img:{width:'100%',aspectRatio:4/3,borderRadius:8,backgroundColor:'#eee'},
|
||||||
|
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 CardOrderDetailsScreen;
|
||||||
84
src/screens/Card/CardOrdersScreen.js
Normal file
84
src/screens/Card/CardOrdersScreen.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import React, { useCallback, useState } from 'react';
|
||||||
|
import { View, Text, StyleSheet, FlatList, TouchableOpacity, ActivityIndicator, 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 = '#EFF6FF';
|
||||||
|
const CIRCLE_BG = '#7FB3FF';
|
||||||
|
|
||||||
|
const CardOrdersScreen = () => {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
const [orders,setOrders] = useState([]);
|
||||||
|
const [loading,setLoading]=useState(true);
|
||||||
|
const [refreshing,setRefreshing]=useState(false);
|
||||||
|
const { getLabel } = useBaseEnums();
|
||||||
|
|
||||||
|
const fetchData = async()=>{
|
||||||
|
try{
|
||||||
|
const res = await apiService.getCardOrders();
|
||||||
|
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(()=>{ fetchData(); },[]));
|
||||||
|
|
||||||
|
const onRefresh = ()=>{ setRefreshing(true); fetchData(); };
|
||||||
|
|
||||||
|
const handlePress = (item)=>{ navigation.navigate('CardOrderDetails',{orderId:item.id}); };
|
||||||
|
|
||||||
|
const renderItem=({item})=>{
|
||||||
|
const created = item.created_at? new Date(item.created_at).toLocaleDateString():'';
|
||||||
|
return (
|
||||||
|
<TouchableOpacity style={styles.card} onPress={()=>handlePress(item)}>
|
||||||
|
<View style={styles.circle}><Text style={styles.circleText}>{item.id}</Text></View>
|
||||||
|
<View style={styles.content}>
|
||||||
|
<Text style={styles.titleText}>{item.unique_id}</Text>
|
||||||
|
<Text style={styles.line}>Kart görnüşi: {getLabel('card_types',item.card_type_id)}</Text>
|
||||||
|
<Text style={styles.line}>Tölenildi: {item.paid? 'Hawa':'Ýok'}</Text>
|
||||||
|
<Text style={styles.status}>{item.status}</Text>
|
||||||
|
<Text style={styles.date}>{created}</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
); };
|
||||||
|
|
||||||
|
if(loading) return <View style={styles.center}><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}>Täze kart sargytlary</Text>
|
||||||
|
</View>
|
||||||
|
<FlatList data={orders} keyExtractor={i=>i.id.toString()} renderItem={renderItem} refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh}/>} contentContainerStyle={orders.length===0 && styles.empty} ListEmptyComponent={<Text style={styles.emptyText}>Sargyt ýok</Text>} />
|
||||||
|
<TouchableOpacity style={styles.fab} onPress={()=>navigation.navigate('CreateCardOrder')}>
|
||||||
|
<Ionicons name="add" size={28} color={COLORS.white}/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</SafeAreaView>
|
||||||
|
); };
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container:{flex:1,backgroundColor:COLORS.backgroundSecondary},
|
||||||
|
header:{flexDirection:'row',alignItems:'center',paddingHorizontal:24,paddingVertical:16},
|
||||||
|
headerTitle:{fontSize:20,fontWeight:'bold',color:COLORS.textPrimary},
|
||||||
|
center:{flex:1,justifyContent:'center',alignItems:'center'},
|
||||||
|
card:{flexDirection:'row',backgroundColor:CARD_BG,marginHorizontal:24,marginTop:16,borderRadius:12,padding:16},
|
||||||
|
circle:{width:40,height:40,borderRadius:20,backgroundColor:CIRCLE_BG,alignItems:'center',justifyContent:'center',marginRight:16},
|
||||||
|
circleText:{color:COLORS.white,fontWeight:'600'},
|
||||||
|
content:{flex:1},
|
||||||
|
titleText:{fontWeight:'700',color:COLORS.textPrimary},
|
||||||
|
line:{fontSize:12,color:COLORS.textSecondary},
|
||||||
|
status:{fontSize:13,fontWeight:'600',marginTop:2},
|
||||||
|
date:{fontSize:11,color:COLORS.textSecondary},
|
||||||
|
fab:{position:'absolute',bottom:32,right:32,width:56,height:56,borderRadius:28,backgroundColor:COLORS.primary,alignItems:'center',justifyContent:'center',elevation:4},
|
||||||
|
empty:{flex:1,justifyContent:'center',alignItems:'center'},
|
||||||
|
emptyText:{color:COLORS.textSecondary,fontSize:16},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default CardOrdersScreen;
|
||||||
201
src/screens/Card/CreateCardOrderScreen.js
Normal file
201
src/screens/Card/CreateCardOrderScreen.js
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, SafeAreaView, ScrollView, Modal } 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 DateInput from '../../components/DateInput';
|
||||||
|
import ImageInput from '../../components/ImageInput';
|
||||||
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
|
import { useBaseEnums } from '../../contexts/BaseEnumsContext';
|
||||||
|
import { WebView } from 'react-native-webview';
|
||||||
|
|
||||||
|
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 CreateCardOrderScreen = ()=>{
|
||||||
|
const navigation=useNavigation();
|
||||||
|
const { user } = useAuth();
|
||||||
|
const { getOptions, getBranches } = useBaseEnums();
|
||||||
|
|
||||||
|
const [cardState,setCardState]=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 [oldSurname,setOldSurname]=useState('');
|
||||||
|
const [bornAt,setBornAt]=useState('');
|
||||||
|
const [phone,setPhone]=useState('');
|
||||||
|
const [phoneAdditional,setPhoneAdditional]=useState('');
|
||||||
|
const [citizenship] = useState('');
|
||||||
|
const [passportSerie,setPassportSerie]=useState('');
|
||||||
|
const [passportId,setPassportId]=useState('');
|
||||||
|
const [passportGivenAt,setPassportGivenAt]=useState('');
|
||||||
|
const [passportGivenBy,setPassportGivenBy]=useState('');
|
||||||
|
const [bornPlace,setBornPlace]=useState('');
|
||||||
|
const [passportAddress,setPassportAddress]=useState('');
|
||||||
|
const [realAddress,setRealAddress]=useState('');
|
||||||
|
const [jobLocation,setJobLocation]=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 [webVisible,setWebVisible]=useState(false);
|
||||||
|
const [paymentUrl,setPaymentUrl]=useState('');
|
||||||
|
const [newOrderId,setNewOrderId]=useState(null);
|
||||||
|
|
||||||
|
const cardStateOptions = getOptions('card_states');
|
||||||
|
const cardTypeOptions = getOptions('card_types');
|
||||||
|
const regionOptions = getOptions('regions');
|
||||||
|
|
||||||
|
// Prefill from user
|
||||||
|
useEffect(()=>{
|
||||||
|
if(user){
|
||||||
|
if(user.passport_serie) setPassportSerie(user.passport_serie);
|
||||||
|
if(user.passport_id) setPassportId(String(user.passport_id));
|
||||||
|
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 br=await getBranches(region); setBranchOptions(br.map(r=>({label:r.name,value:r.id}))); }else setBranchOptions([]); })(); },[region]);
|
||||||
|
|
||||||
|
const validate=()=>{
|
||||||
|
return cardState && cardType && region && branchId && customerName && customerSurname && bornAt && passportSerie && passportId.trim() && passportGivenAt && passportGivenBy && bornPlace && jobLocation && passportAddress && realAddress && phone && passportOne && passportTwo && passportThree && passportFour;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit=async()=>{
|
||||||
|
if(!validate()){ Alert.alert('Ýalňyşlyk','Ähli zerur meýdançalar doldurylmaly'); return; }
|
||||||
|
const fd=new FormData();
|
||||||
|
fd.append('card_state_id',cardState);
|
||||||
|
fd.append('card_type_id',cardType);
|
||||||
|
fd.append('region',region);
|
||||||
|
fd.append('branch_id',branchId);
|
||||||
|
fd.append('customer_name',customerName);
|
||||||
|
fd.append('customer_surname',customerSurname);
|
||||||
|
if(customerPatro) fd.append('customer_patronic_name',customerPatro);
|
||||||
|
if(oldSurname) fd.append('old_surname',oldSurname);
|
||||||
|
fd.append('born_at',bornAt);
|
||||||
|
fd.append('passport_serie',passportSerie);
|
||||||
|
fd.append('passport_id',passportId.trim());
|
||||||
|
fd.append('passport_given_at',passportGivenAt);
|
||||||
|
fd.append('passport_given_by',passportGivenBy);
|
||||||
|
fd.append('born_place',bornPlace);
|
||||||
|
fd.append('job_location',jobLocation);
|
||||||
|
fd.append('passport_address',passportAddress);
|
||||||
|
fd.append('real_address',realAddress);
|
||||||
|
fd.append('phone',parseInt(phone));
|
||||||
|
if(phoneAdditional) fd.append('phone_additional',parseInt(phoneAdditional));
|
||||||
|
fd.append('passport_one',{uri:passportOne,name:'p1.jpg',type:'image/jpeg'});
|
||||||
|
fd.append('passport_two',{uri:passportTwo,name:'p2.jpg',type:'image/jpeg'});
|
||||||
|
fd.append('passport_three',{uri:passportThree,name:'p3.jpg',type:'image/jpeg'});
|
||||||
|
fd.append('passport_four',{uri:passportFour,name:'p4.jpg',type:'image/jpeg'});
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
const res = await apiService.createCardOrder(fd);
|
||||||
|
setLoading(false);
|
||||||
|
if(res.success){
|
||||||
|
const payment=res.data.payment;
|
||||||
|
Alert.alert('Üstünlik',res.data.message||'Döredildi');
|
||||||
|
// Try to get latest order id
|
||||||
|
const list = await apiService.getCardOrders();
|
||||||
|
if(list.success && Array.isArray(list.data) && list.data.length>0){ const maxOrder=list.data.reduce((a,b)=>a.id>b.id?a:b); setNewOrderId(maxOrder.id); }
|
||||||
|
if(payment && payment.url){ setPaymentUrl(payment.url); setWebVisible(true);} else if(newOrderId){ navigation.replace('CardOrderDetails',{orderId:newOrderId}); }
|
||||||
|
} else {
|
||||||
|
Alert.alert('Ýalňyşlyk',res.error||'Ýalňyşlyk');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const webRef=useRef();
|
||||||
|
const handleWebNav=state=>{
|
||||||
|
if(state.url.includes('/online-payment-store')){
|
||||||
|
setTimeout(()=>{ setWebVisible(false); if(newOrderId) navigation.replace('CardOrderDetails',{orderId:newOrderId}); },3000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={styles.container}>
|
||||||
|
<StatusBar style="dark" />
|
||||||
|
<ScrollView contentContainerStyle={{paddingHorizontal:24,paddingTop:40,paddingBottom:120}} 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 kart sargyt</Text>
|
||||||
|
|
||||||
|
{/* Card */}
|
||||||
|
<SelectInput label="*Kart çykarylmagynyň sebabi" value={cardState} onValueChange={setCardState} options={cardStateOptions} placeholder="Saýla" />
|
||||||
|
<SelectInput label="*Kart görnüşi" value={cardType} onValueChange={setCardType} options={cardTypeOptions} placeholder="Saýla" />
|
||||||
|
|
||||||
|
{/* 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} 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}/>
|
||||||
|
<Input label="Köne familiýa" value={oldSurname} onChangeText={setOldSurname}/>
|
||||||
|
<DateInput label="*Doglan güni" value={bornAt} onChange={setBornAt}/>
|
||||||
|
<Input label="*Telefon" value={phone} onChangeText={setPhone} keyboardType="numeric" maxLength={8}/>
|
||||||
|
<Input label="Telefon goşmaça" value={phoneAdditional} onChangeText={setPhoneAdditional} keyboardType="numeric" maxLength={8}/>
|
||||||
|
|
||||||
|
<Input label="*Ýazgy edilen salgyňyz" value={passportAddress} onChangeText={setPassportAddress}/>
|
||||||
|
<Input label="*Häzirki ýaşaýyş ýeri" value={realAddress} onChangeText={setRealAddress}/>
|
||||||
|
<Input label="*Işleýän ýeriňiz we wezipäňiz" value={jobLocation} onChangeText={setJobLocation}/>
|
||||||
|
|
||||||
|
{/* 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" />
|
||||||
|
<DateInput label="*Passport berlen senesi" value={passportGivenAt} onChange={setPassportGivenAt}/>
|
||||||
|
<Input label="*Kim tarapyndan berildi" value={passportGivenBy} onChangeText={setPassportGivenBy}/>
|
||||||
|
<Input label="*Doglan ýeri (passport)" value={bornPlace} onChangeText={setBornPlace}/>
|
||||||
|
|
||||||
|
{/* Images */}
|
||||||
|
<Text style={styles.sectionTitle}>Pasport faýllar</Text>
|
||||||
|
<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}>Sargyt et</Text>}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
{/* Payment WebView */}
|
||||||
|
<Modal visible={webVisible} onRequestClose={()=>setWebVisible(false)} animationType="slide">
|
||||||
|
<SafeAreaView style={{flex:1}}>
|
||||||
|
<View style={{flexDirection:'row',alignItems:'center',padding:12,backgroundColor:COLORS.primary}}>
|
||||||
|
<TouchableOpacity onPress={()=>{setWebVisible(false); if(newOrderId) navigation.replace('CardOrderDetails',{orderId:newOrderId});}}><Ionicons name="close" size={24} color="#fff"/></TouchableOpacity>
|
||||||
|
<Text style={{color:'#fff',fontWeight:'600',marginLeft:16}}>Töleg</Text>
|
||||||
|
</View>
|
||||||
|
{paymentUrl!=='' && <WebView ref={webRef} source={{uri:paymentUrl}} onNavigationStateChange={handleWebNav}/>}
|
||||||
|
</SafeAreaView>
|
||||||
|
</Modal>
|
||||||
|
</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 CreateCardOrderScreen;
|
||||||
@@ -59,20 +59,20 @@ const LoanOrderDetailsScreen = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
Alert.alert('Confirm', 'Delete this order?', [
|
Alert.alert('Tassykla', 'Bu sargydy pozmak isleýärsiňizmi?', [
|
||||||
{ text: 'Cancel', style: 'cancel' },
|
{ text: 'Goýbolsun', style: 'cancel' },
|
||||||
{ text: 'Delete', style: 'destructive', onPress: deleteOrder },
|
{ text: 'Poz', style: 'destructive', onPress: deleteOrder },
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteOrder = async () => {
|
const deleteOrder = async () => {
|
||||||
const res = await apiService.deleteLoanOrder(orderId);
|
const res = await apiService.deleteLoanOrder(orderId);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
Alert.alert('Deleted', res.message || 'Order deleted', [
|
Alert.alert('Pozuldy', res.message || 'Sargyt pozuldy', [
|
||||||
{ text: 'OK', onPress: () => navigation.goBack() },
|
{ text: 'Bolýar', onPress: () => navigation.goBack() },
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
Alert.alert('Error', res.error || 'Could not delete');
|
Alert.alert('Ýalňyşlyk', res.error || 'Pozup bolmady');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -34,13 +34,13 @@ const MenuScreen = () => {
|
|||||||
{ id: 8, title: 'Kart pin bukjalar', icon: 'key', description: 'Pin bukjalar' },
|
{ id: 8, title: 'Kart pin bukjalar', icon: 'key', description: 'Pin bukjalar' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
title: 'Halkara tölegler',
|
// title: 'Halkara tölegler',
|
||||||
items: [
|
// items: [
|
||||||
{ id: 9, title: 'Visa/Master tölegleri (talyplar üçin)', icon: 'logo-usd', description: 'Visa/Master' },
|
// { id: 9, title: 'Visa/Master tölegleri (talyplar üçin)', icon: 'logo-usd', description: 'Visa/Master' },
|
||||||
{ id: 10, title: 'Sber tölegler (talyplar üçin)', icon: 'globe', description: 'Sber tölegler' },
|
// { id: 10, title: 'Sber tölegler (talyplar üçin)', icon: 'globe', description: 'Sber tölegler' },
|
||||||
],
|
// ],
|
||||||
},
|
// },
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleMenuItemPress = (item) => {
|
const handleMenuItemPress = (item) => {
|
||||||
@@ -50,6 +50,8 @@ const MenuScreen = () => {
|
|||||||
navigation.navigate('LoanRemainingOrders');
|
navigation.navigate('LoanRemainingOrders');
|
||||||
} else if (item.id === 3) {
|
} else if (item.id === 3) {
|
||||||
navigation.navigate('LoanPaidOffLetterOrders');
|
navigation.navigate('LoanPaidOffLetterOrders');
|
||||||
|
} else if (item.id === 4) {
|
||||||
|
navigation.navigate('CardOrders');
|
||||||
} else if (item.id === 5) {
|
} else if (item.id === 5) {
|
||||||
navigation.navigate('CardTransactionOrders');
|
navigation.navigate('CardTransactionOrders');
|
||||||
} else if (item.id === 6) {
|
} else if (item.id === 6) {
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ const ProfileScreen = () => {
|
|||||||
handleSecuritySettings();
|
handleSecuritySettings();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Alert.alert('Üns beriň', 'Bu funksiýa entek işlenok');
|
// Alert.alert('Üns beriň', 'Bu funksiýa entek işlenok');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -524,6 +524,46 @@ class ApiService {
|
|||||||
return { success: false, error: error.message };
|
return { success: false, error: error.message };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================================
|
||||||
|
// Card Orders (Täze kart)
|
||||||
|
// ================================
|
||||||
|
|
||||||
|
async getCardOrders() {
|
||||||
|
try {
|
||||||
|
const data = await authService.getCardOrders();
|
||||||
|
return { success: true, data };
|
||||||
|
} catch (error) {
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createCardOrder(payload) {
|
||||||
|
try {
|
||||||
|
const response = await authService.createCardOrder(payload);
|
||||||
|
return { success: true, data: response };
|
||||||
|
} catch (error) {
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCardOrder(orderId) {
|
||||||
|
try {
|
||||||
|
const data = await authService.getCardOrder(orderId);
|
||||||
|
return { success: true, data };
|
||||||
|
} catch (error) {
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteCardOrder(orderId) {
|
||||||
|
try {
|
||||||
|
const response = await authService.deleteCardOrder(orderId);
|
||||||
|
return { success: true, message: response.message };
|
||||||
|
} catch (error) {
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new ApiService();
|
export default new ApiService();
|
||||||
@@ -563,6 +563,30 @@ class AuthService {
|
|||||||
async deleteCardPinOrder(orderId) {
|
async deleteCardPinOrder(orderId) {
|
||||||
return this.makeRequest(`/card-pin-order/${orderId}`, null, true, 'DELETE');
|
return this.makeRequest(`/card-pin-order/${orderId}`, null, true, 'DELETE');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================================
|
||||||
|
// Card Orders (Täze kart)
|
||||||
|
// ================================
|
||||||
|
|
||||||
|
// LIST
|
||||||
|
async getCardOrders() {
|
||||||
|
return this.makeRequest('/card-order', null, true, 'GET');
|
||||||
|
}
|
||||||
|
|
||||||
|
// CREATE – returns { message, payment { status, url } }
|
||||||
|
async createCardOrder(payload) {
|
||||||
|
return this.makeRequest('/card-order', payload, true, 'POST');
|
||||||
|
}
|
||||||
|
|
||||||
|
// SHOW
|
||||||
|
async getCardOrder(orderId) {
|
||||||
|
return this.makeRequest(`/card-order/${orderId}`, null, true, 'GET');
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
async deleteCardOrder(orderId) {
|
||||||
|
return this.makeRequest(`/card-order/${orderId}`, null, true, 'DELETE');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new AuthService();
|
export default new AuthService();
|
||||||
Reference in New Issue
Block a user