import React, { useState, useEffect } from "react"; import { StyleSheet, View, Text, ScrollView, Image, TouchableOpacity, Alert, ActivityIndicator, StatusBar, TextInput, } from "react-native"; import * as Keychain from "react-native-keychain"; import {Ionicons} from "@react-native-vector-icons/ionicons"; import { launchImageLibrary } from "react-native-image-picker"; import { IconProps } from "react-native-elements"; import { base_URL } from "../../utils/ApiUrls"; type CustomButtonProps = { title: string; onPress: () => void; type?: "primary" | "secondary" | "danger" | "outline"; icon?: React.ComponentProps['name']; style?: any; }; const CustomButton = ({ title, onPress, type = "primary", icon, style }: CustomButtonProps) => { const getButtonStyle = () => { switch (type) { case "secondary": return [styles.button, styles.buttonSecondary]; case "danger": return [styles.button, styles.buttonDanger]; case "outline": return [styles.button, styles.buttonOutline]; default: return styles.button; } }; const getTextStyle = () => { switch (type) { case "secondary": return [styles.buttonText, styles.buttonTextSecondary]; case "danger": return [styles.buttonText, styles.buttonTextDanger]; case "outline": return [styles.buttonText, styles.buttonTextOutline]; default: return styles.buttonText; } }; return ( {icon && ( s && typeof s === "object" && "color" in s)?.color || "#fff" : (getTextStyle() as any).color || "#fff" } style={styles.buttonIcon} /> )} {title} ); }; // Custom Input Component type CustomInputProps = { label?: string; placeholder?: string; value?: string; onChangeText?: (text: string) => void; icon?: React.ComponentProps['name']; [key: string]: any; }; const CustomInput = ({ label, placeholder, value, onChangeText, icon, ...props }: CustomInputProps) => { return ( {label && {label}} {icon && } ); }; type UserProfile = { id: string; userId: number; firstName: string | null; lastName: string | null; mobile: string | null; gender: string | null; email: string | null; username: string | null; address: string | null; bankName: string | null; branchCode: string | null; filePath: string | null; bankAccountNumber: string | null; imageBlob: string | null; organizerStatus?: string; role?: string; }; const Profile = () => { const [user, setUser] = useState(null); const [fetching, setFetching] = useState(true); const [editing, setEditing] = useState(false); const [imageUpdating, setImageUpdating] = useState(false); const fetchUserProfile = async () => { try { const credentials = await Keychain.getGenericPassword(); if (!credentials) { Alert.alert("Error", "You are not logged in"); return; } const token = credentials.password; setFetching(true); const response = await fetch( `${base_URL}user/profile/view`, { method: "GET", headers: { Accept: "*/*", Authorization: `Bearer ${token}`, }, } ); const result = await response.json(); if (result.status === "success") { setUser(result.data); } else { Alert.alert("Error", "Failed to fetch user profile"); } } catch (error) { console.error("❌ Error fetching profile:", error); Alert.alert("Error", "Could not connect to server"); } finally { setFetching(false); } }; const updateProfile = async () => { try { const credentials = await Keychain.getGenericPassword(); if (!credentials) { Alert.alert("Error", "You are not logged in"); return; } const token = credentials.password; const response = await fetch( `${base_URL}user/profile/update`, { method: "PUT", headers: { Accept: "*/*", "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ address: user?.address || null, email: user?.email || null, firstName: user?.firstName || null, gender: user?.gender || null, lastName: user?.lastName || null, username: user?.username || null, }), } ); const result = await response.json(); if (result.status === "success") { Alert.alert("Success", "Profile updated successfully"); setEditing(false); fetchUserProfile(); // reload updated data } else { Alert.alert("Error", "Failed to update profile"); } } catch (error) { console.error("❌ Error updating profile:", error); Alert.alert("Error", "Could not update profile"); } }; const pickImage = () => { launchImageLibrary({ mediaType: "photo", quality: 0.5 }, (response) => { if (response.didCancel) return; if (response.errorCode) { Alert.alert("Error", response.errorMessage || "Something went wrong"); } else if (response.assets && response.assets.length > 0) { const selectedImage = response.assets[0]; setUser((prev) => prev ? { ...prev, filePath: selectedImage.uri ?? null } : prev ); } }); }; const handleUpdateImage = async () => { try { const credentials = await Keychain.getGenericPassword(); if (!credentials) { Alert.alert("Error", "You are not logged in"); return; } const token = credentials.password; if (!user?.filePath) { Alert.alert("Error", "Please select an image first"); return; } setImageUpdating(true); const formData = new FormData(); formData.append("file", { uri: user.filePath, type: "image/jpeg", name: "profile.jpg", } as any); const response = await fetch( `${base_URL}user/update/userImg`, { method: "PUT", headers: { Accept: "application/json", Authorization: `Bearer ${token}`, }, body: formData, } ); const result = await response.json(); if (result.status === "success") { Alert.alert("Success", "Profile image updated successfully"); // If API returns updated image, set it if (result.data?.imageBlob) { setUser((prev) => prev ? { ...prev, imageBlob: result.data.imageBlob, filePath: null } : prev); } else { // Otherwise, keep the local filePath until next fetch setUser((prev) => prev ? { ...prev, filePath: user.filePath } : prev); } // Refresh profile from API as well fetchUserProfile(); } else { Alert.alert("Error", result.message || "Failed to update profile image"); } } catch (error) { console.error("❌ Error updating profile image:", error); Alert.alert("Error", "Could not update profile image"); } finally { setImageUpdating(false); } }; useEffect(() => { fetchUserProfile(); }, []); console.log("user image :", user?.filePath); const getProfileImage = () => { if (user?.imageBlob) { return `data:image/jpeg;base64,${user.imageBlob}`; } if (user?.filePath) { return user.filePath; } return "https://randomuser.me/api/portraits/men/1.jpg"; }; if (fetching || !user) { return ( Loading profile... ); } return ( {/* Header */} User Profile {/* Profile Card */} {/* Profile Image Section */} console.log("Error loading image")} /> {!editing ? ( <> {/* Profile Details */} Personal Information Become Organizer Name {user.firstName} {user.lastName} Email {user.email || "Not provided"} Mobile {user.mobile || "Not provided"} Username {user.username} Address {user.address || "Not provided"} Gender {user.gender || "Not specified"} Role {user.role || "User"} setEditing(true)} icon="create" style={styles.editButton} /> ) : ( <> {/* Edit Form */} Edit Profile setUser({ ...user, firstName: text })} icon="person" /> setUser({ ...user, lastName: text })} icon="person" /> setUser({ ...user, email: text })} icon="mail" keyboardType="email-address" /> setUser({ ...user, username: text })} icon="at" /> setUser({ ...user, address: text })} icon="location" multiline /> setUser({ ...user, gender: text })} icon="male-female" /> setEditing(false)} icon="close" style={styles.cancelButton} /> )} ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#f8fafc" }, loadingContainer: { flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: "#f8fafc" }, loadingText: { marginTop: 10, fontSize: 16, color: "#64748b" }, header: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", padding: 16, paddingTop: 50, backgroundColor: "#6366f1", borderBottomLeftRadius: 20, borderBottomRightRadius: 20, elevation: 4, shadowColor: "#000", shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 4, }, headerTitle: { fontSize: 22, fontWeight: "700", color: "#fff" }, refreshButton: { padding: 4, }, scrollContainer: { flex: 1, }, profileCard: { borderRadius: 20, margin: 16, backgroundColor: "#fff", padding: 20, shadowColor: "#000", shadowOpacity: 0.1, shadowRadius: 10, shadowOffset: { width: 0, height: 4 }, elevation: 5, }, profileImageSection: { alignItems: "center", marginBottom: 24, paddingBottom: 20, borderBottomWidth: 1, borderBottomColor: "#f1f5f9", }, avatarContainer: { position: "relative", marginBottom: 16, }, avatar: { width: 120, height: 120, borderRadius: 60, borderWidth: 4, borderColor: "#e2e8f0", }, cameraIcon: { position: "absolute", right: 0, bottom: 0, backgroundColor: "#6366f1", borderRadius: 20, width: 40, height: 40, justifyContent: "center", alignItems: "center", borderWidth: 3, borderColor: "#fff", elevation: 2, }, updateImageButton: { width: "80%", }, detailsSection: { marginBottom: 24, }, sectionTitle: { fontSize: 18, fontWeight: "700", color: "#1e293b", marginBottom: 16, paddingBottom: 8, borderBottomWidth: 1, borderBottomColor: "#f1f5f9", }, detailRow: { flexDirection: "row", alignItems: "center", marginBottom: 16, paddingHorizontal: 8, }, detailIcon: { marginRight: 12, }, detailLabel: { fontSize: 12, color: "#64748b", marginBottom: 2, }, detailText: { fontSize: 16, color: "#334155", fontWeight: "500", }, editButton: { marginTop: 8, }, editSection: { marginBottom: 20, }, editButtons: { gap: 12, }, saveButton: { marginTop: 8, }, cancelButton: { marginTop: 4, }, inputContainer: { marginBottom: 16, }, inputLabel: { fontSize: 14, fontWeight: "600", color: "#374151", marginBottom: 6, }, inputWrapper: { flexDirection: "row", alignItems: "center", position: "relative", }, inputIcon: { position: "absolute", left: 12, zIndex: 1, }, input: { borderWidth: 1, borderColor: "#d1d5db", borderRadius: 12, padding: 12, backgroundColor: "#fff", color: "#1f2937", fontSize: 16, flex: 1, }, inputWithIcon: { paddingLeft: 40, }, button: { backgroundColor: "#6366f1", paddingVertical: 14, borderRadius: 12, alignItems: "center", justifyContent: "center", flexDirection: "row", shadowColor: "#6366f1", shadowOpacity: 0.3, shadowRadius: 6, shadowOffset: { width: 0, height: 3 }, elevation: 4, }, buttonSecondary: { backgroundColor: "#f1f5f9", }, buttonDanger: { backgroundColor: "#ef4444", }, buttonOutline: { backgroundColor: "transparent", borderWidth: 1, borderColor: "#6366f1", }, buttonText: { color: "#fff", fontSize: 16, fontWeight: "600", }, buttonTextSecondary: { color: "#334155", }, buttonTextDanger: { color: "#fff", }, buttonTextOutline: { color: "#6366f1", }, buttonIcon: { marginRight: 8, }, }); export default Profile;