From d4ec6c89a7beb3309fccc0d7749115164c90a8e3 Mon Sep 17 00:00:00 2001 From: Ajeett01 Date: Wed, 19 Jun 2024 17:01:43 +0530 Subject: [PATCH 1/2] Added CSV export --- babel.config.js | 3 +- package.json | 2 + views/Activity/Activity.tsx | 61 ++++++++++-- views/Activity/JsonToCsv.tsx | 181 +++++++++++++++++++++++++++++++++++ yarn.lock | 31 ++++++ 5 files changed, 268 insertions(+), 10 deletions(-) create mode 100644 views/Activity/JsonToCsv.tsx diff --git a/babel.config.js b/babel.config.js index 8d75b6b1c..1368491b1 100644 --- a/babel.config.js +++ b/babel.config.js @@ -2,6 +2,7 @@ module.exports = { presets: ["module:@react-native/babel-preset"], plugins: [ ["@babel/plugin-proposal-decorators", { legacy: true }], - 'react-native-reanimated/plugin' + 'react-native-reanimated/plugin', + ['@babel/plugin-transform-export-namespace-from'] ] } \ No newline at end of file diff --git a/package.json b/package.json index 3c7f71c7a..ad26904cb 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "fetch-libraries": "bash fetch-libraries.sh" }, "dependencies": { + "@json2csv/plainjs": "^7.0.6", "@keystonehq/bc-ur-registry": "0.4.2", "@lightninglabs/lnc-core": "file:zeus_modules/@lightninglabs/lnc-core", "@ngraveio/bc-ur": "1.1.12", @@ -156,6 +157,7 @@ "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6", "@babel/plugin-proposal-optional-chaining": "7.21.0", "@babel/plugin-transform-arrow-functions": "7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.24.7", "@babel/plugin-transform-shorthand-properties": "7.22.5", "@babel/plugin-transform-template-literals": "7.22.5", "@babel/preset-env": "7.22.9", diff --git a/views/Activity/Activity.tsx b/views/Activity/Activity.tsx index 61f8d9c05..97854e0fa 100644 --- a/views/Activity/Activity.tsx +++ b/views/Activity/Activity.tsx @@ -31,6 +31,8 @@ import { SATS_PER_BTC } from '../../stores/UnitsStore'; import Filter from '../../assets/images/SVG/Filter On.svg'; import Invoice from '../../models/Invoice'; +import JsonToCsv from './JsonToCsv'; +import { Alert } from 'react-native'; interface ActivityProps { navigation: StackNavigationProp; @@ -55,7 +57,8 @@ export default class Activity extends React.PureComponent< invoicesListener: any; state = { - selectedPaymentForOrder: null + selectedPaymentForOrder: null, + isCsvModalVisible: false, }; async UNSAFE_componentWillMount() { @@ -132,6 +135,21 @@ export default class Activity extends React.PureComponent< return 'secondaryText'; }; + handleDownloadPress = () => { + const { ActivityStore } = this.props; + const { startDate, endDate } = ActivityStore.filters; + const { filteredActivity } = ActivityStore; + + if (!startDate || !endDate) { + Alert.alert( + 'Error', + 'Please select Start and End date.', + ); + } else { + this.setState({ isCsvModalVisible: true, filteredData: filteredActivity }); + } + }; + render() { const { navigation, @@ -141,7 +159,7 @@ export default class Activity extends React.PureComponent< SettingsStore, route } = this.props; - const { selectedPaymentForOrder } = this.state; + const { selectedPaymentForOrder , isCsvModalVisible} = this.state; const { loading, filteredActivity, getActivityAndFilter } = ActivityStore; @@ -225,6 +243,20 @@ export default class Activity extends React.PureComponent< ); + const DownloadButton = () => { + return ( + + ); + }; + return (
- ) : null - ) : ( - - ) + + {order ? ( + selectedPaymentForOrder ? ( + + ) : null + ) : ( + + )} + + } navigation={navigation} /> + + this.setState({ isCsvModalVisible: false })} + isVisible={isCsvModalVisible} + /> + + {loading ? ( diff --git a/views/Activity/JsonToCsv.tsx b/views/Activity/JsonToCsv.tsx new file mode 100644 index 000000000..9f77a8a48 --- /dev/null +++ b/views/Activity/JsonToCsv.tsx @@ -0,0 +1,181 @@ +import React, { useState } from 'react'; +import { + StyleSheet, + Text, + View, + PermissionsAndroid, + Platform, + Alert, + Modal, + TextInput, + TouchableOpacity, +} from 'react-native'; +import RNFS from 'react-native-fs'; +import { Parser } from '@json2csv/plainjs'; + +const JsonToCsv = ({ filteredActivity, isVisible, closeModal }) => { + const [csvData, setCsvData] = useState(''); + const [customFileName, setCustomFileName] = useState(''); + + const getFormattedDateTime =()=> { + const now = new Date(); + const year = now.getFullYear(); + const month = (now.getMonth() + 1).toString().padStart(2, '0'); // Months are zero-based + const day = now.getDate().toString().padStart(2, '0'); + const hours = now.getHours().toString().padStart(2, '0'); + const minutes = now.getMinutes().toString().padStart(2, '0'); + const seconds = now.getSeconds().toString().padStart(2, '0'); + + return `${year}${month}${day}_${hours}${minutes}${seconds}`; +} + + const filterData = (data, keys) => { + return data.map(item => { + let filteredItem = {}; + keys.forEach(key => { + if (item.hasOwnProperty(key)) { + filteredItem[key] = item[key]; + } + }); + return filteredItem; + }); + }; + + const convertJsonToCsv = async (data) => { + if (!data || data.length === 0) { + console.log('Filtered data is empty.'); + return ''; + } + + const keysToInclude = [ + 'amt_paid', + 'amt_paid_sat', + 'cltv_expiry', + 'creation_date', + 'expiry' + ]; + + const filteredData = filterData(data, keysToInclude); + + try { + const parser = new Parser(); + const csv = parser.parse(filteredData); + setCsvData(csv); + // console.log(csv); + return csv; + } catch (err) { + console.error(err); + return ''; + } + }; + + const downloadCsv = async () => { + const csv = await convertJsonToCsv(filteredActivity); + if (!csv) return; + + try { + if (Platform.OS === 'android') { + const granted = await PermissionsAndroid.request( + PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, + { + title: 'Storage Permission Required', + message: 'This app needs access to your storage to save CSV files', + buttonNeutral: 'Ask Me Later', + buttonNegative: 'Cancel', + buttonPositive: 'OK', + }, + ); + if (granted !== PermissionsAndroid.RESULTS.GRANTED) { + console.error('Storage permission denied'); + return; + } + } + + const dateTime = getFormattedDateTime(); + + const fileName = customFileName ? `${customFileName}.csv` : `data_${dateTime}.csv`; + const filePath = `${RNFS.DownloadDirectoryPath}/${fileName}`; + + await RNFS.writeFile(filePath, csv, 'utf8'); + Alert.alert('Success', 'CSV file has been downloaded'); + closeModal(); + + } catch (err) { + if (DocumentPicker.isCancel(err)) { + console.log('User canceled the picker'); + } else { + console.error('Failed to save CSV file:', err); + } + } + }; + + return ( + + + + + + + Download CSV + + + Close + + + + + + ); +}; + +const styles = StyleSheet.create({ + modalOverlay: { + flex: 1, + backgroundColor: 'rgba(0, 0, 0, 0.5)', + justifyContent: 'center', + alignItems: 'center', + }, + modalView: { + width: '80%', + backgroundColor: 'white', + padding: 20, + borderRadius: 10, + alignItems: 'center', + }, + textInput: { + height: 40, + borderColor: 'gray', + borderWidth: 1, + marginBottom: 20, + width: '100%', + paddingHorizontal: 10, + }, + buttonView:{ + flexDirection: 'row', + justifyContent: 'space-between', + }, + buttonStyle: { + backgroundColor: '#007BFF', + padding: 8, + borderRadius: 5, + alignItems: 'center', + flex: 1, + margin:5, + }, + buttonText: { + color: 'white', + fontSize: 16, + }, +}); + +export default JsonToCsv; diff --git a/yarn.lock b/yarn.lock index efa4642a0..96b811500 100644 --- a/yarn.lock +++ b/yarn.lock @@ -230,6 +230,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz#a924607dd254a65695e5bd209b98b902b3b2f11a" integrity sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ== +"@babel/helper-plugin-utils@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz#98c84fe6fe3d0d3ae7bfc3a5e166a46844feb2a0" + integrity sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg== + "@babel/helper-remap-async-to-generator@^7.18.9", "@babel/helper-remap-async-to-generator@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" @@ -734,6 +739,14 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" +"@babel/plugin-transform-export-namespace-from@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz#176d52d8d8ed516aeae7013ee9556d540c53f197" + integrity sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-transform-flow-strip-types@^7.20.0", "@babel/plugin-transform-flow-strip-types@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.1.tgz#fa8d0a146506ea195da1671d38eed459242b2dcc" @@ -1679,6 +1692,19 @@ dependencies: lodash "^4.17.21" +"@json2csv/formatters@^7.0.6": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@json2csv/formatters/-/formatters-7.0.6.tgz#a4f21daa9d64d6da9a297f7d7b97ba517870f780" + integrity sha512-hjIk1H1TR4ydU5ntIENEPgoMGW+Q7mJ+537sDFDbsk+Y3EPl2i4NfFVjw0NJRgT+ihm8X30M67mA8AS6jPidSA== + +"@json2csv/plainjs@^7.0.6": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@json2csv/plainjs/-/plainjs-7.0.6.tgz#e4149b26ec6f1c59a27c72534a91e971be397098" + integrity sha512-4Md7RPDCSYpmW1HWIpWBOqCd4vWfIqm53S3e/uzQ62iGi7L3r34fK/8nhOMEe+/eVfCx8+gdSCt1d74SlacQHw== + dependencies: + "@json2csv/formatters" "^7.0.6" + "@streamparser/json" "^0.0.20" + "@keystonehq/alias-sampling@^0.1.1": version "0.1.2" resolved "https://registry.yarnpkg.com/@keystonehq/alias-sampling/-/alias-sampling-0.1.2.tgz#63af931ffe6500aef4c0d87775a5b279189abf8d" @@ -2646,6 +2672,11 @@ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== +"@streamparser/json@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@streamparser/json/-/json-0.0.20.tgz#28c0255dea8d37f5d679d72c361f01edf70a770a" + integrity sha512-VqAAkydywPpkw63WQhPVKCD3SdwXuihCUVZbbiY3SfSTGQyHmwRoq27y4dmJdZuJwd5JIlQoMPyGvMbUPY0RKQ== + "@svgr/babel-plugin-add-jsx-attribute@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz#4001f5d5dd87fa13303e36ee106e3ff3a7eb8b22" From d1bc6d860b105e4d77beaefa2e24345b5d9fcf18 Mon Sep 17 00:00:00 2001 From: Ajeett01 Date: Wed, 19 Jun 2024 17:21:08 +0530 Subject: [PATCH 2/2] Prettier done --- views/Activity/Activity.tsx | 35 ++-- views/Activity/JsonToCsv.tsx | 338 ++++++++++++++++++----------------- 2 files changed, 191 insertions(+), 182 deletions(-) diff --git a/views/Activity/Activity.tsx b/views/Activity/Activity.tsx index 97854e0fa..dd4a93a79 100644 --- a/views/Activity/Activity.tsx +++ b/views/Activity/Activity.tsx @@ -58,7 +58,7 @@ export default class Activity extends React.PureComponent< state = { selectedPaymentForOrder: null, - isCsvModalVisible: false, + isCsvModalVisible: false }; async UNSAFE_componentWillMount() { @@ -139,16 +139,16 @@ export default class Activity extends React.PureComponent< const { ActivityStore } = this.props; const { startDate, endDate } = ActivityStore.filters; const { filteredActivity } = ActivityStore; - + if (!startDate || !endDate) { - Alert.alert( - 'Error', - 'Please select Start and End date.', - ); + Alert.alert('Error', 'Please select Start and End date.'); } else { - this.setState({ isCsvModalVisible: true, filteredData: filteredActivity }); + this.setState({ + isCsvModalVisible: true, + filteredData: filteredActivity + }); } - }; + }; render() { const { @@ -159,7 +159,7 @@ export default class Activity extends React.PureComponent< SettingsStore, route } = this.props; - const { selectedPaymentForOrder , isCsvModalVisible} = this.state; + const { selectedPaymentForOrder, isCsvModalVisible } = this.state; const { loading, filteredActivity, getActivityAndFilter } = ActivityStore; @@ -274,8 +274,8 @@ export default class Activity extends React.PureComponent< selectedPaymentForOrder ? ( ) : null - ) : ( - + ) : ( + )} @@ -283,12 +283,13 @@ export default class Activity extends React.PureComponent< navigation={navigation} /> - this.setState({ isCsvModalVisible: false })} - isVisible={isCsvModalVisible} - /> - + + this.setState({ isCsvModalVisible: false }) + } + isVisible={isCsvModalVisible} + /> {loading ? ( diff --git a/views/Activity/JsonToCsv.tsx b/views/Activity/JsonToCsv.tsx index 9f77a8a48..77300e21f 100644 --- a/views/Activity/JsonToCsv.tsx +++ b/views/Activity/JsonToCsv.tsx @@ -1,181 +1,189 @@ import React, { useState } from 'react'; import { - StyleSheet, - Text, - View, - PermissionsAndroid, - Platform, - Alert, - Modal, - TextInput, - TouchableOpacity, + StyleSheet, + Text, + View, + PermissionsAndroid, + Platform, + Alert, + Modal, + TextInput, + TouchableOpacity } from 'react-native'; import RNFS from 'react-native-fs'; import { Parser } from '@json2csv/plainjs'; const JsonToCsv = ({ filteredActivity, isVisible, closeModal }) => { - const [csvData, setCsvData] = useState(''); - const [customFileName, setCustomFileName] = useState(''); - - const getFormattedDateTime =()=> { - const now = new Date(); - const year = now.getFullYear(); - const month = (now.getMonth() + 1).toString().padStart(2, '0'); // Months are zero-based - const day = now.getDate().toString().padStart(2, '0'); - const hours = now.getHours().toString().padStart(2, '0'); - const minutes = now.getMinutes().toString().padStart(2, '0'); - const seconds = now.getSeconds().toString().padStart(2, '0'); - - return `${year}${month}${day}_${hours}${minutes}${seconds}`; -} - - const filterData = (data, keys) => { - return data.map(item => { - let filteredItem = {}; - keys.forEach(key => { - if (item.hasOwnProperty(key)) { - filteredItem[key] = item[key]; + const [csvData, setCsvData] = useState(''); + const [customFileName, setCustomFileName] = useState(''); + + const getFormattedDateTime = () => { + const now = new Date(); + const year = now.getFullYear(); + const month = (now.getMonth() + 1).toString().padStart(2, '0'); // Months are zero-based + const day = now.getDate().toString().padStart(2, '0'); + const hours = now.getHours().toString().padStart(2, '0'); + const minutes = now.getMinutes().toString().padStart(2, '0'); + const seconds = now.getSeconds().toString().padStart(2, '0'); + + return `${year}${month}${day}_${hours}${minutes}${seconds}`; + }; + + const filterData = (data, keys) => { + return data.map((item) => { + let filteredItem = {}; + keys.forEach((key) => { + if (item.hasOwnProperty(key)) { + filteredItem[key] = item[key]; + } + }); + return filteredItem; + }); + }; + + const convertJsonToCsv = async (data) => { + if (!data || data.length === 0) { + console.log('Filtered data is empty.'); + return ''; } - }); - return filteredItem; - }); - }; - - const convertJsonToCsv = async (data) => { - if (!data || data.length === 0) { - console.log('Filtered data is empty.'); - return ''; - } - const keysToInclude = [ - 'amt_paid', - 'amt_paid_sat', - 'cltv_expiry', - 'creation_date', - 'expiry' - ]; - - const filteredData = filterData(data, keysToInclude); - - try { - const parser = new Parser(); - const csv = parser.parse(filteredData); - setCsvData(csv); - // console.log(csv); - return csv; - } catch (err) { - console.error(err); - return ''; - } - }; - - const downloadCsv = async () => { - const csv = await convertJsonToCsv(filteredActivity); - if (!csv) return; - - try { - if (Platform.OS === 'android') { - const granted = await PermissionsAndroid.request( - PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, - { - title: 'Storage Permission Required', - message: 'This app needs access to your storage to save CSV files', - buttonNeutral: 'Ask Me Later', - buttonNegative: 'Cancel', - buttonPositive: 'OK', - }, - ); - if (granted !== PermissionsAndroid.RESULTS.GRANTED) { - console.error('Storage permission denied'); - return; + const keysToInclude = [ + 'amt_paid', + 'amt_paid_sat', + 'cltv_expiry', + 'creation_date', + 'expiry' + ]; + + const filteredData = filterData(data, keysToInclude); + + try { + const parser = new Parser(); + const csv = parser.parse(filteredData); + setCsvData(csv); + // console.log(csv); + return csv; + } catch (err) { + console.error(err); + return ''; } - } - - const dateTime = getFormattedDateTime(); - - const fileName = customFileName ? `${customFileName}.csv` : `data_${dateTime}.csv`; - const filePath = `${RNFS.DownloadDirectoryPath}/${fileName}`; - - await RNFS.writeFile(filePath, csv, 'utf8'); - Alert.alert('Success', 'CSV file has been downloaded'); - closeModal(); - - } catch (err) { - if (DocumentPicker.isCancel(err)) { - console.log('User canceled the picker'); - } else { - console.error('Failed to save CSV file:', err); - } - } - }; - - return ( - - - - - - - Download CSV - - - Close - - - - - - ); + }; + + const downloadCsv = async () => { + const csv = await convertJsonToCsv(filteredActivity); + if (!csv) return; + + try { + if (Platform.OS === 'android') { + const granted = await PermissionsAndroid.request( + PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, + { + title: 'Storage Permission Required', + message: + 'This app needs access to your storage to save CSV files', + buttonNeutral: 'Ask Me Later', + buttonNegative: 'Cancel', + buttonPositive: 'OK' + } + ); + if (granted !== PermissionsAndroid.RESULTS.GRANTED) { + console.error('Storage permission denied'); + return; + } + } + + const dateTime = getFormattedDateTime(); + + const fileName = customFileName + ? `${customFileName}.csv` + : `data_${dateTime}.csv`; + const filePath = `${RNFS.DownloadDirectoryPath}/${fileName}`; + + await RNFS.writeFile(filePath, csv, 'utf8'); + Alert.alert('Success', 'CSV file has been downloaded'); + closeModal(); + } catch (err) { + if (DocumentPicker.isCancel(err)) { + console.log('User canceled the picker'); + } else { + console.error('Failed to save CSV file:', err); + } + } + }; + + return ( + + + + + + + Download CSV + + + Close + + + + + + ); }; const styles = StyleSheet.create({ - modalOverlay: { - flex: 1, - backgroundColor: 'rgba(0, 0, 0, 0.5)', - justifyContent: 'center', - alignItems: 'center', - }, - modalView: { - width: '80%', - backgroundColor: 'white', - padding: 20, - borderRadius: 10, - alignItems: 'center', - }, - textInput: { - height: 40, - borderColor: 'gray', - borderWidth: 1, - marginBottom: 20, - width: '100%', - paddingHorizontal: 10, - }, - buttonView:{ - flexDirection: 'row', - justifyContent: 'space-between', - }, - buttonStyle: { - backgroundColor: '#007BFF', - padding: 8, - borderRadius: 5, - alignItems: 'center', - flex: 1, - margin:5, - }, - buttonText: { - color: 'white', - fontSize: 16, - }, + modalOverlay: { + flex: 1, + backgroundColor: 'rgba(0, 0, 0, 0.5)', + justifyContent: 'center', + alignItems: 'center' + }, + modalView: { + width: '80%', + backgroundColor: 'white', + padding: 20, + borderRadius: 10, + alignItems: 'center' + }, + textInput: { + height: 40, + borderColor: 'gray', + borderWidth: 1, + marginBottom: 20, + width: '100%', + paddingHorizontal: 10 + }, + buttonView: { + flexDirection: 'row', + justifyContent: 'space-between' + }, + buttonStyle: { + backgroundColor: '#007BFF', + padding: 8, + borderRadius: 5, + alignItems: 'center', + flex: 1, + margin: 5 + }, + buttonText: { + color: 'white', + fontSize: 16 + } }); export default JsonToCsv;