group edit fixed

This commit is contained in:
Abin Toppo
2025-10-13 15:13:59 +05:30
parent bb5ae9af4b
commit f6d88d251d
2 changed files with 58 additions and 62 deletions
+55 -52
View File
@@ -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<RootStackParamList, 'EditGroup'>,
@@ -89,16 +93,20 @@ type EditGroupScreenProps = CompositeScreenProps<
>;
type EditingCard = 'groupInfo' | 'financialRules' | 'configuration' | null;
type GroupManagementNavProp = StackNavigationProp<RootStackParamList, 'EditGroup'>;
// --- Main Screen Component ---
const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
const { groupId } = route?.params;
console.log("edit group screen groupId : ", route?.params)
console.log("edit group screen groupId : ", route?.params)
const navigation = useNavigation<GroupManagementNavProp>();
const [editingCard, setEditingCard] = useState<EditingCard>(null);
const [groupDetails, setGroupDetails] = useState<GroupDetails | null>(null);
const [editableData, setEditableData] = useState<EditGroupPayload>({
@@ -114,6 +122,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ 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<EditGroupScreenProps> = ({ route }) => {
};
const fetchGroupDetails = useCallback(async () => {
setLoading(true);
setError(null);
@@ -148,6 +158,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ 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<EditGroupScreenProps> = ({ 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<EditGroupScreenProps> = ({ route }) => {
}
}
console.log("groupid : ", groupId)
useEffect(() => {
getPaymentStatus()
}, [])
useEffect(() => {
fetchGroupDetails();
}, [fetchGroupDetails]);
const handleCancel = () => {
if (groupDetails) {
initializeEditableData(groupDetails);
@@ -197,10 +203,10 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ 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<EditGroupScreenProps> = ({ 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<EditGroupScreenProps> = ({ 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<EditGroupScreenProps> = ({ 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<EditGroupScreenProps> = ({ 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<EditGroupScreenProps> = ({ route }) => {
return (
<View style={styles.fieldContainer}>
<Text style={styles.infoLabel}>{label}</Text>
{isEditing && isEditable ? (
{isEditing ? (
<View style={{ marginTop: 8 }}>{getInputForField()}</View>
) : (
<Text style={styles.infoValue}>{displayValue}</Text>
@@ -337,9 +351,11 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ 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 (
<View style={styles.card}>
@@ -353,6 +369,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
</View>
{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<EditGroupScreenProps> = ({ route }) => {
);
}
if (loading) return <View style={styles.centered}><ActivityIndicator size="large" color="#4F46E5" /></View>;
if (error) return <View style={styles.centered}><Text style={styles.errorText}>{error}</Text></View>;
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<EditGroupScreenProps> = ({ 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<EditGroupScreenProps> = ({ 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<EditGroupScreenProps> = ({ 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<EditGroupScreenProps> = ({ 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<EditGroupScreenProps> = ({ 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<EditGroupScreenProps> = ({ 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 (
<AppView style={{ flex: 1, paddingHorizontal: 0 }}>
@@ -514,7 +514,6 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
)}
</View>
<ScrollView
style={styles.container}
contentContainerStyle={styles.contentContainer}
@@ -523,7 +522,6 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
<View style={[styles.avatarContainer, { flexDirection: 'row', flex: 1, gap: 10 }]}>
<View style={{}}>
<Image
// source={{ uri: groupDetails?.imageBlob ? groupDetails?.imageBlob : selectedImage || "https://img.freepik.com/premium-vector/people-group-frame-icon_791764-2649.jpg" }}
source={{ uri: getProfileImage() }}
style={styles.avatar}
onError={() => console.log("Error loading image")}
@@ -548,6 +546,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ 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<EditGroupScreenProps> = ({ 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')}
<View style={styles.card}>
<Text style={styles.cardTitle}>Participants ({groupDetails.totalParticipants})</Text>
{groupDetails.participants.length > 0 ? (
@@ -590,7 +591,6 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
</View>
</ScrollView>
{canStartGroup && (
<View style={styles.floatingButtonContainer}>
<TouchableOpacity
@@ -606,7 +606,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
</TouchableOpacity>
</View>
)}
{selectedUser?.cycleStatus !== "PENDING_PAYMENTS" && groupDetails.status === 'ACTIVE' && selectedUser?.cycleStatus === "PENDING_SELECTION" &&(
{selectedUser?.cycleStatus !== "PENDING_PAYMENTS" && groupDetails.status === 'ACTIVE' && selectedUser?.cycleStatus === "PENDING_SELECTION" && (
<View style={styles.floatingButtonContainer}>
<TouchableOpacity
style={styles.activateButton}
@@ -624,7 +624,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
}
{
selectedUser?.selectedUser !== null && selectedUser?.cycleStatus === "READY_FOR_WITHDRAWAL" &&
selectedUser?.selectedUser !== null && selectedUser?.cycleStatus === "READY_FOR_WITHDRAWAL" &&
<View style={styles.floatingButtonContainer}>
<CustomButton
buttonBackground={colors?.pastelBlue}
@@ -643,6 +643,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ 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;
@@ -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: {