enovate new dashboard lik a real time application

This commit is contained in:
Jaanu423
2025-10-14 11:27:27 +05:30
20 changed files with 540 additions and 326 deletions
+2
View File
@@ -60,6 +60,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: "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: "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 = () => {
@@ -96,6 +97,7 @@ const Home: React.FC = () => {
} else if (item.action === "TRANSACTION") {
navigation.navigate("TransactionList" as never);
} else if (item.action === "NAVIGATE_CREATE_GROUP") {
navigation.navigate("GroupCreation" as never);
} else {
ToastAndroid.show(`Unknown action: ${item.title}`, ToastAndroid.SHORT);
@@ -164,7 +164,7 @@ const CycleDashboard = ({ data }: { data: CycleDashboardData }) => {
</View>
<View style={styles.summaryBox}>
<Text style={styles.summaryLabel}>Cycles</Text>
<Text style={styles.summaryValue}>{data.currentCycle}/{data.totalCycles}</Text>
<Text style={styles.summaryValue}>{data.currentCycle}/{data.totalParticipants}</Text>
</View>
</View>
</View>
+82 -18
View File
@@ -12,16 +12,18 @@ import {
import { useRoute } from "@react-navigation/native";
import AppView from "../../../components/AppView";
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 { apiCall } from "../../../utils/ApiHelper";
import { BASE_URL, CONTRIBUTE, GROUP_ALL_MEMBER } from "../../../utils/ApiUrls";
import ContributionScreen from "../../ContributionScreen";
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 selectedLabel = items.find((item:any) => item.value === selectedValue)?.label || placeholder;
const selectedLabel = items.find((item: any) => item.value === selectedValue)?.label || placeholder;
return (
@@ -31,6 +33,7 @@ const CustomDropdown = ({ items, selectedValue, onValueChange, placeholder = "Se
<Ionicons name='chevron-down' size={22} color={colors.black} />
</TouchableOpacity>
<Modal
transparent
animationType="fade"
@@ -40,7 +43,7 @@ const CustomDropdown = ({ items, selectedValue, onValueChange, placeholder = "Se
<TouchableOpacity style={styles.modalOverlay} onPress={() => setOpen(false)}>
<View style={styles.dropdownListContainer}>
<ScrollView>
{items.map(item => (
{items.map((item: any) => (
<TouchableOpacity
key={item.value}
onPress={() => {
@@ -63,9 +66,11 @@ const CustomDropdown = ({ items, selectedValue, onValueChange, placeholder = "Se
);
};
const GroupDetails = () => {
const route = useRoute<any>();
console.log(" route id : ", route)
const [memberData, setMemberData] = useState([]);
const [loading, setLoading] = useState(false);
@@ -75,6 +80,8 @@ const GroupDetails = () => {
const [cycleDashboardData, setCycleDashboardData] = useState<any>();
const [selectedCycleNumber, setSelectedCycleNumber] = useState(null);
const [cycleItems, setCycleItems] = useState([]);
const [selectedUser, setSelectedUser] = useState() as any
const group = {
id: route.params.item?.id,
@@ -86,14 +93,26 @@ const GroupDetails = () => {
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 () => {
try {
const response = await apiCall(`${BASE_URL}payments/groups/${group.id || route?.params?.groupId}/cycle-dashboard`, "GET") as any;
const responseData = response.data;
setCycleDashboardData(responseData);
console.log("cycledata", responseData)
if (responseData?.cycles) {
const items = responseData.cycles.map(cycle => ({
const items = responseData.cycles.map((cycle: any) => ({
label: `Cycle ${cycle.cycleNumber}`,
value: cycle.cycleNumber
}));
@@ -107,6 +126,7 @@ const GroupDetails = () => {
}
};
const fetchGroupDetails = async () => {
try {
const res = await apiCall(`${GROUP_ALL_MEMBER}?groupId=${group.id || route?.params?.groupId}`, "GET") as any;
@@ -117,6 +137,7 @@ const GroupDetails = () => {
}
};
const handleContribution = async () => {
try {
const res = await apiCall(`${CONTRIBUTE}${group.id || route?.params?.groupId}/calculate-contribution`, 'GET') as any;
@@ -126,6 +147,7 @@ const GroupDetails = () => {
}
};
const fetchAllData = useCallback(async () => {
setLoading(true);
await Promise.all([
@@ -136,22 +158,28 @@ const GroupDetails = () => {
setLoading(false);
}, [group.id || route?.params?.groupId]);
useEffect(() => {
if (group.id || route?.params?.groupId) {
fetchAllData();
getPaymentStatus();
}
}, [group.id || route?.params?.groupId, fetchAllData]);
const onRefresh = useCallback(async () => {
setRefreshing(true);
await fetchAllData();
await getPaymentStatus();
setRefreshing(false);
}, [fetchAllData]);
const confirmPayment = async () => {
try {
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);
onRefresh();
} else {
@@ -162,10 +190,19 @@ const GroupDetails = () => {
}
};
const selectedCycleData = cycleDashboardData?.cycles.find(c => c.cycleNumber === selectedCycleNumber);
const selectedCycleData = cycleDashboardData?.cycles.find((c: any) => c.cycleNumber === selectedCycleNumber);
return (
<AppView paddingHorizontal={0} style={{ flex: 1 }}>
{selectedUser?.firstName && (
<View style={styles.floatingWinnerContainer}>
<Text style={styles.floatingWinnerText}>
Winner: {`${selectedUser.firstName} ${selectedUser.lastName || ''}`}
</Text>
</View>
)}
<ScrollView
refreshControl={
<RefreshControl refreshing={refreshing || loading} onRefresh={onRefresh} colors={[colors.black]} />
@@ -175,21 +212,23 @@ const GroupDetails = () => {
<Header title={group.name || route.name} />
<Text style={styles.description}>{group.description || route.name}</Text>
<View style={styles.statsRow}>
<View style={styles.statBox}>
<Text style={styles.statValue}>{group.members.length ?? 0}</Text>
<Text style={styles.statValue}>{memberData.length ?? 0}</Text>
<Text style={styles.statLabel}>Members</Text>
</View>
<View style={styles.statBox}>
<Text style={styles.statValue}>₹{group.collected}</Text>
<Text style={styles.statValue}>₹{cycleDashboardData?.contributionAmount || group.collected}</Text>
<Text style={styles.statLabel}>Contribution</Text>
</View>
</View>
<View style={styles.statBox}>
<Text style={styles.statValue}>₹{group.goal}</Text>
<Text style={styles.statValue}>₹{cycleDashboardData?.contributionAmount * memberData.length || group.goal}</Text>
<Text style={styles.statLabel}>Total Amount</Text>
</View>
</View>
{cycleItems.length > 0 && (
<CustomDropdown
items={cycleItems}
@@ -198,19 +237,21 @@ const GroupDetails = () => {
placeholder="Select a Cycle"
/>
)}
{selectedCycleData && cycleDashboardData && (
<CycleDashboard data={{ ...cycleDashboardData, cycles: [selectedCycleData] }} />
)}
<View style={styles.membersHeader}>
<Text style={styles.sectionTitle}>Members</Text>
<View style={styles.statusBadge}>
<Text style={styles.statusText}>{group.status}</Text>
<Text style={styles.statusText}>{cycleDashboardData?.status || group?.status}</Text>
</View>
</View>
{memberData.map((member:any) => (
{memberData.map((member: any) => (
<View key={member.userId} style={styles.memberCard}>
<Ionicons name="person" size={40} color={colors.black} />
<View style={styles.memberInfo}>
@@ -221,14 +262,15 @@ const GroupDetails = () => {
</View>
))}
{/* Button at the bottom inside scroll (no absolute positioning) */}
{group.status === 'ACTIVE' && !payable?.hasPaid && (
{cycleDashboardData?.status === 'ACTIVE' && !payable?.hasPaid && (
<TouchableOpacity onPress={() => setOpenModal(true)} style={styles.button}>
<Text style={styles.buttonText}>Contribute</Text>
</TouchableOpacity>
)}
</ScrollView>
{openModal && (
<ContributionScreen
data={payable}
@@ -244,8 +286,10 @@ const GroupDetails = () => {
);
};
export default GroupDetails;
const styles = StyleSheet.create({
dropdownWrapper: {
marginHorizontal: 15,
@@ -261,6 +305,10 @@ const styles = StyleSheet.create({
borderRadius: 8,
backgroundColor: '#fff',
},
headerText: {
fontSize: 15,
color: colors.black,
},
modalOverlay: {
flex: 1,
justifyContent: 'center',
@@ -335,7 +383,7 @@ const styles = StyleSheet.create({
statusText: {
fontSize: 14,
fontWeight: '600',
color: colors.Home,
color: colors.black,
},
memberCard: {
flexDirection: "row",
@@ -373,4 +421,20 @@ const styles = StyleSheet.create({
scrollContent: {
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,
},
});