import { NativeStackScreenProps } from '@react-navigation/native-stack'; import React, { useState, useEffect, useCallback } from 'react'; import { View, Text, TextInput, 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'; import { Picker } from '@react-native-picker/picker'; // --- Type Definitions --- interface Participant { id: string; firstName: string; lastName: string | null; mobile: string; role: 'ORGANIZER' | 'USER'; } export interface GroupDetails { id: string; groupName: string; description: string; startDate: [number, number, number] | null; endDate: [number, number, number] | null; contributionAmount: number; minParticipants: number; maxParticipants: number; portalCharge: number | null; latePaymentCharge: number | null; cycleType: 'QUARTERLY' | 'MONTHLY' | 'WEEKLY' | 'CUSTOM'; customCycleDays: number; withdrawalMethod: 'RANDOM' | 'SEQUENTIAL'; status: 'PENDING' | 'ACTIVE' | 'COMPLETED' | 'APPROVED'; visibility: 'PUBLIC' | 'PRIVATE'; organizerId: string; totalParticipants: number; participants: Participant[]; } export interface EditGroupPayload { groupName: string; description: string; maxParticipants: number; contributionAmount: number; customCycleDays: number | null; cycleType: 'QUARTERLY' | 'MONTHLY' | 'WEEKLY' | 'CUSTOM'; latePaymentCharge: number; visibility: 'PUBLIC' | 'PRIVATE'; } type EditGroupScreenProps = NativeStackScreenProps; type EditingField = keyof EditGroupPayload | null; // --- Main Screen Component --- const EditGroupScreen: React.FC = ({ route }) => { const { groupId } = route.params; const [editingField, setEditingField] = useState(null); const [groupDetails, setGroupDetails] = useState(null); const [editableData, setEditableData] = useState({ groupName: '', description: '', maxParticipants: 0, contributionAmount: 0, customCycleDays: null, cycleType: 'MONTHLY', latePaymentCharge: 0, visibility: 'PRIVATE', }); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [starting, setStarting] = useState(false); const [error, setError] = useState(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); setEditableData({ groupName: response.data.groupName, description: response.data.description, maxParticipants: response.data.maxParticipants, contributionAmount: response.data.contributionAmount, customCycleDays: response.data.customCycleDays, cycleType: response.data.cycleType, latePaymentCharge: response.data.latePaymentCharge ?? 0, visibility: response.data.visibility, }); } else { throw new Error(response.message || 'Failed to fetch group data.'); } } catch (e: any) { setError(e.message); } finally { setLoading(false); } }, [groupId]); useEffect(() => { fetchGroupDetails(); }, [fetchGroupDetails]); const handleCancel = () => { if (groupDetails) { setEditableData({ groupName: groupDetails.groupName, description: groupDetails.description, maxParticipants: groupDetails.maxParticipants, contributionAmount: groupDetails.contributionAmount, customCycleDays: groupDetails.customCycleDays, cycleType: groupDetails.cycleType, latePaymentCharge: groupDetails.latePaymentCharge ?? 0, visibility: groupDetails.visibility, }); } setEditingField(null); }; const handleSave = async () => { if (!groupDetails || !editingField) return; if (editingField === 'maxParticipants') { const newMaxParticipants = Number(editableData.maxParticipants); if (newMaxParticipants < groupDetails.totalParticipants) { Alert.alert('Invalid Input', `Max participants cannot be less than the current count (${groupDetails.totalParticipants}).`); return; } } setSaving(true); try { await apiCall(`https://exotic-humpback-mistakenly.ngrok-free.app/apifund/groups/${groupDetails.id}/edit`, 'PUT', editableData); Alert.alert('Success', 'Group has been updated.'); setEditingField(null); await fetchGroupDetails(); } catch (e: any) { Alert.alert('Save Error', e.message || 'An unexpected error occurred.'); } finally { setSaving(false); } }; const handleStartGroup = async () => { if (!groupDetails) return; Alert.alert( "Confirm Start", "Are you sure you want to start this group? This action cannot be undone.", [ { text: "Cancel", style: "cancel" }, { text: "Start", onPress: async () => { setStarting(true); try { await apiCall(`https://exotic-humpback-mistakenly.ngrok-free.app/api/fund/group/start?groupId=${groupDetails.id}`, 'GET'); Alert.alert('Success', 'Group has been started successfully.'); await fetchGroupDetails(); } catch (e: any) { Alert.alert('Start Error', e.message || 'An unexpected error occurred.'); } finally { setStarting(false); } }, style: 'destructive' } ] ); }; const handleUpdateField = (field: keyof EditGroupPayload, value: any) => { setEditableData(prev => ({ ...prev, [field]: value })); }; if (loading) return ; if (error) return {error}; if (!groupDetails) return null; const isEditable = groupDetails.status === 'PENDING' || groupDetails.status === 'APPROVED'; const canStartGroup = groupDetails.status === 'APPROVED' && groupDetails.totalParticipants >= groupDetails.minParticipants; const renderInputField = (field: EditingField) => { if (!field || !editableData) return null; const commonProps = { style: styles.input, placeholderTextColor: "#9CA3AF" }; switch(field) { case 'groupName': return handleUpdateField('groupName', text)} />; case 'description': return handleUpdateField('description', text)} multiline />; case 'contributionAmount': case 'maxParticipants': case 'latePaymentCharge': case 'customCycleDays': return handleUpdateField(field, text ? Number(text.replace(/[^0-9]/g, '')) : 0)} keyboardType="numeric" />; case 'cycleType': return handleUpdateField('cycleType', itemValue)} style={styles.picker}> {['MONTHLY', 'WEEKLY', 'QUARTERLY', 'CUSTOM'].map(c => )} ; case 'visibility': return handleUpdateField('visibility', itemValue)} style={styles.picker}> ; default: return null; } } const renderRow = (field: EditingField, label: string, displayValue: string | number) => { return ( {label} {editingField !== field && ( {displayValue} {isEditable && setEditingField(field)} style={styles.editButton}> ✎ } )} {editingField === field && isEditable && ( {renderInputField(field)} Cancel {saving ? : Save} )} ) } return ( Edit Group {canStartGroup && ( Ready to Start? This group has met the minimum participant requirement. {starting ? : Start Group} )} Group Info {renderRow('groupName', 'Group Name', groupDetails.groupName)} {renderRow('description', 'Description', groupDetails.description)} Financial Rules {renderRow('contributionAmount', 'Contribution', `₹${groupDetails.contributionAmount.toLocaleString('en-IN')}`)} {renderRow('maxParticipants', 'Max Participants', `${groupDetails.totalParticipants} / ${groupDetails.maxParticipants}`)} {renderRow('latePaymentCharge', 'Late Fee', `₹${(groupDetails.latePaymentCharge ?? 0).toLocaleString('en-IN')}`)} Configuration {renderRow('cycleType', 'Payment Cycle', groupDetails.cycleType)} {(editableData.cycleType === 'CUSTOM' || groupDetails.cycleType === 'CUSTOM') && renderRow('customCycleDays', 'Custom Days', `${editableData.customCycleDays ?? groupDetails.customCycleDays} days`)} {renderRow('visibility', 'Visibility', groupDetails.visibility)} Participants ({groupDetails.totalParticipants}) {groupDetails.participants.length > 0 ? ( groupDetails.participants.map(p => ( {`${p.firstName} ${p.lastName || ''}`.trim()} {p.role === 'ORGANIZER' && Organizer} {p.mobile} )) ) : ( No participants yet. )} ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#F3F4F6' }, contentContainer: { padding: 16, paddingBottom: 40 }, centered: { flex: 1, justifyContent: 'center', alignItems: 'center' }, errorText: { color: '#EF4444', fontSize: 16, textAlign: 'center' }, headerTitle: { fontSize: 28, fontWeight: 'bold', color: '#111827', marginBottom: 20 }, 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 }, editButton: { padding: 8, marginLeft: 8 }, editButtonText: { color: '#4F46E5', fontSize: 20, fontWeight: 'bold' }, fieldContainer: { borderBottomWidth: 1, borderBottomColor: '#F3F4F6', paddingVertical: 12 }, infoRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }, infoLabel: { fontSize: 16, color: '#6B7280' }, infoValue: { fontSize: 16, fontWeight: '600', color: '#111827', textTransform: 'capitalize' }, valueContainer: { flexDirection: 'row', alignItems: 'center' }, editingContainer: { marginTop: 12 }, input: { backgroundColor: '#F9FAFB', borderWidth: 1, borderColor: '#D1D5DB', borderRadius: 10, padding: 14, fontSize: 16, color: '#111827' }, textArea: { height: 100, textAlignVertical: 'top' }, picker: { backgroundColor: '#F9FAFB', borderWidth: 1, borderColor: '#D1D5DB', borderRadius: 10, padding: 0, margin:0 }, participantRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: '#F3F4F6' }, participantName: { fontSize: 16, fontWeight: '600', color: '#111827' }, participantMobile: { fontSize: 14, color: '#6B7280', marginTop: 4 }, organizerBadge: { backgroundColor: '#FEF3C7', paddingHorizontal: 8, paddingVertical: 3, borderRadius: 12, marginLeft: 10 }, organizerBadgeText: { color: '#92400E', fontSize: 12, fontWeight: '500' }, noParticipantsText: { fontSize: 15, color: '#6B7280', textAlign: 'center', paddingVertical: 20 }, cardActions: { flexDirection: 'row', justifyContent: 'flex-end', marginTop: 20, paddingTop: 16, borderTopWidth: 1, borderTopColor: '#E5E7EB' }, button: { borderRadius: 10, paddingVertical: 12, paddingHorizontal: 20, marginLeft: 10, minWidth: 80, alignItems: 'center', justifyContent: 'center' }, saveButton: { backgroundColor: '#4F46E5' }, cancelButton: { backgroundColor: '#E5E7EB' }, startButton: { backgroundColor: '#22c55e', marginTop: 10 }, startGroupText: { fontSize: 15, color: '#6B7280', lineHeight: 22, marginBottom: 10}, buttonText: { color: '#FFFFFF', fontSize: 15, fontWeight: '600' }, }); export default EditGroupScreen;