From f6d88d251d799a2adc8eeda32e0ffa7d6b84168b Mon Sep 17 00:00:00 2001 From: Abin Toppo Date: Mon, 13 Oct 2025 15:13:59 +0530 Subject: [PATCH] group edit fixed --- src/screens/management/EditGroupScreen.tsx | 107 +++++++++--------- .../management/RandomSelectUserScreen.tsx | 13 +-- 2 files changed, 58 insertions(+), 62 deletions(-) diff --git a/src/screens/management/EditGroupScreen.tsx b/src/screens/management/EditGroupScreen.tsx index 11aa315..3dbf815 100644 --- a/src/screens/management/EditGroupScreen.tsx +++ b/src/screens/management/EditGroupScreen.tsx @@ -32,6 +32,7 @@ import { StackNavigationProp } from '@react-navigation/stack'; import WinnerUser from './WinnerUser'; + // --- Type Definitions --- interface Participant { id: string; @@ -42,6 +43,7 @@ interface Participant { } + export interface GroupDetails { id: string; groupName: string; @@ -67,11 +69,12 @@ export interface GroupDetails { } + export interface EditGroupPayload { groupName: string; description: string; maxParticipants: number; - minParticipants?:number; + minParticipants?: number; contributionAmount: number; customCycleDays: number | null; cycleType: 'QUARTERLY' | 'MONTHLY' | 'WEEKLY' | 'CUSTOM'; @@ -82,6 +85,7 @@ export interface EditGroupPayload { } + // Correct props for a screen inside a nested Tab Navigator type EditGroupScreenProps = CompositeScreenProps< BottomTabScreenProps, @@ -89,16 +93,20 @@ type EditGroupScreenProps = CompositeScreenProps< >; + type EditingCard = 'groupInfo' | 'financialRules' | 'configuration' | null; + type GroupManagementNavProp = StackNavigationProp; // --- Main Screen Component --- const EditGroupScreen: React.FC = ({ route }) => { const { groupId } = route?.params; - console.log("edit group screen groupId : ", route?.params) + + console.log("edit group screen groupId : ", route?.params) const navigation = useNavigation(); + const [editingCard, setEditingCard] = useState(null); const [groupDetails, setGroupDetails] = useState(null); const [editableData, setEditableData] = useState({ @@ -114,6 +122,7 @@ const EditGroupScreen: React.FC = ({ route }) => { lateFeeValue: null, }); + const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [starting, setStarting] = useState(false); @@ -141,6 +150,7 @@ const EditGroupScreen: React.FC = ({ route }) => { }; + const fetchGroupDetails = useCallback(async () => { setLoading(true); setError(null); @@ -148,6 +158,7 @@ const EditGroupScreen: React.FC = ({ route }) => { const response = await apiCall(`${BASE_URL}fund/group/details?groupId=${groupId}`, 'GET') as { status: string; data?: GroupDetails; message?: string }; console.log("groupDetails", response) + if (response.status === 'success' && response.data) { setGroupDetails(response.data); initializeEditableData(response.data); @@ -160,14 +171,6 @@ const EditGroupScreen: React.FC = ({ route }) => { setLoading(false); } }, [groupId]); - // const getPaymentStatus = async()=>{ - // const response = await apiCall(`${BASE_URL}payments/group/${groupId}/payment/paid`, 'GET') as { status: string; data?: { currentCycle: number ,status: boolean }; message?: string }; - // console.log("paymentStatus",response) - // if (response.status === 'success') { - // setPaymentStatus(response.data?.status || false); - // setCurrentCycle(response.data?.currentCycle || null); - // } - // } const getPaymentStatus = async () => { const response = await apiCall(`${BASE_URL}payments/group/${groupId}/payment/paid`, 'GET') as { status: string; data?: { currentCycle: number, allPaid: boolean, selectedUser: any }; message?: string }; @@ -179,16 +182,19 @@ const EditGroupScreen: React.FC = ({ route }) => { } } + console.log("groupid : ", groupId) useEffect(() => { getPaymentStatus() }, []) + useEffect(() => { fetchGroupDetails(); }, [fetchGroupDetails]); + const handleCancel = () => { if (groupDetails) { initializeEditableData(groupDetails); @@ -197,10 +203,10 @@ const EditGroupScreen: React.FC = ({ route }) => { }; + const handleSave = async () => { if (!groupDetails) return; - if (editableData.maxParticipants < groupDetails.totalParticipants) { Alert.alert('Invalid Input', `Max participants cannot be less than the current count (${groupDetails.totalParticipants}).`); return; @@ -208,7 +214,8 @@ const EditGroupScreen: React.FC = ({ route }) => { setSaving(true); try { - await apiCall(`${BASE_URL}fund/groups/${groupDetails.id}/edit`, 'PUT', editableData); + const res = await apiCall(`${BASE_URL}fund/groups/${groupDetails.id}/edit`, 'PUT', editableData); + console.log("Edit Group Response: ", res,editableData) Alert.alert('Success', 'Group has been updated.'); setEditingCard(null); await fetchGroupDetails(); @@ -220,16 +227,19 @@ const EditGroupScreen: React.FC = ({ route }) => { }; + const handleDistribute = async () => { - navigation.navigate('RandomSelectUserScreen', { groupId , cycleNumber: currentCycle || 0 }); + navigation.navigate('RandomSelectUserScreen', { groupId, cycleNumber: currentCycle || 0 }); }; + const handleStartGroup = async () => { if (!groupDetails) return; + Alert.alert( "Confirm Start", "Are you sure you want to start this group? This action cannot be undone.", @@ -256,13 +266,13 @@ const EditGroupScreen: React.FC = ({ route }) => { ); }; + const handleInvite = async () => { if (!groupDetails) return; setInviting(true); try { const response = await apiCall(`${BASE_URL}invite/generate?groupId=${groupDetails.id}`, 'GET') as { data?: string }; - const inviteLink = response.data; if (inviteLink) { @@ -284,12 +294,16 @@ const EditGroupScreen: React.FC = ({ route }) => { }; + const handleUpdateField = (field: keyof EditGroupPayload, value: any) => { setEditableData(prev => ({ ...prev, [field]: value })); }; - const renderField = (field: keyof EditGroupPayload, label: string, displayValue: string | number, isEditing: boolean, isEditable?: boolean) => { + // --- CORRECTED FUNCTION --- + // The 'isEditable' parameter was removed, and the condition now correctly + // uses 'isEditing' to show the input fields. + const renderField = (field: keyof EditGroupPayload, label: string, displayValue: string | number, isEditing: boolean) => { const commonInputProps = { style: styles.input, placeholderTextColor: "#9CA3AF" }; const getInputForField = () => { @@ -328,7 +342,7 @@ const EditGroupScreen: React.FC = ({ route }) => { return ( {label} - {isEditing && isEditable ? ( + {isEditing ? ( {getInputForField()} ) : ( {displayValue} @@ -337,9 +351,11 @@ const EditGroupScreen: React.FC = ({ route }) => { ); } + const renderCard = (title: string, fields: Array<{ field: keyof EditGroupPayload, label: string, displayValue: () => string | number }>, cardKey: EditingCard) => { const isEditingThisCard = editingCard === cardKey; - + // This isEditable check correctly controls the visibility of the "Update" button itself. + const isEditable = groupDetails.status === 'PENDING' || groupDetails.status === 'APPROVED'; return ( @@ -353,6 +369,7 @@ const EditGroupScreen: React.FC = ({ route }) => { {fields.map(({ field, label, displayValue }) => + // The call now correctly matches the updated renderField signature renderField(field, label, displayValue(), isEditingThisCard) )} @@ -370,28 +387,17 @@ const EditGroupScreen: React.FC = ({ route }) => { ); } + if (loading) return ; if (error) return {error}; if (!groupDetails) return null; - const isEditable = groupDetails.status === 'PENDING' || groupDetails.status === 'APPROVED'; const isInvitable = groupDetails.status === 'APPROVED'; const canStartGroup = groupDetails.status === 'APPROVED' && groupDetails.totalParticipants >= groupDetails.minParticipants; + console.log(" myu status : ", groupDetails.status) - // const pickImage = () => { - // launchImageLibrary({ mediaType: "photo", quality: 0.5 }, (response) => { - // if (response.didCancel) return; - // if (response.errorCode) { - // Alert.alert("Error", response.errorMessage || "Something went wrong"); - // } else if (response.assets && response.assets.length > 0) { - // const selectedImage = response.assets[0]; - // console.log(selectedImage?.uri) - // setSelectedImage(selectedImage?.uri) - // } - // }); - // }; const pickImage = () => { launchImageLibrary({ mediaType: "photo", quality: 0.5 }, (response) => { @@ -411,7 +417,6 @@ const EditGroupScreen: React.FC = ({ route }) => { const maxSizeKB = 1000; const maxSizeBytes = maxSizeKB * 1024; - // Size validation if (selectedImage.fileSize && selectedImage.fileSize > maxSizeBytes) { const currentSizeKB = (selectedImage.fileSize / 1024).toFixed(2); Alert.alert( @@ -421,23 +426,15 @@ const EditGroupScreen: React.FC = ({ route }) => { return; } - // Additional validation for required fields if (!selectedImage.uri) { Alert.alert("Error", "Invalid image file"); return; } - - // console.log("✅ Image selected:", { - // uri: selectedImage.uri, - // size: (selectedImage?.fileSize / 1024).toFixed(2) + "KB", - // width: selectedImage.width, - // height: selectedImage.height - // }); - setSelectedImage(selectedImage.uri); }); }; + const handleUpdateImage = async () => { try { const credentials = await Keychain.getGenericPassword(); @@ -447,16 +444,18 @@ const EditGroupScreen: React.FC = ({ route }) => { } const token = credentials.password; + if (!selectedImage) { Alert.alert("Error", "Please select an image first"); return; } + setImageUpdating(true); - // Get file extension and mime type dynamically const fileExtension = selectedImage.split('.').pop(); + const formData = new FormData(); formData.append("file", { uri: selectedImage, @@ -464,12 +463,13 @@ const EditGroupScreen: React.FC = ({ route }) => { name: `group_${groupId}_profile.${fileExtension || "jpg"}`, }); + const url = `${UPLOAD_GROUP_IMAGE}${groupId}/upload-image`; + console.log("url : ", url) console.log("formdata : ", formData) - // return const response = await fetch(url, { method: "PUT", headers: { @@ -479,20 +479,22 @@ const EditGroupScreen: React.FC = ({ route }) => { body: formData, }); + console.log("image response : ", response) if (response?.status === 200) { fetchGroupDetails() ToastAndroid.show('Group uploaded successfully', ToastAndroid.SHORT) } + } catch (error: any) { console.error("❌ Error updating group image:", error); - // Alert.alert("Error", error.message || "Could not update group image"); } finally { setImageUpdating(false); } }; + const getProfileImage = () => { return selectedImage ? selectedImage @@ -500,8 +502,6 @@ const EditGroupScreen: React.FC = ({ route }) => { ? `data:image/jpeg;base64,${groupDetails.imageBlob}` : "https://img.freepik.com/premium-vector/people-group-frame-icon_791764-2649.jpg"; }; - // Helper function to get MIME type - return ( @@ -514,7 +514,6 @@ const EditGroupScreen: React.FC = ({ route }) => { )} - = ({ route }) => { console.log("Error loading image")} @@ -548,6 +546,7 @@ const EditGroupScreen: React.FC = ({ route }) => { ], 'groupInfo')} + {groupDetails.status === 'ACTIVE' ? renderCard('Financial Rules', [ { field: 'contributionAmount', label: 'Contribution', displayValue: () => `₹${groupDetails.contributionAmount.toLocaleString('en-IN')}` }, @@ -564,12 +563,14 @@ const EditGroupScreen: React.FC = ({ route }) => { } + {renderCard('Configuration', [ { field: 'cycleType', label: 'Payment Cycle', displayValue: () => groupDetails.cycleType }, ...(groupDetails.cycleType === 'CUSTOM' ? [{ field: 'customCycleDays' as keyof EditGroupPayload, label: 'Custom Days', displayValue: () => `${groupDetails.customCycleDays} days` }] : []), { field: 'visibility', label: 'Visibility', displayValue: () => groupDetails.visibility }, ], 'configuration')} + Participants ({groupDetails.totalParticipants}) {groupDetails.participants.length > 0 ? ( @@ -590,7 +591,6 @@ const EditGroupScreen: React.FC = ({ route }) => { - {canStartGroup && ( = ({ route }) => { )} - {selectedUser?.cycleStatus !== "PENDING_PAYMENTS" && groupDetails.status === 'ACTIVE' && selectedUser?.cycleStatus === "PENDING_SELECTION" &&( + {selectedUser?.cycleStatus !== "PENDING_PAYMENTS" && groupDetails.status === 'ACTIVE' && selectedUser?.cycleStatus === "PENDING_SELECTION" && ( = ({ route }) => { } { - selectedUser?.selectedUser !== null && selectedUser?.cycleStatus === "READY_FOR_WITHDRAWAL" && + selectedUser?.selectedUser !== null && selectedUser?.cycleStatus === "READY_FOR_WITHDRAWAL" && = ({ route }) => { }; + const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#F3F4F6' }, contentContainer: { padding: 16, paddingTop: 0, paddingBottom: 150 }, @@ -660,6 +661,7 @@ const styles = StyleSheet.create({ }, + avatarContainer: { position: "relative", marginVertical: 16, @@ -695,8 +697,8 @@ const styles = StyleSheet.create({ marginBottom: 12, }, cardTitle: { fontSize: 18, fontWeight: 'bold', color: '#1F2937' }, - editButton: { padding: 8,backgroundColor:colors.pastelOrange, borderRadius: 15,elevation:5 }, - editButtonText: { color: colors.black, fontSize: 15, fontWeight: 'bold' }, + editButton: { padding: 8, backgroundColor: colors.pastelOrange, borderRadius: 15, elevation: 5 }, + editButtonText: { color: colors.black, fontSize: 15, fontWeight: 'bold' }, fieldContainer: { borderBottomWidth: 1, borderBottomColor: '#F3F4F6', paddingBottom: 12, marginBottom: 12 }, infoLabel: { fontSize: 16, color: '#6B7280', marginBottom: 4 }, infoValue: { fontSize: 16, fontWeight: '600', color: '#111827', textTransform: 'capitalize' }, @@ -744,4 +746,5 @@ const styles = StyleSheet.create({ }); + export default EditGroupScreen; diff --git a/src/screens/management/RandomSelectUserScreen.tsx b/src/screens/management/RandomSelectUserScreen.tsx index 9614821..a669308 100644 --- a/src/screens/management/RandomSelectUserScreen.tsx +++ b/src/screens/management/RandomSelectUserScreen.tsx @@ -306,16 +306,9 @@ const styles = StyleSheet.create({ button: { paddingVertical: 16, paddingHorizontal: 50, - backgroundColor: '#4f46e5', + backgroundColor: colors.pastelOrange, borderRadius: 30, - elevation: 3, - shadowColor: "#4f46e5", - shadowOffset: { - width: 0, - height: 4, - }, - shadowOpacity: 0.3, - shadowRadius: 4.65, + elevation: 5, }, buttonDisabled: { backgroundColor: '#9ca3af', @@ -323,7 +316,7 @@ const styles = StyleSheet.create({ }, buttonText: { fontSize: 18, - color: '#fff', + color: colors.black, fontWeight: 'bold', }, winnerTextContainer: {