group edit fixed
This commit is contained in:
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user