passports added
This commit is contained in:
20
package-lock.json
generated
20
package-lock.json
generated
@@ -16,6 +16,7 @@
|
|||||||
"@react-navigation/native": "^7.1.14",
|
"@react-navigation/native": "^7.1.14",
|
||||||
"@react-navigation/stack": "^7.4.2",
|
"@react-navigation/stack": "^7.4.2",
|
||||||
"expo": "~53.0.16",
|
"expo": "~53.0.16",
|
||||||
|
"expo-image-picker": "~16.1.4",
|
||||||
"expo-status-bar": "~2.2.3",
|
"expo-status-bar": "~2.2.3",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-native": "0.79.5",
|
"react-native": "0.79.5",
|
||||||
@@ -3984,6 +3985,25 @@
|
|||||||
"react": "*"
|
"react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expo-image-loader": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-sEBx3zDQIODWbB5JwzE7ZL5FJD+DK3LVLWBVJy6VzsqIA6nDEnSFnsnWyCfCTSvbGigMATs1lgkC2nz3Jpve1Q==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/expo-image-picker": {
|
||||||
|
"version": "16.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-16.1.4.tgz",
|
||||||
|
"integrity": "sha512-bTmmxtw1AohUT+HxEBn2vYwdeOrj1CLpMXKjvi9FKSoSbpcarT4xxI0z7YyGwDGHbrJqyyic3I9TTdP2J2b4YA==",
|
||||||
|
"dependencies": {
|
||||||
|
"expo-image-loader": "~5.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/expo-keep-awake": {
|
"node_modules/expo-keep-awake": {
|
||||||
"version": "14.1.4",
|
"version": "14.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.1.4.tgz",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
"expo-image-picker": "~16.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.0"
|
"@babel/core": "^7.20.0"
|
||||||
|
|||||||
95
src/components/ImageInput.js
Normal file
95
src/components/ImageInput.js
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { View, Text, StyleSheet, TouchableOpacity, Image, Alert } from 'react-native';
|
||||||
|
import * as ImagePicker from 'expo-image-picker';
|
||||||
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
|
import { COLORS } from '../constants/colors';
|
||||||
|
|
||||||
|
const ImageInput = ({ label, image, onChange }) => {
|
||||||
|
const [hasPermission, setHasPermission] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
|
||||||
|
setHasPermission(status === 'granted');
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const pickImage = async () => {
|
||||||
|
if (hasPermission === false) {
|
||||||
|
Alert.alert('Permission required', 'Please grant photo library access to select images.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await ImagePicker.launchImageLibraryAsync({
|
||||||
|
mediaTypes: ImagePicker.MediaType,
|
||||||
|
quality: 0.7,
|
||||||
|
base64: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.canceled && result.assets && result.assets.length > 0) {
|
||||||
|
const asset = result.assets[0];
|
||||||
|
const fileObj = {
|
||||||
|
uri: asset.uri,
|
||||||
|
name: asset.fileName || `photo_${Date.now()}.jpg`,
|
||||||
|
type: asset.mimeType || 'image/jpeg',
|
||||||
|
};
|
||||||
|
onChange && onChange(fileObj);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{label && <Text style={styles.label}>{label}</Text>}
|
||||||
|
<TouchableOpacity style={styles.box} activeOpacity={0.8} onPress={pickImage}>
|
||||||
|
{image ? (
|
||||||
|
<Image source={{ uri: image.uri ? image.uri : image }} style={styles.preview} />
|
||||||
|
) : (
|
||||||
|
<View style={styles.placeholder}>
|
||||||
|
<Ionicons name="camera" size={28} color={COLORS.gray[400]} />
|
||||||
|
<Text style={styles.placeholderText}>Saýla</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: COLORS.textPrimary,
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
box: {
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: COLORS.gray[300],
|
||||||
|
borderRadius: 12,
|
||||||
|
backgroundColor: COLORS.white,
|
||||||
|
width: 160,
|
||||||
|
height: 120,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
overflow: 'hidden',
|
||||||
|
alignSelf: 'flex-start',
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
placeholderText: {
|
||||||
|
marginTop: 6,
|
||||||
|
color: COLORS.gray[400],
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
preview: {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
borderRadius: 12,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ImageInput;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, ScrollView, SafeAreaView } from 'react-native';
|
import { Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert, ScrollView, SafeAreaView, View } from 'react-native';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { COLORS } from '../../constants/colors';
|
import { COLORS } from '../../constants/colors';
|
||||||
@@ -11,6 +11,7 @@ import apiService from '../../services/apiService';
|
|||||||
import { useAuth } from '../../contexts/AuthContext';
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
import { useBaseEnums } from '../../contexts/BaseEnumsContext';
|
import { useBaseEnums } from '../../contexts/BaseEnumsContext';
|
||||||
import { StatusBar } from 'expo-status-bar';
|
import { StatusBar } from 'expo-status-bar';
|
||||||
|
import ImageInput from '../../components/ImageInput';
|
||||||
|
|
||||||
const CreateLoanOrderScreen = () => {
|
const CreateLoanOrderScreen = () => {
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
@@ -60,6 +61,11 @@ const CreateLoanOrderScreen = () => {
|
|||||||
const [cardMonth, setCardMonth] = useState('');
|
const [cardMonth, setCardMonth] = useState('');
|
||||||
const [cardYear, setCardYear] = useState('');
|
const [cardYear, setCardYear] = useState('');
|
||||||
|
|
||||||
|
const [passportOne, setPassportOne] = useState(null);
|
||||||
|
const [passportTwo, setPassportTwo] = useState(null);
|
||||||
|
const [passportThree, setPassportThree] = useState(null);
|
||||||
|
const [passportFour, setPassportFour] = useState(null);
|
||||||
|
|
||||||
const monthOptions = Array.from({ length: 12 }, (_, i) => {
|
const monthOptions = Array.from({ length: 12 }, (_, i) => {
|
||||||
const val = (i + 1).toString().padStart(2, '0');
|
const val = (i + 1).toString().padStart(2, '0');
|
||||||
return { value: val, label: val };
|
return { value: val, label: val };
|
||||||
@@ -122,7 +128,7 @@ const CreateLoanOrderScreen = () => {
|
|||||||
const branchOptions = region && branchesByRegion[region] ? branchesByRegion[region].map((b) => ({ label: b.name, value: b.id })) : [];
|
const branchOptions = region && branchesByRegion[region] ? branchesByRegion[region].map((b) => ({ label: b.name, value: b.id })) : [];
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
if (!loanType || !loanAmount || !region || !branchId || !customerName || !customerSurname || !passportSerie || !passportId || !bornAt || !phone || !education || !marriageStatus || !passportAddress || !realAddress || !cardNumber || !cardName || !cardMonth || !cardYear) {
|
if (!loanType || !loanAmount || !region || !branchId || !customerName || !customerSurname || !passportSerie || !passportId || !bornAt || !phone || !education || !marriageStatus || !passportAddress || !realAddress || !passportOne || !passportTwo || !passportThree || !passportFour || !cardNumber || !cardName || !cardMonth || !cardYear) {
|
||||||
Alert.alert('Error', 'Fill all required fields');
|
Alert.alert('Error', 'Fill all required fields');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -166,6 +172,10 @@ const CreateLoanOrderScreen = () => {
|
|||||||
card_name: cardName,
|
card_name: cardName,
|
||||||
card_month: cardMonth,
|
card_month: cardMonth,
|
||||||
card_year: cardYear,
|
card_year: cardYear,
|
||||||
|
passport_one: passportOne,
|
||||||
|
passport_two: passportTwo,
|
||||||
|
passport_three: passportThree,
|
||||||
|
passport_four: passportFour,
|
||||||
};
|
};
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -200,6 +210,7 @@ const CreateLoanOrderScreen = () => {
|
|||||||
|
|
||||||
<SelectInput label="Bilimi" value={education} options={educationOptions} onValueChange={setEducation} placeholder="Saýla" />
|
<SelectInput label="Bilimi" value={education} options={educationOptions} onValueChange={setEducation} placeholder="Saýla" />
|
||||||
<SelectInput label="Maşgala ýagdaýy" value={marriageStatus} options={marriageOptions} onValueChange={setMarriageStatus} placeholder="Saýla" />
|
<SelectInput label="Maşgala ýagdaýy" value={marriageStatus} options={marriageOptions} onValueChange={setMarriageStatus} placeholder="Saýla" />
|
||||||
|
<DateInput label="Doglan senesi" value={bornAt} onChange={setBornAt} />
|
||||||
|
|
||||||
<Input label="Ýazgy edilen salgyňyz" placeholder="Kemine 100/190" value={passportAddress} onChangeText={setPassportAddress} />
|
<Input label="Ýazgy edilen salgyňyz" placeholder="Kemine 100/190" value={passportAddress} onChangeText={setPassportAddress} />
|
||||||
<Input label="Häzirki ýaşaýyş ýeri" placeholder="Kemine 100/200" value={realAddress} onChangeText={setRealAddress} />
|
<Input label="Häzirki ýaşaýyş ýeri" placeholder="Kemine 100/200" value={realAddress} onChangeText={setRealAddress} />
|
||||||
@@ -211,7 +222,6 @@ const CreateLoanOrderScreen = () => {
|
|||||||
<Input label="Doglan ýeri (passport)" placeholder="Ashgabat" value={bornPlace} onChangeText={setBornPlace} />
|
<Input label="Doglan ýeri (passport)" placeholder="Ashgabat" value={bornPlace} onChangeText={setBornPlace} />
|
||||||
<Input label="E-poçta" placeholder="example@mail.com" value={email} onChangeText={setEmail} keyboardType="email-address" />
|
<Input label="E-poçta" placeholder="example@mail.com" value={email} onChangeText={setEmail} keyboardType="email-address" />
|
||||||
|
|
||||||
<DateInput label="Doglan senesi" value={bornAt} onChange={setBornAt} />
|
|
||||||
<Input label="Telefon (+9936...)" value={phone} onChangeText={setPhone} keyboardType="numeric" />
|
<Input label="Telefon (+9936...)" value={phone} onChangeText={setPhone} keyboardType="numeric" />
|
||||||
<Input label="Telefon goşmaça" value={phoneAdditional} onChangeText={setPhoneAdditional} keyboardType="numeric" />
|
<Input label="Telefon goşmaça" value={phoneAdditional} onChangeText={setPhoneAdditional} keyboardType="numeric" />
|
||||||
<Input label="Öý telefony" value={phoneHome} onChangeText={setPhoneHome} />
|
<Input label="Öý telefony" value={phoneHome} onChangeText={setPhoneHome} />
|
||||||
@@ -230,6 +240,13 @@ const CreateLoanOrderScreen = () => {
|
|||||||
<SelectInput label="Kart Möhleti (aý)" value={cardMonth} options={monthOptions} onValueChange={setCardMonth} placeholder="Saýla" />
|
<SelectInput label="Kart Möhleti (aý)" value={cardMonth} options={monthOptions} onValueChange={setCardMonth} placeholder="Saýla" />
|
||||||
<SelectInput label="Kart Möhleti (ýyl)" value={cardYear} options={yearOptions} onValueChange={setCardYear} placeholder="Saýla" />
|
<SelectInput label="Kart Möhleti (ýyl)" value={cardYear} options={yearOptions} onValueChange={setCardYear} placeholder="Saýla" />
|
||||||
|
|
||||||
|
<View style={styles.passportGrid}>
|
||||||
|
<ImageInput label="Pasport (sahypa 1)" image={passportOne} onChange={setPassportOne} />
|
||||||
|
<ImageInput label="Pasport (2-3-nji sahypa)" image={passportTwo} onChange={setPassportTwo} />
|
||||||
|
<ImageInput label="Pasport (8-9 sahypa)" image={passportThree} onChange={setPassportThree} />
|
||||||
|
<ImageInput label="Pasport (32-nji sahypa)" image={passportFour} onChange={setPassportFour} />
|
||||||
|
</View>
|
||||||
|
|
||||||
<TouchableOpacity style={styles.submitBtn} onPress={handleSubmit} disabled={loading}>
|
<TouchableOpacity style={styles.submitBtn} onPress={handleSubmit} disabled={loading}>
|
||||||
{loading ? <ActivityIndicator color={COLORS.white} /> : <Text style={styles.submitText}>Ýatda sakla</Text>}
|
{loading ? <ActivityIndicator color={COLORS.white} /> : <Text style={styles.submitText}>Ýatda sakla</Text>}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
@@ -244,6 +261,11 @@ const styles = StyleSheet.create({
|
|||||||
title: { fontSize: 24, fontWeight: 'bold', color: COLORS.textPrimary, marginBottom: 24 },
|
title: { fontSize: 24, fontWeight: 'bold', color: COLORS.textPrimary, marginBottom: 24 },
|
||||||
submitBtn: { marginTop: 32, backgroundColor: COLORS.primary, paddingVertical: 16, borderRadius: 8, alignItems: 'center' },
|
submitBtn: { marginTop: 32, backgroundColor: COLORS.primary, paddingVertical: 16, borderRadius: 8, alignItems: 'center' },
|
||||||
submitText: { color: COLORS.white, fontSize: 16, fontWeight: '600' },
|
submitText: { color: COLORS.white, fontSize: 16, fontWeight: '600' },
|
||||||
|
passportGrid: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default CreateLoanOrderScreen;
|
export default CreateLoanOrderScreen;
|
||||||
Reference in New Issue
Block a user