updated code with dsc and device binding
This commit is contained in:
+281
-240
@@ -1,7 +1,6 @@
|
||||
import React, { useState, useRef, useMemo } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
Animated,
|
||||
@@ -12,40 +11,39 @@ import {
|
||||
useFetchLiveTender,
|
||||
useFetchPurchaseTender,
|
||||
} from '../../Services/ApiCall/Hooks';
|
||||
import colors from '../../Constant/Color';
|
||||
import { ShoppingCart, Lock } from 'lucide-react-native';
|
||||
import { ShoppingCart, Lock, Radio, Package } from 'lucide-react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import NavigationString from '../../Constant/NavigationString';
|
||||
import FontsFamily from '../../Constant/FontsFamily';
|
||||
import { useSelector } from 'react-redux';
|
||||
import LiveTender from './LiveTender';
|
||||
import PurchaseTenderAllList from './PurchaseTenderAllList';
|
||||
import { useTheme } from '../../Hooks/useTheme';
|
||||
import AppText from '../../components/AppText';
|
||||
|
||||
const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
||||
const TAB_HEIGHT = 44;
|
||||
const TAB_HEIGHT = 48;
|
||||
const HORIZONTAL_PADDING = 32;
|
||||
const SWITCH_INSETS = 2;
|
||||
const SWITCH_INSETS = 3;
|
||||
|
||||
const Tender = () => {
|
||||
const { colors, spacing, shadows } = useTheme();
|
||||
const Navigation = useNavigation() as any;
|
||||
const { data: liveTenderData }: any = useFetchLiveTender();
|
||||
const { data: purchaseTenderData }: any = useFetchPurchaseTender();
|
||||
const SelectedPurchaseList = useSelector(
|
||||
(state: any) => state?.selectPurchaseTender,
|
||||
);
|
||||
const SelectedPurchaseList = useSelector((state: any) => state?.selectPurchaseTender);
|
||||
const reduxToken = useSelector((state: any) => state.token?.token);
|
||||
|
||||
const liveCount = liveTenderData?.pages?.[0]?.totalCount || 0;
|
||||
const purchaseCount = purchaseTenderData?.pages?.[0]?.totalCount || 0;
|
||||
|
||||
const [selectedTab, setSelectedTab] = useState<'live' | 'purchase'>('live');
|
||||
const translateX = useRef(new Animated.Value(0)).current;
|
||||
|
||||
const TAB_WIDTH = useMemo(() => {
|
||||
const availableWidth = SCREEN_WIDTH - HORIZONTAL_PADDING - SWITCH_INSETS * 2;
|
||||
return availableWidth / 2.4;
|
||||
return availableWidth / 2;
|
||||
}, []);
|
||||
|
||||
const [selectedTab, setSelectedTab] = useState<'live' | 'purchase'>('purchase');
|
||||
const translateX = useRef(new Animated.Value(TAB_WIDTH)).current;
|
||||
|
||||
const onTabPress = (tab: 'live' | 'purchase', index: number) => {
|
||||
setSelectedTab(tab);
|
||||
Animated.spring(translateX, {
|
||||
@@ -55,252 +53,295 @@ const Tender = () => {
|
||||
}).start();
|
||||
};
|
||||
|
||||
// Check if user is auction type
|
||||
const isAuctionUser = reduxToken?.userType?.toLowerCase() === 'auction';
|
||||
const cartCount = SelectedPurchaseList.length;
|
||||
|
||||
// Render restricted access message
|
||||
const renderRestrictedAccess = () => (
|
||||
<View style={styles.restrictedContainer}>
|
||||
<View style={styles.restrictedCard}>
|
||||
<View style={styles.iconContainer}>
|
||||
<Lock size={64} color={colors.primary} />
|
||||
const styles = makeStyles(colors, spacing, shadows, TAB_WIDTH, TAB_HEIGHT);
|
||||
|
||||
if (isAuctionUser) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<InsideHeader title="Tender" showArrow />
|
||||
<View style={styles.restrictedContainer}>
|
||||
<View style={styles.restrictedCard}>
|
||||
{/* Double-ring Lock Icon */}
|
||||
<View style={styles.lockRing}>
|
||||
<View style={styles.lockInner}>
|
||||
<Lock size={40} color={colors.primary} strokeWidth={1.8} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<AppText variant="h2" centered color={colors.text} style={styles.restrictedTitle as any}>
|
||||
Access Restricted
|
||||
</AppText>
|
||||
|
||||
<AppText variant="body" centered color={colors.textSecondary} style={styles.restrictedMessage as any}>
|
||||
You're logged in as an{' '}
|
||||
<AppText variant="body" color={colors.primary} style={{ fontWeight: '700' } as any}>
|
||||
Auction user
|
||||
</AppText>
|
||||
{'. '}Tender features are not available for your account type.
|
||||
</AppText>
|
||||
|
||||
<View style={styles.hintBox}>
|
||||
<AppText variant="bodySmall" centered color={colors.textSecondary}>
|
||||
Switch to a Tender account to access live & purchase tender features.
|
||||
</AppText>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.loginButton}
|
||||
activeOpacity={0.85}
|
||||
onPress={() => Navigation.navigate(NavigationString.Login)}
|
||||
>
|
||||
<AppText variant="button" color={colors.buttonText} style={styles.loginBtnText as any}>
|
||||
Switch to Tender Login
|
||||
</AppText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</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 (
|
||||
<View style={styles.container}>
|
||||
<InsideHeader title="Tender" showArrow />
|
||||
|
||||
{isAuctionUser ? (
|
||||
// Show restricted access message for auction users
|
||||
renderRestrictedAccess()
|
||||
) : (
|
||||
// Show normal tender UI for tender users
|
||||
<>
|
||||
<View style={styles.topRow}>
|
||||
<View
|
||||
{/* Tab Row */}
|
||||
<View style={styles.topRow}>
|
||||
{/* Segmented Control */}
|
||||
<View
|
||||
style={[
|
||||
styles.switchWrapper,
|
||||
{ width: TAB_WIDTH * 2 + SWITCH_INSETS * 2 },
|
||||
]}
|
||||
>
|
||||
{/* Sliding pill */}
|
||||
<Animated.View
|
||||
pointerEvents="none"
|
||||
style={[
|
||||
styles.activeTab,
|
||||
{ width: TAB_WIDTH, transform: [{ translateX }] },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* Live Tab */}
|
||||
<TouchableOpacity
|
||||
style={[styles.tab, { width: TAB_WIDTH }]}
|
||||
activeOpacity={0.8}
|
||||
onPress={() => onTabPress('live', 0)}
|
||||
>
|
||||
<Radio
|
||||
size={13}
|
||||
color={selectedTab === 'live' ? colors.primary : colors.textSecondary}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<AppText
|
||||
variant="caption"
|
||||
color={selectedTab === 'live' ? colors.primary : colors.textSecondary}
|
||||
style={[
|
||||
styles.switchWrapper,
|
||||
{
|
||||
width: TAB_WIDTH * 2 + SWITCH_INSETS * 2,
|
||||
},
|
||||
styles.tabText,
|
||||
selectedTab === 'live' && (styles.activeTabText as any),
|
||||
]}
|
||||
>
|
||||
<Animated.View
|
||||
pointerEvents="none"
|
||||
style={[
|
||||
styles.activeTab,
|
||||
{
|
||||
width: TAB_WIDTH,
|
||||
transform: [{ translateX }],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
Live {liveCount ? `(${liveCount})` : ''}
|
||||
</AppText>
|
||||
</TouchableOpacity>
|
||||
|
||||
<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>
|
||||
{/* Purchase Tab */}
|
||||
<TouchableOpacity
|
||||
style={[styles.tab, { width: TAB_WIDTH }]}
|
||||
activeOpacity={0.8}
|
||||
onPress={() => onTabPress('purchase', 1)}
|
||||
>
|
||||
<Package
|
||||
size={13}
|
||||
color={selectedTab === 'purchase' ? colors.primary : colors.textSecondary}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<AppText
|
||||
variant="caption"
|
||||
color={selectedTab === 'purchase' ? colors.primary : colors.textSecondary}
|
||||
style={[
|
||||
styles.tabText,
|
||||
selectedTab === 'purchase' && (styles.activeTabText as any),
|
||||
]}
|
||||
>
|
||||
Purchased {purchaseCount ? `(${purchaseCount})` : ''}
|
||||
</AppText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<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>
|
||||
{/* Content */}
|
||||
{selectedTab === 'live' ? <LiveTender /> : <PurchaseTenderAllList />}
|
||||
|
||||
<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>
|
||||
{/* Cart Floating Button */}
|
||||
<TouchableOpacity
|
||||
style={styles.cartFloatingButton}
|
||||
activeOpacity={0.9}
|
||||
onPress={() => Navigation.navigate(NavigationString.PurchaseTenderList)}
|
||||
>
|
||||
<ShoppingCart size={24} color={colors.buttonText} strokeWidth={2} />
|
||||
{cartCount > 0 && (
|
||||
<View style={styles.cartBadge}>
|
||||
<AppText variant="caption" color={colors.primary} style={styles.cartBadgeText as any}>
|
||||
{cartCount}
|
||||
</AppText>
|
||||
</View>
|
||||
|
||||
{selectedTab === 'live' ? <LiveTender /> : <PurchaseTenderAllList />}
|
||||
</>
|
||||
)}
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default Tender;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.white,
|
||||
},
|
||||
topRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
switchWrapper: {
|
||||
flexDirection: 'row',
|
||||
marginVertical: 14,
|
||||
backgroundColor: '#f1f5f9',
|
||||
borderRadius: 18,
|
||||
padding: 2,
|
||||
height: TAB_HEIGHT,
|
||||
},
|
||||
tab: {
|
||||
height: TAB_HEIGHT - 4,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
zIndex: 2,
|
||||
},
|
||||
tabText: {
|
||||
fontSize: 14,
|
||||
color: '#64748b',
|
||||
lineHeight: 18,
|
||||
fontFamily: FontsFamily.poppinsSemiBold,
|
||||
},
|
||||
activeText: {
|
||||
color: colors.primary,
|
||||
},
|
||||
activeTab: {
|
||||
position: 'absolute',
|
||||
top: 2,
|
||||
left: 2,
|
||||
height: TAB_HEIGHT - 4,
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: 16,
|
||||
shadowColor: '#000',
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 6,
|
||||
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,
|
||||
},
|
||||
});
|
||||
/* ─── Styles factory ─── */
|
||||
const makeStyles = (colors: any, spacing: any, shadows: any, TAB_WIDTH: number, TAB_HEIGHT: number) =>
|
||||
StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
|
||||
/* ── Tab Row ── */
|
||||
topRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: spacing.m,
|
||||
paddingVertical: spacing.s + 2,
|
||||
},
|
||||
switchWrapper: {
|
||||
flexDirection: 'row',
|
||||
backgroundColor: colors.surface,
|
||||
borderRadius: 16,
|
||||
padding: SWITCH_INSETS,
|
||||
height: TAB_HEIGHT,
|
||||
...shadows.light,
|
||||
},
|
||||
tab: {
|
||||
height: TAB_HEIGHT - SWITCH_INSETS * 2,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 5,
|
||||
zIndex: 2,
|
||||
},
|
||||
tabText: {
|
||||
fontWeight: '600',
|
||||
letterSpacing: 0.3,
|
||||
} as any,
|
||||
activeTabText: {
|
||||
fontWeight: '700',
|
||||
} as any,
|
||||
activeTab: {
|
||||
position: 'absolute',
|
||||
top: SWITCH_INSETS,
|
||||
left: SWITCH_INSETS,
|
||||
height: TAB_HEIGHT - SWITCH_INSETS * 2,
|
||||
backgroundColor: colors.background,
|
||||
borderRadius: 13,
|
||||
...shadows.medium,
|
||||
},
|
||||
|
||||
/* ── Cart Floating Button ── */
|
||||
cartFloatingButton: {
|
||||
position: 'absolute',
|
||||
bottom: spacing.xl + 80,
|
||||
right: spacing.l,
|
||||
width: 56,
|
||||
height: 56,
|
||||
borderRadius: 28,
|
||||
backgroundColor: colors.primary,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
...shadows.strong,
|
||||
zIndex: 999,
|
||||
},
|
||||
cartBadge: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
minWidth: 20,
|
||||
height: 20,
|
||||
borderRadius: 10,
|
||||
backgroundColor: colors.background,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 4,
|
||||
borderWidth: 1.5,
|
||||
borderColor: colors.primary,
|
||||
},
|
||||
cartBadgeText: {
|
||||
fontSize: 10,
|
||||
fontWeight: '800',
|
||||
lineHeight: 13,
|
||||
} as any,
|
||||
|
||||
/* ── Restricted Access ── */
|
||||
restrictedContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: spacing.l,
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
restrictedCard: {
|
||||
backgroundColor: colors.surface,
|
||||
borderRadius: 28,
|
||||
padding: spacing.l,
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
maxWidth: 400,
|
||||
...shadows.strong,
|
||||
},
|
||||
lockRing: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
borderRadius: 50,
|
||||
backgroundColor: `${colors.primary}12`,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: spacing.l,
|
||||
borderWidth: 1.5,
|
||||
borderColor: `${colors.primary}25`,
|
||||
},
|
||||
lockInner: {
|
||||
width: 72,
|
||||
height: 72,
|
||||
borderRadius: 36,
|
||||
backgroundColor: `${colors.primary}18`,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
restrictedTitle: {
|
||||
marginBottom: spacing.s + 4,
|
||||
} as any,
|
||||
restrictedMessage: {
|
||||
marginBottom: spacing.s,
|
||||
lineHeight: 24,
|
||||
} as any,
|
||||
hintBox: {
|
||||
backgroundColor: colors.overlay,
|
||||
borderRadius: 14,
|
||||
paddingHorizontal: spacing.m,
|
||||
paddingVertical: spacing.s + 2,
|
||||
marginBottom: spacing.l,
|
||||
width: '100%',
|
||||
},
|
||||
loginButton: {
|
||||
backgroundColor: colors.buttonPrimary,
|
||||
paddingHorizontal: spacing.l,
|
||||
paddingVertical: 14,
|
||||
borderRadius: 16,
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
...shadows.medium,
|
||||
},
|
||||
loginBtnText: {
|
||||
letterSpacing: 1.2,
|
||||
} as any,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user