This commit is contained in:
Abin Toppo
2025-09-18 17:23:54 +05:30
10 changed files with 134 additions and 92 deletions
+3 -2
View File
@@ -48,7 +48,7 @@ export default function GroupCreationForm() {
minParticipants: "", minParticipants: "",
startDate: "", startDate: "",
visibility: "PRIVATE", visibility: "PRIVATE",
fundDistribution: "SEQUENTIAL", withdrawalMethod: "SEQUENTIAL",
}); });
const [errors, setErrors] = useState<any>({}); // State for validation errors const [errors, setErrors] = useState<any>({}); // State for validation errors
@@ -96,6 +96,7 @@ export default function GroupCreationForm() {
const handleReview = () => { const handleReview = () => {
if (validateForm()) { if (validateForm()) {
console.log("------",form)
navigation.navigate("GroupReview", { form }); navigation.navigate("GroupReview", { form });
} else { } else {
Alert.alert("Incomplete Form", "Please fill out all required fields correctly."); Alert.alert("Incomplete Form", "Please fill out all required fields correctly.");
@@ -121,7 +122,7 @@ export default function GroupCreationForm() {
<Text style={styles.label}>Fund Distribution</Text> <Text style={styles.label}>Fund Distribution</Text>
<View style={styles.toggleContainer}> <View style={styles.toggleContainer}>
{distributionTypes.map((type) => (<TouchableOpacity key={type} onPress={() => handleChange("fundDistribution", type)} style={[styles.toggleButton, form.fundDistribution === type && styles.selectedButton]}><Text style={[styles.toggleButtonText, form.fundDistribution === type && styles.selectedButtonText]}>{type.charAt(0) + type.slice(1).toLowerCase()}</Text></TouchableOpacity>))} {distributionTypes.map((type) => (<TouchableOpacity key={type} onPress={() => handleChange("withdrawalMethod", type)} style={[styles.toggleButton, form.withdrawalMethod === type && styles.selectedButton]}><Text style={[styles.toggleButtonText, form.withdrawalMethod === type && styles.selectedButtonText]}>{type.charAt(0) + type.slice(1).toLowerCase()}</Text></TouchableOpacity>))}
</View> </View>
<View style={styles.participantsRow}> <View style={styles.participantsRow}>
+2 -1
View File
@@ -82,6 +82,7 @@ export default function GroupReview({ route, navigation }: any) {
maxParticipants: Number(form.maxParticipants) || 0, maxParticipants: Number(form.maxParticipants) || 0,
minParticipants: Number(form.minParticipants) || 0, minParticipants: Number(form.minParticipants) || 0,
}; };
console.log("payload:", payload);
const res = await apiCall(GROUP_CREATION_URL, "POST", payload) as { statusCode?: number }; const res = await apiCall(GROUP_CREATION_URL, "POST", payload) as { statusCode?: number };
if (res?.statusCode === 201) { if (res?.statusCode === 201) {
@@ -111,7 +112,7 @@ export default function GroupReview({ route, navigation }: any) {
{/* --- Timeline Sections --- */} {/* --- Timeline Sections --- */}
<TimelineSection icon="information-circle-outline" title="Basic Information">{renderRow("Name", form.groupName)}{form.description ? ( <View style={styles.descriptionContainer}> <Text style={styles.label}>Description</Text> <Text style={styles.descriptionText}>{form.description}</Text> </View> ) : null}</TimelineSection> <TimelineSection icon="information-circle-outline" title="Basic Information">{renderRow("Name", form.groupName)}{form.description ? ( <View style={styles.descriptionContainer}> <Text style={styles.label}>Description</Text> <Text style={styles.descriptionText}>{form.description}</Text> </View> ) : null}</TimelineSection>
<TimelineSection icon="shield-checkmark-outline" title="Rules & Privacy">{renderRow("Visibility", form.visibility)}{renderRow("Distribution", form.fundDistribution)}{renderRow("Min Members", form.minParticipants)}{renderRow("Max Members", form.maxParticipants)}</TimelineSection> <TimelineSection icon="shield-checkmark-outline" title="Rules & Privacy">{renderRow("Visibility", form.visibility)}{renderRow("Distribution", form.withdrawalMethod)}{renderRow("Min Members", form.minParticipants)}{renderRow("Max Members", form.maxParticipants)}</TimelineSection>
<TimelineSection icon="wallet-outline" title="Financial Details">{renderRow("Contribution", `${form.contributionAmount} INR`)}{renderRow("Cycle", form.cycleType)}{form.cycleType === "CUSTOM" && renderRow("Cycle Days", `${form.customCycleDays} days`)}{renderRow("Start Date", form.startDate)}</TimelineSection> <TimelineSection icon="wallet-outline" title="Financial Details">{renderRow("Contribution", `${form.contributionAmount} INR`)}{renderRow("Cycle", form.cycleType)}{form.cycleType === "CUSTOM" && renderRow("Cycle Days", `${form.customCycleDays} days`)}{renderRow("Start Date", form.startDate)}</TimelineSection>
<TouchableOpacity onPress={handleSubmit} style={styles.submitBtn} disabled={isLoading}> <TouchableOpacity onPress={handleSubmit} style={styles.submitBtn} disabled={isLoading}>
+1 -1
View File
@@ -21,7 +21,7 @@ const SplashScreen = () => {
try { try {
const credentials = await Keychain.getGenericPassword(); const credentials = await Keychain.getGenericPassword();
if (!credentials) { if (!credentials) {
// Alert.alert("Error", "You are not logged in");
return; return;
} }
const token = credentials.password; const token = credentials.password;
+9 -7
View File
@@ -11,6 +11,7 @@ import {
import * as Keychain from "react-native-keychain"; import * as Keychain from "react-native-keychain";
import { useFocusEffect, useNavigation } from "@react-navigation/native"; import { useFocusEffect, useNavigation } from "@react-navigation/native";
import { BASE_URL } from "../../utils/ApiUrls"; import { BASE_URL } from "../../utils/ApiUrls";
import colors from "../../components/colors";
export type ApiResponse = { export type ApiResponse = {
message: string; message: string;
@@ -136,13 +137,14 @@ const UserProfileStatus: React.FC = () => {
<TouchableOpacity <TouchableOpacity
style={[styles.button, { backgroundColor: "#ccc", marginTop: 8 }]} style={[styles.button, { backgroundColor: "#ccc", marginTop: 8 }]}
onPress={() => { if (intervalId) { onPress={() => {
clearInterval(intervalId); if (intervalId) {
setIntervalId(null); clearInterval(intervalId);
} setIntervalId(null);
}
setShowModal(false); setShowModal(false);
}} }}
> >
<Text style={[styles.buttonText, { color: "#333" }]}>Close</Text> <Text style={[styles.buttonText, { color: "#333" }]}>Close</Text>
</TouchableOpacity> </TouchableOpacity>
@@ -221,7 +223,7 @@ const styles = StyleSheet.create({
fontSize: 20, fontSize: 20,
fontWeight: "700", fontWeight: "700",
marginBottom: 10, marginBottom: 10,
color: "#FF9500", color: colors.primary,
}, },
modalText: { modalText: {
fontSize: 14, fontSize: 14,
@@ -116,6 +116,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route, navigation })
} }
setLoading(true); setLoading(true);
try { try {
console.log(`Fetching details for group ID: ${groupId}`);
const response = await apiCall( const response = await apiCall(
`https://api.godemo.in/FundCollect/api/fund/group/details?groupId=${groupId}`, `https://api.godemo.in/FundCollect/api/fund/group/details?groupId=${groupId}`,
'GET', 'GET',
+2 -2
View File
@@ -14,7 +14,7 @@ import {
import { useNavigation, useFocusEffect } from "@react-navigation/native"; import { useNavigation, useFocusEffect } from "@react-navigation/native";
import { StackNavigationProp } from "@react-navigation/stack"; import { StackNavigationProp } from "@react-navigation/stack";
import { RootStackParamList } from "../../navigation/AppNavigator"; import { RootStackParamList } from "../../navigation/AppNavigator";
import { APPROVE_REQUEST_URL, BASE_URL, DECLINE_REQUEST_URL, MY_GROUPS_URL, REQUESTS_URL } from "../../utils/ApiUrls"; import { APPROVE_REQUEST_URL, BASE_URL, DECLINE_REQUEST_URL, JOIN_GROUP_URL, MY_GROUPS_URL, REQUESTS_URL } from "../../utils/ApiUrls";
import colors from "../../components/colors"; import colors from "../../components/colors";
import AppView from "../../components/AppView"; import AppView from "../../components/AppView";
import { apiCall } from "../../utils/ApiHelper"; import { apiCall } from "../../utils/ApiHelper";
@@ -46,7 +46,7 @@ const ManagementScreen: FC = () => {
// Fetch all data concurrently // 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<ApiResponse<MyGroup[]>>, apiCall(MY_GROUPS_URL, "GET") as Promise<ApiResponse<MyGroup[]>>,
apiCall(DECLINE_REQUEST_URL, "GET") as Promise<ApiResponse<JoinedGroup[]>>, // Assuming this is also needed apiCall(JOIN_GROUP_URL, "GET") as Promise<ApiResponse<JoinedGroup[]>>, // Assuming this is also needed
apiCall(REQUESTS_URL, "GET") as Promise<ApiResponse<JoinRequest[]>>, apiCall(REQUESTS_URL, "GET") as Promise<ApiResponse<JoinRequest[]>>,
]); ]);
+28 -2
View File
@@ -9,12 +9,17 @@ import {
KeyboardAvoidingView, KeyboardAvoidingView,
Platform, Platform,
SafeAreaView, SafeAreaView,
TouchableOpacity,
} from "react-native"; } from "react-native";
import * as Keychain from "react-native-keychain"; import * as Keychain from "react-native-keychain";
import { CustomInput, CustomButton } from "../../components/common/Custom"; import { CustomInput, CustomButton } from "../../components/common/Custom";
import colors from "../../components/colors"; import colors from "../../components/colors";
import { useNavigation } from "@react-navigation/native";
import AppView from "../../components/AppView";
import Ionicons from "@react-native-vector-icons/ionicons";
const BecomeOrg = () => { const BecomeOrg = () => {
const navigation = useNavigation<any>();
const [bankAccountNumber, setBankAccountNumber] = useState(""); const [bankAccountNumber, setBankAccountNumber] = useState("");
const [bankName, setBankName] = useState(""); const [bankName, setBankName] = useState("");
const [branchCode, setBranchCode] = useState(""); const [branchCode, setBranchCode] = useState("");
@@ -58,6 +63,7 @@ const BecomeOrg = () => {
setBankAccountNumber(""); setBankAccountNumber("");
setBankName(""); setBankName("");
setBranchCode(""); setBranchCode("");
navigation.navigate("Home" as never);
} else { } else {
Alert.alert("Error", result.message || "Failed to apply"); Alert.alert("Error", result.message || "Failed to apply");
} }
@@ -70,7 +76,14 @@ const BecomeOrg = () => {
}; };
return ( return (
<SafeAreaView style={{ flex: 1, backgroundColor: colors.pastelPink }}> <AppView barStyle="dark-content">
<View style={styles.header}>
<TouchableOpacity onPress={() => navigation.replace("Home" as never)} >
<Ionicons name="arrow-back" size={24} color={'#000'} />
</TouchableOpacity>
<Text style={styles.headerTitle}>User Profile</Text>
</View>
<KeyboardAvoidingView <KeyboardAvoidingView
style={{ flex: 1 }} style={{ flex: 1 }}
behavior={Platform.OS === "ios" ? "padding" : undefined} behavior={Platform.OS === "ios" ? "padding" : undefined}
@@ -121,7 +134,7 @@ const BecomeOrg = () => {
)} )}
</ScrollView> </ScrollView>
</KeyboardAvoidingView> </KeyboardAvoidingView>
</SafeAreaView> </AppView>
); );
}; };
@@ -140,4 +153,17 @@ const styles = StyleSheet.create({
textAlign: "center", textAlign: "center",
color: colors.textDark, color: colors.textDark,
}, },
header: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
padding: 16,
borderBottomLeftRadius: 20,
borderBottomRightRadius: 20,
},
headerTitle: {
fontSize: 22,
fontWeight: "700",
color: "#000"
},
}); });
+79 -73
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback} from "react"; import React, { useState, useEffect, useCallback } from "react";
import { import {
StyleSheet, StyleSheet,
View, View,
@@ -243,35 +243,35 @@ const Profile = () => {
} }
}; };
const logout = async (navigation: any) => { const logout = async (navigation: any) => {
try { try {
const credentials = await Keychain.getGenericPassword(); const credentials = await Keychain.getGenericPassword();
if (!credentials) { if (!credentials) {
Alert.alert("Info", "No user is currently logged in"); Alert.alert("Info", "No user is currently logged in");
return; return;
}
// Clear stored credentials
await Keychain.resetGenericPassword();
Alert.alert("Success", "You have been logged out");
navigation.replace("Login");
} catch (error) {
console.error("Logout error:", error);
Alert.alert("Error", "Failed to log out. Please try again.");
} }
};
// Clear stored credentials const confirmLogout = (navigation: any) => {
await Keychain.resetGenericPassword(); Alert.alert(
Alert.alert("Success", "You have been logged out"); "Logout",
navigation.replace("Login"); "Are you sure you want to log out?",
} catch (error) { [
console.error("Logout error:", error); { text: "Cancel", style: "cancel" },
Alert.alert("Error", "Failed to log out. Please try again."); { text: "Yes, Logout", style: "destructive", onPress: () => logout(navigation) },
} ]
}; );
};
const confirmLogout = (navigation: any) => {
Alert.alert(
"Logout",
"Are you sure you want to log out?",
[
{ text: "Cancel", style: "cancel" },
{ text: "Yes, Logout", style: "destructive", onPress: () => logout(navigation) },
]
);
};
useFocusEffect( useFocusEffect(
@@ -281,7 +281,7 @@ const Profile = () => {
); );
useEffect(() => { useEffect(() => {
userStatus(); userStatus();
fetchUserProfile(); fetchUserProfile();
}, []); }, []);
@@ -316,7 +316,7 @@ const Profile = () => {
</TouchableOpacity> </TouchableOpacity>
</View> </View>
<KeyboardAvoidingView <KeyboardAvoidingView
style={{ flex: 1 }} // style={{ flex: 1 }}
behavior={Platform.OS === "ios" ? "padding" : undefined} behavior={Platform.OS === "ios" ? "padding" : undefined}
> >
<ScrollView <ScrollView
@@ -327,11 +327,11 @@ const Profile = () => {
{/* Profile Card */} {/* Profile Card */}
<View style={styles.profileCard}> <View style={styles.profileCard}>
{/* Profile Image Section */} {/* Profile Image Section */}
{/* { { user?.role === "ORGANIZER" && user?.organizerStatus === "NOT_APPLIED" || "PENDING" ? (
statusData?.role && statusData?.organizerStatus ? <Text style={{ color: "#64748b", marginBottom: 16, textAlign: 'center', fontSize: 18 }}>Your Role: {user.role} | Organizer Status: {user.organizerStatus}</Text>
<Text style={{ color: "#64748b", marginBottom: 16, textAlign: 'center', fontSize: 18 }}>Your Role: {statusData.role} | Organizer Status: {statusData.organizerStatus}</Text> ) : (
: <SwitchNavigator role={user?.role || ''} status={user?.organizerStatus === "NOT_APPLIED" ? '' : user?.organizerStatus || ''} />
<SwitchNavigator role={statusData?.role || ''} status={statusData?.organizerStatus || ''} />} */} )}
<View style={styles.profileImageSection}> <View style={styles.profileImageSection}>
<View style={styles.avatarContainer}> <View style={styles.avatarContainer}>
<Image <Image
@@ -358,12 +358,25 @@ const Profile = () => {
<> <>
{/* Profile Details */} {/* Profile Details */}
<View style={styles.detailsSection}> <View style={styles.detailsSection}>
<Text style={styles.sectionTitle}>Personal Information</Text> <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<View style={{ marginBottom: 16 }}> <View>
<Text style={styles.sectionTitle}>Personal Information</Text>
<Text style={{ color: "#64748b", marginBottom: 16 }}>Become Organizer</Text>
{/* <View style={{ marginBottom: 16 }}>
<Text style={{ color: "#64748b", marginBottom: 16 }}>Become Organizer</Text>
</View> */}
</View>
<View>
<CustomButton
title="Edit Profile"
onPress={() => setEditing(true)}
icon="create"
style={styles.editButton}
buttonBackground={colors.pastelBlue}
/>
</View>
</View> </View>
<View style={styles.detailRow}> <View style={styles.detailRow}>
<Ionicons name="person" size={20} color={colors.pastelBlue} style={styles.detailIcon} /> <Ionicons name="person" size={20} color={colors.pastelBlue} style={styles.detailIcon} />
<View> <View>
@@ -419,15 +432,11 @@ const Profile = () => {
<Text style={styles.detailText}>{user.role || "User"}</Text> <Text style={styles.detailText}>{user.role || "User"}</Text>
</View> </View>
</View> </View>
</View>
<CustomButton
title="Edit Profile"
onPress={() => setEditing(true)}
icon="create" </View>
style={styles.editButton}
buttonBackground={colors.pastelBlue}
/>
</> </>
) : ( ) : (
<> <>
@@ -482,22 +491,24 @@ const Profile = () => {
onChangeText={(text) => setUser({ ...user, gender: text })} onChangeText={(text) => setUser({ ...user, gender: text })}
icon="male-female" icon="male-female"
/> />
</View>
<View style={styles.editButtons}> <View style={styles.editButtons}>
<CustomButton <CustomButton
title="Save Changes" title="Save Changes"
onPress={updateProfile} onPress={updateProfile}
icon="save" icon="save"
style={styles.saveButton} style={styles.saveButton}
buttonBackground="#10b981" buttonBackground={colors.pastelBlue}
/> />
<CustomButton <CustomButton
title="Cancel" title="Cancel"
onPress={() => setEditing(false)} onPress={() => setEditing(false)}
icon="close" icon="close"
style={styles.cancelButton} style={styles.cancelButton}
buttonBackground="#f38080ff" buttonBackground=""
/> />
</View>
</View> </View>
</> </>
)} )}
@@ -530,15 +541,8 @@ const styles = StyleSheet.create({
justifyContent: "space-between", justifyContent: "space-between",
alignItems: "center", alignItems: "center",
padding: 16, padding: 16,
// paddingTop: 50,
// backgroundColor: "#6366f1",
borderBottomLeftRadius: 20, borderBottomLeftRadius: 20,
borderBottomRightRadius: 20, borderBottomRightRadius: 20,
// elevation: 4,
// shadowColor: "#000",
// shadowOffset: { width: 0, height: 2 },
// shadowOpacity: 0.2,
// shadowRadius: 4,
}, },
headerTitle: { headerTitle: {
fontSize: 22, fontSize: 22,
@@ -598,13 +602,13 @@ const styles = StyleSheet.create({
borderRadius: 25, borderRadius: 25,
}, },
detailsSection: { detailsSection: {
marginBottom: 24, marginBottom: "40%",
}, },
sectionTitle: { sectionTitle: {
fontSize: 18, fontSize: 18,
fontWeight: "700", fontWeight: "700",
color: "#1e293b", color: "#1e293b",
marginBottom: 16, // marginBottom: 16,
paddingBottom: 8, paddingBottom: 8,
borderBottomWidth: 1, borderBottomWidth: 1,
borderBottomColor: "#f1f5f9", borderBottomColor: "#f1f5f9",
@@ -629,10 +633,10 @@ const styles = StyleSheet.create({
fontWeight: "500", fontWeight: "500",
}, },
editButton: { editButton: {
marginTop: 8, paddingHorizontal: 8,
}, },
editSection: { editSection: {
marginBottom: 20, marginBottom: '40%',
flex: 1, flex: 1,
}, },
editButtons: { editButtons: {
@@ -643,6 +647,8 @@ const styles = StyleSheet.create({
}, },
cancelButton: { cancelButton: {
marginTop: 4, marginTop: 4,
borderWidth: 1,
borderColor: "#cbd5e1",
}, },
inputContainer: { inputContainer: {
marginBottom: 16, marginBottom: 16,
+7 -2
View File
@@ -2,6 +2,7 @@ import React, { useState } from "react";
import { View, Text, StyleSheet, Switch } from "react-native"; import { View, Text, StyleSheet, Switch } from "react-native";
import { useNavigation } from "@react-navigation/native"; import { useNavigation } from "@react-navigation/native";
import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
import colors from "../../components/colors";
type RootStackParamList = { type RootStackParamList = {
BecomeOrg: undefined; BecomeOrg: undefined;
@@ -24,6 +25,10 @@ const SwitchNavigator: React.FC<SwitchNavigatorProps> = ({ role, status }) => {
if (newValue) { if (newValue) {
navigation.navigate("BecomeOrg"); // Navigate when ON navigation.navigate("BecomeOrg"); // Navigate when ON
setTimeout(() => {
setIsEnabled(false);
}, 2000);
// Reset switch to OFF after navigation
} else { } else {
navigation.navigate("Profile"); // Navigate when OFF navigation.navigate("Profile"); // Navigate when OFF
} }
@@ -40,8 +45,8 @@ const SwitchNavigator: React.FC<SwitchNavigatorProps> = ({ role, status }) => {
: `Request Organizer Role ${status}`} : `Request Organizer Role ${status}`}
</Text> </Text>
<Switch <Switch
trackColor={{ false: "#767577", true: "#81b0ff" }} trackColor={{ false: "#767577", true: colors.textLight }}
thumbColor={isEnabled ? "#007AFF" : "#f4f3f4"} thumbColor={isEnabled ? colors.pastelBlue : "#f4f3f4"}
ios_backgroundColor="#3e3e3e" ios_backgroundColor="#3e3e3e"
onValueChange={toggleSwitch} onValueChange={toggleSwitch}
value={isEnabled} value={isEnabled}
+2 -2
View File
@@ -5,8 +5,8 @@ export const ORGRAGISTRATION = `${BASE_URL}organizer/registration/apply`
export const GROUP_CREATION_URL = `${BASE_URL}fund/groups/{organizerId}` export const GROUP_CREATION_URL = `${BASE_URL}fund/groups/{organizerId}`
export const PROFILE_URL =`${BASE_URL}user/profile/view` export const PROFILE_URL =`${BASE_URL}user/profile/view`
export const PUBLIC_GROUPS_URL =`${BASE_URL}fund/user/public` export const PUBLIC_GROUPS_URL =`${BASE_URL}fund/user/public`
export const JOIN_GROUP_URL = `${BASE_URL}fund`; export const JOIN_GROUP_URL = `${BASE_URL}fund/my/join/groups`;
export const MY_GROUPS_URL =`https://api.godemo.in/FundCollect/api/fund/my/join/groups` 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` 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 //https://api.godemo.in/FundCollect/api/fund/groups/2a80cdaa-8532-4b8a-87f0-9c1023d18a7f/join/64