301 lines
6.8 KiB
TypeScript
301 lines
6.8 KiB
TypeScript
import React, { useEffect, useState, useCallback } from 'react';
|
|
import {
|
|
StyleSheet,
|
|
Text,
|
|
View,
|
|
FlatList,
|
|
ActivityIndicator,
|
|
SafeAreaView,
|
|
Image,
|
|
TouchableOpacity,
|
|
Alert,
|
|
} from 'react-native';
|
|
import { Ionicons } from "@react-native-vector-icons/ionicons"; // Assuming you use Expo or have vector icons installed
|
|
import { apiCall } from '../../utils/ApiHelper'; // Adjust path to your apiCall utility
|
|
import AppView from '../../components/AppView';
|
|
import colors from '../../components/colors';
|
|
import { RouteProp, useRoute } from '@react-navigation/native';
|
|
import { BASE_URL } from '../../utils/ApiUrls';
|
|
|
|
// --- Type Definitions ---
|
|
interface Member {
|
|
id: string;
|
|
userId: number;
|
|
firstName: string;
|
|
lastName: string | null;
|
|
mobile: string;
|
|
gender: string;
|
|
email: string;
|
|
username: string;
|
|
address: string;
|
|
role: 'USER' | 'ORGANIZER';
|
|
}
|
|
|
|
interface ApiResponse {
|
|
message: string;
|
|
status: string;
|
|
statusCode: number;
|
|
data: Member[];
|
|
}
|
|
type RootStackParamList = {
|
|
Member: { groupId: string }; // or number, depending on your data type
|
|
};
|
|
|
|
type MemberScreenRouteProp = RouteProp<RootStackParamList, 'Member'>;
|
|
|
|
interface Props {
|
|
route: MemberScreenRouteProp;
|
|
}
|
|
|
|
// --- Main Screen Component ---
|
|
// This assumes the component receives `groupId` from a navigator (e.g., Tab Navigator)
|
|
const MemberScreen = ({ route }: Props) => {
|
|
|
|
const { groupId } = route.params;
|
|
const [members, setMembers] = useState<Member[]>([]);
|
|
const [loading, setLoading] = useState<boolean>(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const fetchMembers = useCallback(async () => {
|
|
if (!groupId) {
|
|
setError('Group ID is missing.');
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
try {
|
|
const response = await apiCall<ApiResponse>(
|
|
`${BASE_URL}fund/group/all/member?groupId=${groupId}`,
|
|
'GET'
|
|
);
|
|
|
|
if (response && response.status === 'success') {
|
|
setMembers(response.data);
|
|
setError(null);
|
|
} else {
|
|
throw new Error(response?.message || 'Failed to fetch members.');
|
|
}
|
|
} catch (e: any) {
|
|
setError(e.message || 'An unexpected network error occurred.');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [groupId]);
|
|
|
|
useEffect(() => {
|
|
fetchMembers();
|
|
}, [fetchMembers]);
|
|
|
|
// --- Render Functions ---
|
|
const renderItem = ({ item }: { item: Member }) => {
|
|
const fullName = `${item.firstName} ${item.lastName || ''}`.trim();
|
|
const initial = item.firstName ? item.firstName.charAt(0).toUpperCase() : '?';
|
|
const isOrganizer = item.role === 'ORGANIZER';
|
|
|
|
return (
|
|
|
|
<TouchableOpacity style={styles.card}>
|
|
<View style={styles.avatarPlaceholder}>
|
|
<Text style={styles.avatarText}>{initial}</Text>
|
|
</View>
|
|
<View style={styles.infoContainer}>
|
|
<View style={{ flexDirection: "row", justifyContent: "space-between" }}>
|
|
<Text style={styles.name}>{fullName}</Text>
|
|
{isOrganizer && (
|
|
<View style={styles.organizerIcon}>
|
|
<Text>Orginzer</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
<Text style={styles.username}>@{item.username}</Text>
|
|
|
|
</View>
|
|
</TouchableOpacity>
|
|
);
|
|
};
|
|
|
|
const renderContent = () => {
|
|
if (loading) {
|
|
return (
|
|
<View style={styles.centered}>
|
|
<ActivityIndicator size="large" color="#4F46E5" />
|
|
<Text style={styles.loadingText}>Loading Members...</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<View style={styles.centered}>
|
|
<Text style={styles.errorText}>no members found</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
if (members.length === 0) {
|
|
return (
|
|
<View style={styles.centered}>
|
|
<Ionicons name="people-outline" size={60} color="#9CA3AF" />
|
|
<Text style={styles.emptyTitle}>No Members Yet</Text>
|
|
<Text style={styles.emptyText}>Invite people to join your group.</Text>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<FlatList
|
|
data={members}
|
|
renderItem={renderItem}
|
|
keyExtractor={item => item.id}
|
|
contentContainerStyle={styles.listContainer}
|
|
showsVerticalScrollIndicator={false}
|
|
/>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<AppView>
|
|
<View style={styles.header}>
|
|
<Text style={styles.headerTitle}>Group Members</Text>
|
|
<Text style={styles.headerSubtitle}>{members.length} member(s)</Text>
|
|
</View>
|
|
{renderContent()}
|
|
</AppView>
|
|
);
|
|
};
|
|
|
|
// --- Styles ---
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: '#F3F4F6',
|
|
},
|
|
header: {
|
|
paddingHorizontal: 20,
|
|
paddingTop: 20,
|
|
paddingBottom: 12,
|
|
|
|
},
|
|
headerTitle: {
|
|
fontSize: 28,
|
|
fontWeight: 'bold',
|
|
color: '#111827',
|
|
},
|
|
headerSubtitle: {
|
|
fontSize: 16,
|
|
color: '#6B7280',
|
|
marginTop: 4,
|
|
},
|
|
listContainer: {
|
|
padding: 16,
|
|
},
|
|
card: {
|
|
flexDirection: 'row',
|
|
backgroundColor: '#FFFFFF',
|
|
padding: 16,
|
|
borderRadius: 12,
|
|
marginBottom: 16,
|
|
alignItems: 'center',
|
|
shadowColor: '#374151',
|
|
shadowOpacity: 0.08,
|
|
shadowRadius: 10,
|
|
shadowOffset: { width: 0, height: 4 },
|
|
elevation: 3,
|
|
},
|
|
avatarPlaceholder: {
|
|
width: 52,
|
|
height: 52,
|
|
borderRadius: 26,
|
|
backgroundColor: '#D1D5DB',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginRight: 16,
|
|
},
|
|
avatarText: {
|
|
color: '#4B5563',
|
|
fontSize: 24,
|
|
fontWeight: 'bold',
|
|
},
|
|
organizerIcon: {
|
|
borderRadius: 10,
|
|
padding: 2,
|
|
borderWidth: 2,
|
|
borderColor: colors.pastelOrange
|
|
},
|
|
infoContainer: {
|
|
flex: 1,
|
|
},
|
|
name: {
|
|
fontSize: 18,
|
|
fontWeight: '700',
|
|
color: '#111827',
|
|
},
|
|
username: {
|
|
fontSize: 14,
|
|
color: '#6B7280',
|
|
marginBottom: 8,
|
|
},
|
|
contactRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
marginBottom: 4,
|
|
},
|
|
contactText: {
|
|
marginLeft: 8,
|
|
fontSize: 14,
|
|
color: '#374151',
|
|
},
|
|
menuButton: {
|
|
padding: 8,
|
|
marginLeft: 8,
|
|
},
|
|
centered: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
padding: 20,
|
|
},
|
|
loadingText: {
|
|
marginTop: 10,
|
|
fontSize: 16,
|
|
color: '#6B7280',
|
|
},
|
|
errorTitle: {
|
|
fontSize: 22,
|
|
fontWeight: 'bold',
|
|
color: '#EF4444',
|
|
marginBottom: 8,
|
|
},
|
|
errorText: {
|
|
fontSize: 16,
|
|
color: '#374151',
|
|
textAlign: 'center',
|
|
marginBottom: 20,
|
|
},
|
|
retryButton: {
|
|
backgroundColor: '#4F46E5',
|
|
paddingVertical: 12,
|
|
paddingHorizontal: 30,
|
|
borderRadius: 8,
|
|
},
|
|
retryButtonText: {
|
|
color: 'white',
|
|
fontSize: 16,
|
|
fontWeight: 'bold',
|
|
},
|
|
emptyTitle: {
|
|
fontSize: 22,
|
|
fontWeight: 'bold',
|
|
color: '#374151',
|
|
marginBottom: 8,
|
|
},
|
|
emptyText: {
|
|
fontSize: 16,
|
|
color: '#6B7280',
|
|
textAlign: 'center',
|
|
}
|
|
});
|
|
|
|
export default MemberScreen;
|