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
+8
View File
@@ -1,6 +1,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<!-- Android 12 and below -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<!-- Android 13+ -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<application
android:name=".MainApplication"
+137
View File
@@ -30,8 +30,10 @@
"react-native": "0.79.0",
"react-native-alert-notification": "^0.4.2",
"react-native-app-intro-slider": "^4.0.4",
"react-native-blob-util": "^0.24.6",
"react-native-bouncy-checkbox": "^4.1.2",
"react-native-date-picker": "^5.0.13",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "^2.30.0",
"react-native-gifted-charts": "^1.4.70",
"react-native-haptic-feedback": "^2.3.3",
@@ -2210,6 +2212,27 @@
"deprecated": "Use @eslint/object-schema instead",
"license": "BSD-3-Clause"
},
"node_modules/@isaacs/balanced-match": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
"integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
"license": "MIT",
"engines": {
"node": "20 || >=22"
}
},
"node_modules/@isaacs/brace-expansion": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
"license": "MIT",
"dependencies": {
"@isaacs/balanced-match": "^4.0.1"
},
"engines": {
"node": "20 || >=22"
}
},
"node_modules/@isaacs/ttlcache": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz",
@@ -4968,6 +4991,11 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT"
},
"node_modules/base-64": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -10589,6 +10617,15 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/minipass": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"license": "ISC",
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
@@ -11084,6 +11121,31 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"license": "MIT"
},
"node_modules/path-scurry": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz",
"integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==",
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^11.0.0",
"minipass": "^7.1.2"
},
"engines": {
"node": "20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/path-scurry/node_modules/lru-cache": {
"version": "11.2.4",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz",
"integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==",
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
}
},
"node_modules/path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
@@ -11597,6 +11659,56 @@
"integrity": "sha512-Zkjaol6X3BbZkHUpVDj2LjdidpS6rCgKi0fx80xgGKa0pHxBRd4swWTv2bHnnvu5k1/HXwYk0mY2TbK+2jHl5w==",
"license": "MIT"
},
"node_modules/react-native-blob-util": {
"version": "0.24.6",
"resolved": "https://registry.npmjs.org/react-native-blob-util/-/react-native-blob-util-0.24.6.tgz",
"integrity": "sha512-e4231VTT0bO1Yh9KGtY6jXq7pkLXgDTz+ziClEEbiGwWBuG92Z+wFRLptlzdVbibW8XtrO1XrdtiE8AOlplEXQ==",
"license": "MIT",
"dependencies": {
"base-64": "0.1.0",
"glob": "13.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ronradtke"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-blob-util/node_modules/glob": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz",
"integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==",
"license": "BlueOak-1.0.0",
"dependencies": {
"minimatch": "^10.1.1",
"minipass": "^7.1.2",
"path-scurry": "^2.0.0"
},
"engines": {
"node": "20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/react-native-blob-util/node_modules/minimatch": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/brace-expansion": "^5.0.0"
},
"engines": {
"node": "20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/react-native-bouncy-checkbox": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/react-native-bouncy-checkbox/-/react-native-bouncy-checkbox-4.1.2.tgz",
@@ -11616,6 +11728,25 @@
"react-native": ">= 0.64.3"
}
},
"node_modules/react-native-fs": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.20.0.tgz",
"integrity": "sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ==",
"license": "MIT",
"dependencies": {
"base-64": "^0.1.0",
"utf8": "^3.0.0"
},
"peerDependencies": {
"react-native": "*",
"react-native-windows": "*"
},
"peerDependenciesMeta": {
"react-native-windows": {
"optional": true
}
}
},
"node_modules/react-native-gesture-handler": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.30.0.tgz",
@@ -13602,6 +13733,12 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/utf8": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz",
"integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==",
"license": "MIT"
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+2
View File
@@ -32,8 +32,10 @@
"react-native": "0.79.0",
"react-native-alert-notification": "^0.4.2",
"react-native-app-intro-slider": "^4.0.4",
"react-native-blob-util": "^0.24.6",
"react-native-bouncy-checkbox": "^4.1.2",
"react-native-date-picker": "^5.0.13",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "^2.30.0",
"react-native-gifted-charts": "^1.4.70",
"react-native-haptic-feedback": "^2.3.3",
@@ -5,7 +5,6 @@ export interface AuctionItem {
[key: string]: any;
}
// ✅ DEFAULT = EMPTY ARRAY
const initialState: AuctionItem[] = [];
export const selectPurchaseAuctionSlice = createSlice({
+54 -56
View File
@@ -1,52 +1,40 @@
import React, { useCallback, useState, useEffect } from 'react';
import React, { useCallback, useState, useEffect, useRef } from 'react';
import { View, FlatList, ActivityIndicator, RefreshControl, Text } from 'react-native';
import Animated, { FadeInDown, FadeOutUp } from 'react-native-reanimated';
import LiveAuctionTile from './LiveAuctionTile';
import { useNavigation } from '@react-navigation/native';
import { useFetchLiveAuction } from '../../Services/BBPS/Hooks';
import { SearchInput } from '../../Component/Serach/SeacrhInput';
import { Skelton } from '../../Component/Index';
import colors from '../../Constant/Color';
import { useDispatch, useSelector } from 'react-redux';
import { selectPurchaseAuctionSlice, togglePurchaseAuction } from '../../Redux/Slices/selectPurchaseAuction';
import { togglePurchaseAuction } from '../../Redux/Slices/selectPurchaseAuction';
import { apiCall } from '../../Axios/Axios';
import { GET_MY_AUCTION, LIVE_AUCTION } from '../../Services/BBPS/ApiUrls';
import moment from 'moment';
import { GET_MY_AUCTION } from '../../Services/BBPS/ApiUrls';
const LiveAuction = () => {
const navigation: any = useNavigation();
const Dispatch = useDispatch()
const onEndReachedCalledDuringMomentum = useRef(true);
const SelectedPurchaseList = useSelector((state: any) => state.selectPurchaseAuction);
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isRefetching, refetch }: any = useFetchLiveAuction();
const [showRefreshBanner, setShowRefreshBanner] = useState(false);
const [allBoolean, setAllBoolean] = useState({ liveAuctionData: [] })
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isRefetching, refetch, isPending }: any = useFetchLiveAuction();
useEffect(() => {
if (isRefetching) {
setShowRefreshBanner(true);
} else {
const timer = setTimeout(() => {
setShowRefreshBanner(false);
}, 1000);
// My Methods
return () => clearTimeout(timer);
const getMyAuction = async () => {
try {
const res = await apiCall<any>('get', `${GET_MY_AUCTION}`,);
if (res?.statusCode === 200) {
const purchasedIds = new Set(res.data.map((item: any) => item.auctionId));
const filteredLive = data?.result.map((item: any) => ({ ...item, active: !purchasedIds.has(item.auctionId) }));
setAllBoolean(prev => ({ ...prev, liveAuctionData: filteredLive }));
}
} catch (error) {
console.error('getMyAuction error', error);
} finally {
setAllBoolean(prev => ({ ...prev, isLoading: false }));
}
}, [isRefetching]);
const loadMore = useCallback(() => {
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
}, [hasNextPage, isFetchingNextPage]);
const renderFooter = useCallback(() => {
if (!isFetchingNextPage) return null;
return (
<View style={{ paddingVertical: 12 }}>
<ActivityIndicator size="small" color={colors.primary} />
</View>
);
}, [isFetchingNextPage]);
};
const renderItem = useCallback(
({ item, index }: any) => {
@@ -68,32 +56,35 @@ const LiveAuction = () => {
[SelectedPurchaseList]
);
const getMyAuction = async () => {
try {
const loadMore = useCallback(() => {
if (onEndReachedCalledDuringMomentum.current) return;
const res = await apiCall<any>('get', `${GET_MY_AUCTION}`,);
if (!hasNextPage || isFetchingNextPage) return;
if (res?.statusCode === 200) {
const purchasedIds = new Set(
res.data.map((item: any) => item.auctionId),
);
fetchNextPage();
onEndReachedCalledDuringMomentum.current = true;
}, [hasNextPage, isFetchingNextPage, fetchNextPage]);
const filteredLive = data?.result.map((item: any) => ({
...item,
active: !purchasedIds.has(item.auctionId), // false if match, true otherwise
}));
const renderFooter = useCallback(() => {
if (!isFetchingNextPage) return null;
return (
<View style={{ paddingVertical: 12 }}>
<ActivityIndicator size="small" color={colors.primary} />
</View>
);
}, [isFetchingNextPage]);
setAllBoolean(prev => ({
...prev,
liveAuctionData: filteredLive
}));
}
} catch (error) {
console.error('getMyAuction error', error);
} finally {
setAllBoolean(prev => ({ ...prev, isLoading: false }));
useEffect(() => {
if (isRefetching) {
setShowRefreshBanner(true);
} else {
const timer = setTimeout(() => {
setShowRefreshBanner(false);
}, 1000);
return () => clearTimeout(timer);
}
};
}, [isRefetching]);
useEffect(() => {
if (data?.result?.length > 0) {
@@ -148,27 +139,34 @@ const LiveAuction = () => {
<FlatList
data={allBoolean.liveAuctionData ?? []}
keyExtractor={(item: any) => String(item.auctionNumber)}
renderItem={renderItem}
showsVerticalScrollIndicator={false}
contentContainerStyle={{ paddingBottom: 50, paddingTop: 10 }}
onEndReached={loadMore}
onEndReachedThreshold={0.2}
onEndReachedThreshold={0.1}
onMomentumScrollBegin={() => {
onEndReachedCalledDuringMomentum.current = false;
}}
ListFooterComponent={renderFooter}
refreshControl={
<RefreshControl
refreshing={isRefetching}
onRefresh={refetch}
tintColor="transparent" // Set to transparent if you only want your custom banner to show
tintColor="transparent"
colors={[colors.primary]}
/>
}
initialNumToRender={6}
maxToRenderPerBatch={6}
windowSize={7}
removeClippedSubviews
updateCellsBatchingPeriod={50}
/>
</View>
);
};
-2
View File
@@ -21,8 +21,6 @@ const LiveAuctionTile: React.FC<LiveAuctionTileProps> = ({
return (
<View style={styles.cardWrapper}>
{/* Status Tag */}
{
!showDetail ?
<>
+113 -66
View File
@@ -1,4 +1,4 @@
import React, { memo, useState } from 'react';
import React, { memo, useEffect, useState } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ScrollView, SafeAreaView, Platform, StatusBar } from 'react-native';
import Modal from 'react-native-modal';
import moment from 'moment';
@@ -6,26 +6,29 @@ import { Briefcase, Calendar, FileText, Download, Eye, Layers, ArrowLeft, Clock
import colors from '../../../Constant/Color';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import AuctionItemDetail from './AuctionItemDetail';
import { useAucitonDocDownload, useAuctionItem, useGetAuciton, useGetAucitonDocument } from '../../../Services/BBPS/Hooks';
import ReactNativeBlobUtil from 'react-native-blob-util';
import { AUCTION_DOCUMENT_DOWNLOAD, ITEM_DOCUMENT_DOWNLOAD } from '../../../Services/BBPS/ApiUrls';
import { useSelector } from 'react-redux';
import { Loader } from '../../../Component/Index';
interface Props {
visible: boolean;
onClose: () => void;
data?: any;
}
interface Props { visible: boolean; onClose: () => void; data?: any }
const AuctionFullDetail: React.FC<Props> = ({ visible, onClose, data }) => {
const inset = useSafeAreaInsets()
const [allBoolean, setAllBoolean] = useState({ showItemDetail: false })
const { data: auctionData, isLoading } = useGetAuciton({ auctionId: data?.auctionId })
const [aucitonDetail, setAuciotnDetail] = useState<any>(auctionData)
const [allBoolean, setAllBoolean] = useState({ showItemDetail: false, selectedItem: {}, fileLoading: false });
const { data: auctionDocuments } = useGetAucitonDocument({ auctionId: data?.auctionId })
const { mutate: downloadDocument, isPending } = useAucitonDocDownload()
const { mutate } = useAuctionItem()
const formatDate = (date?: string) => {
if (!date) return '-';
return moment(date, 'DD-MM-YYYY HH:mm:ss').format('DD MMM YYYY, hh:mm A');
};
const isLive = data?.purchaseEnd
? moment().isBefore(moment(data.purchaseEnd, 'DD-MM-YYYY HH:mm:ss'))
: false;
const isLive = data?.purchaseEnd ? moment().isBefore(moment(data.purchaseEnd, 'DD-MM-YYYY HH:mm:ss')) : false;
// Internal Components
const Section = ({ title, icon: Icon, children }: any) => (
<View style={styles.sectionContainer}>
<View style={styles.sectionHeader}>
@@ -43,6 +46,64 @@ const AuctionFullDetail: React.FC<Props> = ({ visible, onClose, data }) => {
</View>
);
const handleDownload = async (id: number, fileName: string) => {
try {
setAllBoolean((prev: any) => ({ ...prev, fileLoading: true }));
const { dirs } = ReactNativeBlobUtil.fs;
// ✅ PUBLIC DOWNLOADS DIRECTORY
const path = `${dirs.DownloadDir}/${fileName}.pdf`;
console.log('📂 Saving to:', path);
const downloadUrl = `${AUCTION_DOCUMENT_DOWNLOAD}?documentId=${id}&isAmendment=false`;
console.log('🌐 Download URL:', downloadUrl);
const response = await ReactNativeBlobUtil.config({
fileCache: false, // important for public folder
addAndroidDownloads: {
useDownloadManager: true,
notification: true,
path,
mime: 'application/pdf',
title: `${fileName}.pdf`,
description: 'Downloading document',
mediaScannable: true, // 👈 makes it visible instantly
},
}).fetch('GET', downloadUrl, {
Accept: 'application/pdf',
});
console.log('✅ Download completed');
console.log('📦 Status:', response.info().status);
console.log('📦 Headers:', response.info().headers);
console.log('📍 Saved at:', path);
const stat = await ReactNativeBlobUtil.fs.stat(path);
console.log('📦 File size:', stat.size);
} catch (error: any) {
console.log('❌ Download failed');
console.log('❌ Error:', error?.message || error);
} finally {
setAllBoolean((prev: any) => ({ ...prev, fileLoading: false }));
}
};
useEffect(() => {
mutate({ auctionNumber: data?.auctionNumber, status: data?.auctionPattern }, {
onSuccess: (res) => {
setAuciotnDetail((prev: any) => ({ ...prev, auctionItems: res.data, auctionDocuments }))
}, onError: (err) => {
console.log(err, '-----auction item error');
}
});
}, [data.auctionNumber, auctionDocuments]);
return (
<Modal
isVisible={visible}
@@ -94,12 +155,11 @@ const AuctionFullDetail: React.FC<Props> = ({ visible, onClose, data }) => {
{/* 2. Work Description */}
<Section title="Auction Details" icon={Briefcase}>
<View style={styles.grid}>
<InfoBox label="Auction Name" value={'Auction of cars'} />
<InfoBox label="Category Of Auction " value={'Live'} />
<InfoBox label="Auto Time Increment " value={'yes'} />
<InfoBox label="Auction Fees (In Figure) " value={'500'} />
<InfoBox label="Auction Fees (In Words)" value={'Five Hundred'} />
<InfoBox label="Auction Keyword" value={'Dev Furniture, Furnture'} />
<InfoBox label="Auction Name" value={auctionData?.auctionOfWork} />
<InfoBox label="Category Of Auction " value={auctionData?.auctionCategories} />
<InfoBox label="Auction Fees (In Figure) " value={auctionData?.auctionFees} />
<InfoBox label="Auction Fees (In Words)" value={auctionData?.auctionFeesInWord} />
<InfoBox label="Auction Keyword" value={auctionData?.auctionKeywordName} />
</View>
</Section>
@@ -129,68 +189,55 @@ const AuctionFullDetail: React.FC<Props> = ({ visible, onClose, data }) => {
<View style={styles.tableWrapper}>
<View style={styles.tableHead}>
<Text style={[styles.tableLabel, { flex: 2 }]}>Item</Text>
<Text style={styles.tableLabel}>Qty</Text>
<Text style={styles.tableLabel}>Time</Text>
<Text style={[styles.tableLabel, { textAlign: 'right' }]}>View</Text>
</View>
{/* Dummy Item Row */}
<View style={styles.tableRow}>
<View style={{ flex: 2 }}>
<Text style={styles.itemMain}>Sick Sari / Laundry</Text>
<Text style={styles.itemSub}>Category: Sari</Text>
{aucitonDetail?.auctionItems?.map((item: any, index: number) => (
<View style={styles.tableRow}>
<View style={{ flex: 2 }}>
<Text style={styles.itemMain}>{item.productName}</Text>
<Text style={styles.itemSub}>Category: {item?.categoryName}</Text>
</View>
<Text style={styles.itemQty}>{item.timeDurationType}</Text>
<TouchableOpacity style={styles.actionIcon} onPress={() => { setAllBoolean((prev: any) => ({ ...prev, showItemDetail: true, selectedItem: item })) }}>
<Eye size={18} color={colors.primary} />
</TouchableOpacity>
</View>
<Text style={styles.itemQty}>120,000</Text>
<TouchableOpacity style={styles.actionIcon} onPress={() => { setAllBoolean((prev: any) => ({ ...prev, showItemDetail: true })) }}>
<Eye size={18} color={colors.primary} />
</TouchableOpacity>
</View>
))}
</View>
</Section>
{/* 5. Downloads */}
<Section title="Downloads" icon={Download}>
<TouchableOpacity style={styles.fileCard}>
<View style={styles.fileIcon}><FileText size={20} color="#ef4444" /></View>
<View style={{ flex: 1 }}>
<Text style={styles.fileName}>Auction_Notice_Main.pdf</Text>
<Text style={styles.fileSize}>1.2 MB • PDF</Text>
</View>
<Download size={20} color="#94a3b8" />
</TouchableOpacity>
{aucitonDetail?.auctionDocuments?.map((doc: any, index: number) => (
<TouchableOpacity style={styles.fileCard} onPress={() => {
handleDownload(
doc.auctionDocumentId,
doc.fileName.replace('.pdf', '')
)
}}>
<View style={styles.fileIcon}><FileText size={20} color="#ef4444" /></View>
<View style={{ flex: 1 }}>
<Text style={styles.fileName}>{doc.fileName}</Text>
</View>
{allBoolean.fileLoading ? (
<View >
<Loader size='small' color='#94a3b8' />
</View>
) : (
<Download size={20} color="#94a3b8" />
)}
</TouchableOpacity>
))}
</Section>
</ScrollView>
{/* Sticky Footer */}
<View style={styles.footer}>
<TouchableOpacity
style={[styles.mainBtn, !isLive && styles.btnDisabled]}
disabled={!isLive}
>
<Text style={styles.mainBtnText}>
{isLive ? 'Add to Cart / Purchase' : 'Auction Closed'}
</Text>
</TouchableOpacity>
</View>
<AuctionItemDetail
data={{
productName: "sick sari",
timeDuration: "112 YEAR",
category: "laundry",
subCategory: "sari",
reservePriceFig: "120000",
reservePriceWords: "One Lakh Twenty Thousand",
auctionStartFig: "120000",
auctionStartWords: "One Lakh Twenty Thousand",
bidVariationFig: "200",
bidVariationWords: "Two Hundred",
emdFig: "2000",
emdWords: "Two Thousand",
description: "Auction of cars",
documentName: "BBPS screens.pdf"
}} onClose={() => { setAllBoolean((prev: any) => ({ ...prev, showItemDetail: false })) }} visible={allBoolean.showItemDetail} />
{allBoolean.showItemDetail && <AuctionItemDetail data={allBoolean.selectedItem} onClose={() => { setAllBoolean((prev: any) => ({ ...prev, showItemDetail: false })) }} visible={allBoolean.showItemDetail} />}
</View>
</Modal>
</Modal >
);
};
@@ -242,7 +289,7 @@ const styles = StyleSheet.create({
liveText: { fontSize: 11, fontWeight: '800', color: '#475569' },
scrollView: { flex: 1, backgroundColor: '#f8fafc' },
scrollContent: { padding: 16, paddingBottom: 100 },
scrollContent: { padding: 16, paddingBottom: 30 },
sectionContainer: {
backgroundColor: '#fff',
+86 -77
View File
@@ -1,60 +1,21 @@
import React, { memo } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
ScrollView,
SafeAreaView,
Platform,
StatusBar,
Dimensions
} from 'react-native';
import React, { memo, useState } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ScrollView, Platform, Dimensions } from 'react-native';
import Modal from 'react-native-modal';
import {
X,
Package,
Clock,
Tag,
IndianRupee,
FileText,
Download,
ArrowLeft,
Info,
Layers,
ChevronRight
} from 'lucide-react-native';
import { Package, IndianRupee, FileText, Download, ArrowLeft, Info, } from 'lucide-react-native';
import colors from '../../../Constant/Color';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { useAucitonItemDetail } from '../../../Services/BBPS/Hooks';
import ReactNativeBlobUtil from 'react-native-blob-util';
import { AUCTION_ITEM_DOCUMENT_DOWNLOAD } from '../../../Services/BBPS/ApiUrls';
import { Loader } from '../../../Component/Index';
const { width } = Dimensions.get('window');
interface Props { visible: boolean; onClose: () => void; data?: any; }
// --- Exact Dummy Data Mapping ---
const ITEM_DUMMY_DATA = {
productName: "sick sari",
timeDuration: "112 YEAR",
category: "laundry",
subCategory: "sari",
reservePriceFig: "120000",
reservePriceWords: "One Lakh Twenty Thousand",
auctionStartFig: "120000",
auctionStartWords: "One Lakh Twenty Thousand",
bidVariationFig: "200",
bidVariationWords: "Two Hundred",
emdFig: "2000",
emdWords: "Two Thousand",
description: "Auction of cars",
documentName: "BBPS screens.pdf"
};
interface Props {
visible: boolean;
onClose: () => void;
data?: typeof ITEM_DUMMY_DATA;
}
const AuctionItemDetail: React.FC<Props> = ({ visible, onClose, data = ITEM_DUMMY_DATA }) => {
const AuctionItemDetail: React.FC<Props> = ({ visible, onClose, data }) => {
const inset = useSafeAreaInsets()
const [allBoolean, setAllBoolean] = useState({ fileLoading: false });
const { data: itemData } = useAucitonItemDetail({ auctionItemId: data.id });
const Section = ({ title, icon: Icon, children }: any) => (
<View style={styles.sectionCard}>
<View style={styles.sectionHeader}>
@@ -75,6 +36,50 @@ const AuctionItemDetail: React.FC<Props> = ({ visible, onClose, data = ITEM_DUMM
</View>
);
const handleDownload = async (id: number, fileName: string) => {
try {
setAllBoolean((prev: any) => ({ ...prev, fileLoading: true }));
const { dirs } = ReactNativeBlobUtil.fs;
// ✅ PUBLIC DOWNLOADS DIRECTORY
const path = `${dirs.DownloadDir}/${fileName}.pdf`;
console.log('📂 Saving to:', path);
const downloadUrl = `${AUCTION_ITEM_DOCUMENT_DOWNLOAD}?auctionItemId=${id}`;
console.log('🌐 Download URL:', downloadUrl);
const response = await ReactNativeBlobUtil.config({
fileCache: false, // important for public folder
addAndroidDownloads: {
useDownloadManager: true,
notification: true,
path,
mime: 'application/pdf',
title: `${fileName}.pdf`,
description: 'Downloading document',
mediaScannable: true, // 👈 makes it visible instantly
},
}).fetch('GET', downloadUrl, {
Accept: 'application/pdf',
});
console.log('✅ Download completed');
console.log('📦 Status:', response.info().status);
console.log('📦 Headers:', response.info().headers);
console.log('📍 Saved at:', path);
const stat = await ReactNativeBlobUtil.fs.stat(path);
console.log('📦 File size:', stat.size);
} catch (error: any) {
console.log('❌ Download failed');
console.log('❌ Error:', error?.message || error);
} finally {
setAllBoolean((prev: any) => ({ ...prev, fileLoading: false }));
}
};
return (
<Modal
isVisible={visible}
@@ -106,72 +111,76 @@ const AuctionItemDetail: React.FC<Props> = ({ visible, onClose, data = ITEM_DUMM
contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false}
>
{/* Product Identity Card */}
<Section title="Product Specification" icon={Package}>
<View style={styles.identityHeader}>
<Text style={styles.productNameText}>{data.productName.toUpperCase()}</Text>
<View style={styles.categoryBadge}>
<Text style={styles.categoryText}>{data.category}</Text>
<Text style={styles.categoryText}>{data.categoryName}</Text>
</View>
</View>
<View style={styles.specGrid}>
<View style={styles.specItem}>
<Text style={styles.label}>Sub Category</Text>
<Text style={styles.specValue}>{data.subCategory}</Text>
<Text style={styles.specValue}>{data.subCategoryName}</Text>
</View>
<View style={styles.specItem}>
<Text style={styles.label}>Duration</Text>
<Text style={styles.specValue}>{data.timeDuration}</Text>
<Text style={styles.specValue}>{data.timeDurationType}</Text>
</View>
</View>
</Section>
{/* Pricing & Financials */}
<Section title="Reserve & Bid Values" icon={IndianRupee}>
<PriceTile
label="Reserve Price"
fig={data.reservePriceFig}
words={data.reservePriceWords}
fig={data.reservePrice}
words={data.reservePriceInWords}
full
/>
<View style={styles.hDivider} />
<PriceTile
label="Auction Start Value"
fig={data.auctionStartFig}
words={data.auctionStartWords}
fig={data.reservePrice}
words={data.reservePriceInWords}
full
/>
<View style={styles.hDivider} />
<View style={styles.row}>
{/* <View style={styles.row}>
<PriceTile label="Bid Variation" fig={data.bidVariationFig} words={data.bidVariationWords} />
<PriceTile label="EMD Amount" fig={data.emdFig} words={data.emdWords} />
</View>
<PriceTile label="Auction Start Value" fig={data.auctionStartValue} words={data.auctionStartValueInWords
} />
</View> */}
</Section>
{/* Description Card */}
<Section title="Brief Description" icon={Info}>
<Text style={styles.descContent}>{data.description}</Text>
<Text style={styles.descContent}>{data.briefDescription}</Text>
</Section>
{/* Documents Card */}
<Section title="View Documents" icon={FileText}>
{itemData?.length && <Section title="View Documents" icon={FileText}>
<View style={styles.docTableHead}>
<Text style={styles.tableLabel}>Sr No.</Text>
<Text style={[styles.tableLabel, { flex: 2 }]}>Document Name</Text>
<Text style={styles.tableLabel}>Action</Text>
</View>
<TouchableOpacity style={styles.docRow} activeOpacity={0.7}>
<Text style={styles.docIndex}>1</Text>
<View style={{ flex: 2 }}>
<Text style={styles.docTitle} numberOfLines={1}>{data.documentName}</Text>
</View>
<View style={styles.downloadIconBox}>
<Download size={18} color={colors.primary} />
</View>
</TouchableOpacity>
</Section>
{itemData?.map((doc: any, index: any) => (
<TouchableOpacity style={styles.docRow} activeOpacity={0.7} onPress={() => { handleDownload(doc.assetId, doc.fileName) }} key={index}>
<Text style={styles.docIndex}>{index + 1}</Text>
<View style={{ flex: 2 }}>
<Text style={styles.docTitle} numberOfLines={1}>{doc.fileName}</Text>
</View>
<View style={styles.downloadIconBox}>
{allBoolean.fileLoading ? (
<View >
<Loader size='small' color='#94a3b8' />
</View>
) : (
<Download size={20} color={colors.primary} />
)}
</View>
</TouchableOpacity>))}
</Section>}
</ScrollView>
</View>
+33 -32
View File
@@ -1,14 +1,15 @@
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, StyleSheet, TouchableOpacity, Alert } from 'react-native';
import { InsideHeader, Loader } from '../../Component/Index';
import colors from '../../Constant/Color';
import { CreditCard, Calendar, User, DollarSign, ArrowRight } from 'lucide-react-native';
import { useDispatch, useSelector } from 'react-redux';
import { useOfflineAuctionPurchase } from '../../Services/BBPS/Hooks';
import { useOfflineAuctionPurchase, usePurchaseAuctionList } from '../../Services/BBPS/Hooks';
import { showSuccessAlert } from '../../Constant/ShowDailog';
import { clearPurchaseAuction } from '../../Redux/Slices/selectPurchaseAuction';
import { CommonActions, useNavigation } from '@react-navigation/native';
import { CommonActions, useFocusEffect, useNavigation } from '@react-navigation/native';
import NavigationString from '../../Constant/NavigationString';
import Skeleton from '../../Component/Skelton/Skelton';
const dummyAuctionList = [
{
@@ -23,39 +24,18 @@ const dummyAuctionList = [
portalCharge: 100,
payableAmount: 3050,
},
{
auctionId: 2,
auctionNumber: 'AUC-2025-002',
auctionPattern: 'Two Stage',
auctionDispatchDate: '28 Feb 2025',
auctionDispatcherNumber: 'DISP-002',
auctionFees: 1800,
gst: 18,
gstAmount: 324,
portalCharge: 80,
payableAmount: 2204,
},
{
auctionId: 3,
auctionNumber: 'AUC-2025-003',
auctionPattern: 'Single Stage',
auctionDispatchDate: '01 Mar 2025',
auctionDispatcherNumber: 'DISP-003',
auctionFees: 3200,
gst: 18,
gstAmount: 576,
portalCharge: 120,
payableAmount: 3896,
},
];
const PurchaseAuctionList = () => {
const Navigation = useNavigation()
const Dispatch = useDispatch()
const [loading, setLoading] = useState(false);
const [list, setList] = useState(dummyAuctionList);
const [list, setList] = useState([]);
const SelectedPurchaseList = useSelector((state: any) => state.selectPurchaseAuction);
const LoginUser = useSelector((state: any) => state.token.token);
const { mutate, isPending } = useOfflineAuctionPurchase()
const { mutate: purchaseListMutation, isPending: purchaseListPending } = usePurchaseAuctionList();
const calculateTotalAmount = (item: any) => {
const gstAmount =
(Number(item.auctionFees) + Number(item.portalCharge)) *
@@ -68,7 +48,7 @@ const PurchaseAuctionList = () => {
const handleOfflinePurchase = async () => {
const body = {
auctionDetails: SelectedPurchaseList.map((b: any) => ({
auctionDetails: list.map((b: any) => ({
auctionDispatchDate: b?.auctionDispatchDate,
auctionDispatcherNumber: b?.auctionDispatcherNumber,
auctionFees: b?.auctionFees,
@@ -155,16 +135,37 @@ const PurchaseAuctionList = () => {
</View>
);
const getPurchaseList = () => {
const auctionNumbers: string[] = SelectedPurchaseList.map(
(item: { auctionNumber: string }) => item.auctionNumber
);
purchaseListMutation({ auctionNumbers, contractorId: LoginUser.contractorId }, {
onSuccess: (res) => {
if (res.statusCode == 200) {
setList(res.data)
}
},
onError: (error) => {
console.log(error, "=================purchase list error");
}
})
}
useFocusEffect(
React.useCallback(() => {
getPurchaseList()
}, [SelectedPurchaseList])
);
return (
<View style={{ flex: 1, backgroundColor: colors.white }}>
<InsideHeader title="Purchase List" showArrow />
<View style={styles.screen}>
<FlatList
data={SelectedPurchaseList}
{purchaseListPending ? <Skeleton /> : <FlatList
data={list}
renderItem={renderItem}
keyExtractor={(item) => item.auctionId.toString()}
contentContainerStyle={styles.container}
/>
/>}
</View>
<View style={styles.bottomButtonContainer}>
@@ -31,7 +31,6 @@ const PurchaseAuctionTile = ({ item, onPressDetails, onStartBidding, buttonEnabl
const statusColor = isAuctionEnd ? '#EF4444' : buttonEnable ? colors.primary : '#94A3B8';
const lightStatusBg = isAuctionEnd ? '#FEF2F2' : buttonEnable ? '#EFF6FF' : '#F8FAFC';
console.log(item?.keyDates?.auctionBidding?.endDateTime, 'kk');
return (
<View style={{ flex: 1 }}>
+1 -1
View File
@@ -26,7 +26,7 @@ const PurchaseList = () => {
{/* Header */}
<View style={styles.header}>
<Text style={styles.heading}>Purchase List</Text>
<Info size={20} color="#0F172A" />
{/* <Info size={20} color="#0F172A" /> */}
</View>
{/* Cards */}
+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 && (
-11
View File
@@ -142,17 +142,6 @@ const Home = () => {
},
];
/* ---------------- WebSocket ---------------- */
const { connect } = useWebSocketService(
Variables.webSocketUrl,
() => console.log('WebSocket Connected'),
error => console.log('WebSocket Error:', error),
);
useFocusEffect(() => {
connect();
});
return (
<View style={styles.screen}>
<Header title="Dashboard" />
+13 -1
View File
@@ -61,13 +61,25 @@ const ITEM_DOCUMENT_DOWNLOAD = `${BASE_URL_Auction}api/auction/home/downloadDocu
// Rohit
const DASHBOARD_COUNT = `${BASE_URL_Auction}api/auction/home/dashboard/counts`
const AUCTION_ONE_TIME_BID =`${BASE_URL_Auction}api/bidding/getBidForAuction`
const AUCTION_ONE_TIME_BID = `${BASE_URL_Auction}api/bidding/getBidForAuction`
const AUCITON_SINGLE_EMD = `${BASE_URL_Auction}api/auction/addEMDDocumentsAuctionWise`
const PURCHASE_AUCTION_LIST = `${BASE_URL_Auction}api/auction/proceedToPurchase`
const VIEW_AUCTION_ITEM_DETAIL = `${BASE_URL_Auction}api/auction/home/viewDocumentsForAuctionItemDetails`
const VIEW_AUCTION_DOCUMENTS = `${BASE_URL_Auction}api/auction/viewDocumentsForAuction`
const AUCTION_DOCUMENT_DOWNLOAD = `${BASE_URL_Auction}api/auction/home/downloadDocumentForAuction`
const AUCTION_ITEM_DOCUMENT_DOWNLOAD = `${BASE_URL_Auction}api/auction/home/downloadDocument/auctionItemId`
export {
// Rohit
VIEW_AUCTION_ITEM_DETAIL,
AUCTION_ITEM_DOCUMENT_DOWNLOAD,
AUCTION_DOCUMENT_DOWNLOAD,
VIEW_AUCTION_DOCUMENTS,
AUCITON_SINGLE_EMD,
DASHBOARD_COUNT,
AUCTION_ONE_TIME_BID,
ITEM_DOCUMENT_DOWNLOAD,
PURCHASE_AUCTION_LIST,
ITEM_DETAIL,
TECHNICAL_DOCUMENT_SUBMIT,
TECHNICAL_VALUE_SUBMIT,
+51 -13
View File
@@ -1,5 +1,5 @@
import { useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query';
import { fetchAuctionItem, fetchDashboard, fetchEmdCheck, fetchLiveAuction, fetchLiveTender, fetchPurchaseAuction, fetchPurchaseTender, offlineAuctionPurchase, userLogin } from './apis';
import { fetchAuctionItem, fetchDashboard, fetchEmdCheck, fetchLiveAuction, fetchLiveTender, fetchPurchaseAuction, fetchPurchaseAuctionList, fetchPurchaseTender, getAuction, offlineAuctionPurchase, userLogin, viewAuctionDocumentDownload, viewAuctionDocuments, viewAuctionItemDocuments } from './apis';
// User
@@ -32,6 +32,7 @@ export const useFetchLiveTender = () => {
queryFn: () => fetchLiveTender()
});
};
export const useFetchPurchaseAuction = () => {
return useQuery({
queryKey: ['fetchPurchaseAuction',],
@@ -39,25 +40,28 @@ export const useFetchPurchaseAuction = () => {
});
};
export const useFetchLiveAuction = () => {
return useInfiniteQuery<any>({
queryKey: ['fetchLiveAuction',],
queryFn: ({ pageParam = 1 }) =>
queryKey: ['fetchLiveAuction'],
queryFn: ({ pageParam = 0 }) =>
fetchLiveAuction({
page: pageParam,
size: 10,
}),
initialPageParam: 1,
getNextPageParam: (lastPage) => {
const { page, size, totalCount } = lastPage;
initialPageParam: 0,
getNextPageParam: (lastPage, allPages) => {
const { size, totalCount } = lastPage;
const totalPages = Math.ceil(totalCount / size);
const nextPage = page + 1;
const nextPage = allPages.length;
return nextPage < totalPages ? nextPage : undefined;
},
select: (data) => {
const mergedResults = data.pages.flatMap((page: any) => page.data ?? []);
select: (data) => {
const mergedResults = data.pages.flatMap(
(page: any) => page?.data ?? []
);
return {
...data,
result: mergedResults,
@@ -67,7 +71,6 @@ export const useFetchLiveAuction = () => {
};
// Auction Purchase offline
export const useOfflineAuctionPurchase = () => {
@@ -85,6 +88,41 @@ export const useEmdCheck = () => {
export const useAuctionItem = () => {
return useMutation({
mutationFn: (payload: any) => fetchAuctionItem(payload),
mutationFn: (payload: { auctionNumber: string, status: string }) => fetchAuctionItem(payload),
});
};
};
// Purchase Auction List
export const usePurchaseAuctionList = () => {
return useMutation({
mutationFn: (payload: any) => fetchPurchaseAuctionList(payload),
});
};
export const useGetAuciton = (payload: { auctionId: string }) => {
return useQuery({
queryKey: ['getAuction'],
queryFn: () => getAuction(payload)
});
};
export const useGetAucitonDocument = (payload: { auctionId: number }) => {
return useQuery({
queryKey: ['viewAuctionDocuments'],
queryFn: () => viewAuctionDocuments(payload)
});
};
export const useAucitonItemDetail = (payload: { auctionItemId: number }) => {
return useQuery({
queryKey: ['viewAuctionItemDocuments'],
queryFn: () => viewAuctionItemDocuments(payload)
});
};
export const useAucitonDocDownload = () => {
return useMutation({
mutationFn: (payload: { documentId: number, isAmendment: boolean }) => viewAuctionDocumentDownload(payload),
});
};
+26 -3
View File
@@ -1,5 +1,5 @@
import axios from "axios";
import { DASHBOARD_COUNT, EMD_CHECK, FORARD_AUCTION_ITEM, GET_MY_AUCTION, GET_MY_TENDER, LIVE_AUCTION, LIVE_TENDER, LOG_IN_URL, PROCEED_PURCHASE_AUCTION, PURCHASE_OFFLINE_AUCTION, REVERSE_AUCTION_ITEM } from "./ApiUrls";
import { AUCTION_DOCUMENT_DOWNLOAD, DASHBOARD_COUNT, EMD_CHECK, FORARD_AUCTION_ITEM, GET_AUCTION_DETAILS, GET_MY_AUCTION, GET_MY_TENDER, LIVE_AUCTION, LIVE_TENDER, LOG_IN_URL, PROCEED_PURCHASE_AUCTION, PURCHASE_AUCTION_LIST, PURCHASE_OFFLINE_AUCTION, REVERSE_AUCTION_ITEM, VIEW_AUCTION_DOCUMENTS, VIEW_AUCTION_ITEM_DETAIL } from "./ApiUrls";
import { apiCall } from "../../Axios/Axios";
// User
@@ -32,7 +32,7 @@ export const fetchPurchaseAuction = async (): Promise<any> => {
return data
}
export const fetchLiveAuction = async (payload: any): Promise<any> => {
const data = await apiCall<any>('get', `${LIVE_AUCTION}`, {}, { payload });
const data = await apiCall<any>('get', `${LIVE_AUCTION}`, {}, payload);
return data
}
@@ -71,4 +71,27 @@ export const fetchAuctionItem = async (payload: any): Promise<any> => {
} catch (error: any) {
throw error.response?.data || error.message;
}
};
};
export const fetchPurchaseAuctionList = async (payload: any): Promise<any> => {
const data = await apiCall<any>('post', `${PURCHASE_AUCTION_LIST}`, payload);
return data
}
export const getAuction = async (payload: any) => {
const selectedRes = await apiCall<any>('get', GET_AUCTION_DETAILS, {}, { id: payload.auctionId });
return selectedRes.data;
}
export const viewAuctionItemDocuments = async (payload: { auctionItemId: number }) => {
const selectedRes = await apiCall<any>('get', VIEW_AUCTION_ITEM_DETAIL, {}, payload);
return selectedRes.data;
}
export const viewAuctionDocuments = async (payload: { auctionId: number }) => {
const selectedRes = await apiCall<any>('get', VIEW_AUCTION_DOCUMENTS, {}, payload);
return selectedRes.data;
}
export const viewAuctionDocumentDownload = async (payload: { documentId: number, isAmendment: boolean }) => {
const selectedRes = await apiCall<any>('get', AUCTION_DOCUMENT_DOWNLOAD, {}, payload);
return selectedRes.data;
}
+5 -4
View File
@@ -1,5 +1,5 @@
// import React, { useCallback, useEffect, useState } from 'react';
// import { View, FlatList, StyleSheet, Platform, Alert, BackHandler, Text, ToastAndroid } from 'react-native';
// import { View, FlatList, StyleSheet, Platform, Alert, BackHandler, Text } from 'react-native';
// import LottieView from 'lottie-react-native';
// import { InsideHeader } from '../../Component/Index';
// import colors from '../../Constant/Color';
@@ -36,12 +36,13 @@
// if (SelectedAuction.auctionPattern === 'Forward') {
// const res = await apiCall<any>('get', FORWARD_AUCTION_ITEM, {}, payload);
// if (res?.statusCode === 200) auctionItem = res.data || [];
// } else if(SelectedAuction.auctionPattern === 'Reverse') {
// } 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 || [])
@@ -98,7 +99,7 @@
// };
// subscribe(`/topic/room/${SelectedAuction.auctionNumber}`, async (message) => {
// // console.log('RAW MESSAGE:', message[message.length - 1]);
// const result = message[message.length - 1]
// const updatedBidItem = (prev: any[]) =>
@@ -160,6 +161,7 @@
// {
// text: 'Submit',
// onPress: () => {
// placeBidSubmit(payload);
// },
// },
@@ -180,7 +182,6 @@
// : item
// );
// // ToastAndroid.show(result.message, ToastAndroid.SHORT)
// // setItems(updatedBidItem);