Skip to content

Commit

Permalink
feat(webapp): balance sheet export csv/xlsx
Browse files Browse the repository at this point in the history
  • Loading branch information
abouolia committed Nov 22, 2023
1 parent 47fd8f0 commit f242471
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 13 deletions.
Empty file.
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// @ts-nocheck
import React from 'react';
import {
NavbarGroup,
Button,
Expand All @@ -13,11 +12,12 @@ import classNames from 'classnames';
import { DashboardActionsBar, FormattedMessage as T, Icon } from '@/components';

import NumberFormatDropdown from '@/components/NumberFormatDropdown';
import { BalanceSheetExportMenu } from './components';

import { compose, saveInvoke } from '@/utils';
import { useBalanceSheetContext } from './BalanceSheetProvider';
import withBalanceSheet from './withBalanceSheet';
import withBalanceSheetActions from './withBalanceSheetActions';
import { compose, saveInvoke } from '@/utils';

/**
* Balance sheet - actions bar.
Expand Down Expand Up @@ -114,11 +114,18 @@ function BalanceSheetActionsBar({
icon={<Icon icon="print-16" iconSize={16} />}
text={<T id={'print'} />}
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="file-export-16" iconSize={16} />}
text={<T id={'export'} />}
/>
<Popover
content={<BalanceSheetExportMenu />}
interactionKind={PopoverInteractionKind.CLICK}
placement="bottom-start"
minimal
>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="file-export-16" iconSize={16} />}
text={<T id={'export'} />}
/>
</Popover>
</NavbarGroup>
</DashboardActionsBar>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
// @ts-nocheck
import React from 'react';
import { Button } from '@blueprintjs/core';
import React, { useRef } from 'react';
import {
Button,
Classes,
Intent,
Menu,
MenuItem,
ProgressBar,
Text,
} from '@blueprintjs/core';
import classNames from 'classnames';

import { FormattedMessage as T, Icon, If } from '@/components';
import {
FormattedMessage as T,
Icon,
If,
Stack,
AppToaster,
} from '@/components';

import FinancialLoadingBar from '../FinancialLoadingBar';
import { useBalanceSheetContext } from './BalanceSheetProvider';
import { FinancialComputeAlert } from '../FinancialReportPage';
import { dynamicColumns } from './dynamicColumns';
import {
useBalanceSheetCsvExport,
useBalanceSheetXlsxExport,
} from '@/hooks/query';

/**
* Balance sheet alerts.
Expand Down Expand Up @@ -66,3 +85,87 @@ export const useBalanceSheetColumns = () => {
[table],
);
};

/**
*
* @returns
*/
export const BalanceSheetExportMenu = () => {
const toastKey = useRef(null);
const commonToastConfig = {
isCloseButtonShown: true,
timeout: 2000,
};

const openProgressToast = (amount: number) => {
return (
<Stack spacing={8}>
<Text>The report has been exported successfully.</Text>
<ProgressBar
className={classNames('toast-progress', {
[Classes.PROGRESS_NO_STRIPES]: amount >= 100,
})}
intent={amount < 100 ? Intent.PRIMARY : Intent.SUCCESS}
value={amount / 100}
/>
</Stack>
);
};

// Export the report to xlsx.
const { mutateAsync: xlsxExport } = useBalanceSheetXlsxExport({
onDownloadProgress: (xlsxExportProgress: number) => {
if (!toastKey.current) {
toastKey.current = AppToaster.show({
message: openProgressToast(xlsxExportProgress),
...commonToastConfig,
});
} else {
AppToaster.show(
{
message: openProgressToast(xlsxExportProgress),
...commonToastConfig,
},
toastKey.current,
);
}
},
});
// Export the report to csv.
const { mutateAsync: csvExport } = useBalanceSheetCsvExport({
onDownloadProgress: (xlsxExportProgress: number) => {
if (!toastKey.current) {
toastKey.current = AppToaster.show({
message: openProgressToast(xlsxExportProgress),
...commonToastConfig,
});
} else {
AppToaster.show(
{
message: openProgressToast(xlsxExportProgress),
...commonToastConfig,
},
toastKey.current,
);
}
},
});

const handleCsvExportBtnClick = () => {
csvExport().then(() => {});
};

const handleXlsxExportBtnClick = () => {
xlsxExport().then(() => {});
};

return (
<Menu>
<MenuItem
text={'XLSX (Microsoft Excel)'}
onClick={handleXlsxExportBtnClick}
/>
<MenuItem text={'CSV'} onClick={handleCsvExportBtnClick} />
</Menu>
);
};
30 changes: 27 additions & 3 deletions packages/webapp/src/hooks/query/financialReports.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
// @ts-nocheck
import { useRequestQuery } from '../useQueryRequest';
import {
trialBalanceSheetReducer,
generalLedgerTableRowsReducer,
journalTableRowsReducer,
ARAgingSummaryTableRowsMapper,
APAgingSummaryTableRowsMapper,
inventoryValuationReducer,
purchasesByItemsReducer,
salesByItemsReducer,
} from '@/containers/FinancialStatements/reducers';
import t from './types';
import { useDownloadFile } from '../useDownloadFile';

/**
* Retrieve balance sheet.
Expand All @@ -33,6 +31,32 @@ export function useBalanceSheet(query, props) {
);
}

export const useBalanceSheetXlsxExport = (args) => {
return useDownloadFile({
url: '/financial_statements/balance_sheet',
config: {
headers: {
accept: 'application/xlsx',
},
},
filename: 'balance_sheet.xlsx',
...args
});
};

export const useBalanceSheetCsvExport = (args) => {
return useDownloadFile({
url: '/financial_statements/balance_sheet',
config: {
headers: {
accept: 'application/csv',
},
},
filename: 'balance_sheet.csv',
...args
});
};

/**
* Retrieve trial balance sheet.
*/
Expand Down
72 changes: 72 additions & 0 deletions packages/webapp/src/hooks/useDownloadFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// @ts-nocheck
import axios, { AxiosError, AxiosRequestConfig } from 'axios';
import { useState } from 'react';
import { useMutation } from 'react-query';
import useApiRequest from './useRequest';

interface IArgs {
url: string;
filename: string;
mime?: string;
config?: AxiosRequestConfig;
onDownloadProgress?: (progress: number) => void;
}

export const useDownloadFile = (args: IArgs) => {
const apiRequest = useApiRequest();

const mutation = useMutation<void, AxiosError, IArgs>(() =>
apiRequest
.get(args.url, {
responseType: 'blob',
onDownloadProgress: (ev) => {
args.onDownloadProgress &&
args.onDownloadProgress(Math.round((ev.loaded * 100) / ev.total));
},
...args.config,
})
.then((res) => {
downloadFile(res.data, args.filename, args.mime);
return res;
}),
);
return { ...mutation };
};

export function downloadFile(data, filename, mime, bom) {
var blobData = typeof bom !== 'undefined' ? [bom, data] : [data];
var blob = new Blob(blobData, { type: mime || 'application/octet-stream' });
if (typeof window.navigator.msSaveBlob !== 'undefined') {
// IE workaround for "HTML7007: One or more blob URLs were
// revoked by closing the blob for which they were created.
// These URLs will no longer resolve as the data backing
// the URL has been freed."
window.navigator.msSaveBlob(blob, filename);
} else {
var blobURL =
window.URL && window.URL.createObjectURL
? window.URL.createObjectURL(blob)
: window.webkitURL.createObjectURL(blob);
var tempLink = document.createElement('a');
tempLink.style.display = 'none';
tempLink.href = blobURL;
tempLink.setAttribute('download', filename);

// Safari thinks _blank anchor are pop ups. We only want to set _blank
// target if the browser does not support the HTML5 download attribute.
// This allows you to download files in desktop safari if pop up blocking
// is enabled.
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank');
}

document.body.appendChild(tempLink);
tempLink.click();

// Fixes "webkit blob resource error 1"
setTimeout(function () {
document.body.removeChild(tempLink);
window.URL.revokeObjectURL(blobURL);
}, 200);
}
}

0 comments on commit f242471

Please sign in to comment.