import React, { useState, useEffect, useCallback } from "react"; import { View, Text, StyleSheet, FlatList, TouchableOpacity, TextInput, Dimensions, ActivityIndicator, Image, ScrollView, Alert, RefreshControl, } from "react-native"; import { FAB } from "react-native-paper"; import { Ionicons } from "@react-native-vector-icons/ionicons"; import { MaterialIcons } from "@react-native-vector-icons/material-icons"; import { useNavigation, useFocusEffect } from "@react-navigation/native"; import { StackNavigationProp } from "@react-navigation/stack"; import { RootStackParamList } from "../navigation/AppNavigator"; import AppView from "../components/AppView"; import { apiCall } from "../utils/ApiHelper"; import { BASE_URL, GET_JOINED_GROUP_URL, JOIN_GROUP_URL, PUBLIC_GROUPS_URL, PUT_USER_STATUS } from "../utils/ApiUrls"; import colors from "../components/colors"; import { ApiResponse } from "./home/UserProfileStatus"; import * as Keychain from "react-native-keychain"; const { width } = Dimensions.get('window'); // --- Types --- interface Group { id: string; groupName: string; description: string; maxParticipants: number; currentParticipants: number; contributionAmount: number; cycleType: string; createdByName: string; imageBlob: string } // --- UI Components --- const GroupCard = ({ item, onJoin, onView, isGrid }: { item: Group; onJoin:()=>void; onView: (group: Group) => void; isGrid?: boolean; }) => { const isFull = item.currentParticipants >= item.maxParticipants; const cardStyle = isGrid ? styles.gridCard : styles.listCard; const [loading, setLoading] = useState(false) const navigation = useNavigation() return ( {/* navigation.navigate("GroupDetails" as never)}> */} {item.groupName} {item.description} {item.currentParticipants}/{item.maxParticipants} Members {item.cycleType} {/* */} {/* onView(item)}> View */} { setLoading(true); await onJoin(item.id); setLoading(false); }} disabled={isFull || loading} // Also disable button while loading > {loading ? : {isFull ? "Full" : "Join"}} ); }; const FeaturedGroupCard = ({ item }: { item: Group }) => { return ( {item.groupName} by {item.createdByName} ) }; export default function GroupScreen() { const [activeTab, setActiveTab] = useState("Public"); const [searchQuery, setSearchQuery] = useState(""); const [isGridView, setIsGridView] = useState(false); const navigation = useNavigation>(); const [myGroups, setMyGroups] = useState([]); const [joinedGroups, setJoinedGroups] = useState([]); const [publicGroups, setPublicGroups] = useState([]); const [featuredGroups, setFeaturedGroups] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [role, setRole] = useState(null); const [refreshing, setRefreshing] = useState(false) const fetchGroups = async () => { if (loading) return; setLoading(true); setError(null); try { const res = await apiCall(PUBLIC_GROUPS_URL, "GET") as { data?: Group[] }; if (res && res.data) { setPublicGroups(res.data); setFeaturedGroups(res.data.slice(0, 5)); } else { throw new Error("No data received"); } } catch (e) { setError("Failed to fetch groups. Please try again."); } finally { setLoading(false); } }; const fetchJoinedGroups = async () => { if (loading) return; setLoading(true); setError(null); try { console.log("first-????-") const res = await apiCall(GET_JOINED_GROUP_URL, "GET") as { data?: Group[] }; console.log("first---", res) if (res && res.data) { setJoinedGroups(res.data); } else { throw new Error("No data received"); } } catch (e) { setError("Failed to fetch groups. Please try again."); } finally { setLoading(false); } }; useFocusEffect(React.useCallback(() => { fetchGroups() fetchJoinedGroups() }, [])); const filterData = (data: Group[]) => { if (!searchQuery.trim()) return data; return data.filter(g => g.groupName.toLowerCase().includes(searchQuery.toLowerCase()) ); }; const handleJoinGroup = async (groupId: string, userId: string) => { console.log("Joining group:", groupId, "User:", userId); try { const url = `${JOIN_GROUP_URL}groups/${groupId}/join/10`; console.log("Joining group URL:", url); const res = await apiCall(url, "POST") as any; console.log("JOIN_GROUP_URL response:", res); if (res.statusCode === 200) { Alert.alert("Success", "You requested to join the group."); } else { // Alert.alert("Error", res.message || "Failed to join group."); } } catch (error) { console.error("Join Group Error:", error); // Alert.alert("Error", "An unexpected error occurred."); } }; const userStatus = async () => { try { const credentials = await Keychain.getGenericPassword(); if (!credentials) { Alert.alert("Error", "You are not logged in"); return; } const token = credentials.password; setLoading(true); setError(null); const response = await fetch(PUT_USER_STATUS, { method: "GET", headers: { Accept: "*/*", Authorization: `Bearer ${token}`, }, }); const json: ApiResponse = await response.json(); console.log("👤 Profile Status Response:", json); if (json.status === "success") { setRole(json.data.role); if (json.data.profileStatus === false) { } } else { setError(json.message || "Failed to fetch status"); } } catch (err) { setError("Something went wrong. Please try again."); } finally { setLoading(false); } }; useFocusEffect( useCallback(() => { userStatus(); }, []) ); const handleViewGroup = (group: Group) => { // navigation.navigate('GroupDetails', { group }); Alert.alert("View Group", `Viewing details for ${group.groupName}`); }; const onRefresh = () => { setRefreshing(true) fetchGroups() setTimeout(() => { setRefreshing(false) }, 20000); } const renderContent = () => { let data: Group[]; switch (activeTab) { case "My Groups": data = myGroups; break; case "Joined": data = joinedGroups; break; case "Public": data = publicGroups; break; default: data = []; } if (loading && !publicGroups.length) return ; if (error) return {error}; const filteredData = filterData(data); if (filteredData.length === 0 && !loading) return No groups found.; return ( item.id} renderItem={({ item }) => ( { console.log('mmmm') if (activeTab === 'Public') { handleJoinGroup(item.id, 'currentUserId'); } }} onView={handleViewGroup} isGrid={isGridView} /> )} columnWrapperStyle={isGridView ? { justifyContent: 'space-between' } : null} contentContainerStyle={{ paddingBottom: 150 }} refreshControl={ } // ListFooterComponent={} /> ); }; return ( {/* Fixed Header */} Explore Groups {/* Scrollable Content */} {role === "ORGANIZER" ? (["Public"].map((tab) => ( setActiveTab(tab)}> {tab} ))) : (["Joined", "Public"].map((tab) => ( setActiveTab(tab)}> {tab} )))} setIsGridView(!isGridView)} style={styles.layoutToggleButton}> {/* The list now renders inside a simple View, as FlatList handles its own scroll */} {renderContent()} {role === "ORGANIZER" && ( } onPress={() => navigation.navigate('GroupCreation')} /> )} ); } const styles = StyleSheet.create({ headerContainer: { backgroundColor: '#f5f6fa', paddingHorizontal: 16 }, title: { fontSize: 22, fontWeight: "600", color: colors.Group }, searchContainer: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#fff', borderRadius: 20, paddingHorizontal: 10, elevation: 5, marginVertical: 20 , borderWidth:2,borderColor:colors.pastelGreen }, searchIcon: { marginRight: 8 }, searchInput: { flex: 1, height: 45, fontSize: 16, color: '#333' }, sectionTitle: { fontSize: 20, fontWeight: 'bold', color: colors.Group, marginBottom: 12, paddingHorizontal: 16 }, featuredList: { paddingLeft: 16, marginBottom: 20 }, featuredCard: { width: width * 0.45, marginRight: 15, backgroundColor: '#fff', borderRadius: 12, elevation: 3, overflow: 'hidden' }, featuredImage: { width: '100%', height: 100 }, featuredContent: { padding: 10 }, featuredName: { fontSize: 14, fontWeight: 'bold', color: '#333' }, featuredCreator: { fontSize: 12, color: '#777', marginTop: 2 }, tabHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: 16, marginBottom: 16 }, segmentedControl: { flex: 1, flexDirection: 'row', borderRadius: 10, padding: 4, marginRight: 10 }, segmentButton: { flex: 1, paddingVertical: 10, borderRadius: 8, alignItems: 'center' }, segmentButtonActive: { backgroundColor: colors.pastelGreen, elevation: 5, shadowColor: '#000', shadowOpacity: 0.1, shadowRadius: 5 , borderRadius:15 }, segmentText: { fontSize: 14, fontWeight: '600', color: '#555' }, segmentTextActive: { color: '#333' }, layoutToggleButton: { padding: 8 }, fab: { position: "absolute", right: 20, bottom: 90, width: 60, height: 60, justifyContent: "center", alignItems: "center", backgroundColor: colors.pastelGreen, borderRadius: 30, elevation: 4 }, listCard: { backgroundColor: colors.pastelGreen, padding: 16, borderRadius: 20, marginBottom: 12,marginHorizontal:15, elevation: 5 }, gridCard: { backgroundColor: colors.pastelGreen, padding: 12, borderRadius: 20, marginBottom: 12,marginHorizontal:15, elevation: 5, width: (width - 48) / 2 }, groupName: { fontSize: 16, fontWeight: 'bold', color: '#333', width: '50%' }, groupDescription: { fontSize: 14, color: '#666', marginVertical: 4, width: '40%' }, groupMeta: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 8, borderTopWidth: 1, borderTopColor: '#f0f0f0', paddingTop: 8, marginBottom: 10 }, groupMetaText: { fontSize: 12, color: '#888', fontWeight: '500', textTransform: 'capitalize' }, emptyText: { textAlign: 'center', marginTop: 40, fontSize: 16, color: '#888' }, emptyFeaturedText: { marginLeft: 20, color: '#888' }, buttonContainer: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 8 }, viewButton: { flex: 1, backgroundColor: '#e9ecef', paddingVertical: 10, borderRadius: 8, alignItems: 'center', marginRight: 5 }, viewButtonText: { color: '#333', fontWeight: 'bold', fontSize: 14 }, joinButton: { flex: 1, backgroundColor: colors.white, paddingVertical: 10, borderRadius: 8, alignItems: 'center', marginLeft: 5 }, joinButtonDisabled: { backgroundColor: '#bdc3c7' }, joinButtonText: { color: '#333', fontWeight: 'bold', fontSize: 14 }, });