botton navi...
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
import { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import React, { useState, useEffect, useCallback, FC } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
Alert,
|
||||
ActivityIndicator,
|
||||
} from 'react-native';
|
||||
import { RootStackParamList } from '../navigation/AppNavigator'; // Adjust path
|
||||
import { apiCall } from '../utils/ApiHelper'; // Adjust path
|
||||
import AppView from '../components/AppView'; // Adjust path
|
||||
import colors from '../components/colors'; // Adjust path
|
||||
|
||||
// --- Type Definitions ---
|
||||
interface Participant {
|
||||
id: string;
|
||||
firstName: string;
|
||||
lastName: string | null;
|
||||
mobile: string;
|
||||
role: 'ORGANIZER' | 'USER';
|
||||
}
|
||||
|
||||
interface GroupDetails {
|
||||
id: string;
|
||||
groupName: string;
|
||||
description: string;
|
||||
contributionAmount: number;
|
||||
maxParticipants: number;
|
||||
cycleType: 'QUARTERLY' | 'MONTHLY' | 'WEEKLY' | 'CUSTOM';
|
||||
status: 'PENDING' | 'ACTIVE' | 'COMPLETED' | 'APPROVED';
|
||||
organizerFirstName: string;
|
||||
organizerLastName: string | null;
|
||||
totalParticipants: number;
|
||||
participants: Participant[];
|
||||
}
|
||||
|
||||
type InviteScreenProps = NativeStackScreenProps<RootStackParamList, 'Invite'>; // Assuming 'Invite' is in your stack
|
||||
|
||||
// --- Main Screen Component ---
|
||||
const InviteScreen: FC<InviteScreenProps> = ({ route }) => {
|
||||
const { groupId,code} = route.params;
|
||||
|
||||
const [groupDetails, setGroupDetails] = useState<GroupDetails | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [joining, setJoining] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchGroupDetails = useCallback(async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await apiCall(`https://exotic-humpback-mistakenly.ngrok-free.app/api/fund/group/details?groupId=${groupId}`, 'GET') as { status: string; data?: GroupDetails; message?: string };
|
||||
if (response.status === 'success' && response.data) {
|
||||
setGroupDetails(response.data);
|
||||
} else {
|
||||
throw new Error(response.message || 'Failed to fetch group invitation details.');
|
||||
}
|
||||
} catch (e: any) {
|
||||
setError(e.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [groupId]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchGroupDetails();
|
||||
}, [fetchGroupDetails]);
|
||||
|
||||
const handleJoinGroup = async () => {
|
||||
if (!groupDetails) return;
|
||||
|
||||
setJoining(true);
|
||||
try {
|
||||
// Replace with your actual API call to join the group
|
||||
// For now, it just shows an alert.
|
||||
// await apiCall(`/api/fund/group/join`, 'POST', { groupId: groupDetails.id });
|
||||
Alert.alert('Success!', `You have requested to join "${groupDetails.groupName}".`);
|
||||
} catch (e: any) {
|
||||
Alert.alert('Join Error', e.message || 'An unexpected error occurred.');
|
||||
} finally {
|
||||
setJoining(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <View style={styles.centered}><ActivityIndicator size="large" color={colors.primary} /></View>;
|
||||
if (error) return <View style={styles.centered}><Text style={styles.errorText}>{error}</Text></View>;
|
||||
if (!groupDetails) return null;
|
||||
|
||||
const organizerName = `${groupDetails.organizerFirstName} ${groupDetails.organizerLastName || ''}`.trim();
|
||||
|
||||
return (
|
||||
<AppView>
|
||||
<ScrollView contentContainerStyle={styles.contentContainer}>
|
||||
<Text style={styles.headerTitle}>You're Invited!</Text>
|
||||
<Text style={styles.headerSubtitle}>Join the group <Text style={{ fontWeight: 'bold' }}>{groupDetails.groupName}</Text></Text>
|
||||
|
||||
{/* Group Info Card */}
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.cardTitle}>Group Details</Text>
|
||||
<InfoRow label="Organizer" value={organizerName} />
|
||||
<InfoRow label="Contribution" value={`₹${groupDetails.contributionAmount.toLocaleString('en-IN')}`} />
|
||||
<InfoRow label="Payment Cycle" value={groupDetails.cycleType} />
|
||||
<InfoRow label="Status" value={groupDetails.status} />
|
||||
</View>
|
||||
|
||||
{/* Participants Card */}
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.cardTitle}>Participants ({groupDetails.totalParticipants} / {groupDetails.maxParticipants})</Text>
|
||||
{groupDetails.participants.length > 0 ? (
|
||||
groupDetails.participants.map(p => (
|
||||
<View key={p.id} style={styles.participantRow}>
|
||||
<Text style={styles.participantName}>{`${p.firstName} ${p.lastName || ''}`.trim()}</Text>
|
||||
{p.role === 'ORGANIZER' && <View style={styles.organizerBadge}><Text style={styles.organizerBadgeText}>Organizer</Text></View>}
|
||||
</View>
|
||||
))
|
||||
) : (
|
||||
<Text style={styles.noParticipantsText}>Be the first to join!</Text>
|
||||
)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
{/* Floating Join Button */}
|
||||
<View style={styles.floatingButtonContainer}>
|
||||
<TouchableOpacity
|
||||
style={[styles.joinButton, groupDetails.status !== 'PENDING' && groupDetails.status !== 'APPROVED' ? styles.disabledButton : {}]}
|
||||
onPress={handleJoinGroup}
|
||||
disabled={joining || (groupDetails.status !== 'PENDING' && groupDetails.status !== 'APPROVED')}
|
||||
>
|
||||
{joining ? (
|
||||
<ActivityIndicator color="#FFFFFF" />
|
||||
) : (
|
||||
<Text style={styles.joinButtonText}>
|
||||
{groupDetails.status === 'PENDING' || groupDetails.status === 'APPROVED' ? 'Join Group' : `Group is ${groupDetails.status}`}
|
||||
</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</AppView>
|
||||
);
|
||||
};
|
||||
|
||||
// Helper component for displaying rows
|
||||
const InfoRow = ({ label, value }: { label: string; value: string }) => (
|
||||
<View style={styles.infoRow}>
|
||||
<Text style={styles.infoLabel}>{label}</Text>
|
||||
<Text style={styles.infoValue}>{value}</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
centered: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F3F4F6' },
|
||||
errorText: { color: '#EF4444', fontSize: 16, textAlign: 'center', padding: 20 },
|
||||
contentContainer: { padding: 16, paddingBottom: 100 }, // Add padding to avoid overlap with button
|
||||
|
||||
headerTitle: { fontSize: 32, fontWeight: 'bold', color: '#111827', textAlign: 'center' },
|
||||
headerSubtitle: { fontSize: 18, color: '#4B5563', textAlign: 'center', marginBottom: 24, marginTop: 4 },
|
||||
|
||||
card: { backgroundColor: '#FFFFFF', borderRadius: 16, padding: 20, marginBottom: 16, shadowColor: '#374151', shadowOpacity: 0.05, shadowRadius: 10 },
|
||||
cardTitle: { fontSize: 18, fontWeight: 'bold', color: '#1F2937', marginBottom: 12, borderBottomWidth: 1, borderBottomColor: '#E5E7EB', paddingBottom: 10 },
|
||||
|
||||
infoRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 8 },
|
||||
infoLabel: { fontSize: 16, color: '#6B7280' },
|
||||
infoValue: { fontSize: 16, fontWeight: '600', color: '#111827', textTransform: 'capitalize' },
|
||||
|
||||
participantRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 10 },
|
||||
participantName: { fontSize: 16, color: '#1F2937' },
|
||||
organizerBadge: { backgroundColor: '#FEF3C7', paddingHorizontal: 8, paddingVertical: 3, borderRadius: 12 },
|
||||
organizerBadgeText: { color: '#92400E', fontSize: 12, fontWeight: '500' },
|
||||
noParticipantsText: { fontSize: 15, color: '#6B7280', textAlign: 'center', paddingVertical: 10 },
|
||||
|
||||
floatingButtonContainer: {
|
||||
position: 'absolute',
|
||||
bottom: 20,
|
||||
left: 20,
|
||||
right: 20,
|
||||
alignItems: 'center',
|
||||
},
|
||||
joinButton: {
|
||||
backgroundColor: colors.primary,
|
||||
borderRadius: 14,
|
||||
paddingVertical: 16,
|
||||
paddingHorizontal: 32,
|
||||
elevation: 5,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 4,
|
||||
width: '80%',
|
||||
alignItems: 'center',
|
||||
},
|
||||
joinButtonText: {
|
||||
color: '#FFFFFF',
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
disabledButton: {
|
||||
backgroundColor: '#9CA3AF', // Gray color when disabled
|
||||
},
|
||||
});
|
||||
|
||||
export default InviteScreen;
|
||||
|
||||
Reference in New Issue
Block a user