diff --git a/package-lock.json b/package-lock.json index 06c04c7..404dccb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "0.0.1", "dependencies": { "@react-native-async-storage/async-storage": "^2.2.0", + "@react-native-community/datetimepicker": "^9.1.0", + "@react-native-documents/picker": "^12.0.1", "@react-native/new-app-screen": "0.83.1", "@react-navigation/bottom-tabs": "^7.15.9", "@react-navigation/native": "^7.1.26", @@ -2976,6 +2978,42 @@ "node": ">=10" } }, + "node_modules/@react-native-community/datetimepicker": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-9.1.0.tgz", + "integrity": "sha512-eadbnk+I2vxvW30iTAsm/qlCnMMAadkifIMYNEB2lzhxN/SvlKc7S2V4k5DyrwjdCbqdcMk3t9K6fnUMcAV34w==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4" + }, + "peerDependencies": { + "expo": ">=52.0.0", + "react": "*", + "react-native": "*", + "react-native-windows": "*" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + }, + "react-native-windows": { + "optional": true + } + } + }, + "node_modules/@react-native-documents/picker": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@react-native-documents/picker/-/picker-12.0.1.tgz", + "integrity": "sha512-vpJKb4t/5bnxe9+gQl+plJfKrrIsmYwANGhNH2B9E1dS1+6FDBzg4Dwmcq4ueaGfkRKEPJ606mJttVEH1ZKZaA==", + "license": "MIT", + "funding": { + "url": "https://github.com/react-native-documents/document-picker?sponsor=1" + }, + "peerDependencies": { + "react": "*", + "react-native": ">=0.79.0" + } + }, "node_modules/@react-native/assets-registry": { "version": "0.83.1", "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.83.1.tgz", diff --git a/package.json b/package.json index 2f05667..cabeacf 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ }, "dependencies": { "@react-native-async-storage/async-storage": "^2.2.0", + "@react-native-community/datetimepicker": "^9.1.0", + "@react-native-documents/picker": "^12.0.1", "@react-native/new-app-screen": "0.83.1", "@react-navigation/bottom-tabs": "^7.15.9", "@react-navigation/native": "^7.1.26", diff --git a/src/navigation/AppNav.js b/src/navigation/AppNav.js index 32f9592..7769427 100644 --- a/src/navigation/AppNav.js +++ b/src/navigation/AppNav.js @@ -16,8 +16,7 @@ import ClientDetailsScreen from '../screens/ClientDetailsScreen'; import EditClientScreen from '../screens/EditClientScreen'; import PipelineScreen from '../screens/PipelineScreen'; -import EnquiryScreen from '../screens/EnquiryScreen'; -import EnquiryListScreen from '../screens/EnquiryListScreen'; +import AddOpportunityScreen from '../screens/AddOpportunityScreen'; import ExpenseScreen from '../screens/ExpenseScreen'; import IncentiveScreen from '../screens/IncentiveScreen'; import LogActivityScreen from '../screens/LogActivityScreen'; @@ -52,7 +51,7 @@ const TabNavigator = () => ( - + ); @@ -77,8 +76,7 @@ const AppNav = () => { - - + diff --git a/src/screens/AddClientScreen.js b/src/screens/AddClientScreen.js index 1a8b8dd..dd4c26e 100644 --- a/src/screens/AddClientScreen.js +++ b/src/screens/AddClientScreen.js @@ -1,18 +1,39 @@ -import React, { useState } from 'react'; -import { View, Text, TextInput, Button, StyleSheet, Alert, ScrollView, Platform, PermissionsAndroid, ActivityIndicator } from 'react-native'; +import React, { useState, useEffect, useContext } from 'react'; +import { + View, Text, TextInput, Button, StyleSheet, Alert, ScrollView, + Platform, PermissionsAndroid, ActivityIndicator, TouchableOpacity, Modal, FlatList +} from 'react-native'; import Geolocation from 'react-native-geolocation-service'; +import { pick } from '@react-native-documents/picker'; import api from '../services/api'; import Colors from '../constants/Colors'; +import { AuthContext } from '../context/AuthContext'; const AddClientScreen = ({ navigation }) => { - const [name, setName] = useState(''); + const { userInfo } = useContext(AuthContext); + const [companyName, setCompanyName] = useState(''); + const [contactName, setContactName] = useState(''); const [phone, setPhone] = useState(''); const [email, setEmail] = useState(''); const [address, setAddress] = useState(''); const [landmark, setLandmark] = useState(''); + const [closingProbability, setClosingProbability] = useState(''); + const [expectedClosingTimeframe, setExpectedClosingTimeframe] = useState(''); + const [isDemoDone, setIsDemoDone] = useState(false); const [location, setLocation] = useState(null); const [loading, setLoading] = useState(false); const [locating, setLocating] = useState(false); + const [selectedFiles, setSelectedFiles] = useState([]); + + // Assignment state + const [users, setUsers] = useState([]); + const [assignedUser, setAssignedUser] = useState(null); + const [userModal, setUserModal] = useState(false); + + useEffect(() => { + setAssignedUser({ id: userInfo?.id, name: 'Myself' }); + api.get('/users').then(r => setUsers(r.data)).catch(() => {}); + }, [userInfo]); const requestLocationPermission = async () => { if (Platform.OS === 'android') { @@ -43,13 +64,11 @@ const AddClientScreen = ({ navigation }) => { setLocating(true); Geolocation.getCurrentPosition( (position) => { - console.log('Location success:', position); setLocation(position.coords); setLocating(false); Alert.alert("Success", "Location Captured!"); }, (error) => { - console.log('Location error:', error); setLocating(false); Alert.alert("Location Error", error.message); }, @@ -57,30 +76,71 @@ const AddClientScreen = ({ navigation }) => { ); }; + const pickFiles = async () => { + try { + const results = await pick({ + multiple: true, + }); + + const uploadedFiles = []; + for (const res of results) { + const formData = new FormData(); + formData.append('file', { + uri: Platform.OS === 'ios' ? res.uri.replace('file://', '') : res.uri, + type: res.type || 'application/octet-stream', + name: res.name || 'file', + }); + + try { + const uploadRes = await api.post('/upload', formData, { + headers: { 'Content-Type': 'multipart/form-data' }, + }); + uploadedFiles.push({ + name: res.name, + type: res.type, + size: res.size, + url: uploadRes.data.url + }); + } catch (err) { + console.error('Upload failed', err); + Alert.alert('Upload Failed', `Could not upload ${res.name}`); + } + } + setSelectedFiles([...selectedFiles, ...uploadedFiles]); + } catch (err) { + if (!DocumentPicker.isCancel(err)) { + console.error(err); + } + } + }; + + const removeFile = (index) => { + setSelectedFiles(selectedFiles.filter((_, i) => i !== index)); + }; + const handleSubmit = async () => { - if (!name || !phone) { - Alert.alert("Error", "Name and Phone are required"); + if (!contactName || !phone) { + Alert.alert("Error", "Contact Name and Phone are required"); return; } - console.log('Current Location Check Before Submit:', location); - if (!location) { - Alert.alert("Debug", "Location state is null! Did you click capture?"); - } - const payload = { - name, + name: companyName || contactName, + companyName, + contactName, phone, status: 'LEAD', + assignedTo: assignedUser?.id || userInfo?.id, + closingProbability: closingProbability ? parseInt(closingProbability) : 0, + expectedClosingTimeframe, + isDemoDone, + files: selectedFiles, ...(email ? { email } : {}), ...(address ? { address } : {}), ...(landmark ? { landmark } : {}), ...(location ? { lat: location.latitude, lng: location.longitude } : {}) }; - console.log('Submitting Payload:', JSON.stringify(payload, null, 2)); - - setLoading(true); try { await api.post('/clients', payload); @@ -97,76 +157,122 @@ const AddClientScreen = ({ navigation }) => { return ( - Name * - + Company Name + + + Contact Name * + Phone * - + + + Assigned To + setUserModal(true)}> + {assignedUser?.name || 'Myself'} + + Email - + Address - + Landmark - + - -