Auciton process complete

This commit is contained in:
rohit-kumar-fullstack
2026-01-08 16:56:02 +05:30
parent 743d571519
commit 7145f0df11
19 changed files with 908 additions and 332 deletions
+112 -14
View File
@@ -1,12 +1,20 @@
import React, { memo, useCallback, useRef } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, Platform } from 'react-native';
import React, { memo, useCallback, useRef, useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, Platform, TextInput, Alert } from 'react-native';
import Animated, { FadeInUp, Layout, interpolate, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
import colors from '../../../Constant/Color';
import AuctionItemDetail from '../../Auction/Modal/AuctionItemDetail';
import MainStyle from '../../../Styles/MainStyle';
import { Loader } from '../../../Component/Index';
import { useSelector } from 'react-redux';
import axios from 'axios';
import Variables from '../../../Constant/Variable';
import { showErrorAlert, showSuccessAlert } from '../../../Constant/ShowDailog';
const BiddingCard = ({ item, bidValue, onIncrease, onDecrease, onPlaceBid, SelectedAuction }: any) => {
const [showDetail, setShowDetail] = React.useState(false);
const BiddingCard = ({ item, bidValue, onIncrease, onDecrease, onPlaceBid, SelectedAuction, allBoolean, setAllBoolean }: any) => {
const Token = useSelector((state: any) => state.token.token)
const [showDetail, setShowDetail] = useState(false);
const [value, setValue] = useState(`${item.bidAmount}`);
const [allState, setAllState] = useState({ isLoading: false })
const isMin = bidValue <= item.bidAmount;
// 🔒 Persist expand state safely
@@ -26,6 +34,11 @@ const BiddingCard = ({ item, bidValue, onIncrease, onDecrease, onPlaceBid, Selec
opacity: interpolate(animation.value, [0, 0.5, 1], [0, 0, 1]),
marginTop: interpolate(animation.value, [0, 1], [0, 15]),
}));
const bodyStyle2 = useAnimatedStyle(() => ({
height: interpolate(animation.value, [0, 1], [0, 100]),
opacity: interpolate(animation.value, [0, 0.5, 1], [0, 0, 1]),
marginTop: interpolate(animation.value, [0, 1], [0, 15]),
}));
const arrowStyle = useAnimatedStyle(() => ({
transform: [
@@ -34,6 +47,53 @@ const BiddingCard = ({ item, bidValue, onIncrease, onDecrease, onPlaceBid, Selec
},
],
}));
const MAX_VALUE = Number(item.bidAmount);
const handleChange = (text: string) => {
let cleaned = text.replace(/[^0-9.]/g, '');
if ((cleaned.match(/\./g) || []).length > 1) return;
setValue(cleaned);
};
const oneTimePlaceBid = async () => {
try {
if (Number(value) < MAX_VALUE) {
showErrorAlert("Enter the greather vlaue to current bid value")
return false
}
setAllState((prev: any) => ({ ...prev, isLoading: true }))
const payload = {
itemId: item.id,
bidAmount: Number(value),
auctionId: SelectedAuction.auctionId,
contractorId: Token.contractorId,
fullName: Token.fullName,
};
const res = await axios.post(
`${Variables.socketUrl}bidding/placeBidForAuction`,
[payload],
{ headers: { Authentication: `Bearer ${Token.token}` } }
);
if (res.data.statusCode === 200) {
showSuccessAlert(res.data.message);
}
setAllBoolean((prev: any) => ({ ...prev, reload: !prev.reload }))
} catch (error) {
if (axios.isAxiosError(error)) {
showErrorAlert(error.response?.data.message)
} else {
console.log('UNKNOWN ERROR:', error);
}
} finally {
setAllState((prev: any) => ({ ...prev, isLoading: false }))
}
}
// console.log(item, 'jkjjj');
return (
<View>
@@ -80,7 +140,7 @@ const BiddingCard = ({ item, bidValue, onIncrease, onDecrease, onPlaceBid, Selec
<View style={styles.priceRow}>
<View>
<Text style={styles.label}>Current Bid</Text>
<Text style={styles.mainPrice}>₹{item?.bidAmount}</Text>
<Text style={styles.mainPrice}>₹{item?.bidAmount || item.auctionStartValueFigure}</Text>
</View>
<View style={{ alignItems: 'flex-end' }}>
@@ -92,12 +152,12 @@ const BiddingCard = ({ item, bidValue, onIncrease, onDecrease, onPlaceBid, Selec
</View>
{/* COLLAPSIBLE BODY */}
<Animated.View style={[styles.collapsibleContainer, bodyStyle]}>
<Animated.View style={[styles.collapsibleContainer, (!allBoolean.rebid && item.contractorId == undefined) ? bodyStyle : bodyStyle2]}>
<View style={styles.divider} />
<Text style={styles.bidLabel}>Set Your Bid</Text>
<View style={styles.stepperContainer}>
{!allBoolean.oneTimeBid && <View style={styles.stepperContainer}>
<TouchableOpacity
onPress={onDecrease}
disabled={isMin}
@@ -114,16 +174,30 @@ const BiddingCard = ({ item, bidValue, onIncrease, onDecrease, onPlaceBid, Selec
<TouchableOpacity onPress={onIncrease} style={styles.stepBtn}>
<Text style={styles.stepText}>+</Text>
</TouchableOpacity>
</View>
</View>}
{
(allBoolean.oneTimeBid || allBoolean.rebid) && <View style={styles.inputBox2}>
<Text style={styles.currency2}>₹</Text>
<TouchableOpacity
style={styles.submitBtn}
onPress={onPlaceBid}
// activeOpacity={0.85}
<TextInput
value={value}
onChangeText={handleChange}
keyboardType="decimal-pad"
placeholder="0.00"
style={styles.input}
placeholderTextColor="#94A3B8"
/>
</View>
}
{(!allBoolean.rebid && item.contractorId == undefined) ? <TouchableOpacity
style={[styles.submitBtn]}
onPress={(allBoolean.oneTimeBid || allBoolean.rebid) ? oneTimePlaceBid : onPlaceBid}
>
<Text style={styles.submitBtnText}>Confirm Bid</Text>
</TouchableOpacity>
{
allState.isLoading ? <Loader size='small' color={colors.white} /> : <Text style={styles.submitBtnText}>Confirm Bid</Text>
}
</TouchableOpacity> : null}
</Animated.View>
</Animated.View>
:
@@ -135,7 +209,31 @@ const BiddingCard = ({ item, bidValue, onIncrease, onDecrease, onPlaceBid, Selec
};
export default memo(BiddingCard);
const styles = StyleSheet.create({
inputBox2: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#F1F5F9',
borderRadius: 12,
height: 45,
paddingHorizontal: 12,
width: '100%',
marginBottom: 20
},
currency2: {
fontSize: 16,
fontWeight: 'bold',
color: '#64748B',
marginRight: 4,
},
input: {
flex: 1,
fontSize: 18,
fontWeight: '700',
color: '#0F172A',
width: '100%'
},
card: {
backgroundColor: '#FFF',
borderRadius: 24,
+155 -9
View File
@@ -11,9 +11,12 @@ import { useNavigation } from '@react-navigation/native';
import { useSelector } from 'react-redux';
import { useAuctionItem, useEmdCheck } from '../../Services/BBPS/Hooks';
import { apiCall } from '../../Axios/Axios';
import { AUCTION_ITEM_SUBMIT, CHECK_TECHNICAL_DOCUMENTAION, GET_TECHNICAL_DOCUMENTAION } from '../../Services/BBPS/ApiUrls';
import { AUCITON_SINGLE_EMD, AUCTION_ITEM_SUBMIT, CHECK_TECHNICAL_DOCUMENTAION, GET_AUCTION_DETAILS, GET_TECHNICAL_DOCUMENTAION } from '../../Services/BBPS/ApiUrls';
import TemplateFormModal from './Component/TemplateFormModal';
import { SelectedItem } from './Type/BidType';
import FontsFamily from '../../Constant/FontsFamily';
import { all } from 'axios';
import { showSuccessAlert } from '../../Constant/ShowDailog';
const Emd = () => {
@@ -21,7 +24,7 @@ const Emd = () => {
const SelectedAuction = useSelector((state: any) => state.auction.auction)
const [selectedItems, setSelectedItems] = useState<SelectedItem[]>([]);
const [acceptedPolicy, setAcceptedPolicy] = useState(false);
const [allBoolean, setAllBoolean] = useState({ isSubmitEmdLoading: false, isEmdComplete: false, auctionItems: [], technicalDocumentStatus: false, selectTemplate: {}, technicalDocModalVisible: false })
const [allBoolean, setAllBoolean]: any = useState({ isSubmitEmdLoading: false, isEmdComplete: false, auctionItems: [], technicalDocumentStatus: false, selectTemplate: {}, technicalDocModalVisible: false, emdType: false, fileName: '', file: {} })
const { mutate } = useEmdCheck()
const { mutate: AuctionItemMutate, isPending } = useAuctionItem()
@@ -62,8 +65,15 @@ const Emd = () => {
}
};
const pickFileSingle = async () => {
try {
const res = await pick({ type: ['application/pdf'] });
const file = res[0];
setAllBoolean((prev: any) => ({ ...prev, file: { name: file.name ?? 'document.pdf', uri: file.uri, type: 'application/pdf' } }))
} catch {
console.log('Pick cancelled');
}
};
/* ---------------- Render Item ---------------- */
const renderItem = ({ item }: any) => {
const selected = isSelected(item.id);
@@ -201,15 +211,33 @@ const Emd = () => {
setAllBoolean((prev: any) => ({ ...prev, isLoading: false }))
}
}
const getAuction = async () => {
const selectedRes = await apiCall<any>('get', GET_AUCTION_DETAILS, {}, { id: SelectedAuction.auctionId });
const emdType = String(selectedRes.data.emd)
.trim()
.replace(/\s+/g, ' ')
.toLowerCase();
if (selectedRes.statusCode == 200 && emdType === 'auction wise') {
return true
} else {
return false
}
}
const AlreadyEmdCheck = async () => {
try {
mutate({ auctionNumber: SelectedAuction.auctionNumber }, {
onSuccess: (res => {
console.log(SelectedAuction.auctionNumber, res , SelectedAuction,'iiiiiiiiiiiiiiiiiiiiiiiii');
onSuccess: (async (res) => {
if (res.statusCode == 200) {
if (res.message == 'No EMD Documents') {
const result = await getAuction()
if (result) {
setAllBoolean((prev: any) => ({ ...prev, emdType: true }))
return
}
setAllBoolean((prev: any) => ({ ...prev, isEmdComplete: false }))
getAuctionItem()
} else {
@@ -307,7 +335,53 @@ const Emd = () => {
getTemplateDocumentation()
}
}
const singleEmdSubmit = async () => {
try {
setAllBoolean((prev: any) => ({ ...prev, isSubmitEmdLoading: true }))
if (!acceptedPolicy) {
Alert.alert('Policy Required', 'Please accept terms');
return;
}
const formData = new FormData();
formData.append('auctionId', SelectedAuction.auctionId);
formData.append('productImageName', allBoolean.fileName);
formData.append('files', allBoolean.file);
const response = await apiCall<any>(
'post',
AUCITON_SINGLE_EMD,
formData,
{},
'multipart/form-data'
);
if (response?.statusCode === 200) {
showSuccessAlert(response.message || 'EMD submitted successfully');
if (SelectedAuction.auctionPattern === 'Forward') {
Navigation.navigate(NavigationString.StartBidding)
} else {
Navigation.navigate(NavigationString.StartReverseBidding)
}
}
} catch (error: any) {
if (error?.response) {
console.log('Server error:', error.response.data);
} else if (error?.request) {
console.log('Network error:', error.request);
} else {
console.log('Error message:', error.message);
}
} finally {
setAllBoolean((prev: any) => ({ ...prev, isSubmitEmdLoading: false }))
}
};
useEffect(() => {
// getAuction()
AlreadyEmdCheck()
}, [])
@@ -323,7 +397,49 @@ const Emd = () => {
<Skelton />
) : allBoolean.isEmdComplete ? (
<EmdComplete onNext={EmdContinueHandle} />
) : <>
) : allBoolean.emdType ? <View style={{ flex: 1 }}>
<View style={styles.container2}>
<Text style={{ color: colors.black, fontSize: 17, fontFamily: FontsFamily.poppinsSemiBold }}>Document Name</Text>
<TextInput
style={styles.input2}
placeholder="Enter document name...."
value={allBoolean.fileName}
editable={true}
placeholderTextColor={colors.grayText}
onChangeText={(text) => { setAllBoolean((prev: any) => ({ ...prev, fileName: text })) }}
/>
<TouchableOpacity style={styles.button} onPress={() => { pickFileSingle() }}>
<Text style={styles.buttonText}>{allBoolean?.file?.name ? allBoolean?.file?.name : 'Choose File'}</Text>
</TouchableOpacity>
</View>
<View style={styles.bottom}>
<BouncyCheckbox
size={18}
fillColor={colors.primary}
iconStyle={{ borderRadius: 5, borderWidth: 1.5, borderColor: colors.primary }}
innerIconStyle={{ borderRadius: 5 }}
isChecked={acceptedPolicy}
onPress={() => setAcceptedPolicy(p => !p)}
text="I agree to Terms & Privacy Policy"
textStyle={styles.policyText}
/>
<TouchableOpacity
style={[
styles.submitBtn,
!acceptedPolicy && styles.disabled,
]}
onPress={singleEmdSubmit}
disabled={!acceptedPolicy}
>
{
allBoolean.isSubmitEmdLoading ? <Loader size='small' color={colors.white} /> : <Text style={styles.submitText}>Submit EMD</Text>
}
</TouchableOpacity>
</View>
</View> : <>
<FlatList
data={allBoolean.auctionItems}
keyExtractor={(item: any) => item.auctionId}
@@ -369,9 +485,29 @@ export default Emd;
const styles = StyleSheet.create({
container: {
flex: 1,
paddingHorizontal: 10
// paddingHorizontal: 10,
backgroundColor: colors.white
},
container2: {
paddingHorizontal: 10,
paddingVertical: 10,
backgroundColor: colors.white,
elevation: 2,
borderRadius: 10,
marginTop: 20,
marginHorizontal: 10
},
button: {
backgroundColor: colors.primary,
padding: 12,
borderRadius: 6,
alignItems: 'center',
},
buttonText: {
color: '#fff',
fontWeight: '600',
},
card: {
backgroundColor: '#FFFFFF',
marginHorizontal: 5,
@@ -433,6 +569,16 @@ const styles = StyleSheet.create({
fontSize: 14,
color: colors.black
},
input2: {
borderWidth: 1,
borderColor: '#CBD5E1',
borderRadius: 14,
padding: 12,
backgroundColor: '#FFFFFF',
fontSize: 14,
color: colors.black,
marginBottom: 20
},
uploadBtn: {
marginTop: 12,
+112 -41
View File
@@ -7,7 +7,7 @@ import { useWebSocketService } from '../../socket/Socket';
import Variables from '../../Constant/Variable';
import { useSelector } from 'react-redux';
import { apiCall } from '../../Axios/Axios';
import { FORWARD_AUCTION_ITEM, REVERSE_AUCTION_ITEM, SELECTED_AUCTION_ITEM } from '../../Services/BBPS/ApiUrls';
import { AUCTION_ONE_TIME_BID, FORWARD_AUCTION_ITEM, GET_AUCTION_DETAILS, REVERSE_AUCTION_ITEM, SELECTED_AUCTION_ITEM } from '../../Services/BBPS/ApiUrls';
import BiddingCard from './Component/BiddingCard';
import { CommonActions, useFocusEffect, useNavigation } from '@react-navigation/native';
import NavigationString from '../../Constant/NavigationString';
@@ -20,7 +20,7 @@ const StartBidding = () => {
const Navigation: any = useNavigation()
const [items, setItems] = useState<any>([]);
const [bidValues, setBidValues] = useState<Record<number, number>>({});
const [allBoolean, setAllBoolean] = useState({ showSuccess: false, showDialog: false, bidPayload: {} })
const [allBoolean, setAllBoolean] = useState({ showSuccess: false, showDialog: false, bidPayload: {}, oneTimeBid: false, rebid: false, reload: false })
const { subscribe, createRoom, placeBid } = useWebSocketService(
Variables.webSocketUrl,
() => console.log('Connected!'),
@@ -28,6 +28,25 @@ const StartBidding = () => {
loginUser.token,
);
const getAuction = async () => {
const selectedRes = await apiCall<any>('get', GET_AUCTION_DETAILS, {}, { id: SelectedAuction.auctionId });
if (selectedRes.statusCode == 200 && selectedRes.data.contractorId.length) {
setAllBoolean((prev: any) => ({ ...prev, oneTimeBid: true }))
}
}
const getOneTimeBidValue = async () => {
try {
const selectedRes = await apiCall<any>('get', AUCTION_ONE_TIME_BID, {}, { auctionId: SelectedAuction.auctionId });
if (selectedRes.statusCode == 200) {
return selectedRes.data
} else {
return []
}
} catch (error) {
}
}
const getAllAcutionItems = async () => {
try {
const payload = { auctionNumber: SelectedAuction.auctionNumber };
@@ -36,13 +55,9 @@ const StartBidding = () => {
if (SelectedAuction.auctionPattern === 'Forward') {
const res = await apiCall<any>('get', FORWARD_AUCTION_ITEM, {}, payload);
if (res?.statusCode === 200) auctionItem = res.data || [];
} else {
const res = await apiCall<any>('get', REVERSE_AUCTION_ITEM, {}, payload);
if (res?.statusCode === 200) auctionItem = res.data || [];
}
const selectedRes = await apiCall<any>('get', SELECTED_AUCTION_ITEM, {}, payload);
console.log(selectedRes,'jhhhhhhhhhh');
const uniqueArray: number[] = Array.from(
new Set(selectedRes?.data || [])
@@ -52,47 +67,96 @@ console.log(selectedRes,'jhhhhhhhhhh');
uniqueArray.includes(item.id)
);
if (SelectedAuction.auctionCategories == 'Live') {
const result = await createRoom(SelectedAuction.auctionNumber);
const result = await createRoom(SelectedAuction.auctionNumber);
subscribe(`/topic/room/${SelectedAuction.auctionNumber}`, data => {
console.log('🔥 BID UPDATE:', data);
});
const bidAmountMap2 = new Map<number, { bidAmount: number; contractorId: number }>();
(result || []).forEach((r: any) => {
bidAmountMap2.set(r.auctionItemId, {
bidAmount: r.bidAmount,
contractorId: r.contractorId,
subscribe(`/topic/room/${SelectedAuction.auctionNumber}`, data => {
console.log('🔥 BID UPDATE:', data);
});
});
const bidAmountMap2 = new Map<number, { bidAmount: number; contractorId: number }>();
(result || []).forEach((r: any) => {
bidAmountMap2.set(r.auctionItemId, {
bidAmount: r.bidAmount,
contractorId: r.contractorId,
});
});
// Check One Time bid enable butoon
if (SelectedAuction.auctionCategories != 'Live') {
const myBid = result.find(
(bid: any) => bid.contractorId === loginUser.contractorId
);
if (myBid) {
setAllBoolean((prev: any) => ({ ...prev, oneTimeBid: true }))
}
}
const bidAmountMap = new Map<number, number>();
const bidAmountMap = new Map<number, number>();
(result || []).forEach((r: any) => {
bidAmountMap.set(r.auctionItemId, r.bidAmount);
});
(result || []).forEach((r: any) => {
bidAmountMap.set(r.auctionItemId, r.bidAmount);
});
const updatedFilteredItems = filteredItems.map(item => {
const bidData = bidAmountMap2.get(item.id);
return {
...item,
bidAmount: bidData?.bidAmount ?? item.auctionStartValueFigure,
bidAvilable: bidData?.bidAmount ?? 0,
me: bidData?.contractorId == loginUser.contractorId ? true : false
};
});
const updatedFilteredItems = filteredItems.map(item => {
const bidData = bidAmountMap2.get(item.id);
return {
...item,
bidAmount: bidData?.bidAmount ?? item.auctionStartValueFigure,
bidAvilable: bidData?.bidAmount ?? 0,
me: bidData?.contractorId == loginUser.contractorId ? true : false
};
});
const initialBids: Record<number, number> = {};
updatedFilteredItems.forEach(item => {
initialBids[item.id] = item.bidAmount;
});
setBidValues(initialBids);
setItems(updatedFilteredItems);
const initialBids: Record<number, number> = {};
updatedFilteredItems.forEach(item => {
initialBids[item.id] = item.bidAmount;
});
setBidValues(initialBids);
setItems(updatedFilteredItems);
} else {
let result = []
if (SelectedAuction.rebidAllowed) {
setAllBoolean((prev: any) => ({ ...prev, rebid: true }))
}
if (SelectedAuction.auctionCategories != 'Live') {
result = await getOneTimeBidValue()
}
const bidMap = new Map<number, any>();
result.forEach((bid: any) => {
bidMap.set(bid.auctionItemId, bid);
});
const mergedData = auctionItem.map((item: any) => {
const bid = bidMap.get(item.id);
if (!bid) {
return {
...item,
bidAmount: item.auctionStartValueFigure,
contractorId: item.contractorId,
};
}
return {
...item,
bidAmount: bid.bidAmount,
bidId: bid.bidId,
contractorId: bid.contractorId,
contractorName: bid.contractorName,
me: bid.contractorId === loginUser.contractorId,
bidTime: bid.bidTime,
};
});
const initialBids: Record<number, number> = {};
mergedData.forEach(item => {
initialBids[item.id] = item.bidAmount;
});
setBidValues(initialBids);
setItems(mergedData)
setBidValues(initialBids);
}
} catch (error) {
console.error('Error fetching auction items:', error);
}
@@ -122,11 +186,14 @@ console.log(selectedRes,'jhhhhhhhhhh');
}
});
useEffect(() => {
if (SelectedAuction.auctionCategories == 'One-time') {
getAuction()
}
getAllAcutionItems();
subscribe(`/topic/room/${SelectedAuction.auctionNumber}`, (data) => { });
}, []);
}, [allBoolean.reload]);
useEffect(() => {
const initial: Record<number, number> = {};
@@ -233,6 +300,8 @@ console.log(selectedRes,'jhhhhhhhhhh');
return () => subscription.remove();
}, [Navigation])
);
// console.log(bidValues, 'jjfdlsjfldsjfl');
return (
<View style={styles.container}>
<InsideHeader title="Bidding Panel" showArrow />
@@ -261,9 +330,11 @@ console.log(selectedRes,'jhhhhhhhhhh');
onDecrease={() => handleUpdate(item.id, -item.bidVariationValue)}
onPlaceBid={() => { submitBid(item) }}
SelectedAuction={SelectedAuction}
allBoolean={allBoolean}
setAllBoolean={setAllBoolean}
/>
)}
contentContainerStyle={{ padding: 16 }}
contentContainerStyle={{ padding: 16, paddingBottom: 300 }}
/>
{allBoolean.showSuccess && (