demo ready
This commit is contained in:
@@ -46,12 +46,14 @@ export type RootStackParamList = {
|
|||||||
TransactionList: undefined
|
TransactionList: undefined
|
||||||
RandomSelectUserScreen: { groupId: string | number, cycleNumber: number };
|
RandomSelectUserScreen: { groupId: string | number, cycleNumber: number };
|
||||||
Notification: undefined;
|
Notification: undefined;
|
||||||
NotificationRedirection:{ data: any };
|
NotificationRedirection: { data: any };
|
||||||
Management:
|
Management:
|
||||||
| { actionTab: "My Groups" }
|
| { actionTab: "My Groups" }
|
||||||
| { actionTab1: "Joined Groups" }
|
| { actionTab1: "Joined Groups" }
|
||||||
| { actionTab2: "Requests" }
|
| { actionTab2: "Requests" }
|
||||||
| { actionTab3: "Your Requests" };
|
| { actionTab3: "Your Requests" }
|
||||||
|
| { groupId: string | number; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<RootStackParamList>();
|
const Stack = createNativeStackNavigator<RootStackParamList>();
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export type RootStackParamList = {
|
|||||||
ManageJoinedGroup: { groupId: string | number };
|
ManageJoinedGroup: { groupId: string | number };
|
||||||
Invite: { groupId: string | number; code: string | number };
|
Invite: { groupId: string | number; code: string | number };
|
||||||
ManagementTabs:any
|
ManagementTabs:any
|
||||||
|
NotificationRedirection: { data: any };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
Alert,
|
Alert,
|
||||||
Image,
|
Image,
|
||||||
|
ToastAndroid,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { useNavigation } from "@react-navigation/native";
|
import { useNavigation } from "@react-navigation/native";
|
||||||
import { CustomInput } from "../components/common/Custom";
|
import { CustomInput } from "../components/common/Custom";
|
||||||
@@ -75,8 +76,8 @@ export default function GroupCreationForm() {
|
|||||||
console.log("Reviewing Payload:", payload);
|
console.log("Reviewing Payload:", payload);
|
||||||
navigation.navigate("GroupReview", { form: payload });
|
navigation.navigate("GroupReview", { form: payload });
|
||||||
} else {
|
} else {
|
||||||
Alert.alert("Incomplete Form", "Please fill out all required fields correctly.");
|
ToastAndroid.show('Please fill out all required fields correctly.', ToastAndroid.SHORT);
|
||||||
setErrors('')
|
setErrors('');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
ScrollView,
|
ScrollView,
|
||||||
Alert,
|
Alert,
|
||||||
RefreshControl,
|
RefreshControl,
|
||||||
|
ToastAndroid,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { FAB } from "react-native-paper";
|
import { FAB } from "react-native-paper";
|
||||||
import { Ionicons } from "@react-native-vector-icons/ionicons";
|
import { Ionicons } from "@react-native-vector-icons/ionicons";
|
||||||
@@ -176,13 +177,12 @@ export default function GroupScreen() {
|
|||||||
console.log("JOIN_GROUP_URL response:", res);
|
console.log("JOIN_GROUP_URL response:", res);
|
||||||
|
|
||||||
if (res.statusCode === 200) {
|
if (res.statusCode === 200) {
|
||||||
Alert.alert("Success", "You requested to join the group.");
|
ToastAndroid.show('You requested to join the group.', ToastAndroid.SHORT);
|
||||||
} else {
|
} else {
|
||||||
// Alert.alert("Error", res.message || "Failed to join group.");
|
ToastAndroid.show(res.message || "Failed to join group.", ToastAndroid.SHORT);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Join Group Error:", error);
|
console.error("Join Group Error:", error);
|
||||||
// Alert.alert("Error", "An unexpected error occurred.");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,14 +18,11 @@ const NotificationRedirection: React.FC = () => {
|
|||||||
// This effect runs once when the component mounts
|
// This effect runs once when the component mounts
|
||||||
if (data) {
|
if (data) {
|
||||||
console.log('🔄 Processing notification data:', data);
|
console.log('🔄 Processing notification data:', data);
|
||||||
|
|
||||||
// --- Your Redirection Logic Goes Here ---
|
// --- Your Redirection Logic Goes Here ---
|
||||||
// Use the 'data' object to decide where to go.
|
// Use the 'data' object to decide where to go.
|
||||||
// For this example, we'll navigate based on 'msgType'.
|
// For this example, we'll navigate based on 'msgType'.
|
||||||
|
|
||||||
let destination: keyof RootStackParamList = 'Home'; // Default destination
|
let destination: keyof RootStackParamList = 'Home'; // Default destination
|
||||||
let params: object | undefined = undefined;
|
let params: object | undefined = undefined;
|
||||||
|
|
||||||
switch (data.msgType) {
|
switch (data.msgType) {
|
||||||
case 'JOIN_REQUEST':
|
case 'JOIN_REQUEST':
|
||||||
destination = 'Home';
|
destination = 'Home';
|
||||||
@@ -62,10 +59,9 @@ const NotificationRedirection: React.FC = () => {
|
|||||||
// Keep the default destination as 'Home'
|
// Keep the default destination as 'Home'
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
console.log(`➡ Redirecting to ${destination} with params:`, params);
|
||||||
// Use replace to prevent the user from navigating back to this loading screen
|
// Use replace to prevent the user from navigating back to this loading screen
|
||||||
navigation.replace(destination, params);
|
navigation.replace(destination, params);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// If no data is passed for some reason, just go to the home screen
|
// If no data is passed for some reason, just go to the home screen
|
||||||
console.warn('⚠ No data found in notification, redirecting to Home.');
|
console.warn('⚠ No data found in notification, redirecting to Home.');
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ const userActions: ActionItem[] = [
|
|||||||
{ id: "1", title: "Joined Groups", subtitle: "See your groups", action: "NAVIGATE_MANAGEMENT", tab: "Joined Groups", iconName: "person-outline", backOne: '#D7E9F7', backTwo: '#82AAE3' },
|
{ id: "1", title: "Joined Groups", subtitle: "See your groups", action: "NAVIGATE_MANAGEMENT", tab: "Joined Groups", iconName: "person-outline", backOne: '#D7E9F7', backTwo: '#82AAE3' },
|
||||||
{ id: "2", title: "Your Requests", subtitle: "Track your requests", action: "NAVIGATE_MANAGEMENT", tab: "Your Requests", iconName: "mail-open-outline", backOne: '#FFF4D2', backTwo: '#FFB344' },
|
{ id: "2", title: "Your Requests", subtitle: "Track your requests", action: "NAVIGATE_MANAGEMENT", tab: "Your Requests", iconName: "mail-open-outline", backOne: '#FFF4D2', backTwo: '#FFB344' },
|
||||||
{ id: "3", title: "Transactions", subtitle: "View your history", action: "TRANSACTION", iconName: "receipt-outline", backOne: '#decbfcff', backTwo: '#B799FF' },
|
{ id: "3", title: "Transactions", subtitle: "View your history", action: "TRANSACTION", iconName: "receipt-outline", backOne: '#decbfcff', backTwo: '#B799FF' },
|
||||||
|
{ id: "4", title: "Create Group", subtitle: "Create a new group", action: "NAVIGATE_CREATE_GROUP", tab: "Requests", iconName: "people-outline", backOne: '#DFF6DD', backTwo: '#88D18A' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const Home: React.FC = () => {
|
const Home: React.FC = () => {
|
||||||
@@ -95,6 +96,7 @@ const Home: React.FC = () => {
|
|||||||
} else if (item.action === "TRANSACTION") {
|
} else if (item.action === "TRANSACTION") {
|
||||||
navigation.navigate("TransactionList" as never);
|
navigation.navigate("TransactionList" as never);
|
||||||
} else if (item.action === "NAVIGATE_CREATE_GROUP") {
|
} else if (item.action === "NAVIGATE_CREATE_GROUP") {
|
||||||
|
|
||||||
navigation.navigate("GroupCreation" as never);
|
navigation.navigate("GroupCreation" as never);
|
||||||
} else {
|
} else {
|
||||||
Alert.alert("Unknown action", item.title);
|
Alert.alert("Unknown action", item.title);
|
||||||
|
|||||||
@@ -12,16 +12,19 @@ import {
|
|||||||
import { useRoute } from "@react-navigation/native";
|
import { useRoute } from "@react-navigation/native";
|
||||||
import AppView from "../../../components/AppView";
|
import AppView from "../../../components/AppView";
|
||||||
import colors from "../../../components/colors";
|
import colors from "../../../components/colors";
|
||||||
import { Ionicons } from "@react-native-vector-icons/ionicons";
|
import Ionicons from '@react-native-vector-icons/ionicons';
|
||||||
import Header from "../../../components/common/Header";
|
import Header from "../../../components/common/Header";
|
||||||
import { apiCall } from "../../../utils/ApiHelper";
|
import { apiCall } from "../../../utils/ApiHelper";
|
||||||
import { BASE_URL, CONTRIBUTE, GROUP_ALL_MEMBER } from "../../../utils/ApiUrls";
|
import { BASE_URL, CONTRIBUTE, GROUP_ALL_MEMBER } from "../../../utils/ApiUrls";
|
||||||
import ContributionScreen from "../../ContributionScreen";
|
import ContributionScreen from "../../ContributionScreen";
|
||||||
import CycleDashboard from "./CycleDashboard";
|
import CycleDashboard from "./CycleDashboard";
|
||||||
|
|
||||||
const CustomDropdown = ({ items, selectedValue, onValueChange, placeholder = "Select an item" }:any) => {
|
|
||||||
|
const CustomDropdown = ({ items, selectedValue, onValueChange, placeholder = "Select an item" }: any) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const selectedLabel = items.find((item:any) => item.value === selectedValue)?.label || placeholder;
|
const selectedLabel = items.find((item: any) => item.value === selectedValue)?.label || placeholder;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.dropdownWrapper}>
|
<View style={styles.dropdownWrapper}>
|
||||||
@@ -30,6 +33,7 @@ const CustomDropdown = ({ items, selectedValue, onValueChange, placeholder = "Se
|
|||||||
<Ionicons name='chevron-down' size={22} color={colors.black} />
|
<Ionicons name='chevron-down' size={22} color={colors.black} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
transparent
|
transparent
|
||||||
animationType="fade"
|
animationType="fade"
|
||||||
@@ -39,7 +43,7 @@ const CustomDropdown = ({ items, selectedValue, onValueChange, placeholder = "Se
|
|||||||
<TouchableOpacity style={styles.modalOverlay} onPress={() => setOpen(false)}>
|
<TouchableOpacity style={styles.modalOverlay} onPress={() => setOpen(false)}>
|
||||||
<View style={styles.dropdownListContainer}>
|
<View style={styles.dropdownListContainer}>
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
{items.map(item => (
|
{items.map((item: any) => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
key={item.value}
|
key={item.value}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
@@ -62,8 +66,12 @@ const CustomDropdown = ({ items, selectedValue, onValueChange, placeholder = "Se
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const GroupDetails = () => {
|
const GroupDetails = () => {
|
||||||
const route = useRoute<any>();
|
const route = useRoute<any>();
|
||||||
|
|
||||||
|
|
||||||
|
console.log(" route id : ", route)
|
||||||
const [memberData, setMemberData] = useState([]);
|
const [memberData, setMemberData] = useState([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [openModal, setOpenModal] = useState(false);
|
const [openModal, setOpenModal] = useState(false);
|
||||||
@@ -72,6 +80,8 @@ const GroupDetails = () => {
|
|||||||
const [cycleDashboardData, setCycleDashboardData] = useState<any>();
|
const [cycleDashboardData, setCycleDashboardData] = useState<any>();
|
||||||
const [selectedCycleNumber, setSelectedCycleNumber] = useState(null);
|
const [selectedCycleNumber, setSelectedCycleNumber] = useState(null);
|
||||||
const [cycleItems, setCycleItems] = useState([]);
|
const [cycleItems, setCycleItems] = useState([]);
|
||||||
|
const [selectedUser, setSelectedUser] = useState() as any
|
||||||
|
|
||||||
|
|
||||||
const group = {
|
const group = {
|
||||||
id: route.params.item?.id,
|
id: route.params.item?.id,
|
||||||
@@ -83,15 +93,26 @@ const GroupDetails = () => {
|
|||||||
members: memberData,
|
members: memberData,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const getPaymentStatus = async () => {
|
||||||
|
const response = await apiCall(`${BASE_URL}payments/group/${group.id || route?.params?.groupId}/payment/paid`, 'GET') as { status: string; data?: { currentCycle: number, allPaid: boolean, selectedUser: any }; message?: string };
|
||||||
|
console.log("paymentStatus", response.data?.selectedUser)
|
||||||
|
if (response.status === 'success') {
|
||||||
|
setSelectedUser(response.data?.selectedUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const getCycleDashboard = async () => {
|
const getCycleDashboard = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await apiCall(`${BASE_URL}payments/groups/${group.id}/cycle-dashboard`, "GET") as any;
|
const response = await apiCall(`${BASE_URL}payments/groups/${group.id || route?.params?.groupId}/cycle-dashboard`, "GET") as any;
|
||||||
console.log("Cycle Dashboard Response: ", response);
|
|
||||||
const responseData = response.data;
|
const responseData = response.data;
|
||||||
setCycleDashboardData(responseData);
|
setCycleDashboardData(responseData);
|
||||||
|
console.log("cycledata", responseData)
|
||||||
if (responseData?.cycles) {
|
if (responseData?.cycles) {
|
||||||
const items = responseData.cycles.map(cycle => ({
|
const items = responseData.cycles.map((cycle: any) => ({
|
||||||
label: `Cycle ${cycle.cycleNumber}`,
|
label: `Cycle ${cycle.cycleNumber}`,
|
||||||
value: cycle.cycleNumber
|
value: cycle.cycleNumber
|
||||||
}));
|
}));
|
||||||
@@ -105,9 +126,10 @@ const GroupDetails = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const fetchGroupDetails = async () => {
|
const fetchGroupDetails = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await apiCall(`${GROUP_ALL_MEMBER}?groupId=${group.id}`, "GET") as any;
|
const res = await apiCall(`${GROUP_ALL_MEMBER}?groupId=${group.id || route?.params?.groupId}`, "GET") as any;
|
||||||
console.log("Group Members Response: ", res);
|
console.log("Group Members Response: ", res);
|
||||||
setMemberData(res.data);
|
setMemberData(res.data);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@@ -115,16 +137,17 @@ const GroupDetails = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleContribution = async () => {
|
const handleContribution = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await apiCall(`${CONTRIBUTE}${group.id}/calculate-contribution`, 'GET') as any;
|
const res = await apiCall(`${CONTRIBUTE}${group.id || route?.params?.groupId}/calculate-contribution`, 'GET') as any;
|
||||||
console.log("Contribution Calculation Response: ", res);
|
|
||||||
setPayable(res.data);
|
setPayable(res.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Error in handleContribution: ", error);
|
console.log("Error in handleContribution: ", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const fetchAllData = useCallback(async () => {
|
const fetchAllData = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@@ -133,24 +156,30 @@ const GroupDetails = () => {
|
|||||||
getCycleDashboard()
|
getCycleDashboard()
|
||||||
]);
|
]);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, [group.id]);
|
}, [group.id || route?.params?.groupId]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (group.id) {
|
if (group.id || route?.params?.groupId) {
|
||||||
fetchAllData();
|
fetchAllData();
|
||||||
|
getPaymentStatus();
|
||||||
}
|
}
|
||||||
}, [group.id, fetchAllData]);
|
}, [group.id || route?.params?.groupId, fetchAllData]);
|
||||||
|
|
||||||
|
|
||||||
const onRefresh = useCallback(async () => {
|
const onRefresh = useCallback(async () => {
|
||||||
setRefreshing(true);
|
setRefreshing(true);
|
||||||
await fetchAllData();
|
await fetchAllData();
|
||||||
|
await getPaymentStatus();
|
||||||
setRefreshing(false);
|
setRefreshing(false);
|
||||||
}, [fetchAllData]);
|
}, [fetchAllData]);
|
||||||
|
|
||||||
|
|
||||||
const confirmPayment = async () => {
|
const confirmPayment = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await apiCall(`${BASE_URL}payments/contribute/${group?.id}?amount=${payable?.totalPayable}`, 'POST') as any;
|
const res = await apiCall(`${BASE_URL}payments/contribute/${group?.id}?amount=${payable?.totalPayable}`, 'POST') as any;
|
||||||
if (res.status === 200 || res.status === 201) {
|
console.log('payment res ', res)
|
||||||
|
if (res.status === 200 || res.status === 201 || res.statusCode === 200) {
|
||||||
ToastAndroid.show('Payment done!', ToastAndroid.SHORT);
|
ToastAndroid.show('Payment done!', ToastAndroid.SHORT);
|
||||||
onRefresh();
|
onRefresh();
|
||||||
} else {
|
} else {
|
||||||
@@ -161,18 +190,28 @@ const GroupDetails = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectedCycleData = cycleDashboardData?.cycles.find(c => c.cycleNumber === selectedCycleNumber);
|
|
||||||
|
const selectedCycleData = cycleDashboardData?.cycles.find((c: any) => c.cycleNumber === selectedCycleNumber);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppView paddingHorizontal={0} style={{ flex: 1 }}>
|
<AppView paddingHorizontal={0} style={{ flex: 1 }}>
|
||||||
|
{selectedUser?.firstName && (
|
||||||
|
<View style={styles.floatingWinnerContainer}>
|
||||||
|
<Text style={styles.floatingWinnerText}>
|
||||||
|
Winner: {`${selectedUser.firstName} ${selectedUser.lastName || ''}`}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
<ScrollView
|
<ScrollView
|
||||||
refreshControl={
|
refreshControl={
|
||||||
<RefreshControl refreshing={refreshing || loading} onRefresh={onRefresh} colors={[colors.black]} />
|
<RefreshControl refreshing={refreshing || loading} onRefresh={onRefresh} colors={[colors.black]} />
|
||||||
}
|
}
|
||||||
contentContainerStyle={styles.scrollContent}
|
contentContainerStyle={styles.scrollContent}
|
||||||
>
|
>
|
||||||
<Header title={cycleDashboardData?.groupName || group?.name} />
|
<Header title={group.name || route.name} />
|
||||||
<Text style={styles.description}>{group.description}</Text>
|
<Text style={styles.description}>{group.description || route.name}</Text>
|
||||||
|
|
||||||
|
|
||||||
<View style={styles.statsRow}>
|
<View style={styles.statsRow}>
|
||||||
<View style={styles.statBox}>
|
<View style={styles.statBox}>
|
||||||
@@ -180,15 +219,16 @@ const GroupDetails = () => {
|
|||||||
<Text style={styles.statLabel}>Members</Text>
|
<Text style={styles.statLabel}>Members</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.statBox}>
|
<View style={styles.statBox}>
|
||||||
<Text style={styles.statValue}>₹{cycleDashboardData?.contributionAmount|| group.collected}</Text>
|
<Text style={styles.statValue}>₹{cycleDashboardData?.contributionAmount || group.collected}</Text>
|
||||||
<Text style={styles.statLabel}>Contribution</Text>
|
<Text style={styles.statLabel}>Contribution</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.statBox}>
|
<View style={styles.statBox}>
|
||||||
<Text style={styles.statValue}>₹{cycleDashboardData?.contributionAmount*memberData.length|| group.goal}</Text>
|
<Text style={styles.statValue}>₹{cycleDashboardData?.contributionAmount * memberData.length || group.goal}</Text>
|
||||||
<Text style={styles.statLabel}>Total Amount</Text>
|
<Text style={styles.statLabel}>Total Amount</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|
||||||
{cycleItems.length > 0 && (
|
{cycleItems.length > 0 && (
|
||||||
<CustomDropdown
|
<CustomDropdown
|
||||||
items={cycleItems}
|
items={cycleItems}
|
||||||
@@ -202,6 +242,7 @@ const GroupDetails = () => {
|
|||||||
<CycleDashboard data={{ ...cycleDashboardData, cycles: [selectedCycleData] }} />
|
<CycleDashboard data={{ ...cycleDashboardData, cycles: [selectedCycleData] }} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
<View style={styles.membersHeader}>
|
<View style={styles.membersHeader}>
|
||||||
<Text style={styles.sectionTitle}>Members</Text>
|
<Text style={styles.sectionTitle}>Members</Text>
|
||||||
<View style={styles.statusBadge}>
|
<View style={styles.statusBadge}>
|
||||||
@@ -209,7 +250,8 @@ const GroupDetails = () => {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{memberData.map((member:any) => (
|
|
||||||
|
{memberData.map((member: any) => (
|
||||||
<View key={member.userId} style={styles.memberCard}>
|
<View key={member.userId} style={styles.memberCard}>
|
||||||
<Ionicons name="person" size={40} color={colors.black} />
|
<Ionicons name="person" size={40} color={colors.black} />
|
||||||
<View style={styles.memberInfo}>
|
<View style={styles.memberInfo}>
|
||||||
@@ -220,7 +262,7 @@ const GroupDetails = () => {
|
|||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Button at the bottom inside scroll (no absolute positioning) */}
|
|
||||||
{cycleDashboardData?.status === 'ACTIVE' && !payable?.hasPaid && (
|
{cycleDashboardData?.status === 'ACTIVE' && !payable?.hasPaid && (
|
||||||
<TouchableOpacity onPress={() => setOpenModal(true)} style={styles.button}>
|
<TouchableOpacity onPress={() => setOpenModal(true)} style={styles.button}>
|
||||||
<Text style={styles.buttonText}>Contribute</Text>
|
<Text style={styles.buttonText}>Contribute</Text>
|
||||||
@@ -228,6 +270,7 @@ const GroupDetails = () => {
|
|||||||
)}
|
)}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
|
||||||
{openModal && (
|
{openModal && (
|
||||||
<ContributionScreen
|
<ContributionScreen
|
||||||
data={payable}
|
data={payable}
|
||||||
@@ -243,8 +286,10 @@ const GroupDetails = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default GroupDetails;
|
export default GroupDetails;
|
||||||
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
dropdownWrapper: {
|
dropdownWrapper: {
|
||||||
marginHorizontal: 15,
|
marginHorizontal: 15,
|
||||||
@@ -260,6 +305,10 @@ const styles = StyleSheet.create({
|
|||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
},
|
},
|
||||||
|
headerText: {
|
||||||
|
fontSize: 15,
|
||||||
|
color: colors.black,
|
||||||
|
},
|
||||||
modalOverlay: {
|
modalOverlay: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -334,7 +383,7 @@ const styles = StyleSheet.create({
|
|||||||
statusText: {
|
statusText: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
color: colors.Home,
|
color: colors.black,
|
||||||
},
|
},
|
||||||
memberCard: {
|
memberCard: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
@@ -372,4 +421,20 @@ const styles = StyleSheet.create({
|
|||||||
scrollContent: {
|
scrollContent: {
|
||||||
paddingBottom: 50,
|
paddingBottom: 50,
|
||||||
},
|
},
|
||||||
|
floatingWinnerContainer: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 80,
|
||||||
|
alignSelf: 'center',
|
||||||
|
backgroundColor: colors.pastelBlue,
|
||||||
|
paddingHorizontal: 15,
|
||||||
|
paddingVertical: 8,
|
||||||
|
borderRadius: 20,
|
||||||
|
elevation: 5,
|
||||||
|
zIndex: 10,
|
||||||
|
},
|
||||||
|
floatingWinnerText: {
|
||||||
|
color: colors.black,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
try {
|
try {
|
||||||
const res = 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)
|
console.log("Edit Group Response: ", res,editableData)
|
||||||
|
|
||||||
Alert.alert('Success', 'Group has been updated.');
|
Alert.alert('Success', 'Group has been updated.');
|
||||||
setEditingCard(null);
|
setEditingCard(null);
|
||||||
await fetchGroupDetails();
|
await fetchGroupDetails();
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ const Payments: React.FC = () => {
|
|||||||
// These are regular functions and do not call hooks inside them, which is correct.
|
// These are regular functions and do not call hooks inside them, which is correct.
|
||||||
const getCycleData = async () => {
|
const getCycleData = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await apiCall(`${API_BASE_URL}/groups/${groupId}/cycle-dashboard`, 'GET') as ApiResponse<any>;
|
const response = await apiCall(`${BASE_URL}/payments/groups/${groupId}/cycle-dashboard`, 'GET') as ApiResponse<any>;
|
||||||
if (response && response.statusCode === 200) {
|
if (response && response.statusCode === 200) {
|
||||||
setCycleData(response.data);
|
setCycleData(response.data);
|
||||||
}
|
}
|
||||||
@@ -131,7 +131,7 @@ const Payments: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const url = `${BASE_URL}groups/${groupId}/cycles/${paymentDetails.currentCycle}/waive/${currentParticipant.userId}?amountToWaive=${amountToWaive}`;
|
const url = `https://api.godemo.in/FundCollect/api/payments/groups/${groupId}/cycles/${paymentDetails.currentCycle}/waive/${currentParticipant.userId}?amountToWaive=${amountToWaive}`;
|
||||||
const response = await apiCall(url, 'PUT', {}) as any;
|
const response = await apiCall(url, 'PUT', {}) as any;
|
||||||
|
|
||||||
if (response && response.statusCode === 200) {
|
if (response && response.statusCode === 200) {
|
||||||
@@ -247,8 +247,8 @@ const Payments: React.FC = () => {
|
|||||||
<Text style={styles.summaryValue}>₹{paymentDetails.contributionAmount}</Text>
|
<Text style={styles.summaryValue}>₹{paymentDetails.contributionAmount}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.summaryBox}>
|
<View style={styles.summaryBox}>
|
||||||
<Text style={styles.summaryLabel}>Total Due</Text>
|
<Text style={styles.summaryLabel}>Total Amount</Text>
|
||||||
<Text style={[styles.summaryValue, { color: colors.danger }]}>₹{paymentDetails.totalDueWithLateFee}</Text>
|
<Text style={[styles.summaryValue, { color: colors.black }]}>₹{paymentDetails.totalDueWithLateFee}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Text style={styles.sectionTitle}>Participants</Text>
|
<Text style={styles.sectionTitle}>Participants</Text>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Ionicons } from '@react-native-vector-icons/ionicons'
|
|||||||
import CustomModal from '../../components/common/CustomModal'
|
import CustomModal from '../../components/common/CustomModal'
|
||||||
import { apiCall } from '../../utils/ApiHelper'
|
import { apiCall } from '../../utils/ApiHelper'
|
||||||
import { BASE_URL } from '../../utils/ApiUrls'
|
import { BASE_URL } from '../../utils/ApiUrls'
|
||||||
|
import colors from '../../components/colors'
|
||||||
|
|
||||||
interface WinnerUserProps {
|
interface WinnerUserProps {
|
||||||
data: {
|
data: {
|
||||||
@@ -111,6 +112,7 @@ const WinnerUser: React.FC<WinnerUserProps> = ({ data, visible, onClose, groupId
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
confirmText={canProceedToWithdrawal ? 'Proceed to Withdraw' : 'Close'}
|
confirmText={canProceedToWithdrawal ? 'Proceed to Withdraw' : 'Close'}
|
||||||
onConfirm={canProceedToWithdrawal ? onConfirm : onClose}
|
onConfirm={canProceedToWithdrawal ? onConfirm : onClose}
|
||||||
|
confirmButton={colors.pastelOrange}
|
||||||
>
|
>
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Animated.View style={{ transform: [{ scale: scaleAnim }] }}>
|
<Animated.View style={{ transform: [{ scale: scaleAnim }] }}>
|
||||||
|
|||||||
@@ -44,22 +44,20 @@ const Notification = () => {
|
|||||||
dispatch(clearAllNotifications());
|
dispatch(clearAllNotifications());
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMarkAsRead = (messageId: string, screen: string) => {
|
const handleMarkAsRead = (messageId: string, screen: string, groupId: string, cycleNumber: string, contributionId: string) => {
|
||||||
console.log("screen type : ", screen);
|
console.log("screen type : ", screen);
|
||||||
handleMarkAsReadAndNavigate(
|
handleMarkAsReadAndNavigate(
|
||||||
messageId,
|
messageId,
|
||||||
screen,
|
screen,
|
||||||
{
|
{
|
||||||
// Pass additional parameters based on notification type
|
groupId: groupId,
|
||||||
groupId: 'ebc27862-acde-4129-b12c-fd4c2966e858',
|
cycleNumber,
|
||||||
cycleNumber: '1',
|
contributionId,
|
||||||
contributionId: '12345',
|
actionTab3: 'Your Requests',
|
||||||
actionTab3: 'Your Requests' ,
|
|
||||||
actionTab1: 'Joined Groups',
|
actionTab1: 'Joined Groups',
|
||||||
GroupDetails: {
|
GroupDetails: {
|
||||||
item: {}
|
item: {}
|
||||||
},
|
},
|
||||||
// etc.
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -240,74 +238,77 @@ const Notification = () => {
|
|||||||
keyExtractor={(item) => item.messageId}
|
keyExtractor={(item) => item.messageId}
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
contentContainerStyle={styles.listContainer}
|
contentContainerStyle={styles.listContainer}
|
||||||
renderItem={({ item }) => (
|
renderItem={({ item }) => {
|
||||||
<TouchableOpacity
|
console.log("noti items : ", item)
|
||||||
onPress={() => handleMarkAsRead(item.messageId, item?.data?.msgType)}
|
return (
|
||||||
activeOpacity={0.7}
|
<TouchableOpacity
|
||||||
>
|
onPress={() => handleMarkAsRead(item.messageId, item?.data?.msgType, item?.data?.groupId, item?.data?.cycleNumber, item?.data?.contributionId)}
|
||||||
<LinearGradient
|
activeOpacity={0.7}
|
||||||
colors={item.read ? ['#f8f9fa', '#e9ecef'] : ['#ffffff', '#f8f9fa']}
|
>
|
||||||
style={[
|
<LinearGradient
|
||||||
styles.notificationCard,
|
colors={item.read ? ['#f8f9fa', '#e9ecef'] : ['#ffffff', '#f8f9fa']}
|
||||||
!item.read && styles.unreadNotification, { borderLeftColor: getNotificationColor(item.data.msgType) }
|
style={[
|
||||||
]}>
|
styles.notificationCard,
|
||||||
<View style={styles.notificationContent}>
|
!item.read && styles.unreadNotification, { borderLeftColor: getNotificationColor(item.data.msgType) }
|
||||||
{/* Icon */}
|
]}>
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.notificationContent}>
|
||||||
<Ionicons
|
{/* Icon */}
|
||||||
name={getNotificationIcon(item.data.msgType)}
|
<View style={styles.iconContainer}>
|
||||||
size={24}
|
<Ionicons
|
||||||
color={getNotificationColor(item.data.msgType)}
|
name={getNotificationIcon(item.data.msgType)}
|
||||||
/>
|
size={24}
|
||||||
</View>
|
color={getNotificationColor(item.data.msgType)}
|
||||||
|
/>
|
||||||
{/* Content */}
|
|
||||||
<View style={styles.textContent}>
|
|
||||||
<View style={styles.headerRow}>
|
|
||||||
<Text style={[
|
|
||||||
styles.title,
|
|
||||||
!item.read && styles.unreadTitle
|
|
||||||
]}>
|
|
||||||
{item.data.title}
|
|
||||||
</Text>
|
|
||||||
<Text style={styles.time}>
|
|
||||||
{formatTime(item.sentTime)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Text style={styles.message}>
|
{/* Content */}
|
||||||
{item.data.body}
|
<View style={styles.textContent}>
|
||||||
</Text>
|
<View style={styles.headerRow}>
|
||||||
|
|
||||||
{item.data.msgType && (
|
|
||||||
<View style={styles.tagContainer}>
|
|
||||||
<Text style={[
|
<Text style={[
|
||||||
styles.tag,
|
styles.title,
|
||||||
{ backgroundColor: getNotificationColor(item.data.msgType) }
|
!item.read && styles.unreadTitle
|
||||||
]}>
|
]}>
|
||||||
{/* {item.data.msgType} */}
|
{item.data.title}
|
||||||
{getFilterDisplayName(item.data.msgType as FilterType)}
|
</Text>
|
||||||
|
<Text style={styles.time}>
|
||||||
|
{formatTime(item.sentTime)}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Actions */}
|
<Text style={styles.message}>
|
||||||
<View style={styles.actionsContainer}>
|
{item.data.body}
|
||||||
{!item.read && (
|
</Text>
|
||||||
<View style={styles.unreadIndicator} />
|
|
||||||
)}
|
{item.data.msgType && (
|
||||||
<TouchableOpacity
|
<View style={styles.tagContainer}>
|
||||||
onPress={() => handleDelete(item.messageId)}
|
<Text style={[
|
||||||
style={styles.deleteButton}
|
styles.tag,
|
||||||
>
|
{ backgroundColor: getNotificationColor(item.data.msgType) }
|
||||||
<Ionicons name="trash-outline" size={20} color="#e63946" />
|
]}>
|
||||||
</TouchableOpacity>
|
{/* {item.data.msgType} */}
|
||||||
|
{getFilterDisplayName(item.data.msgType as FilterType)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Actions */}
|
||||||
|
<View style={styles.actionsContainer}>
|
||||||
|
{!item.read && (
|
||||||
|
<View style={styles.unreadIndicator} />
|
||||||
|
)}
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => handleDelete(item.messageId)}
|
||||||
|
style={styles.deleteButton}
|
||||||
|
>
|
||||||
|
<Ionicons name="trash-outline" size={20} color="#e63946" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</LinearGradient>
|
||||||
</LinearGradient>
|
</TouchableOpacity>
|
||||||
</TouchableOpacity>
|
)
|
||||||
)}
|
}}
|
||||||
ListEmptyComponent={
|
ListEmptyComponent={
|
||||||
<View style={styles.emptyContainer}>
|
<View style={styles.emptyContainer}>
|
||||||
<Ionicons name="notifications-off-outline" size={64} color="#ccc" />
|
<Ionicons name="notifications-off-outline" size={64} color="#ccc" />
|
||||||
|
|||||||
@@ -161,13 +161,13 @@ const Profile = () => {
|
|||||||
// Alert.alert("Success", "Profile updated successfully");
|
// Alert.alert("Success", "Profile updated successfully");
|
||||||
// setEditing(false);
|
// setEditing(false);
|
||||||
// fetchUserProfile();
|
// fetchUserProfile();
|
||||||
// if (result.status === "success") {
|
if (result.status === "success") {
|
||||||
// Alert.alert("Success", "Profile updated successfully");
|
Alert.alert("Success", "Profile updated successfully");
|
||||||
// setEditing(false);
|
setEditing(false);
|
||||||
// fetchUserProfile(); // reload updated data
|
fetchUserProfile(); // reload updated data
|
||||||
// } else {
|
} else {
|
||||||
// Alert.alert("Error", "Failed to update profile");
|
Alert.alert("Error", "Failed to update profile");
|
||||||
// }
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ Error updating profile:", error);
|
console.error("❌ Error updating profile:", error);
|
||||||
// Alert.alert("Error", "Could not update profile");
|
// Alert.alert("Error", "Could not update profile");
|
||||||
@@ -309,7 +309,7 @@ const Profile = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await deleteFcmToken();
|
await deleteFcmToken(user?.userId?.toString());
|
||||||
|
|
||||||
// Clear stored credentials
|
// Clear stored credentials
|
||||||
await Keychain.resetGenericPassword();
|
await Keychain.resetGenericPassword();
|
||||||
@@ -463,11 +463,6 @@ const Profile = () => {
|
|||||||
<View>
|
<View>
|
||||||
<Text style={styles.sectionTitle}>Personal Information</Text>
|
<Text style={styles.sectionTitle}>Personal Information</Text>
|
||||||
<Text style={{ color: "#64748b", marginBottom: 16 }}>Become Organizer</Text>
|
<Text style={{ color: "#64748b", marginBottom: 16 }}>Become Organizer</Text>
|
||||||
{/* <View style={{ marginBottom: 16 }}>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</View> */}
|
|
||||||
</View>
|
</View>
|
||||||
<View>
|
<View>
|
||||||
<TouchableOpacity onPress={() => navigation.navigate('Transaction')}>
|
<TouchableOpacity onPress={() => navigation.navigate('Transaction')}>
|
||||||
|
|||||||
@@ -176,10 +176,7 @@ export const getFcmToken = async (userId: string) => {
|
|||||||
console.log('FCM Token:', token);
|
console.log('FCM Token:', token);
|
||||||
|
|
||||||
// Send token to backend with user ID
|
// Send token to backend with user ID
|
||||||
await apiCall(STORE_FCM_TOKEN, 'POST', {
|
await apiCall(`${STORE_FCM_TOKEN}${token}`, 'POST');
|
||||||
token,
|
|
||||||
userId
|
|
||||||
});
|
|
||||||
console.log('FCM Token sent to backend', token);
|
console.log('FCM Token sent to backend', token);
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
@@ -239,6 +236,7 @@ const processAndAddNotification = (remoteMessage: any) => {
|
|||||||
title: title,
|
title: title,
|
||||||
additionalProp1: data?.additionalProp1 || '',
|
additionalProp1: data?.additionalProp1 || '',
|
||||||
additionalProp2: data?.additionalProp2 || '',
|
additionalProp2: data?.additionalProp2 || '',
|
||||||
|
groupId: data?.groupId || '',
|
||||||
...data // Spread any additional data
|
...data // Spread any additional data
|
||||||
},
|
},
|
||||||
from: from || 'unknown',
|
from: from || 'unknown',
|
||||||
|
|||||||
Reference in New Issue
Block a user