mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 21:00:31 +00:00
feat: fix a bunch of bugs.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import React, { useMemo, useState, useCallback } from 'react';
|
||||
import Icon from 'components/Icon';
|
||||
import {
|
||||
Button,
|
||||
@@ -29,6 +29,8 @@ function AccountsActionsBar({
|
||||
getResourceFields,
|
||||
addAccountsTableQueries,
|
||||
onFilterChanged,
|
||||
onBulkDelete,
|
||||
onBulkArchive,
|
||||
}) {
|
||||
const {path} = useRouteMatch();
|
||||
const onClickNewAccount = () => { openDialog('account-form', {}); };
|
||||
@@ -51,6 +53,15 @@ function AccountsActionsBar({
|
||||
onFilterChanged && onFilterChanged(filterConditions);
|
||||
},
|
||||
});
|
||||
|
||||
const handleBulkArchive = useCallback(() => {
|
||||
onBulkArchive && onBulkArchive(selectedRows.map(r => r.id));
|
||||
}, [onBulkArchive, selectedRows]);
|
||||
|
||||
const handleBulkDelete = useCallback(() => {
|
||||
onBulkDelete && onBulkDelete(selectedRows.map(r => r.id));
|
||||
}, [onBulkDelete, selectedRows]);
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
@@ -77,6 +88,7 @@ function AccountsActionsBar({
|
||||
onClick={onClickNewAccount}
|
||||
/>
|
||||
<Popover
|
||||
minimal={true}
|
||||
content={filterDropdown}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}>
|
||||
@@ -92,6 +104,7 @@ function AccountsActionsBar({
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon='archive' iconSize={15} />}
|
||||
text='Archive'
|
||||
onClick={handleBulkArchive}
|
||||
/>
|
||||
)}
|
||||
{hasSelectedRows && (
|
||||
@@ -100,6 +113,7 @@ function AccountsActionsBar({
|
||||
icon={<Icon icon='trash' iconSize={15} />}
|
||||
text='Delete'
|
||||
intent={Intent.DANGER}
|
||||
onClick={handleBulkDelete}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
|
||||
@@ -19,6 +19,7 @@ import ViewConnect from 'connectors/View.connector';
|
||||
import LoadingIndicator from 'components/LoadingIndicator';
|
||||
import DataTable from 'components/DataTable';
|
||||
import Money from 'components/Money';
|
||||
import { useUpdateEffect } from 'hooks';
|
||||
|
||||
function AccountsDataTable({
|
||||
accounts,
|
||||
@@ -31,9 +32,16 @@ function AccountsDataTable({
|
||||
setTopbarEditView,
|
||||
accountsLoading,
|
||||
onFetchData,
|
||||
setSelectedRowsAccounts,
|
||||
onSelectedRowsChange
|
||||
}) {
|
||||
const { custom_view_id: customViewId } = useParams();
|
||||
const {custom_view_id: customViewId} = useParams();
|
||||
const [initialMount, setInitialMount] = useState(false);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
if (!accountsLoading) {
|
||||
setInitialMount(true);
|
||||
}
|
||||
}, [accountsLoading, setInitialMount]);
|
||||
|
||||
useEffect(() => {
|
||||
const viewMeta = getViewItem(customViewId);
|
||||
@@ -52,12 +60,20 @@ function AccountsDataTable({
|
||||
openDialog('account-form', { action: 'edit', id: account.id });
|
||||
}, [openDialog]);
|
||||
|
||||
const actionMenuList = useCallback(account => (
|
||||
const handleNewParentAccount = useCallback((account) => {
|
||||
openDialog('account-form', { action: 'new_child', id: account.id });
|
||||
}, [openDialog]);
|
||||
|
||||
const actionMenuList = useCallback((account) => (
|
||||
<Menu>
|
||||
<MenuItem text='View Details' />
|
||||
<MenuDivider />
|
||||
<MenuItem text='Edit Account' onClick={handleEditAccount(account)} />
|
||||
<MenuItem text='New Account' />
|
||||
<MenuItem
|
||||
text='Edit Account'
|
||||
onClick={handleEditAccount(account)} />
|
||||
<MenuItem
|
||||
text='New Account'
|
||||
onClick={() => handleNewParentAccount(account)} />
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text='Inactivate Account'
|
||||
@@ -152,17 +168,22 @@ function AccountsDataTable({
|
||||
onFetchData && onFetchData(...params);
|
||||
}, []);
|
||||
|
||||
const handleSelectedRowsChange = useCallback((selectedRows) => {
|
||||
onSelectedRowsChange && onSelectedRowsChange(selectedRows.map(s => s.original));
|
||||
}, [onSelectedRowsChange]);
|
||||
|
||||
return (
|
||||
<LoadingIndicator loading={accountsLoading} spinnerSize={30}>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={accounts}
|
||||
onFetchData={handleDatatableFetchData}
|
||||
manualSortBy={true}
|
||||
selectionColumn={selectionColumn}
|
||||
expandable={true}
|
||||
treeGraph={true} />
|
||||
</LoadingIndicator>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={accounts}
|
||||
onFetchData={handleDatatableFetchData}
|
||||
manualSortBy={true}
|
||||
selectionColumn={selectionColumn}
|
||||
expandable={true}
|
||||
treeGraph={true}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
loading={accountsLoading && !initialMount}
|
||||
spinnerProps={{size: 30}} />
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import PrivateRoute from 'components/PrivateRoute';
|
||||
import Authentication from 'components/Authentication';
|
||||
import Dashboard from 'components/Dashboard/Dashboard';
|
||||
import { isAuthenticated } from 'store/authentication/authentication.reducer'
|
||||
import Progress from 'components/NProgress/Progress';
|
||||
import messages from 'lang/en';
|
||||
import 'style/App.scss';
|
||||
|
||||
@@ -22,6 +23,8 @@ function App(props) {
|
||||
<PrivateRoute isAuthenticated={props.isAuthorized} component={Dashboard} />
|
||||
</Router>
|
||||
</div>
|
||||
|
||||
<Progress isAnimating={props.isLoading} />
|
||||
</IntlProvider>
|
||||
);
|
||||
}
|
||||
@@ -37,5 +40,6 @@ App.propTypes = {
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
isAuthorized: isAuthenticated(state),
|
||||
isLoading: !!state.dashboard.requestsLoading,
|
||||
});
|
||||
export default connect(mapStateToProps)(App);
|
||||
@@ -6,7 +6,7 @@ export default function DashboardInsider({
|
||||
loading,
|
||||
children,
|
||||
name,
|
||||
mount = true,
|
||||
mount = false,
|
||||
}) {
|
||||
return (
|
||||
<div className={classnames({
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import React, {useEffect, useMemo, useCallback} from 'react';
|
||||
import React, {useEffect, useRef, useCallback} from 'react';
|
||||
import {
|
||||
useTable,
|
||||
useExpanded,
|
||||
useRowSelect,
|
||||
usePagination,
|
||||
useResizeColumns,
|
||||
useAsyncDebounce,
|
||||
useSortBy,
|
||||
useFlexLayout
|
||||
} from 'react-table'
|
||||
import {Checkbox} from '@blueprintjs/core';
|
||||
} from 'react-table';
|
||||
import { Checkbox, Spinner } from '@blueprintjs/core';
|
||||
import classnames from 'classnames';
|
||||
import { FixedSizeList } from 'react-window'
|
||||
import { ConditionalWrapper } from 'utils';
|
||||
import { useUpdateEffect } from 'hooks';
|
||||
|
||||
const IndeterminateCheckbox = React.forwardRef(
|
||||
({ indeterminate, ...rest }, ref) => {
|
||||
@@ -40,6 +40,8 @@ export default function DataTable({
|
||||
payload,
|
||||
expandable = false,
|
||||
expandToggleColumn = 2,
|
||||
noInitialFetch = false,
|
||||
spinnerProps = { size: 40 },
|
||||
}) {
|
||||
const {
|
||||
getTableProps,
|
||||
@@ -62,7 +64,7 @@ export default function DataTable({
|
||||
isAllRowsExpanded,
|
||||
|
||||
// Get the state from the instance
|
||||
state: { pageIndex, pageSize, sortBy, selectedRowIds },
|
||||
state: { pageIndex, pageSize, sortBy, selectedRowIds, selectedRows },
|
||||
} = useTable(
|
||||
{
|
||||
columns,
|
||||
@@ -115,12 +117,22 @@ export default function DataTable({
|
||||
])
|
||||
}
|
||||
);
|
||||
|
||||
const isInitialMount = useRef(noInitialFetch);
|
||||
|
||||
// When these table states change, fetch new data!
|
||||
useEffect(() => {
|
||||
onFetchData && onFetchData({ pageIndex, pageSize, sortBy })
|
||||
if (isInitialMount.current) {
|
||||
isInitialMount.current = false;
|
||||
} else {
|
||||
onFetchData && onFetchData({ pageIndex, pageSize, sortBy })
|
||||
}
|
||||
}, [pageIndex, pageSize, sortBy]);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
onSelectedRowsChange && onSelectedRowsChange(selectedFlatRows);
|
||||
}, [selectedRowIds, onSelectedRowsChange]);
|
||||
|
||||
// Renders table cell.
|
||||
const RenderCell = useCallback(({ row, cell, index }) => (
|
||||
<ConditionalWrapper
|
||||
@@ -188,13 +200,18 @@ export default function DataTable({
|
||||
{RenderVirtualizedRows}
|
||||
</FixedSizeList>
|
||||
) : RenderPage();
|
||||
}, [fixedSizeHeight, rows, fixedItemSize, virtualizedRows, RenderVirtualizedRows, RenderPage]);
|
||||
}, [fixedSizeHeight, rows, fixedItemSize, virtualizedRows,
|
||||
RenderVirtualizedRows, RenderPage]);
|
||||
|
||||
return (
|
||||
<div className={classnames(
|
||||
'bigcapital-datatable',
|
||||
className,
|
||||
{'has-sticky-header': stickyHeader, 'is-expandable': expandable})}>
|
||||
{
|
||||
'has-sticky-header': stickyHeader,
|
||||
'is-expandable': expandable,
|
||||
'has-virtualized-rows': virtualizedRows,
|
||||
})}>
|
||||
<div {...getTableProps()} className="table">
|
||||
<div className="thead">
|
||||
{headerGroups.map(headerGroup => (
|
||||
@@ -240,13 +257,16 @@ export default function DataTable({
|
||||
))}
|
||||
</div>
|
||||
<div {...getTableBodyProps()} className="tbody">
|
||||
{ RenderTBody() }
|
||||
|
||||
{ (page.length === 0) && (
|
||||
{ !loading && RenderTBody() }
|
||||
|
||||
{ !loading && (page.length === 0) && (
|
||||
<div className={'tr no-results'}>
|
||||
<div class="td">{ noResults }</div>
|
||||
</div>
|
||||
)}
|
||||
{ loading && (
|
||||
<div class="loading"><Spinner size={spinnerProps.size} /></div>
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, {useEffect, useMemo} from 'react';
|
||||
import React, {useEffect, useMemo, useCallback, useRef} from 'react';
|
||||
import {
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
@@ -10,67 +10,94 @@ import {
|
||||
import { useFormik } from 'formik';
|
||||
import { isEqual } from 'lodash';
|
||||
import { usePrevious } from 'react-use';
|
||||
import { debounce } from 'lodash';
|
||||
import Icon from 'components/Icon';
|
||||
import { checkRequiredProperties } from 'utils';
|
||||
|
||||
export default function FilterDropdown({
|
||||
fields,
|
||||
onFilterChange,
|
||||
}) {
|
||||
const conditionalsItems = [
|
||||
const conditionalsItems = useMemo(() => [
|
||||
{ value: 'and', label: 'AND' },
|
||||
{ value: 'or', label: 'OR' },
|
||||
];
|
||||
const resourceFields = [
|
||||
], []);
|
||||
|
||||
const resourceFields = useMemo(() => [
|
||||
...fields.map((field) => ({ value: field.key, label: field.label_name, })),
|
||||
];
|
||||
const compatatorsItems = [
|
||||
], [fields]);
|
||||
|
||||
const compatatorsItems = useMemo(() => [
|
||||
{value: '', label: 'Select a compatator'},
|
||||
{value: 'equals', label: 'Equals'},
|
||||
{value: 'not_equal', label: 'Not Equal'},
|
||||
{value: 'contain', label: 'Contain'},
|
||||
{value: 'not_contain', label: 'Not Contain'},
|
||||
];
|
||||
const defaultFilterCondition = {
|
||||
], []);
|
||||
|
||||
const defaultFilterCondition = useMemo(() => ({
|
||||
condition: 'and',
|
||||
field_key: fields.length > 0 ? fields[0].key : '',
|
||||
compatator: 'equals',
|
||||
value: '',
|
||||
};
|
||||
const formik = useFormik({
|
||||
}), [fields]);
|
||||
|
||||
const {
|
||||
setFieldValue,
|
||||
getFieldProps,
|
||||
values,
|
||||
errors,
|
||||
touched,
|
||||
} = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
conditions: [ defaultFilterCondition ],
|
||||
},
|
||||
});
|
||||
|
||||
const onClickNewFilter = () => {
|
||||
formik.setFieldValue('conditions', [
|
||||
...formik.values.conditions, defaultFilterCondition,
|
||||
const onClickNewFilter = useCallback(() => {
|
||||
setFieldValue('conditions', [
|
||||
...values.conditions, defaultFilterCondition,
|
||||
]);
|
||||
};
|
||||
}, [values, defaultFilterCondition, setFieldValue]);
|
||||
|
||||
const filteredFilterConditions = useMemo(() => {
|
||||
return formik.values.conditions.filter(condition => !!condition.value);
|
||||
}, [formik.values.conditions]);
|
||||
const requiredProps = ['field_key', 'condition', 'compatator', 'value'];
|
||||
|
||||
return values.conditions
|
||||
.filter((condition) =>
|
||||
!checkRequiredProperties(condition, requiredProps));
|
||||
}, [values.conditions]);
|
||||
|
||||
const prevConditions = usePrevious(filteredFilterConditions);
|
||||
|
||||
const onClickRemoveCondition = (index) => () => {
|
||||
if (formik.values.conditions.length === 1) { return; }
|
||||
|
||||
const conditions = [ ...formik.values.conditions ];
|
||||
conditions.splice(index, 1);
|
||||
formik.setFieldValue('conditions', [ ...conditions ]);
|
||||
}
|
||||
|
||||
const onFilterChangeThrottled = useRef(debounce((conditions) => {
|
||||
onFilterChange && onFilterChange(conditions);
|
||||
}, 1000));
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEqual(filteredFilterConditions, prevConditions)) {
|
||||
onFilterChange(filteredFilterConditions);
|
||||
if (!isEqual(prevConditions, filteredFilterConditions) && prevConditions) {
|
||||
onFilterChangeThrottled.current(filteredFilterConditions);
|
||||
}
|
||||
}, [filteredFilterConditions])
|
||||
}, [filteredFilterConditions]);
|
||||
|
||||
// Handle click remove condition.
|
||||
const onClickRemoveCondition = (index) => () => {
|
||||
if (values.conditions.length === 1) {
|
||||
setFieldValue('conditions', [
|
||||
defaultFilterCondition,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
const conditions = [ ...values.conditions ];
|
||||
conditions.splice(index, 1);
|
||||
setFieldValue('conditions', [ ...conditions ]);
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="filter-dropdown">
|
||||
<div class="filter-dropdown__body">
|
||||
{formik.values.conditions.map((condition, index) => (
|
||||
{values.conditions.map((condition, index) => (
|
||||
<div class="filter-dropdown__condition">
|
||||
<FormGroup
|
||||
className={'form-group--condition'}>
|
||||
@@ -78,7 +105,7 @@ export default function FilterDropdown({
|
||||
options={conditionalsItems}
|
||||
className={Classes.FILL}
|
||||
disabled={index > 1}
|
||||
{...formik.getFieldProps(`conditions[${index}].condition`)} />
|
||||
{...getFieldProps(`conditions[${index}].condition`)} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
@@ -87,7 +114,7 @@ export default function FilterDropdown({
|
||||
options={resourceFields}
|
||||
value={1}
|
||||
className={Classes.FILL}
|
||||
{...formik.getFieldProps(`conditions[${index}].field_key`)} />
|
||||
{...getFieldProps(`conditions[${index}].field_key`)} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
@@ -95,14 +122,14 @@ export default function FilterDropdown({
|
||||
<HTMLSelect
|
||||
options={compatatorsItems}
|
||||
className={Classes.FILL}
|
||||
{...formik.getFieldProps(`conditions[${index}].compatator`)} />
|
||||
{...getFieldProps(`conditions[${index}].compatator`)} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
className={'form-group--value'}>
|
||||
<InputGroup
|
||||
placeholder="Value"
|
||||
{...formik.getFieldProps(`conditions[${index}].value`)} />
|
||||
{...getFieldProps(`conditions[${index}].value`)} />
|
||||
</FormGroup>
|
||||
|
||||
<Button
|
||||
|
||||
@@ -39,6 +39,8 @@ export default class Icon extends React.Component{
|
||||
color,
|
||||
htmlTitle,
|
||||
iconSize = Icon.SIZE_STANDARD,
|
||||
height,
|
||||
width,
|
||||
intent,
|
||||
title = icon,
|
||||
tagName = "span",
|
||||
@@ -57,6 +59,9 @@ export default class Icon extends React.Component{
|
||||
const classes = classNames(Classes.ICON, Classes.iconClass(icon), Classes.intentClass(intent), className);
|
||||
const viewBox = iconPath.viewBox;
|
||||
|
||||
const computedHeight = height || iconSize;
|
||||
const computedWidth = width || iconSize;
|
||||
|
||||
return React.createElement(
|
||||
tagName,
|
||||
{
|
||||
@@ -64,7 +69,7 @@ export default class Icon extends React.Component{
|
||||
className: classes,
|
||||
title: htmlTitle,
|
||||
},
|
||||
<svg fill={color} data-icon={icon} width={iconSize} height={iconSize} viewBox={viewBox}>
|
||||
<svg fill={color} data-icon={icon} width={computedWidth} height={computedHeight} viewBox={viewBox}>
|
||||
{title && <desc>{title}</desc>}
|
||||
{paths}
|
||||
</svg>,
|
||||
|
||||
38
client/src/components/NProgress/Bar.js
Normal file
38
client/src/components/NProgress/Bar.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import * as React from 'react'
|
||||
|
||||
const Bar = ({ progress, animationDuration }) => (
|
||||
<div
|
||||
style={{
|
||||
background: '#79b8ff',
|
||||
height: 3,
|
||||
left: 0,
|
||||
marginLeft: `${(-1 + progress) * 100}%`,
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
transition: `margin-left ${animationDuration}ms linear`,
|
||||
width: '100%',
|
||||
zIndex: 1031,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
boxShadow: '0 0 10px #79b8ff, 0 0 5px #79b8ff',
|
||||
display: 'block',
|
||||
height: '100%',
|
||||
opacity: 1,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
transform: 'rotate(3deg) translate(0px, -4px)',
|
||||
width: 100,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
Bar.propTypes = {
|
||||
animationDuration: PropTypes.number.isRequired,
|
||||
progress: PropTypes.number.isRequired,
|
||||
}
|
||||
|
||||
export default Bar;
|
||||
22
client/src/components/NProgress/Container.js
Normal file
22
client/src/components/NProgress/Container.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import * as React from 'react'
|
||||
|
||||
const Container = ({ children, isFinished, animationDuration }) => (
|
||||
<div
|
||||
style={{
|
||||
opacity: isFinished ? 0 : 1,
|
||||
pointerEvents: 'none',
|
||||
transition: `opacity ${animationDuration}ms linear`,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
||||
Container.propTypes = {
|
||||
animationDuration: PropTypes.number.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
isFinished: PropTypes.bool.isRequired,
|
||||
}
|
||||
|
||||
export default Container;
|
||||
27
client/src/components/NProgress/Progress.js
Normal file
27
client/src/components/NProgress/Progress.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import { useNProgress } from '@tanem/react-nprogress'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import Bar from './Bar'
|
||||
import Container from './Container'
|
||||
import Spinner from './Spinner'
|
||||
|
||||
const Progress = ({
|
||||
isAnimating,
|
||||
minimum = 0.2
|
||||
}) => {
|
||||
const { animationDuration, isFinished, progress } = useNProgress({
|
||||
isAnimating, minimum,
|
||||
});
|
||||
|
||||
return (
|
||||
<Container isFinished={isFinished} animationDuration={animationDuration}>
|
||||
<Bar progress={progress} animationDuration={animationDuration} />
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
Progress.propTypes = {
|
||||
isAnimating: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default Progress;
|
||||
29
client/src/components/NProgress/Spinner.js
Normal file
29
client/src/components/NProgress/Spinner.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import * as React from 'react'
|
||||
|
||||
const Spinner = () => (
|
||||
<div
|
||||
style={{
|
||||
display: 'block',
|
||||
position: 'fixed',
|
||||
right: 15,
|
||||
top: 15,
|
||||
zIndex: 1031,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
animation: '400ms linear infinite spinner',
|
||||
borderBottom: '2px solid transparent',
|
||||
borderLeft: '2px solid #29d',
|
||||
borderRadius: '50%',
|
||||
borderRight: '2px solid transparent',
|
||||
borderTop: '2px solid #29d',
|
||||
boxSizing: 'border-box',
|
||||
height: 18,
|
||||
width: 18,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default Spinner;
|
||||
@@ -1,23 +1,12 @@
|
||||
import React from 'react';
|
||||
import appMeta from 'config/app';
|
||||
import Icon from 'components/Icon';
|
||||
|
||||
export default function() {
|
||||
return (
|
||||
<div className="sidebar__head">
|
||||
<div className="sidebar__head-logo">
|
||||
|
||||
</div>
|
||||
|
||||
<div className="sidebar__head-company-meta">
|
||||
<div className="company-name">
|
||||
{ appMeta.app_name }
|
||||
</div>
|
||||
|
||||
<div className="company-meta">
|
||||
<span class="version">
|
||||
{ appMeta.app_version }
|
||||
</span>
|
||||
</div>
|
||||
<Icon icon={'bigcapital'} width={140} height={28} className="bigcapital--alt" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user