diff --git a/src/components/common/Custom.tsx b/src/components/common/Custom.tsx index b1d17f1..ae3f3ec 100644 --- a/src/components/common/Custom.tsx +++ b/src/components/common/Custom.tsx @@ -63,7 +63,7 @@ const CustomButton: React.FC = ({ ? colors.pastelOrange : colors.primary); - const textColor = type === "secondary" ? colors.textDark : colors.white; + const textColor = type === "secondary" ? colors.textDark : '#000'; return ( { - let data ; + let data: Group[]; switch (activeTab) { case "My Groups": data = myGroups; break; case "Joined": data = joinedGroups; break; @@ -168,7 +168,11 @@ export default function GroupScreen() { renderItem={({ item }) => ( { }} + onJoin={() => { + if (activeTab === 'Public') { + handleJoinGroup + } + }} onView={handleViewGroup} isGrid={isGridView} /> diff --git a/src/screens/home/GroupList.tsx b/src/screens/home/GroupList.tsx new file mode 100644 index 0000000..7c13a42 --- /dev/null +++ b/src/screens/home/GroupList.tsx @@ -0,0 +1,206 @@ +import React, { useState, useEffect } from "react"; +import { + View, + Text, + TextInput, + StyleSheet, + FlatList, + ActivityIndicator, + TouchableOpacity, + LayoutAnimation, + UIManager, + Platform, +} from "react-native"; +import Ionicons from "react-native-vector-icons/Ionicons"; +import colors from "../../components/colors"; + +// ✅ Enable LayoutAnimation for Android +if (Platform.OS === "android" && UIManager.setLayoutAnimationEnabledExperimental) { + UIManager.setLayoutAnimationEnabledExperimental(true); +} + +const API_URL = "https://api.godemo.in/FundCollect/api/fund/getAll/my/groups"; +const TOKEN = + "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI2MjY1MjI5MzcxIiwidXNlcklkIjoiMTAwMDAxIiwiaWF0IjoxNzU4MDAyOTkxLCJleHAiOjE3NTgwODkzOTF9.mMRPsULcvdfdCrJJBDjnT2okE3dJsTn42JBqi_vP0Xk"; + +// ✅ Each group card with expandable details +const GroupCard = ({ item }: { item: any }) => { + const [expanded, setExpanded] = useState(false); + + const toggleExpand = () => { + LayoutAnimation.easeInEaseOut(); + setExpanded(!expanded); + }; + + const initial = item.groupName ? item.groupName.charAt(0).toUpperCase() : "?"; + + return ( + + + + {initial} + + + {item.groupName} + {item.description} + + {item.visibility} • {item.cycleType} + + + + + + + + {expanded && ( + + {Object.entries(item).map(([key, value]) => ( + + {key}:{" "} + {Array.isArray(value) ? value.join(", ") : String(value)} + + ))} + + )} + + ); +}; + +// ✅ Group list with search +const GroupList = () => { + const [groups, setGroups] = useState([]); + const [filteredGroups, setFilteredGroups] = useState([]); + const [loading, setLoading] = useState(true); + const [search, setSearch] = useState(""); + + useEffect(() => { + const fetchGroups = async () => { + try { + const res = await fetch(API_URL, { + headers: { Accept: "*/*", Authorization: TOKEN }, + }); + const data = await res.json(); + setGroups(data.data || []); + setFilteredGroups(data.data || []); + } catch (e) { + console.error("API error", e); + } finally { + setLoading(false); + } + }; + fetchGroups(); + }, []); + + useEffect(() => { + if (search.trim() === "") { + setFilteredGroups(groups); + } else { + setFilteredGroups( + groups.filter((g) => + g.groupName.toLowerCase().includes(search.toLowerCase()) + ) + ); + } + }, [search, groups]); + + if (loading) { + return ( + + ); + } + + return ( + + {/* Search bar */} + + + + + + {/* Group list */} + item.id} + renderItem={({ item }) => } + ListEmptyComponent={ + No groups found + } + contentContainerStyle={{ paddingBottom: 20 }} + /> + + ); +}; + +export default GroupList; + +const styles = StyleSheet.create({ + searchContainer: { + flexDirection: "row", + alignItems: "center", + backgroundColor: colors.white, + borderRadius: 12, + paddingHorizontal: 12, + margin: 10, + height: 45, + shadowColor: "#000", + shadowOpacity: 0.05, + shadowRadius: 5, + elevation: 2, + }, + searchInput: { flex: 1, marginLeft: 8, color: colors.textDark }, + + groupCard: { + backgroundColor: colors.white, + padding: 14, + borderRadius: 14, + marginHorizontal: 10, + marginBottom: 12, + shadowColor: "#000", + shadowOpacity: 0.05, + shadowRadius: 4, + elevation: 2, + }, + groupHeader: { flexDirection: "row", alignItems: "center" }, + avatar: { + width: 50, + height: 50, + borderRadius: 25, + backgroundColor: colors.primary + "33", + justifyContent: "center", + alignItems: "center", + marginRight: 12, + }, + avatarText: { fontSize: 18, fontWeight: "700", color: colors.primary }, + groupName: { fontSize: 16, fontWeight: "600", color: colors.textDark }, + groupDescription: { fontSize: 13, color: colors.textLight, marginTop: 2 }, + groupMeta: { fontSize: 12, color: colors.primary, marginTop: 2 }, + + groupDetails: { + marginTop: 10, + padding: 10, + borderRadius: 8, + backgroundColor: "#f9f9f9", + }, + detailText: { fontSize: 12, color: colors.textDark, marginBottom: 4 }, + + emptyText: { + textAlign: "center", + color: colors.textLight, + marginTop: 20, + fontSize: 14, + }, +}); diff --git a/src/screens/home/GroupsTabs.tsx b/src/screens/home/GroupsTabs.tsx new file mode 100644 index 0000000..aecc24d --- /dev/null +++ b/src/screens/home/GroupsTabs.tsx @@ -0,0 +1,261 @@ +import React, { useState, useEffect } from "react"; +import { + View, + Text, + StyleSheet, + TouchableOpacity, + TextInput, + FlatList, + ActivityIndicator, + LayoutAnimation, + UIManager, + Platform, +} from "react-native"; +import { Ionicons } from "@react-native-vector-icons/ionicons"; +import colors from "../../components/colors"; +import AppView from "../../components/AppView"; + +// Enable LayoutAnimation for Android +if (Platform.OS === "android" && UIManager.setLayoutAnimationEnabledExperimental) { + UIManager.setLayoutAnimationEnabledExperimental(true); +} + +const API_URL = "https://api.godemo.in/FundCollect/api/fund/getAll/my/groups"; +const TOKEN = + "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI2MjY1MjI5MzcxIiwidXNlcklkIjoiMTAwMDAxIiwiaWF0IjoxNzU4MDAyOTkxLCJleHAiOjE3NTgwODkzOTF9.mMRPsULcvdfdCrJJBDjnT2okE3dJsTn42JBqi_vP0Xk"; + +// ✅ Group Card +const GroupCard = ({ item }: { item: any }) => { + const [expanded, setExpanded] = useState(false); + + const toggleExpand = () => { + LayoutAnimation.easeInEaseOut(); + setExpanded(!expanded); + }; + + const initial = item.groupName ? item.groupName.charAt(0).toUpperCase() : "?"; + + return ( + + + + {initial} + + + {item.groupName} + {item.description} + + {item.visibility} • {item.cycleType} + + + + + + + + {expanded && ( + + {Object.entries(item).map(([key, value]) => ( + + {key}:{" "} + {Array.isArray(value) ? value.join("-") : String(value)} + + ))} + + )} + + ); +}; + +// ✅ Group List with Search +const GroupList = () => { + const [groups, setGroups] = useState([]); + const [filteredGroups, setFilteredGroups] = useState([]); + const [loading, setLoading] = useState(true); + const [search, setSearch] = useState(""); + + useEffect(() => { + const fetchGroups = async () => { + try { + const res = await fetch(API_URL, { + headers: { Accept: "*/*", Authorization: TOKEN }, + }); + const data = await res.json(); + setGroups(data.data || []); + setFilteredGroups(data.data || []); + } catch (e) { + console.error("API error", e); + } finally { + setLoading(false); + } + }; + fetchGroups(); + }, []); + + useEffect(() => { + if (search.trim() === "") { + setFilteredGroups(groups); + } else { + setFilteredGroups( + groups.filter((g) => + g.groupName.toLowerCase().includes(search.toLowerCase()) + ) + ); + } + }, [search, groups]); + + if (loading) return ; + + return ( + <> + + + + + + item.id} + renderItem={({ item }) => } + ListEmptyComponent={ + No groups found + } + /> + + ); +}; + +// ✅ Tab Layout +const GroupsTabs = () => { + const [activeTab, setActiveTab] = useState("MyGroups"); + + const renderContent = () => { + switch (activeTab) { + case "MyGroups": + return ; + case "Create": + return ( + + Create Group Screen + + ); + case "History": + return ( + + History Screen + + ); + default: + return null; + } + }; + + return ( + + + {/* Tabs */} + + {["MyGroups", "Create", "History"].map((tab) => ( + setActiveTab(tab)} + > + + {tab === "MyGroups" ? "My Groups" : tab} + + + ))} + + + {/* Content */} + {renderContent()} + + + ); +}; + +export default GroupsTabs; + +const styles = StyleSheet.create({ + container: { }, + tabs: { + flexDirection: "row", + justifyContent: "space-around", + // marginVertical: 10, + borderBottomWidth: 1, + borderBottomColor: "#ddd", + }, + tabButton: { paddingVertical: 10, flex: 1, alignItems: "center" }, + tabText: { fontSize: 16, color: colors.textLight }, + activeTabButton: { borderBottomWidth: 2, borderBottomColor: colors.primary }, + activeTabText: { color: colors.primary, fontWeight: "600" }, + + searchContainer: { + flexDirection: "row", + alignItems: "center", + backgroundColor: colors.white, + borderRadius: 12, + paddingHorizontal: 12, + marginVertical: 10, + height: 45, + shadowColor: "#000", + shadowOpacity: 0.05, + shadowRadius: 5, + elevation: 2, + }, + searchInput: { flex: 1, marginLeft: 8, color: colors.textDark }, + + groupCard: { + backgroundColor: colors.white, + padding: 14, + borderRadius: 14, + marginBottom: 12, + shadowColor: "#000", + shadowOpacity: 0.05, + shadowRadius: 4, + elevation: 2, + }, + groupLeft: { flexDirection: "row", alignItems: "center" }, + groupAvatar: { + width: 50, + height: 50, + borderRadius: 25, + backgroundColor: colors.primary + "33", + justifyContent: "center", + alignItems: "center", + marginRight: 12, + }, + avatarText: { fontSize: 18, fontWeight: "700", color: colors.primary }, + groupName: { fontSize: 16, fontWeight: "600", color: colors.textDark }, + groupDescription: { fontSize: 13, color: colors.textLight, marginTop: 2 }, + groupMeta: { fontSize: 12, color: colors.primary, marginTop: 2 }, + detailsBox: { + marginTop: 10, + padding: 10, + borderRadius: 8, + backgroundColor: "#f9f9f9", + }, + detailText: { fontSize: 12, color: colors.textDark, marginBottom: 4 }, + emptyText: { textAlign: "center", color: colors.textLight, marginTop: 20 }, + centered: { flex: 1, justifyContent: "center", alignItems: "center" }, + sectionTitle: { fontSize: 18, fontWeight: "600", color: colors.textDark }, +}); diff --git a/src/screens/home/Home.tsx b/src/screens/home/Home.tsx index ebe88cf..bf6f9a2 100644 --- a/src/screens/home/Home.tsx +++ b/src/screens/home/Home.tsx @@ -1,82 +1,43 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import { StyleSheet, Text, View, - TextInput, TouchableOpacity, - FlatList, - Image, + UIManager, + Platform, } from "react-native"; import AppView from "../../components/AppView"; import UserProfileStatus from "./UserProfileStatus"; import colors from "../../components/colors"; -import {Ionicons} from "@react-native-vector-icons/ionicons"; +import { Ionicons } from "@react-native-vector-icons/ionicons"; +import GroupsTabs from "./GroupsTabs"; + +// Enable animation for Android +if (Platform.OS === "android" && UIManager.setLayoutAnimationEnabledExperimental) { + UIManager.setLayoutAnimationEnabledExperimental(true); +} -const dummyChats = [ - { id: "1", name: "Amit Sharma", lastMessage: "Sent ₹500", avatar: "https://i.pravatar.cc/100?img=1" }, - { id: "2", name: "Sneha Patel", lastMessage: "Requested ₹200", avatar: "https://i.pravatar.cc/100?img=2" }, - { id: "3", name: "Group Savings", lastMessage: "Target reached 🎉", avatar: "https://i.pravatar.cc/100?img=3" }, -]; const Home = () => { return ( - - + {/* Header */} Hello, Pooja 👋 - + {/* Status */} + - {/* Search Bar */} - - - - - - {/* Quick Actions */} - - - - Send - - - - Request - - - - History - - - - {/* Recent Chats / Transactions */} - Recent Activity - item.id} - renderItem={({ item }) => ( - - - - {item.name} - {item.lastMessage} - - - - )} - /> - ); }; @@ -84,87 +45,14 @@ const Home = () => { export default Home; const styles = StyleSheet.create({ - container: { - flex: 1, - // padding: 16, - }, + container: { }, header: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", marginBottom: 12, }, - greeting: { - fontSize: 22, - fontWeight: "600", - color: colors.textDark, - }, - searchContainer: { - flexDirection: "row", - alignItems: "center", - backgroundColor: colors.white, - borderRadius: 12, - paddingHorizontal: 12, - marginVertical: 10, - height: 45, - shadowColor: "#000", - shadowOpacity: 0.05, - shadowRadius: 5, - elevation: 2, - }, - searchInput: { - flex: 1, - marginLeft: 8, - color: colors.textDark, - }, - quickActions: { - flexDirection: "row", - justifyContent: "space-between", - marginVertical: 16, - }, - actionButton: { - alignItems: "center", - flex: 1, - }, - actionText: { - marginTop: 6, - fontSize: 14, - color: colors.textDark, - }, - sectionTitle: { - fontSize: 18, - fontWeight: "600", - marginVertical: 10, - color: colors.textDark, - }, - chatItem: { - flexDirection: "row", - alignItems: "center", - backgroundColor: colors.white, - padding: 12, - borderRadius: 12, - marginBottom: 10, - shadowColor: "#000", - shadowOpacity: 0.05, - shadowRadius: 4, - elevation: 1, - }, - avatar: { - width: 45, - height: 45, - borderRadius: 22, - marginRight: 12, - }, - chatInfo: { - flex: 1, - }, - chatName: { - fontSize: 16, - fontWeight: "500", - color: colors.textDark, - }, - chatMessage: { - fontSize: 14, - color: colors.textLight, - }, -}); + greeting: { fontSize: 22, fontWeight: "600", color: colors.textDark }, + + +}); \ No newline at end of file diff --git a/src/screens/home/UserProfileStatus.tsx b/src/screens/home/UserProfileStatus.tsx index d3f4a4d..c0318fe 100644 --- a/src/screens/home/UserProfileStatus.tsx +++ b/src/screens/home/UserProfileStatus.tsx @@ -153,7 +153,7 @@ export default UserProfileStatus; const styles = StyleSheet.create({ container: { - flex: 1, + // flex: 1, // padding: 16, // backgroundColor: "#f5f7fb", }, diff --git a/src/screens/profile/Profile.tsx b/src/screens/profile/Profile.tsx index 90f5e83..0aad48d 100644 --- a/src/screens/profile/Profile.tsx +++ b/src/screens/profile/Profile.tsx @@ -240,6 +240,40 @@ const Profile = () => { } }; + + const handleLogout = (navigation: any) => { + Alert.alert( + "Logout", + "Are you sure you want to logout?", + [ + { + text: "Cancel", + style: "cancel", + }, + { + text: "Yes, Logout", + style: "destructive", + onPress: async () => { + try { + // Clear saved credentials from Keychain + await Keychain.resetGenericPassword(); + + // Navigate to login screen + navigation.reset({ + index: 0, + routes: [{ name: "Login" }], + }); + } catch (error) { + console.error("Logout error:", error); + Alert.alert("Error", "Failed to logout. Please try again."); + } + }, + }, + ], + { cancelable: true } + ); + }; + useFocusEffect( useCallback(() => { userStatus(); @@ -267,7 +301,7 @@ const Profile = () => { if (fetching || !user) { return ( - + Loading profile... ); @@ -281,7 +315,7 @@ const Profile = () => { User Profile - + { onError={() => console.log("Error loading image")} /> - + @@ -318,7 +352,7 @@ const Profile = () => { onPress={handleUpdateImage} icon={imageUpdating ? undefined : "cloud-upload"} style={styles.updateImageButton} - buttonBackground={colors.Profile} + buttonBackground={colors.pastelBlue} /> @@ -334,7 +368,7 @@ const Profile = () => { - + Name {user.firstName} {user.lastName} @@ -342,7 +376,7 @@ const Profile = () => { - + Email {user.email || "Not provided"} @@ -350,7 +384,7 @@ const Profile = () => { - + Mobile {user.mobile || "Not provided"} @@ -358,7 +392,7 @@ const Profile = () => { - + Username {user.username} @@ -366,7 +400,7 @@ const Profile = () => { - + Address {user.address || "Not provided"} @@ -374,7 +408,7 @@ const Profile = () => { - + Gender {user.gender || "Not specified"} @@ -382,7 +416,7 @@ const Profile = () => { - + Role {user.role || "User"} @@ -395,7 +429,7 @@ const Profile = () => { onPress={() => setEditing(true)} icon="create" style={styles.editButton} - buttonBackground={colors.Profile} + buttonBackground={colors.pastelBlue} /> ) : ( @@ -521,13 +555,13 @@ const styles = StyleSheet.create({ profileCard: { borderRadius: 20, margin: 16, - backgroundColor: "#fff", - padding: 20, - shadowColor: "#000", - shadowOpacity: 0.1, - shadowRadius: 10, - shadowOffset: { width: 0, height: 4 }, - elevation: 5, + // backgroundColor: "#fff", + // padding: 20, + // shadowColor: "#000", + // shadowOpacity: 0.1, + // shadowRadius: 10, + // shadowOffset: { width: 0, height: 4 }, + // elevation: 5, }, profileImageSection: { alignItems: "center", @@ -551,7 +585,7 @@ const styles = StyleSheet.create({ position: "absolute", right: 0, bottom: 0, - backgroundColor: "#6366f1", + backgroundColor: colors.pastelBlue, borderRadius: 20, width: 40, height: 40, @@ -645,19 +679,6 @@ const styles = StyleSheet.create({ inputWithIcon: { paddingLeft: 40, }, - button: { - backgroundColor: "#6366f1", - paddingVertical: 14, - borderRadius: 12, - alignItems: "center", - justifyContent: "center", - flexDirection: "row", - shadowColor: "#6366f1", - shadowOpacity: 0.3, - shadowRadius: 6, - shadowOffset: { width: 0, height: 3 }, - elevation: 4, - }, buttonSecondary: { backgroundColor: "#f1f5f9", },