update excel sheet open
This commit is contained in:
Generated
+15
-1
@@ -49,6 +49,7 @@
|
|||||||
"react-native-skeleton-placeholder": "^5.2.4",
|
"react-native-skeleton-placeholder": "^5.2.4",
|
||||||
"react-native-svg": "^15.12.0",
|
"react-native-svg": "^15.12.0",
|
||||||
"react-native-svg-transformer": "^1.5.1",
|
"react-native-svg-transformer": "^1.5.1",
|
||||||
|
"react-native-webview": "^13.16.0",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
"sockjs-client": "^1.6.1",
|
"sockjs-client": "^1.6.1",
|
||||||
@@ -3937,7 +3938,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.16.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.16.tgz",
|
||||||
"integrity": "sha512-bpMGOmV4OPmif7TNMteU/Ehf/hoC0Kf98PDc0F4BZkFrEapRMEqI/V6YS0lyzwSV6PQpY1y4xxArUIfBW5LVxQ==",
|
"integrity": "sha512-bpMGOmV4OPmif7TNMteU/Ehf/hoC0Kf98PDc0F4BZkFrEapRMEqI/V6YS0lyzwSV6PQpY1y4xxArUIfBW5LVxQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/query-core": "5.90.16"
|
"@tanstack/query-core": "5.90.16"
|
||||||
},
|
},
|
||||||
@@ -12251,6 +12251,20 @@
|
|||||||
"react-native-svg": ">=12.0.0"
|
"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": {
|
"node_modules/react-native/node_modules/@react-native/virtualized-lists": {
|
||||||
"version": "0.79.0",
|
"version": "0.79.0",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.79.0.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.79.0.tgz",
|
||||||
|
|||||||
@@ -51,6 +51,7 @@
|
|||||||
"react-native-skeleton-placeholder": "^5.2.4",
|
"react-native-skeleton-placeholder": "^5.2.4",
|
||||||
"react-native-svg": "^15.12.0",
|
"react-native-svg": "^15.12.0",
|
||||||
"react-native-svg-transformer": "^1.5.1",
|
"react-native-svg-transformer": "^1.5.1",
|
||||||
|
"react-native-webview": "^13.16.0",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
"sockjs-client": "^1.6.1",
|
"sockjs-client": "^1.6.1",
|
||||||
|
|||||||
@@ -1,73 +1,114 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import {
|
import { View, Text, TextInput, ScrollView, TouchableOpacity, StyleSheet, ActivityIndicator, Alert, } from 'react-native';
|
||||||
View,
|
|
||||||
Text,
|
|
||||||
TextInput,
|
|
||||||
ScrollView,
|
|
||||||
TouchableOpacity,
|
|
||||||
StyleSheet,
|
|
||||||
ActivityIndicator,
|
|
||||||
Alert,
|
|
||||||
} from 'react-native';
|
|
||||||
import RNFS from 'react-native-fs';
|
import RNFS from 'react-native-fs';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
import axios from 'axios';
|
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 EnvelopeCExcelPreview = () => {
|
||||||
const [data, setData] = useState<any[]>([]);
|
const [data, setData] = useState<any[]>([]);
|
||||||
const [columns, setColumns] = useState<string[]>([]);
|
const [columns, setColumns] = useState<string[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const token = useSelector((state: any) => state.token.token);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadExcelFromApi();
|
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 () => {
|
const loadExcelFromApi = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const urlWithParams = `${EXCEL_API_URL}?tenderId=2479`;
|
const response = await axios.get(EXCEL_API_URL, {
|
||||||
|
|
||||||
// ⬇️ Fetch Excel as blob
|
|
||||||
const response = await axios.get(urlWithParams, {
|
|
||||||
responseType: 'arraybuffer',
|
responseType: 'arraybuffer',
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJBQklOVE9QUE85NkBHTUFJTC5DT00iLCJpYXQiOjE3Njc5MzgzMDMsImV4cCI6MTc2Nzk3NDMwM30.NiT5iOIdQH0y8TutLfLVp9ApQIAI3NHbkmRjb1fl-JlGxeyX21n3oPGvU6sDw_uU9iTZHI183e2FH_IL2sZYpQ`,
|
Authentication: `Bearer ${token.token}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
console.log(response, '--------------------------------------');
|
|
||||||
|
|
||||||
// ⬇️ Save file locally
|
const base64 = arrayBufferToBase64(response.data);
|
||||||
const filePath = `${RNFS.DocumentDirectoryPath}/envelopeC.xlsx`;
|
const workbook = XLSX.read(base64, { type: 'base64' });
|
||||||
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 sheet = workbook.Sheets[workbook.SheetNames[0]];
|
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(rows[0] || {}));
|
||||||
setColumns(Object.keys(jsonData[0] || {}));
|
setData(rows);
|
||||||
} catch (error: any) {
|
|
||||||
console.log('Excel load error:', error);
|
await RNFS.writeFile(
|
||||||
Alert.alert('Error', error.message || 'Unable to load Excel');
|
`${RNFS.DocumentDirectoryPath}/envelopeC.xlsx`,
|
||||||
|
base64,
|
||||||
|
'base64'
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
Alert.alert('Error', 'Failed to load Excel');
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ✏️ UPDATE CELL
|
// UPDATE CELL (QTY / RATE) + AUTO VALUE + AUTO TOTAL
|
||||||
const updateCell = (rowIndex: number, key: string, value: string) => {
|
const updateCell = (rowIndex: number, key: string, value: string) => {
|
||||||
const updated = [...data];
|
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);
|
setData(updated);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 💾 SAVE UPDATED EXCEL (LOCAL)
|
// SAVE UPDATED EXCEL
|
||||||
const saveUpdatedExcel = async () => {
|
const saveUpdatedExcel = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -76,16 +117,20 @@ const EnvelopeCExcelPreview = () => {
|
|||||||
const workbook = XLSX.utils.book_new();
|
const workbook = XLSX.utils.book_new();
|
||||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
||||||
|
|
||||||
const base64Excel = XLSX.write(workbook, { bookType: 'xlsx', type: 'base64' });
|
const base64 = XLSX.write(workbook, {
|
||||||
const updatedPath = `${RNFS.DocumentDirectoryPath}/envelopeC_updated.xlsx`;
|
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');
|
Alert.alert('Success', 'Excel saved with auto-calculation');
|
||||||
console.log('Saved at:', updatedPath);
|
} catch (e) {
|
||||||
} catch (error) {
|
Alert.alert('Error', 'Save failed');
|
||||||
console.log('Save error:', error);
|
|
||||||
Alert.alert('Error', 'Failed to save Excel');
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -101,9 +146,9 @@ const EnvelopeCExcelPreview = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<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>
|
<View>
|
||||||
{/* HEADER */}
|
{/* HEADER */}
|
||||||
<View style={styles.row}>
|
<View style={styles.row}>
|
||||||
@@ -118,14 +163,32 @@ const EnvelopeCExcelPreview = () => {
|
|||||||
<ScrollView style={{ maxHeight: 420 }}>
|
<ScrollView style={{ maxHeight: 420 }}>
|
||||||
{data.map((row, rowIndex) => (
|
{data.map((row, rowIndex) => (
|
||||||
<View key={rowIndex} style={styles.row}>
|
<View key={rowIndex} style={styles.row}>
|
||||||
{columns.map(key => (
|
{columns.map(key => {
|
||||||
<TextInput
|
const isTotal = row['DEMO DEPARTEMENT'] === TOTAL_LABEL;
|
||||||
key={key}
|
const isEditable =
|
||||||
value={String(row[key])}
|
!isTotal && (key === QTY_KEY || key === RATE_KEY);
|
||||||
style={styles.cell}
|
|
||||||
onChangeText={text => updateCell(rowIndex, key, text)}
|
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>
|
</View>
|
||||||
))}
|
))}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
@@ -142,24 +205,10 @@ const EnvelopeCExcelPreview = () => {
|
|||||||
export default EnvelopeCExcelPreview;
|
export default EnvelopeCExcelPreview;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: { flex: 1, backgroundColor: '#fff' },
|
||||||
flex: 1,
|
loader: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
||||||
padding: 16,
|
title: { fontSize: 18, fontWeight: '700', marginBottom: 10 },
|
||||||
backgroundColor: '#fff',
|
row: { flexDirection: 'row' },
|
||||||
},
|
|
||||||
loader: {
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: '700',
|
|
||||||
marginBottom: 10,
|
|
||||||
},
|
|
||||||
row: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
},
|
|
||||||
headerCell: {
|
headerCell: {
|
||||||
minWidth: 140,
|
minWidth: 140,
|
||||||
padding: 10,
|
padding: 10,
|
||||||
@@ -173,6 +222,13 @@ const styles = StyleSheet.create({
|
|||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: '#cbd5e1',
|
borderColor: '#cbd5e1',
|
||||||
},
|
},
|
||||||
|
valueCell: {
|
||||||
|
backgroundColor: '#f8fafc',
|
||||||
|
},
|
||||||
|
totalCell: {
|
||||||
|
backgroundColor: '#e5e7eb',
|
||||||
|
fontWeight: '700',
|
||||||
|
},
|
||||||
button: {
|
button: {
|
||||||
marginTop: 15,
|
marginTop: 15,
|
||||||
backgroundColor: '#2563eb',
|
backgroundColor: '#2563eb',
|
||||||
|
|||||||
@@ -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>
|
||||||
|
`;
|
||||||
@@ -6,8 +6,8 @@ import { apiCall } from "../../Axios/Axios";
|
|||||||
|
|
||||||
export const userLogin = async (payload: { emailAdd: string, password: string }): Promise<any> => {
|
export const userLogin = async (payload: { emailAdd: string, password: string }): Promise<any> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(LOG_IN_URL, { ...payload, divice: "mobileApp" });
|
// 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(TENDER_LOGIN, { ...payload, device: "mobileApp" });
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
throw error.response?.data || error.message;
|
throw error.response?.data || error.message;
|
||||||
|
|||||||
Reference in New Issue
Block a user