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,
onBackPress,
rightAction,
titleColor = colors.primary,
arrowColor = colors.primary
titleColor = '#000',
arrowColor = '#000'
}) => {
const navigation = useNavigation();
@@ -33,12 +33,12 @@ const Header: React.FC<HeaderProps> = ({
return (
<View style={styles.header}>
{/* {showBack && (
{showBack && (
<TouchableOpacity onPress={handleBack} style={{padding:6}}>
<Ionicons name="arrow-back" size={24} color={arrowColor} />
</TouchableOpacity>
)} */}
<Text style={[styles.headerTitle, {color:colors.black}]}>{title}</Text>
)}
<Text style={[styles.headerTitle, {color:titleColor}]}>{title}</Text>
<View>{rightAction}</View>
</View>
);
@@ -57,6 +57,6 @@ const styles = StyleSheet.create({
headerTitle: {
fontSize: 18,
fontWeight: "600",
textTransform:'uppercase'
textTransform: "capitalize",
},
});
+4 -13
View File
@@ -66,8 +66,8 @@ const userActions: ActionItem[] = [
const Home: React.FC = () => {
const user = useSelector((state: any) => state.user);
const notifications = useSelector(
(state: any) => state.notifications.notifications
);
(state: any) => state.notifications.notifications
);
const navigation = useNavigation<TabNavigationProp>();
const dispatch = useDispatch();
@@ -97,7 +97,6 @@ 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);
@@ -133,13 +132,12 @@ const Home: React.FC = () => {
<Text style={styles.userName}>{user.firstName || apiUser?.firstName || 'User'}</Text>
</View>
<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} />
</TouchableOpacity>
</View>
<UserProfileStatus />
<UserProfileStatus profileImage={getProfileImageUri()} />
<Text style={styles.sectionTitle}>Quick Actions</Text>
@@ -178,13 +176,6 @@ const styles = StyleSheet.create({
fontWeight: 'bold',
color: colors.textDark,
},
profileImage: {
width: 50,
height: 50,
borderRadius: 25,
borderColor: colors.pastelPink, // Using pastelPink for border
borderWidth: 2,
},
sectionTitle: {
fontSize: 20,
fontWeight: '600',
+282 -217
View File
@@ -1,249 +1,314 @@
import React, { use, useCallback, useEffect, useState } from "react";
import React, { useCallback, useEffect, useState } from "react";
import {
StyleSheet,
Text,
View,
ActivityIndicator,
TouchableOpacity,
Modal,
Alert,
StyleSheet,
Text,
View,
TouchableOpacity,
Modal,
ToastAndroid,
Image,
Animated,
} from "react-native";
import * as Keychain from "react-native-keychain";
import { useFocusEffect, useNavigation } from "@react-navigation/native";
import { PUT_USER_STATUS } from "../../utils/ApiUrls";
import colors from "../../components/colors";
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 = {
message: string;
status: string;
statusCode: number;
data: {
role: string;
organizerStatus: string;
profileStatus: boolean;
};
message: string;
status: string;
statusCode: number;
data: {
role: string;
organizerStatus: string;
profileStatus: boolean;
};
};
type UserProfileStatusProps = {
profileImage?: { uri: string };
};
const UserProfileStatus: React.FC = () => {
const navigation = useNavigation();
const [loading, setLoading] = useState(true);
const [statusData, setStatusData] = useState<ApiResponse["data"] | null>(null);
const [error, setError] = useState<string | null>(null);
const [showModal, setShowModal] = useState(false);
const [intervalId, setIntervalId] = useState<null | number>(null);
const UserProfileStatus: React.FC<UserProfileStatusProps> = ({ profileImage }) => {
const navigation = useNavigation();
const [loading, setLoading] = useState(true);
const [statusData, setStatusData] = useState<ApiResponse["data"] | null>(null);
const [showModal, setShowModal] = useState(false);
const [intervalId, setIntervalId] = useState<null | number>(null);
const fadeAnim = useState(new Animated.Value(0))[0];
const userStatus = async () => {
try {
const credentials = await Keychain.getGenericPassword();
if (!credentials) {
ToastAndroid.show('You are not logged in', ToastAndroid.SHORT);
return;
}
const token = credentials.password;
const userStatus = async () => {
try {
const credentials = await Keychain.getGenericPassword();
if (!credentials) {
ToastAndroid.show("You are not logged in", ToastAndroid.SHORT);
return;
}
const token = credentials.password;
setLoading(true);
setError(null);
const response = await fetch(PUT_USER_STATUS, {
method: "GET",
headers: {
Accept: "*/*",
Authorization: `Bearer ${token}`,
},
});
setLoading(true);
const response = await fetch(PUT_USER_STATUS, {
method: "GET",
headers: {
Accept: "*/*",
Authorization: `Bearer ${token}`,
},
});
const json: ApiResponse = await response.json();
console.log("Profile Status :", json);
if (json.status === "success") {
setStatusData(json.data);
if (json.data.profileStatus === false) {
setShowModal(true);
}
} else {
setError(json.message || "Failed to fetch status");
}
} catch (err) {
setError("Something went wrong. Please try again.");
} finally {
setLoading(false);
const json: ApiResponse = await response.json();
if (json.status === "success") {
setStatusData(json.data);
if (json.data.profileStatus === false) {
setShowModal(true);
}
};
} else {
ToastAndroid.show("Failed to fetch status", ToastAndroid.SHORT);
}
} catch {
ToastAndroid.show("Something went wrong", ToastAndroid.SHORT);
} finally {
setLoading(false);
}
};
useFocusEffect(
useCallback(() => {
userStatus();
}, [])
);
useFocusEffect(
useCallback(() => {
userStatus();
}, [])
);
useEffect(() => {
if (statusData && statusData.profileStatus === false) {
const id = setInterval(() => {
setShowModal(true);
}, 5000);
setIntervalId(id);
return () => clearInterval(id);
}
}, [statusData]);
useEffect(() => {
Animated.timing(fadeAnim, {
toValue: 1,
duration: 600,
useNativeDriver: true,
}).start();
}, []);
const handleGoToProfile = () => {
// Stop modal repetition
if (intervalId) {
clearInterval(intervalId);
setIntervalId(null);
}
const handleGoToProfile = () => {
if (intervalId) {
clearInterval(intervalId);
setIntervalId(null);
}
setShowModal(false);
navigation.navigate("Profile" as never);
};
setShowModal(false);
const handlePress = (label: string) => {
ToastAndroid.show(`${label} clicked`, ToastAndroid.SHORT);
};
// Option 1: Mark profile as completed temporarily (so condition stops)
setStatusData(prev => (prev ? { ...prev, profileStatus: true } : prev));
return (
<Animated.View style={[styles.container, { opacity: fadeAnim }]}>
<LinearGradient
colors={[colors.primary, colors.pastelPink]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.card}
>
{/* LEFT SECTION */}
<View style={styles.leftSection}>
<Text style={styles.header}>Profile Overview</Text>
// Navigate to Profile screen
navigation.navigate("Profile" as never);
};
return (
<View style={styles.container}>
{/* <Text style={styles.title}>Welcome Back 👋</Text> */}
<LinearGradient
colors={[colors.pastelPink, colors.pastelPink
]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.card}
>
<View>
<Text style={styles.cardTitle}>Profile Status</Text>
<Text style={{ color: '#fff' }}>Role: {statusData?.role}</Text>
<Text style={{ color: '#fff' }}>Organizer: {statusData?.organizerStatus}</Text>
<Text style={{ color: '#fff' }}>
Profile Completed: {statusData?.profileStatus ? "Yes ✅" : "No ❌"}
</Text>
</View>
<Ionicons name="person-circle" color={'#fff'} size={100}/>
</LinearGradient>
{/* Custom Modal */}
{showModal && (
<Modal
visible={showModal}
animationType="slide"
transparent
onRequestClose={() => setShowModal(false)}
<View style={styles.infoRow}>
<View>
{[
"Role",
"Total Active Groups",
"Total Joined Groups",
"Total Created Groups",
"Total Due Payment",
"Total Completed Groups",
].map((label, i) => (
<TouchableOpacity
key={i}
style={styles.infoLabelBox}
onPress={() => handlePress(label)}
>
<View style={styles.modalOverlay}>
<View style={styles.modalBox}>
<Text style={styles.modalTitle}>Complete Your Profile</Text>
<Text style={styles.modalText}>
Your profile is incomplete. Please update your details to continue.
</Text>
<Text style={styles.infoLabel}>{label}</Text>
</TouchableOpacity>
))}
</View>
<TouchableOpacity style={styles.button} onPress={handleGoToProfile}>
<Text style={styles.buttonText}>Go to Profile</Text>
</TouchableOpacity>
<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>
<TouchableOpacity
style={[styles.button, { backgroundColor: "#ccc", marginTop: 8 }]}
onPress={() => {
if (intervalId) {
clearInterval(intervalId);
setIntervalId(null);
}
setShowModal(false);
}}
>
<Text style={[styles.buttonText, { color: "#333" }]}>Close</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
)}
<View style={styles.statusBox}>
<Text style={styles.statusText}>
Organizer: {statusData?.organizerStatus || "-"}
</Text>
<Text style={styles.statusText}>
Profile Completed:{" "}
{statusData?.profileStatus ? "Yes ✅" : "No ❌"}
</Text>
</View>
</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>
{/* Modal */}
{showModal && (
<Modal visible transparent animationType="fade">
<View style={styles.modalOverlay}>
<View style={styles.modalBox}>
<Ionicons name="alert-circle" size={50} color={colors.primary} />
<Text style={styles.modalTitle}>Complete Your Profile</Text>
<Text style={styles.modalText}>
Your profile is incomplete. Please update your details to continue.
</Text>
<TouchableOpacity style={styles.button} onPress={handleGoToProfile}>
<Text style={styles.buttonText}>Go to Profile</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={() => setShowModal(false)}
>
<Text style={[styles.buttonText, styles.secondaryText]}>Later</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
)}
</Animated.View>
);
};
export default UserProfileStatus;
const styles = StyleSheet.create({
container: {
// flex: 1,
// padding: 16,
// backgroundColor: "#f5f7fb",
},
centered: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
title: {
fontSize: 22,
fontWeight: "700",
marginBottom: 20,
},
card: {
backgroundColor: "#fff",
padding: 16,
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",
},
buttonText: {
color: "#fff",
fontWeight: "600",
},
errorText: {
color: "red",
fontSize: 16,
marginBottom: 12,
},
modalOverlay: {
flex: 1,
backgroundColor: "rgba(0,0,0,0.4)",
justifyContent: "center",
alignItems: "center",
},
modalBox: {
width: "85%",
backgroundColor: "#fff",
borderRadius: 16,
padding: 20,
alignItems: "center",
elevation: 5,
},
modalTitle: {
fontSize: 20,
fontWeight: "700",
marginBottom: 10,
color: colors.primary,
},
modalText: {
fontSize: 14,
color: "#444",
textAlign: "center",
marginBottom: 20,
},
container: {
margin: 12,
},
card: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
borderRadius: 16,
padding: 18,
elevation: 5,
shadowColor: "#000",
shadowOpacity: 0.2,
shadowRadius: 6,
},
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",
fontWeight: "600",
fontSize: 13,
},
statusBox: {
backgroundColor: "rgba(255,255,255,0.15)",
padding: 10,
borderRadius: 8,
},
statusText: {
color: "#fff",
fontSize: 13,
},
avatar: {
width: 80,
height: 80,
borderRadius: 40,
borderWidth: 2,
borderColor: "#fff",
},
modalOverlay: {
flex: 1,
backgroundColor: "rgba(0,0,0,0.5)",
justifyContent: "center",
alignItems: "center",
},
modalBox: {
width: "85%",
backgroundColor: "#fff",
borderRadius: 18,
padding: 20,
alignItems: "center",
elevation: 6,
},
modalTitle: {
fontSize: 20,
fontWeight: "700",
color: colors.primary,
marginVertical: 8,
},
modalText: {
textAlign: "center",
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",
},
});