Merge branch 'pooja' of https://git.zeptic.in/abintoppo_lincpay/CircleCollect into feature_profile
This commit is contained in:
Generated
+1
-1
@@ -19,7 +19,7 @@
|
||||
"@react-navigation/native": "^7.1.17",
|
||||
"@react-navigation/native-stack": "^7.3.26",
|
||||
"@react-navigation/stack": "^7.4.8",
|
||||
"axios": "^1.11.0",
|
||||
"axios": "^1.12.2",
|
||||
"install": "^0.13.0",
|
||||
"npm": "^11.6.0",
|
||||
"react": "19.1.0",
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
"@react-navigation/native": "^7.1.17",
|
||||
"@react-navigation/native-stack": "^7.3.26",
|
||||
"@react-navigation/stack": "^7.4.8",
|
||||
"axios": "^1.11.0",
|
||||
"axios": "^1.12.2",
|
||||
"install": "^0.13.0",
|
||||
"npm": "^11.6.0",
|
||||
"react": "19.1.0",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// colors.ts
|
||||
const colors = {
|
||||
pastelPink: "#decbfcff", // darker pink
|
||||
pastelBlue: "#D7E9F7", // darker blue
|
||||
pastelGreen: "#DFF6DD", // darker green
|
||||
pastelOrange: "#FFF4D2", // darker orange
|
||||
|
||||
// darker accent colors
|
||||
Home: "#995dfaff", // Rose red for contrast
|
||||
Profile: "#2D9CDB", // Deep sky blue
|
||||
Group: "#27AE60", // Green for freshness
|
||||
Settings: "#F39C12", // Amber for warmth
|
||||
// common text + UI colors
|
||||
textDark: "#212121",
|
||||
textLight: "#555555",
|
||||
white: "#FFFFFF",
|
||||
black: "#000000",
|
||||
primary: "#007AFF",
|
||||
};
|
||||
|
||||
export default colors;
|
||||
@@ -5,48 +5,46 @@ import {
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
TextInputProps,
|
||||
} from "react-native";
|
||||
import { TextInputProps } from "react-native";
|
||||
import {Ionicons} from "@react-native-vector-icons/ionicons";
|
||||
import {Ionicons, IoniconsIconName} from "@react-native-vector-icons/ionicons";
|
||||
import colors from "../colors";
|
||||
|
||||
// --- Custom Input ---
|
||||
type CustomInputProps = TextInputProps & {
|
||||
label: string;
|
||||
value: string;
|
||||
placeholder: string;
|
||||
onChangeText: (text: string) => void;
|
||||
icon?: string;
|
||||
label?: string;
|
||||
icon?: IoniconsIconName;
|
||||
};
|
||||
|
||||
const CustomInput: React.FC<CustomInputProps> = ({
|
||||
label,
|
||||
value,
|
||||
placeholder,
|
||||
onChangeText,
|
||||
icon,
|
||||
style,
|
||||
...rest
|
||||
}) => (
|
||||
<View style={styles.inputContainer}>
|
||||
<Text style={styles.inputLabel}>{label}</Text>
|
||||
<View style={styles.inputWrapper}>
|
||||
{icon && <Ionicons name={icon} size={20} color="#555" style={styles.inputIcon} />}
|
||||
<TextInput
|
||||
style={[styles.input, icon && { paddingLeft: 36 }]}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
placeholderTextColor="#999"
|
||||
onChangeText={onChangeText}
|
||||
{...rest}
|
||||
/>
|
||||
}) => {
|
||||
return (
|
||||
<View style={styles.inputContainer}>
|
||||
{label ? <Text style={styles.inputLabel}>{label}</Text> : null}
|
||||
<View style={styles.inputWrapper}>
|
||||
{icon && <Ionicons name={icon} size={20} color={colors.textDark} style={styles.inputIcon} />}
|
||||
<TextInput
|
||||
style={[styles.input, icon && { paddingLeft: 36 }, style]}
|
||||
placeholderTextColor={colors.textLight}
|
||||
{...rest}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
// --- Custom Button ---
|
||||
type CustomButtonProps = {
|
||||
title: string;
|
||||
onPress: () => void;
|
||||
type?: "primary" | "secondary" | "danger" | "outline";
|
||||
icon?: string | null;
|
||||
type?: "primary" | "secondary" | "danger";
|
||||
icon?: IoniconsIconName;
|
||||
style?: object;
|
||||
buttonBackground?: string;
|
||||
};
|
||||
|
||||
const CustomButton: React.FC<CustomButtonProps> = ({
|
||||
@@ -54,77 +52,37 @@ const CustomButton: React.FC<CustomButtonProps> = ({
|
||||
onPress,
|
||||
type = "primary",
|
||||
icon,
|
||||
}) => (
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.button,
|
||||
type === "secondary" && styles.buttonSecondary,
|
||||
type === "danger" && styles.buttonDanger,
|
||||
]}
|
||||
onPress={onPress}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<View style={styles.buttonContent}>
|
||||
{icon && <Ionicons name={icon || 'user'} size={20} color={type === "secondary" ? "#333" : "#fff"} style={{ marginRight: 8 }} />}
|
||||
<Text
|
||||
style={[
|
||||
styles.buttonText,
|
||||
type === "secondary" && styles.buttonTextSecondary,
|
||||
type === "danger" && styles.buttonTextDanger,
|
||||
]}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
style = {},
|
||||
buttonBackground,
|
||||
}) => {
|
||||
const backgroundColor =
|
||||
buttonBackground ??
|
||||
(type === "secondary"
|
||||
? colors.pastelBlue
|
||||
: type === "danger"
|
||||
? colors.pastelOrange
|
||||
: colors.primary);
|
||||
|
||||
const textColor = type === "secondary" ? colors.textDark : colors.white;
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={[styles.button, { backgroundColor }, style]}
|
||||
onPress={onPress}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<View style={styles.buttonContent}>
|
||||
{icon && <Ionicons name={icon} size={20} color={textColor} style={{ marginRight: 8 }} />}
|
||||
<Text style={[styles.buttonText, { color: textColor }]}>{title}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
// --- Styles ---
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1, backgroundColor: "#f5f7fb" },
|
||||
loadingContainer: { flex: 1, justifyContent: "center", alignItems: "center" },
|
||||
loadingText: { marginTop: 10, fontSize: 16, color: "#007AFF" },
|
||||
header: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
padding: 16,
|
||||
paddingTop: 40,
|
||||
backgroundColor: "#fff",
|
||||
alignItems: "center",
|
||||
borderBottomWidth: 1,
|
||||
borderColor: "#eee",
|
||||
},
|
||||
headerTitle: { fontSize: 20, fontWeight: "600", color: "#333" },
|
||||
profileCard: {
|
||||
borderRadius: 16,
|
||||
margin: 16,
|
||||
backgroundColor: "#fff",
|
||||
padding: 20,
|
||||
shadowColor: "#000",
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 6,
|
||||
elevation: 4,
|
||||
},
|
||||
profileHeader: { alignItems: "center", marginBottom: 16 },
|
||||
avatar: { width: 100, height: 100, borderRadius: 50, marginBottom: 8 },
|
||||
cameraIcon: {
|
||||
position: "absolute",
|
||||
right: 110,
|
||||
bottom: 10,
|
||||
backgroundColor: "#007AFF",
|
||||
borderRadius: 15,
|
||||
width: 32,
|
||||
height: 32,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
detail: {
|
||||
fontSize: 16,
|
||||
marginBottom: 10,
|
||||
color: "#444",
|
||||
paddingLeft: 4,
|
||||
},
|
||||
inputContainer: { marginBottom: 12 ,browRadius: 12},
|
||||
inputLabel: { fontSize: 14, fontWeight: "500", color: "#555"},
|
||||
inputContainer: { marginBottom: 12 },
|
||||
inputLabel: { fontSize: 14, fontWeight: "500", color: colors.textDark, marginBottom: 6 },
|
||||
inputWrapper: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
@@ -138,38 +96,29 @@ const styles = StyleSheet.create({
|
||||
input: {
|
||||
flex: 1,
|
||||
borderWidth: 1,
|
||||
borderColor: "#ddd",
|
||||
borderRadius: 20,
|
||||
borderColor: colors.textLight,
|
||||
borderRadius: 10,
|
||||
padding: 12,
|
||||
paddingLeft: 12,
|
||||
backgroundColor: "#fdfdfd",
|
||||
color: "#000",
|
||||
backgroundColor: colors.white,
|
||||
color: colors.textDark,
|
||||
fontSize: 15,
|
||||
},
|
||||
button: {
|
||||
backgroundColor: "#007AFF",
|
||||
paddingVertical: 12,
|
||||
borderRadius: 12,
|
||||
alignItems: "center",
|
||||
marginVertical: 6,
|
||||
shadowColor: "#007AFF",
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 4,
|
||||
elevation: 3,
|
||||
},
|
||||
buttonSecondary: {
|
||||
backgroundColor: "#e5e5ea",
|
||||
},
|
||||
buttonDanger: {
|
||||
backgroundColor: "#ff3b30",
|
||||
},
|
||||
buttonContent: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 16,
|
||||
fontWeight: "600",
|
||||
},
|
||||
buttonText: { color: "#fff", fontSize: 16, fontWeight: "600" },
|
||||
buttonTextSecondary: { color: "#333" },
|
||||
buttonTextDanger: { color: "#fff" },
|
||||
});
|
||||
|
||||
export { CustomInput, styles, CustomButton };
|
||||
export { CustomInput, CustomButton, styles };
|
||||
|
||||
@@ -10,6 +10,7 @@ import OrganizerRegisterScreen from "../screens/OrgnizerRagister";
|
||||
import GroupCreation from "../screens/GroupCreation";
|
||||
import GroupReview from "../screens/GroupReview";
|
||||
import BottomTabs from "./BottomTabs";
|
||||
import BecomeOrg from "../screens/profile/BecomeOrg";
|
||||
|
||||
const Stack = createNativeStackNavigator();
|
||||
export type RootStackParamList = {
|
||||
@@ -18,7 +19,7 @@ export type RootStackParamList = {
|
||||
Home: undefined
|
||||
HomeTabs: undefined
|
||||
splash: undefined
|
||||
OrganizerRegister:undefined
|
||||
OrganizerRegister: undefined
|
||||
Profile: undefined;
|
||||
GroupCreation: undefined;
|
||||
GroupReview: undefined;
|
||||
@@ -40,13 +41,13 @@ function HomeScreen() {
|
||||
|
||||
export default function AppNavigator() {
|
||||
const linking = {
|
||||
prefixes: ["myapp://", "https://api.godemo.in"], // supports both
|
||||
config: {
|
||||
screens: {
|
||||
Profile: "EProcurementSB", // dynamic route
|
||||
prefixes: ["myapp://", "https://api.godemo.in"], // supports both
|
||||
config: {
|
||||
screens: {
|
||||
Profile: "EProcurementSB", // dynamic route
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
return (
|
||||
<NavigationContainer linking={linking} >
|
||||
<Stack.Navigator initialRouteName="Splash">
|
||||
@@ -54,10 +55,11 @@ export default function AppNavigator() {
|
||||
<Stack.Screen name="Home" component={BottomTabs} options={{ headerShown: false }} />
|
||||
<Stack.Screen name="Login" component={LoginScreen} options={{ headerShown: false }} />
|
||||
<Stack.Screen name="OtpVarify" component={OtpVerificationScreen} options={{ headerShown: false }} />
|
||||
<Stack.Screen name="OrganizerRegister" component={OrganizerRegisterScreen} options={{ headerShown: false }} />
|
||||
<Stack.Screen name="Profile" component={Profile} options={{ headerShown: false }}/>
|
||||
<Stack.Screen name="GroupCreation" component={GroupCreation}options={{ headerShown: false }} />
|
||||
<Stack.Screen name="GroupReview" component={GroupReview}options={{ headerShown: false }} />
|
||||
<Stack.Screen name="OrganizerRegister" component={OrganizerRegisterScreen} options={{ headerShown: false }} />
|
||||
<Stack.Screen name="Profile" component={Profile} options={{ headerShown: false }} />
|
||||
<Stack.Screen name="GroupCreation" component={GroupCreation} options={{ headerShown: false }} />
|
||||
<Stack.Screen name="GroupReview" component={GroupReview} options={{ headerShown: false }} />
|
||||
<Stack.Screen name="BecomeOrg" component={BecomeOrg} options={{ headerShown: false }} />
|
||||
</Stack.Navigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import React from "react";
|
||||
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
|
||||
import { View, Text, StyleSheet, Platform } from "react-native";
|
||||
import { Ionicons } from "@react-native-vector-icons/ionicons";
|
||||
import { Ionicons, IoniconsIconName } from "@react-native-vector-icons/ionicons";
|
||||
import GroupScreen from "../screens/GroupScreen";
|
||||
import Profile from "../screens/profile/Profile";
|
||||
import Home from "../screens/home/Home";
|
||||
import colors from "../components/colors";
|
||||
import Setting from "../screens/setting/Setting";
|
||||
|
||||
const Tab = createBottomTabNavigator();
|
||||
|
||||
@@ -15,15 +18,15 @@ const TAB_CONFIG = {
|
||||
iconSize: 28,
|
||||
};
|
||||
|
||||
// 1. Define a color map for active tabs
|
||||
// Background (tab highlight) colors
|
||||
const activeTabColors: { [key: string]: string } = {
|
||||
Home: "#ff7675", // A soft red
|
||||
Profile: "#74b9ff", // A nice blue
|
||||
Group: "#55efc4", // Your theme's green
|
||||
Settings: "#ffeaa7", // A gentle yellow
|
||||
Home: colors.pastelPink, // Soft pink
|
||||
Profile: colors.pastelBlue, // Light sky blue
|
||||
Group: colors.pastelGreen, // Gentle mint green
|
||||
Settings: colors.pastelOrange, // Warm pastel yellow
|
||||
};
|
||||
|
||||
// 2. Define a text color map for contrasting icon colors
|
||||
// Icon/Text colors on active tabs
|
||||
const activeIconColors: { [key: string]: string } = {
|
||||
Home: "#333",
|
||||
Profile: "#333",
|
||||
@@ -57,7 +60,7 @@ export default function BottomTabs() {
|
||||
) : null,
|
||||
|
||||
tabBarIcon: ({ focused }) => {
|
||||
let iconName: string = "home-outline";
|
||||
let iconName: IoniconsIconName = "home-outline";
|
||||
|
||||
// Determine icon based on route
|
||||
if (route.name === "Home") iconName = focused ? "home" : "home-outline";
|
||||
@@ -90,10 +93,10 @@ export default function BottomTabs() {
|
||||
},
|
||||
})}
|
||||
>
|
||||
<Tab.Screen name="Home" component={HomeScreen} />
|
||||
<Tab.Screen name="Home" component={Home} />
|
||||
<Tab.Screen name="Profile" component={Profile} />
|
||||
<Tab.Screen name="Group" component={GroupScreen} />
|
||||
<Tab.Screen name="Settings" component={SettingsScreen} />
|
||||
<Tab.Screen name="Settings" component={Setting} />
|
||||
</Tab.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,8 @@ export default function OtpVerificationScreen() {
|
||||
if (res.data.statusCode === 200) {
|
||||
saveToken(res.data.data);
|
||||
navigation.navigate('Home')
|
||||
ToastAndroid.show("OTP Verified Successfully!", ToastAndroid.SHORT);}
|
||||
ToastAndroid.show("OTP Verified Successfully!", ToastAndroid.SHORT);
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error("❌ OTP Verification Error:", err?.response?.data || err.message);
|
||||
ToastAndroid.show(err.message || "Invalid OTP. Please try again.", ToastAndroid.SHORT);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
import React from 'react'
|
||||
import AppView from '../../components/AppView'
|
||||
import UserProfileStatus from './UserProfileStatus'
|
||||
import colors from '../../components/colors'
|
||||
|
||||
const Home = () => {
|
||||
return (
|
||||
<AppView backgroundColor={colors.pastelPink} barStyle="dark-content">
|
||||
<UserProfileStatus/>
|
||||
</AppView>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
||||
|
||||
const styles = StyleSheet.create({})
|
||||
@@ -0,0 +1,226 @@
|
||||
import React, { use, useCallback, useEffect, useState } from "react";
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
ActivityIndicator,
|
||||
TouchableOpacity,
|
||||
Modal,
|
||||
Alert,
|
||||
} from "react-native";
|
||||
import * as Keychain from "react-native-keychain";
|
||||
import { useFocusEffect, useNavigation } from "@react-navigation/native";
|
||||
import { BASE_URL } from "../../utils/ApiUrls";
|
||||
|
||||
export type ApiResponse = {
|
||||
message: string;
|
||||
status: string;
|
||||
statusCode: number;
|
||||
data: {
|
||||
role: string;
|
||||
organizerStatus: string;
|
||||
profileStatus: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
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 userStatus = async () => {
|
||||
try {
|
||||
const credentials = await Keychain.getGenericPassword();
|
||||
if (!credentials) {
|
||||
Alert.alert("Error", "You are not logged in");
|
||||
return;
|
||||
}
|
||||
const token = credentials.password;
|
||||
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const response = await fetch(`${BASE_URL}user/profile/status`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "*/*",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
const json: ApiResponse = await response.json();
|
||||
|
||||
console.log("👤 Profile Status Response:", 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);
|
||||
}
|
||||
};
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
userStatus();
|
||||
}, [])
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (statusData && statusData.profileStatus === false) {
|
||||
const id = setInterval(() => {
|
||||
setShowModal(true);
|
||||
}, 5000);
|
||||
setIntervalId(id);
|
||||
return () => clearInterval(id);
|
||||
}
|
||||
}, [statusData]);
|
||||
|
||||
const handleGoToProfile = () => {
|
||||
// Stop modal repetition
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
setIntervalId(null);
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Welcome Back 👋</Text>
|
||||
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.cardTitle}>Profile Status</Text>
|
||||
<Text>Role: {statusData?.role}</Text>
|
||||
<Text>Organizer: {statusData?.organizerStatus}</Text>
|
||||
<Text>
|
||||
Profile Completed: {statusData?.profileStatus ? "Yes ✅" : "No ❌"}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Custom Modal */}
|
||||
{showModal && (
|
||||
<Modal
|
||||
visible={showModal}
|
||||
animationType="slide"
|
||||
transparent
|
||||
onRequestClose={() => setShowModal(false)}
|
||||
>
|
||||
<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>
|
||||
|
||||
<TouchableOpacity style={styles.button} onPress={handleGoToProfile}>
|
||||
<Text style={styles.buttonText}>Go to Profile</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.button, { backgroundColor: "#ccc", marginTop: 8 }]}
|
||||
onPress={() => setShowModal(false)}
|
||||
>
|
||||
<Text style={[styles.buttonText, { color: "#333" }]}>Close</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
)}
|
||||
</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: 20,
|
||||
},
|
||||
cardTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: "600",
|
||||
marginBottom: 10,
|
||||
},
|
||||
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: "#FF9500",
|
||||
},
|
||||
modalText: {
|
||||
fontSize: 14,
|
||||
color: "#444",
|
||||
textAlign: "center",
|
||||
marginBottom: 20,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,143 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
Alert,
|
||||
ActivityIndicator,
|
||||
ScrollView,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
SafeAreaView,
|
||||
} from "react-native";
|
||||
import * as Keychain from "react-native-keychain";
|
||||
import { CustomInput, CustomButton } from "../../components/common/Custom";
|
||||
import colors from "../../components/colors";
|
||||
|
||||
const BecomeOrg = () => {
|
||||
const [bankAccountNumber, setBankAccountNumber] = useState("");
|
||||
const [bankName, setBankName] = useState("");
|
||||
const [branchCode, setBranchCode] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!bankAccountNumber || !bankName || !branchCode) {
|
||||
Alert.alert("Error", "Please fill all fields");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
const credentials = await Keychain.getGenericPassword();
|
||||
if (!credentials) {
|
||||
Alert.alert("Error", "You are not logged in");
|
||||
return;
|
||||
}
|
||||
const token = credentials.password;
|
||||
|
||||
const response = await fetch(
|
||||
"https://api.godemo.in/FundCollect/api/organizer/registration/apply",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "*/*",
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
bankAccountNumber,
|
||||
bankName,
|
||||
branchCode,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
if (result.status === "success") {
|
||||
Alert.alert("Success", "Organizer registration applied successfully");
|
||||
setBankAccountNumber("");
|
||||
setBankName("");
|
||||
setBranchCode("");
|
||||
} else {
|
||||
Alert.alert("Error", result.message || "Failed to apply");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Registration error:", error);
|
||||
Alert.alert("Error", "Something went wrong");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.pastelPink }}>
|
||||
<KeyboardAvoidingView
|
||||
style={{ flex: 1 }}
|
||||
behavior={Platform.OS === "ios" ? "padding" : undefined}
|
||||
keyboardVerticalOffset={Platform.OS === "ios" ? 60 : 0}
|
||||
>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.container}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
<Text style={styles.title}>Organizer Registration</Text>
|
||||
|
||||
<CustomInput
|
||||
placeholder="Bank Account Number"
|
||||
keyboardType="numeric"
|
||||
value={bankAccountNumber}
|
||||
onChangeText={setBankAccountNumber}
|
||||
icon="card-outline"
|
||||
/>
|
||||
|
||||
<CustomInput
|
||||
placeholder="Bank Name"
|
||||
value={bankName}
|
||||
onChangeText={setBankName}
|
||||
icon="business-outline"
|
||||
/>
|
||||
|
||||
<CustomInput
|
||||
placeholder="Branch Code"
|
||||
value={branchCode}
|
||||
onChangeText={setBranchCode}
|
||||
icon="key-outline"
|
||||
/>
|
||||
|
||||
<CustomButton
|
||||
title={loading ? "Submitting..." : "Request Organizer Role"}
|
||||
onPress={handleSubmit}
|
||||
icon="send-outline"
|
||||
buttonBackground={colors.pastelBlue}
|
||||
style={{ opacity: loading ? 0.6 : 1 }}
|
||||
/>
|
||||
|
||||
{loading && (
|
||||
<ActivityIndicator
|
||||
style={{ marginTop: 15 }}
|
||||
size="large"
|
||||
color={colors.primary}
|
||||
/>
|
||||
)}
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
export default BecomeOrg;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexGrow: 1,
|
||||
padding: 20,
|
||||
justifyContent: "center",
|
||||
},
|
||||
title: {
|
||||
fontSize: 22,
|
||||
fontWeight: "700",
|
||||
marginBottom: 20,
|
||||
textAlign: "center",
|
||||
color: colors.textDark,
|
||||
},
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
@@ -16,8 +16,12 @@ import {
|
||||
import * as Keychain from "react-native-keychain";
|
||||
import { Ionicons } from "@react-native-vector-icons/ionicons";
|
||||
import { launchImageLibrary } from "react-native-image-picker";
|
||||
import { base_URL } from "../../utils/ApiUrls";
|
||||
import { CustomButton, CustomInput } from "../../components/common/Custom";
|
||||
import colors from "../../components/colors";
|
||||
import SwitchNavigator from "./SwitchNavigator";
|
||||
import { useFocusEffect } from "@react-navigation/native";
|
||||
import { ApiResponse } from "../home/UserProfileStatus";
|
||||
import { BASE_URL } from "../../utils/ApiUrls";
|
||||
|
||||
|
||||
|
||||
@@ -45,6 +49,9 @@ const Profile = () => {
|
||||
const [fetching, setFetching] = useState(true);
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [imageUpdating, setImageUpdating] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [statusData, setStatusData] = useState<null | { profileStatus: boolean; organizerStatus: string; role: string }>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchUserProfile = async () => {
|
||||
try {
|
||||
@@ -57,7 +64,7 @@ const Profile = () => {
|
||||
|
||||
setFetching(true);
|
||||
const response = await fetch(
|
||||
`${base_URL}user/profile/view`,
|
||||
`${BASE_URL}user/profile/view`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
@@ -91,7 +98,7 @@ const Profile = () => {
|
||||
const token = credentials.password;
|
||||
|
||||
const response = await fetch(
|
||||
`${base_URL}user/profile/update`,
|
||||
`${BASE_URL}user/profile/update`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
@@ -162,7 +169,7 @@ const Profile = () => {
|
||||
} as any);
|
||||
|
||||
const response = await fetch(
|
||||
`${base_URL}user/update/userImg`,
|
||||
`${BASE_URL}user/update/userImg`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
@@ -198,6 +205,48 @@ const Profile = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const userStatus = async () => {
|
||||
try {
|
||||
const credentials = await Keychain.getGenericPassword();
|
||||
if (!credentials) {
|
||||
Alert.alert("Error", "You are not logged in");
|
||||
return;
|
||||
}
|
||||
const token = credentials.password;
|
||||
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const response = await fetch(`${BASE_URL}user/profile/status`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "*/*",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
const json: ApiResponse = await response.json();
|
||||
|
||||
console.log("👤 Profile Status Response:", json);
|
||||
if (json.status === "success") {
|
||||
setStatusData(json.data);
|
||||
|
||||
} else {
|
||||
setError(json.message || "Failed to fetch status");
|
||||
}
|
||||
} catch (err) {
|
||||
setError("Something went wrong. Please try again.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
userStatus();
|
||||
}, [])
|
||||
);
|
||||
|
||||
console.log("login user data :", user);
|
||||
|
||||
useEffect(() => {
|
||||
fetchUserProfile();
|
||||
@@ -226,7 +275,7 @@ const Profile = () => {
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<StatusBar backgroundColor="#6366f1" barStyle="light-content" />
|
||||
<StatusBar barStyle="light-content" />
|
||||
|
||||
{/* Header */}
|
||||
<View style={styles.header}>
|
||||
@@ -243,9 +292,15 @@ const Profile = () => {
|
||||
contentContainerStyle={{ flexGrow: 1 }}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
|
||||
{/* Profile Card */}
|
||||
<View style={styles.profileCard}>
|
||||
{/* Profile Image Section */}
|
||||
{
|
||||
statusData?.role && statusData?.organizerStatus ?
|
||||
<Text style={{ color: "#64748b", marginBottom: 16, textAlign: 'center', fontSize: 18 }}>Your Role: {statusData.role} | Organizer Status: {statusData.organizerStatus}</Text>
|
||||
:
|
||||
<SwitchNavigator role={statusData?.role || ''} status={statusData?.organizerStatus || ''} />}
|
||||
<View style={styles.profileImageSection}>
|
||||
<View style={styles.avatarContainer}>
|
||||
<Image
|
||||
@@ -263,7 +318,7 @@ const Profile = () => {
|
||||
onPress={handleUpdateImage}
|
||||
icon={imageUpdating ? undefined : "cloud-upload"}
|
||||
style={styles.updateImageButton}
|
||||
// disabled={imageUpdating}
|
||||
buttonBackground="#6366f1"
|
||||
|
||||
/>
|
||||
</View>
|
||||
@@ -340,6 +395,7 @@ const Profile = () => {
|
||||
onPress={() => setEditing(true)}
|
||||
icon="create"
|
||||
style={styles.editButton}
|
||||
buttonBackground="#6366f1"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
@@ -402,13 +458,14 @@ const Profile = () => {
|
||||
onPress={updateProfile}
|
||||
icon="save"
|
||||
style={styles.saveButton}
|
||||
buttonBackground="#10b981"
|
||||
/>
|
||||
<CustomButton
|
||||
title="Cancel"
|
||||
type="outline"
|
||||
onPress={() => setEditing(false)}
|
||||
icon="close"
|
||||
style={styles.cancelButton}
|
||||
buttonBackground="#f38080ff"
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
@@ -423,8 +480,8 @@ const Profile = () => {
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: "#f8fafc",
|
||||
marginBottom: '30%',
|
||||
backgroundColor: colors.pastelBlue,
|
||||
paddingBottom: '30%',
|
||||
},
|
||||
loadingContainer: {
|
||||
flex: 1,
|
||||
@@ -443,19 +500,19 @@ const styles = StyleSheet.create({
|
||||
alignItems: "center",
|
||||
padding: 16,
|
||||
paddingTop: 50,
|
||||
backgroundColor: "#6366f1",
|
||||
// backgroundColor: "#6366f1",
|
||||
borderBottomLeftRadius: 20,
|
||||
borderBottomRightRadius: 20,
|
||||
elevation: 4,
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 4,
|
||||
// elevation: 4,
|
||||
// shadowColor: "#000",
|
||||
// shadowOffset: { width: 0, height: 2 },
|
||||
// shadowOpacity: 0.2,
|
||||
// shadowRadius: 4,
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: 22,
|
||||
fontWeight: "700",
|
||||
color: "#fff"
|
||||
color: "#000"
|
||||
},
|
||||
refreshButton: {
|
||||
padding: 4,
|
||||
@@ -506,6 +563,8 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
updateImageButton: {
|
||||
width: "80%",
|
||||
paddingHorizontal: 10,
|
||||
borderRadius: 25,
|
||||
},
|
||||
detailsSection: {
|
||||
marginBottom: 24,
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import React, { useState } from "react";
|
||||
import { View, Text, StyleSheet, Switch } from "react-native";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
||||
|
||||
type RootStackParamList = {
|
||||
BecomeOrg: undefined;
|
||||
Profile: undefined;
|
||||
};
|
||||
|
||||
type SwitchNavigatorProps = {
|
||||
role: string;
|
||||
status: string;
|
||||
};
|
||||
|
||||
const SwitchNavigator: React.FC<SwitchNavigatorProps> = ({ role, status }) => {
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const [isEnabled, setIsEnabled] = useState(false);
|
||||
|
||||
const toggleSwitch = () => {
|
||||
const newValue = !isEnabled;
|
||||
setIsEnabled(newValue);
|
||||
|
||||
if (newValue) {
|
||||
navigation.navigate("BecomeOrg"); // Navigate when ON
|
||||
} else {
|
||||
navigation.navigate("Profile"); // Navigate when OFF
|
||||
}
|
||||
};
|
||||
|
||||
console.log("status :", status);
|
||||
console.log("role :", role);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>
|
||||
{!isEnabled && role !== "USER"
|
||||
? "Become an Organizer"
|
||||
: `Request Organizer Role ${status}`}
|
||||
</Text>
|
||||
<Switch
|
||||
trackColor={{ false: "#767577", true: "#81b0ff" }}
|
||||
thumbColor={isEnabled ? "#007AFF" : "#f4f3f4"}
|
||||
ios_backgroundColor="#3e3e3e"
|
||||
onValueChange={toggleSwitch}
|
||||
value={isEnabled}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default SwitchNavigator;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: "row",
|
||||
justifyContent: 'flex-end',
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 10,
|
||||
gap: 10,
|
||||
},
|
||||
text: {
|
||||
fontSize: 18,
|
||||
flexShrink: 1, // Wrap text if too long
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
import React from 'react'
|
||||
import AppView from '../../components/AppView'
|
||||
import colors from '../../components/colors'
|
||||
|
||||
const Setting = () => {
|
||||
return (
|
||||
<AppView backgroundColor={colors.pastelOrange} barStyle="dark-content">
|
||||
<Text>Setting</Text>
|
||||
</AppView>
|
||||
)
|
||||
}
|
||||
|
||||
export default Setting
|
||||
|
||||
const styles = StyleSheet.create({})
|
||||
Reference in New Issue
Block a user