diff --git a/package-lock.json b/package-lock.json
index dd83136..ec031c4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
"@react-navigation/native": "^7.1.14",
"@react-navigation/stack": "^7.4.2",
"expo": "~53.0.16",
+ "expo-image-picker": "~16.1.4",
"expo-status-bar": "~2.2.3",
"react": "19.0.0",
"react-native": "0.79.5",
@@ -3984,6 +3985,25 @@
"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": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.1.4.tgz",
diff --git a/package.json b/package.json
index 62762c1..918184c 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,8 @@
"react-native-modal-datetime-picker": "^15.0.1",
"react-native-safe-area-context": "^5.5.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": {
"@babel/core": "^7.20.0"
diff --git a/src/components/ImageInput.js b/src/components/ImageInput.js
new file mode 100644
index 0000000..b4aeddf
--- /dev/null
+++ b/src/components/ImageInput.js
@@ -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 (
+
+ {label && {label}}
+
+ {image ? (
+
+ ) : (
+
+
+ Saýla
+
+ )}
+
+
+ );
+};
+
+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;
\ No newline at end of file
diff --git a/src/screens/Loan/CreateLoanOrderScreen.js b/src/screens/Loan/CreateLoanOrderScreen.js
index c195a25..cbe3712 100644
--- a/src/screens/Loan/CreateLoanOrderScreen.js
+++ b/src/screens/Loan/CreateLoanOrderScreen.js
@@ -1,5 +1,5 @@
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 { useNavigation } from '@react-navigation/native';
import { COLORS } from '../../constants/colors';
@@ -11,6 +11,7 @@ import apiService from '../../services/apiService';
import { useAuth } from '../../contexts/AuthContext';
import { useBaseEnums } from '../../contexts/BaseEnumsContext';
import { StatusBar } from 'expo-status-bar';
+import ImageInput from '../../components/ImageInput';
const CreateLoanOrderScreen = () => {
const navigation = useNavigation();
@@ -60,6 +61,11 @@ const CreateLoanOrderScreen = () => {
const [cardMonth, setCardMonth] = 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 val = (i + 1).toString().padStart(2, '0');
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 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');
return;
}
@@ -166,6 +172,10 @@ const CreateLoanOrderScreen = () => {
card_name: cardName,
card_month: cardMonth,
card_year: cardYear,
+ passport_one: passportOne,
+ passport_two: passportTwo,
+ passport_three: passportThree,
+ passport_four: passportFour,
};
setLoading(true);
@@ -200,6 +210,7 @@ const CreateLoanOrderScreen = () => {
+
@@ -211,7 +222,6 @@ const CreateLoanOrderScreen = () => {
-
@@ -230,6 +240,13 @@ const CreateLoanOrderScreen = () => {
+
+
+
+
+
+
+
{loading ? : Ýatda sakla}
@@ -244,6 +261,11 @@ const styles = StyleSheet.create({
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' },
+ passportGrid: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ justifyContent: 'space-between',
+ },
});
export default CreateLoanOrderScreen;
\ No newline at end of file