diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 2461564..4ae8bc2 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -19,7 +19,7 @@ android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:exported="true"> - + @@ -27,9 +27,11 @@ - + + + diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx index d7655da..25d7039 100644 --- a/src/navigation/AppNavigator.tsx +++ b/src/navigation/AppNavigator.tsx @@ -14,6 +14,7 @@ import YourContacts from "../screens/management/YourContacts"; import Transaction from "../screens/profile/Transaction"; import GroupDetails from "../screens/home/user-home/GroupDetails"; import EditGroupScreen from "../screens/management/EditGroupScreen"; +import EditBankDetails from "../screens/profile/EditBankDetails"; export type RootStackParamList = { Splash: undefined; @@ -30,16 +31,20 @@ export type RootStackParamList = { BecomeOrg: undefined; YourContacts: undefined; Transaction: undefined; + EditBankDetails: { currentBankName: string | null; currentAccountNumber: string | null; currentBranchCode: string | null }; }; const Stack = createNativeStackNavigator(); export default function AppNavigator() { const linking = { - prefixes: ["myapp://", "https://api.godemo.in"], + prefixes: [ + "myapp://", // A custom scheme for internal or test links. + "https://192.168.1.19" // Your production domain for verified App Links. + ], config: { screens: { - Profile: "EProcurementSB", + Profile: "invite", }, }, }; @@ -60,6 +65,7 @@ export default function AppNavigator() { + ); diff --git a/src/screens/SplashScreen.tsx b/src/screens/SplashScreen.tsx index cde9f53..8d9f920 100644 --- a/src/screens/SplashScreen.tsx +++ b/src/screens/SplashScreen.tsx @@ -1,20 +1,21 @@ import React, { useEffect, useRef } from "react"; import { View, Text, StyleSheet, ActivityIndicator, Animated, Alert, ToastAndroid } from "react-native"; import * as Keychain from "react-native-keychain"; -import { StackNavigationProp } from "@react-navigation/stack"; import { useNavigation } from "@react-navigation/native"; import { RootStackParamList } from "../navigation/AppNavigator"; import { PROFILE_URL } from "../utils/ApiUrls"; import { useDispatch } from "react-redux"; import { clearUser, setUserData } from "../redux/userSlice"; import colors from "../components/colors"; +import { StackNavigationProp } from "@react-navigation/stack"; const SplashScreen = () => { const navigation = useNavigation>(); - const fadeAnim = useRef(new Animated.Value(1)).current; const dispatch = useDispatch(); + const fadeAnim = useRef(new Animated.Value(1)).current; + const fetchUserProfile = async (dispatch: any, navigation: any) => { try { diff --git a/src/screens/home/RequestList.tsx b/src/screens/home/RequestList.tsx index 4dba2b6..6668595 100644 --- a/src/screens/home/RequestList.tsx +++ b/src/screens/home/RequestList.tsx @@ -58,7 +58,7 @@ const RequestList: React.FC = () => { setError(null); const response = await fetch( - "https://api.godemo.in/FundCollect/api/fund/group/pending/request", + "https://exotic-humpback-mistakenly.ngrok-free.app/api/fund/group/pending/request", { method: "GET", headers: { @@ -124,7 +124,7 @@ const RequestList: React.FC = () => { // Make API call to approve the request const response = await fetch( - `https://api.godemo.in/FundCollect/api/fund/requests/${requestId}/approve`, + `https://exotic-humpback-mistakenly.ngrok-free.app/api/fund/requests/${requestId}/approve`, { method: "PUT", headers: { @@ -170,7 +170,7 @@ const RequestList: React.FC = () => { // Make API call to decline the request const response = await fetch( - `https://api.godemo.in/FundCollect/api/fund/requests/${requestedId}/reject?reason=/${reason}`, + `https://exotic-humpback-mistakenly.ngrok-free.app/api/fund/requests/${requestedId}/reject?reason=/${reason}`, { method: "PUT", headers: { diff --git a/src/screens/management/EditGroupScreen.tsx b/src/screens/management/EditGroupScreen.tsx index 1478ab9..1b7fd02 100644 --- a/src/screens/management/EditGroupScreen.tsx +++ b/src/screens/management/EditGroupScreen.tsx @@ -85,7 +85,7 @@ const EditGroupScreen: React.FC = ({ route }) => { setLoading(true); setError(null); try { - const response = await apiCall(`https://api.godemo.in/FundCollect/api/fund/group/details?groupId=${groupId}`, 'GET') as { status: string; data?: GroupDetails; message?: string }; + const response = await apiCall(`https://exotic-humpback-mistakenly.ngrok-free.app/api/fund/group/details?groupId=${groupId}`, 'GET') as { status: string; data?: GroupDetails; message?: string }; if (response.status === 'success' && response.data) { setGroupDetails(response.data); setEditableData({ @@ -141,7 +141,7 @@ const EditGroupScreen: React.FC = ({ route }) => { setSaving(true); try { - await apiCall(`https://api.godemo.in/FundCollect/api/fund/groups/${groupDetails.id}/edit`, 'PUT', editableData); + await apiCall(`https://exotic-humpback-mistakenly.ngrok-free.app/apifund/groups/${groupDetails.id}/edit`, 'PUT', editableData); Alert.alert('Success', 'Group has been updated.'); setEditingField(null); await fetchGroupDetails(); @@ -165,7 +165,7 @@ const EditGroupScreen: React.FC = ({ route }) => { onPress: async () => { setStarting(true); try { - await apiCall(`https://api.godemo.in/FundCollect/api/fund/group/start?groupId=${groupDetails.id}`, 'GET'); + await apiCall(`https://exotic-humpback-mistakenly.ngrok-free.app/api/fund/group/start?groupId=${groupDetails.id}`, 'GET'); Alert.alert('Success', 'Group has been started successfully.'); await fetchGroupDetails(); } catch (e: any) { diff --git a/src/screens/management/ManagementScreen.tsx b/src/screens/management/ManagementScreen.tsx index 9e2f5df..e6e8190 100644 --- a/src/screens/management/ManagementScreen.tsx +++ b/src/screens/management/ManagementScreen.tsx @@ -40,16 +40,15 @@ const ManagementScreen: FC = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [userRequest, setUserRequest] = useState([]) - - const fetchData = useCallback(async () => { if (loading) return; + setLoading(true); setError(null); + try { - console.log(" abin user : ", userRole) if (userRole.role === "ORGANIZER") { - // Fetch all data concurrently + // Fetch all data concurrently for organizers const [myGroupsRes, joinedGroupsRes, requestsRes] = await Promise.all([ apiCall(MY_GROUPS_URL, "GET") as Promise>, apiCall(GET_JOINED_GROUP_URL, "GET") as Promise>, @@ -59,19 +58,22 @@ const ManagementScreen: FC = () => { console.log("My Groups:", myGroupsRes.data); console.log("Joined Groups:", joinedGroupsRes.data); console.log("Requests:", requestsRes.data); + setMyGroups(myGroupsRes.data || []); setJoinedGroups(joinedGroupsRes.data || []); setRequests(requestsRes.data || []); } else { - // Fetch only joined groups for non-organizers - const [joinedGroupsRes, userRequest] = await Promise.all([ - apiCall(GET_JOINED_GROUP_URL, "GET") as Promise>, // Assuming this is also needed - apiCall(USER_REQUEST, "GET") as Promise>, // Assuming this is also needed + // Fetch only joined groups and user requests for non-organizers + const [joinedGroupsRes, userRequestRes] = await Promise.all([ + apiCall(GET_JOINED_GROUP_URL, "GET") as Promise>, + apiCall(USER_REQUEST, "GET") as Promise>, ]); + + console.log("Joined Groups:", joinedGroupsRes.data); + console.log("User Requests:", userRequestRes.data); + setJoinedGroups(joinedGroupsRes.data || []); - setUserRequest(userRequest?.data || []); - console.log("joinedGroupsRes data : ", joinedGroupsRes) - console.log("user request data : ", userRequest) + setUserRequest(userRequestRes.data || []); } } catch (e) { console.error("Fetch Data Error:", e); diff --git a/src/screens/profile/BecomeOrg.tsx b/src/screens/profile/BecomeOrg.tsx index bae00a7..0d63471 100644 --- a/src/screens/profile/BecomeOrg.tsx +++ b/src/screens/profile/BecomeOrg.tsx @@ -41,7 +41,7 @@ const BecomeOrg = () => { const token = credentials.password; const response = await fetch( - "https://api.godemo.in/FundCollect/api/organizer/registration/apply", + "https://exotic-humpback-mistakenly.ngrok-free.app/api/organizer/registration/apply", { method: "POST", headers: { diff --git a/src/screens/profile/EditBankDetails.tsx b/src/screens/profile/EditBankDetails.tsx new file mode 100644 index 0000000..6c4a4b1 --- /dev/null +++ b/src/screens/profile/EditBankDetails.tsx @@ -0,0 +1,162 @@ +import React, { useState } from "react"; +import { + StyleSheet, + View, + Text, + Alert, + ActivityIndicator, + TouchableOpacity, +} from "react-native"; +import { useNavigation, useRoute } from "@react-navigation/native"; +import { CustomButton, CustomInput } from "../../components/common/Custom"; +import colors from "../../components/colors"; +import AppView from "../../components/AppView"; +import { Ionicons } from "@react-native-vector-icons/ionicons"; +import { UPDATE_BANK_URL } from "../../utils/ApiUrls"; +import { apiCall } from "../../utils/ApiHelper"; + +const EditBankDetails = () => { + const navigation = useNavigation(); + const route = useRoute(); + + // Get initial data from route parameters passed from the Profile screen + const { + currentBankName, + currentAccountNumber, + currentBranchCode + } = route.params as { + currentBankName?: string; + currentAccountNumber?: string; + currentBranchCode?: string; + }; + + // Initialize state with the passed-in details + const [bankName, setBankName] = useState(currentBankName || ""); + const [bankAccountNumber, setBankAccountNumber] = useState(currentAccountNumber || ""); + const [branchCode, setBranchCode] = useState(currentBranchCode || ""); + const [loading, setLoading] = useState(false); + + // The update function remains the same, using the local state + const handleUpdateBankDetails = async () => { + if (!bankName || !bankAccountNumber || !branchCode) { + Alert.alert("Validation Error", "Please fill in all bank details."); + return; + } + + try { + setLoading(true); + const response = await apiCall(UPDATE_BANK_URL, 'PUT', { + bankName, + bankAccountNumber, + branchCode, + }) as { status: string; message?: string }; + console.log("Update Bank Response:", response); + if (response.status === "success") { + Alert.alert("Success", response.message || "Bank details updated successfully!"); + navigation.goBack(); + } else { + Alert.alert("Update Failed", response.message || "Could not update bank details."); + } + } catch (error: any) { + console.error("Error updating bank details:", error); + Alert.alert("Error", error.message || "An unexpected error occurred. Please try again."); + } finally { + setLoading(false); + } + }; + + return ( + + + navigation.goBack()} style={styles.backButton}> + + + Edit Bank Details + + + + Update Your Bank Information + + + + + + + + + + navigation.goBack()} + style={styles.cancelButton} + buttonBackground={colors.pastelBlue} + /> + + + + ); +}; + +const styles = StyleSheet.create({ + header: { + flexDirection: "row", + alignItems: "center", + padding: 16, + }, + backButton: { + marginRight: 16, + }, + headerTitle: { + fontSize: 22, + fontWeight: "600", + color: colors.Profile, + }, + container: { + flex: 1, + padding: 16, + }, + sectionTitle: { + fontSize: 18, + fontWeight: "700", + color: "#1e293b", + marginBottom: 20, + }, + buttonContainer: { + marginTop: 24, + }, + cancelButton: { + marginTop: 12, + borderWidth: 1, + borderColor: "#cbd5e1", + }, +}); + +export default EditBankDetails; diff --git a/src/screens/profile/Profile.tsx b/src/screens/profile/Profile.tsx index 7dd5b3b..85f980c 100644 --- a/src/screens/profile/Profile.tsx +++ b/src/screens/profile/Profile.tsx @@ -26,6 +26,9 @@ import { BASE_URL, PUT_USER_PROFILE, PUT_USER_PROFILE_IMG, PUT_USER_STATUS } fro import AppView from "../../components/AppView"; import { useDispatch } from "react-redux"; import { clearUser, setUserData } from "../../redux/userSlice"; +import { StackNavigationProp } from "@react-navigation/stack"; +import { RootStackParamList } from "../../navigation/AppNavigator"; + type UserProfile = { id: string; @@ -46,8 +49,9 @@ type UserProfile = { role?: string; }; + const Profile = () => { - const navigation = useNavigation(); + const navigation = useNavigation>(); const dispatch = useDispatch() const [user, setUser] = useState(null); const [fetching, setFetching] = useState(true); @@ -58,6 +62,7 @@ const Profile = () => { const [error, setError] = useState(null); const [refreshing, setRefreshing] = useState(false); + const fetchUserProfile = async () => { try { const credentials = await Keychain.getGenericPassword(); @@ -67,6 +72,7 @@ const Profile = () => { } const token = credentials.password; + setFetching(true); const response = await fetch( `${BASE_URL}user/profile/view`, @@ -79,6 +85,7 @@ const Profile = () => { } ); + const result = await response.json(); console.log("👤 Profile Response:", result); if (result.status === "success") { @@ -115,6 +122,7 @@ const Profile = () => { } }; + const updateProfile = async () => { try { const credentials = await Keychain.getGenericPassword(); @@ -124,6 +132,7 @@ const Profile = () => { } const token = credentials.password; + const response = await fetch( PUT_USER_PROFILE, { @@ -144,6 +153,7 @@ const Profile = () => { } ); + const result = await response.json(); if (result.status === "success") { Alert.alert("Success", "Profile updated successfully"); @@ -158,6 +168,7 @@ const Profile = () => { } }; + const pickImage = () => { launchImageLibrary({ mediaType: "photo", quality: 0.5 }, (response) => { if (response.didCancel) return; @@ -165,13 +176,14 @@ const Profile = () => { Alert.alert("Error", response.errorMessage || "Something went wrong"); } else if (response.assets && response.assets.length > 0) { const selectedImage = response.assets[0]; - setUser((prev:any) => + setUser((prev: any) => prev ? { ...prev, filePath: selectedImage.uri ?? null } : prev ); } }); }; + const handleUpdateImage = async () => { try { const credentials = await Keychain.getGenericPassword(); @@ -181,13 +193,16 @@ const Profile = () => { } const token = credentials.password; + if (!user?.filePath) { Alert.alert("Error", "Please select an image first"); return; } + setImageUpdating(true); + const formData = new FormData(); formData.append("file", { uri: user?.filePath, @@ -195,6 +210,7 @@ const Profile = () => { name: "profile.jpg", } as any); + const response = await fetch( PUT_USER_PROFILE_IMG, { @@ -207,18 +223,21 @@ const Profile = () => { } ); + const result = await response.json(); if (result.status === "success") { Alert.alert("Success", "Profile image updated successfully"); + // If API returns updated image, set it if (result.data?.imageBlob) { - setUser((prev:any) => prev ? { ...prev, imageBlob: result.data.imageBlob, filePath: null } : prev); + setUser((prev: any) => prev ? { ...prev, imageBlob: result.data.imageBlob, filePath: null } : prev); } else { // Otherwise, keep the local filePath until next fetch - setUser((prev:any) => prev ? { ...prev, filePath: user?.filePath } : prev); + setUser((prev: any) => prev ? { ...prev, filePath: user?.filePath } : prev); } + // Refresh profile from API as well fetchUserProfile(); } else { @@ -232,6 +251,7 @@ const Profile = () => { } }; + const userStatus = async () => { try { const credentials = await Keychain.getGenericPassword(); @@ -241,6 +261,7 @@ const Profile = () => { } const token = credentials.password; + setLoading(true); setError(null); const response = await fetch(PUT_USER_STATUS, { @@ -251,13 +272,16 @@ const Profile = () => { }, }); + const json: ApiResponse = await response.json(); + console.log("👤 Profile Status Response:", json); if (json.status === "success") { setStatusData(json.data); console.log("statusData response :", json); + } else { setError(json.message || "Failed to fetch status"); } @@ -268,15 +292,18 @@ const Profile = () => { } }; + const logout = async (navigation: any) => { try { const credentials = await Keychain.getGenericPassword(); + if (!credentials) { Alert.alert("Info", "No user is currently logged in"); return; } + // Clear stored credentials await Keychain.resetGenericPassword(); dispatch(clearUser()) @@ -288,6 +315,7 @@ const Profile = () => { } }; + const confirmLogout = (navigation: any) => { Alert.alert( "Logout", @@ -299,17 +327,22 @@ const Profile = () => { ); }; + console.log("statusData?.role :", statusData?.role); + console.log("statusData?.organizerStatus :", statusData?.organizerStatus); + useFocusEffect( useCallback(() => { userStatus(); }, []) ); + useEffect(() => { userStatus(); fetchUserProfile(); }, []); + const onRefresh = () => { setRefreshing(true); // Simulate a network request or data fetching @@ -319,6 +352,7 @@ const Profile = () => { }, 2000); // 2 seconds delay }; + console.log("user image :", user?.filePath); const getProfileImage = () => { if (user?.imageBlob) { @@ -333,6 +367,7 @@ const Profile = () => { + return ( {/* Header */} @@ -350,6 +385,7 @@ const Profile = () => { Loading profile... + } { } > + {/* Profile Card */} {/* Profile Image Section */} @@ -388,6 +425,7 @@ const Profile = () => { ) } + { + { style={styles.updateImageButton} buttonBackground={colors.pastelBlue} + /> + {!editing ? ( <> {/* Profile Details */} @@ -420,8 +461,9 @@ const Profile = () => { Become Organizer {/* - - */} + + + */} navigation.navigate('Transaction')}> @@ -444,6 +486,7 @@ const Profile = () => { + @@ -452,6 +495,7 @@ const Profile = () => { + @@ -460,6 +504,7 @@ const Profile = () => { + @@ -468,6 +513,7 @@ const Profile = () => { + @@ -476,6 +522,7 @@ const Profile = () => { + @@ -484,6 +531,7 @@ const Profile = () => { + @@ -492,7 +540,46 @@ const Profile = () => { + {/* BANK DETAILS SECTION */} + + + Bank Information + {/* Navigate to a screen you will create for editing bank details */} + { navigation.navigate("EditBankDetails", { + currentBankName: user?.bankName, + currentAccountNumber: user?.bankAccountNumber, + currentBranchCode: user?.branchCode, + })}} + icon="create-outline" + style={styles.editButton} + buttonBackground={colors.pastelBlue} + /> + + + + + Bank Name + {user?.bankName || "Not provided"} + + + + + + Branch Code + {user?.branchCode || "Not provided"} + + + + + + Account Number + {user?.bankAccountNumber || "Not provided"} + + + @@ -503,8 +590,9 @@ const Profile = () => { Edit Profile + { icon="person" /> + { icon="person" /> + { keyboardType="email-address" /> + { icon="at" /> { multiline /> { icon="male-female" /> + { /> + )} @@ -584,6 +677,7 @@ const Profile = () => { ); }; + const styles = StyleSheet.create({ container: { flex: 1, @@ -616,6 +710,7 @@ const styles = StyleSheet.create({ padding: 4, }, + profileCard: { borderRadius: 20, margin: 16, @@ -775,4 +870,5 @@ const styles = StyleSheet.create({ }, }); -export default Profile; \ No newline at end of file + +export default Profile; diff --git a/src/utils/ApiHelper.tsx b/src/utils/ApiHelper.tsx index 897d140..84552fa 100644 --- a/src/utils/ApiHelper.tsx +++ b/src/utils/ApiHelper.tsx @@ -17,7 +17,7 @@ export async function apiCall( try { // Get saved credentials from Keychain const creds = await Keychain.getGenericPassword(); - + console.log("hihihi", creds); const headers = { "Content-Type": "application/json", ...(creds ? { Authorization: `Bearer ${creds.password}` } : {}), diff --git a/src/utils/ApiUrls.tsx b/src/utils/ApiUrls.tsx index 37b43a7..7205ace 100644 --- a/src/utils/ApiUrls.tsx +++ b/src/utils/ApiUrls.tsx @@ -17,3 +17,4 @@ export const PUT_USER_PROFILE = `${BASE_URL}user/profile/update` export const PUT_USER_STATUS = `${BASE_URL}user/profile/status` export const USER_REQUEST = `${BASE_URL}fund/my-requests` export const GROUP_ALL_MEMBER = `${BASE_URL}fund/group/all/member` +export const UPDATE_BANK_URL = `${BASE_URL}user/bank/update`;