diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx index 58cfcfe..01ab69c 100644 --- a/src/navigation/AppNavigator.tsx +++ b/src/navigation/AppNavigator.tsx @@ -46,12 +46,14 @@ export type RootStackParamList = { TransactionList: undefined RandomSelectUserScreen: { groupId: string | number, cycleNumber: number }; Notification: undefined; - NotificationRedirection:{ data: any }; - Management: - | { actionTab: "My Groups" } - | { actionTab1: "Joined Groups" } - | { actionTab2: "Requests" } - | { actionTab3: "Your Requests" }; + NotificationRedirection: { data: any }; + Management: + | { actionTab: "My Groups" } + | { actionTab1: "Joined Groups" } + | { actionTab2: "Requests" } + | { actionTab3: "Your Requests" } + | { groupId: string | number; } + }; const Stack = createNativeStackNavigator(); diff --git a/src/navigation/types.ts b/src/navigation/types.ts index 26debb7..5517adb 100644 --- a/src/navigation/types.ts +++ b/src/navigation/types.ts @@ -32,6 +32,7 @@ export type RootStackParamList = { ManageJoinedGroup: { groupId: string | number }; Invite: { groupId: string | number; code: string | number }; ManagementTabs:any + NotificationRedirection: { data: any }; }; diff --git a/src/screens/GroupCreation.tsx b/src/screens/GroupCreation.tsx index e51bd18..8461b49 100644 --- a/src/screens/GroupCreation.tsx +++ b/src/screens/GroupCreation.tsx @@ -9,6 +9,7 @@ import { TouchableOpacity, Alert, Image, + ToastAndroid, } from "react-native"; import { useNavigation } from "@react-navigation/native"; import { CustomInput } from "../components/common/Custom"; @@ -75,8 +76,8 @@ export default function GroupCreationForm() { console.log("Reviewing Payload:", payload); navigation.navigate("GroupReview", { form: payload }); } else { - Alert.alert("Incomplete Form", "Please fill out all required fields correctly."); - setErrors('') + ToastAndroid.show('Please fill out all required fields correctly.', ToastAndroid.SHORT); + setErrors(''); } }; diff --git a/src/screens/GroupScreen.tsx b/src/screens/GroupScreen.tsx index 9aee568..e97539c 100644 --- a/src/screens/GroupScreen.tsx +++ b/src/screens/GroupScreen.tsx @@ -12,6 +12,7 @@ import { ScrollView, Alert, RefreshControl, + ToastAndroid, } from "react-native"; import { FAB } from "react-native-paper"; import { Ionicons } from "@react-native-vector-icons/ionicons"; @@ -176,13 +177,12 @@ export default function GroupScreen() { console.log("JOIN_GROUP_URL response:", res); if (res.statusCode === 200) { - Alert.alert("Success", "You requested to join the group."); + ToastAndroid.show('You requested to join the group.', ToastAndroid.SHORT); } else { - // Alert.alert("Error", res.message || "Failed to join group."); + ToastAndroid.show(res.message || "Failed to join group.", ToastAndroid.SHORT); } } catch (error) { console.error("Join Group Error:", error); - // Alert.alert("Error", "An unexpected error occurred."); } }; diff --git a/src/screens/NotificationRedirection.tsx b/src/screens/NotificationRedirection.tsx index b6f3781..8449698 100644 --- a/src/screens/NotificationRedirection.tsx +++ b/src/screens/NotificationRedirection.tsx @@ -18,14 +18,11 @@ const NotificationRedirection: React.FC = () => { // This effect runs once when the component mounts if (data) { console.log('🔄 Processing notification data:', data); - // --- Your Redirection Logic Goes Here --- // Use the 'data' object to decide where to go. // For this example, we'll navigate based on 'msgType'. - let destination: keyof RootStackParamList = 'Home'; // Default destination let params: object | undefined = undefined; - switch (data.msgType) { case 'JOIN_REQUEST': destination = 'Home'; @@ -62,10 +59,9 @@ const NotificationRedirection: React.FC = () => { // Keep the default destination as 'Home' break; } - + console.log(`➡ Redirecting to ${destination} with params:`, params); // Use replace to prevent the user from navigating back to this loading screen navigation.replace(destination, params); - } else { // If no data is passed for some reason, just go to the home screen console.warn('⚠ No data found in notification, redirecting to Home.'); diff --git a/src/screens/home/Home.tsx b/src/screens/home/Home.tsx index 71e74ec..e10113b 100644 --- a/src/screens/home/Home.tsx +++ b/src/screens/home/Home.tsx @@ -59,6 +59,7 @@ const userActions: ActionItem[] = [ { id: "1", title: "Joined Groups", subtitle: "See your groups", action: "NAVIGATE_MANAGEMENT", tab: "Joined Groups", iconName: "person-outline", backOne: '#D7E9F7', backTwo: '#82AAE3' }, { id: "2", title: "Your Requests", subtitle: "Track your requests", action: "NAVIGATE_MANAGEMENT", tab: "Your Requests", iconName: "mail-open-outline", backOne: '#FFF4D2', backTwo: '#FFB344' }, { id: "3", title: "Transactions", subtitle: "View your history", action: "TRANSACTION", iconName: "receipt-outline", backOne: '#decbfcff', backTwo: '#B799FF' }, + { id: "4", title: "Create Group", subtitle: "Create a new group", action: "NAVIGATE_CREATE_GROUP", tab: "Requests", iconName: "people-outline", backOne: '#DFF6DD', backTwo: '#88D18A' }, ]; const Home: React.FC = () => { @@ -95,6 +96,7 @@ const Home: React.FC = () => { } else if (item.action === "TRANSACTION") { navigation.navigate("TransactionList" as never); } else if (item.action === "NAVIGATE_CREATE_GROUP") { + navigation.navigate("GroupCreation" as never); } else { Alert.alert("Unknown action", item.title); diff --git a/src/screens/home/user-home/GroupDetails.tsx b/src/screens/home/user-home/GroupDetails.tsx index b439f60..11bf3da 100644 --- a/src/screens/home/user-home/GroupDetails.tsx +++ b/src/screens/home/user-home/GroupDetails.tsx @@ -12,16 +12,19 @@ import { import { useRoute } from "@react-navigation/native"; import AppView from "../../../components/AppView"; import colors from "../../../components/colors"; -import { Ionicons } from "@react-native-vector-icons/ionicons"; +import Ionicons from '@react-native-vector-icons/ionicons'; import Header from "../../../components/common/Header"; import { apiCall } from "../../../utils/ApiHelper"; import { BASE_URL, CONTRIBUTE, GROUP_ALL_MEMBER } from "../../../utils/ApiUrls"; import ContributionScreen from "../../ContributionScreen"; import CycleDashboard from "./CycleDashboard"; -const CustomDropdown = ({ items, selectedValue, onValueChange, placeholder = "Select an item" }:any) => { + +const CustomDropdown = ({ items, selectedValue, onValueChange, placeholder = "Select an item" }: any) => { const [open, setOpen] = useState(false); - const selectedLabel = items.find((item:any) => item.value === selectedValue)?.label || placeholder; + const selectedLabel = items.find((item: any) => item.value === selectedValue)?.label || placeholder; + + return ( @@ -30,6 +33,7 @@ const CustomDropdown = ({ items, selectedValue, onValueChange, placeholder = "Se + { const route = useRoute(); + + + console.log(" route id : ", route) const [memberData, setMemberData] = useState([]); const [loading, setLoading] = useState(false); const [openModal, setOpenModal] = useState(false); @@ -72,6 +80,8 @@ const GroupDetails = () => { const [cycleDashboardData, setCycleDashboardData] = useState(); const [selectedCycleNumber, setSelectedCycleNumber] = useState(null); const [cycleItems, setCycleItems] = useState([]); + const [selectedUser, setSelectedUser] = useState() as any + const group = { id: route.params.item?.id, @@ -83,15 +93,26 @@ const GroupDetails = () => { members: memberData, }; + + + const getPaymentStatus = async () => { + const response = await apiCall(`${BASE_URL}payments/group/${group.id || route?.params?.groupId}/payment/paid`, 'GET') as { status: string; data?: { currentCycle: number, allPaid: boolean, selectedUser: any }; message?: string }; + console.log("paymentStatus", response.data?.selectedUser) + if (response.status === 'success') { + setSelectedUser(response.data?.selectedUser); + } + } + + + const getCycleDashboard = async () => { try { - const response = await apiCall(`${BASE_URL}payments/groups/${group.id}/cycle-dashboard`, "GET") as any; - console.log("Cycle Dashboard Response: ", response); + const response = await apiCall(`${BASE_URL}payments/groups/${group.id || route?.params?.groupId}/cycle-dashboard`, "GET") as any; const responseData = response.data; setCycleDashboardData(responseData); - + console.log("cycledata", responseData) if (responseData?.cycles) { - const items = responseData.cycles.map(cycle => ({ + const items = responseData.cycles.map((cycle: any) => ({ label: `Cycle ${cycle.cycleNumber}`, value: cycle.cycleNumber })); @@ -105,9 +126,10 @@ const GroupDetails = () => { } }; + const fetchGroupDetails = async () => { try { - const res = await apiCall(`${GROUP_ALL_MEMBER}?groupId=${group.id}`, "GET") as any; + const res = await apiCall(`${GROUP_ALL_MEMBER}?groupId=${group.id || route?.params?.groupId}`, "GET") as any; console.log("Group Members Response: ", res); setMemberData(res.data); } catch (error: any) { @@ -115,16 +137,17 @@ const GroupDetails = () => { } }; + const handleContribution = async () => { try { - const res = await apiCall(`${CONTRIBUTE}${group.id}/calculate-contribution`, 'GET') as any; - console.log("Contribution Calculation Response: ", res); + const res = await apiCall(`${CONTRIBUTE}${group.id || route?.params?.groupId}/calculate-contribution`, 'GET') as any; setPayable(res.data); } catch (error) { console.log("Error in handleContribution: ", error); } }; + const fetchAllData = useCallback(async () => { setLoading(true); await Promise.all([ @@ -133,24 +156,30 @@ const GroupDetails = () => { getCycleDashboard() ]); setLoading(false); - }, [group.id]); + }, [group.id || route?.params?.groupId]); + useEffect(() => { - if (group.id) { + if (group.id || route?.params?.groupId) { fetchAllData(); + getPaymentStatus(); } - }, [group.id, fetchAllData]); + }, [group.id || route?.params?.groupId, fetchAllData]); + const onRefresh = useCallback(async () => { setRefreshing(true); await fetchAllData(); + await getPaymentStatus(); setRefreshing(false); }, [fetchAllData]); + const confirmPayment = async () => { try { const res = await apiCall(`${BASE_URL}payments/contribute/${group?.id}?amount=${payable?.totalPayable}`, 'POST') as any; - if (res.status === 200 || res.status === 201) { + console.log('payment res ', res) + if (res.status === 200 || res.status === 201 || res.statusCode === 200) { ToastAndroid.show('Payment done!', ToastAndroid.SHORT); onRefresh(); } else { @@ -161,18 +190,28 @@ const GroupDetails = () => { } }; - const selectedCycleData = cycleDashboardData?.cycles.find(c => c.cycleNumber === selectedCycleNumber); + + const selectedCycleData = cycleDashboardData?.cycles.find((c: any) => c.cycleNumber === selectedCycleNumber); + return ( + {selectedUser?.firstName && ( + + + Winner: {`${selectedUser.firstName} ${selectedUser.lastName || ''}`} + + + )} } contentContainerStyle={styles.scrollContent} > -
- {group.description} +
+ {group.description || route.name} + @@ -180,15 +219,16 @@ const GroupDetails = () => { Members - ₹{cycleDashboardData?.contributionAmount|| group.collected} + ₹{cycleDashboardData?.contributionAmount || group.collected} Contribution - ₹{cycleDashboardData?.contributionAmount*memberData.length|| group.goal} + ₹{cycleDashboardData?.contributionAmount * memberData.length || group.goal} Total Amount + {cycleItems.length > 0 && ( { placeholder="Select a Cycle" /> )} - + {selectedCycleData && cycleDashboardData && ( )} + Members @@ -209,7 +250,8 @@ const GroupDetails = () => { - {memberData.map((member:any) => ( + + {memberData.map((member: any) => ( @@ -220,7 +262,7 @@ const GroupDetails = () => { ))} - {/* Button at the bottom inside scroll (no absolute positioning) */} + {cycleDashboardData?.status === 'ACTIVE' && !payable?.hasPaid && ( setOpenModal(true)} style={styles.button}> Contribute @@ -228,6 +270,7 @@ const GroupDetails = () => { )} + {openModal && ( { ); }; + export default GroupDetails; + const styles = StyleSheet.create({ dropdownWrapper: { marginHorizontal: 15, @@ -260,6 +305,10 @@ const styles = StyleSheet.create({ borderRadius: 8, backgroundColor: '#fff', }, + headerText: { + fontSize: 15, + color: colors.black, + }, modalOverlay: { flex: 1, justifyContent: 'center', @@ -334,7 +383,7 @@ const styles = StyleSheet.create({ statusText: { fontSize: 14, fontWeight: '600', - color: colors.Home, + color: colors.black, }, memberCard: { flexDirection: "row", @@ -372,4 +421,20 @@ const styles = StyleSheet.create({ scrollContent: { paddingBottom: 50, }, + floatingWinnerContainer: { + position: 'absolute', + bottom: 80, + alignSelf: 'center', + backgroundColor: colors.pastelBlue, + paddingHorizontal: 15, + paddingVertical: 8, + borderRadius: 20, + elevation: 5, + zIndex: 10, + }, + floatingWinnerText: { + color: colors.black, + fontWeight: 'bold', + fontSize: 16, + }, }); diff --git a/src/screens/management/EditGroupScreen.tsx b/src/screens/management/EditGroupScreen.tsx index 3dbf815..032a697 100644 --- a/src/screens/management/EditGroupScreen.tsx +++ b/src/screens/management/EditGroupScreen.tsx @@ -216,6 +216,7 @@ const EditGroupScreen: React.FC = ({ route }) => { try { const res = await apiCall(`${BASE_URL}fund/groups/${groupDetails.id}/edit`, 'PUT', editableData); console.log("Edit Group Response: ", res,editableData) + Alert.alert('Success', 'Group has been updated.'); setEditingCard(null); await fetchGroupDetails(); diff --git a/src/screens/management/Payments.tsx b/src/screens/management/Payments.tsx index a241901..e94a45a 100644 --- a/src/screens/management/Payments.tsx +++ b/src/screens/management/Payments.tsx @@ -63,7 +63,7 @@ const Payments: React.FC = () => { // These are regular functions and do not call hooks inside them, which is correct. const getCycleData = async () => { try { - const response = await apiCall(`${API_BASE_URL}/groups/${groupId}/cycle-dashboard`, 'GET') as ApiResponse; + const response = await apiCall(`${BASE_URL}/payments/groups/${groupId}/cycle-dashboard`, 'GET') as ApiResponse; if (response && response.statusCode === 200) { setCycleData(response.data); } @@ -131,7 +131,7 @@ const Payments: React.FC = () => { } try { - const url = `${BASE_URL}groups/${groupId}/cycles/${paymentDetails.currentCycle}/waive/${currentParticipant.userId}?amountToWaive=${amountToWaive}`; + const url = `https://api.godemo.in/FundCollect/api/payments/groups/${groupId}/cycles/${paymentDetails.currentCycle}/waive/${currentParticipant.userId}?amountToWaive=${amountToWaive}`; const response = await apiCall(url, 'PUT', {}) as any; if (response && response.statusCode === 200) { @@ -247,8 +247,8 @@ const Payments: React.FC = () => { ₹{paymentDetails.contributionAmount} - Total Due - ₹{paymentDetails.totalDueWithLateFee} + Total Amount + ₹{paymentDetails.totalDueWithLateFee} Participants diff --git a/src/screens/management/WinnerUser.tsx b/src/screens/management/WinnerUser.tsx index d69e7d2..3601d2b 100644 --- a/src/screens/management/WinnerUser.tsx +++ b/src/screens/management/WinnerUser.tsx @@ -4,6 +4,7 @@ import { Ionicons } from '@react-native-vector-icons/ionicons' import CustomModal from '../../components/common/CustomModal' import { apiCall } from '../../utils/ApiHelper' import { BASE_URL } from '../../utils/ApiUrls' +import colors from '../../components/colors' interface WinnerUserProps { data: { @@ -111,6 +112,7 @@ const WinnerUser: React.FC = ({ data, visible, onClose, groupId onClose={onClose} confirmText={canProceedToWithdrawal ? 'Proceed to Withdraw' : 'Close'} onConfirm={canProceedToWithdrawal ? onConfirm : onClose} + confirmButton={colors.pastelOrange} > diff --git a/src/screens/notification/Notification.tsx b/src/screens/notification/Notification.tsx index 6adcaff..a9aaa4b 100644 --- a/src/screens/notification/Notification.tsx +++ b/src/screens/notification/Notification.tsx @@ -44,22 +44,20 @@ const Notification = () => { dispatch(clearAllNotifications()); }; - const handleMarkAsRead = (messageId: string, screen: string) => { + const handleMarkAsRead = (messageId: string, screen: string, groupId: string, cycleNumber: string, contributionId: string) => { console.log("screen type : ", screen); handleMarkAsReadAndNavigate( messageId, screen, { - // Pass additional parameters based on notification type - groupId: 'ebc27862-acde-4129-b12c-fd4c2966e858', - cycleNumber: '1', - contributionId: '12345', - actionTab3: 'Your Requests' , + groupId: groupId, + cycleNumber, + contributionId, + actionTab3: 'Your Requests', actionTab1: 'Joined Groups', GroupDetails: { item: {} }, - // etc. } ); }; @@ -240,74 +238,77 @@ const Notification = () => { keyExtractor={(item) => item.messageId} showsVerticalScrollIndicator={false} contentContainerStyle={styles.listContainer} - renderItem={({ item }) => ( - handleMarkAsRead(item.messageId, item?.data?.msgType)} - activeOpacity={0.7} - > - - - {/* Icon */} - - - - - {/* Content */} - - - - {item.data.title} - - - {formatTime(item.sentTime)} - + renderItem={({ item }) => { + console.log("noti items : ", item) + return ( + handleMarkAsRead(item.messageId, item?.data?.msgType, item?.data?.groupId, item?.data?.cycleNumber, item?.data?.contributionId)} + activeOpacity={0.7} + > + + + {/* Icon */} + + - - {item.data.body} - - - {item.data.msgType && ( - + {/* Content */} + + - {/* {item.data.msgType} */} - {getFilterDisplayName(item.data.msgType as FilterType)} + {item.data.title} + + + {formatTime(item.sentTime)} - )} - - {/* Actions */} - - {!item.read && ( - - )} - handleDelete(item.messageId)} - style={styles.deleteButton} - > - - + + {item.data.body} + + + {item.data.msgType && ( + + + {/* {item.data.msgType} */} + {getFilterDisplayName(item.data.msgType as FilterType)} + + + )} + + + {/* Actions */} + + {!item.read && ( + + )} + handleDelete(item.messageId)} + style={styles.deleteButton} + > + + + - - - - )} + + + ) + }} ListEmptyComponent={ diff --git a/src/screens/profile/Profile.tsx b/src/screens/profile/Profile.tsx index eed046b..2cf62e0 100644 --- a/src/screens/profile/Profile.tsx +++ b/src/screens/profile/Profile.tsx @@ -161,13 +161,13 @@ const Profile = () => { // Alert.alert("Success", "Profile updated successfully"); // setEditing(false); // fetchUserProfile(); - // if (result.status === "success") { - // Alert.alert("Success", "Profile updated successfully"); - // setEditing(false); - // fetchUserProfile(); // reload updated data - // } else { - // Alert.alert("Error", "Failed to update profile"); - // } + if (result.status === "success") { + Alert.alert("Success", "Profile updated successfully"); + setEditing(false); + fetchUserProfile(); // reload updated data + } else { + Alert.alert("Error", "Failed to update profile"); + } } catch (error) { console.error("❌ Error updating profile:", error); // Alert.alert("Error", "Could not update profile"); @@ -309,7 +309,7 @@ const Profile = () => { return; } - await deleteFcmToken(); + await deleteFcmToken(user?.userId?.toString()); // Clear stored credentials await Keychain.resetGenericPassword(); @@ -463,11 +463,6 @@ const Profile = () => { Personal Information Become Organizer - {/* - - - - */} navigation.navigate('Transaction')}> diff --git a/src/utils/firebase/firebaseServices.ts b/src/utils/firebase/firebaseServices.ts index eefe30d..8f4422a 100644 --- a/src/utils/firebase/firebaseServices.ts +++ b/src/utils/firebase/firebaseServices.ts @@ -176,10 +176,7 @@ export const getFcmToken = async (userId: string) => { console.log('FCM Token:', token); // Send token to backend with user ID - await apiCall(STORE_FCM_TOKEN, 'POST', { - token, - userId - }); + await apiCall(`${STORE_FCM_TOKEN}${token}`, 'POST'); console.log('FCM Token sent to backend', token); return token; @@ -239,6 +236,7 @@ const processAndAddNotification = (remoteMessage: any) => { title: title, additionalProp1: data?.additionalProp1 || '', additionalProp2: data?.additionalProp2 || '', + groupId: data?.groupId || '', ...data // Spread any additional data }, from: from || 'unknown',