fund distribution
This commit is contained in:
@@ -21,6 +21,7 @@ import { CompositeScreenProps } from '@react-navigation/native';
|
||||
import { BottomTabScreenProps } from '@react-navigation/bottom-tabs';
|
||||
import { ManagementTabParamList } from './ManagementTabNavigation'; // Adjust path
|
||||
|
||||
|
||||
// --- Type Definitions ---
|
||||
interface Participant {
|
||||
id: string;
|
||||
@@ -30,6 +31,7 @@ interface Participant {
|
||||
role: 'ORGANIZER' | 'USER';
|
||||
}
|
||||
|
||||
|
||||
export interface GroupDetails {
|
||||
id: string;
|
||||
groupName: string;
|
||||
@@ -53,6 +55,7 @@ export interface GroupDetails {
|
||||
participants: Participant[];
|
||||
}
|
||||
|
||||
|
||||
export interface EditGroupPayload {
|
||||
groupName: string;
|
||||
description: string;
|
||||
@@ -66,18 +69,22 @@ export interface EditGroupPayload {
|
||||
lateFeeValue: number | null;
|
||||
}
|
||||
|
||||
|
||||
// Correct props for a screen inside a nested Tab Navigator
|
||||
type EditGroupScreenProps = CompositeScreenProps<
|
||||
BottomTabScreenProps<ManagementTabParamList, 'EditGroup'>,
|
||||
NativeStackScreenProps<RootStackParamList>
|
||||
>;
|
||||
|
||||
|
||||
type EditingCard = 'groupInfo' | 'financialRules' | 'configuration' | null;
|
||||
|
||||
|
||||
// --- Main Screen Component ---
|
||||
const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
||||
const { groupId } = route.params;
|
||||
|
||||
|
||||
const [editingCard, setEditingCard] = useState<EditingCard>(null);
|
||||
const [groupDetails, setGroupDetails] = useState<GroupDetails | null>(null);
|
||||
const [editableData, setEditableData] = useState<EditGroupPayload>({
|
||||
@@ -99,7 +106,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
||||
const [inviting, setInviting] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// ... (All your handler functions: initializeEditableData, fetchGroupDetails, handleCancel, handleSave, etc. remain the same)
|
||||
|
||||
const initializeEditableData = (data: GroupDetails) => {
|
||||
setEditableData({
|
||||
groupName: data.groupName,
|
||||
@@ -115,6 +122,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const fetchGroupDetails = useCallback(async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
@@ -134,10 +142,12 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
||||
}
|
||||
}, [groupId]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
fetchGroupDetails();
|
||||
}, [fetchGroupDetails]);
|
||||
|
||||
|
||||
const handleCancel = () => {
|
||||
if (groupDetails) {
|
||||
initializeEditableData(groupDetails);
|
||||
@@ -145,9 +155,11 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
||||
setEditingCard(null);
|
||||
};
|
||||
|
||||
|
||||
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;
|
||||
@@ -166,9 +178,11 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleStartGroup = async () => {
|
||||
if (!groupDetails) return;
|
||||
|
||||
|
||||
Alert.alert(
|
||||
"Confirm Start",
|
||||
"Are you sure you want to start this group? This action cannot be undone.",
|
||||
@@ -200,6 +214,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
||||
try {
|
||||
const response = await apiCall(`https://exotic-humpback-mistakenly.ngrok-free.app/api/invite/generate?groupId=${groupDetails.id}`, 'GET') as { data?: string };
|
||||
|
||||
|
||||
const inviteLink = response.data;
|
||||
|
||||
if (inviteLink) {
|
||||
@@ -220,12 +235,13 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleUpdateField = (field: keyof EditGroupPayload, value: any) => {
|
||||
setEditableData(prev => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
// ... (renderField and renderCard functions also remain the same)
|
||||
const renderField = (field: keyof EditGroupPayload, label: string, displayValue: string | number, isEditing: boolean) => {
|
||||
|
||||
const renderField = (field: keyof EditGroupPayload, label: string, displayValue: string | number, isEditing: boolean, isEditable: boolean) => {
|
||||
const commonInputProps = { style: styles.input, placeholderTextColor: "#9CA3AF" };
|
||||
|
||||
const getInputForField = () => {
|
||||
@@ -276,6 +292,7 @@ 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;
|
||||
|
||||
|
||||
return (
|
||||
<View style={styles.card}>
|
||||
<View style={styles.cardHeader}>
|
||||
@@ -288,7 +305,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
||||
</View>
|
||||
|
||||
{fields.map(({ field, label, displayValue }) =>
|
||||
renderField(field, label, displayValue(), isEditingThisCard)
|
||||
renderField(field, label, displayValue(), isEditingThisCard, isEditable)
|
||||
)}
|
||||
|
||||
{isEditingThisCard && (
|
||||
@@ -305,16 +322,18 @@ 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;
|
||||
|
||||
|
||||
return (
|
||||
// Use AppView as the root container with flex: 1
|
||||
<AppView style={{ flex: 1, paddingHorizontal: 0 }}>
|
||||
<View style={styles.headerContainer}>
|
||||
<Text style={styles.headerTitle}>Manage Group</Text>
|
||||
@@ -325,6 +344,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
||||
)}
|
||||
</View>
|
||||
|
||||
|
||||
<ScrollView
|
||||
style={styles.container}
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
@@ -335,20 +355,29 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
||||
{ field: 'description', label: 'Description', displayValue: () => groupDetails.description },
|
||||
], 'groupInfo')}
|
||||
|
||||
{renderCard('Financial Rules', [
|
||||
{ field: 'contributionAmount', label: 'Contribution', displayValue: () => `₹${groupDetails.contributionAmount.toLocaleString('en-IN')}` },
|
||||
{ field: 'maxParticipants', label: 'Max Participants', displayValue: () => `${groupDetails.totalParticipants} / ${groupDetails.maxParticipants}` },
|
||||
{ field: 'minParticipants', label: 'Min Participants', displayValue: () => `${groupDetails.minParticipants}` },
|
||||
{ field: 'latePaymentCharge', label: 'Late Fee', displayValue: () => `₹${(groupDetails.latePaymentCharge ?? 0).toLocaleString('en-IN')}` },
|
||||
], 'financialRules')}
|
||||
|
||||
{groupDetails.status === 'ACTIVE' ?
|
||||
renderCard('Financial Rules', [
|
||||
{ field: 'contributionAmount', label: 'Contribution', displayValue: () => `₹${groupDetails.contributionAmount.toLocaleString('en-IN')}` },
|
||||
{ field: 'latePaymentCharge', label: 'Late Fee', displayValue: () => `₹${(groupDetails.latePaymentCharge ?? 0).toLocaleString('en-IN')}` },
|
||||
{ field: 'maxParticipants', label: 'Current Participants', displayValue: () => `${groupDetails.totalParticipants}` },
|
||||
], 'financialRules')
|
||||
:
|
||||
renderCard('Financial Rules', [
|
||||
{ field: 'contributionAmount', label: 'Contribution', displayValue: () => `₹${groupDetails.contributionAmount.toLocaleString('en-IN')}` },
|
||||
{ field: 'maxParticipants', label: 'Max Participants', displayValue: () => `${groupDetails.totalParticipants} / ${groupDetails.maxParticipants}` },
|
||||
{ field: 'minParticipants', label: 'Min Participants', displayValue: () => `${groupDetails.minParticipants}` },
|
||||
{ field: 'latePaymentCharge', label: 'Late Fee', displayValue: () => `₹${(groupDetails.latePaymentCharge ?? 0).toLocaleString('en-IN')}` },
|
||||
], 'financialRules')
|
||||
}
|
||||
|
||||
|
||||
{renderCard('Configuration', [
|
||||
{ field: 'cycleType', label: 'Payment Cycle', displayValue: () => groupDetails.cycleType },
|
||||
{ field: 'customCycleDays', label: 'Custom Days', displayValue: () => `${groupDetails.customCycleDays ?? ''} days` },
|
||||
...(groupDetails.cycleType === 'CUSTOM' ? [{ field: 'customCycleDays' as keyof EditGroupPayload, label: 'Custom Days', displayValue: () => `${groupDetails.customCycleDays} days` }] : []),
|
||||
{ field: 'visibility', label: 'Visibility', displayValue: () => groupDetails.visibility },
|
||||
], 'configuration')}
|
||||
|
||||
{/* Participants Card ... */}
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.cardTitle}>Participants ({groupDetails.totalParticipants})</Text>
|
||||
{groupDetails.participants.length > 0 ? (
|
||||
@@ -369,8 +398,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
{/* --- FLOATING BUTTON AREA --- */}
|
||||
{/* This View is a sibling of ScrollView, allowing it to float on top */}
|
||||
|
||||
{canStartGroup && (
|
||||
<View style={styles.floatingButtonContainer}>
|
||||
<TouchableOpacity
|
||||
@@ -390,10 +418,9 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
||||
);
|
||||
};
|
||||
|
||||
// --- Styles ---
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1, backgroundColor: '#F3F4F6' },
|
||||
// Added more paddingBottom to ensure content doesn't hide behind the button AND the tab bar
|
||||
contentContainer: { padding: 16, paddingTop: 0, paddingBottom: 150 },
|
||||
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
||||
errorText: { color: '#EF4444', fontSize: 16, textAlign: 'center' },
|
||||
@@ -437,15 +464,13 @@ const styles = StyleSheet.create({
|
||||
cancelButton: { backgroundColor: '#E5E7EB' },
|
||||
inviteButton: { backgroundColor: colors.primary },
|
||||
buttonText: { color: '#ffffffff', fontSize: 15, fontWeight: '600' },
|
||||
|
||||
// --- FLOATING BUTTON STYLES ---
|
||||
floatingButtonContainer: {
|
||||
position: 'absolute',
|
||||
bottom: 85, // Positioned above the tab bar
|
||||
bottom: 85,
|
||||
left: 20,
|
||||
right: 20,
|
||||
alignItems: 'center', // Center the button horizontally
|
||||
zIndex: 1, // Ensure it's on top
|
||||
alignItems: 'center',
|
||||
zIndex: 1,
|
||||
},
|
||||
activateButton: {
|
||||
backgroundColor: colors.pastelOrange,
|
||||
@@ -457,7 +482,7 @@ const styles = StyleSheet.create({
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 4,
|
||||
width: '90%', // Adjust width as needed
|
||||
width: '90%',
|
||||
alignItems: 'center',
|
||||
},
|
||||
activateButtonText: {
|
||||
@@ -467,4 +492,5 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
export default EditGroupScreen;
|
||||
|
||||
Reference in New Issue
Block a user