Skip to content

Commit

Permalink
feat(webapp): dynamic columns of sales by items sheet
Browse files Browse the repository at this point in the history
  • Loading branch information
abouolia committed Jan 18, 2024
1 parent 4a92017 commit 471ce1b
Show file tree
Hide file tree
Showing 8 changed files with 276 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,23 @@ import {
ITableRow,
} from '@/interfaces';
import { tableRowMapper } from '@/utils';
import FinancialSheet from '../FinancialSheet';
import { FinancialSheetStructure } from '../FinancialSheetStructure';
import { FinancialTable } from '../FinancialTable';
import { ROW_TYPE } from './constants';

export class SalesByItemsTable {
export class SalesByItemsTable extends R.compose(
FinancialTable,
FinancialSheetStructure
)(FinancialSheet) {
private readonly data: ISalesByItemsSheetStatement;

/**
* Constructor method.
* @param {ISalesByItemsSheetStatement} data
*/
constructor(data: ISalesByItemsSheetStatement) {
super();
this.data = data;
}

Expand All @@ -39,8 +47,9 @@ export class SalesByItemsTable {
*/
private itemMap = (item: ISalesByItemsItem): ITableRow => {
const columns = this.commonTableAccessors();
const meta = {};

const meta = {
rowTypes: [ROW_TYPE.ITEM],
};
return tableRowMapper(item, columns, meta);
};

Expand All @@ -60,8 +69,9 @@ export class SalesByItemsTable {
*/
private totalMap = (total: ISalesByItemsTotal) => {
const columns = this.commonTableAccessors();
const meta = {};

const meta = {
rowTypes: [ROW_TYPE.TOTAL],
};
return tableRowMapper(total, columns, meta);
};

Expand All @@ -81,11 +91,12 @@ export class SalesByItemsTable {
* @returns {ITableColumn[]}
*/
public tableColumns(): ITableColumn[] {
return [
const columns = [
{ key: 'item_name', label: 'Item name' },
{ key: 'sold_quantity', label: 'Sold quantity' },
{ key: 'sold_amount', label: 'Sold amount' },
{ key: 'average_price', label: 'Average price' },
];
return R.compose(this.tableColumnsCellIndexing)(columns);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@


export enum ROW_TYPE {
ITEM = 'ITEM',
TOTAL = 'TOTAL',
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @ts-nocheck
import React, { createContext, useContext } from 'react';
import { createContext, useContext } from 'react';
import FinancialReportPage from '../FinancialReportPage';
import { useSalesByItems } from '@/hooks/query';
import { useSalesByItemsTable } from '@/hooks/query';
import { transformFilterFormToQuery } from '../common';

const SalesByItemsContext = createContext();
Expand All @@ -12,7 +12,7 @@ function SalesByItemProvider({ query, ...props }) {
isFetching,
isLoading,
refetch,
} = useSalesByItems(
} = useSalesByItemsTable(
{
...transformFilterFormToQuery(query),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import withSalesByItemsActions from './withSalesByItemsActions';

import { compose, saveInvoke } from '@/utils';
import { useSalesByItemsContext } from './SalesByItemProvider';
import { SalesByItemsSheetExportMenu } from './components';

function SalesByItemsActionsBar({
// #withSalesByItems
Expand Down Expand Up @@ -108,11 +109,18 @@ function SalesByItemsActionsBar({
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={<SalesByItemsSheetExportMenu />}
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
Expand Up @@ -5,7 +5,7 @@ import styled from 'styled-components';

import { ReportDataTable, FinancialSheet } from '@/components';
import { useSalesByItemsContext } from './SalesByItemProvider';
import { useSalesByItemsTableColumns } from './components';
import { useSalesByItemsTableColumns } from './dynamicColumns';
import { tableRowTypesToClassnames } from '@/utils';
import { TableStyle } from '@/constants';

Expand All @@ -15,7 +15,7 @@ import { TableStyle } from '@/constants';
export default function SalesByItemsTable({ companyName }) {
// Sales by items context.
const {
salesByItems: { tableRows, query },
salesByItems: { table, query },
isLoading,
} = useSalesByItemsContext();

Expand All @@ -32,7 +32,7 @@ export default function SalesByItemsTable({ companyName }) {
>
<SalesByItemsDataTable
columns={columns}
data={tableRows}
data={table.rows}
expandable={true}
expandToggleColumn={1}
expandColumnSpace={1}
Expand All @@ -59,7 +59,7 @@ const SalesByItemsDataTable = styled(ReportDataTable)`
padding-top: 0.4rem;
padding-bottom: 0.4rem;
}
.tr.row_type--total .td {
.tr.row_type--TOTAL .td {
border-top: 1px solid #bbb;
font-weight: 500;
border-bottom: 3px double #000;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import React, { useMemo } from 'react';
import intl from 'react-intl-universal';

import { getColumnWidth } from '@/utils';
import { If } from '@/components';
import { If, Stack } from '@/components';
import { Align } from '@/constants';
import { CellTextSpan } from '@/components/Datatable/Cells';
import { useSalesByItemsContext } from './SalesByItemProvider';
import FinancialLoadingBar from '../FinancialLoadingBar';

import { Menu, MenuItem, ProgressBar, Text } from '@blueprintjs/core';
import { useBalanceSheetXlsxExport } from '@/hooks/query';

/**
* Retrieve sales by items table columns.
Expand Down Expand Up @@ -77,3 +78,88 @@ export function SalesByItemsLoadingBar() {
</If>
);
}

/**
* Retrieves the sales by items export menu.
* @returns {JSX.Element}
*/
export const SalesByItemsSheetExportMenu = () => {
const toastKey = useRef(null);
const commonToastConfig = {
isCloseButtonShown: true,
timeout: 2000,
};
const { query } = useBalanceSheetContext();

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(query, {
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(query, {
onDownloadProgress: (xlsxExportProgress: number) => {
if (!toastKey.current) {
toastKey.current = AppToaster.show({
message: openProgressToast(xlsxExportProgress),
...commonToastConfig,
});
} else {
AppToaster.show(
{
message: openProgressToast(xlsxExportProgress),
...commonToastConfig,
},
toastKey.current,
);
}
},
});
// Handle csv export button click.
const handleCsvExportBtnClick = () => {
csvExport().then(() => {});
};
// Handle xlsx export button click.
const handleXlsxExportBtnClick = () => {
xlsxExport().then(() => {});
};

return (
<Menu>
<MenuItem
text={'XLSX (Microsoft Excel)'}
onClick={handleXlsxExportBtnClick}
/>
<MenuItem text={'CSV'} onClick={handleCsvExportBtnClick} />
</Menu>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// @ts-nocheck
import { getColumnWidth } from '@/utils';
import * as R from 'ramda';
import { Align } from '@/constants';
import { useSalesByItemsContext } from './SalesByItemProvider';

const getTableCellValueAccessor = (index) => `cells[${index}].value`;

const getReportColWidth = (data, accessor, headerText) => {
return getColumnWidth(
data,
accessor,
{ magicSpacing: 10, minWidth: 100 },
headerText,
);
};

/**
* Account name column mapper.
*/
const commonColumnMapper = R.curry((data, column) => {
const accessor = getTableCellValueAccessor(column.cell_index);

return {
key: column.key,
Header: column.label,
accessor,
className: column.key,
textOverview: true,
};
});

/**
* Numeric columns accessor.
*/
const numericColumnAccessor = R.curry((data, column) => {
const accessor = getTableCellValueAccessor(column.cell_index);
const width = getReportColWidth(data, accessor, column.label);

return {
...column,
align: Align.Right,
width,
};
});

const dynamiColumnMapper = R.curry((data, column) => {
const _numericColumnAccessor = numericColumnAccessor(data);

return R.compose(
R.when(R.pathEq(['key'], 'sold_quantity'), _numericColumnAccessor),
R.when(R.pathEq(['key'], 'sold_amount'), _numericColumnAccessor),
R.when(R.pathEq(['key'], 'average_price'), _numericColumnAccessor),
commonColumnMapper(data),
)(column);
});

/**
* Composes the dynamic columns that fetched from request to columns to table component.
*/
export const dynamicColumns = R.curry((data, columns) => {
return R.map(dynamiColumnMapper(data), columns);
});

/**
* Retrieves the G/L sheet table columns for table component.
*/
export const useSalesByItemsTableColumns = () => {
const { salesByItems } = useSalesByItemsContext();

if (!salesByItems) {
throw new Error('Sales by items context not found');
}
const { table } = salesByItems;

return dynamicColumns(table.rows, table.columns);
};
Loading

0 comments on commit 471ce1b

Please sign in to comment.