import React, { useEffect, useMemo, useState, useCallback } from 'react'; import { View, ScrollView, StyleSheet, TouchableOpacity, RefreshControl, ActivityIndicator, Dimensions, Platform, ViewStyle, TextStyle, Modal, TextInput, KeyboardAvoidingView, Alert, StatusBar, Image, } from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import Animated, { FadeInDown, FadeInUp, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated'; import { AlertCircle, UserCircle, User, Mail, Phone, Briefcase, Building2, Building, Tag, Network, Globe, Award, MapPin, Home, Map, Mailbox, FileText, CreditCard, Type, Calendar, Receipt, ClipboardCheck, Star, List, Factory, CheckCircle2, CheckCircle, CalendarClock, ShieldCheck, LucideIcon, Edit, X, Save, ChevronRight, LogOut } from 'lucide-react-native'; import { useFocusEffect, useNavigation } from '@react-navigation/native'; import { useContractorProfile, useUpdateUserProfile, useESetuAdharVarification } from '../../Services/ApiCall/Hooks'; import { useSelector } from 'react-redux'; import ConfirmDialog from '../../Constant/Dialog/ConfirmDialog'; import { generateTransactionId } from '../../Helper/uniqueId'; import NavigationString from '../../Constant/NavigationString'; import { useTheme } from '../../Hooks/useTheme'; import AppText from '../../components/AppText'; import ImagePath from '../../Constant/ImagePath'; const { width } = Dimensions.get('window'); // Type Definitions interface ContractorData { contractorId: number; firstName: string; middleName: string; lastName: string; fullName: string; emailId: string; mobileNo: string; company: string; contractorType: string; dialCode: string; twoFactorAuthEnabled: boolean; colorCodeHSL: string | null; isActive: string; isEnabled: boolean | null; role: string; contractorStatus: boolean; designation: string | null; contOtherId: number; contCompanyType: string | null; contOrganization: string; agentAddress: string; agentPincode: string; agentCity: string; agentState: string | null; agentWebsite: string; agentRegno: string; orgPAN: string; orgPANdob: string; orgPANname: string; gistnNo: string; comAadharNo: string; contractorClass: string; socialCategory: string; wizardStepStatus: string; registrationStatus: string; indianOrgName: string; expiryDate: string; msmeStatus: string; } interface ProfileHeaderProps { data: ContractorData; displayName: string; } interface QuickStatsProps { data: ContractorData; onAadhaarPress: () => void; } interface InfoSectionProps { title: string; IconComponent: LucideIcon; children: React.ReactNode; delay?: number; onEdit?: () => void; showEdit?: boolean; } interface InfoRowProps { label: string; value: string | null | undefined; IconComponent: LucideIcon; valueColor?: string; } interface StatItem { Icon: LucideIcon; label: string; value: string; color: string; onPress?: () => void; } interface UpdateProfilePayload { firstName: string; middleName: string; lastName: string; mobileNo: string; emailId: string; fullname: string; } // Helper function to check if value is valid const isValidValue = (value: any): boolean => { if (value === null || value === undefined) return false; if (typeof value === 'string') { const trimmed = value.trim(); return trimmed.length > 0 && trimmed.toLowerCase() !== 'n/a'; } return true; }; // Helper function to check if section has any valid data const hasSectionData = (values: any[]): boolean => { return values.some(value => isValidValue(value)); }; // Main Component const ProfileTab: React.FC = () => { const { colors, spacing, shadows, isDarkMode } = useTheme(); const styles = useMemo(() => createStyles(colors), [colors]); const { data, refetch, isLoading, error } = useContractorProfile(); const user = useSelector((state: any) => state.token?.token); console.log("User: ", user) const Navigation: any = useNavigation(); const { mutateAsync: eSetuAdharVarification, isPending } = useESetuAdharVarification(); const [editModalVisible, setEditModalVisible] = useState(false); const [showDialog, setShowDialog] = useState(false); const [isNavigating, setIsNavigating] = useState(false); const handleAadhaarPress = () => { if (user?.esetuVerified === 'Not Verified' || user?.esetuVerified === null) { setShowDialog(true); } }; useFocusEffect(() => { setIsNavigating(false) }) const handleProceedVerification = async () => { try { setIsNavigating(true); const payload = { contId: user.id, transactionId: generateTransactionId(), udf1: "23.77", udf2: "77.34", udf3: "string", udf4: "string", // responseUrl: "http://dev.192.168.6.194:8090/" }; const res = await eSetuAdharVarification(payload); setShowDialog(false); Navigation.navigate(NavigationString.KycWebViewScreen, { res }); } catch (err) { Alert.alert('Error', 'Failed to initiate verification. Please try again.'); setIsNavigating(false); } }; if (isLoading) { return ( Loading profile... ); } if (error) { return ( {error instanceof Error ? error.message : String(error)} refetch()}> Retry ); } if (!data) return null; return ( <> } showsVerticalScrollIndicator={false} > {/* Header with Gradient */} {/* Quick Stats */} {/* Personal Information - Always show */} setEditModalVisible(true)} > {/* Company Details - Only show if has data */} {hasSectionData([ data.company, data.contractorType, data.contOrganization, data.agentWebsite, data.agentRegno ]) && ( )} {/* Address Information - Only show if has data */} {hasSectionData([ data.agentAddress, data.agentCity, data.agentState, data.agentPincode ]) && ( )} {/* Tax & Legal - Only show if has data */} {hasSectionData([ data.orgPAN, data.orgPANname, data.orgPANdob, data.gistnNo, data.comAadharNo ]) && ( )} {/* Registration Status - Only show if has data */} {hasSectionData([ data.contractorClass, data.socialCategory, data.msmeStatus, data.expiryDate ]) && ( )} {/* Edit Modal */} setEditModalVisible(false)} data={data} onSuccess={refetch} /> {/* Aadhaar Verification Dialog */} !isPending && !isNavigating && setShowDialog(false)} loading={isPending || isNavigating} /> ); }; // Edit Profile Modal Component interface EditProfileModalProps { visible: boolean; onClose: () => void; data: ContractorData; onSuccess: () => void; } const EditProfileModal: React.FC = ({ visible, onClose, data, onSuccess, }) => { const { colors } = useTheme(); const styles = useMemo(() => createStyles(colors), [colors]); const [formData, setFormData] = useState({ firstName: data.firstName || '', middleName: data.middleName || '', lastName: data.lastName || '', mobileNo: data.mobileNo || '', emailId: data.emailId || '', fullname: data.fullName || '', }); const [errors, setErrors] = useState>({}); const updateProfileMutation = useUpdateUserProfile(); useEffect(() => { if (visible) { setFormData({ firstName: data.firstName || '', middleName: data.middleName || '', lastName: data.lastName || '', mobileNo: data.mobileNo || '', emailId: data.emailId || '', fullname: data.fullName || '', }); setErrors({}); } }, [visible, data]); const validateForm = (): boolean => { const newErrors: Partial = {}; if (!formData.firstName.trim()) { newErrors.firstName = 'First name is required'; } if (!formData.lastName.trim()) { newErrors.lastName = 'Last name is required'; } if (!formData.emailId.trim()) { newErrors.emailId = 'Email is required'; } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.emailId)) { newErrors.emailId = 'Invalid email format'; } if (!formData.mobileNo.trim()) { newErrors.mobileNo = 'Mobile number is required'; } else if (!/^\d{10}$/.test(formData.mobileNo.replace(/\s/g, ''))) { newErrors.mobileNo = 'Invalid mobile number'; } const generatedFullName = `${formData.firstName} ${formData.middleName} ${formData.lastName}`.trim(); setFormData(prev => ({ ...prev, fullname: generatedFullName })); setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSave = async () => { if (!validateForm()) { return; } try { const result = await updateProfileMutation.mutateAsync(formData); Alert.alert('Success', 'Profile updated successfully'); onSuccess(); onClose(); } catch (error: any) { Alert.alert('Error', error?.message || 'Failed to update profile'); } }; return ( Edit Personal Information First Name * setFormData({ ...formData, firstName: text })} placeholder="Enter first name" placeholderTextColor={colors.textSecondary} /> {errors.firstName && ( {errors.firstName} )} Middle Name setFormData({ ...formData, middleName: text })} placeholder="Enter middle name" placeholderTextColor={colors.textSecondary} /> Last Name * setFormData({ ...formData, lastName: text })} placeholder="Enter last name" placeholderTextColor={colors.textSecondary} /> {errors.lastName && ( {errors.lastName} )} Email * setFormData({ ...formData, emailId: text })} placeholder="Enter email" placeholderTextColor={colors.textSecondary} keyboardType="email-address" autoCapitalize="none" /> {errors.emailId && ( {errors.emailId} )} Mobile Number * setFormData({ ...formData, mobileNo: text })} placeholder="Enter mobile number" placeholderTextColor={colors.textSecondary} keyboardType="phone-pad" maxLength={10} /> {errors.mobileNo && ( {errors.mobileNo} )} Cancel {updateProfileMutation.isPending ? ( ) : ( <> Save )} ); }; // Profile Header Component - OPTIMIZED const ProfileHeader: React.FC = React.memo(({ data, displayName }) => { const { colors } = useTheme(); const styles = useMemo(() => createStyles(colors), [colors]); const getInitials = (name: string): string => { if (!name || typeof name !== 'string' || name.trim().length === 0) { const fallbackName = `${data.firstName || ''} ${data.lastName || ''}`.trim(); if (fallbackName) { return fallbackName .split(' ') .filter((n) => n.length > 0) .map((n) => n[0]) .join('') .toUpperCase() .substring(0, 2); } return 'NA'; } return ( name .split(' ') .filter((n) => n.length > 0) .map((n) => n[0]) .join('') .toUpperCase() .substring(0, 2) || 'NA' ); }; const getStatusColor = (): string => { if (data.isActive === 'Active') return colors.success; return colors.error; }; return ( {getInitials(displayName)} {displayName || 'Unknown User'} {data.role} ); }); ProfileHeader.displayName = 'ProfileHeader'; // Quick Stats Component - Glass Cards const QuickStats: React.FC = React.memo(({ data, onAadhaarPress }) => { const user = useSelector((state: any) => state.token?.token); const { colors } = useTheme(); const stats: StatItem[] = useMemo( () => [ { Icon: CheckCircle, label: 'Aadhaar', value: user?.esetuVerified || 'Pending', color: user?.esetuVerified === 'Not Verified' ? colors.error : colors.success, onPress: onAadhaarPress, }, { Icon: ShieldCheck, label: '2FA', value: data.twoFactorAuthEnabled ? 'Enabled' : 'Disabled', color: data.twoFactorAuthEnabled ? colors.success : colors.textSecondary, }, // ... ], [data.twoFactorAuthEnabled, user?.esetuVerified, onAadhaarPress, colors] ); return ( {stats.map((stat, index) => { const content = ( {stat.value} {stat.label} ); return stat.onPress ? ( {content} ) : ( {content} ); })} ); }); // Info Section Component - Glass Card Container const InfoSection: React.FC = React.memo( ({ title, IconComponent, children, onEdit, showEdit = false, delay = 0 }) => { const { colors } = useTheme(); return ( {title} {showEdit && onEdit && ( )} {children} ); } ); // Info Row Component const InfoRow: React.FC = React.memo( ({ label, value, IconComponent, valueColor }) => { const { colors } = useTheme(); if (!isValidValue(value)) return null; return ( {label} {value} ); } ); // Styles const createStyles = (colors: any) => StyleSheet.create({ container: { flex: 1, paddingHorizontal: 20 }, contentContainer: { paddingBottom: 20, }, centerContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: colors.background, padding: 20, }, loadingText: { marginTop: 16, fontSize: 16, color: colors.textSecondary, fontWeight: '500', }, screenErrorText: { marginTop: 16, fontSize: 16, color: colors.error, textAlign: 'center', fontWeight: '500', }, retryButton: { marginTop: 20, backgroundColor: colors.primary, paddingHorizontal: 32, paddingVertical: 12, borderRadius: 12, elevation: 2, shadowColor: colors.shadow, shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 4, }, retryButtonText: { color: colors.white, fontSize: 16, fontWeight: '600', }, headerGradient: { paddingTop: Platform.OS === 'ios' ? 60 : 40, paddingBottom: 30, alignItems: 'center', }, avatarContainer: { position: 'relative', marginBottom: 16, }, avatar: { width: 100, height: 100, borderRadius: 50, backgroundColor: colors.card, justifyContent: 'center', alignItems: 'center', borderWidth: 4, borderColor: colors.card, elevation: 4, shadowColor: colors.shadow, shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 4, }, avatarText: { fontSize: 36, fontWeight: '700', color: colors.primary, }, statusBadge: { position: 'absolute', bottom: 4, right: 4, width: 24, height: 24, borderRadius: 12, borderWidth: 3, borderColor: colors.card, // Updated justifyContent: 'center', alignItems: 'center', }, statusDot: { width: 10, height: 10, borderRadius: 5, backgroundColor: colors.white, }, headerName: { fontSize: 26, fontWeight: '700', color: colors.text, // Updated marginBottom: 8, textAlign: 'center', paddingHorizontal: 20, }, roleBadge: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.primary + '20', // Updated opacity approach paddingHorizontal: 16, paddingVertical: 6, borderRadius: 20, gap: 6, }, roleText: { color: colors.primary, // Updated fontSize: 14, fontWeight: '600', }, statsContainer: { flexDirection: 'row', marginHorizontal: 16, marginTop: -30, gap: 12, zIndex: 10, }, statCard: { flex: 1, backgroundColor: colors.card, borderRadius: 16, padding: 16, alignItems: 'center', elevation: 4, shadowColor: colors.shadow, shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.15, shadowRadius: 6, }, statValue: { fontSize: 14, fontWeight: '700', color: colors.text, marginTop: 8, textAlign: 'center', }, statLabel: { fontSize: 12, color: colors.textSecondary, marginTop: 4, }, section: { marginHorizontal: 16, marginTop: 24, backgroundColor: colors.card, borderRadius: 20, padding: 20, elevation: 2, shadowColor: colors.shadow, shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, }, sectionHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16, paddingBottom: 12, borderBottomWidth: 1, borderBottomColor: colors.border, }, sectionHeaderLeft: { flexDirection: 'row', alignItems: 'center', flex: 1, }, sectionIconContainer: { width: 40, height: 40, borderRadius: 12, backgroundColor: colors.primary + '10', // Updated justifyContent: 'center', alignItems: 'center', marginRight: 12, }, sectionTitle: { fontSize: 18, fontWeight: '700', color: colors.text, flex: 1, }, sectionContent: { gap: 12, }, editButton: { padding: 8, borderRadius: 8, backgroundColor: colors.primary + '10', // Updated }, infoRow: { borderRadius: 12, overflow: 'hidden', }, infoRowTouchable: { padding: 14, backgroundColor: colors.background, // Updated borderRadius: 12, }, infoRowContent: { flexDirection: 'row', alignItems: 'flex-start', }, infoIconContainer: { width: 32, height: 32, borderRadius: 8, backgroundColor: colors.card, // Updated justifyContent: 'center', alignItems: 'center', marginRight: 12, marginTop: 2, }, infoTextContainer: { flex: 1, gap: 4, }, infoLabel: { fontSize: 13, color: colors.textSecondary, fontWeight: '500', lineHeight: 18, }, infoValue: { fontSize: 15, color: colors.text, fontWeight: '600', lineHeight: 21, }, modalOverlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.5)', justifyContent: 'flex-end', }, modalContent: { borderTopLeftRadius: 24, borderTopRightRadius: 24, maxHeight: '90%', paddingTop: 20, borderRadius: 30 }, modalHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 20, paddingBottom: 16, borderBottomWidth: 1, borderBottomColor: colors.border, }, modalTitle: { fontSize: 20, fontWeight: '700', color: colors.text, }, closeButton: { padding: 4, }, formContainer: { padding: 20, maxHeight: 500, }, inputGroup: { marginBottom: 20, }, inputLabel: { fontSize: 14, fontWeight: '600', color: colors.text, marginBottom: 8, }, input: { borderWidth: 1, borderColor: colors.border, borderRadius: 12, padding: 14, fontSize: 15, color: colors.text, backgroundColor: colors.card, // Updated }, inputError: { borderColor: colors.error, }, errorText: { fontSize: 12, color: colors.error, marginTop: 4, }, modalFooter: { flexDirection: 'row', padding: 20, gap: 12, borderTopWidth: 1, borderTopColor: colors.border, }, button: { flex: 1, paddingVertical: 14, borderRadius: 12, alignItems: 'center', justifyContent: 'center', flexDirection: 'row', gap: 8, }, cancelButton: { backgroundColor: colors.background, // Updated }, saveButton: { backgroundColor: colors.primary, }, cancelButtonText: { fontSize: 16, fontWeight: '600', color: colors.text, }, saveButtonText: { fontSize: 16, fontWeight: '600', color: colors.white, }, navigationOverlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(255, 255, 255, 0.9)', justifyContent: 'center', alignItems: 'center', zIndex: 999, }, }); export default ProfileTab;