This commit is contained in:
Abin Toppo
2026-01-19 14:16:38 +05:30
parent 96d71d1011
commit b6c927367d
9 changed files with 610 additions and 279 deletions
+2 -2
View File
@@ -51,9 +51,9 @@ const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
{/* Actions */} {/* Actions */}
<View style={styles.actions}> <View style={styles.actions}>
{/* <TouchableOpacity style={styles.cancelBtn} onPress={onClose}> <TouchableOpacity style={styles.cancelBtn} onPress={onClose}>
<Text style={styles.cancelText}>{cancelText}</Text> <Text style={styles.cancelText}>{cancelText}</Text>
</TouchableOpacity> */} </TouchableOpacity>
<TouchableOpacity <TouchableOpacity
style={styles.confirmBtn} style={styles.confirmBtn}
+2 -2
View File
@@ -32,7 +32,6 @@ export default function MainStack() {
<Stack.Screen name={NavigationString.Signup} component={Screens.Signup} options={option} /> <Stack.Screen name={NavigationString.Signup} component={Screens.Signup} options={option} />
<Stack.Screen name={NavigationString.Onboarding} component={Screens.Onboarding} options={option} /> <Stack.Screen name={NavigationString.Onboarding} component={Screens.Onboarding} options={option} />
<Stack.Screen name={NavigationString.Login} component={Screens.Login} options={option} /> <Stack.Screen name={NavigationString.Login} component={Screens.Login} options={option} />
<Stack.Screen name={NavigationString.Home} component={BottomTab} options={option} /> <Stack.Screen name={NavigationString.Home} component={BottomTab} options={option} />
<Stack.Screen name={NavigationString.PurchaseList} component={Screens.PurchaseList} options={option} /> <Stack.Screen name={NavigationString.PurchaseList} component={Screens.PurchaseList} options={option} />
<Stack.Screen name={NavigationString.Emd} component={Screens.Emd} options={option} /> <Stack.Screen name={NavigationString.Emd} component={Screens.Emd} options={option} />
@@ -46,7 +45,8 @@ export default function MainStack() {
<Stack.Screen name={NavigationString.Registration} component={Screens.Registration} options={option} /> <Stack.Screen name={NavigationString.Registration} component={Screens.Registration} options={option} />
<Stack.Screen name={NavigationString.Partnership} component={Screens.Partnership} options={option} /> <Stack.Screen name={NavigationString.Partnership} component={Screens.Partnership} options={option} />
<Stack.Screen name={NavigationString.LiveTenderDetail} component={Screens.LiveTenderDetail} options={option} /> <Stack.Screen name={NavigationString.LiveTenderDetail} component={Screens.LiveTenderDetail} options={option} />
<Stack.Screen name={NavigationString.ViewTenderDetail} component={Screens.ViewTenderDetail} options={option} /> <Stack.Screen name={NavigationString.KycWebViewScreen} component={Screens.KycWebViewScreen} options={option} /> <Stack.Screen name={NavigationString.ViewTenderDetail} component={Screens.ViewTenderDetail} options={option} />
<Stack.Screen name={NavigationString.KycWebViewScreen} component={Screens.KycWebViewScreen} options={option} />
</Stack.Navigator> </Stack.Navigator>
); );
} }
+191 -86
View File
@@ -5,7 +5,7 @@ import { useFetchLiveAuction, useFetchPurchaseAuction } from '../../Services/BBP
import colors from '../../Constant/Color'; import colors from '../../Constant/Color';
import LiveAuction from './LiveAuction'; import LiveAuction from './LiveAuction';
import PurchaseAuction from './PurchaseAuction'; import PurchaseAuction from './PurchaseAuction';
import { ShoppingCart } from 'lucide-react-native'; import { ShoppingCart, Lock } from 'lucide-react-native';
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import NavigationString from '../../Constant/NavigationString'; import NavigationString from '../../Constant/NavigationString';
import FontsFamily from '../../Constant/FontsFamily'; import FontsFamily from '../../Constant/FontsFamily';
@@ -21,6 +21,7 @@ const Auction = () => {
const { data: liveAuctionData = {} }: any = useFetchLiveAuction(); const { data: liveAuctionData = {} }: any = useFetchLiveAuction();
const { data: purchaseAuctionData = {} } = useFetchPurchaseAuction(); const { data: purchaseAuctionData = {} } = useFetchPurchaseAuction();
const SelectedPurchaseList = useSelector((state: any) => state.selectPurchaseAuction); const SelectedPurchaseList = useSelector((state: any) => state.selectPurchaseAuction);
const reduxToken = useSelector((state: any) => state.token?.token);
const liveCount = Array.isArray(liveAuctionData?.result) const liveCount = Array.isArray(liveAuctionData?.result)
? liveAuctionData?.pages?.[0]?.totalCount ? liveAuctionData?.pages?.[0]?.totalCount
@@ -31,7 +32,6 @@ const Auction = () => {
: 0; : 0;
const [selectedTab, setSelectedTab] = useState<'live' | 'purchase'>('live'); const [selectedTab, setSelectedTab] = useState<'live' | 'purchase'>('live');
const translateX = useRef(new Animated.Value(0)).current; const translateX = useRef(new Animated.Value(0)).current;
const TAB_WIDTH = useMemo(() => { const TAB_WIDTH = useMemo(() => {
@@ -48,97 +48,136 @@ const Auction = () => {
}).start(); }).start();
}; };
// Check if user is tender type
const isTenderUser = reduxToken?.userType?.toLowerCase() === 'tender';
// Render restricted access message
const renderRestrictedAccess = () => (
<View style={styles.restrictedContainer}>
<View style={styles.restrictedCard}>
<View style={styles.iconContainer}>
<Lock size={64} color={colors.primary} />
</View>
<Text style={styles.restrictedTitle}>Auction Access Restricted</Text>
<Text style={styles.restrictedMessage}>
You are currently logged in as a Tender user. Auction features are not available for your account type.
</Text>
<Text style={styles.restrictedSubMessage}>
To access auction features, please login with an Auction user account.
</Text>
<TouchableOpacity
style={styles.loginButton}
onPress={() => Navigation.navigate(NavigationString.Login)}
>
<Text style={styles.loginButtonText}>Switch to Auction Login</Text>
</TouchableOpacity>
</View>
</View>
);
return ( return (
<View style={styles.container}> <View style={styles.container}>
<InsideHeader title="Auction" showArrow /> <InsideHeader title="Auction" showArrow />
<View style={styles.topRow}> {isTenderUser ? (
<View // Show restricted access message for tender users
style={[ renderRestrictedAccess()
styles.switchWrapper, ) : (
{ // Show normal auction UI for auction users
width: TAB_WIDTH * 2 + SWITCH_INSETS * 2, <>
}, <View style={styles.topRow}>
]}
>
<Animated.View
pointerEvents="none"
style={[
styles.activeTab,
{
width: TAB_WIDTH,
transform: [{ translateX }],
},
]}
/>
<TouchableOpacity
style={[styles.tab, { width: TAB_WIDTH }]}
activeOpacity={0.8}
onPress={() => onTabPress('live', 0)}
>
<Text
style={[
styles.tabText,
selectedTab === 'live' && styles.activeText,
]}
>
Live ({liveCount})
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.tab, { width: TAB_WIDTH }]}
activeOpacity={0.8}
onPress={() => onTabPress('purchase', 1)}
>
<Text
style={[
styles.tabText,
selectedTab === 'purchase' && styles.activeText,
]}
>
Purchased ({purchaseCount})
</Text>
</TouchableOpacity>
</View>
<View>
{SelectedPurchaseList.length ? (
<View <View
style={{ style={[
borderWidth: 2, styles.switchWrapper,
borderColor: colors.white, {
backgroundColor: colors.primaryLight, width: TAB_WIDTH * 2 + SWITCH_INSETS * 2,
position: 'absolute', },
borderRadius: 50, ]}
zIndex: 20,
right: 0,
top: -10,
paddingHorizontal: 2,
}}
> >
<Text style={{ fontSize: 12, color: colors.black }}> <Animated.View
{SelectedPurchaseList.length} pointerEvents="none"
</Text> style={[
</View> styles.activeTab,
) : null} {
<TouchableOpacity width: TAB_WIDTH,
style={{ transform: [{ translateX }],
padding: 10, },
backgroundColor: colors.primaryLight, ]}
borderRadius: 20, />
}}
onPress={() => {
Navigation.navigate(NavigationString.PurchaseList);
}}
>
<ShoppingCart size={22} color={colors.black} strokeWidth={2} />
</TouchableOpacity>
</View>
</View>
{selectedTab === 'live' ? <LiveAuction /> : <PurchaseAuction />} <TouchableOpacity
style={[styles.tab, { width: TAB_WIDTH }]}
activeOpacity={0.8}
onPress={() => onTabPress('live', 0)}
>
<Text
style={[
styles.tabText,
selectedTab === 'live' && styles.activeText,
]}
>
Live ({liveCount})
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.tab, { width: TAB_WIDTH }]}
activeOpacity={0.8}
onPress={() => onTabPress('purchase', 1)}
>
<Text
style={[
styles.tabText,
selectedTab === 'purchase' && styles.activeText,
]}
>
Purchased ({purchaseCount})
</Text>
</TouchableOpacity>
</View>
<View>
{SelectedPurchaseList.length ? (
<View
style={{
borderWidth: 2,
borderColor: colors.white,
backgroundColor: colors.primaryLight,
position: 'absolute',
borderRadius: 50,
zIndex: 20,
right: 0,
top: -10,
paddingHorizontal: 2,
}}
>
<Text style={{ fontSize: 12, color: colors.black }}>
{SelectedPurchaseList.length}
</Text>
</View>
) : null}
<TouchableOpacity
style={{
padding: 10,
backgroundColor: colors.primaryLight,
borderRadius: 20,
}}
onPress={() => {
Navigation.navigate(NavigationString.PurchaseList);
}}
>
<ShoppingCart size={22} color={colors.black} strokeWidth={2} />
</TouchableOpacity>
</View>
</View>
{selectedTab === 'live' ? <LiveAuction /> : <PurchaseAuction />}
</>
)}
</View> </View>
); );
}; };
@@ -191,4 +230,70 @@ const styles = StyleSheet.create({
shadowRadius: 6, shadowRadius: 6,
elevation: 3, elevation: 3,
}, },
restrictedContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 24,
backgroundColor: colors.lightGray,
},
restrictedCard: {
backgroundColor: colors.white,
borderRadius: 24,
padding: 32,
alignItems: 'center',
maxWidth: 400,
width: '100%',
},
iconContainer: {
width: 120,
height: 120,
borderRadius: 60,
backgroundColor: colors.primaryHighlight,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 24,
},
restrictedTitle: {
fontSize: 24,
fontWeight: '700',
color: colors.darkText,
textAlign: 'center',
marginBottom: 16,
fontFamily: FontsFamily.poppinsBold,
},
restrictedMessage: {
fontSize: 16,
color: colors.grayText,
textAlign: 'center',
lineHeight: 24,
marginBottom: 12,
fontFamily: FontsFamily.poppinsRegular,
},
restrictedSubMessage: {
fontSize: 14,
color: colors.grayText,
textAlign: 'center',
lineHeight: 22,
marginBottom: 32,
fontFamily: FontsFamily.poppinsRegular,
},
loginButton: {
backgroundColor: colors.primary,
paddingHorizontal: 32,
paddingVertical: 14,
borderRadius: 12,
shadowColor: colors.primary,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 4,
},
loginButtonText: {
color: colors.white,
fontSize: 16,
fontWeight: '600',
fontFamily: FontsFamily.poppinsSemiBold,
},
}); });
-1
View File
@@ -86,7 +86,6 @@ const Home = () => {
udf2: "77.34", udf2: "77.34",
udf3: "string", udf3: "string",
udf4: "string", udf4: "string",
responseUrl: "https://dev.procurelinc.in/"
}; };
console.log("Payload:", payload); console.log("Payload:", payload);
+213 -85
View File
@@ -52,9 +52,14 @@ import {
X, X,
Save, Save,
} from 'lucide-react-native'; } from 'lucide-react-native';
import { useNavigation } from '@react-navigation/native';
import { useContractorProfile, useUpdateUserProfile } from '../../Services/BBPS/Hooks'; import { useContractorProfile, useUpdateUserProfile, useESetuAdharVarification } from '../../Services/BBPS/Hooks';
import colors from '../../Constant/Color'; import colors from '../../Constant/Color';
import { useSelector } from 'react-redux';
import ConfirmDialog from '../../Constant/Dialog/ConfirmDialog';
import { generateTransactionId } from '../../Helper/uniqueId';
import NavigationString from '../../Constant/NavigationString';
const { width } = Dimensions.get('window'); const { width } = Dimensions.get('window');
@@ -102,10 +107,12 @@ interface ContractorData {
interface ProfileHeaderProps { interface ProfileHeaderProps {
data: ContractorData; data: ContractorData;
displayName: string;
} }
interface QuickStatsProps { interface QuickStatsProps {
data: ContractorData; data: ContractorData;
onAadhaarPress: () => void;
} }
interface InfoSectionProps { interface InfoSectionProps {
@@ -129,6 +136,7 @@ interface StatItem {
label: string; label: string;
value: string; value: string;
color: string; color: string;
onPress?: () => void;
} }
interface UpdateProfilePayload { interface UpdateProfilePayload {
@@ -150,11 +158,61 @@ const isValidValue = (value: any): boolean => {
return true; 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 // Main Component
const ProfileTab: React.FC = () => { const ProfileTab: React.FC = () => {
const { data, refetch, isLoading, error } = useContractorProfile(); const { data, refetch, isLoading, error } = useContractorProfile();
console.log("ioioi",data) const user = useSelector((state: any) => state.token?.token);
const Navigation: any = useNavigation();
const { mutateAsync: eSetuAdharVarification, isPending } = useESetuAdharVarification();
const [editModalVisible, setEditModalVisible] = useState(false); const [editModalVisible, setEditModalVisible] = useState(false);
const [showDialog, setShowDialog] = useState<boolean>(false);
const [isNavigating, setIsNavigating] = useState<boolean>(false);
// Check if user is auction type
const isAuctionUser = user?.userType?.toLowerCase() === 'auction';
// Get display data based on user type
const displayName = isAuctionUser ? user?.fullName : data?.fullName;
const displayMobile = isAuctionUser ? user?.mobileNo : data?.mobileNo;
const handleAadhaarPress = () => {
if (user?.esetuVerified === 'Not Verified') {
setShowDialog(true);
}
};
const handleProceedVerification = async () => {
try {
setIsNavigating(true);
const payload = {
contId: user.id,
transactionId: generateTransactionId(),
udf1: "23.77",
udf2: "77.34",
udf3: "string",
udf4: "string",
responseUrl: "https://dev.procurelinc.in/"
};
const res = await eSetuAdharVarification(payload);
setShowDialog(false);
Navigation.navigate(NavigationString.KycWebViewScreen, { res });
} catch (err) {
console.log("Verification error:", err);
Alert.alert('Error', 'Failed to initiate verification. Please try again.');
setIsNavigating(false);
}
};
if (isLoading) { if (isLoading) {
return ( return (
@@ -200,78 +258,134 @@ const ProfileTab: React.FC = () => {
windowSize={10} windowSize={10}
> >
{/* Header with Gradient */} {/* Header with Gradient */}
<ProfileHeader data={data} /> <ProfileHeader data={data} displayName={displayName || data.fullName} />
{/* Quick Stats */} {/* Quick Stats */}
<QuickStats data={data} /> <QuickStats data={data} onAadhaarPress={handleAadhaarPress} />
{/* Personal Information */} {/* Personal Information - Always show */}
<InfoSection <InfoSection
title="Personal Information" title="Personal Information"
IconComponent={UserCircle} IconComponent={UserCircle}
delay={0} delay={0}
showEdit={true} showEdit={!isAuctionUser}
onEdit={() => setEditModalVisible(true)} onEdit={() => setEditModalVisible(true)}
> >
<InfoRow label="Full Name" value={data.firstName+" "+data.middleName+" "+data.lastName} IconComponent={User} /> <InfoRow
label="Full Name"
value={isAuctionUser ? displayName : `${data.firstName} ${data.middleName} ${data.lastName}`.trim()}
IconComponent={User}
/>
<InfoRow label="Email" value={data.emailId} IconComponent={Mail} /> <InfoRow label="Email" value={data.emailId} IconComponent={Mail} />
<InfoRow <InfoRow
label="Mobile" label="Mobile"
value={`${data.dialCode} ${data.mobileNo}`} value={isAuctionUser ? displayMobile : `${data.dialCode} ${data.mobileNo}`}
IconComponent={Phone} IconComponent={Phone}
/> />
<InfoRow label="Designation" value={data.designation} IconComponent={Briefcase} /> <InfoRow label="Designation" value={data.designation} IconComponent={Briefcase} />
</InfoSection> </InfoSection>
{/* Company Details */} {/* Company Details - Only show if has data */}
<InfoSection title="Company Details" IconComponent={Building2} delay={0}> {hasSectionData([
<InfoRow label="Company" value={data.company} IconComponent={Building} /> data.company,
<InfoRow label="Type" value={data.contractorType} IconComponent={Tag} /> data.contractorType,
<InfoRow label="Organization" value={data.contOrganization} IconComponent={Network} /> data.contOrganization,
<InfoRow label="Website" value={data.agentWebsite} IconComponent={Globe} /> data.agentWebsite,
<InfoRow label="Registration No" value={data.agentRegno} IconComponent={Award} /> data.agentRegno
</InfoSection> ]) && (
<InfoSection title="Company Details" IconComponent={Building2} delay={0}>
<InfoRow label="Company" value={data.company} IconComponent={Building} />
<InfoRow label="Type" value={data.contractorType} IconComponent={Tag} />
<InfoRow label="Organization" value={data.contOrganization} IconComponent={Network} />
<InfoRow label="Website" value={data.agentWebsite} IconComponent={Globe} />
<InfoRow label="Registration No" value={data.agentRegno} IconComponent={Award} />
</InfoSection>
)}
{/* Address Information */} {/* Address Information - Only show if has data */}
<InfoSection title="Address Information" IconComponent={MapPin} delay={0}> {hasSectionData([
<InfoRow label="Address" value={data.agentAddress} IconComponent={Home} /> data.agentAddress,
<InfoRow label="City" value={data.agentCity} IconComponent={Building2} /> data.agentCity,
<InfoRow label="State" value={data.agentState} IconComponent={Map} /> data.agentState,
<InfoRow label="Pincode" value={data.agentPincode} IconComponent={Mailbox} /> data.agentPincode
</InfoSection> ]) && (
<InfoSection title="Address Information" IconComponent={MapPin} delay={0}>
<InfoRow label="Address" value={data.agentAddress} IconComponent={Home} />
<InfoRow label="City" value={data.agentCity} IconComponent={Building2} />
<InfoRow label="State" value={data.agentState} IconComponent={Map} />
<InfoRow label="Pincode" value={data.agentPincode} IconComponent={Mailbox} />
</InfoSection>
)}
{/* Tax & Legal */} {/* Tax & Legal - Only show if has data */}
<InfoSection title="Tax & Legal Information" IconComponent={FileText} delay={0}> {hasSectionData([
<InfoRow label="PAN Number" value={data.orgPAN} IconComponent={CreditCard} /> data.orgPAN,
<InfoRow label="PAN Name" value={data.orgPANname} IconComponent={Type} /> data.orgPANname,
<InfoRow label="PAN DOB" value={data.orgPANdob} IconComponent={Calendar} /> data.orgPANdob,
<InfoRow label="GSTIN" value={data.gistnNo} IconComponent={Receipt} /> data.gistnNo,
<InfoRow label="Aadhar" value={data.comAadharNo} IconComponent={FileText} /> data.comAadharNo
</InfoSection> ]) && (
<InfoSection title="Tax & Legal Information" IconComponent={FileText} delay={0}>
<InfoRow label="PAN Number" value={data.orgPAN} IconComponent={CreditCard} />
<InfoRow label="PAN Name" value={data.orgPANname} IconComponent={Type} />
<InfoRow label="PAN DOB" value={data.orgPANdob} IconComponent={Calendar} />
<InfoRow label="GSTIN" value={data.gistnNo} IconComponent={Receipt} />
<InfoRow label="Aadhar" value={data.comAadharNo} IconComponent={FileText} />
</InfoSection>
)}
{/* Registration Status */} {/* Registration Status - Only show if has data */}
<InfoSection title="Registration Details" IconComponent={ClipboardCheck} delay={0}> {hasSectionData([
<InfoRow label="Class" value={data.contractorClass} IconComponent={Star} /> data.contractorClass,
<InfoRow label="Category" value={data.socialCategory} IconComponent={List} /> data.socialCategory,
<InfoRow label="MSME Status" value={data.msmeStatus} IconComponent={Factory} /> data.msmeStatus,
<InfoRow data.expiryDate
label="Expiry Date" ]) && (
value={data.expiryDate} <InfoSection title="Registration Details" IconComponent={ClipboardCheck} delay={0}>
IconComponent={CalendarClock} <InfoRow label="Class" value={data.contractorClass} IconComponent={Star} />
valueColor={colors.warning} <InfoRow label="Category" value={data.socialCategory} IconComponent={List} />
/> <InfoRow label="MSME Status" value={data.msmeStatus} IconComponent={Factory} />
</InfoSection> <InfoRow
label="Expiry Date"
value={data.expiryDate}
IconComponent={CalendarClock}
valueColor={colors.warning}
/>
</InfoSection>
)}
<View style={{ height: 40 }} /> <View style={{ height: 40 }} />
</ScrollView> </ScrollView>
{/* Edit Modal */} {/* Edit Modal - Only for tender users */}
<EditProfileModal {!isAuctionUser && (
visible={editModalVisible} <EditProfileModal
onClose={() => setEditModalVisible(false)} visible={editModalVisible}
data={data} onClose={() => setEditModalVisible(false)}
onSuccess={refetch} data={data}
onSuccess={refetch}
/>
)}
{/* Aadhaar Verification Dialog */}
<ConfirmDialog
visible={showDialog}
title="Aadhaar Verification Required"
message="To access all features, please complete your Aadhaar verification."
confirmText={isPending || isNavigating ? "Processing..." : "Proceed"}
cancelText="Cancel"
onConfirm={handleProceedVerification}
onClose={() => !isPending && !isNavigating && setShowDialog(false)}
loading={isPending || isNavigating}
/> />
{/* Navigation Overlay */}
{isNavigating && (
<View style={styles.navigationOverlay}>
<ActivityIndicator size="large" color={colors.primary || '#2563EB'} />
<Text style={styles.loadingText}>Loading verification...</Text>
</View>
)}
</> </>
); );
}; };
@@ -303,7 +417,6 @@ const EditProfileModal: React.FC<EditProfileModalProps> = ({
const updateProfileMutation = useUpdateUserProfile(); const updateProfileMutation = useUpdateUserProfile();
// Reset form when modal opens
useEffect(() => { useEffect(() => {
if (visible) { if (visible) {
setFormData({ setFormData({
@@ -341,7 +454,6 @@ const EditProfileModal: React.FC<EditProfileModalProps> = ({
newErrors.mobileNo = 'Invalid mobile number'; newErrors.mobileNo = 'Invalid mobile number';
} }
// Auto-generate fullname
const generatedFullName = `${formData.firstName} ${formData.middleName} ${formData.lastName}`.trim(); const generatedFullName = `${formData.firstName} ${formData.middleName} ${formData.lastName}`.trim();
setFormData(prev => ({ ...prev, fullname: generatedFullName })); setFormData(prev => ({ ...prev, fullname: generatedFullName }));
@@ -376,7 +488,6 @@ const EditProfileModal: React.FC<EditProfileModalProps> = ({
behavior={Platform.OS === 'ios' ? 'padding' : 'height'} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
> >
<View style={styles.modalContent}> <View style={styles.modalContent}>
{/* Header */}
<View style={styles.modalHeader}> <View style={styles.modalHeader}>
<Text style={styles.modalTitle}>Edit Personal Information</Text> <Text style={styles.modalTitle}>Edit Personal Information</Text>
<TouchableOpacity onPress={onClose} style={styles.closeButton}> <TouchableOpacity onPress={onClose} style={styles.closeButton}>
@@ -384,9 +495,7 @@ const EditProfileModal: React.FC<EditProfileModalProps> = ({
</TouchableOpacity> </TouchableOpacity>
</View> </View>
{/* Form */}
<ScrollView style={styles.formContainer} showsVerticalScrollIndicator={false}> <ScrollView style={styles.formContainer} showsVerticalScrollIndicator={false}>
{/* First Name */}
<View style={styles.inputGroup}> <View style={styles.inputGroup}>
<Text style={styles.inputLabel}>First Name *</Text> <Text style={styles.inputLabel}>First Name *</Text>
<TextInput <TextInput
@@ -401,7 +510,6 @@ const EditProfileModal: React.FC<EditProfileModalProps> = ({
)} )}
</View> </View>
{/* Middle Name */}
<View style={styles.inputGroup}> <View style={styles.inputGroup}>
<Text style={styles.inputLabel}>Middle Name</Text> <Text style={styles.inputLabel}>Middle Name</Text>
<TextInput <TextInput
@@ -413,7 +521,6 @@ const EditProfileModal: React.FC<EditProfileModalProps> = ({
/> />
</View> </View>
{/* Last Name */}
<View style={styles.inputGroup}> <View style={styles.inputGroup}>
<Text style={styles.inputLabel}>Last Name *</Text> <Text style={styles.inputLabel}>Last Name *</Text>
<TextInput <TextInput
@@ -428,7 +535,6 @@ const EditProfileModal: React.FC<EditProfileModalProps> = ({
)} )}
</View> </View>
{/* Email */}
<View style={styles.inputGroup}> <View style={styles.inputGroup}>
<Text style={styles.inputLabel}>Email *</Text> <Text style={styles.inputLabel}>Email *</Text>
<TextInput <TextInput
@@ -445,7 +551,6 @@ const EditProfileModal: React.FC<EditProfileModalProps> = ({
)} )}
</View> </View>
{/* Mobile Number */}
<View style={styles.inputGroup}> <View style={styles.inputGroup}>
<Text style={styles.inputLabel}>Mobile Number *</Text> <Text style={styles.inputLabel}>Mobile Number *</Text>
<TextInput <TextInput
@@ -463,7 +568,6 @@ const EditProfileModal: React.FC<EditProfileModalProps> = ({
</View> </View>
</ScrollView> </ScrollView>
{/* Footer Buttons */}
<View style={styles.modalFooter}> <View style={styles.modalFooter}>
<TouchableOpacity <TouchableOpacity
style={[styles.button, styles.cancelButton]} style={[styles.button, styles.cancelButton]}
@@ -495,7 +599,7 @@ const EditProfileModal: React.FC<EditProfileModalProps> = ({
}; };
// Profile Header Component - OPTIMIZED // Profile Header Component - OPTIMIZED
const ProfileHeader: React.FC<ProfileHeaderProps> = React.memo(({ data }) => { const ProfileHeader: React.FC<ProfileHeaderProps> = React.memo(({ data, displayName }) => {
const getInitials = (name: string): string => { const getInitials = (name: string): string => {
if (!name || typeof name !== 'string' || name.trim().length === 0) { if (!name || typeof name !== 'string' || name.trim().length === 0) {
const fallbackName = `${data.firstName || ''} ${data.lastName || ''}`.trim(); const fallbackName = `${data.firstName || ''} ${data.lastName || ''}`.trim();
@@ -537,7 +641,7 @@ const ProfileHeader: React.FC<ProfileHeaderProps> = React.memo(({ data }) => {
> >
<View style={styles.avatarContainer}> <View style={styles.avatarContainer}>
<View style={styles.avatar}> <View style={styles.avatar}>
<Text style={styles.avatarText}>{getInitials(data.fullName)}</Text> <Text style={styles.avatarText}>{getInitials(displayName)}</Text>
</View> </View>
<View style={[styles.statusBadge, { backgroundColor: getStatusColor() }]}> <View style={[styles.statusBadge, { backgroundColor: getStatusColor() }]}>
<View style={styles.statusDot} /> <View style={styles.statusDot} />
@@ -545,9 +649,7 @@ const ProfileHeader: React.FC<ProfileHeaderProps> = React.memo(({ data }) => {
</View> </View>
<Text style={styles.headerName}> <Text style={styles.headerName}>
{data.fullName || {displayName || 'Unknown User'}
`${data.firstName} ${data.lastName}`.trim() ||
'Unknown User'}
</Text> </Text>
<View style={styles.roleBadge}> <View style={styles.roleBadge}>
@@ -561,15 +663,18 @@ const ProfileHeader: React.FC<ProfileHeaderProps> = React.memo(({ data }) => {
ProfileHeader.displayName = 'ProfileHeader'; ProfileHeader.displayName = 'ProfileHeader';
// Quick Stats Component - OPTIMIZED // Quick Stats Component - OPTIMIZED WITH TOUCHABLE AADHAAR
const QuickStats: React.FC<QuickStatsProps> = React.memo(({ data }) => { const QuickStats: React.FC<QuickStatsProps> = React.memo(({ data, onAadhaarPress }) => {
const user = useSelector((state: any) => state.token?.token);
const stats: StatItem[] = useMemo( const stats: StatItem[] = useMemo(
() => [ () => [
{ {
Icon: CheckCircle, Icon: CheckCircle,
label: 'Status', label: 'Aadhaar',
value: data.isActive, value: user?.esetuVerified,
color: colors.success, color: user?.esetuVerified === 'Not Verified' ? colors.error : colors.success,
onPress: onAadhaarPress,
}, },
{ {
Icon: ShieldCheck, Icon: ShieldCheck,
@@ -584,20 +689,37 @@ const QuickStats: React.FC<QuickStatsProps> = React.memo(({ data }) => {
color: colors.info, color: colors.info,
}, },
], ],
[data.isActive, data.twoFactorAuthEnabled, data.registrationStatus] [data.twoFactorAuthEnabled, data.registrationStatus, user?.esetuVerified, onAadhaarPress]
); );
return ( return (
<View style={styles.statsContainer}> <View style={styles.statsContainer}>
{stats.map((stat) => ( {stats.map((stat) => {
<View key={stat.label} style={styles.statCard}> const CardContent = (
<stat.Icon size={28} color={stat.color} /> <View style={styles.statCard}>
<Text style={styles.statValue} numberOfLines={2}> <stat.Icon size={28} color={stat.color} />
{stat.value} <Text style={styles.statValue} numberOfLines={2}>
</Text> {stat.value}
<Text style={styles.statLabel}>{stat.label}</Text> </Text>
</View> <Text style={styles.statLabel}>{stat.label}</Text>
))} </View>
);
if (stat.onPress) {
return (
<TouchableOpacity
key={stat.label}
activeOpacity={0.7}
onPress={stat.onPress}
style={{ flex: 1 }}
>
{CardContent}
</TouchableOpacity>
);
}
return <View key={stat.label} style={{ flex: 1 }}>{CardContent}</View>;
})}
</View> </View>
); );
}); });
@@ -633,7 +755,6 @@ InfoSection.displayName = 'InfoSection';
// Info Row Component - OPTIMIZED // Info Row Component - OPTIMIZED
const InfoRow: React.FC<InfoRowProps> = React.memo( const InfoRow: React.FC<InfoRowProps> = React.memo(
({ label, value, IconComponent, valueColor }) => { ({ label, value, IconComponent, valueColor }) => {
// Return null if value is not valid
if (!isValidValue(value)) { if (!isValidValue(value)) {
return null; return null;
} }
@@ -715,6 +836,7 @@ interface Styles {
saveButton: ViewStyle; saveButton: ViewStyle;
cancelButtonText: TextStyle; cancelButtonText: TextStyle;
saveButtonText: TextStyle; saveButtonText: TextStyle;
navigationOverlay: ViewStyle;
} }
const styles = StyleSheet.create<Styles>({ const styles = StyleSheet.create<Styles>({
@@ -763,7 +885,6 @@ const styles = StyleSheet.create<Styles>({
fontWeight: '600', fontWeight: '600',
}, },
// Header Styles
headerGradient: { headerGradient: {
paddingTop: Platform.OS === 'ios' ? 60 : 40, paddingTop: Platform.OS === 'ios' ? 60 : 40,
paddingBottom: 30, paddingBottom: 30,
@@ -841,7 +962,6 @@ const styles = StyleSheet.create<Styles>({
fontWeight: '600', fontWeight: '600',
}, },
// Stats Styles
statsContainer: { statsContainer: {
flexDirection: 'row', flexDirection: 'row',
marginHorizontal: 16, marginHorizontal: 16,
@@ -874,7 +994,6 @@ const styles = StyleSheet.create<Styles>({
marginTop: 4, marginTop: 4,
}, },
// Section Styles
section: { section: {
marginHorizontal: 16, marginHorizontal: 16,
marginTop: 24, marginTop: 24,
@@ -925,7 +1044,6 @@ const styles = StyleSheet.create<Styles>({
backgroundColor: colors.primaryHighlight, backgroundColor: colors.primaryHighlight,
}, },
// Info Row Styles
infoRow: { infoRow: {
borderRadius: 12, borderRadius: 12,
overflow: 'hidden', overflow: 'hidden',
@@ -966,7 +1084,6 @@ const styles = StyleSheet.create<Styles>({
lineHeight: 21, lineHeight: 21,
}, },
// Modal Styles
modalOverlay: { modalOverlay: {
flex: 1, flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)', backgroundColor: 'rgba(0, 0, 0, 0.5)',
@@ -1058,6 +1175,17 @@ const styles = StyleSheet.create<Styles>({
fontWeight: '600', fontWeight: '600',
color: colors.white, 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; export default ProfileTab;
+3 -2
View File
@@ -12,6 +12,7 @@ const ProceedToBidDetail = () => {
const { data, refetch, isRefetching } = useProceedToBid(tenderDataId?.tenderId); const { data, refetch, isRefetching } = useProceedToBid(tenderDataId?.tenderId);
const { data: tenderResponse, refetch: tenderRefetch }: any = useFetchTenderById(tenderDataId?.tenderId); const { data: tenderResponse, refetch: tenderRefetch }: any = useFetchTenderById(tenderDataId?.tenderId);
const tenderData = tenderResponse?.data; const tenderData = tenderResponse?.data;
console.log("dataopop",tenderData)
const [refreshing, setRefreshing] = React.useState(false); const [refreshing, setRefreshing] = React.useState(false);
const [loading, setLoading] = React.useState(false); const [loading, setLoading] = React.useState(false);
@@ -174,8 +175,8 @@ const ProceedToBidDetail = () => {
<Text style={styles.sectionTitle}>Envelope Submission Status</Text> <Text style={styles.sectionTitle}>Envelope Submission Status</Text>
<View style={styles.statusGrid}> <View style={styles.statusGrid}>
<StatusIndicator status={tenderData?.envelopeASubmittStatus} label="Envelope A" /> <StatusIndicator status={tenderData?.envelopeASubmittStatus} label="Envelope A" />
<StatusIndicator status={tenderData?.envelopeBSubmittStatus} label="Envelope B" /> {tenderData?.showEB &&<StatusIndicator status={tenderData?.envelopeBSubmittStatus} label="Envelope B" />}
<StatusIndicator status={tenderData?.envelopeCSubmittStatus} label="Envelope C" /> {tenderData?.showEC &&<StatusIndicator status={tenderData?.envelopeCSubmittStatus} label="Envelope C" />}
</View> </View>
</View> </View>
+191 -85
View File
@@ -13,7 +13,7 @@ import {
useFetchPurchaseTender, useFetchPurchaseTender,
} from '../../Services/BBPS/Hooks'; } from '../../Services/BBPS/Hooks';
import colors from '../../Constant/Color'; import colors from '../../Constant/Color';
import { ShoppingCart } from 'lucide-react-native'; import { ShoppingCart, Lock } from 'lucide-react-native';
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import NavigationString from '../../Constant/NavigationString'; import NavigationString from '../../Constant/NavigationString';
import FontsFamily from '../../Constant/FontsFamily'; import FontsFamily from '../../Constant/FontsFamily';
@@ -33,6 +33,7 @@ const Tender = () => {
const SelectedPurchaseList = useSelector( const SelectedPurchaseList = useSelector(
(state: any) => state?.selectPurchaseTender, (state: any) => state?.selectPurchaseTender,
); );
const reduxToken = useSelector((state: any) => state.token?.token);
const liveCount = liveTenderData?.pages?.[0]?.totalCount || 0; const liveCount = liveTenderData?.pages?.[0]?.totalCount || 0;
const purchaseCount = purchaseTenderData?.pages?.[0]?.totalCount || 0; const purchaseCount = purchaseTenderData?.pages?.[0]?.totalCount || 0;
@@ -54,97 +55,136 @@ const Tender = () => {
}).start(); }).start();
}; };
// Check if user is auction type
const isAuctionUser = reduxToken?.userType?.toLowerCase() === 'auction';
// Render restricted access message
const renderRestrictedAccess = () => (
<View style={styles.restrictedContainer}>
<View style={styles.restrictedCard}>
<View style={styles.iconContainer}>
<Lock size={64} color={colors.primary} />
</View>
<Text style={styles.restrictedTitle}>Tender Access Restricted</Text>
<Text style={styles.restrictedMessage}>
You are currently logged in as an Auction user. Tender features are not available for your account type.
</Text>
<Text style={styles.restrictedSubMessage}>
To access tender features, please login with a Tender user account.
</Text>
<TouchableOpacity
style={styles.loginButton}
onPress={() => Navigation.navigate(NavigationString.Login)}
>
<Text style={styles.loginButtonText}>Switch to Tender Login</Text>
</TouchableOpacity>
</View>
</View>
);
return ( return (
<View style={styles.container}> <View style={styles.container}>
<InsideHeader title="Tender" showArrow /> <InsideHeader title="Tender" showArrow />
<View style={styles.topRow}> {isAuctionUser ? (
<View // Show restricted access message for auction users
style={[ renderRestrictedAccess()
styles.switchWrapper, ) : (
{ // Show normal tender UI for tender users
width: TAB_WIDTH * 2 + SWITCH_INSETS * 2, <>
}, <View style={styles.topRow}>
]}
>
<Animated.View
pointerEvents="none"
style={[
styles.activeTab,
{
width: TAB_WIDTH,
transform: [{ translateX }],
},
]}
/>
<TouchableOpacity
style={[styles.tab, { width: TAB_WIDTH }]}
activeOpacity={0.8}
onPress={() => onTabPress('live', 0)}
>
<Text
style={[
styles.tabText,
selectedTab === 'live' && styles.activeText,
]}
>
Live ({liveCount})
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.tab, { width: TAB_WIDTH }]}
activeOpacity={0.8}
onPress={() => onTabPress('purchase', 1)}
>
<Text
style={[
styles.tabText,
selectedTab === 'purchase' && styles.activeText,
]}
>
Purchased ({purchaseCount})
</Text>
</TouchableOpacity>
</View>
<View>
{SelectedPurchaseList.length ? (
<View <View
style={{ style={[
borderWidth: 2, styles.switchWrapper,
borderColor: colors.white, {
backgroundColor: colors.primaryLight, width: TAB_WIDTH * 2 + SWITCH_INSETS * 2,
position: 'absolute', },
borderRadius: 50, ]}
zIndex: 20,
right: 0,
top: -10,
paddingHorizontal: 2,
}}
> >
<Text style={{ fontSize: 12, color: colors.black }}> <Animated.View
{SelectedPurchaseList.length} pointerEvents="none"
</Text> style={[
</View> styles.activeTab,
) : null} {
<TouchableOpacity width: TAB_WIDTH,
style={{ transform: [{ translateX }],
padding: 10, },
backgroundColor: colors.primaryLight, ]}
borderRadius: 20, />
}}
onPress={() => {
Navigation.navigate(NavigationString.PurchaseTenderList);
}}
>
<ShoppingCart size={22} color={colors.black} strokeWidth={2} />
</TouchableOpacity>
</View>
</View>
{selectedTab === 'live' ? <LiveTender /> : <PurchaseTenderAllList />} <TouchableOpacity
style={[styles.tab, { width: TAB_WIDTH }]}
activeOpacity={0.8}
onPress={() => onTabPress('live', 0)}
>
<Text
style={[
styles.tabText,
selectedTab === 'live' && styles.activeText,
]}
>
Live ({liveCount})
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.tab, { width: TAB_WIDTH }]}
activeOpacity={0.8}
onPress={() => onTabPress('purchase', 1)}
>
<Text
style={[
styles.tabText,
selectedTab === 'purchase' && styles.activeText,
]}
>
Purchased ({purchaseCount})
</Text>
</TouchableOpacity>
</View>
<View>
{SelectedPurchaseList.length ? (
<View
style={{
borderWidth: 2,
borderColor: colors.white,
backgroundColor: colors.primaryLight,
position: 'absolute',
borderRadius: 50,
zIndex: 20,
right: 0,
top: -10,
paddingHorizontal: 2,
}}
>
<Text style={{ fontSize: 12, color: colors.black }}>
{SelectedPurchaseList.length}
</Text>
</View>
) : null}
<TouchableOpacity
style={{
padding: 10,
backgroundColor: colors.primaryLight,
borderRadius: 20,
}}
onPress={() => {
Navigation.navigate(NavigationString.PurchaseTenderList);
}}
>
<ShoppingCart size={22} color={colors.black} strokeWidth={2} />
</TouchableOpacity>
</View>
</View>
{selectedTab === 'live' ? <LiveTender /> : <PurchaseTenderAllList />}
</>
)}
</View> </View>
); );
}; };
@@ -197,4 +237,70 @@ const styles = StyleSheet.create({
shadowRadius: 6, shadowRadius: 6,
elevation: 3, elevation: 3,
}, },
restrictedContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 24,
backgroundColor: colors.lightGray,
},
restrictedCard: {
backgroundColor: colors.white,
borderRadius: 24,
padding: 32,
alignItems: 'center',
maxWidth: 400,
width: '100%',
},
iconContainer: {
width: 120,
height: 120,
borderRadius: 60,
backgroundColor: colors.primaryHighlight,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 24,
},
restrictedTitle: {
fontSize: 24,
fontWeight: '700',
color: colors.darkText,
textAlign: 'center',
marginBottom: 16,
fontFamily: FontsFamily.poppinsBold,
},
restrictedMessage: {
fontSize: 16,
color: colors.grayText,
textAlign: 'center',
lineHeight: 24,
marginBottom: 12,
fontFamily: FontsFamily.poppinsRegular,
},
restrictedSubMessage: {
fontSize: 14,
color: colors.grayText,
textAlign: 'center',
lineHeight: 22,
marginBottom: 32,
fontFamily: FontsFamily.poppinsRegular,
},
loginButton: {
backgroundColor: colors.primary,
paddingHorizontal: 32,
paddingVertical: 14,
borderRadius: 12,
shadowColor: colors.primary,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 4,
},
loginButtonText: {
color: colors.white,
fontSize: 16,
fontWeight: '600',
fontFamily: FontsFamily.poppinsSemiBold,
},
}); });
+5 -13
View File
@@ -122,7 +122,7 @@ const ViewTenderDetail = () => {
</View> </View>
{item.fieldValue && ( {item.fieldValue && (
<View style={styles.fieldRow}> <View style={[styles.fieldRow,{backgroundColor:colors.lightGray,borderRadius:20,padding:10}]}>
<Type size={16} color={colors.gray} /> <Type size={16} color={colors.gray} />
<Text style={styles.fieldValue}>{item.fieldValue}</Text> <Text style={styles.fieldValue}>{item.fieldValue}</Text>
</View> </View>
@@ -143,15 +143,7 @@ const ViewTenderDetail = () => {
</TouchableOpacity> </TouchableOpacity>
)} )}
<View style={styles.metaInfo}> </View>
<Text style={styles.metaText}>
Type: {getLabelTypeText(item.labelTypeA || item.labelTypeB)}
</Text>
{item.docEnvelopeSeqId && (
<Text style={styles.metaText}>ID: {item.docEnvelopeSeqId}</Text>
)}
</View>
</View>
))} ))}
</View> </View>
); );
@@ -515,12 +507,12 @@ const styles = StyleSheet.create({
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
marginBottom: 12, marginBottom:8,
}, },
labelName: { labelName: {
fontSize: 16, fontSize: 16,
fontWeight: '700', fontWeight: '700',
color: colors.darkText, color: colors.primary,
}, },
mandatoryTag: { mandatoryTag: {
fontSize: 12, fontSize: 12,
@@ -534,7 +526,7 @@ const styles = StyleSheet.create({
fieldRow: { fieldRow: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
marginTop: 8, marginTop: 4,
}, },
fieldValue: { fieldValue: {
fontSize: 14, fontSize: 14,
+3 -3
View File
@@ -42,8 +42,8 @@ const Screens = {
Registration, Registration,
Partnership, Partnership,
LiveTenderDetail, LiveTenderDetail,
ProfileTab, , ProfileTab,
KycWebViewScreen KycWebViewScreen,
ViewTenderDetail ViewTenderDetail,
} }
export default Screens export default Screens