envelop c

This commit is contained in:
Jaanu423
2026-01-12 23:08:53 +05:30
parent f4433d8e92
commit 39924066bc
7 changed files with 425 additions and 9 deletions
@@ -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<any>()
// Format date from array
@@ -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<any[]>([]);
const [columns, setColumns] = useState<string[]>([]);
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 (
<View style={styles.loader}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<View style={styles.container}>
<ScrollView horizontal contentContainerStyle={{ paddingHorizontal: 10, paddingTop: 20, backgroundColor: colors.white }}>
<View>
{/* HEADER */}
<View style={styles.row}>
{columns.map(col => (
<Text key={col} style={styles.headerCell}>
{col}
</Text>
))}
</View>
{/* DATA */}
<ScrollView style={{ maxHeight: 420 }}>
{data.map((row, rowIndex) => (
<View key={rowIndex} style={styles.row}>
{columns.map(key => {
const isTotal = row['DEMO DEPARTEMENT'] === TOTAL_LABEL;
const isEditable =
!isTotal && (key === QTY_KEY || key === RATE_KEY);
return (
<TextInput
key={key}
value={String(row[key])}
editable={isEditable}
keyboardType={
key === QTY_KEY || key === RATE_KEY
? 'numeric'
: 'default'
}
style={[
styles.cell,
isTotal && styles.totalCell,
key === VALUE_KEY && styles.valueCell,
]}
onChangeText={text =>
updateCell(rowIndex, key, text)
}
/>
);
})}
</View>
))}
</ScrollView>
</View>
</ScrollView>
<TouchableOpacity style={styles.button} onPress={saveUpdatedExcel}>
<Text style={styles.buttonText}>Save Updated Excel</Text>
</TouchableOpacity>
</View>
);
};
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',
},
});
@@ -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"
/>
)}
<Text>this is envelope {activeEnvelope}</Text>
</>
) : <Text>Envelop {activeEnvelope} unavailable</Text>}
</View>
);
};
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 = () => {
</View>
</View>
{/* Envelope A List */}
{tenderData?.[`tenderEnvelope${activeEnvelope}List`]?.length > 0 && activeEnvelope !== 'C' && (
{tenderData?.[`tenderEnvelope${activeEnvelope}List`]?.length > 0 && (
<View style={styles.card}>
<Text style={styles.sectionTitle}>Envelope {activeEnvelope} Details</Text>
<FlatList
@@ -491,6 +497,7 @@ const TenderBidding = () => {
renderItem={({ item }) => renderEnvelopeItem({ item, envelopeType: activeEnvelope })}
scrollEnabled={false}
/>
<View style={[styles.infoRow, { flexDirection: 'row', gap: 10 }]}>
<TouchableOpacity onPress={() => setCheck(!check)}>
{check ? <Fontisto name="checkbox-active" size={24} color={colors.primary} /> :
@@ -501,6 +508,31 @@ const TenderBidding = () => {
</View>
)}
{activeEnvelope === 'C' && (
<View style={styles.card}>
<Text style={styles.sectionTitle}>Envelope C Status</Text>
{tenderData?.envelopeCSubmittStatus ? (
<>
<Text style={{ color: 'green', marginBottom: 8 }}>
Envelope C has been submitted successfully.
</Text>
{tenderData?.envelopeC_Logs && (
<>
<Text>Date: {tenderData.envelopeC_Logs.dateTime}</Text>
<Text>Message: {tenderData.envelopeC_Logs.logMessage}</Text>
</>
)}
</>
) : (
<>
<EditableExcel tenderId={tenderData?.tenderId} />
</>
)}
</View>
)}
<View style={styles.footer}>
{hasEnvelopes() ? (
<TouchableOpacity
@@ -511,9 +543,13 @@ const TenderBidding = () => {
}
if (activeEnvelope === 'A') {
handleSubmitEnvelopeA()
} else {
}
else if (activeEnvelope === 'B') {
handleSubmitEnvelopeB()
}
else {
handleSubmitEnvelopeC()
}
}}
>
<Text style={styles.primaryButtonText}>{loading ? 'Submitting...' : `Submit Bid (${activeEnvelope})`}</Text>
+3 -1
View File
@@ -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
};
-2
View File
@@ -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);