From d4a1866f3469c4439c5a0d2106c2ab627b339c7f Mon Sep 17 00:00:00 2001 From: Abin Toppo Date: Fri, 19 Sep 2025 13:39:04 +0530 Subject: [PATCH] with helper funtion --- src/screens/LoginScreen.tsx | 4 +- src/screens/OtpVarificationScreen.tsx | 8 +- src/screens/SplashScreen.tsx | 60 +-- src/screens/management/EditGroupScreen.tsx | 453 +++++++++----------- src/screens/management/ManagementScreen.tsx | 35 +- src/screens/profile/Profile.tsx | 2 +- src/utils/ApiUrls.tsx | 6 +- src/utils/HelperFunction.tsx | 62 +++ 8 files changed, 314 insertions(+), 316 deletions(-) create mode 100644 src/utils/HelperFunction.tsx diff --git a/src/screens/LoginScreen.tsx b/src/screens/LoginScreen.tsx index 27c8a2a..8fce795 100644 --- a/src/screens/LoginScreen.tsx +++ b/src/screens/LoginScreen.tsx @@ -19,6 +19,7 @@ import type { StackNavigationProp } from "@react-navigation/stack"; import { RootStackParamList } from "../navigation/AppNavigator"; import { CustomButton, CustomInput } from "../components/common/Custom"; import colors from "../components/colors"; +import { useSelector } from "react-redux"; const theme = { primary: "#111111", @@ -41,7 +42,8 @@ export default function LoginScreen() { const fade = useRef(new Animated.Value(0)).current; const slide = useRef(new Animated.Value(24)).current; const ctaScale = useRef(new Animated.Value(1)).current; - + const user = useSelector((state:any) => state.user) + console.log("current user ----",user) useEffect(() => { Animated.parallel([ Animated.timing(fade, { toValue: 1, duration: 600, useNativeDriver: true }), diff --git a/src/screens/OtpVarificationScreen.tsx b/src/screens/OtpVarificationScreen.tsx index c611f49..17f78a4 100644 --- a/src/screens/OtpVarificationScreen.tsx +++ b/src/screens/OtpVarificationScreen.tsx @@ -18,9 +18,12 @@ import { } from "react-native"; import { RootStackParamList } from "../navigation/AppNavigator"; import * as Keychain from "react-native-keychain"; -import { LOGIN_URL, VARIFY_OTP_URL } from "../utils/ApiUrls"; +import { BASE_URL, LOGIN_URL, VARIFY_OTP_URL } from "../utils/ApiUrls"; import { StackNavigationProp } from "@react-navigation/stack"; import colors from "../components/colors"; +import { useDispatch } from "react-redux"; +import { setUserData } from "../redux/userSlice"; +import { fetchUserProfile } from "../utils/HelperFunction"; const theme = { primary: "#111111", @@ -47,6 +50,7 @@ export default function OtpVerificationScreen() { const slide = useRef(new Animated.Value(24)).current; const ctaScale = useRef(new Animated.Value(1)).current; + useEffect(() => { Animated.parallel([ Animated.timing(fade, { toValue: 1, duration: 600, useNativeDriver: true }), @@ -74,6 +78,7 @@ export default function OtpVerificationScreen() { } }; + const onSubmit = async (otp: string, mobile: string) => { try { const payload = { mobile, otp }; @@ -86,6 +91,7 @@ export default function OtpVerificationScreen() { console.log("✅ OTP Verified:", res.data); if (res.data.statusCode === 200) { saveToken(res.data.data); + fetchUserProfile() navigation.navigate('Home') ToastAndroid.show("OTP Verified Successfully!", ToastAndroid.SHORT); } diff --git a/src/screens/SplashScreen.tsx b/src/screens/SplashScreen.tsx index ff214e1..44371f8 100644 --- a/src/screens/SplashScreen.tsx +++ b/src/screens/SplashScreen.tsx @@ -9,67 +9,16 @@ import { useDispatch, useSelector } from "react-redux"; import { setUserData } from "../redux/userSlice"; import colors from "../components/colors"; import { ApiResponse } from "./home/UserProfileStatus"; +import { fetchUserProfile } from "../utils/HelperFunction"; const SplashScreen = () => { - const dispatch = useDispatch(); + const user = useSelector((state: any) => state.user); const navigation = useNavigation>(); const fadeAnim = useRef(new Animated.Value(1)).current; - const fetchUserProfile = async () => { - try { - const credentials = await Keychain.getGenericPassword(); - if (!credentials) { - // Alert.alert("Error", "You are not logged in"); - return; - } - const token = credentials.password; - const response = await fetch( - `${BASE_URL}user/profile/view`, - { - method: "GET", - headers: { - Accept: "*/*", - Authorization: `Bearer ${token}`, - }, - } - ); - - const result = await response.json(); - if (result.status === "success") { - dispatch(setUserData({ - id: result?.data.id, - userId: result?.data.userId, - username: result?.data.username, - firstName: result?.data.firstName, - lastName: result?.data.lastName, - gender: result?.data.gender, - mobile: result?.data.mobile, - email: result?.data.email, - address: result?.data.address, - bankAccountNumber: result?.data.bankAccountNumber, - bankName: result?.data.bankName, - branchCode: result?.data.branchCode, - profilePicture: result?.data.filePath, // or keep as `filePath` - filePath: result?.data.filePath, - imageBlob: result?.data.imageBlob, - role: result?.data.role, - organizerId: result?.data.organizerId, - organizerStatus: result?.data.organizerStatus, - profileStatus: true, // set true once profile is fetched - })); - - console.log("splash user data:", result.data); - } else { - Alert.alert("Error", "Failed to fetch user profile"); - } - } catch (error) { - console.error("❌ Error fetching profile:", error); - Alert.alert("Error", "Could not connect to server"); - } - }; const userStatus = async () => { @@ -133,7 +82,10 @@ const SplashScreen = () => { }, [navigation, fadeAnim]); useEffect(() => { - fetchUserProfile(); + const fetchProfile = async () => { + await fetchUserProfile(); + }; + fetchProfile(); }, []) return ( diff --git a/src/screens/management/EditGroupScreen.tsx b/src/screens/management/EditGroupScreen.tsx index b56280b..b0964fe 100644 --- a/src/screens/management/EditGroupScreen.tsx +++ b/src/screens/management/EditGroupScreen.tsx @@ -1,5 +1,5 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { View, Text, @@ -30,282 +30,251 @@ export interface GroupDetails { startDate: [number, number, number]; endDate: [number, number, number] | null; contributionAmount: number; + minParticipants: number; maxParticipants: number; latePaymentCharge: number | null; cycleType: 'QUARTERLY' | 'MONTHLY' | 'WEEKLY' | 'CUSTOM'; + status: 'PENDING' | 'ACTIVE' | 'COMPLETED'; visibility: 'PUBLIC' | 'PRIVATE'; - organizerFirstName: string; - organizerLastName: string; - customCycleDays: number; - status: string; totalParticipants: number; participants: Participant[]; } export interface EditGroupPayload { - groupName: string; - description: string; - visibility: 'PUBLIC' | 'PRIVATE'; - cycleType: 'QUARTERLY' | 'MONTHLY' | 'WEEKLY' | 'CUSTOM'; - contributionAmount: number; - maxParticipants: number; - latePaymentCharge: number; - customCycleDays: number; - startDate: string; - endDate: string | null; + groupName?: string; + description?: string; + maxParticipants?: number; } type EditGroupScreenProps = NativeStackScreenProps; +type EditingSection = 'details' | 'financials' | null; -// --- Helper Components for Read-Only View --- -const InfoRow: React.FC<{ label: string; value: string | number; isStatus?: boolean }> = ({ label, value, isStatus = false }) => ( - - {label} - - {value} - - +// --- Helper Components --- +const InfoRow: React.FC<{ label: string; value: string | number | null; isStatus?: boolean }> = ({ label, value, isStatus = false }) => ( + + {label} + + {value ?? 'N/A'} + + ); const ParticipantRow: React.FC<{ participant: Participant }> = ({ participant }) => ( - - - - {`${participant.firstName} ${participant.lastName || ''}`.trim()} - {participant.role === 'ORGANIZER' && ( - - Organizer - - )} - - {participant.mobile} + + + + {`${participant.firstName} ${participant.lastName || ''}`.trim()} + {participant.role === 'ORGANIZER' && ( + Organizer + )} + + {participant.mobile} + - ); +const FormInput: React.FC<{ label: string; value: string; onChangeText: (text: string) => void; multiline?: boolean; keyboardType?: 'default' | 'numeric' }> = +({ label, value, onChangeText, multiline = false, keyboardType = 'default' }) => ( + + {label} + + +); // --- Main Screen Component --- -const EditGroupScreen: React.FC = ({ route, navigation }) => { - const { groupId } = route.params; +const EditGroupScreen: React.FC = ({ route }) => { + const { groupId } = route.params; - // --- State Management --- - const [isEditing, setIsEditing] = useState(false); - const [groupDetails, setGroupDetails] = useState(null); - const [loading, setLoading] = useState(true); - const [saving, setSaving] = useState(false); - const [error, setError] = useState(null); + const [editingSection, setEditingSection] = useState(null); + const [groupDetails, setGroupDetails] = useState(null); + const [loading, setLoading] = useState(true); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(null); - // --- Form State --- - const [groupName, setGroupName] = useState(''); - const [description, setDescription] = useState(''); - const [contributionAmount, setContributionAmount] = useState(''); - const [maxParticipants, setMaxParticipants] = useState(''); - const [latePaymentCharge, setLatePaymentCharge] = useState(''); + const [groupName, setGroupName] = useState(''); + const [description, setDescription] = useState(''); + const [maxParticipants, setMaxParticipants] = useState(''); - // --- Data Fetching --- - const fetchGroupDetails = async () => { - if (!groupId) { - setError("Group ID not provided."); - setLoading(false); - return; - } - setLoading(true); - try { - console.log(`Fetching details for group ID: ${groupId}`); - const response = await apiCall( - `https://api.godemo.in/FundCollect/api/fund/group/details?groupId=${groupId}`, - 'GET', - ) as { status: string; data?: GroupDetails; message?: string }; - - if (response.status === 'success' && response.data) { - setGroupDetails(response.data); - // Populate form state immediately - const data = response.data; - setGroupName(data.groupName); - setDescription(data.description); - setContributionAmount(data.contributionAmount.toString()); - setMaxParticipants(data.maxParticipants.toString()); - setLatePaymentCharge((data.latePaymentCharge || 0).toString()); - } else { - throw new Error(response.message || 'Failed to fetch group data.'); - } - } catch (e: any) { - setError(e.message); - } finally { - setLoading(false); - } - }; + const fetchGroupDetails = useCallback(async () => { + 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 }; + if (response.status === 'success' && response.data) { + setGroupDetails(response.data); + setGroupName(response.data.groupName); + setDescription(response.data.description); + setMaxParticipants(response.data.maxParticipants.toString()); + } else { + throw new Error(response.message || 'Failed to fetch group data.'); + } + } catch (e: any) { + setError(e.message); + } finally { + setLoading(false); + } + }, [groupId]); - useEffect(() => { - fetchGroupDetails(); - }, [groupId]); + useEffect(() => { + fetchGroupDetails(); + }, [fetchGroupDetails]); - // --- Action Handlers --- - const handleCancelEdit = () => { - // Re-populate state from original details to discard any changes - if (groupDetails) { - setGroupName(groupDetails.groupName); - setDescription(groupDetails.description); - setContributionAmount(groupDetails.contributionAmount.toString()); - setMaxParticipants(groupDetails.maxParticipants.toString()); - setLatePaymentCharge((groupDetails.latePaymentCharge || 0).toString()); - } - setIsEditing(false); - }; - - const handleSaveChanges = async () => { - if (!groupDetails) return; - setSaving(true); - const payload: EditGroupPayload = { - groupName, - description, - visibility: groupDetails.visibility, // Assuming these are not editable - cycleType: groupDetails.cycleType, - contributionAmount: parseFloat(contributionAmount) || 0, - maxParticipants: parseInt(maxParticipants, 10) || 0, - latePaymentCharge: parseFloat(latePaymentCharge) || 0, - customCycleDays: groupDetails.customCycleDays, - startDate: new Date(groupDetails.startDate.join('-')).toISOString().split('T')[0], - endDate: groupDetails.endDate ? new Date(groupDetails.endDate.join('-')).toISOString().split('T')[0] : null, + const handleCancel = () => { + if (groupDetails) { + setGroupName(groupDetails.groupName); + setDescription(groupDetails.description); + setMaxParticipants(groupDetails.maxParticipants.toString()); + } + setEditingSection(null); }; - try { - const response = await apiCall( - `https://api.godemo.in/FundCollect/api/fund/groups/${groupDetails.id}/edit`, 'PUT', payload - ) as { status: string; message?: string }; + const handleSave = async (section: EditingSection) => { + if (!groupDetails || !section) return; - if (response.status === 'success') { - Alert.alert('Success', 'Group details have been updated.'); - setIsEditing(false); - await fetchGroupDetails(); // Refresh data to show updated view - } else { - throw new Error(response.message || 'Failed to save changes.'); - } - } catch (e: any) { - Alert.alert('Save Error', e.message); - } finally { - setSaving(false); - } - }; + let payload: EditGroupPayload = {}; + if (section === 'details') { + payload = { groupName, description }; + } else if (section === 'financials') { + const newMaxParticipants = parseInt(maxParticipants, 10); + if (newMaxParticipants < groupDetails.totalParticipants) { + Alert.alert('Invalid Input', `Max participants cannot be less than the current count (${groupDetails.totalParticipants}).`); + return; + } + payload = { maxParticipants: newMaxParticipants }; + } - // --- Render Logic --- - if (loading) return ; - if (error) return {error}; - if (!groupDetails) return null; - - return ( - - - - Group Overview - {!isEditing && groupDetails.status !== 'ACTIVE' ? ( - setIsEditing(true)}> - Edit - - ) : isEditing ? ( - - Cancel - - ) : null} - - - {/* --- Main Details Card --- */} - - {isEditing ? ( - <> - Group Name - - Description - - - ) : ( - <> - {groupDetails.groupName} - {groupDetails.description} - - )} - - - - Contribution - {`₹${groupDetails.contributionAmount.toLocaleString('en-IN')}`} - - - Cycle - {groupDetails.cycleType} - + setSaving(true); + try { + await apiCall(`https://api.godemo.in/FundCollect/api/fund/groups/${groupDetails.id}/edit`, 'PUT', payload); + Alert.alert('Success', 'Group has been updated.'); + setEditingSection(null); + await fetchGroupDetails(); + } catch (e: any) { + Alert.alert('Save Error', e.message || 'An unexpected error occurred.'); + } finally { + setSaving(false); + } + }; + + const CardActionButtons: React.FC<{ section: EditingSection }> = ({ section }) => ( + + + Cancel + + handleSave(section)} disabled={saving}> + {saving ? : Save} + - - - {/* --- Participants Card --- */} - - Participants ({groupDetails.totalParticipants}/{groupDetails.maxParticipants}) - {groupDetails.participants.map(p => )} - + ); - {/* --- Other Details Card (Always Read-Only) --- */} - - - - - - - {isEditing && ( - - {saving ? : Save Changes} - - )} - - - ); + if (loading) return ; + if (error) return {error}; + if (!groupDetails) return null; + + const isEditable = groupDetails.status === 'PENDING'; + + return ( + + + Group Overview + + + + Group Details + {isEditable && editingSection !== 'details' && ( + setEditingSection('details')}>Edit + )} + + {editingSection === 'details' ? ( + <> + + + + + ) : ( + <> + {groupDetails.groupName} + {groupDetails.description} + + )} + + + + + Financial Rules + {isEditable && editingSection !== 'financials' && ( + setEditingSection('financials')}>Edit + )} + + + {editingSection === 'financials' ? ( + <> + + + + ) : ( + + )} + + + + + + Participants ({groupDetails.totalParticipants}) + {groupDetails.participants.length > 0 ? ( + groupDetails.participants.map(p => ) + ) : ( + No participants have joined yet. + )} + + + + ); }; -// --- Styles --- const styles = StyleSheet.create({ - container: { flex: 1, backgroundColor: '#F3F4F6' }, - contentContainer: { paddingVertical: 20, paddingHorizontal: 16 }, - centered: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F3F4F6' }, - errorText: { color: '#EF4444', fontSize: 16, textAlign: 'center' }, - - header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }, - headerTitle: { fontSize: 26, fontWeight: 'bold', color: '#111827' }, - editButton: { backgroundColor: '#E0E7FF', paddingHorizontal: 16, paddingVertical: 8, borderRadius: 20 }, - editButtonText: { color: '#4338CA', fontSize: 14, fontWeight: '600' }, - cancelButtonText: { fontSize: 16, color: '#6B7280', fontWeight: '500' }, - - card: { backgroundColor: '#FFFFFF', borderRadius: 16, padding: 20, marginBottom: 16, elevation: 1, shadowColor: '#374151', shadowOpacity: 0.05, shadowRadius: 10, shadowOffset: { width: 0, height: 4 } }, - cardTitle: { fontSize: 18, fontWeight: '600', color: '#1F2937', marginBottom: 10 }, - - groupNameView: { fontSize: 22, fontWeight: '700', color: '#1F2937', marginBottom: 8 }, - descriptionView: { fontSize: 16, color: '#6B7280', lineHeight: 24 }, - - divider: { height: 1, backgroundColor: '#E5E7EB', marginVertical: 20 }, - - row: { flexDirection: 'row', gap: 20 }, - column: { flex: 1 }, - - inputLabel: { fontSize: 14, color: '#6B7280', marginBottom: 8, fontWeight: '500' }, - input: { backgroundColor: '#F9FAFB', borderWidth: 1, borderColor: '#D1D5DB', borderRadius: 10, paddingHorizontal: 14, paddingVertical: 12, fontSize: 16, color: '#111827', marginBottom: 16 }, - textArea: { height: 80, textAlignVertical: 'top' }, - dataText: { fontSize: 18, fontWeight: '600', color: '#111827' }, - - infoRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: '#F3F4F6' }, - infoLabel: { fontSize: 16, color: '#6B7280' }, - infoValue: { fontSize: 16, color: '#1F2937', fontWeight: '600' }, - - participantRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: '#F3F4F6' }, - participantName: { fontSize: 16, fontWeight: '600', color: '#111827' }, - participantMobile: { fontSize: 14, color: '#6B7280', marginTop: 4 }, - organizerBadge: { backgroundColor: '#FEF3C7', paddingHorizontal: 8, paddingVertical: 3, borderRadius: 12, marginLeft: 10 }, - organizerBadgeText: { color: '#92400E', fontSize: 12, fontWeight: '500' }, - - saveButton: { backgroundColor: '#4F46E5', borderRadius: 12, paddingVertical: 16, alignItems: 'center', marginTop: 10, elevation: 2 }, - saveButtonText: { color: '#FFFFFF', fontSize: 16, fontWeight: '600' }, + container: { flex: 1, backgroundColor: '#F3F4F6' }, + contentContainer: { padding: 16, paddingBottom: 40 }, + centered: { flex: 1, justifyContent: 'center', alignItems: 'center' }, + errorText: { color: '#EF4444', fontSize: 16, textAlign: 'center' }, + headerTitle: { fontSize: 28, fontWeight: 'bold', color: '#111827', marginBottom: 20 }, + + card: { backgroundColor: '#FFFFFF', borderRadius: 16, padding: 20, marginBottom: 16, shadowColor: '#374151', shadowOpacity: 0.05, shadowRadius: 10 }, + cardHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderBottomWidth: 1, borderBottomColor: '#E5E7EB', paddingBottom: 10, marginBottom: 16 }, + cardTitle: { fontSize: 18, fontWeight: 'bold', color: '#1F2937' }, + editButtonText: { color: '#4F46E5', fontSize: 15, fontWeight: '600' }, + + groupNameView: { fontSize: 22, fontWeight: '700', color: '#1F2937', marginBottom: 8 }, + descriptionView: { fontSize: 16, color: '#6B7280', lineHeight: 24 }, + + inputContainer: { marginBottom: 16 }, + inputLabel: { fontSize: 14, color: '#6B7280', marginBottom: 8, fontWeight: '500' }, + input: { backgroundColor: '#F9FAFB', borderWidth: 1, borderColor: '#D1D5DB', borderRadius: 10, padding: 14, fontSize: 16 }, + textArea: { height: 100, textAlignVertical: 'top' }, + + infoRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 14, borderBottomWidth: 1, borderBottomColor: '#F3F4F6' }, + infoLabel: { fontSize: 16, color: '#6B7280' }, + infoValue: { fontSize: 16, fontWeight: '600', textTransform: 'capitalize' }, + + participantRow: { paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: '#F3F4F6' }, + participantName: { fontSize: 16, fontWeight: '600', color: '#111827' }, + participantMobile: { fontSize: 14, color: '#6B7280', marginTop: 4 }, + organizerBadge: { backgroundColor: '#FEF3C7', paddingHorizontal: 8, paddingVertical: 3, borderRadius: 12, marginLeft: 10 }, + organizerBadgeText: { color: '#92400E', fontSize: 12, fontWeight: '500' }, + noParticipantsText: { fontSize: 15, color: '#6B7280', textAlign: 'center', paddingVertical: 20 }, + + cardActions: { flexDirection: 'row', justifyContent: 'flex-end', marginTop: 20, borderTopWidth: 1, borderTopColor: '#E5E7EB', paddingTop: 16 }, + button: { borderRadius: 10, paddingVertical: 10, paddingHorizontal: 20, marginLeft: 10, minWidth: 80, alignItems: 'center' }, + saveButton: { backgroundColor: '#4F46E5' }, + cancelButton: { backgroundColor: '#E5E7EB' }, + buttonText: { color: '#FFFFFF', fontSize: 15, fontWeight: '600' }, }); export default EditGroupScreen; diff --git a/src/screens/management/ManagementScreen.tsx b/src/screens/management/ManagementScreen.tsx index 1c8ab5d..ec59644 100644 --- a/src/screens/management/ManagementScreen.tsx +++ b/src/screens/management/ManagementScreen.tsx @@ -14,7 +14,7 @@ import { import { useNavigation, useFocusEffect } from "@react-navigation/native"; import { StackNavigationProp } from "@react-navigation/stack"; import { RootStackParamList } from "../../navigation/AppNavigator"; -import { APPROVE_REQUEST_URL, BASE_URL, DECLINE_REQUEST_URL, JOIN_GROUP_URL, MY_GROUPS_URL, REQUESTS_URL } from "../../utils/ApiUrls"; +import { APPROVE_REQUEST_URL, BASE_URL, DECLINE_REQUEST_URL, GET_JOINED_GROUP_URL, JOIN_GROUP_URL, MY_GROUPS_URL, REQUESTS_URL } from "../../utils/ApiUrls"; import colors from "../../components/colors"; import AppView from "../../components/AppView"; import { apiCall } from "../../utils/ApiHelper"; @@ -23,11 +23,13 @@ import { MyGroup, JoinedGroup, JoinRequest, ApiResponse } from '../../types'; import { MyGroupCard } from "../../components/MyGroupCard"; import { JoinedGroupCard } from "../../components/JoinedGroupCard"; import { RequestCard } from "../../components/RequestCard"; +import { useSelector } from "react-redux"; type GroupManagementNavProp = StackNavigationProp; -type ActiveTab = "My Groups" | "Joined Groups" | "Requests"; +type ActiveTab = "My Groups" | "Joined Groups" | "Requests" | "Your Requests"; const ManagementScreen: FC = () => { + const user = useSelector((state:any) => state.user) const [activeTab, setActiveTab] = useState("My Groups"); const navigation = useNavigation(); const [myGroups, setMyGroups] = useState([]); @@ -35,8 +37,9 @@ const ManagementScreen: FC = () => { const [requests, setRequests] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const userRole = "ORGANIZER"; + const userRole = user.role; + console.log(" abin user : ", user) const fetchData = useCallback(async () => { if (loading) return; setLoading(true); @@ -44,9 +47,9 @@ const ManagementScreen: FC = () => { try { if (userRole === "ORGANIZER") { // Fetch all data concurrently - const [myGroupsRes,joinedGroupsRes,requestsRes] = await Promise.all([ + const [myGroupsRes, joinedGroupsRes, requestsRes] = await Promise.all([ apiCall(MY_GROUPS_URL, "GET") as Promise>, - apiCall(JOIN_GROUP_URL, "GET") as Promise>, // Assuming this is also needed + apiCall(GET_JOINED_GROUP_URL, "GET") as Promise>, // Assuming this is also needed apiCall(REQUESTS_URL, "GET") as Promise>, ]); @@ -58,8 +61,10 @@ const ManagementScreen: FC = () => { setRequests(requestsRes.data || []); } else { // Fetch only joined groups for non-organizers - // const joinedGroupsRes = await apiCall(JOINED_GROUPS_URL, "GET") as Promise>; - // setJoinedGroups(joinedGroupsRes.data || []); + const [joinedGroupsRes] = await Promise.all([ + apiCall(GET_JOINED_GROUP_URL, "GET") as Promise>, // Assuming this is also needed + ]); + setJoinedGroups(joinedGroupsRes.data || []); } } catch (e) { console.error("Fetch Data Error:", e); @@ -74,7 +79,7 @@ const ManagementScreen: FC = () => { const handleAction = async (url: string, body: object, successMessage: string, failureMessage: string, action: string) => { // ... (logic remains the same) console.log(url) - try { + try { setLoading(true); // Optionally show a loading state during the action // Make the POST request with an empty body const res = await apiCall(url, "PUT", {}) as ApiResponse | null; @@ -93,15 +98,15 @@ const ManagementScreen: FC = () => { } }; - const handleApprove = (req: JoinRequest) => handleAction(APPROVE_REQUEST_URL+req.requestId+"/approve", { requestId: req.requestId }, `${req.userName} has been added.`, "Failed to approve request.","Approve"); - const handleDecline = (req: JoinRequest) => handleAction(DECLINE_REQUEST_URL+req.requestId+"/reject?reason=/"+" ", { requestId: req.requestId }, `Request from ${req.userName} has been declined.`, "Failed to decline request.","Decline"); + const handleApprove = (req: JoinRequest) => handleAction(APPROVE_REQUEST_URL + req.requestId + "/approve", { requestId: req.requestId }, `${req.userName} has been added.`, "Failed to approve request.", "Approve"); + const handleDecline = (req: JoinRequest) => handleAction(DECLINE_REQUEST_URL + req.requestId + "/reject?reason=/" + " ", { requestId: req.requestId }, `Request from ${req.userName} has been declined.`, "Failed to decline request.", "Decline"); const handleManageGroup = (group: MyGroup) => navigation.navigate('EditGroup', { groupId: group.id }); const renderContent = () => { if (error) { return {error}; } - + switch (activeTab) { case "My Groups": if (loading) { @@ -151,9 +156,9 @@ const ManagementScreen: FC = () => { default: return null; } }; - - const tabs: ActiveTab[] = userRole === 'ORGANIZER' ? ["My Groups", "Joined Groups", "Requests"] : ["Joined Groups"]; - + + const tabs: ActiveTab[] = userRole === 'ORGANIZER' ? ["My Groups", "Joined Groups", "Requests"] : ["Joined Groups", "Your Requests"]; + return ( @@ -176,7 +181,7 @@ const ManagementScreen: FC = () => { const styles = StyleSheet.create({ container: { flex: 1, - backgroundColor: '#f5f6fa' + backgroundColor: '#f5f6fa' }, header: { paddingHorizontal: 20, diff --git a/src/screens/profile/Profile.tsx b/src/screens/profile/Profile.tsx index 378ba90..d33d621 100644 --- a/src/screens/profile/Profile.tsx +++ b/src/screens/profile/Profile.tsx @@ -288,7 +288,7 @@ const Profile = () => { console.log("user image :", user?.filePath); const getProfileImage = () => { if (user?.imageBlob) { - return `data:image/jpeg;base64,${user.imageBlob}`; + return `data:image/jpeg;base64,${user.imageBlob}`; } if (user?.filePath) { return user.filePath; diff --git a/src/utils/ApiUrls.tsx b/src/utils/ApiUrls.tsx index 01cc889..8e62644 100644 --- a/src/utils/ApiUrls.tsx +++ b/src/utils/ApiUrls.tsx @@ -5,10 +5,12 @@ export const ORGRAGISTRATION = `${BASE_URL}organizer/registration/apply` export const GROUP_CREATION_URL = `${BASE_URL}fund/groups/{organizerId}` export const PROFILE_URL =`${BASE_URL}user/profile/view` export const PUBLIC_GROUPS_URL =`${BASE_URL}fund/user/public` -export const JOIN_GROUP_URL = `${BASE_URL}fund/my/join/groups`; +export const JOIN_GROUP_URL = `${BASE_URL}fund/`; export const MY_GROUPS_URL =`https://api.godemo.in/FundCollect/api/fund/getAll/my/groups` export const REQUESTS_URL =`https://api.godemo.in/FundCollect/api/fund/group/pending/request` //https://api.godemo.in/FundCollect/api/fund/groups/2a80cdaa-8532-4b8a-87f0-9c1023d18a7f/join/64 export const APPROVE_REQUEST_URL =`https://api.godemo.in/FundCollect/api/fund/requests/` -export const DECLINE_REQUEST_URL =`https://api.godemo.in/FundCollect/api/fund/user/my/groups` \ No newline at end of file +export const DECLINE_REQUEST_URL =`https://api.godemo.in/FundCollect/api/fund/user/my/groups` +// https://api.godemo.in/FundCollect/api/fund/groups/b89f56a1-952d-41b3-ab8d-3c28c91697f5/join/100005 +export const GET_JOINED_GROUP_URL = `https://api.godemo.in/FundCollect/api/fund/my/join/groups` \ No newline at end of file diff --git a/src/utils/HelperFunction.tsx b/src/utils/HelperFunction.tsx new file mode 100644 index 0000000..563798f --- /dev/null +++ b/src/utils/HelperFunction.tsx @@ -0,0 +1,62 @@ +import { useDispatch } from "react-redux"; +import { BASE_URL } from "./ApiUrls"; +import * as Keychain from "react-native-keychain"; +import { setUserData } from "../redux/userSlice"; +import { Alert } from "react-native"; + + + +export const fetchUserProfile = async () => { + const dispatch = useDispatch(); + try { + const credentials = await Keychain.getGenericPassword(); + if (!credentials) { + // Alert.alert("Error", "You are not logged in"); + return; + } + const token = credentials.password; + + const response = await fetch( + `${BASE_URL}user/profile/view`, + { + method: "GET", + headers: { + Accept: "*/*", + Authorization: `Bearer ${token}`, + }, + } + ); + + const result = await response.json(); + if (result.status === "success") { + dispatch(setUserData({ + id: result?.data.id, + userId: result?.data.userId, + username: result?.data.username, + firstName: result?.data.firstName, + lastName: result?.data.lastName, + gender: result?.data.gender, + mobile: result?.data.mobile, + email: result?.data.email, + address: result?.data.address, + bankAccountNumber: result?.data.bankAccountNumber, + bankName: result?.data.bankName, + branchCode: result?.data.branchCode, + profilePicture: result?.data.filePath, // or keep as `filePath` + filePath: result?.data.filePath, + imageBlob: result?.data.imageBlob, + role: result?.data.role, + organizerId: result?.data.organizerId, + organizerStatus: result?.data.organizerStatus, + profileStatus: true, // set true once profile is fetched + })); + + console.log("splash user data:", result.data); + } else { + Alert.alert("Error", "Failed to fetch user profile"); + } + } catch (error) { + console.error("❌ Error fetching profile:", error); + Alert.alert("Error", "Could not connect to server"); + } + }; \ No newline at end of file