From 39924066bcdae1cb91621445f2cb288883e83c95 Mon Sep 17 00:00:00 2001 From: Jaanu423 Date: Mon, 12 Jan 2026 23:08:53 +0530 Subject: [PATCH 1/2] envelop c --- package-lock.json | 135 ++++++++++ package.json | 2 + src/Screens/Tender/ProceedToBidDetail.tsx | 1 + .../Tender/component/EditableExcel.tsx | 242 ++++++++++++++++++ .../Tender/tenderBidding/TenderBidding.tsx | 48 +++- src/Services/BBPS/ApiUrls.tsx | 4 +- src/Services/BBPS/Hooks.ts | 2 - 7 files changed, 425 insertions(+), 9 deletions(-) create mode 100644 src/Screens/Tender/component/EditableExcel.tsx diff --git a/package-lock.json b/package-lock.json index 91c64a1..613ff0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "react-native-app-intro-slider": "^4.0.4", "react-native-bouncy-checkbox": "^4.1.3", "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", @@ -50,6 +51,7 @@ "redux-persist": "^6.0.0", "sockjs-client": "^1.6.1", "text-encoding": "^0.7.0", + "xlsx": "^0.18.5", "yup": "^1.7.1" }, "devDependencies": { @@ -4568,6 +4570,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -5094,6 +5105,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", @@ -5406,6 +5422,19 @@ ], "license": "CC-BY-4.0" }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5571,6 +5600,15 @@ "node": ">= 0.12.0" } }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/collect-v8-coverage": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", @@ -5797,6 +5835,18 @@ } } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -7486,6 +7536,15 @@ "node": ">=0.10.0" } }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -11751,6 +11810,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", @@ -12902,6 +12980,18 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "license": "BSD-3-Clause" }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -13741,6 +13831,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", @@ -13949,6 +14045,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -14003,6 +14117,27 @@ "async-limiter": "~1.0.0" } }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", diff --git a/package.json b/package.json index 3c534f4..d1e932d 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "react-native-app-intro-slider": "^4.0.4", "react-native-bouncy-checkbox": "^4.1.3", "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", @@ -52,6 +53,7 @@ "redux-persist": "^6.0.0", "sockjs-client": "^1.6.1", "text-encoding": "^0.7.0", + "xlsx": "^0.18.5", "yup": "^1.7.1" }, "devDependencies": { diff --git a/src/Screens/Tender/ProceedToBidDetail.tsx b/src/Screens/Tender/ProceedToBidDetail.tsx index f981123..b9ec5ce 100644 --- a/src/Screens/Tender/ProceedToBidDetail.tsx +++ b/src/Screens/Tender/ProceedToBidDetail.tsx @@ -11,6 +11,7 @@ const ProceedToBidDetail = () => { const tenderData = route.params?.tender; const {data, refetch} = useProceedToBid(tenderData?.tenderId); + console.log("proceed to bid tender detail : ", tenderData) const navigation = useNavigation() // Format date from array diff --git a/src/Screens/Tender/component/EditableExcel.tsx b/src/Screens/Tender/component/EditableExcel.tsx new file mode 100644 index 0000000..d5b2a62 --- /dev/null +++ b/src/Screens/Tender/component/EditableExcel.tsx @@ -0,0 +1,242 @@ +import React, { useEffect, useState } from 'react'; +import { View, Text, TextInput, ScrollView, TouchableOpacity, StyleSheet, ActivityIndicator, Alert, } from 'react-native'; +import RNFS from 'react-native-fs'; +import * as XLSX from 'xlsx'; +import axios from 'axios'; +import { useSelector } from 'react-redux'; +import { InsideHeader } from '../../../Component/Index'; +import colors from '../../../Constant/Color'; +import { SUBMIT_ENVELOPE_C } from '../../../Services/BBPS/ApiUrls'; + + +// Excel column keys +const QTY_KEY = '__EMPTY_1'; +const RATE_KEY = '__EMPTY_2'; +const VALUE_KEY = '__EMPTY_3'; +const TOTAL_LABEL = 'TOTAL'; + +const EnvelopeCExcelPreview = (tenderId:any) => { + const token = useSelector((state: any) => state.token.token); + const [data, setData] = useState([]); + const [columns, setColumns] = useState([]); + const [loading, setLoading] = useState(false); + + console.log("tender id for excel : ", tenderId) + useEffect(() => { + loadExcelFromApi(); + }, []); + + // ArrayBuffer → Base64 + const arrayBufferToBase64 = (buffer: ArrayBuffer) => { + let binary = ''; + const bytes = new Uint8Array(buffer); + for (let i = 0; i < bytes.length; i++) { + binary += String.fromCharCode(bytes[i]); + } + return global.btoa(binary); + }; + + // Calculate TOTAL VALUE + const calculateTotal = (rows: any[]) => + rows.reduce((sum, row) => { + if (typeof row['DEMO DEPARTEMENT'] !== 'number') return sum; + const val = Number(row[VALUE_KEY]); + return sum + (isNaN(val) ? 0 : val); + }, 0); + + // LOAD EXCEL + const loadExcelFromApi = async () => { + try { + setLoading(true); + + const response = await axios.get(`${SUBMIT_ENVELOPE_C}${tenderId?.tenderId}`, { + responseType: 'arraybuffer', + headers: { + Authentication: `Bearer ${token.token}`, + }, + }); + + const base64 = arrayBufferToBase64(response.data); + const workbook = XLSX.read(base64, { type: 'base64' }); + const sheet = workbook.Sheets[workbook.SheetNames[0]]; + const rows: any[] = XLSX.utils.sheet_to_json(sheet, { defval: '' }); + + setColumns(Object.keys(rows[0] || {})); + setData(rows); + + await RNFS.writeFile( + `${RNFS.DocumentDirectoryPath}/envelopeC.xlsx`, + base64, + 'base64' + ); + } catch (e) { + Alert.alert('Error', 'Failed to load Excel'); + } finally { + setLoading(false); + } + }; + + // UPDATE CELL (QTY / RATE) + AUTO VALUE + AUTO TOTAL + const updateCell = (rowIndex: number, key: string, value: string) => { + const updated = [...data]; + const row = updated[rowIndex]; + + // TOTAL row locked + if (row['DEMO DEPARTEMENT'] === TOTAL_LABEL) return; + + // Allow only QTY & RATE edit + if (key === QTY_KEY || key === RATE_KEY) { + row[key] = value; + + const qty = Number(row[QTY_KEY]); + const rate = Number(row[RATE_KEY]); + + // 🔥 Auto VALUE = QTY × RATE + row[VALUE_KEY] = + !isNaN(qty) && !isNaN(rate) ? qty * rate : 0; + } + + // 🔥 Update TOTAL + const totalRowIndex = updated.findIndex( + r => r['DEMO DEPARTEMENT'] === TOTAL_LABEL + ); + + if (totalRowIndex !== -1) { + updated[totalRowIndex][VALUE_KEY] = calculateTotal(updated); + } + + setData(updated); + }; + + // SAVE UPDATED EXCEL + const saveUpdatedExcel = async () => { + try { + setLoading(true); + + const worksheet = XLSX.utils.json_to_sheet(data); + const workbook = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1'); + + const base64 = XLSX.write(workbook, { + bookType: 'xlsx', + type: 'base64', + }); + + await RNFS.writeFile( + `${RNFS.DocumentDirectoryPath}/envelopeC_updated.xlsx`, + base64, + 'base64' + ); + + Alert.alert('Success', 'Excel saved with auto-calculation'); + } catch (e) { + Alert.alert('Error', 'Save failed'); + } finally { + setLoading(false); + } + }; + + if (loading) { + return ( + + + + ); + } + + return ( + + + + + {/* HEADER */} + + {columns.map(col => ( + + {col} + + ))} + + + {/* DATA */} + + {data.map((row, rowIndex) => ( + + {columns.map(key => { + const isTotal = row['DEMO DEPARTEMENT'] === TOTAL_LABEL; + const isEditable = + !isTotal && (key === QTY_KEY || key === RATE_KEY); + + return ( + + updateCell(rowIndex, key, text) + } + /> + ); + })} + + ))} + + + + + + Save Updated Excel + + + ); +}; + +export default EnvelopeCExcelPreview; + +const styles = StyleSheet.create({ + container: { flex: 1, backgroundColor: '#fff' }, + loader: { flex: 1, justifyContent: 'center', alignItems: 'center' }, + title: { fontSize: 18, fontWeight: '700', marginBottom: 10 }, + row: { flexDirection: 'row' }, + headerCell: { + minWidth: 140, + padding: 10, + backgroundColor: '#f1f5f9', + borderWidth: 1, + fontWeight: '700', + }, + cell: { + minWidth: 140, + padding: 10, + borderWidth: 1, + borderColor: '#cbd5e1', + }, + valueCell: { + backgroundColor: '#f8fafc', + }, + totalCell: { + backgroundColor: '#e5e7eb', + fontWeight: '700', + }, + button: { + marginTop: 15, + backgroundColor: '#2563eb', + padding: 14, + borderRadius: 8, + }, + buttonText: { + color: '#fff', + textAlign: 'center', + fontWeight: '600', + }, +}); diff --git a/src/Screens/Tender/tenderBidding/TenderBidding.tsx b/src/Screens/Tender/tenderBidding/TenderBidding.tsx index efd1b74..044f2ca 100644 --- a/src/Screens/Tender/tenderBidding/TenderBidding.tsx +++ b/src/Screens/Tender/tenderBidding/TenderBidding.tsx @@ -18,6 +18,7 @@ import colors from '../../../Constant/Color'; import { SUBMIT_ENVELOPE_A, SUBMIT_ENVELOPE_B, SUBMIT_ENVELOPE_FILE_A, SUBMIT_ENVELOPE_FILE_B } from '../../../Services/BBPS/ApiUrls'; import { Fontisto } from '@react-native-vector-icons/fontisto'; import { apiCall } from '../../../Axios/Axios'; +import EditableExcel from '../component/EditableExcel'; type EnvelopeType = 'A' | 'B' | 'C'; type EnvelopeFormState = { @@ -130,7 +131,7 @@ const TenderBidding = () => { const labelType = item?.[`labelType${envelopeType}`] || ''; const showInput = - activeEnvelope === envelopeType !== 'C' && + activeEnvelope === envelopeType && !bidData?.[`envelope${envelopeType}SubmittStatus`]; // console.log("tender ana : ", tenderData) @@ -208,17 +209,19 @@ const TenderBidding = () => { keyboardType="numeric" /> )} + this is envelope {activeEnvelope} ) : Envelop {activeEnvelope} unavailable} ); }; - + const hasC = activeEnvelope === 'C' && !tenderData?.envelopeCSubmittStatus; + ; const hasEnvelopes = () => { const hasA = tenderData?.tenderEnvelopeAList?.length > 0; const hasB = tenderData?.tenderEnvelopeBList?.length > 0; - // const hasC = tenderData?.tenderEnvelopeCList?.length > 0; + return hasA || hasB; }; @@ -357,7 +360,7 @@ const TenderBidding = () => { } try { - const res = await apiCall('post',SUBMIT_ENVELOPE_A, payload) + const res = await apiCall('post', SUBMIT_ENVELOPE_A, payload) console.log("res A envelop : ", res) ToastAndroid.show(res.message, ToastAndroid.SHORT) @@ -426,6 +429,9 @@ const TenderBidding = () => { } } + const handleSubmitEnvelopeC = async () => { + Alert.alert('Info', 'Envelope C submission not implemented yet'); + } return ( @@ -482,7 +488,7 @@ const TenderBidding = () => { {/* Envelope A List */} - {tenderData?.[`tenderEnvelope${activeEnvelope}List`]?.length > 0 && activeEnvelope !== 'C' && ( + {tenderData?.[`tenderEnvelope${activeEnvelope}List`]?.length > 0 && ( Envelope {activeEnvelope} Details { renderItem={({ item }) => renderEnvelopeItem({ item, envelopeType: activeEnvelope })} scrollEnabled={false} /> + setCheck(!check)}> {check ? : @@ -501,6 +508,31 @@ const TenderBidding = () => { )} + {activeEnvelope === 'C' && ( + + Envelope C Status + + {tenderData?.envelopeCSubmittStatus ? ( + <> + + Envelope C has been submitted successfully. + + + {tenderData?.envelopeC_Logs && ( + <> + Date: {tenderData.envelopeC_Logs.dateTime} + Message: {tenderData.envelopeC_Logs.logMessage} + + )} + + ) : ( + <> + + + )} + + )} + {hasEnvelopes() ? ( { } if (activeEnvelope === 'A') { handleSubmitEnvelopeA() - } else { + } + else if (activeEnvelope === 'B') { handleSubmitEnvelopeB() } + else { + handleSubmitEnvelopeC() + } }} > {loading ? 'Submitting...' : `Submit Bid (${activeEnvelope})`} diff --git a/src/Services/BBPS/ApiUrls.tsx b/src/Services/BBPS/ApiUrls.tsx index d360396..a8f9c25 100644 --- a/src/Services/BBPS/ApiUrls.tsx +++ b/src/Services/BBPS/ApiUrls.tsx @@ -71,6 +71,7 @@ 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=` +const SUBMIT_ENVELOPE_C = `${BASE_URL}eTendering/getEnvelopeCTemplate/tenderId?tenderId=` @@ -137,6 +138,7 @@ export { SUBMIT_ENVELOPE_A, SUBMIT_ENVELOPE_B, SUBMIT_ENVELOPE_FILE_A, - SUBMIT_ENVELOPE_FILE_B + SUBMIT_ENVELOPE_FILE_B, + SUBMIT_ENVELOPE_C }; diff --git a/src/Services/BBPS/Hooks.ts b/src/Services/BBPS/Hooks.ts index 8006ef3..aea8494 100644 --- a/src/Services/BBPS/Hooks.ts +++ b/src/Services/BBPS/Hooks.ts @@ -30,8 +30,6 @@ export const useFetchPurchaseTender = () => { }), initialPageParam: 0, getNextPageParam: (lastPage, allPages) => { - console.log('last page : ', lastPage) - console.log("all page : ", allPages) const { totalCount } = lastPage; const totalPages = Math.ceil(totalCount / 10); From e5f13c81f45bd88958e381f87679b99176f558f0 Mon Sep 17 00:00:00 2001 From: Jaanu423 Date: Tue, 13 Jan 2026 16:03:47 +0530 Subject: [PATCH 2/2] app crash from fill b upload --- src/Axios/Axios.ts | 1 + src/Screens/Tender/ProceedToBidDetail.tsx | 54 ++++++++---- src/Screens/Tender/PurchaseTenderTile.tsx | 9 +- .../Tender/component/EditableExcel.tsx | 66 ++++++++++++--- .../Tender/tenderBidding/TenderBidding.tsx | 82 ++++++++++--------- src/Services/BBPS/ApiUrls.tsx | 10 ++- src/Services/BBPS/Hooks.ts | 11 ++- 7 files changed, 152 insertions(+), 81 deletions(-) diff --git a/src/Axios/Axios.ts b/src/Axios/Axios.ts index 0c18004..0716236 100644 --- a/src/Axios/Axios.ts +++ b/src/Axios/Axios.ts @@ -24,6 +24,7 @@ export const apiCall = async ( 'Content-Type': contentType || 'application/json', }, }); + return response.data; } catch (error: any) { const errorMessage = error?.response?.data?.error; diff --git a/src/Screens/Tender/ProceedToBidDetail.tsx b/src/Screens/Tender/ProceedToBidDetail.tsx index b9ec5ce..e16962f 100644 --- a/src/Screens/Tender/ProceedToBidDetail.tsx +++ b/src/Screens/Tender/ProceedToBidDetail.tsx @@ -1,24 +1,30 @@ -import { StyleSheet, Text, View, ScrollView, StatusBar, TouchableOpacity } from 'react-native' -import React from 'react' +import { StyleSheet, Text, View, ScrollView, StatusBar, TouchableOpacity, RefreshControl } from 'react-native' +import React, { useEffect } from 'react' import { InsideHeader } from '../../Component/Index' import colors from '../../Constant/Color' import { useNavigation, useRoute } from '@react-navigation/native' -import { useProceedToBid } from '../../Services/BBPS/Hooks' +import { useFetchTenderById, useProceedToBid } from '../../Services/BBPS/Hooks' const ProceedToBidDetail = () => { // Sample data based on your JSON - const route = useRoute() - const tenderData = route.params?.tender; - const {data, refetch} = useProceedToBid(tenderData?.tenderId); + const route = useRoute(); + const tenderDataId = route.params?.tender; + const { data, refetch } = useProceedToBid(tenderDataId?.tenderId); + const { data: tenderResponse , refetch:tenderRefetch}: any = useFetchTenderById(tenderDataId?.tenderId); + const tenderData = tenderResponse?.data; + const [refreshing, setRefreshing] = React.useState(false); - console.log("proceed to bid tender detail : ", tenderData) + console.log("proceed to bid tender detail : ", tenderDataId) + console.log("proceed to data detail : ", data) + console.log("tender data for updated : ", tenderData) const navigation = useNavigation() // Format date from array const handleStartBidding = () => { refetch(); if (data) { - navigation.navigate('TenderBidding', { tenderData: tenderData , bidData: data.data }); + console.log(" ppp",tenderData ,"ll",data) + navigation.navigate('TenderBidding', { tenderData: tenderData, bidData: data.data }); } } const formatDate = (dateArray: any[]) => { @@ -28,7 +34,7 @@ const ProceedToBidDetail = () => { }; // Status indicator component - const StatusIndicator = ({ status, label }) => ( + const StatusIndicator = ({ status, label }: { status: boolean; label: string }) => ( @@ -38,7 +44,7 @@ const ProceedToBidDetail = () => { ); // Envelope log card component - const EnvelopeLogCard = ({ log, title }) => ( + const EnvelopeLogCard = ({ log, title }: { log: any; title: string }) => ( {title} @@ -82,7 +88,7 @@ const ProceedToBidDetail = () => { ); // Information row component - const InfoRow = ({ label, value, isHighlighted = false }) => ( + const InfoRow = ({ label, value, isHighlighted = false }: { label: string; value: string; isHighlighted?: boolean }) => ( {label}: {value} @@ -90,7 +96,7 @@ const ProceedToBidDetail = () => { ); // Feature chip component - const FeatureChip = ({ label, enabled }) => ( + const FeatureChip = ({ label, enabled }: { label: string; enabled: boolean }) => ( {label} {enabled ? '✓' : '✗'} @@ -98,12 +104,32 @@ const ProceedToBidDetail = () => { ); + const onRefresh = async () => { + try { + setRefreshing(true); + await refetch(); + } finally { + setRefreshing(false); + } + }; + + useEffect(()=>{ + tenderRefetch() + },[tenderDataId?.tenderId]) + return ( - - + + }> {/* Tender Header Section */} diff --git a/src/Screens/Tender/PurchaseTenderTile.tsx b/src/Screens/Tender/PurchaseTenderTile.tsx index 30b79db..0971565 100644 --- a/src/Screens/Tender/PurchaseTenderTile.tsx +++ b/src/Screens/Tender/PurchaseTenderTile.tsx @@ -36,15 +36,8 @@ const PurchaseTenderTile = ({ const { data: tenderResponse, }: any = useFetchTenderById(item?.tenderId); const tender = tenderResponse?.data; - // console.log("item my : ", item) - - { - if (item?.nitNumber == 'TN/111') { - console.log("tender item : ", item?.nitNumber) - - } - } + console.log("tender for bid : ", tender) const handlePressIn = () => { scale.value = withTiming(0.97, { duration: 120 }); elevation.value = withTiming(3, { duration: 120 }); diff --git a/src/Screens/Tender/component/EditableExcel.tsx b/src/Screens/Tender/component/EditableExcel.tsx index d5b2a62..db4f684 100644 --- a/src/Screens/Tender/component/EditableExcel.tsx +++ b/src/Screens/Tender/component/EditableExcel.tsx @@ -6,7 +6,8 @@ import axios from 'axios'; import { useSelector } from 'react-redux'; import { InsideHeader } from '../../../Component/Index'; import colors from '../../../Constant/Color'; -import { SUBMIT_ENVELOPE_C } from '../../../Services/BBPS/ApiUrls'; +import { GET_ENVELOPE_C, SUBMIT_ENVELOPE_C_UPLOAD } from '../../../Services/BBPS/ApiUrls'; +import { useNavigation } from '@react-navigation/native'; // Excel column keys @@ -15,13 +16,13 @@ const RATE_KEY = '__EMPTY_2'; const VALUE_KEY = '__EMPTY_3'; const TOTAL_LABEL = 'TOTAL'; -const EnvelopeCExcelPreview = (tenderId:any) => { +const EnvelopeCExcelPreview = (tenderData: any) => { const token = useSelector((state: any) => state.token.token); + const navigation = useNavigation() as any; const [data, setData] = useState([]); const [columns, setColumns] = useState([]); const [loading, setLoading] = useState(false); - console.log("tender id for excel : ", tenderId) useEffect(() => { loadExcelFromApi(); }, []); @@ -49,7 +50,7 @@ const EnvelopeCExcelPreview = (tenderId:any) => { try { setLoading(true); - const response = await axios.get(`${SUBMIT_ENVELOPE_C}${tenderId?.tenderId}`, { + const response = await axios.get(`${GET_ENVELOPE_C}${tenderData?.tenderId?.tenderId}`, { responseType: 'arraybuffer', headers: { Authentication: `Bearer ${token.token}`, @@ -117,25 +118,66 @@ const EnvelopeCExcelPreview = (tenderId:any) => { const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1'); - const base64 = XLSX.write(workbook, { + // Generate binary string + const binary = XLSX.write(workbook, { bookType: 'xlsx', - type: 'base64', + type: 'binary', }); - await RNFS.writeFile( - `${RNFS.DocumentDirectoryPath}/envelopeC_updated.xlsx`, - base64, - 'base64' + // Convert binary string to buffer + const buffer = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; i++) { + buffer[i] = binary.charCodeAt(i) & 0xff; + } + + + const filePath = `${RNFS.DocumentDirectoryPath}/envelopeC_updated.xlsx`; + + // Write as binary + await RNFS.writeFile(filePath, buffer.toString(), 'ascii'); + // const fileBinary = await RNFS.readFile(filePath, 'ascii'); + + // console.log('Excel binary content:', fileBinary); + // Alert.alert('Success', 'Excel saved successfully'); + const formData = new FormData(); + + // requestDto payload + formData.append( + 'requestDto', + JSON.stringify({ + tenderId: tenderData?.tenderId?.tenderId, + deptId: tenderData?.tenderId?.deptId, + typesOfTender: tenderData?.tenderId?.typesOfTender, + fieldValue: '', + fieldValueInWord: 'zero', + }) ); - Alert.alert('Success', 'Excel saved with auto-calculation'); + // binary file + formData.append('file', { + uri: `file://${filePath}`, + name: 'envelopeC_updated.xlsx', + type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + }); + + const res = await axios.post(SUBMIT_ENVELOPE_C_UPLOAD, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + Authentication: `Bearer ${token.token}`, + }, + }); + navigation.goBack(); + console.log("res of updated excel file : ", res) } catch (e) { + console.error(e); Alert.alert('Error', 'Save failed'); } finally { setLoading(false); } }; + + if (loading) { return ( @@ -230,7 +272,7 @@ const styles = StyleSheet.create({ }, button: { marginTop: 15, - backgroundColor: '#2563eb', + backgroundColor: colors.primary, padding: 14, borderRadius: 8, }, diff --git a/src/Screens/Tender/tenderBidding/TenderBidding.tsx b/src/Screens/Tender/tenderBidding/TenderBidding.tsx index 044f2ca..a3278d0 100644 --- a/src/Screens/Tender/tenderBidding/TenderBidding.tsx +++ b/src/Screens/Tender/tenderBidding/TenderBidding.tsx @@ -19,6 +19,8 @@ import { SUBMIT_ENVELOPE_A, SUBMIT_ENVELOPE_B, SUBMIT_ENVELOPE_FILE_A, SUBMIT_EN import { Fontisto } from '@react-native-vector-icons/fontisto'; import { apiCall } from '../../../Axios/Axios'; import EditableExcel from '../component/EditableExcel'; +import axios from 'axios'; +import { useSelector } from 'react-redux'; type EnvelopeType = 'A' | 'B' | 'C'; type EnvelopeFormState = { @@ -40,6 +42,7 @@ interface InfoRowProps { const TenderBidding = () => { const route = useRoute(); const navigation = useNavigation(); + const token = useSelector((state: any) => state.token.token); const [bidData] = useState(route.params?.tenderData || null); const tenderData: any = route.params?.bidData || null; @@ -209,7 +212,6 @@ const TenderBidding = () => { keyboardType="numeric" /> )} - this is envelope {activeEnvelope} ) : Envelop {activeEnvelope} unavailable} @@ -355,12 +357,12 @@ const TenderBidding = () => { console.log('Envelope A Payload:', payload); setLoading(true) if (envelopForm?.[activeEnvelope]?.[3]) { - handleSubmitEnvelopeFile() + handleSubmitEnvelopeFile(SUBMIT_ENVELOPE_FILE_A) return } try { - const res = await apiCall('post', SUBMIT_ENVELOPE_A, payload) + const res = await apiCall('post', SUBMIT_ENVELOPE_A, payload) as any console.log("res A envelop : ", res) ToastAndroid.show(res.message, ToastAndroid.SHORT) @@ -385,11 +387,11 @@ const TenderBidding = () => { if (envelopForm?.[activeEnvelope]?.[3]) { - handleSubmitEnvelopeFile() + handleSubmitEnvelopeFile(SUBMIT_ENVELOPE_FILE_B) return } try { - const res = await apiCall('post', SUBMIT_ENVELOPE_B, payload) + const res = await apiCall('post', SUBMIT_ENVELOPE_B, payload) as any console.log("res B envelop : ", res) ToastAndroid.show(res.message, ToastAndroid.SHORT) @@ -401,11 +403,10 @@ const TenderBidding = () => { } } - const handleSubmitEnvelopeFile = async () => { - + const handleSubmitEnvelopeFile = async (url: string) => { + console.log("url :: ", url) setLoadingUpload(true) const payload = buildEnvelopePayload(); - console.log(`Envelope file ${activeEnvelope} Payload:`, payload); try { const formData = new FormData(); @@ -414,26 +415,27 @@ const TenderBidding = () => { name: envelopForm?.[activeEnvelope]?.[3].name, type: envelopForm?.[activeEnvelope]?.[3].type, }); - formData.append('requestDto', payload) - - - console.log("reuestDto for fil doc : ", formData) - const res = await apiCall('post', activeEnvelope === 'B' ? SUBMIT_ENVELOPE_FILE_B : SUBMIT_ENVELOPE_FILE_A, payload) - console.log(`res file ${activeEnvelope} Payload:`, res) + // formData.append('requestDto', payload) + console.log("first token : ", token?.token) + formData.append('requestDto', JSON.stringify(payload)) + console.log("payload for fil doc : ", formData) + const res = await axios.post(url, formData, { + headers: { + Authentication: `Bearer ${token?.token}`, + 'Content-Type': 'multipart/form-data' + }, + }) as any; + console.log(`res file ${activeEnvelope} api response:`, res) ToastAndroid.show(res.message, ToastAndroid.SHORT) - // navigation.goBack() - } catch (error) { - console.log(`Envelope file ${activeEnvelope} error:`, error) + navigation.goBack() + } catch (error: any) { + console.log(`Envelope file ${activeEnvelope} error:`, error?.response || error.message || error); + ToastAndroid.show('Upload failed', ToastAndroid.SHORT); } finally { - setLoadingUpload(false) + setLoadingUpload(false); } } - const handleSubmitEnvelopeC = async () => { - Alert.alert('Info', 'Envelope C submission not implemented yet'); - } - - return ( @@ -473,7 +475,7 @@ const TenderBidding = () => { if (tenderData?.envelopeASubmittStatus) { setActiveEnvelope('B') } else { - Alert.alert('Info', 'First Fill Envelop A First') + Alert.alert('Info', 'Submit Envelop A First') } }} /> @@ -482,7 +484,13 @@ const TenderBidding = () => { setActiveEnvelope('C')} + onPress={() => { + if (tenderData?.envelopeBSubmittStatus) { + setActiveEnvelope('C') + } else { + Alert.alert('Info', 'Submit Envelop B First') + } + }} /> )} @@ -527,43 +535,43 @@ const TenderBidding = () => { ) : ( <> - + )} )} - {hasEnvelopes() ? ( + {hasEnvelopes() && activeEnvelope !== 'C' ? ( { if (!check) { - Alert.alert('Info', 'check first terms & condition') + Alert.alert('Info', 'check first terms & condition'); + return; } + if (activeEnvelope === 'A') { - handleSubmitEnvelopeA() - } - else if (activeEnvelope === 'B') { - handleSubmitEnvelopeB() - } - else { - handleSubmitEnvelopeC() + handleSubmitEnvelopeA(); + } else if (activeEnvelope === 'B') { + handleSubmitEnvelopeB(); } }} > - {loading ? 'Submitting...' : `Submit Bid (${activeEnvelope})`} + + {loading ? 'Submitting...' : `Submit Bid (${activeEnvelope})`} + ) : ( navigation.goBack()} - > Go Back )} + diff --git a/src/Services/BBPS/ApiUrls.tsx b/src/Services/BBPS/ApiUrls.tsx index a8f9c25..11d1d2a 100644 --- a/src/Services/BBPS/ApiUrls.tsx +++ b/src/Services/BBPS/ApiUrls.tsx @@ -69,9 +69,10 @@ 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=` -const SUBMIT_ENVELOPE_C = `${BASE_URL}eTendering/getEnvelopeCTemplate/tenderId?tenderId=` +const SUBMIT_ENVELOPE_FILE_A = `${BASE_URL}tenderBidding/addDocumentsEnvelopeA` +const SUBMIT_ENVELOPE_FILE_B = `https://procurelinc.in/EProcurementSB/tenderBidding/addDocumentsEnvelopeB` +const GET_ENVELOPE_C = `${BASE_URL}eTendering/getEnvelopeCTemplate/tenderId?tenderId=` +const SUBMIT_ENVELOPE_C_UPLOAD = `${BASE_URL}tenderBidding/getSaveEnvelopeCData` @@ -139,6 +140,7 @@ export { SUBMIT_ENVELOPE_B, SUBMIT_ENVELOPE_FILE_A, SUBMIT_ENVELOPE_FILE_B, - SUBMIT_ENVELOPE_C + GET_ENVELOPE_C, + SUBMIT_ENVELOPE_C_UPLOAD, }; diff --git a/src/Services/BBPS/Hooks.ts b/src/Services/BBPS/Hooks.ts index aea8494..cf2450a 100644 --- a/src/Services/BBPS/Hooks.ts +++ b/src/Services/BBPS/Hooks.ts @@ -104,13 +104,12 @@ export const useOfflineTenderPurchase = () => { }; export const useFetchTenderById = (tenderId: string) => { - return useQuery({ - queryKey: ['fetchTenderById', tenderId], - queryFn: () => fetchTenderById(tenderId) - - }); +return useQuery({ + queryKey: ['fetchTenderById', tenderId], + queryFn: () => fetchTenderById(tenderId), + enabled: !!tenderId, // prevents running with undefined id +}); } - export const useProceedToBid = (tenderId?: string) => { return useQuery({ queryKey: ['proceedToBid', tenderId],