import React, { useState, useMemo, useEffect, useCallback } from 'react'; import { View, Text, StyleSheet, FlatList, TouchableOpacity, ScrollView, ToastAndroid, RefreshControl, } from 'react-native'; import { useNavigation, useRoute, RouteProp, useFocusEffect } from '@react-navigation/native'; import { useSelector } from 'react-redux'; import { responsiveFontSize } from 'react-native-responsive-dimensions'; import LucideIcon from '../../components/common/LucideIcon'; import { APPROVE_REQUEST_URL, DECLINE_REQUEST_URL, GET_JOINED_GROUP_URL, MY_GROUPS_URL, REQUESTS_URL, USER_REQUEST } from '../../utils/ApiUrls'; import AppView from '../../components/AppView'; import { useTheme } from '../../hooks/ui/useTheme'; import { useFetch } from '../../hooks/core/useFetch'; import { useUpdate } from '../../hooks/core/useUpdate'; import { MyGroup, JoinRequest } from '../../types'; import MyGroupCard from '../../components/MyGroupCard'; import { JoinedGroupCard } from '../../components/JoinedGroupCard'; import { RequestCard } from '../../components/RequestCard'; import { UserRequested } from '../../components/UserRequested'; import SkeletonLoader from '../../components/common/SkeletonLoader'; import FontsFamily from '../../constents/FontsFamily'; import { RootTabParamList } from '../../navigation/BottomTabs'; type ManagementRouteProp = RouteProp; type ActiveTab = 'My Groups' | 'Joined Groups' | 'Requests' | 'My Requests'; type RequestSubTab = 'Join Requests' | 'My Requests'; type GroupStatusFilter = 'ALL' | 'PENDING' | 'APPROVED' | 'ACTIVE' | 'DELAY' | 'COMPLETED' | 'REJECTED' | 'CANCELLED'; const GROUP_STATUS_FILTERS: GroupStatusFilter[] = [ 'ALL', 'PENDING', 'APPROVED', 'ACTIVE', 'DELAY', 'COMPLETED', 'REJECTED', 'CANCELLED', ]; const JOINED_GROUP_STATUS_FILTERS: GroupStatusFilter[] = [ 'ALL', 'ACTIVE', 'DELAY', 'COMPLETED', 'APPROVED' ]; const getListFromResponse = (response: any): T[] => { if (!response) return []; if (Array.isArray(response?.data?.content)) return response.data.content; if (Array.isArray(response?.data)) return response.data; return []; }; const ManagementScreen: React.FC = () => { const theme = useTheme(); const user = useSelector((state: any) => state.user); const navigation = useNavigation(); const route = useRoute(); const [activeTab, setActiveTab] = useState( user.role === 'ORGANIZER' ? 'My Groups' : 'Joined Groups' ); const [myGroupStatusFilter, setMyGroupStatusFilter] = useState('APPROVED'); const [joinedGroupStatusFilter, setJoinedGroupStatusFilter] = useState('ALL'); const [requestSubTab, setRequestSubTab] = useState('Join Requests'); useEffect(() => { if (!route.params?.actionTab) return; const routeTab = route.params.actionTab as ActiveTab; setActiveTab(routeTab); }, [route.params?.actionTab]); const myGroupsParams = useMemo(() => { return { ...(myGroupStatusFilter && myGroupStatusFilter !== 'ALL' ? { status: myGroupStatusFilter } : {}), page: 0, size: 10, }; }, [myGroupStatusFilter]); const joinedGroupsParams = useMemo(() => { return { ...(joinedGroupStatusFilter && joinedGroupStatusFilter !== 'ALL' ? { status: joinedGroupStatusFilter } : {}), page: 0, size: 10, }; }, [joinedGroupStatusFilter]); const requestsParams = useMemo(() => ({ page: 0, size: 10 }), []); const myRequestsParams = useMemo(() => ({ page: 0, size: 10 }), []); console.log('joinedGroupsParams:', joinedGroupsParams); // Data Fetching const { data: myGroupsRes, isLoading: myLoading, refetch: refetchMy } = useFetch( MY_GROUPS_URL, myGroupsParams, { enabled: user.role === 'ORGANIZER' } ); const { data: joinedRes, isLoading: joinedLoading, refetch: refetchJoined } = useFetch( GET_JOINED_GROUP_URL, joinedGroupsParams ); const { data: requestsRes, isLoading: requestsLoading, refetch: refetchRequests } = useFetch( REQUESTS_URL, requestsParams, { enabled: user.role === 'ORGANIZER' } ); const { data: userReqRes, isLoading: userReqLoading, refetch: refetchUserReq } = useFetch( USER_REQUEST, myRequestsParams ); const { mutate: updateRequest } = useUpdate(''); const myGroups = useMemo(() => getListFromResponse(myGroupsRes), [myGroupsRes]); const joinedGroups = useMemo(() => getListFromResponse(joinedRes), [joinedRes]); const incomingRequests = useMemo(() => getListFromResponse(requestsRes), [requestsRes]); const myRequests = useMemo(() => getListFromResponse(userReqRes), [userReqRes]); const [refreshing, setRefreshing] = useState(false); const onRefresh = useCallback(() => { setRefreshing(true); Promise.all([ refetchMy(), refetchJoined(), refetchRequests(), refetchUserReq(), ]).finally(() => { setRefreshing(false); }); }, [refetchMy, refetchJoined, refetchRequests, refetchUserReq]); useFocusEffect( useCallback(() => { onRefresh(); }, []) ); const tabs: ActiveTab[] = useMemo(() => user.role === 'ORGANIZER' ? ['My Groups', 'Joined Groups', 'Requests'] : ['Joined Groups', 'My Requests'], [user.role]); const handleApprove = (req: JoinRequest) => { updateRequest({ resource: `${APPROVE_REQUEST_URL}${req.requestId}/approve`, payload: {} } as any, { onSuccess: () => { ToastAndroid.show('Request approved successfully', ToastAndroid.SHORT); refetchRequests(); }, onError: (err) => ToastAndroid.show(err.message || 'Failed to approve', ToastAndroid.SHORT), }); }; const handleDecline = (req: JoinRequest) => { updateRequest({ resource: `${DECLINE_REQUEST_URL}${req.requestId}/reject?reason=/`, payload: {} } as any, { onSuccess: () => { ToastAndroid.show('Request declined', ToastAndroid.SHORT); refetchRequests(); }, onError: (err) => ToastAndroid.show(err.message || 'Failed to decline', ToastAndroid.SHORT), }); }; const handleManageGroup = (group: MyGroup) => navigation.navigate('ManagementTabs', { groupId: group.id }); const renderSkeletons = () => ( {[1, 2, 3].map(i => ( ))} ); const getFilteredData = () => { switch (activeTab) { case 'My Groups': return myGroups; case 'Joined Groups': return joinedGroups; case 'Requests': return requestSubTab === 'Join Requests' ? incomingRequests : myRequests; case 'My Requests': return myRequests; default: return []; } }; const isLoading = myLoading || joinedLoading || requestsLoading || userReqLoading; return ( Account Hub Manage your circles and requests {tabs.map((tab) => ( setActiveTab(tab)} style={[ styles.tab, activeTab === tab && { backgroundColor: theme.colors.card, borderRadius: 12, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 2 } ]} > {tab} ))} {user.role === 'ORGANIZER' && activeTab === 'My Groups' && ( {GROUP_STATUS_FILTERS.map((status, index) => { const isActive = myGroupStatusFilter === status; return ( setMyGroupStatusFilter(status)} style={[ styles.statusFilterChip, index !== GROUP_STATUS_FILTERS.length - 1 && styles.statusFilterChipSpaced, { borderColor: isActive ? theme.colors.primary : theme.colors.border, backgroundColor: isActive ? theme.colors.primarySoft : theme.colors.surface, }, ]} > {status} ); })} )} {activeTab === 'Joined Groups' && ( {JOINED_GROUP_STATUS_FILTERS.map((status, index) => { const isActive = joinedGroupStatusFilter === status; return ( setJoinedGroupStatusFilter(status)} style={[ styles.statusFilterChip, index !== JOINED_GROUP_STATUS_FILTERS.length - 1 && styles.statusFilterChipSpaced, { borderColor: isActive ? theme.colors.primary : theme.colors.border, backgroundColor: isActive ? theme.colors.primarySoft : theme.colors.surface, }, ]} > {status} ); })} )} {activeTab === 'Requests' && user.role === 'ORGANIZER' && ( {(['Join Requests', 'My Requests'] as RequestSubTab[]).map((tab, index) => { const isActive = requestSubTab === tab; return ( setRequestSubTab(tab)} style={[ styles.statusFilterChip, index !== (['Join Requests', 'My Requests'] as RequestSubTab[]).length - 1 && styles.statusFilterChipSpaced, { borderColor: isActive ? theme.colors.primary : theme.colors.border, backgroundColor: isActive ? theme.colors.primarySoft : theme.colors.surface, }, ]} > {tab} ); })} )} String(item.id || item.requestId || index)} renderItem={({ item }) => { if (activeTab === 'My Groups') return ; if (activeTab === 'Joined Groups') return ; if (activeTab === 'Requests') { if (requestSubTab === 'Join Requests') { return ; } else { return ; } } if (activeTab === 'My Requests') return ; return null; }} refreshControl={ } ListEmptyComponent={ !isLoading ? ( Nothing to show here ) : renderSkeletons() } /> ); }; const styles = StyleSheet.create({ header: { paddingHorizontal: 14, paddingTop: 10, paddingBottom: 12, zIndex: 10, }, headerTitleRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', }, refreshBtn: { padding: 8, borderRadius: 12, alignItems: 'center', justifyContent: 'center', }, title: { fontSize: responsiveFontSize(3.2), fontFamily: FontsFamily.nunitoBold, }, subtitle: { fontSize: responsiveFontSize(1.6), fontFamily: FontsFamily.nunitoRegular, marginTop: 4, }, tabContainer: { flexDirection: 'row', marginHorizontal: 12, padding: 5, borderRadius: 16, marginBottom: 12, zIndex: 10, }, tab: { flex: 1, paddingVertical: 10, alignItems: 'center', justifyContent: 'center', }, tabText: { fontSize: responsiveFontSize(1.4), fontFamily: FontsFamily.nunitoMedium, }, statusFilterContainer: { minHeight: 46, maxHeight: 46, marginBottom: 10, }, statusFilterRow: { paddingHorizontal: 12, alignItems: 'center', }, statusFilterChip: { borderWidth: 1, borderRadius: 999, minHeight: 34, paddingHorizontal: 14, paddingVertical: 0, justifyContent: 'center', alignSelf: 'center', }, statusFilterChipSpaced: { marginRight: 8, }, statusFilterText: { fontSize: responsiveFontSize(1.25), fontFamily: FontsFamily.nunitoSemiBold, }, listContent: { paddingBottom: 86, }, skeletonContainer: { paddingHorizontal: 12, }, emptyState: { alignItems: 'center', marginTop: 64, }, emptyText: { marginTop: 16, fontSize: responsiveFontSize(1.6), fontFamily: FontsFamily.nunitoMedium, }, }); export default ManagementScreen;