This commit is contained in:
Jaanu423
2025-10-16 15:23:46 +05:30
parent be6d465a28
commit 79cf7470d4
3 changed files with 292 additions and 236 deletions
+6 -6
View File
@@ -18,8 +18,8 @@ const Header: React.FC<HeaderProps> = ({
showBack = true, showBack = true,
onBackPress, onBackPress,
rightAction, rightAction,
titleColor = colors.primary, titleColor = '#000',
arrowColor = colors.primary arrowColor = '#000'
}) => { }) => {
const navigation = useNavigation(); const navigation = useNavigation();
@@ -33,12 +33,12 @@ const Header: React.FC<HeaderProps> = ({
return ( return (
<View style={styles.header}> <View style={styles.header}>
{/* {showBack && ( {showBack && (
<TouchableOpacity onPress={handleBack} style={{padding:6}}> <TouchableOpacity onPress={handleBack} style={{padding:6}}>
<Ionicons name="arrow-back" size={24} color={arrowColor} /> <Ionicons name="arrow-back" size={24} color={arrowColor} />
</TouchableOpacity> </TouchableOpacity>
)} */} )}
<Text style={[styles.headerTitle, {color:colors.black}]}>{title}</Text> <Text style={[styles.headerTitle, {color:titleColor}]}>{title}</Text>
<View>{rightAction}</View> <View>{rightAction}</View>
</View> </View>
); );
@@ -57,6 +57,6 @@ const styles = StyleSheet.create({
headerTitle: { headerTitle: {
fontSize: 18, fontSize: 18,
fontWeight: "600", fontWeight: "600",
textTransform:'uppercase' textTransform: "capitalize",
}, },
}); });
+2 -11
View File
@@ -97,7 +97,6 @@ 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 {
ToastAndroid.show(`Unknown action: ${item.title}`, ToastAndroid.SHORT); ToastAndroid.show(`Unknown action: ${item.title}`, ToastAndroid.SHORT);
@@ -133,13 +132,12 @@ const Home: React.FC = () => {
<Text style={styles.userName}>{user.firstName || apiUser?.firstName || 'User'}</Text> <Text style={styles.userName}>{user.firstName || apiUser?.firstName || 'User'}</Text>
</View> </View>
<TouchableOpacity onPress={() => navigation.navigate("Notification" as never)}> <TouchableOpacity onPress={() => navigation.navigate("Notification" as never)}>
{/* <Image source={getProfileImageUri()} style={styles.profileImage} /> */} <Text style={{ position: 'absolute', right: -6, top: -4, backgroundColor: colors.danger, color: colors.white, borderRadius: 8, width: 16, height: 16, textAlign: 'center', fontSize: 10, fontWeight: 'bold', lineHeight: 16, zIndex: 1 }}>{unreadCount > 0 ? unreadCount : ''}</Text>
<Text style={{position: 'absolute', right: -6, top: -4, backgroundColor: colors.danger, color: colors.white, borderRadius: 8, width: 16, height: 16, textAlign: 'center', fontSize: 10, fontWeight: 'bold', lineHeight: 16, zIndex: 1}}>{unreadCount > 0 ? unreadCount : ''}</Text>
<Ionicons name="notifications-outline" size={30} color={colors.textDark} /> <Ionicons name="notifications-outline" size={30} color={colors.textDark} />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
<UserProfileStatus /> <UserProfileStatus profileImage={getProfileImageUri()} />
<Text style={styles.sectionTitle}>Quick Actions</Text> <Text style={styles.sectionTitle}>Quick Actions</Text>
@@ -178,13 +176,6 @@ const styles = StyleSheet.create({
fontWeight: 'bold', fontWeight: 'bold',
color: colors.textDark, color: colors.textDark,
}, },
profileImage: {
width: 50,
height: 50,
borderRadius: 25,
borderColor: colors.pastelPink, // Using pastelPink for border
borderWidth: 2,
},
sectionTitle: { sectionTitle: {
fontSize: 20, fontSize: 20,
fontWeight: '600', fontWeight: '600',
+171 -106
View File
@@ -1,19 +1,20 @@
import React, { use, useCallback, useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
import { import {
StyleSheet, StyleSheet,
Text, Text,
View, View,
ActivityIndicator,
TouchableOpacity, TouchableOpacity,
Modal, Modal,
Alert, ToastAndroid,
Image,
Animated,
} from "react-native"; } from "react-native";
import * as Keychain from "react-native-keychain"; import * as Keychain from "react-native-keychain";
import { useFocusEffect, useNavigation } from "@react-navigation/native"; import { useFocusEffect, useNavigation } from "@react-navigation/native";
import { PUT_USER_STATUS } from "../../utils/ApiUrls"; import { PUT_USER_STATUS } from "../../utils/ApiUrls";
import colors from "../../components/colors"; import colors from "../../components/colors";
import LinearGradient from "react-native-linear-gradient"; import LinearGradient from "react-native-linear-gradient";
import Ionicons from "@react-native-vector-icons/ionicons"; import {Ionicons} from "@react-native-vector-icons/ionicons";
export type ApiResponse = { export type ApiResponse = {
message: string; message: string;
@@ -26,26 +27,28 @@ export type ApiResponse = {
}; };
}; };
type UserProfileStatusProps = {
profileImage?: { uri: string };
};
const UserProfileStatus: React.FC = () => { const UserProfileStatus: React.FC<UserProfileStatusProps> = ({ profileImage }) => {
const navigation = useNavigation(); const navigation = useNavigation();
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [statusData, setStatusData] = useState<ApiResponse["data"] | null>(null); const [statusData, setStatusData] = useState<ApiResponse["data"] | null>(null);
const [error, setError] = useState<string | null>(null);
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false);
const [intervalId, setIntervalId] = useState<null | number>(null); const [intervalId, setIntervalId] = useState<null | number>(null);
const fadeAnim = useState(new Animated.Value(0))[0];
const userStatus = async () => { const userStatus = async () => {
try { try {
const credentials = await Keychain.getGenericPassword(); const credentials = await Keychain.getGenericPassword();
if (!credentials) { if (!credentials) {
ToastAndroid.show('You are not logged in', ToastAndroid.SHORT); ToastAndroid.show("You are not logged in", ToastAndroid.SHORT);
return; return;
} }
const token = credentials.password; const token = credentials.password;
setLoading(true); setLoading(true);
setError(null);
const response = await fetch(PUT_USER_STATUS, { const response = await fetch(PUT_USER_STATUS, {
method: "GET", method: "GET",
headers: { headers: {
@@ -55,19 +58,16 @@ const UserProfileStatus: React.FC = () => {
}); });
const json: ApiResponse = await response.json(); const json: ApiResponse = await response.json();
console.log("Profile Status :", json);
if (json.status === "success") { if (json.status === "success") {
setStatusData(json.data); setStatusData(json.data);
if (json.data.profileStatus === false) { if (json.data.profileStatus === false) {
setShowModal(true); setShowModal(true);
} }
} else { } else {
setError(json.message || "Failed to fetch status"); ToastAndroid.show("Failed to fetch status", ToastAndroid.SHORT);
} }
} catch (err) { } catch {
setError("Something went wrong. Please try again."); ToastAndroid.show("Something went wrong", ToastAndroid.SHORT);
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -80,64 +80,108 @@ const UserProfileStatus: React.FC = () => {
); );
useEffect(() => { useEffect(() => {
if (statusData && statusData.profileStatus === false) { Animated.timing(fadeAnim, {
const id = setInterval(() => { toValue: 1,
setShowModal(true); duration: 600,
}, 5000); useNativeDriver: true,
setIntervalId(id); }).start();
return () => clearInterval(id); }, []);
}
}, [statusData]);
const handleGoToProfile = () => { const handleGoToProfile = () => {
// Stop modal repetition
if (intervalId) { if (intervalId) {
clearInterval(intervalId); clearInterval(intervalId);
setIntervalId(null); setIntervalId(null);
} }
setShowModal(false); setShowModal(false);
// Option 1: Mark profile as completed temporarily (so condition stops)
setStatusData(prev => (prev ? { ...prev, profileStatus: true } : prev));
// Navigate to Profile screen
navigation.navigate("Profile" as never); navigation.navigate("Profile" as never);
}; };
return ( const handlePress = (label: string) => {
<View style={styles.container}> ToastAndroid.show(`${label} clicked`, ToastAndroid.SHORT);
{/* <Text style={styles.title}>Welcome Back 👋</Text> */} };
return (
<Animated.View style={[styles.container, { opacity: fadeAnim }]}>
<LinearGradient <LinearGradient
colors={[colors.pastelPink, colors.pastelPink colors={[colors.primary, colors.pastelPink]}
]}
start={{ x: 0, y: 0 }} start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }} end={{ x: 1, y: 1 }}
style={styles.card} style={styles.card}
> >
{/* LEFT SECTION */}
<View style={styles.leftSection}>
<Text style={styles.header}>Profile Overview</Text>
<View style={styles.infoRow}>
<View> <View>
<Text style={styles.cardTitle}>Profile Status</Text> {[
<Text style={{ color: '#fff' }}>Role: {statusData?.role}</Text> "Role",
<Text style={{ color: '#fff' }}>Organizer: {statusData?.organizerStatus}</Text> "Total Active Groups",
<Text style={{ color: '#fff' }}> "Total Joined Groups",
Profile Completed: {statusData?.profileStatus ? "Yes ✅" : "No ❌"} "Total Created Groups",
"Total Due Payment",
"Total Completed Groups",
].map((label, i) => (
<TouchableOpacity
key={i}
style={styles.infoLabelBox}
onPress={() => handlePress(label)}
>
<Text style={styles.infoLabel}>{label}</Text>
</TouchableOpacity>
))}
</View>
<View>
{[
statusData?.role || "-",
"5",
"3",
"2",
"₹1500",
"4",
].map((value, i) => (
<TouchableOpacity
key={i}
style={styles.infoValueBox}
onPress={() => handlePress(value)}
>
<Text style={styles.infoValue}>{value}</Text>
</TouchableOpacity>
))}
</View>
</View>
<View style={styles.statusBox}>
<Text style={styles.statusText}>
Organizer: {statusData?.organizerStatus || "-"}
</Text>
<Text style={styles.statusText}>
Profile Completed:{" "}
{statusData?.profileStatus ? "Yes ✅" : "No ❌"}
</Text> </Text>
</View> </View>
<Ionicons name="person-circle" color={'#fff'} size={100}/> </View>
{/* RIGHT SECTION - IMAGE */}
{/* <TouchableOpacity
onPress={() => navigation.navigate("Profile" as never)}
activeOpacity={0.8}
>
{profileImage ? (
<Image source={{ uri: profileImage.uri }} style={styles.avatar} />
) : (
<Ionicons name="person-circle-outline" size={80} color="#fff" />
)}
</TouchableOpacity> */}
</LinearGradient> </LinearGradient>
{/* Modal */}
{/* Custom Modal */}
{showModal && ( {showModal && (
<Modal <Modal visible transparent animationType="fade">
visible={showModal}
animationType="slide"
transparent
onRequestClose={() => setShowModal(false)}
>
<View style={styles.modalOverlay}> <View style={styles.modalOverlay}>
<View style={styles.modalBox}> <View style={styles.modalBox}>
<Ionicons name="alert-circle" size={50} color={colors.primary} />
<Text style={styles.modalTitle}>Complete Your Profile</Text> <Text style={styles.modalTitle}>Complete Your Profile</Text>
<Text style={styles.modalText}> <Text style={styles.modalText}>
Your profile is incomplete. Please update your details to continue. Your profile is incomplete. Please update your details to continue.
@@ -148,102 +192,123 @@ const UserProfileStatus: React.FC = () => {
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity <TouchableOpacity
style={[styles.button, { backgroundColor: "#ccc", marginTop: 8 }]} style={[styles.button, styles.secondaryButton]}
onPress={() => { onPress={() => setShowModal(false)}
if (intervalId) {
clearInterval(intervalId);
setIntervalId(null);
}
setShowModal(false);
}}
> >
<Text style={[styles.buttonText, { color: "#333" }]}>Close</Text> <Text style={[styles.buttonText, styles.secondaryText]}>Later</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
</View> </View>
</Modal> </Modal>
)} )}
</View> </Animated.View>
); );
}; };
export default UserProfileStatus; export default UserProfileStatus;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
// flex: 1, margin: 12,
// padding: 16,
// backgroundColor: "#f5f7fb",
},
centered: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
title: {
fontSize: 22,
fontWeight: "700",
marginBottom: 20,
}, },
card: { card: {
backgroundColor: "#fff", flexDirection: "row",
padding: 16, justifyContent: "space-between",
borderRadius: 12,
elevation: 3,
shadowOpacity: 0.1,
shadowRadius: 6,
marginBottom: 10,
justifyContent:'space-between',
flexDirection:'row'
},
cardTitle: {
fontSize: 18,
fontWeight: "600",
marginBottom: 10,
color: '#fff'
},
button: {
backgroundColor: "#007AFF",
paddingVertical: 10,
paddingHorizontal: 20,
borderRadius: 8,
alignItems: "center", alignItems: "center",
borderRadius: 16,
padding: 18,
elevation: 5,
shadowColor: "#000",
shadowOpacity: 0.2,
shadowRadius: 6,
}, },
buttonText: { header: {
fontSize: 20,
fontWeight: "700",
color: "#fff",
marginBottom: 10,
},
leftSection: {
flex: 1,
},
infoRow: {
flexDirection: "row",
justifyContent: "space-between",
marginBottom: 10,
},
infoLabelBox: {
marginBottom: 6,
},
infoLabel: {
color: "rgba(255,255,255,0.8)",
fontSize: 13,
},
infoValueBox: {
marginBottom: 6,
alignItems: "flex-end",
},
infoValue: {
color: "#fff", color: "#fff",
fontWeight: "600", fontWeight: "600",
fontSize: 13,
}, },
errorText: { statusBox: {
color: "red", backgroundColor: "rgba(255,255,255,0.15)",
fontSize: 16, padding: 10,
marginBottom: 12, borderRadius: 8,
},
statusText: {
color: "#fff",
fontSize: 13,
},
avatar: {
width: 80,
height: 80,
borderRadius: 40,
borderWidth: 2,
borderColor: "#fff",
}, },
modalOverlay: { modalOverlay: {
flex: 1, flex: 1,
backgroundColor: "rgba(0,0,0,0.4)", backgroundColor: "rgba(0,0,0,0.5)",
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
}, },
modalBox: { modalBox: {
width: "85%", width: "85%",
backgroundColor: "#fff", backgroundColor: "#fff",
borderRadius: 16, borderRadius: 18,
padding: 20, padding: 20,
alignItems: "center", alignItems: "center",
elevation: 5, elevation: 6,
}, },
modalTitle: { modalTitle: {
fontSize: 20, fontSize: 20,
fontWeight: "700", fontWeight: "700",
marginBottom: 10,
color: colors.primary, color: colors.primary,
marginVertical: 8,
}, },
modalText: { modalText: {
fontSize: 14,
color: "#444",
textAlign: "center", textAlign: "center",
marginBottom: 20, color: "#444",
fontSize: 14,
marginBottom: 18,
},
button: {
backgroundColor: colors.primary,
paddingVertical: 10,
paddingHorizontal: 30,
borderRadius: 8,
marginTop: 8,
},
buttonText: {
color: "#fff",
fontWeight: "600",
},
secondaryButton: {
backgroundColor: "#eee",
},
secondaryText: {
color: "#333",
}, },
}); });