update excel sheet open

This commit is contained in:
rohit-kumar-fullstack
2026-01-12 18:12:41 +05:30
parent 5391040b0b
commit ffc7e4ead0
5 changed files with 207 additions and 73 deletions
+15 -1
View File
@@ -49,6 +49,7 @@
"react-native-skeleton-placeholder": "^5.2.4",
"react-native-svg": "^15.12.0",
"react-native-svg-transformer": "^1.5.1",
"react-native-webview": "^13.16.0",
"react-redux": "^9.2.0",
"redux-persist": "^6.0.0",
"sockjs-client": "^1.6.1",
@@ -3937,7 +3938,6 @@
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.16.tgz",
"integrity": "sha512-bpMGOmV4OPmif7TNMteU/Ehf/hoC0Kf98PDc0F4BZkFrEapRMEqI/V6YS0lyzwSV6PQpY1y4xxArUIfBW5LVxQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@tanstack/query-core": "5.90.16"
},
@@ -12251,6 +12251,20 @@
"react-native-svg": ">=12.0.0"
}
},
"node_modules/react-native-webview": {
"version": "13.16.0",
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.16.0.tgz",
"integrity": "sha512-Nh13xKZWW35C0dbOskD7OX01nQQavOzHbCw9XoZmar4eXCo7AvrYJ0jlUfRVVIJzqINxHlpECYLdmAdFsl9xDA==",
"license": "MIT",
"dependencies": {
"escape-string-regexp": "^4.0.0",
"invariant": "2.2.4"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native/node_modules/@react-native/virtualized-lists": {
"version": "0.79.0",
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.79.0.tgz",
+1
View File
@@ -51,6 +51,7 @@
"react-native-skeleton-placeholder": "^5.2.4",
"react-native-svg": "^15.12.0",
"react-native-svg-transformer": "^1.5.1",
"react-native-webview": "^13.16.0",
"react-redux": "^9.2.0",
"redux-persist": "^6.0.0",
"sockjs-client": "^1.6.1",
+126 -70
View File
@@ -1,73 +1,114 @@
import React, { useEffect, useState } from 'react';
import {
View,
Text,
TextInput,
ScrollView,
TouchableOpacity,
StyleSheet,
ActivityIndicator,
Alert,
} from 'react-native';
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';
const EXCEL_API_URL = 'https://procurelinc.in/EProcurementSB/eTendering/getEnvelopeCTemplate/tenderId';
const EXCEL_API_URL = 'https://procurelinc.in/EProcurementSB/eTendering/getEnvelopeCTemplate/tenderId?tenderId=2479';
// Excel column keys
const QTY_KEY = '__EMPTY_1';
const RATE_KEY = '__EMPTY_2';
const VALUE_KEY = '__EMPTY_3';
const TOTAL_LABEL = 'TOTAL';
const EnvelopeCExcelPreview = () => {
const [data, setData] = useState<any[]>([]);
const [columns, setColumns] = useState<string[]>([]);
const [loading, setLoading] = useState(false);
const token = useSelector((state: any) => state.token.token);
useEffect(() => {
loadExcelFromApi();
}, []);
// 📥 FETCH EXCEL BLOB + READ
// 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 urlWithParams = `${EXCEL_API_URL}?tenderId=2479`;
// ⬇️ Fetch Excel as blob
const response = await axios.get(urlWithParams, {
const response = await axios.get(EXCEL_API_URL, {
responseType: 'arraybuffer',
headers: {
Authorization: `Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJBQklOVE9QUE85NkBHTUFJTC5DT00iLCJpYXQiOjE3Njc5MzgzMDMsImV4cCI6MTc2Nzk3NDMwM30.NiT5iOIdQH0y8TutLfLVp9ApQIAI3NHbkmRjb1fl-JlGxeyX21n3oPGvU6sDw_uU9iTZHI183e2FH_IL2sZYpQ`,
Authentication: `Bearer ${token.token}`,
},
});
console.log(response, '--------------------------------------');
// ⬇️ Save file locally
const filePath = `${RNFS.DocumentDirectoryPath}/envelopeC.xlsx`;
const buffer = Buffer.from(response.data, 'binary'); // convert arraybuffer to buffer
await RNFS.writeFile(filePath, buffer.toString('base64'), 'base64');
// 📖 Read Excel
const workbook = XLSX.readFile(filePath);
const base64 = arrayBufferToBase64(response.data);
const workbook = XLSX.read(base64, { type: 'base64' });
const sheet = workbook.Sheets[workbook.SheetNames[0]];
const jsonData: any[] = XLSX.utils.sheet_to_json(sheet, { defval: '' });
const rows: any[] = XLSX.utils.sheet_to_json(sheet, { defval: '' });
setData(jsonData);
setColumns(Object.keys(jsonData[0] || {}));
} catch (error: any) {
console.log('Excel load error:', error);
Alert.alert('Error', error.message || 'Unable to load Excel');
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
// UPDATE CELL (QTY / RATE) + AUTO VALUE + AUTO TOTAL
const updateCell = (rowIndex: number, key: string, value: string) => {
const updated = [...data];
updated[rowIndex][key] = value;
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 (LOCAL)
// SAVE UPDATED EXCEL
const saveUpdatedExcel = async () => {
try {
setLoading(true);
@@ -76,16 +117,20 @@ const EnvelopeCExcelPreview = () => {
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
const base64Excel = XLSX.write(workbook, { bookType: 'xlsx', type: 'base64' });
const updatedPath = `${RNFS.DocumentDirectoryPath}/envelopeC_updated.xlsx`;
const base64 = XLSX.write(workbook, {
bookType: 'xlsx',
type: 'base64',
});
await RNFS.writeFile(updatedPath, base64Excel, 'base64');
await RNFS.writeFile(
`${RNFS.DocumentDirectoryPath}/envelopeC_updated.xlsx`,
base64,
'base64'
);
Alert.alert('Success', 'Updated Excel saved successfully');
console.log('Saved at:', updatedPath);
} catch (error) {
console.log('Save error:', error);
Alert.alert('Error', 'Failed to save Excel');
Alert.alert('Success', 'Excel saved with auto-calculation');
} catch (e) {
Alert.alert('Error', 'Save failed');
} finally {
setLoading(false);
}
@@ -101,9 +146,9 @@ const EnvelopeCExcelPreview = () => {
return (
<View style={styles.container}>
<Text style={styles.title}>Envelope-C Excel Preview</Text>
<InsideHeader title='Envelope-C Excel Preview' />
<ScrollView horizontal>
<ScrollView horizontal contentContainerStyle={{ paddingHorizontal: 10, paddingTop: 20, backgroundColor: colors.white }}>
<View>
{/* HEADER */}
<View style={styles.row}>
@@ -118,14 +163,32 @@ const EnvelopeCExcelPreview = () => {
<ScrollView style={{ maxHeight: 420 }}>
{data.map((row, rowIndex) => (
<View key={rowIndex} style={styles.row}>
{columns.map(key => (
<TextInput
key={key}
value={String(row[key])}
style={styles.cell}
onChangeText={text => updateCell(rowIndex, key, text)}
/>
))}
{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>
@@ -142,24 +205,10 @@ const EnvelopeCExcelPreview = () => {
export default EnvelopeCExcelPreview;
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
backgroundColor: '#fff',
},
loader: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
title: {
fontSize: 18,
fontWeight: '700',
marginBottom: 10,
},
row: {
flexDirection: 'row',
},
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,
@@ -173,6 +222,13 @@ const styles = StyleSheet.create({
borderWidth: 1,
borderColor: '#cbd5e1',
},
valueCell: {
backgroundColor: '#f8fafc',
},
totalCell: {
backgroundColor: '#e5e7eb',
fontWeight: '700',
},
button: {
marginTop: 15,
backgroundColor: '#2563eb',
+63
View File
@@ -0,0 +1,63 @@
export const spreadHtml = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link
href="https://cdn.grapecity.com/spreadjs/15.2.2/css/gc.spread.sheets.excel2013white.css"
rel="stylesheet"
/>
<script src="https://cdn.grapecity.com/spreadjs/15.2.2/gc.spread.sheets.all.min.js"></script>
<script src="https://cdn.grapecity.com/spreadjs/15.2.2/plugins/gc.spread.sheets.charts.min.js"></script>
<style>
html, body {
margin: 0;
height: 100%;
width: 100%;
overflow: hidden;
}
#spread {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="spread"></div>
<script>
let spread;
document.addEventListener('message', function (e) {
const base64 = e.data;
spread = new GC.Spread.Sheets.Workbook(
document.getElementById('spread'),
{ sheetCount: 1 }
);
GC.Spread.Excel.IO.open(base64, spread, function () {
console.log('Excel loaded');
});
});
function exportExcel() {
const excelIO = new GC.Spread.Excel.IO();
excelIO.save(spread, function (blob) {
const reader = new FileReader();
reader.onload = function () {
const base64 = reader.result.split(',')[1];
window.ReactNativeWebView.postMessage(base64);
};
reader.readAsDataURL(blob);
});
}
</script>
</body>
</html>
`;
+2 -2
View File
@@ -6,8 +6,8 @@ import { apiCall } from "../../Axios/Axios";
export const userLogin = async (payload: { emailAdd: string, password: string }): Promise<any> => {
try {
const response = await axios.post(LOG_IN_URL, { ...payload, divice: "mobileApp" });
// const response = await axios.post(TENDER_LOGIN, { ...payload, device: "mobileApp" });
// const response = await axios.post(LOG_IN_URL, { ...payload, divice: "mobileApp" });
const response = await axios.post(TENDER_LOGIN, { ...payload, device: "mobileApp" });
return response.data;
} catch (error: any) {
throw error.response?.data || error.message;