Added code to export data to CSV file
This commit is contained in:
parent
1b00fda626
commit
40e7c2875c
|
@ -8,6 +8,9 @@ import Loading from '../Loading'
|
|||
import Pagination from '../Pagination'
|
||||
import usePagination from '../../hooks/usePagination'
|
||||
import { roundToDecimal } from '../../utils'
|
||||
import { exportDataToCSV } from '../../utils/export'
|
||||
import Button from '../Button'
|
||||
import { SaveIcon } from '@heroicons/react/solid'
|
||||
|
||||
const QUOTE_DECIMALS = 6
|
||||
|
||||
|
@ -33,6 +36,30 @@ const AccountFunding = () => {
|
|||
return mangoAccount.publicKey.toString()
|
||||
}, [mangoAccount])
|
||||
|
||||
const exportFundingDataToCSV = () => {
|
||||
const assets = Object.keys(hourlyFunding)
|
||||
let dataToExport = []
|
||||
|
||||
for (const asset of assets) {
|
||||
dataToExport = [
|
||||
...dataToExport,
|
||||
...hourlyFunding[asset].map((funding) => {
|
||||
console.log(funding)
|
||||
return {
|
||||
timestamp: funding.time,
|
||||
asset: asset,
|
||||
amount: funding.total_funding,
|
||||
}
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
const title = 'Mango Markets - Funding History - ' + new Date().toString()
|
||||
const columns = ['Timestamp', 'Asset', 'Amount']
|
||||
|
||||
exportDataToCSV(dataToExport, title, columns, t)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEmpty(hourlyFunding)) {
|
||||
setData(hourlyFunding[selectedAsset])
|
||||
|
@ -89,6 +116,15 @@ const AccountFunding = () => {
|
|||
<>
|
||||
<div className="pb-4 text-th-fgd-1 text-lg">
|
||||
{t('total-funding-stats')}
|
||||
<Button
|
||||
className={`float-right text-sm`}
|
||||
onClick={exportFundingDataToCSV}
|
||||
>
|
||||
<div className={`flex items-center`}>
|
||||
{t('export-data')}
|
||||
<SaveIcon className={`h-4 w-4 ml-1.5`} />
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
{mangoAccount ? (
|
||||
<div>
|
||||
|
|
|
@ -11,6 +11,11 @@ import {
|
|||
getMarketByBaseSymbolAndKind,
|
||||
PerpMarket,
|
||||
} from '@blockworks-foundation/mango-client'
|
||||
import { exportDataToCSV } from '../../utils/export'
|
||||
import { notify } from '../../utils/notifications'
|
||||
import useTradeHistory from '../../hooks/useTradeHistory'
|
||||
import Button from '../Button'
|
||||
import { SaveIcon } from '@heroicons/react/solid'
|
||||
|
||||
const historyViews = [
|
||||
{ label: 'Trades', key: 'Trades' },
|
||||
|
@ -24,6 +29,7 @@ export default function AccountHistory() {
|
|||
const [view, setView] = useState('Trades')
|
||||
const [history, setHistory] = useState(null)
|
||||
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
|
||||
const tradeHistory = useTradeHistory({ excludePerpLiquidations: true })
|
||||
|
||||
const mangoAccountPk = useMemo(() => {
|
||||
console.log('new mango account')
|
||||
|
@ -45,43 +51,118 @@ export default function AccountHistory() {
|
|||
}
|
||||
}, [mangoAccountPk])
|
||||
|
||||
console.log('history', history)
|
||||
const exportHistoryToCSV = () => {
|
||||
let dataToExport
|
||||
let headers
|
||||
|
||||
if (view == 'Trades') {
|
||||
dataToExport = tradeHistory.map((trade) => {
|
||||
console.log(trade)
|
||||
return {
|
||||
asset: trade.marketName,
|
||||
orderType: trade.side.toUpperCase(),
|
||||
quantity: trade.size,
|
||||
price: trade.price,
|
||||
value: trade.value,
|
||||
liquidity: trade.liquidity,
|
||||
fee: trade.feeCost,
|
||||
date: trade.loadTimestamp,
|
||||
}
|
||||
})
|
||||
headers = [
|
||||
'Market',
|
||||
'Side',
|
||||
'Size',
|
||||
'Price',
|
||||
'Value',
|
||||
'Liquidity',
|
||||
'Fee',
|
||||
'Approx. Time',
|
||||
]
|
||||
} else {
|
||||
dataToExport = history
|
||||
.filter((val) => val.activity_type == view)
|
||||
.map((row) => {
|
||||
row = row.activity_details
|
||||
|
||||
return {
|
||||
date: row.block_datetime,
|
||||
asset: row.symbol,
|
||||
quantity: row.quantity,
|
||||
value: row.usd_equivalent,
|
||||
}
|
||||
})
|
||||
headers = ['Timestamp', 'Asset', 'Quantity', 'Value']
|
||||
}
|
||||
|
||||
if (dataToExport.length == 0) {
|
||||
notify({
|
||||
title: t('export-data-empty'),
|
||||
description: '',
|
||||
type: 'info',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const title = historyViews.filter((v) => v.key == view)[0].label
|
||||
|
||||
exportDataToCSV(dataToExport, title, headers, t)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col md:flex-row md:items-end md:justify-between pb-4">
|
||||
<div className="pb-4">
|
||||
<div>
|
||||
<div className="mb-1 text-th-fgd-1 text-lg">{t('history')}</div>
|
||||
<div className="mr-4 text-xs text-th-fgd-3">
|
||||
{t('delay-displaying-recent')} {t('use-explorer-one')}
|
||||
<a
|
||||
href={`https://explorer.solana.com/address/${mangoAccountPk}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{t('use-explorer-two')}
|
||||
</a>
|
||||
{t('use-explorer-three')}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex mb-1 mt-4 md:mt-0">
|
||||
{historyViews.map(({ label, key }, index) => (
|
||||
<div
|
||||
className={`px-2 py-1 ${
|
||||
index > 0 ? 'ml-2' : null
|
||||
} rounded-md cursor-pointer default-transition bg-th-bkg-3
|
||||
${
|
||||
view === key
|
||||
? `ring-1 ring-inset ring-th-primary text-th-primary`
|
||||
: `text-th-fgd-1 opacity-50 hover:opacity-100`
|
||||
}
|
||||
`}
|
||||
onClick={() => setView(key)}
|
||||
key={key as string}
|
||||
>
|
||||
{t(label.toLowerCase())}
|
||||
<div className="flex justify-between mb-4 text-th-fgd-1 text-lg">
|
||||
<div className={`sm:w-3/4`}>
|
||||
<span>{t('history')}</span>
|
||||
<div className="mr-4 text-xs text-th-fgd-3">
|
||||
<div>
|
||||
<span>
|
||||
{t('delay-displaying-recent')} {t('use-explorer-one')}
|
||||
<a
|
||||
href={`https://explorer.solana.com/address/${mangoAccountPk}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{t('use-explorer-two')}
|
||||
</a>
|
||||
{t('use-explorer-three')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<Button
|
||||
className={`flex-none float-right text-sm h-9`}
|
||||
onClick={exportHistoryToCSV}
|
||||
>
|
||||
<div className={`flex items-center`}>
|
||||
{t('export-data')}
|
||||
<SaveIcon className={`h-4 w-4 ml-1.5`} />
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mb-1 mt-4 md:mt-0">
|
||||
<div className="flex justify-end">
|
||||
{historyViews.map(({ label, key }, index) => (
|
||||
<div
|
||||
className={`px-2 py-1 ${
|
||||
index > 0 ? 'ml-2' : null
|
||||
} rounded-md cursor-pointer default-transition bg-th-bkg-3
|
||||
${
|
||||
view === key
|
||||
? `ring-1 ring-inset ring-th-primary text-th-primary`
|
||||
: `text-th-fgd-1 opacity-50 hover:opacity-100`
|
||||
}
|
||||
`}
|
||||
onClick={() => setView(key)}
|
||||
key={key as string}
|
||||
>
|
||||
{t(label.toLowerCase())}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ViewContent view={view} history={history} />
|
||||
|
|
|
@ -12,6 +12,9 @@ import { useViewport } from '../../hooks/useViewport'
|
|||
import { breakpoints } from '../TradePageGrid'
|
||||
import { ExpandableRow } from '../TableElements'
|
||||
import MobileTableHeader from '../mobile/MobileTableHeader'
|
||||
import { exportDataToCSV } from '../../utils/export'
|
||||
import { SaveIcon } from '@heroicons/react/solid'
|
||||
import Button from '../Button'
|
||||
|
||||
interface InterestStats {
|
||||
[key: string]: {
|
||||
|
@ -51,6 +54,35 @@ const AccountInterest = () => {
|
|||
}
|
||||
}, [selectedAsset])
|
||||
|
||||
const exportInterestDataToCSV = () => {
|
||||
const assets = Object.keys(hourlyInterestStats)
|
||||
let dataToExport = []
|
||||
|
||||
for (const asset of assets) {
|
||||
dataToExport = [
|
||||
...dataToExport,
|
||||
...hourlyInterestStats[asset].map((interest) => {
|
||||
return {
|
||||
timestamp: interest.time,
|
||||
asset: asset,
|
||||
deposit_interest: interest.deposit_interest,
|
||||
borrow_interest: interest.borrow_interest,
|
||||
}
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
const title = 'Mango Markets - Interest History - ' + new Date().toString()
|
||||
const headers = [
|
||||
'Timestamp',
|
||||
'Asset',
|
||||
'Deposit Interest',
|
||||
'Borrow Interest',
|
||||
]
|
||||
|
||||
exportDataToCSV(dataToExport, title, headers, t)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEmpty(hourlyInterestStats)) {
|
||||
setData(hourlyInterestStats[selectedAsset])
|
||||
|
@ -115,7 +147,18 @@ const AccountInterest = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="pb-4 text-th-fgd-1 text-lg">{t('interest-earned')}</div>
|
||||
<div className="pb-4 text-th-fgd-1 text-lg">
|
||||
{t('interest-earned')}
|
||||
<Button
|
||||
className={`float-right text-sm`}
|
||||
onClick={exportInterestDataToCSV}
|
||||
>
|
||||
<div className={`flex items-center`}>
|
||||
{t('export-data')}
|
||||
<SaveIcon className={`h-4 w-4 ml-1.5`} />
|
||||
</div>
|
||||
</Button>
|
||||
</div>{' '}
|
||||
{mangoAccount ? (
|
||||
<div>
|
||||
{!isMobile ? (
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
"bs58": "^4.0.1",
|
||||
"buffer-layout": "^1.2.0",
|
||||
"dayjs": "^1.10.4",
|
||||
"export-to-csv": "^0.2.1",
|
||||
"immer": "^9.0.1",
|
||||
"immutable-tuple": "^0.4.10",
|
||||
"intro.js": "^4.2.2",
|
||||
|
|
|
@ -110,6 +110,9 @@
|
|||
"est-period-end": "Est Period End",
|
||||
"estimated-liq-price": "Est. Liq. Price",
|
||||
"explorer": "Explorer",
|
||||
"export-data": "Export Data",
|
||||
"export-data-success": "Data exported successfully!",
|
||||
"export-data-empty": "No data to export!",
|
||||
"fee": "Fee",
|
||||
"fee-discount": "Fee Discount",
|
||||
"funding": "Funding",
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import { ExportToCsv } from 'export-to-csv'
|
||||
import { notify } from './notifications'
|
||||
|
||||
/**
|
||||
* Function to export data to CSV file.
|
||||
*
|
||||
* @param dataToExport Array of objects for exporting
|
||||
* @param filename Name of exported file
|
||||
* @param headers Column headders
|
||||
* @param t Translation function since it cannot be called here
|
||||
*/
|
||||
export function exportDataToCSV(
|
||||
dataToExport: Array<any>,
|
||||
title: string,
|
||||
headers: Array<string>,
|
||||
t: any
|
||||
) {
|
||||
if (dataToExport.length == 0) {
|
||||
notify({
|
||||
title: t('export-data-empty'),
|
||||
description: '',
|
||||
type: 'info',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const options = {
|
||||
fieldSeparator: ',',
|
||||
quoteStrings: '"',
|
||||
decimalSeparator: '.',
|
||||
showLabels: true,
|
||||
showTitle: false,
|
||||
filename: title,
|
||||
useTextFile: false,
|
||||
useBom: true,
|
||||
headers: headers,
|
||||
}
|
||||
|
||||
const exporter = new ExportToCsv(options)
|
||||
exporter.generateCsv(dataToExport)
|
||||
|
||||
notify({
|
||||
title: t('export-data-success'),
|
||||
description: '',
|
||||
})
|
||||
}
|
|
@ -4029,6 +4029,11 @@ expect@^26.6.2:
|
|||
jest-message-util "^26.6.2"
|
||||
jest-regex-util "^26.0.0"
|
||||
|
||||
export-to-csv@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/export-to-csv/-/export-to-csv-0.2.1.tgz#8f997156feebc1cf995096da16341aa0100cce4e"
|
||||
integrity sha512-KTbrd3CAZ0cFceJEZr1e5uiMasabeCpXq1/5uvVxDl53o4jXJHnltasQoj2NkzrxD8hU9kdwjnMhoir/7nNx/A==
|
||||
|
||||
extend-shallow@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
|
||||
|
|
Loading…
Reference in New Issue