diff --git a/src/Component/MyInput.tsx b/src/Component/MyInput.tsx new file mode 100644 index 0000000..d1762ee --- /dev/null +++ b/src/Component/MyInput.tsx @@ -0,0 +1,121 @@ +import { StyleSheet, TextInput, View, TextInputProps, StyleProp, TextStyle, ViewStyle, TouchableOpacity } from 'react-native'; +import React, { ReactNode } from 'react'; +import Icon from 'lucide-react-native'; + +type MyInputProps = { + icon?: string; + placeholder?: string; + value?: string; + onChangeText?: (text: string) => void; + isVarify?: boolean; + maxlength?: number; + onFocus?: () => void; + onBlur?: () => void; + focused?: boolean; + keyboardType?: TextInputProps['keyboardType']; + editable?: boolean; + autoCapitalize?: TextInputProps['autoCapitalize']; + rightIcon?: string, + rightIconPress?:()=>void +} & TextInputProps; + +const MyInput: React.FC = ({ + icon, + placeholder, + value, + onChangeText, + isVarify, + maxlength, + onFocus, + onBlur, + focused, + keyboardType, + editable = true, + autoCapitalize, + rightIcon, + rightIconPress, + ...textInputProps +}) => { + return ( + + {/* */} + + { + rightIcon ? + + {/* */} + + + : null} + {/* {isVarify ? ( + + ) : null} */} + + ); +}; + +export default MyInput; +const styles = StyleSheet.create({ + inputWrapper: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + backgroundColor: '#fff', + borderRadius: 12, + borderWidth: 1, + borderColor: '#e0e0e0', + paddingHorizontal: 10, + marginVertical: 10, + height: 55, + + }, + icon: { + marginRight: 10, + }, + input: { + flex: 1, + fontSize: 15, + color: '#000', + paddingVertical: 0, + }, + inputFocused: { + borderColor: '#007BFF', + }, + inputDisabled: { + backgroundColor: '#f2f2f2', + }, +}); diff --git a/src/Constant/Color.tsx b/src/Constant/Color.tsx index 1e95689..7f2a093 100644 --- a/src/Constant/Color.tsx +++ b/src/Constant/Color.tsx @@ -34,6 +34,10 @@ const colors = { Orange: '#F97316', OrangeLight: '#FDBA74', OrangeSoft: '#FFF3E8', + + darkGray: '#5f6368', + lightestGray: '#f1f3f4', + successLight: '#e6f4ea', }; export default colors; diff --git a/src/Constant/NavigationString.tsx b/src/Constant/NavigationString.tsx index 91644de..b6414a3 100644 --- a/src/Constant/NavigationString.tsx +++ b/src/Constant/NavigationString.tsx @@ -1,3 +1,6 @@ +import ProceedToBidDetail from "../Screens/Tender/ProceedToBidDetail" +import TenderBidding from "../Screens/Tender/tenderBidding/TenderBidding" + const NavigationString = { Login: 'Login', Splash: 'Splash', @@ -12,5 +15,7 @@ const NavigationString = { // tender PurchaseTenderList:'PurchaseTenderList', ProceedToPurTender:'ProceedToPurTender', + TenderBidding:'TenderBidding', + ProceedToBidDetail:'ProceedToBidDetail', } export default NavigationString \ No newline at end of file diff --git a/src/Navigation/MainStack.jsx b/src/Navigation/MainStack.jsx index 446b5f9..f50b2a8 100644 --- a/src/Navigation/MainStack.jsx +++ b/src/Navigation/MainStack.jsx @@ -38,6 +38,8 @@ export default function MainStack() { + + ); diff --git a/src/Redux/Slices/SelectedPurchaseTender.ts b/src/Redux/Slices/SelectedPurchaseTender.ts index 42129b7..edd9b68 100644 --- a/src/Redux/Slices/SelectedPurchaseTender.ts +++ b/src/Redux/Slices/SelectedPurchaseTender.ts @@ -17,7 +17,7 @@ export const selectPurchaseTenderSlice = createSlice({ action: PayloadAction ) => { const index = state.findIndex( - item => item.TenderNumber === action.payload.TenderNumber + item => item.tenderId === action.payload.tenderId ); if (index !== -1) { diff --git a/src/Redux/Slices/SelectedTender.ts b/src/Redux/Slices/SelectedTender.ts index 10c20ed..ca7e58a 100644 --- a/src/Redux/Slices/SelectedTender.ts +++ b/src/Redux/Slices/SelectedTender.ts @@ -13,8 +13,11 @@ export const tenderSlice = createSlice({ }, removeTender: (state) => { state.tender = ''; - } + }, + setTenderDetails: (state, { payload }) => { + state.tender = payload; + }, }, }); -export const { setTender , removeTender} = tenderSlice.actions; +export const { setTender , removeTender, setTenderDetails } = tenderSlice.actions; export default tenderSlice.reducer; diff --git a/src/Screens/Auction/PurchaseAuction.tsx b/src/Screens/Auction/PurchaseAuction.tsx index 5ace178..9a5f139 100644 --- a/src/Screens/Auction/PurchaseAuction.tsx +++ b/src/Screens/Auction/PurchaseAuction.tsx @@ -38,7 +38,6 @@ const PurchaseAuction = () => { return now.isAfter(end); } - console.log(allBoolean); return ( diff --git a/src/Screens/Auction/PurchaseAuctionList.tsx b/src/Screens/Auction/PurchaseAuctionList.tsx index 923c0b5..c64be80 100644 --- a/src/Screens/Auction/PurchaseAuctionList.tsx +++ b/src/Screens/Auction/PurchaseAuctionList.tsx @@ -49,8 +49,7 @@ const PurchaseAuctionList = () => { const [loading, setLoading] = useState(false); const [list, setList] = useState(dummyAuctionList); const SelectedPurchaseList = useSelector((state: any) => state.selectPurchaseAuction); - console.log(SelectedPurchaseList,'iiiiiiiiiiiiiiiiiiiiiiii'); - + const { mutate, isPending } = useOfflineAuctionPurchase() const calculateTotalAmount = (item: any) => { const gstAmount = @@ -80,7 +79,6 @@ const PurchaseAuctionList = () => { }; try { - // console.log(body); mutate(body, { onSuccess: (res) => { console.log(res, '---------------------purchsase response'); diff --git a/src/Screens/Tender/LiveTender.tsx b/src/Screens/Tender/LiveTender.tsx index 9fc046d..eeca2c9 100644 --- a/src/Screens/Tender/LiveTender.tsx +++ b/src/Screens/Tender/LiveTender.tsx @@ -17,7 +17,6 @@ const LiveTender = () => { const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isRefetching, refetch }: any = useFetchLiveTender(); const [showRefreshBanner, setShowRefreshBanner] = useState(false); - console.log("live tender SelectedPurchaseList : ", SelectedPurchaseList) useEffect(() => { if (isRefetching) { setShowRefreshBanner(true); diff --git a/src/Screens/Tender/LiveTenderTile.tsx b/src/Screens/Tender/LiveTenderTile.tsx index 70e563c..71890a6 100644 --- a/src/Screens/Tender/LiveTenderTile.tsx +++ b/src/Screens/Tender/LiveTenderTile.tsx @@ -4,7 +4,8 @@ import moment from 'moment'; import colors from '../../Constant/Color'; import { LiveTenderTileProps } from './types/types'; import { Calendar, Tag, Info, ArrowRight } from 'lucide-react-native'; -import TenderDetail from './Modal/TenderDetail'; +import TenderDetail from './Modal/TenderFullDetail'; +import TenderFullDetail from './Modal/TenderFullDetail'; const LiveTenderTile: React.FC = ({ data, @@ -103,7 +104,7 @@ const LiveTenderTile: React.FC = ({ {/* Tender Detail Modal */} - setShowDetail(false)} /> + setShowDetail(false)} /> ); diff --git a/src/Screens/Tender/Modal/TenderDetail.tsx b/src/Screens/Tender/Modal/TenderFullDetail.tsx similarity index 99% rename from src/Screens/Tender/Modal/TenderDetail.tsx rename to src/Screens/Tender/Modal/TenderFullDetail.tsx index 622ad6a..eaa78e8 100644 --- a/src/Screens/Tender/Modal/TenderDetail.tsx +++ b/src/Screens/Tender/Modal/TenderFullDetail.tsx @@ -32,6 +32,8 @@ interface Props { } const TenderFullDetail: React.FC = ({ visible, onClose, data }) => { + + const inset = useSafeAreaInsets(); const { data: tenderResponse }: any = useFetchTenderById(data?.tenderId); const tender = tenderResponse?.data?.[0]; diff --git a/src/Screens/Tender/Modal/TenderItemDetail.tsx b/src/Screens/Tender/Modal/TenderItemDetail.tsx deleted file mode 100644 index d1814ab..0000000 --- a/src/Screens/Tender/Modal/TenderItemDetail.tsx +++ /dev/null @@ -1,257 +0,0 @@ -import React, { memo } from 'react'; -import { - View, - Text, - StyleSheet, - TouchableOpacity, - ScrollView, - SafeAreaView, - Platform, -} from 'react-native'; -import Modal from 'react-native-modal'; -import { - Package, - IndianRupee, - FileText, - Download, - ArrowLeft, - Info, -} from 'lucide-react-native'; -import colors from '../../../Constant/Color'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; - -interface Props { - visible: boolean; - onClose: () => void; - data?: any; -} - -const TenderItemDetail: React.FC = ({ visible, onClose, data }) => { - const inset = useSafeAreaInsets(); - - if (!data) { - return null; - } - - const Section = ({ title, icon: Icon, children }: any) => ( - - - - - - {title} - - {children} - - ); - - const PriceTile = ({ label, fig, words, full = false }: any) => ( - - {label} - - ₹{Number(fig || 0).toLocaleString('en-IN')} - - {words || '-'} - - ); - - return ( - - - {/* Header */} - - - - - - Item Information - Detail View - - - - - {/* Product Specification */} -
- - - {data.productName || '-'} - - - - {data.category || '-'} - - - - - - - Sub Category - {data.subCategory || '-'} - - - Duration - {data.timeDuration || '-'} - - -
- - {/* Pricing */} -
- - - - - - - - -
- - {/* Description */} -
- - {data.description || '-'} - -
- - {/* Documents */} -
- - 1 - - - {data.documentName || 'Document'} - - - - - - -
-
-
-
- ); -}; - -const styles = StyleSheet.create({ - fullModal: { margin: 0, backgroundColor: '#f8fafc' }, - safeArea: { flex: 1 }, - header: { - flexDirection: 'row', - alignItems: 'center', - padding: 16, - backgroundColor: '#fff', - borderBottomWidth: 1, - borderBottomColor: '#f1f5f9', - }, - backBtn: { padding: 8, marginRight: 8 }, - headerTitle: { fontSize: 18, fontWeight: '800', color: '#0f172a' }, - headerSub: { fontSize: 12, color: '#94a3b8', fontWeight: '600', textTransform: 'uppercase' }, - - scrollView: { flex: 1 }, - scrollContent: { padding: 16, paddingBottom: 50 }, - - sectionCard: { - backgroundColor: '#fff', - borderRadius: 24, - padding: 20, - marginBottom: 16, - ...Platform.select({ - ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.05, shadowRadius: 10 }, - android: { elevation: 3 } - }) - }, - sectionHeader: { - flexDirection: 'row', - alignItems: 'center', - gap: 12, - marginBottom: 20, - }, - iconCircle: { - width: 36, - height: 36, - borderRadius: 12, - backgroundColor: colors.primary + '10', - alignItems: 'center', - justifyContent: 'center', - }, - sectionTitle: { - fontSize: 14, - fontWeight: '800', - color: '#334155', - textTransform: 'uppercase', - letterSpacing: 0.5, - }, - - // Product Identity - identityHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 15 }, - productNameText: { fontSize: 20, fontWeight: '900', color: '#1e293b', flex: 1 }, - categoryBadge: { backgroundColor: '#f1f5f9', paddingHorizontal: 12, paddingVertical: 6, borderRadius: 20 }, - categoryText: { fontSize: 11, fontWeight: '800', color: '#64748b', textTransform: 'uppercase' }, - specGrid: { flexDirection: 'row', gap: 24, marginTop: 5 }, - specItem: { flex: 1 }, - specValue: { fontSize: 15, fontWeight: '700', color: '#334155', marginTop: 2 }, - - // Pricing - priceTile: { marginBottom: 16 }, - label: { fontSize: 11, color: '#94a3b8', fontWeight: '700', textTransform: 'uppercase', marginBottom: 4 }, - priceFig: { fontSize: 22, fontWeight: '900', color: colors.black }, - priceWords: { fontSize: 13, color: '#64748b', fontStyle: 'italic', marginTop: 2 }, - hDivider: { height: 1, backgroundColor: '#f1f5f9', marginBottom: 16 }, - row: { flexDirection: 'row', justifyContent: 'space-between' }, - - // Description - descContent: { fontSize: 15, color: '#475569', lineHeight: 22, fontWeight: '500' }, - - // Table - docTableHead: { flexDirection: 'row', paddingBottom: 10, borderBottomWidth: 1, borderBottomColor: '#f1f5f9', marginBottom: 10 }, - tableLabel: { fontSize: 11, fontWeight: '800', color: '#94a3b8', flex: 1 }, - docRow: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#f8fafc', padding: 12, borderRadius: 12 }, - docIndex: { flex: 1, fontSize: 14, fontWeight: '700', color: '#64748b' }, - docTitle: { fontSize: 14, fontWeight: '700', color: '#1e293b' }, - downloadIconBox: { backgroundColor: '#fff', padding: 8, borderRadius: 10, borderWidth: 1, borderColor: '#e2e8f0' }, - - // Footer - footer: { - position: 'absolute', - bottom: 0, width: '100%', - backgroundColor: '#fff', - padding: 20, - paddingBottom: Platform.OS === 'ios' ? 34 : 20, - borderTopWidth: 1, borderTopColor: '#f1f5f9' - }, - primaryBtn: { - backgroundColor: colors.primary, - height: 56, borderRadius: 18, - flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 10 - }, - primaryBtnText: { color: '#fff', fontSize: 16, fontWeight: '800' } -}); - -export default memo(TenderItemDetail); \ No newline at end of file diff --git a/src/Screens/Tender/ProceedToBidDetail.tsx b/src/Screens/Tender/ProceedToBidDetail.tsx new file mode 100644 index 0000000..f937852 --- /dev/null +++ b/src/Screens/Tender/ProceedToBidDetail.tsx @@ -0,0 +1,618 @@ +import { StyleSheet, Text, View, ScrollView, StatusBar, TouchableOpacity } from 'react-native' +import React from 'react' +import { InsideHeader } from '../../Component/Index' +import colors from '../../Constant/Color' +import { useNavigation } from '@react-navigation/native' + +const ProceedToBidDetail = () => { + // Sample data based on your JSON + const navigation = useNavigation() + const tenderData = { + tenderId: 1372, + envelopeASubmittStatus: true, + envelopeBSubmittStatus: true, + envelopeCSubmittStatus: true, + envelopeA_Logs: { + logId: 314, + contractorId: 1, + activity: "Bid on Envelope A", + tenderId: 1372, + dateTime: "Fri Dec 12 13:07:44 IST 2025", + osName: "Linux", + browserName: "Chrome-142.0.0.0", + ipAddress: "192.168.5.10", + macAddress: "00:00:00:00:00:00", + logMessage: "Envelope A bidding is Submitted", + headingMsg: null, + status: null, + envelope: "EA" + }, + envelopeB_Logs: { + logId: 315, + contractorId: 1, + activity: "Bid on Envelope B", + tenderId: 1372, + dateTime: "Fri Dec 12 13:08:00 IST 2025", + osName: "Linux", + browserName: "Chrome-142.0.0.0", + ipAddress: "192.168.5.10", + macAddress: "00:00:00:00:00:00", + logMessage: "Envelope B bidding is Submitted", + headingMsg: null, + status: null, + envelope: "EB" + }, + envelopeC_Logs: { + logId: 316, + contractorId: 1, + activity: "Bid on Envelope C", + tenderId: 1372, + dateTime: "Fri Dec 12 13:08:31 IST 2025", + osName: "Linux", + browserName: "Chrome-142.0.0.0", + ipAddress: "192.168.5.10", + macAddress: "00:00:00:00:00:00", + logMessage: "Envelope C bidding is Submitted", + headingMsg: "Percentage Rate Tender", + status: null, + envelope: "EC" + }, + showEB: true, + showEC: true, + showQCBS: false, + showLCS: false, + viewBid: true, + proceedtoBid: false, + tenderNumber: "TN/152", + emdInFig: "100", + tenderBidWithdraw: "Allowed", + tenderRebid: "Allowed", + nitNo: "NIT-410", + nameofWork: "Tender is created for Testing Process only", + reBid: "Allowed", + bidWithDraw: "Allowed", + envelopeType: "3", + biddingStatus: true, + tenderStages: "1", + dscStatus: false, + submittedStartDate: [2025, 3, 4, 10, 0], + submittedEndDate: [2026, 3, 4, 23, 50], + bidWithdrawStatus: true + }; + + // Format date from array + const formatDate = (dateArray: any[]) => { + if (!dateArray || dateArray.length < 3) return "N/A"; + const [year, month, day, hour = 0, minute = 0] = dateArray; + return `${day.toString().padStart(2, '0')}/${month.toString().padStart(2, '0')}/${year} ${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`; + }; + + // Status indicator component + const StatusIndicator = ({ status, label }) => ( + + + + {label}: {status ? "Submitted" : "Not Submitted"} + + + ); + + // Envelope log card component + const EnvelopeLogCard = ({ log, title }) => ( + + + {title} + + {log.envelope} + + + + + Activity: + {log.activity} + + + + Date & Time: + {log.dateTime} + + + + System Info: + {log.osName} / {log.browserName} + + + + IP Address: + {log.ipAddress} + + + + Status Message: + {log.logMessage} + + + {log.headingMsg && ( + + Heading: + {log.headingMsg} + + )} + + ); + + // Information row component + const InfoRow = ({ label, value, isHighlighted = false }) => ( + + {label}: + {value} + + ); + + // Feature chip component + const FeatureChip = ({ label, enabled }) => ( + + + {label} {enabled ? '✓' : '✗'} + + + ); + + return ( + + + + + + {/* Tender Header Section */} + + + + {tenderData.tenderNumber} + ID: {tenderData.tenderId} + + + + {tenderData.biddingStatus ? "Active" : "Inactive"} + + + + + {tenderData.nameofWork} + + + + NIT No. + {tenderData.nitNo} + + + + EMD Amount + ₹{tenderData.emdInFig} + + + + + {/* Envelope Submission Status */} + + Envelope Submission Status + + + + + + + + {/* Envelope Logs */} + {tenderData.envelopeA_Logs && ( + + Submission Logs + + + + + )} + + {/* Tender Details */} + + Tender Details + + + + + + + + + + {/* Time Period */} + + Submission Time Period + + + Starts On: + {formatDate(tenderData.submittedStartDate)} + + + Ends On: + {formatDate(tenderData.submittedEndDate)} + + + + + {/* Feature Flags */} + + Available Features + + + + + + + + + + + {/* Final Status */} + + Overall Status + + All Envelopes Submitted: + + {tenderData.envelopeASubmittStatus && tenderData.envelopeBSubmittStatus && tenderData.envelopeCSubmittStatus + ? "COMPLETED" : "PENDING"} + + + + Bidding Status: + + {tenderData.biddingStatus ? "ACTIVE" : "CLOSED"} + + + + + + + + { + navigation.navigate('TenderBidding', { tenderData: tenderData }); + }}> + Proceed to Bid + + { + // tenderData?.proceedtoBid && !tenderData?.viewBid && tenderData?.biddingStatus && tenderData?.bidWithdrawStatus === false ? ( + + // { + // navigation.navigate('TenderBidding', { tenderData: tenderData }); + // }}> + // Proceed to Bid + // + + // ) : One time bidding is completed + } + + + ) +} + +export default ProceedToBidDetail + +const styles = StyleSheet.create({ + container: { + flex: 1, + padding: 16, + marginBottom: 60, + }, + headerCard: { + backgroundColor: colors.primaryLight, + borderRadius: 12, + padding: 20, + marginBottom: 20, + borderLeftWidth: 4, + borderLeftColor: colors.primary, + }, + tenderHeader: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: 12, + }, + tenderNumber: { + fontSize: 22, + fontWeight: 'bold', + color: colors.primary, + }, + tenderId: { + fontSize: 14, + color: colors.darkGray, + marginTop: 4, + }, + statusBadge: { + paddingHorizontal: 12, + paddingVertical: 6, + borderRadius: 20, + }, + statusBadgeText: { + color: colors.white, + fontWeight: '600', + fontSize: 12, + }, + workDescription: { + fontSize: 16, + color: colors.darkText, + lineHeight: 22, + marginBottom: 16, + }, + headerInfoRow: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: colors.white, + borderRadius: 8, + padding: 12, + }, + headerInfoItem: { + flex: 1, + alignItems: 'center', + }, + headerInfoLabel: { + fontSize: 12, + color: colors.darkGray, + marginBottom: 4, + }, + headerInfoValue: { + fontSize: 16, + fontWeight: '600', + color: colors.primary, + }, + separator: { + width: 1, + height: 30, + backgroundColor: colors.lightGray, + }, + section: { + marginBottom: 20, + }, + sectionTitle: { + fontSize: 18, + fontWeight: '600', + color: colors.primary, + marginBottom: 12, + paddingLeft: 4, + }, + statusGrid: { + backgroundColor: colors.white, + borderRadius: 12, + padding: 16, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 3, + }, + statusContainer: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 12, + }, + statusDot: { + width: 12, + height: 12, + borderRadius: 6, + marginRight: 10, + }, + statusText: { + fontSize: 15, + fontWeight: '500', + }, + envelopeCard: { + backgroundColor: colors.white, + borderRadius: 12, + padding: 16, + marginBottom: 12, + borderWidth: 1, + borderColor: colors.lightGray, + }, + cardHeader: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: 12, + }, + cardTitle: { + fontSize: 16, + fontWeight: '600', + color: colors.primary, + }, + envelopeBadge: { + paddingHorizontal: 10, + paddingVertical: 4, + borderRadius: 12, + }, + envelopeBadgeText: { + color: colors.white, + fontWeight: 'bold', + fontSize: 12, + }, + logDetailRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: 8, + }, + label: { + fontSize: 14, + color: colors.darkGray, + flex: 1, + }, + value: { + fontSize: 14, + color: colors.darkText, + fontWeight: '500', + flex: 2, + textAlign: 'right', + }, + successText: { + color: colors.success, + fontWeight: '600', + }, + detailsCard: { + backgroundColor: colors.white, + borderRadius: 12, + padding: 16, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 3, + }, + infoRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingVertical: 10, + borderBottomWidth: 1, + borderBottomColor: colors.lightestGray, + }, + infoLabel: { + fontSize: 14, + color: colors.darkGray, + }, + infoValue: { + fontSize: 15, + fontWeight: '500', + color: colors.darkText, + }, + highlightedValue: { + color: colors.success, + fontWeight: '600', + }, + timeCard: { + backgroundColor: colors.white, + borderRadius: 12, + padding: 16, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 3, + }, + timeRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingVertical: 12, + borderBottomWidth: 1, + borderBottomColor: colors.lightestGray, + }, + timeLabel: { + fontSize: 15, + color: colors.darkGray, + fontWeight: '500', + }, + timeValue: { + fontSize: 15, + color: colors.primary, + fontWeight: '600', + }, + chipsContainer: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 10, + }, + chip: { + paddingHorizontal: 16, + paddingVertical: 8, + borderRadius: 20, + marginBottom: 8, + }, + chipText: { + fontSize: 14, + fontWeight: '500', + }, + finalStatusCard: { + backgroundColor: colors.primaryLight, + borderRadius: 12, + padding: 20, + marginTop: 10, + borderWidth: 2, + borderColor: colors.primary, + }, + finalStatusTitle: { + fontSize: 18, + fontWeight: 'bold', + color: colors.primary, + marginBottom: 16, + textAlign: 'center', + }, + finalStatusRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: 12, + paddingVertical: 8, + borderBottomWidth: 1, + borderBottomColor: 'rgba(0, 0, 0, 0.1)', + }, + finalStatusLabel: { + fontSize: 16, + color: colors.darkText, + fontWeight: '500', + }, + finalStatusValue: { + fontSize: 16, + fontWeight: 'bold', + }, + footerSpace: { + height: 30, + }, + success: { + color: colors.success, + }, + error: { + color: colors.error, + }, + primary: { + color: colors.primary, + }, + warning: { + color: colors.warning, + }, + successLight: { + backgroundColor: colors.successLight, + }, + lightGray: { + backgroundColor: colors.lightGray, + }, + footer: { + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + backgroundColor: colors.white, + padding: 16, + borderTopWidth: 1, + borderTopColor: colors.lightGray, + elevation: 10, + }, + proceedButton: { + backgroundColor: colors.primary, + paddingVertical: 14, + borderRadius: 30, + alignItems: 'center', + }, + proceedButtonText: { + color: colors.white, + fontSize: 16, + fontWeight: '600', + }, + footerText: { + textAlign: 'center', + color: colors.darkGray, + fontSize: 14, + }, +}); \ No newline at end of file diff --git a/src/Screens/Tender/ProceedToPurTender.tsx b/src/Screens/Tender/ProceedToPurTender.tsx index 404466b..133e099 100644 --- a/src/Screens/Tender/ProceedToPurTender.tsx +++ b/src/Screens/Tender/ProceedToPurTender.tsx @@ -12,8 +12,7 @@ const ProceedToPurTender = () => { const navigation: any = useNavigation(); const [loading, setLoading] = useState(false); const SelectedPurchaseList = useSelector((state: any) => state.selectPurchaseTender); - console.log(SelectedPurchaseList, 'iiiiiiiiiiiiiiiiiiiiiiii tender purchase list : ', SelectedPurchaseList); - + const { mutate, isPending } = useOfflineTenderPurchase() const calculateTotalAmount = (item: any) => { const gstAmount = @@ -50,7 +49,6 @@ const ProceedToPurTender = () => { console.log("tender offline body : ", body) try { - // console.log(body); mutate(body, { onSuccess: (res) => { console.log(res, '---------------------purchsase response'); diff --git a/src/Screens/Tender/PurchaseTenderAllList.tsx b/src/Screens/Tender/PurchaseTenderAllList.tsx index c5873f4..69072ac 100644 --- a/src/Screens/Tender/PurchaseTenderAllList.tsx +++ b/src/Screens/Tender/PurchaseTenderAllList.tsx @@ -1,6 +1,5 @@ -import React, { useState, useCallback, memo } from 'react'; +import React, { useState, useCallback, memo, useEffect } from 'react'; import { View, Text, StyleSheet, FlatList, RefreshControl, ActivityIndicator } from 'react-native'; -import LiveTenderTile from './LiveTenderTile'; import { useNavigation } from '@react-navigation/native'; import moment from 'moment'; import { useFetchPurchaseTender } from '../../Services/BBPS/Hooks'; @@ -12,6 +11,8 @@ import { Skelton } from '../../Component/Index'; import { allBooleanProps } from './types/types'; import FilterModal from './Modal/FilterModal'; import colors from '../../Constant/Color'; +import Animated, { FadeInDown, FadeOutUp } from 'react-native-reanimated'; +import NavigationString from '../../Constant/NavigationString'; const PurchaseTenderAllList = () => { @@ -19,28 +20,22 @@ const PurchaseTenderAllList = () => { const Dispatch = useDispatch() const [allBoolean, setAllBoolean] = useState({ isShowFilter: false }) - const [page, setPage] = useState(1); - const { data: data, isPending, isFetching } = useFetchPurchaseTender(page); + const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isRefetching, refetch }: any = useFetchPurchaseTender(); + const [showRefreshBanner, setShowRefreshBanner] = useState(false); - console.log("data my data : ", data); - const [allData, setAllData] = useState([]); + useEffect(() => { + if (isRefetching) { + setShowRefreshBanner(true); + } else { + const timer = setTimeout(() => { + setShowRefreshBanner(false); + }, 1000); + + return () => clearTimeout(timer); + } + }, [isRefetching]); const format = 'DD-MM-YYYY HH:mm:ss'; - const tenderList = data?.data || []; - const totalCount = data?.totalCount || 0; - - // Append new page data - React.useEffect(() => { - if (tenderList.length) { - setAllData(prev => { - const merged = [...prev, ...tenderList]; - const unique = merged.filter( - (v, i, a) => a.findIndex(t => t.tenderId === v.tenderId) === i - ); - return unique; - }); - } - }, [tenderList]); function isTenderActive(item: any): boolean { const now = moment(); const start = moment(item?.keyDates?.TenderBidding?.startDateTime, format); @@ -59,51 +54,88 @@ const PurchaseTenderAllList = () => { return now.isAfter(end); } - console.log(allBoolean); - - const loadMore = () => { - if (allData.length < totalCount && !isFetching) { - setPage(prev => prev + 1); + const loadMore = useCallback(() => { + if (hasNextPage && !isFetchingNextPage) { + fetchNextPage(); } - }; - const renderFooter = () => { - if (!isFetching) return null; - return ( - - ); - }; + }, [hasNextPage, isFetchingNextPage]); - console.log("all data : ", allData) + + const renderFooter = useCallback(() => { + if (!isFetchingNextPage) return null; + return ( + + + + ); + }, [isFetchingNextPage]); + return ( { }} placeholder='Search....' filterPress={() => { setAllBoolean((prev: any) => ({ ...prev, isShowFilter: !prev.isShowFilter })) }} /> - {isPending ? : index.toString()} - showsVerticalScrollIndicator={false} - contentContainerStyle={{ paddingBottom: 200, paddingHorizontal: 10 }} - renderItem={({ item, index }) => ( - { - navigation.navigate('TenderDetail', { item, onAddToPurchase: () => { }, routeFrom: 'PurchasedTender' }); + + {showRefreshBanner && ( + + + { - Dispatch(setTender(item)) - // navigation.navigate(NavigationString, { data: item }) - }} - buttonEnable={isTenderActive(item)} - isTenderEnd={isTenderEnd(item)} - /> - )} - onEndReached={loadMore} - onEndReachedThreshold={0.6} - ListFooterComponent={renderFooter} - />} + > + Refreshing tenders + + + )} + {isLoading ? + : String(item.tenderId || item.id)} + showsVerticalScrollIndicator={false} + contentContainerStyle={{ paddingBottom: 200, paddingHorizontal: 10 }} + renderItem={({ item, index }) => ( + { + navigation.navigate('TenderDetail', { item, onAddToPurchase: () => { }, routeFrom: 'PurchasedTender' }); + }} + onStartBidding={() => { + Dispatch(setTender(item)) + navigation.navigate(NavigationString.TenderBidding, { data: item }) + }} + buttonEnable={isTenderActive(item)} + isTenderEnd={isTenderEnd(item)} + /> + )} + onEndReached={loadMore} + onEndReachedThreshold={0.6} + ListFooterComponent={renderFooter} + refreshing={isRefetching} + onRefresh={refetch} + />} {allBoolean.isShowFilter && diff --git a/src/Screens/Tender/PurchaseTenderList.tsx b/src/Screens/Tender/PurchaseTenderList.tsx index d2dcf8d..6771ab3 100644 --- a/src/Screens/Tender/PurchaseTenderList.tsx +++ b/src/Screens/Tender/PurchaseTenderList.tsx @@ -19,7 +19,6 @@ const PurchaseTenderList = () => { }; - console.log("SelectedPurchaseList : ", SelectedPurchaseList) return ( diff --git a/src/Screens/Tender/PurchaseTenderTile.tsx b/src/Screens/Tender/PurchaseTenderTile.tsx index 3b4dd3b..a3c72bd 100644 --- a/src/Screens/Tender/PurchaseTenderTile.tsx +++ b/src/Screens/Tender/PurchaseTenderTile.tsx @@ -5,6 +5,7 @@ import { StyleSheet, TouchableOpacity, Platform, + Alert, } from 'react-native'; import Animated, { FadeInUp, @@ -15,20 +16,39 @@ import Animated, { import moment from 'moment'; import CountdownTimer from '../../Component/Counter/CountdownTimer'; import colors from '../../Constant/Color'; +import { useFetchTenderById, useProceedToBid } from '../../Services/BBPS/Hooks'; +import { useNavigation } from '@react-navigation/native'; +import TenderFullDetail from './Modal/TenderFullDetail'; const PurchaseTenderTile = ({ item, onPressDetails, - onStartPurchase, }: any) => { const scale = useSharedValue(1); const elevation = useSharedValue(8); + const navigation = useNavigation(); const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }], elevation: elevation.value, })); + const { data: tenderResponse, }: any = useFetchTenderById(item?.tenderId); + const tender = tenderResponse?.data; + + // const { + // refetch, + // isFetching, + // isError, + // } = useProceedToBid(item?.tenderId); + + // const handleProceed = async (item: any) => { + // const res = await refetch(); + // if (res.data && !isError) { + // navigation.navigate('TenderBidding', { tenderData: res.data?.data }); + // } + // }; + const handlePressIn = () => { scale.value = withTiming(0.97, { duration: 120 }); elevation.value = withTiming(3, { duration: 120 }); @@ -43,6 +63,7 @@ const PurchaseTenderTile = ({ const statusColor = isLive ? colors.primary : '#94A3B8'; const lightStatusBg = isLive ? '#EFF6FF' : '#F8FAFC'; + const [showDetail, setShowDetail] = React.useState(false); return ( {/* Date */} - + { + setShowDetail(true) + }}> Purchase Ends: {moment( @@ -88,7 +111,7 @@ const PurchaseTenderTile = ({ 'DD-MM-YYYY HH:mm:ss' ).format('DD MMM, hh:mm A')} - + {/* Footer */} @@ -108,7 +131,11 @@ const PurchaseTenderTile = ({ { + if (isLive) { + navigation.navigate('ProceedToBidDetail', { tender }); + } + }} activeOpacity={0.85} style={[ styles.actionButton, @@ -117,11 +144,15 @@ const PurchaseTenderTile = ({ ]} > - {isLive ? 'Proceed to Purchase' : 'Tender Closed'} + {isLive ? 'Proceed to Bid' : 'Tender Closed'} + {/* Tender Detail Modal */} + + setShowDetail(false)} /> + ); @@ -131,134 +162,134 @@ export default PurchaseTenderTile; const styles = StyleSheet.create({ - shadowContainer: { - marginHorizontal: 5, - marginTop: 14, - marginBottom: 6, - borderRadius: 16, - backgroundColor: '#fff', - elevation: 8, - }, - cardInternal: { - borderRadius: 16, - overflow: 'hidden', - flexDirection: 'row', - backgroundColor: '#fff', - }, - statusAccent: { - width: 5, - height: '100%', - }, - mainInfo: { - flex: 1, - padding: 16, - }, - headerRow: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'flex-start', - marginBottom: 12, - }, - statusBadge: { - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: 8, - paddingVertical: 3, - borderRadius: 6, - marginBottom: 6, - alignSelf: 'flex-start', - }, - statusDot: { - width: 6, - height: 6, - borderRadius: 3, - marginRight: 5, - }, - statusText: { - fontSize: 10, - fontWeight: '800', - letterSpacing: 0.5, - }, - title: { - fontWeight: '800', - fontSize: 16, - color: '#1E293B', - }, - amountContainer: { - alignItems: 'flex-end', - }, - amount: { - fontWeight: '900', - fontSize: 18, - color: '#0F172A', - }, - feeLabel: { - fontSize: 10, - color: '#64748B', - fontWeight: '500', - }, - dateRow: { - backgroundColor: '#F8FAFC', - paddingHorizontal: 10, - paddingVertical: 8, - borderRadius: 10, - marginBottom: 16, - borderWidth: 1, - borderColor: '#F1F5F9', - }, - dateText: { - fontSize: 12, - color: '#334155', - fontWeight: '600', - }, - dateLabel: { - fontWeight: '400', - color: '#64748B', - }, - footer: { - borderTopWidth: 1, - borderTopColor: '#F1F5F9', - paddingTop: 12, - }, - footerTop: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - marginBottom: 12, - }, - footerLabel: { - fontSize: 11, - fontWeight: '600', - color: '#94A3B8', - textTransform: 'uppercase', - }, - endedText: { - fontSize: 13, - fontWeight: '700', - color: '#CBD5E1', - }, - actionButton: { - width: '100%', - height: 48, - borderRadius: 12, - justifyContent: 'center', - alignItems: 'center', - ...Platform.select({ - ios: { - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.1, - shadowRadius: 4, - }, - }), - }, - disabledButton: { - backgroundColor: '#E2E8F0', - }, - addText: { - color: '#fff', - fontSize: 14, - fontWeight: '800', - letterSpacing: 0.2, - }, + shadowContainer: { + marginHorizontal: 5, + marginTop: 14, + marginBottom: 6, + borderRadius: 16, + backgroundColor: '#fff', + elevation: 8, + }, + cardInternal: { + borderRadius: 16, + overflow: 'hidden', + flexDirection: 'row', + backgroundColor: '#fff', + }, + statusAccent: { + width: 5, + height: '100%', + }, + mainInfo: { + flex: 1, + padding: 16, + }, + headerRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'flex-start', + marginBottom: 12, + }, + statusBadge: { + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: 8, + paddingVertical: 3, + borderRadius: 6, + marginBottom: 6, + alignSelf: 'flex-start', + }, + statusDot: { + width: 6, + height: 6, + borderRadius: 3, + marginRight: 5, + }, + statusText: { + fontSize: 10, + fontWeight: '800', + letterSpacing: 0.5, + }, + title: { + fontWeight: '800', + fontSize: 16, + color: '#1E293B', + }, + amountContainer: { + alignItems: 'flex-end', + }, + amount: { + fontWeight: '900', + fontSize: 18, + color: '#0F172A', + }, + feeLabel: { + fontSize: 10, + color: '#64748B', + fontWeight: '500', + }, + dateRow: { + backgroundColor: '#F8FAFC', + paddingHorizontal: 10, + paddingVertical: 8, + borderRadius: 10, + marginBottom: 16, + borderWidth: 1, + borderColor: '#F1F5F9', + }, + dateText: { + fontSize: 12, + color: '#334155', + fontWeight: '600', + }, + dateLabel: { + fontWeight: '400', + color: '#64748B', + }, + footer: { + borderTopWidth: 1, + borderTopColor: '#F1F5F9', + paddingTop: 12, + }, + footerTop: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: 12, + }, + footerLabel: { + fontSize: 11, + fontWeight: '600', + color: '#94A3B8', + textTransform: 'uppercase', + }, + endedText: { + fontSize: 13, + fontWeight: '700', + color: '#CBD5E1', + }, + actionButton: { + width: '100%', + height: 48, + borderRadius: 12, + justifyContent: 'center', + alignItems: 'center', + ...Platform.select({ + ios: { + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 4, + }, + }), + }, + disabledButton: { + backgroundColor: '#E2E8F0', + }, + addText: { + color: '#fff', + fontSize: 14, + fontWeight: '800', + letterSpacing: 0.2, + }, }); diff --git a/src/Screens/Tender/Tender.tsx b/src/Screens/Tender/Tender.tsx index 55b7f3e..b293127 100644 --- a/src/Screens/Tender/Tender.tsx +++ b/src/Screens/Tender/Tender.tsx @@ -30,12 +30,9 @@ const Tender = () => { const { data: purchaseTenderData = {} } : any = useFetchPurchaseTender(); const SelectedPurchaseList = useSelector((state: any) => state?.selectPurchaseTender); - - console.log("liveTenderData : ", liveTenderData) - console.log("purchaseTenderData : ", purchaseTenderData) const liveCount = liveTenderData?.totalCount || 0; - const purchaseCount = purchaseTenderData?.totalCount || 0; + const purchaseCount = purchaseTenderData?.pages?.[0]?.totalCount || 0; const [selectedTab, setSelectedTab] = useState<'live' | 'purchase'>('live'); const translateX = useRef(new Animated.Value(0)).current; diff --git a/src/Screens/Tender/tenderBidding/TenderBidding.tsx b/src/Screens/Tender/tenderBidding/TenderBidding.tsx new file mode 100644 index 0000000..b4e10bf --- /dev/null +++ b/src/Screens/Tender/tenderBidding/TenderBidding.tsx @@ -0,0 +1,663 @@ +import { + ActivityIndicator, + Alert, + FlatList, + ScrollView, + StyleSheet, + Text, + ToastAndroid, + TouchableOpacity, + View, +} from 'react-native'; +import React, { use, useEffect, useState } from 'react'; +import { useNavigation, useRoute } from '@react-navigation/native'; +import MyInput from '../../../../src/Component/MyInput'; +import { pick, types } from '@react-native-documents/picker'; +import { InsideHeader } from '../../../Component/Index'; +import colors from '../../../Constant/Color'; + +type EnvelopeType = 'A' | 'B' | 'C'; +type EnvelopeFormState = { + A: Record; + B: Record; + C: Record; +}; + +interface EnvelopeButtonProps { + title: string; + onPress: () => void; +} + +interface InfoRowProps { + title: string; + value: string; +} + +interface TenderBiddingData { + data?: { + tenderNumber?: string; + nitNo?: string; + emdInFig?: string; + tenderRebid?: boolean; + showEA?: boolean; + showEB?: boolean; + showEC?: boolean; + tenderEnvelopeAList?: any[]; + tenderEnvelopeBList?: any[]; + tenderEnvelopeCList?: any[]; + }; +} + +const TenderBidding = () => { + const route = useRoute(); + const navigation = useNavigation(); + + const [bidData] = useState(route.params?.tenderData || null); + const [activeEnvelope, setActiveEnvelope] = useState('A'); + const [isLoading, setIsLoading] = useState(false); + const [envelopForm, setEnvelopForm] = useState({ + A: {}, + B: {}, + C: {}, + }); + const [loading, setLoading] = useState(false) + const [loadingUpload, setLoadingUpload] = useState(false) + const [check, setCheck] = useState(false) + + + const InfoRow = ({ title, value }: InfoRowProps) => ( + + {title} + {value || '-'} + + ); + + const EnvelopeButton = ({ title, envelopeType, onPress }: EnvelopeButtonProps & { envelopeType: EnvelopeType }) => ( + + {title} + + ); + + + const handleEnvelopeChange = ( + envelopeType: EnvelopeType, + sequenceId: number, + value: any + ) => { + setEnvelopForm(prev => ({ + ...prev, + [envelopeType]: { + ...prev[envelopeType], + [sequenceId]: value, + }, + })); + }; + + const pickPdf = async ( + envelopeType: EnvelopeType, + sequenceId: number + ) => { + try { + const res = await pick({ + type: types.pdf, + allowMultiSelection: false, + }); + + const file = res[0]; + + handleEnvelopeChange(envelopeType, sequenceId, file); + } catch (err: any) { + if (err?.code === 'DOCUMENT_PICKER_CANCELED') { + return; + } + console.error(err); + } + }; + + + const renderEnvelopeItem = ({ + item, + envelopeType, + }: { + item: any; + envelopeType: EnvelopeType; + }) => { + const sequenceId = item?.[`tenderEnvelope${envelopeType}SequnceId`]; + + if (!sequenceId) { + return null; + } + + const labelName = item?.[`labelName${envelopeType}`] || ''; + const labelType = item?.[`labelType${envelopeType}`] || ''; + + const showInput = + activeEnvelope === envelopeType !== 'C' && + !bidData?.[`envelope${envelopeType}SubmittStatus`]; + + + return ( + + + + {/* Label Name */} + {labelName || '-'} + {item?.[`labelMandatory${activeEnvelope}`] && *} + + + + {/* + Label Type + {labelType || '-'} + */} + + + {showInput ? ( + <> + {labelType === '1' && ( + + handleEnvelopeChange(envelopeType, sequenceId, text) + } + keyboardType="numeric" + /> + )} + + {labelType === '2' && ( + + handleEnvelopeChange(envelopeType, sequenceId, text) + } + keyboardType="default" + /> + )} + {labelType === '3' && ( + <> + pickPdf(activeEnvelope, sequenceId)} + > + + {envelopForm?.[activeEnvelope]?.[sequenceId] + ? 'Change Document' + : 'Upload Document'} + + + + {envelopForm?.[activeEnvelope]?.[sequenceId] && ( + + {(envelopForm[activeEnvelope][sequenceId] as any)?.name} + + )} + + + )} + + {labelType === '4' && ( + + handleEnvelopeChange(envelopeType, sequenceId, text) + } + keyboardType="numeric" + /> + )} + + + ) : Envelop {activeEnvelope} unavailable} + + ); + }; + + const hasEnvelopes = () => { + const hasA = bidData?.tenderEnvelopeAList?.length > 0; + const hasB = bidData?.tenderEnvelopeBList?.length > 0; + // const hasC = bidData?.tenderEnvelopeCList?.length > 0; + return hasA || hasB; + }; + + const digitToWordMap: Record = { + '0': 'Zero', + '1': 'One', + '2': 'Two', + '3': 'Three', + '4': 'Four', + '5': 'Five', + '6': 'Six', + '7': 'Seven', + '8': 'Eight', + '9': 'Nine', + }; + + const convertDigitsToWords = (value: string): string => { + return value + .split('') + .map(digit => digitToWordMap[digit] || '') + .join(' ') || value; + }; + + const buildEnvelopePayload = () => { + const list = + bidData?.[`tenderEnvelope${activeEnvelope}List`] || []; + const formValues = envelopForm?.[activeEnvelope] || {}; + + // 🔹 NON-FILE FIELDS (labelType !== '3') + const saveData = list + .filter( + (item: any) => + item[`labelType${activeEnvelope}`] !== '3' + ) + .map((item: any) => { + const seqId = + item[`tenderEnvelope${activeEnvelope}SequnceId`]; + const value = formValues[seqId]; + + return { + [`tenderEnvelope${activeEnvelope}SequnceId`]: seqId, + [`labelName${activeEnvelope}`]: + item[`labelName${activeEnvelope}`], + [`labelType${activeEnvelope}`]: + item[`labelType${activeEnvelope}`], + [`labelMandatory${activeEnvelope}`]: + item[`labelMandatory${activeEnvelope}`], + fieldValue: value ?? '', + fieldValueInWord: value + ? convertDigitsToWords(value) + : '', + }; + }); + + // 🔹 FILE FIELDS (labelType === '3') → upload separately + const fileData = list + .filter( + (item: any) => + item[`labelType${activeEnvelope}`] === '3' + ) + .map((item: any) => { + const seqId = + item[`tenderEnvelope${activeEnvelope}SequnceId`]; + + return { + [`tenderEnvelope${activeEnvelope}SequnceId`]: seqId, + [`labelName${activeEnvelope}`]: + item[`labelName${activeEnvelope}`], + [`labelType${activeEnvelope}`]: + item[`labelType${activeEnvelope}`], + [`labelMandatory${activeEnvelope}`]: + item[`labelMandatory${activeEnvelope}`], + }; + }); + + return payload = { + deptId: bidData?.deptId, + tenderId: bidData?.tenderId, + + + [`tenderEnvelope${activeEnvelope}NameId`]: + bidData?.[`tenderEnvelope${activeEnvelope}List`]?.[0]?.[ + `tenderEnvelope${activeEnvelope}NameId` + ], + [`envelope${activeEnvelope}TemplateName`]: + bidData?.[`tenderEnvelope${activeEnvelope}List`]?.[0]?.[ + `envelope${activeEnvelope}TemplateName` + ], + + [`save${activeEnvelope}Data`]: envelopForm?.[activeEnvelope]?.[3] + ? fileData + : saveData, + }; + + }; + + + const validateEnvelope = () => { + const list = bidData?.[`tenderEnvelope${activeEnvelope}List`] || []; + const formValues = envelopForm[activeEnvelope]; + + const missing = list.some((item: any) => { + const seqId = item[`tenderEnvelope${activeEnvelope}SequnceId`]; + const isMandatory = item[`labelMandatory${activeEnvelope}`]; + const value = formValues?.[seqId]; + + if (!isMandatory) return false; + + // File upload + if (item[`labelType${activeEnvelope}`] === '3') { + return !value; + } + + // Text / numeric + return !value || value.toString().trim() === ''; + }); + + return !missing; + }; + + + const handleSubmitEnvelopeA = async () => { + if (!validateEnvelope()) { + Alert.alert('Validation Error', 'Please fill all mandatory fields'); + return; + } + + const payload = buildEnvelopePayload(); + console.log('Envelope A Payload:', payload); + setLoading(true) + if (envelopForm?.[activeEnvelope]?.[3]) { + handleSubmitEnvelopeFile() + } + try { + + const res = await apiCallForPostWithToken(SUBMIT_ENVELOPE_A, payload) + console.log("res A envelop : ", res) + ToastAndroid.show(res.message, ToastAndroid.SHORT) + + + navigation.goBack() + } catch (error) { + console.log("error A envelop submit : ", error) + } finally { + setLoading(false) + } + }; + + + const handleSubmitEnvelopeB = async () => { + if (!validateEnvelope()) { + Alert.alert('Validation Error', 'Please fill all mandatory fields'); + return; + } + setLoading(true) + const payload = buildEnvelopePayload(); + console.log('Envelope B Payload:', payload); + if (envelopForm?.[activeEnvelope]?.[3]) { + handleSubmitEnvelopeFile() + } + try { + const res = await apiCallForPostWithToken(SUBMIT_ENVELOPE_B, payload) + console.log("res B envelop : ", res) + + ToastAndroid.show(res.message, ToastAndroid.SHORT) + navigation.goBack() + } catch (error) { + console.log("error B envelop submit : ", error) + } finally { + setLoading(false) + } + } + + const handleSubmitEnvelopeFile = async () => { + + setLoadingUpload(true) + const payload = buildEnvelopePayload(); + console.log(`Envelope file ${activeEnvelope} Payload:`, payload); + try { + const formData = new FormData(); + + formData.append('files', { + uri: envelopForm?.[activeEnvelope]?.[3].uri, + name: envelopForm?.[activeEnvelope]?.[3].name, + type: envelopForm?.[activeEnvelope]?.[3].type, + }); + formData.append('requestDto', payload) + + + console.log("reuestDto for fil doc : ", formData) + // return + const res = await apiCallForPostWithToken(activeEnvelope === 'B' ? SUBMIT_ENVELOPE_B : SUBMIT_ENVELOPE_A, payload) + console.log(`res file ${activeEnvelope} Payload:`, res) + ToastAndroid.show(res.message, ToastAndroid.SHORT) + // navigation.goBack() + } catch (error) { + console.log(`Envelope file ${activeEnvelope} error:`, error) + } finally { + setLoadingUpload(false) + } + } + + if (isLoading) { + return ( + <> + + Loading... + + + ); + } + + console.log("route for date : ", route.params) + return ( + + + + {/* Tender Info */} + + + + + + + + + + + + + {/* Envelope Buttons */} + + Bid Envelopes + + {bidData?.showEA && ( + setActiveEnvelope('A')} + /> + )} + {bidData?.showEB && ( + { + if + (bidData?.envelopeASubmittStatus) { + setActiveEnvelope('B') + } else { + Alert.alert('Info', 'First Fill Envelop A First') + } + }} + /> + )} + {bidData?.showEC && ( + setActiveEnvelope('C')} + /> + )} + + + {/* Envelope A List */} + {bidData?.[`tenderEnvelope${activeEnvelope}List`]?.length > 0 && activeEnvelope !== 'C' && ( + + Envelope {activeEnvelope} Details + item?.[`tenderEnvelope${activeEnvelope}SequnceId`]?.toString()} + renderItem={({ item }) => renderEnvelopeItem({ item, envelopeType: activeEnvelope })} + scrollEnabled={false} + /> + {/* + setCheck(!check)}> + {check ? : + + } + Check First Terms&Condition + */} + + )} + + {hasEnvelopes() ? ( + { + if (!check) { + Alert.alert('Info', 'check first terms & condition') + } + if (activeEnvelope === 'A') { + handleSubmitEnvelopeA() + } else { + handleSubmitEnvelopeB() + } + }} + > + {loading ? 'Submitting...' : `Submit Bid (${activeEnvelope})`} + + ) : ( + navigation.goBack()} + + > + Go Back + + )} + + + + + ); +}; + +export default TenderBidding; + +const styles = StyleSheet.create({ + container: { + flex: 1, backgroundColor: colors.white + }, + scrollContainer: { + padding: 16, + paddingBottom: '100%', + }, + loadingContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, + card: { + backgroundColor: '#fff', + borderRadius: 12, + padding: 16, + marginBottom: 16, + elevation: 2, + shadowColor: '#000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.1, + shadowRadius: 2, + }, + sectionTitle: { + fontSize: 16, + fontWeight: '600', + marginBottom: 12, + color: '#333', + }, + row: { + flexDirection: 'row', + justifyContent: 'space-between', + marginBottom: 8, + }, + infoRow: { + flex: 1, + marginBottom: 10, + }, + label: { + fontSize: 13, + color: '#777', + marginBottom: 2, + }, + value: { + fontSize: 15, + color: '#222', + fontWeight: '500', + }, + envelopeRow: { + flexDirection: 'row', + gap: 10, + flexWrap: 'wrap', + }, + envelopeButton: { + backgroundColor: '#EEF2FF', + paddingVertical: 10, + paddingHorizontal: 14, + borderRadius: 8, + minWidth: 100, + alignItems: 'center', + }, + envelopeButtonText: { + color: '#3F51B5', + fontWeight: '600', + fontSize: 14, + }, + envelopeItem: { + borderWidth: 1, + borderColor: '#eee', + borderRadius: 10, + padding: 12, + marginBottom: 12, + backgroundColor: '#f9f9f9', + flexDirection: 'row', + justifyContent: 'space-between' + }, + footer: { + flexDirection: 'row', + justifyContent: 'flex-end', + marginVertical: 20, + }, + primaryButton: { + backgroundColor: '#3F51B5', + paddingVertical: 14, + paddingHorizontal: 28, + borderRadius: 10, + minWidth: 120, + alignItems: 'center', + }, + primaryButtonText: { + color: '#fff', + fontWeight: '600', + fontSize: 15, + }, + secondaryButton: { + borderWidth: 1, + borderColor: '#EEF2FF', + paddingVertical: 14, + paddingHorizontal: 28, + borderRadius: 10, + minWidth: 120, + alignItems: 'center', + }, + secondaryButtonText: { + color: '#3F51B5', + fontWeight: '600', + fontSize: 15, + }, +}); \ No newline at end of file diff --git a/src/Screens/index.ts b/src/Screens/index.ts index 05e568f..3f4d9de 100644 --- a/src/Screens/index.ts +++ b/src/Screens/index.ts @@ -7,9 +7,11 @@ import Home from "./Home/Home"; import Login from "./Login/Login"; import Onboarding from "./Onboarding/Onboarding"; import Splash from "./Splash/Splash"; +import ProceedToBidDetail from "./Tender/ProceedToBidDetail"; import ProceedToPurTender from "./Tender/ProceedToPurTender"; import PurchaseTenderList from "./Tender/PurchaseTenderList"; import Tender from "./Tender/Tender"; +import TenderBidding from "./Tender/tenderBidding/TenderBidding"; const Screens = { @@ -24,6 +26,8 @@ const Screens = { StartBidding, PurchaseAuctionList, PurchaseTenderList, - ProceedToPurTender + ProceedToPurTender, + TenderBidding, + ProceedToBidDetail } export default Screens \ No newline at end of file diff --git a/src/Services/BBPS/ApiUrls.tsx b/src/Services/BBPS/ApiUrls.tsx index 791ba5d..d360396 100644 --- a/src/Services/BBPS/ApiUrls.tsx +++ b/src/Services/BBPS/ApiUrls.tsx @@ -34,7 +34,7 @@ const PURCHASE_OFFLINE_AUCTION = `${BASE_URL_Auction}api/auction/purchaseAuction const PURCHASE_TENDER = `https://procurelinc.in/EProcurementSB/tenderPayment/purchaseTender` const PURCHASE_TENDER_OFFLINE = `https://procurelinc.in/EProcurementSB/tenderPayment/purchaseTenderOffline` const AUCTION_PURCHASE_STATUS = `https://api.godemo.in/Auction/api/auction/transactionStatus?orderNo=` -const GET_TENDER_DETAILS = `${BASE_URL}eTendering/getTenderById?tenderId=` +const GET_TENDER_DETAILS = `https://procurelinc.in/EProcurementSB/tenderBidding/getTenderDetail?tenderId=` const GET_DOCID = `${BASE_URL}eTendering/download/docId?docId=` // https://procurelinc.in/EProcurementSB/eTendering/download/docId?docId=158 // const DOWNLOAD_DOC = `${BASE_URL}eTende ring/download/docId?docId=` @@ -66,6 +66,12 @@ const DASHBOARD_COUNT = `${BASE_URL_Auction}api/auction/home/dashboard/counts` //tender const TENDER_LOGIN = `${BASE_URL}eproc/loginWithoutDecryption` +const PROCEED_TO_BID = `${BASE_URL}tenderBidding/proceedToBid?tenderId=` +const SUBMIT_ENVELOPE_A = `${BASE_URL}tenderBidding/getSaveEnvelopeAData` +const SUBMIT_ENVELOPE_B = `${BASE_URL}tenderBidding/getSaveEnvelopeBData` +const SUBMIT_ENVELOPE_FILE_A = `${BASE_URL}tenderBidding/addDocumentsEnvelopeA?requestDto=` +const SUBMIT_ENVELOPE_FILE_B = `${BASE_URL}tenderBidding/addDocumentsEnvelopeB?requestDto=` + export { @@ -127,5 +133,10 @@ export { PURCHASE_OFFLINE_AUCTION, TENDER_LOGIN, PURCHASE_TENDER_OFFLINE, + PROCEED_TO_BID, + SUBMIT_ENVELOPE_A, + SUBMIT_ENVELOPE_B, + SUBMIT_ENVELOPE_FILE_A, + SUBMIT_ENVELOPE_FILE_B }; diff --git a/src/Services/BBPS/Hooks.ts b/src/Services/BBPS/Hooks.ts index 9e7dbc5..070195a 100644 --- a/src/Services/BBPS/Hooks.ts +++ b/src/Services/BBPS/Hooks.ts @@ -1,5 +1,5 @@ import { useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query'; -import { fetchDashboard, fetchLiveAuction, fetchLiveTender, fetchPurchaseAuction, fetchPurchaseTender, offlineAuctionPurchase, userLogin, offlineTenderPurchase, fetchTenderById } from './apis'; +import { fetchDashboard, fetchLiveAuction, fetchLiveTender, fetchPurchaseAuction, fetchPurchaseTender, offlineAuctionPurchase, userLogin, offlineTenderPurchase, fetchTenderById, proceedToBid } from './apis'; // User @@ -19,11 +19,31 @@ export const useDashboard = () => { // Auction & Tender -export const useFetchPurchaseTender = (size: number) => { - return useQuery({ - queryKey: ['purchaseTender', size], - queryFn: () => fetchPurchaseTender({ page: 0, size }), - }); +export const useFetchPurchaseTender = () => { + return useInfiniteQuery({ + queryKey: ['fetchPurchaseTender'], + initialPageParam: 1, + queryFn: ({ pageParam }) => + fetchPurchaseTender({ + page: pageParam, + size: 10, + }), + + getNextPageParam: (lastPage) => { + const { page, size, totalCount } = lastPage; + + const totalPages = Math.ceil(totalCount / size); + + return page < totalPages ? page + 1 : undefined; + }, + + select: (data) => ({ + ...data, + result: data.pages.flatMap( + (page: any) => page?.data ?? [] + ), + }), + }); }; @@ -37,27 +57,7 @@ export const useFetchLiveTender = () => { export const useFetchPurchaseAuction = () => { return useQuery({ queryKey: ['fetchPurchaseAuction',], - queryFn: ({ pageParam = 1 }) => - fetchPurchaseAuction({ - page: pageParam, - size: 10, - }), - getNextPageParam: (lastPage: any) => { - const { page, size, totalCount } = lastPage; - const totalPages = Math.ceil(totalCount / size); - const nextPage = page + 1; - return nextPage < totalPages ? nextPage : undefined; - }, - select: (data) => { - - const mergedResults = data.pages.flatMap((page: any) => page.data ?? []); - - console.log("mergedResults for tender : ", mergedResults) - return { - ...data, - result: mergedResults, - }; - }, + queryFn: () => fetchPurchaseAuction() }); }; @@ -107,7 +107,16 @@ export const useFetchTenderById = (tenderId: string) => { return useQuery({ queryKey: ['fetchTenderById', tenderId], queryFn: () => fetchTenderById(tenderId) + }); } +export const useProceedToBid = (tenderId?: string) => { + return useQuery({ + queryKey: ['proceedToBid', tenderId], + queryFn: () => proceedToBid(tenderId as string), + enabled: false, // 👈 manual trigger + }); +}; + diff --git a/src/Services/BBPS/apis.ts b/src/Services/BBPS/apis.ts index 0a83786..d952b04 100644 --- a/src/Services/BBPS/apis.ts +++ b/src/Services/BBPS/apis.ts @@ -1,5 +1,5 @@ import axios from "axios"; -import { DASHBOARD_COUNT, GET_MY_AUCTION, GET_MY_TENDER, GET_TENDER_DETAILS, LIVE_AUCTION, LIVE_TENDER, LOG_IN_URL, PROCEED_PURCHASE_AUCTION, PURCHASE_TENDER_OFFLINE, TENDER_LOGIN } from "./ApiUrls"; +import { DASHBOARD_COUNT, GET_MY_AUCTION, GET_MY_TENDER, GET_TENDER_DETAILS, LIVE_AUCTION, LIVE_TENDER, LOG_IN_URL, PROCEED_PURCHASE_AUCTION, PROCEED_TO_BID, PURCHASE_TENDER_OFFLINE, TENDER_LOGIN } from "./ApiUrls"; import { apiCall } from "../../Axios/Axios"; // User @@ -19,8 +19,8 @@ export const fetchDashboard = async (): Promise => { return data } -export const fetchPurchaseAuction = async (payload: any): Promise => { - const data = await apiCall('get', `${GET_MY_AUCTION}`, {}, { payload }); +export const fetchPurchaseAuction = async (): Promise => { + const data = await apiCall('get', `${GET_MY_AUCTION}`); return data } export const fetchLiveAuction = async (payload: any): Promise => { @@ -49,43 +49,37 @@ export const offlineTenderPurchase = async (payload: any): Promise => { }; // tender -export const fetchPurchaseTender = async (payload: any): Promise => { - const data = await apiCall( +export const fetchPurchaseTender = async ({ + page, + size, +}: { + page: number; + size: number; +}) => { + return apiCall( 'get', - `${GET_MY_TENDER}`, {}, - { payload } + `${GET_MY_TENDER}?page=${page}&size=${size}` ); - return data; }; -// export const fetchPurchaseTender = async ( -// page: number, -// size: number -// ): Promise => { -// const data = await apiCall( -// 'get', -// `${GET_MY_TENDER}?page=0&size=${size}` -// ); - -// console.log('Purchased Tenders Response:', data); -// return data; -// }; - export const fetchLiveTender = async (): Promise => { const data = await apiCall('get', `${LIVE_TENDER}`); return data } export const fetchTenderById = async (tenderId: string): Promise => { - console.log('Fetching tender with ID:', tenderId); - const data = await apiCall( 'get', - `https://procurelinc.in/EProcurementSB/eTendering/getTenderById?tenderId=${tenderId}` + `${GET_TENDER_DETAILS}${tenderId}` ); - - console.log('API response:', data); return data; }; +export const proceedToBid = async (tenderId: string): Promise => { + const data = await apiCall( + 'get', + `${PROCEED_TO_BID}${tenderId}` + ); + return data; +}