mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 12:50:38 +00:00
feat: Accounts datatable.
This commit is contained in:
@@ -25,9 +25,10 @@ import ResourceConnect from 'connectors/Resource.connector';
|
||||
function AccountsActionsBar({
|
||||
openDialog,
|
||||
views,
|
||||
bulkActions,
|
||||
selectedRows = [],
|
||||
getResourceFields,
|
||||
onFilterChange,
|
||||
addAccountsTableQueries,
|
||||
onFilterChanged,
|
||||
}) {
|
||||
const {path} = useRouteMatch();
|
||||
const onClickNewAccount = () => { openDialog('account-form', {}); };
|
||||
@@ -37,13 +38,16 @@ function AccountsActionsBar({
|
||||
const viewsMenuItems = views.map((view) => {
|
||||
return (<MenuItem href={`${path}/${view.id}/custom_view`} text={view.name} />);
|
||||
});
|
||||
const hasBulkActionsSelected = useMemo(() => {
|
||||
return Object.keys(bulkActions).length > 0;
|
||||
}, [bulkActions]);
|
||||
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [selectedRows]);
|
||||
|
||||
const filterDropdown = FilterDropdown({
|
||||
fields: accountsFields,
|
||||
onFilterChange,
|
||||
onFilterChange: (filterConditions) => {
|
||||
addAccountsTableQueries({
|
||||
filter_roles: filterConditions || '',
|
||||
});
|
||||
onFilterChanged && onFilterChanged(filterConditions);
|
||||
},
|
||||
});
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
@@ -82,7 +86,7 @@ function AccountsActionsBar({
|
||||
|
||||
</Popover>
|
||||
|
||||
{hasBulkActionsSelected && (
|
||||
{hasSelectedRows && (
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon='archive' iconSize={15} />}
|
||||
@@ -90,7 +94,7 @@ function AccountsActionsBar({
|
||||
/>
|
||||
)}
|
||||
|
||||
{hasBulkActionsSelected && (
|
||||
{hasSelectedRows && (
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon='trash' iconSize={15} />}
|
||||
@@ -115,7 +119,7 @@ function AccountsActionsBar({
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
bulkActions: state.accounts.bulkActions
|
||||
// selectedRows: state.accounts.selectedRows
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
GridComponent,
|
||||
ColumnsDirective,
|
||||
ColumnDirective,
|
||||
Inject,
|
||||
Sort
|
||||
} from '@syncfusion/ej2-react-grids';
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useCallback, useState, useMemo } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Popover,
|
||||
@@ -13,50 +6,34 @@ import {
|
||||
MenuItem,
|
||||
MenuDivider,
|
||||
Position,
|
||||
Checkbox
|
||||
} from '@blueprintjs/core';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import useAsync from 'hooks/async';
|
||||
import Icon from 'components/Icon';
|
||||
import { handleBooleanChange, compose } from 'utils';
|
||||
import { compose } from 'utils';
|
||||
import AccountsConnect from 'connectors/Accounts.connector';
|
||||
import DialogConnect from 'connectors/Dialog.connector';
|
||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
||||
import ViewConnect from 'connectors/View.connector';
|
||||
import LoadingIndicator from 'components/LoadingIndicator';
|
||||
import DataTable from 'components/DataTable';
|
||||
|
||||
function AccountsDataTable({
|
||||
filterConditions,
|
||||
accounts,
|
||||
onDeleteAccount,
|
||||
onInactiveAccount,
|
||||
openDialog,
|
||||
addBulkActionAccount,
|
||||
removeBulkActionAccount,
|
||||
fetchAccounts,
|
||||
changeCurrentView,
|
||||
changePageSubtitle,
|
||||
getViewItem,
|
||||
setTopbarEditView
|
||||
setTopbarEditView,
|
||||
accountsLoading,
|
||||
onFetchData,
|
||||
setSelectedRowsAccounts,
|
||||
}) {
|
||||
const { custom_view_id: customViewId } = useParams();
|
||||
|
||||
// Fetch accounts list according to the given custom view id.
|
||||
const fetchHook = useAsync(async () => {
|
||||
await Promise.all([
|
||||
fetchAccounts({
|
||||
custom_view_id: customViewId,
|
||||
stringified_filter_roles: JSON.stringify(filterConditions) || '',
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
useEffect(() => { fetchHook.execute(); }, [filterConditions]);
|
||||
|
||||
// Refetch accounts list after custom view id change.
|
||||
useEffect(() => {
|
||||
const viewMeta = getViewItem(customViewId);
|
||||
fetchHook.execute();
|
||||
|
||||
if (customViewId) {
|
||||
changeCurrentView(customViewId);
|
||||
@@ -65,10 +42,8 @@ function AccountsDataTable({
|
||||
changePageSubtitle((customViewId && viewMeta) ? viewMeta.name : '');
|
||||
}, [customViewId]);
|
||||
|
||||
useEffect(() => () => {
|
||||
// Clear page subtitle when unmount the page.
|
||||
changePageSubtitle('');
|
||||
}, []);
|
||||
// Clear page subtitle when unmount the page.
|
||||
useEffect(() => () => { changePageSubtitle(''); }, []);
|
||||
|
||||
const handleEditAccount = account => () => {
|
||||
openDialog('account-form', { action: 'edit', id: account.id });
|
||||
@@ -88,95 +63,76 @@ function AccountsDataTable({
|
||||
onClick={() => onDeleteAccount(account)} />
|
||||
</Menu>
|
||||
);
|
||||
const columns = useMemo(() => [
|
||||
{
|
||||
id: 'name',
|
||||
Header: 'Account Name',
|
||||
accessor: 'name',
|
||||
},
|
||||
{
|
||||
id: 'code',
|
||||
Header: 'Code',
|
||||
accessor: 'code'
|
||||
},
|
||||
{
|
||||
id: 'type',
|
||||
Header: 'Type',
|
||||
accessor: 'type.name'
|
||||
},
|
||||
{
|
||||
id: 'normal',
|
||||
Header: 'Normal',
|
||||
Cell: ({ cell }) => {
|
||||
const account = cell.row.original;
|
||||
const type = account.type ? account.type.normal : '';
|
||||
const arrowDirection = type === 'credit' ? 'down' : 'up';
|
||||
|
||||
const handleClickCheckboxBulk = account =>
|
||||
handleBooleanChange(value => {
|
||||
if (value) {
|
||||
addBulkActionAccount(account.id);
|
||||
} else {
|
||||
removeBulkActionAccount(account.id);
|
||||
}
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: '',
|
||||
headerText: '',
|
||||
template: account => (
|
||||
<Checkbox onChange={handleClickCheckboxBulk(account)} />
|
||||
),
|
||||
customAttributes: { class: 'checkbox-row' }
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
headerText: 'Account Name',
|
||||
customAttributes: { class: 'account-name' }
|
||||
},
|
||||
{
|
||||
field: 'code',
|
||||
headerText: 'Code'
|
||||
},
|
||||
{
|
||||
field: 'type.name',
|
||||
headerText: 'Type'
|
||||
},
|
||||
{
|
||||
headerText: 'Normal',
|
||||
template: column => {
|
||||
const type = column.type ? column.type.normal : '';
|
||||
return type === 'credit' ? (
|
||||
<Icon icon={'arrow-down'} />
|
||||
) : (
|
||||
<Icon icon={'arrow-up'} />
|
||||
);
|
||||
return (<Icon icon={`arrow-${arrowDirection}`} />);
|
||||
},
|
||||
customAttributes: { class: 'account-normal' }
|
||||
className: 'normal',
|
||||
},
|
||||
{
|
||||
field: 'balance',
|
||||
headerText: 'Balance',
|
||||
template: (column, data) => {
|
||||
return <span>$10,000</span>;
|
||||
}
|
||||
id: 'balance',
|
||||
Header: 'Balance',
|
||||
Cell: ({ cell }) => {
|
||||
const account = cell.row.original;
|
||||
const {balance} = account;
|
||||
|
||||
return ('undefined' !== typeof balance) ?
|
||||
(<span>{ balance.amount }</span>) :
|
||||
(<span>--</span>);
|
||||
},
|
||||
|
||||
// canResize: false,
|
||||
},
|
||||
{
|
||||
headerText: '',
|
||||
template: account => (
|
||||
id: 'actions',
|
||||
Header: '',
|
||||
Cell: ({ cell }) => (
|
||||
<Popover
|
||||
content={actionMenuList(account)}
|
||||
position={Position.RIGHT_BOTTOM}
|
||||
>
|
||||
content={actionMenuList(cell.row.original)}
|
||||
position={Position.RIGHT_BOTTOM}>
|
||||
<Button icon={<Icon icon='ellipsis-h' />} />
|
||||
</Popover>
|
||||
)
|
||||
),
|
||||
className: 'actions',
|
||||
width: 50,
|
||||
// canResize: false
|
||||
}
|
||||
];
|
||||
], []);
|
||||
|
||||
const handleDatatableFetchData = useCallback(() => {
|
||||
onFetchData && onFetchData();
|
||||
}, []);
|
||||
|
||||
const dataStateChange = state => {};
|
||||
return (
|
||||
<LoadingIndicator loading={fetchHook.pending} spinnerSize={30}>
|
||||
<GridComponent
|
||||
allowSorting={true}
|
||||
allowGrouping={true}
|
||||
dataSource={{ result: accounts, count: 12 }}
|
||||
dataStateChange={dataStateChange}
|
||||
>
|
||||
<ColumnsDirective>
|
||||
{columns.map(column => {
|
||||
return (
|
||||
<ColumnDirective
|
||||
field={column.field}
|
||||
headerText={column.headerText}
|
||||
template={column.template}
|
||||
allowSorting={true}
|
||||
customAttributes={column.customAttributes}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</ColumnsDirective>
|
||||
<Inject services={[Sort]} />
|
||||
</GridComponent>
|
||||
</LoadingIndicator>
|
||||
<LoadingIndicator loading={accountsLoading} spinnerSize={30}>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={accounts}
|
||||
onFetchData={handleDatatableFetchData}
|
||||
manualSortBy={true} />
|
||||
</LoadingIndicator>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, {useEffect} from 'react';
|
||||
import { useHistory } from 'react-router';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
@@ -15,10 +15,14 @@ import { Link } from 'react-router-dom';
|
||||
import { compose } from 'utils';
|
||||
import AccountsConnect from 'connectors/Accounts.connector';
|
||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
||||
import {useUpdateEffect} from 'hooks';
|
||||
|
||||
function AccountsViewsTabs({
|
||||
views,
|
||||
setTopbarEditView,
|
||||
setTopbarEditView,
|
||||
customViewChanged,
|
||||
addAccountsTableQueries,
|
||||
onViewChanged,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { custom_view_id: customViewId } = useParams();
|
||||
@@ -30,6 +34,22 @@ function AccountsViewsTabs({
|
||||
const handleViewLinkClick = () => {
|
||||
setTopbarEditView(customViewId);
|
||||
}
|
||||
|
||||
useUpdateEffect(() => {
|
||||
customViewChanged && customViewChanged(customViewId);
|
||||
|
||||
addAccountsTableQueries({
|
||||
custom_view_id: customViewId || null,
|
||||
});
|
||||
onViewChanged && onViewChanged(customViewId);
|
||||
}, [customViewId]);
|
||||
|
||||
useEffect(() => {
|
||||
addAccountsTableQueries({
|
||||
custom_view_id: customViewId,
|
||||
})
|
||||
}, [customViewId]);
|
||||
|
||||
const tabs = views.map(view => {
|
||||
const baseUrl = '/dashboard/accounts';
|
||||
const link = (
|
||||
@@ -38,7 +58,9 @@ function AccountsViewsTabs({
|
||||
onClick={handleViewLinkClick}
|
||||
>{view.name}</Link>
|
||||
);
|
||||
return <Tab id={`custom_view_${view.id}`} title={link} />;
|
||||
return <Tab
|
||||
id={`custom_view_${view.id}`}
|
||||
title={link} />;
|
||||
});
|
||||
return (
|
||||
<Navbar className='navbar--dashboard-views'>
|
||||
@@ -49,7 +71,9 @@ function AccountsViewsTabs({
|
||||
selectedTabId={`custom_view_${customViewId}`}
|
||||
className='tabs--dashboard-views'
|
||||
>
|
||||
<Tab id='all' title={<Link to={`/dashboard/accounts`}>All</Link>} />
|
||||
<Tab
|
||||
id='all'
|
||||
title={<Link to={`/dashboard/accounts`}>All</Link>} />
|
||||
|
||||
{tabs}
|
||||
<Button
|
||||
|
||||
@@ -1,10 +1,41 @@
|
||||
import React from 'react';
|
||||
import { useTable, useExpanded, usePagination } from 'react-table'
|
||||
import React, {useEffect} from 'react';
|
||||
import {
|
||||
useTable,
|
||||
useExpanded,
|
||||
useRowSelect,
|
||||
usePagination,
|
||||
useResizeColumns,
|
||||
useAsyncDebounce,
|
||||
useSortBy,
|
||||
useFlexLayout
|
||||
} from 'react-table'
|
||||
import {Checkbox} from '@blueprintjs/core';
|
||||
import classnames from 'classnames';
|
||||
import Icon from 'components/Icon';
|
||||
// import { FixedSizeList } from 'react-window'
|
||||
|
||||
const IndeterminateCheckbox = React.forwardRef(
|
||||
({ indeterminate, ...rest }, ref) => {
|
||||
const defaultRef = React.useRef()
|
||||
const resolvedRef = ref || defaultRef
|
||||
|
||||
useEffect(() => {
|
||||
resolvedRef.current.indeterminate = indeterminate
|
||||
}, [resolvedRef, indeterminate])
|
||||
|
||||
return (
|
||||
<Checkbox ref={resolvedRef} {...rest} />
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default function DataTable({
|
||||
columns,
|
||||
data,
|
||||
loading,
|
||||
onFetchData,
|
||||
onSelectedRowsChange,
|
||||
manualSortBy = 'false'
|
||||
}) {
|
||||
const {
|
||||
getTableProps,
|
||||
@@ -20,8 +51,10 @@ export default function DataTable({
|
||||
nextPage,
|
||||
previousPage,
|
||||
setPageSize,
|
||||
|
||||
selectedFlatRows,
|
||||
// Get the state from the instance
|
||||
state: { pageIndex, pageSize },
|
||||
state: { pageIndex, pageSize, sortBy, selectedRowIds },
|
||||
} = useTable(
|
||||
{
|
||||
columns,
|
||||
@@ -33,59 +66,102 @@ export default function DataTable({
|
||||
// pageCount.
|
||||
// pageCount: controlledPageCount,
|
||||
getSubRows: row => row.children,
|
||||
manualSortBy
|
||||
},
|
||||
useSortBy,
|
||||
useExpanded,
|
||||
useRowSelect,
|
||||
usePagination,
|
||||
useResizeColumns,
|
||||
useFlexLayout,
|
||||
hooks => {
|
||||
hooks.visibleColumns.push(columns => [
|
||||
// Let's make a column for selection
|
||||
{
|
||||
id: 'selection',
|
||||
disableResizing: true,
|
||||
minWidth: 35,
|
||||
width: 35,
|
||||
maxWidth: 35,
|
||||
// The header can use the table's getToggleAllRowsSelectedProps method
|
||||
// to render a checkbox
|
||||
Header: ({ getToggleAllRowsSelectedProps }) => (
|
||||
<div>
|
||||
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
|
||||
</div>
|
||||
),
|
||||
// The cell can use the individual row's getToggleRowSelectedProps method
|
||||
// to the render a checkbox
|
||||
Cell: ({ row }) => (
|
||||
<div>
|
||||
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
...columns,
|
||||
])
|
||||
}
|
||||
);
|
||||
|
||||
// Debounce our onFetchData call for 100ms
|
||||
const onFetchDataDebounced = useAsyncDebounce(onFetchData, 100);
|
||||
const onSelectRowsDebounced = useAsyncDebounce(onSelectedRowsChange, 250);
|
||||
|
||||
// When these table states change, fetch new data!
|
||||
useEffect(() => {
|
||||
onFetchDataDebounced({ pageIndex, pageSize, sortBy })
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={'bigcapital-datatable'}>
|
||||
<table {...getTableProps()}>
|
||||
<thead>
|
||||
<div {...getTableProps()} className="table">
|
||||
<div className="thead">
|
||||
{headerGroups.map(headerGroup => (
|
||||
<tr {...headerGroup.getHeaderGroupProps()}>
|
||||
<div {...headerGroup.getHeaderGroupProps()} className="tr">
|
||||
{headerGroup.headers.map(column => (
|
||||
<th {...column.getHeaderProps({
|
||||
className: column.className || '',
|
||||
<div {...column.getHeaderProps({
|
||||
className: classnames(column.className || '', 'th'),
|
||||
})}>
|
||||
{column.render('Header')}
|
||||
<span>
|
||||
{column.isSorted
|
||||
? column.isSortedDesc
|
||||
? ' 🔽'
|
||||
: ' 🔼'
|
||||
: ''}
|
||||
</span>
|
||||
</th>
|
||||
<div {...column.getSortByToggleProps()}>
|
||||
{column.render('Header')}
|
||||
<span>
|
||||
{column.isSorted
|
||||
? column.isSortedDesc
|
||||
? (<Icon icon="sort-down" />)
|
||||
: (<Icon icon="sort-up" />)
|
||||
: ''}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{column.canResize && (
|
||||
<div
|
||||
{...column.getResizerProps()}
|
||||
className={`resizer ${
|
||||
column.isResizing ? 'isResizing' : ''
|
||||
}`}>
|
||||
<div class="inner-resizer" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</tr>
|
||||
</div>
|
||||
))}
|
||||
</thead>
|
||||
<tbody {...getTableBodyProps()}>
|
||||
</div>
|
||||
<div {...getTableBodyProps()} className="tbody">
|
||||
{page.map((row, i) => {
|
||||
prepareRow(row)
|
||||
return (
|
||||
<tr {...row.getRowProps()}>
|
||||
<div {...row.getRowProps()} className="tr">
|
||||
{row.cells.map((cell) => {
|
||||
return <td {...cell.getCellProps({
|
||||
className: cell.column.className || '',
|
||||
})}>{ cell.render('Cell') }</td>
|
||||
return <div {...cell.getCellProps({
|
||||
className: classnames(cell.column.className || '', 'td'),
|
||||
})}>{ cell.render('Cell') }</div>
|
||||
})}
|
||||
</tr>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
<tr>
|
||||
{loading ? (
|
||||
// Use our custom loading state to show a loading indicator
|
||||
<td colSpan="10000">Loading...</td>
|
||||
) : (
|
||||
<td colSpan="10000">
|
||||
{/* Showing {page.length} of ~{controlledPageCount * pageSize}{' '} results */}
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -6,7 +6,7 @@ export default function DialogsContainer() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<AccountFormDialog />
|
||||
<UserFormDialog />
|
||||
{/* <UserFormDialog /> */}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, {useState, useEffect, useMemo} from 'react';
|
||||
import { Spinner } from '@blueprintjs/core';
|
||||
|
||||
export default function LoadingIndicator({
|
||||
@@ -6,15 +6,33 @@ export default function LoadingIndicator({
|
||||
spinnerSize = 40,
|
||||
children
|
||||
}) {
|
||||
const [rendered, setRendered] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading) { setRendered(true); }
|
||||
}, [loading]);
|
||||
|
||||
const componentStyle = useMemo(() => {
|
||||
return {display: !loading ? 'block' : 'none'};
|
||||
}, [loading]);
|
||||
|
||||
const loadingComponent = useMemo(() => (
|
||||
<div class='dashboard__loading-indicator'>
|
||||
<Spinner size={spinnerSize} value={null} />
|
||||
</div>
|
||||
), [spinnerSize]);
|
||||
|
||||
const renderComponent = useMemo(() => (
|
||||
<div style={componentStyle}>{ children }</div>
|
||||
), [children, componentStyle]);
|
||||
|
||||
const maybeRenderComponent = rendered && renderComponent;
|
||||
const maybeRenderLoadingSpinner = loading && loadingComponent;
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading ? (
|
||||
<div class='dashboard__loading-indicator'>
|
||||
<Spinner size={spinnerSize} value={null} />
|
||||
</div>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
{ maybeRenderLoadingSpinner }
|
||||
{ maybeRenderComponent }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {useParams} from 'react-router-dom';
|
||||
import t from 'store/types';
|
||||
import {
|
||||
fetchAccountTypes,
|
||||
fetchAccountsList,
|
||||
deleteAccount,
|
||||
inactiveAccount,
|
||||
fetchAccountsTable,
|
||||
} from 'store/accounts/accounts.actions';
|
||||
import {
|
||||
getAccountsItems,
|
||||
@@ -17,23 +17,32 @@ import {
|
||||
const mapStateToProps = (state, props) => ({
|
||||
views: getResourceViews(state, 'accounts'),
|
||||
accounts: getAccountsItems(state, state.accounts.currentViewId),
|
||||
tableQuery: state.accounts.tableQuery,
|
||||
accountsLoading: state.accounts.loading,
|
||||
});
|
||||
|
||||
const mapActionsToProps = (dispatch) => ({
|
||||
fetchAccounts: (query) => dispatch(fetchAccountsList({ query })),
|
||||
fetchAccountTypes: () => dispatch(fetchAccountTypes()),
|
||||
deleteAccount: (id) => dispatch(deleteAccount({ id })),
|
||||
inactiveAccount: (id) => dispatch(inactiveAccount({ id })),
|
||||
addBulkActionAccount: (id) => dispatch({
|
||||
type: t.ACCOUNT_BULK_ACTION_ADD, account_id: id
|
||||
}),
|
||||
removeBulkActionAccount: (id) => dispatch({
|
||||
type: t.ACCOUNT_BULK_ACTION_REMOVE, account_id: id,
|
||||
}),
|
||||
requestDeleteAccount: (id) => dispatch(deleteAccount({ id })),
|
||||
requestInactiveAccount: (id) => dispatch(inactiveAccount({ id })),
|
||||
changeCurrentView: (id) => dispatch({
|
||||
type: t.ACCOUNTS_SET_CURRENT_VIEW,
|
||||
currentViewId: parseInt(id, 10),
|
||||
}),
|
||||
|
||||
setAccountsTableQuery: (key, value) => dispatch({
|
||||
type: 'ACCOUNTS_TABLE_QUERY_SET', key, value,
|
||||
}),
|
||||
addAccountsTableQueries: (queries) => dispatch({
|
||||
type: 'ACCOUNTS_TABLE_QUERIES_ADD', queries,
|
||||
}),
|
||||
|
||||
fetchAccountsTable: (query = {}) => dispatch(fetchAccountsTable({ query: { ...query } })),
|
||||
|
||||
setSelectedRowsAccounts: (ids) => dispatch({
|
||||
type: t.ACCOUNTS_SELECTED_ROWS_SET, ids,
|
||||
}),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapActionsToProps);
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import {
|
||||
Route,
|
||||
Switch,
|
||||
@@ -22,11 +22,12 @@ import { compose } from 'utils';
|
||||
function AccountsChart({
|
||||
changePageTitle,
|
||||
fetchAccounts,
|
||||
deleteAccount,
|
||||
inactiveAccount,
|
||||
requestDeleteAccount,
|
||||
requestInactiveAccount,
|
||||
fetchResourceViews,
|
||||
fetchResourceFields,
|
||||
getResourceFields,
|
||||
fetchAccountsTable,
|
||||
}) {
|
||||
const [state, setState] = useState({
|
||||
deleteAlertActive: false,
|
||||
@@ -34,8 +35,8 @@ function AccountsChart({
|
||||
inactiveAlertActive: false,
|
||||
targetAccount: {},
|
||||
});
|
||||
|
||||
const [filterConditions, setFilterConditions] = useState([]);
|
||||
const [deleteAccount, setDeleteAccount] = useState(false);
|
||||
const [inactiveAccount, setInactiveAccount] = useState(false);
|
||||
|
||||
const fetchHook = useAsync(async () => {
|
||||
await Promise.all([
|
||||
@@ -44,57 +45,56 @@ function AccountsChart({
|
||||
]);
|
||||
});
|
||||
|
||||
// Fetch accounts list according to the given custom view id.
|
||||
const fetchAccountsHook = useAsync(async () => {
|
||||
await Promise.all([
|
||||
fetchAccountsTable(),
|
||||
]);
|
||||
}, false);
|
||||
|
||||
useEffect(() => {
|
||||
changePageTitle('Chart of Accounts');
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Handle click and cancel/confirm account delete
|
||||
*/
|
||||
const handleDeleteAccount = (account) => {
|
||||
setState({
|
||||
deleteAlertActive: true,
|
||||
deleteAccount: account,
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancelAccountDelete = () => {
|
||||
setState({ deleteAlertActive: false });
|
||||
};
|
||||
const handleConfirmAccountDelete = () => {
|
||||
const { targetAccount: account } = state;
|
||||
deleteAccount(account.id).then(() => {
|
||||
setState({ deleteAlertActive: false });
|
||||
fetchAccounts();
|
||||
// Handle click and cancel/confirm account delete
|
||||
const handleDeleteAccount = (account) => { setDeleteAccount(account); };
|
||||
|
||||
// handle cancel delete account alert.
|
||||
const handleCancelAccountDelete = () => { setDeleteAccount(false); };
|
||||
|
||||
// Handle confirm account delete
|
||||
const handleConfirmAccountDelete = useCallback(() => {
|
||||
requestDeleteAccount(deleteAccount.id).then(() => {
|
||||
setDeleteAccount(false);
|
||||
fetchAccountsHook.execute();
|
||||
AppToaster.show({ message: 'the_account_has_been_deleted' });
|
||||
});
|
||||
};
|
||||
}, [deleteAccount]);
|
||||
|
||||
/**
|
||||
* Handle cancel/confirm account inactive.
|
||||
*/
|
||||
const handleInactiveAccount = (account) => {
|
||||
setState({ inactiveAlertActive: true, targetAccount: account });
|
||||
};
|
||||
// Handle cancel/confirm account inactive.
|
||||
const handleInactiveAccount = useCallback((account) => {
|
||||
setInactiveAccount(account);
|
||||
}, []);
|
||||
|
||||
const handleCancelInactiveAccount = () => {
|
||||
setState({ inactiveAlertActive: false });
|
||||
};
|
||||
// Handle cancel inactive account alert.
|
||||
const handleCancelInactiveAccount = useCallback(() => {
|
||||
setInactiveAccount(false);
|
||||
}, []);
|
||||
|
||||
const handleConfirmAccountActive = () => {
|
||||
const { targetAccount: account } = state;
|
||||
inactiveAccount(account.id).then(() => {
|
||||
setState({ inactiveAlertActive: true });
|
||||
fetchAccounts();
|
||||
// Handle confirm account activation.
|
||||
const handleConfirmAccountActive = useCallback(() => {
|
||||
requestInactiveAccount(inactiveAccount.id).then(() => {
|
||||
setInactiveAccount(false);
|
||||
fetchAccountsTable();
|
||||
AppToaster.show({ message: 'the_account_has_been_inactivated' });
|
||||
});
|
||||
};
|
||||
}, [inactiveAccount]);
|
||||
|
||||
/**
|
||||
* Handle cancel/confirm account restore.
|
||||
*/
|
||||
const handleCancelAccountRestore = () => {
|
||||
setState({ restoreAlertActive: false });
|
||||
|
||||
};
|
||||
|
||||
const handleEditAccount = (account) => {
|
||||
@@ -108,16 +108,22 @@ function AccountsChart({
|
||||
const handleConfirmAccountRestore = (account) => {
|
||||
|
||||
};
|
||||
|
||||
const handleDeleteBulkAccounts = (accounts) => {
|
||||
|
||||
};
|
||||
const handleFilterChange = (conditions) => { setFilterConditions(conditions); };
|
||||
|
||||
const handleSelectedRowsChange = (accounts) => {
|
||||
console.log(accounts);
|
||||
};
|
||||
|
||||
const handleFilterChanged = useCallback(() => { fetchAccountsHook.execute(); }, []);
|
||||
const handleViewChanged = useCallback(() => { fetchAccountsHook.execute(); }, []);
|
||||
const handleFetchData = useCallback(() => { fetchAccountsHook.execute(); }, []);
|
||||
|
||||
return (
|
||||
<DashboardInsider loading={fetchHook.pending} name={'accounts-chart'}>
|
||||
<DashboardActionsBar
|
||||
onFilterChange={handleFilterChange} />
|
||||
onFilterChanged={handleFilterChanged} />
|
||||
<DashboardPageContent>
|
||||
<Switch>
|
||||
<Route
|
||||
@@ -126,14 +132,17 @@ function AccountsChart({
|
||||
'/dashboard/accounts/:custom_view_id/custom_view',
|
||||
'/dashboard/accounts'
|
||||
]}>
|
||||
<AccountsViewsTabs onDeleteBulkAccounts={handleDeleteBulkAccounts} />
|
||||
|
||||
<AccountsViewsTabs
|
||||
onViewChanged={handleViewChanged}
|
||||
onDeleteBulkAccounts={handleDeleteBulkAccounts} />
|
||||
|
||||
<AccountsDataTable
|
||||
filterConditions={filterConditions}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
onDeleteAccount={handleDeleteAccount}
|
||||
onInactiveAccount={handleInactiveAccount}
|
||||
onRestoreAccount={handleRestoreAccount}
|
||||
onEditAccount={handleEditAccount} />
|
||||
onEditAccount={handleEditAccount}
|
||||
onFetchData={handleFetchData} />
|
||||
</Route>
|
||||
</Switch>
|
||||
|
||||
@@ -142,7 +151,7 @@ function AccountsChart({
|
||||
confirmButtonText="Move to Trash"
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={state.deleteAlertActive}
|
||||
isOpen={deleteAccount}
|
||||
onCancel={handleCancelAccountDelete}
|
||||
onConfirm={handleConfirmAccountDelete}>
|
||||
<p>
|
||||
@@ -156,7 +165,7 @@ function AccountsChart({
|
||||
confirmButtonText="Inactivate"
|
||||
icon="trash"
|
||||
intent={Intent.WARNING}
|
||||
isOpen={state.inactiveAlertActive}
|
||||
isOpen={inactiveAccount}
|
||||
onCancel={handleCancelInactiveAccount}
|
||||
onConfirm={handleConfirmAccountActive}>
|
||||
<p>
|
||||
@@ -164,20 +173,6 @@ function AccountsChart({
|
||||
but it will become private to you.
|
||||
</p>
|
||||
</Alert>
|
||||
|
||||
<Alert
|
||||
cancelButtonText="Cancel"
|
||||
confirmButtonText="Move to Trash"
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={state.restoreAlertActive}
|
||||
onCancel={handleCancelAccountRestore}
|
||||
onConfirm={handleConfirmAccountRestore}>
|
||||
<p>
|
||||
Are you sure you want to move <b>filename</b> to Trash? You will be able to restore it later,
|
||||
but it will become private to you.
|
||||
</p>
|
||||
</Alert>
|
||||
</DashboardPageContent>
|
||||
</DashboardInsider>
|
||||
);
|
||||
|
||||
@@ -21,6 +21,7 @@ import AppToaster from 'components/AppToaster';
|
||||
import DialogConnect from 'connectors/Dialog.connector';
|
||||
import DialogReduxConnect from 'components/DialogReduxConnect';
|
||||
import AccountFormDialogConnect from 'connectors/AccountFormDialog.connector';
|
||||
import AccountsConnect from 'connectors/Accounts.connector';
|
||||
|
||||
function AccountFormDialog({
|
||||
name,
|
||||
@@ -33,7 +34,8 @@ function AccountFormDialog({
|
||||
closeDialog,
|
||||
submitAccount,
|
||||
fetchAccount,
|
||||
editAccount
|
||||
editAccount,
|
||||
fetchAccountsTable,
|
||||
}) {
|
||||
const intl = useIntl();
|
||||
const accountFormValidationSchema = Yup.object().shape({
|
||||
@@ -64,6 +66,7 @@ function AccountFormDialog({
|
||||
AppToaster.show({
|
||||
message: 'the_account_has_been_edited'
|
||||
});
|
||||
refetchAccounts.execute();
|
||||
});
|
||||
} else {
|
||||
submitAccount({ form: { ...omit(values, exclude) } }).then(response => {
|
||||
@@ -71,6 +74,7 @@ function AccountFormDialog({
|
||||
AppToaster.show({
|
||||
message: 'the_account_has_been_submit'
|
||||
});
|
||||
refetchAccounts.execute();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -131,15 +135,19 @@ function AccountFormDialog({
|
||||
await Promise.all([
|
||||
fetchAccounts(),
|
||||
fetchAccountTypes(),
|
||||
|
||||
// Fetch the target in case edit mode.
|
||||
...(payload.action === 'edit' ? [fetchAccount(payload.id)] : [])
|
||||
]);
|
||||
});
|
||||
}, false);
|
||||
|
||||
const onDialogOpening = async () => {
|
||||
fetchHook.execute();
|
||||
};
|
||||
const refetchAccounts = useAsync(async () => {
|
||||
await Promise.all([
|
||||
fetchAccountsTable(),
|
||||
]);
|
||||
}, false);
|
||||
|
||||
// Fetch requests on dialog opening.
|
||||
const onDialogOpening = async () => { fetchHook.execute(); };
|
||||
|
||||
const onChangeAccountType = accountType => {
|
||||
setState({ ...state, selectedAccountType: accountType.name });
|
||||
@@ -294,6 +302,7 @@ function AccountFormDialog({
|
||||
|
||||
export default compose(
|
||||
AccountFormDialogConnect,
|
||||
AccountsConnect,
|
||||
DialogReduxConnect,
|
||||
DialogConnect
|
||||
)(AccountFormDialog);
|
||||
|
||||
@@ -1,6 +1,27 @@
|
||||
import {useRef, useEffect} from 'react';
|
||||
import useAsync from './async';
|
||||
|
||||
// import use from 'async';
|
||||
|
||||
/**
|
||||
* A custom useEffect hook that only triggers on updates, not on initial mount
|
||||
* Idea stolen from: https://stackoverflow.com/a/55075818/1526448
|
||||
* @param {Function} effect
|
||||
* @param {Array<any>} dependencies
|
||||
*/
|
||||
export function useUpdateEffect(effect, dependencies = []) {
|
||||
const isInitialMount = useRef(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialMount.current) {
|
||||
isInitialMount.current = false;
|
||||
} else {
|
||||
effect();
|
||||
}
|
||||
}, dependencies);
|
||||
}
|
||||
|
||||
export default {
|
||||
useAsync,
|
||||
useUpdateEffect,
|
||||
}
|
||||
@@ -78,5 +78,13 @@ export default {
|
||||
"pen": {
|
||||
path: ['M493.26 56.26l-37.51-37.51C443.25 6.25 426.87 0 410.49 0s-32.76 6.25-45.25 18.74l-74.49 74.49L256 127.98 12.85 371.12.15 485.34C-1.45 499.72 9.88 512 23.95 512c.89 0 1.79-.05 2.69-.15l114.14-12.61L384.02 256l34.74-34.74 74.49-74.49c25-25 25-65.52.01-90.51zM118.75 453.39l-67.58 7.46 7.53-67.69 231.24-231.24 31.02-31.02 60.14 60.14-31.02 31.02-231.33 231.33zm340.56-340.57l-44.28 44.28-60.13-60.14 44.28-44.28c4.08-4.08 8.84-4.69 11.31-4.69s7.24.61 11.31 4.69l37.51 37.51c6.24 6.25 6.24 16.4 0 22.63z'],
|
||||
viewBox: '0 0 512 512',
|
||||
},
|
||||
"sort-up": {
|
||||
path: ['M279 224H41c-21.4 0-32.1-25.9-17-41L143 64c9.4-9.4 24.6-9.4 33.9 0l119 119c15.2 15.1 4.5 41-16.9 41z'],
|
||||
viewBox: '0 0 320 512'
|
||||
},
|
||||
"sort-down": {
|
||||
path: ['M41 288h238c21.4 0 32.1 25.9 17 41L177 448c-9.4 9.4-24.6 9.4-33.9 0L24 329c-15.1-15.1-4.4-41 17-41z'],
|
||||
viewBox: '0 0 320 512'
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,7 @@ export const fetchAccountTypes = () => {
|
||||
});
|
||||
resolve(response);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
.catch(error => { reject(error); });
|
||||
});
|
||||
};
|
||||
|
||||
@@ -40,6 +38,38 @@ export const fetchAccountsList = ({ query } = {}) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchAccountsTable = ({ query } = {}) => {
|
||||
return (dispatch, getState) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const pageQuery = getState().accounts.tableQuery;
|
||||
|
||||
dispatch({
|
||||
type: t.ACCOUNTS_TABLE_LOADING,
|
||||
loading: true,
|
||||
});
|
||||
ApiService.get('accounts', { params: { ...pageQuery, ...query } })
|
||||
.then(response => {
|
||||
dispatch({
|
||||
type: t.ACCOUNTS_PAGE_SET,
|
||||
accounts: response.data.accounts,
|
||||
customViewId: response.data.customViewId
|
||||
});
|
||||
dispatch({
|
||||
type: t.ACCOUNTS_ITEMS_SET,
|
||||
accounts: response.data.accounts
|
||||
});
|
||||
dispatch({
|
||||
type: t.ACCOUNTS_TABLE_LOADING,
|
||||
loading: false,
|
||||
});
|
||||
resolve(response);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchAccountsDataTable = ({ query }) => {
|
||||
return dispatch =>
|
||||
new Promise((resolve, reject) => {
|
||||
@@ -109,7 +139,12 @@ export const inactiveAccount = ({ id }) => {
|
||||
};
|
||||
|
||||
export const deleteAccount = ({ id }) => {
|
||||
return dispatch => ApiService.delete(`accounts/${id}`);
|
||||
return dispatch => new Promise((resolve, reject) => {
|
||||
ApiService.delete(`accounts/${id}`).then((response) => {
|
||||
dispatch({ type: t.ACCOUNT_DELETE, id });
|
||||
resolve(response);
|
||||
}).catch(error => { reject(error); });
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteBulkAccounts = ({ ids }) => {};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import t from 'store/types';
|
||||
import { createReducer } from '@reduxjs/toolkit';
|
||||
import { createReducer, combineReducers } from '@reduxjs/toolkit';
|
||||
import { createTableQueryReducers } from 'store/queryReducers';
|
||||
|
||||
const initialState = {
|
||||
items: {},
|
||||
@@ -9,10 +10,11 @@ const initialState = {
|
||||
accountFormErrors: [],
|
||||
datatableQuery: {},
|
||||
currentViewId: -1,
|
||||
bulkActions: {},
|
||||
selectedRows: [],
|
||||
loading: false,
|
||||
};
|
||||
|
||||
export default createReducer(initialState, {
|
||||
const accountsReducer = createReducer(initialState, {
|
||||
[t.ACCOUNTS_ITEMS_SET]: (state, action) => {
|
||||
const _items = {};
|
||||
|
||||
@@ -44,19 +46,32 @@ export default createReducer(initialState, {
|
||||
state.accountsById[action.account.id] = action.account;
|
||||
},
|
||||
|
||||
[t.ACCOUNT_BULK_ACTION_ADD]: (state, action) => {
|
||||
state.bulkActions[action.account_id] = true;
|
||||
[t.ACCOUNT_DELETE]: (state, action) => {
|
||||
if (typeof state.items[action.id] !== 'undefined'){
|
||||
delete state.items[action.id];
|
||||
}
|
||||
},
|
||||
|
||||
[t.ACCOUNT_BULK_ACTION_REMOVE]: (state, action) => {
|
||||
delete state.bulkActions[action.account_id];
|
||||
[t.ACCOUNTS_SELECTED_ROWS_SET]: (state, action) => {
|
||||
state.selectedRows.push(...action.ids);
|
||||
},
|
||||
|
||||
[t.ACCOUNTS_SET_CURRENT_VIEW]: (state, action) => {
|
||||
state.currentViewId = action.currentViewId;
|
||||
}
|
||||
},
|
||||
|
||||
[t.ACCOUNTS_TABLE_LOADING]: (state, action) => {
|
||||
state.loading = action.loading;
|
||||
},
|
||||
});
|
||||
|
||||
export default createTableQueryReducers('accounts', accountsReducer);
|
||||
|
||||
export const getAccountById = (state, id) => {
|
||||
return state.accounts.accountsById[id];
|
||||
};
|
||||
};
|
||||
|
||||
// export default {
|
||||
// // ...accountsReducer,
|
||||
// // testReducer,
|
||||
// }
|
||||
@@ -5,11 +5,16 @@ export default {
|
||||
ACCOUNTS_PAGE_SET: 'ACCOUNTS_PAGE_SET',
|
||||
ACCOUNTS_ITEMS_SET: 'ACCOUNTS_ITEMS_SET',
|
||||
ACCOUNT_SET: 'ACCOUNT_SET',
|
||||
ACCOUNT_DELETE: 'ACCOUNT_DELETE',
|
||||
ACCOUNT_FORM_ERRORS: 'ACCOUNT_FORM_ERRORS',
|
||||
CLEAR_ACCOUNT_FORM_ERRORS: 'CLEAR_ACCOUNT_FORM_ERRORS',
|
||||
|
||||
ACCOUNT_BULK_ACTION_ADD: 'ACCOUNT_BULK_ACTION_ADD',
|
||||
ACCOUNT_BULK_ACTION_REMOVE: 'ACCOUNT_BULK_ACTION_REMOVE',
|
||||
ACCOUNTS_SELECTED_ROWS_SET: 'ACCOUNTS_SELECTED_ROWS_SET',
|
||||
|
||||
ACCOUNTS_SET_CURRENT_VIEW: 'ACCOUNTS_SET_CURRENT_VIEW',
|
||||
|
||||
ACCOUNTS_TABLE_QUERY_SET: 'ACCOUNTS_TABLE_QUERY_SET',
|
||||
ACCOUNTS_TABLE_QUERIES_SET: 'ACCOUNTS_TABLE_QUERIES_SET',
|
||||
|
||||
ACCOUNTS_TABLE_LOADING: 'ACCOUNTS_TABLE_LOADING',
|
||||
};
|
||||
28
client/src/store/queryReducers.js
Normal file
28
client/src/store/queryReducers.js
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
|
||||
export const createTableQueryReducers =
|
||||
(resourceName = '', reducer) =>
|
||||
(state, action) => {
|
||||
const RESOURCE_NAME = resourceName.toUpperCase();
|
||||
|
||||
switch (action.type) {
|
||||
case `${RESOURCE_NAME}_TABLE_QUERY_SET`:
|
||||
return {
|
||||
...state,
|
||||
tableQuery: {
|
||||
...state.tableQuery,
|
||||
[state.key]: state.value,
|
||||
}
|
||||
};
|
||||
case `${RESOURCE_NAME}_TABLE_QUERIES_ADD`:
|
||||
return {
|
||||
...state,
|
||||
tableQuery: {
|
||||
...state.tableQuery,
|
||||
...action.queries
|
||||
},
|
||||
};
|
||||
default:
|
||||
return reducer(state, action);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,107 @@
|
||||
|
||||
.bigcapital-datatable{
|
||||
|
||||
display: block;
|
||||
// max-width: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
|
||||
table {
|
||||
.table {
|
||||
text-align: left;
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
display: block;
|
||||
// width: 100%;
|
||||
|
||||
thead{
|
||||
th{
|
||||
height: 48px;
|
||||
padding: 0.5rem 1.5rem;
|
||||
.thead{
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
.th{
|
||||
|
||||
|
||||
padding: 1rem 1.5rem;
|
||||
background: #F8FAFA;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
|
||||
border-bottom: 1px solid rgb(224, 224, 224);
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
:last-child {
|
||||
td {
|
||||
.tr {
|
||||
display: flex;
|
||||
flex: 1 0 auto;
|
||||
|
||||
&:last-child {
|
||||
.td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
.th,
|
||||
.td {
|
||||
box-sizing: border-box;
|
||||
flex: 0 0 auto;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
|
||||
margin: 0;
|
||||
padding: 0.5rem;
|
||||
|
||||
:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.bp3-control{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.resizer {
|
||||
display: inline-block;
|
||||
background: transparent;
|
||||
width: 10px;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transform: translateX(50%);
|
||||
z-index: 1;
|
||||
touch-action:none;
|
||||
|
||||
&.isResizing {
|
||||
// background: red;
|
||||
}
|
||||
|
||||
.inner-resizer{
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
background: #ececec;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
&.isResizing .inner-resizer{
|
||||
background: #1183DA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody{
|
||||
.tbody{
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
|
||||
.tr .td{
|
||||
border-bottom: 1px solid #E0E2E2;
|
||||
}
|
||||
|
||||
.#{$ns}-button--action{
|
||||
.td.actions .#{$ns}-button{
|
||||
background: #E6EFFB;
|
||||
border: 0;
|
||||
box-shadow: 0 0 0;
|
||||
padding: 5px 15px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,12 @@
|
||||
|
||||
.dashboard__insider--accounts-chart{
|
||||
|
||||
.e-grid{
|
||||
.e-gridheader{
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.account-normal{
|
||||
|
||||
.bigcapital-datatable{
|
||||
.normal{
|
||||
.#{$ns}-icon{
|
||||
color: #666;
|
||||
padding-left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-row{
|
||||
width: 0;
|
||||
|
||||
.#{$ns}-control{
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,8 +196,7 @@
|
||||
width: calc(100% - 220px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
|
||||
margin-left: 220px;
|
||||
}
|
||||
|
||||
&__insider{
|
||||
|
||||
@@ -10,6 +10,8 @@ $sidebar-popover-submenu-bg: rgb(1, 20, 62);
|
||||
background: $sidebar-background;
|
||||
color: $sidebar-text-color;
|
||||
width: $sidebar-width;
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
|
||||
&__inner{
|
||||
overflow-y: scroll;
|
||||
|
||||
Reference in New Issue
Block a user