mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-22 07:40:32 +00:00
feat: fix items list datatable.
This commit is contained in:
52
client/src/components/ContextMenu.js
Normal file
52
client/src/components/ContextMenu.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import React, { memo } from 'react';
|
||||||
|
import { Popover, Position, Classes } from '@blueprintjs/core';
|
||||||
|
import { saveInvoke } from 'utils';
|
||||||
|
|
||||||
|
const POPPER_MODIFIERS = {
|
||||||
|
preventOverflow: { boundariesElement: 'viewport' },
|
||||||
|
};
|
||||||
|
|
||||||
|
function ContextMenu(props) {
|
||||||
|
const { bindMenu, isOpen, children, onClosed, popoverProps } = props;
|
||||||
|
|
||||||
|
const handleClosed = () => {
|
||||||
|
requestAnimationFrame(() => saveInvoke(onClosed));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInteraction = (nextOpenState) => {
|
||||||
|
if (!nextOpenState) {
|
||||||
|
// Delay the actual hiding till the event queue clears
|
||||||
|
// to avoid flicker of opening twice
|
||||||
|
requestAnimationFrame(() => saveInvoke(onClosed));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={Classes.CONTEXT_MENU_POPOVER_TARGET} {...bindMenu}>
|
||||||
|
<Popover
|
||||||
|
onClosed={handleClosed}
|
||||||
|
modifiers={POPPER_MODIFIERS}
|
||||||
|
content={children}
|
||||||
|
enforceFocus={true}
|
||||||
|
isOpen={isOpen}
|
||||||
|
minimal={true}
|
||||||
|
position={Position.RIGHT_TOP}
|
||||||
|
target={<div />}
|
||||||
|
usePortal={false}
|
||||||
|
onInteraction={handleInteraction}
|
||||||
|
{...popoverProps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ContextMenu, (prevProps, nextProps) => {
|
||||||
|
if (
|
||||||
|
prevProps.isOpen === nextProps.isOpen &&
|
||||||
|
prevProps.bindMenu.style === nextProps.bindMenu.style
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 Palantir Technologies, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// import classNames from 'classnames';
|
|
||||||
import * as React from "react";
|
|
||||||
import * as ReactDOM from "react-dom";
|
|
||||||
// import { polyfill } from "react-lifecycles-compat";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
Classes,
|
|
||||||
Position,
|
|
||||||
} from '@blueprintjs/core';
|
|
||||||
|
|
||||||
|
|
||||||
// import { IOverlayLifecycleProps } from "../overlay/overlay";
|
|
||||||
// import { Popover } from "../popover/popover";
|
|
||||||
// import { PopperModifiers } from "../popover/popoverSharedProps";
|
|
||||||
|
|
||||||
export interface IOffset {
|
|
||||||
left: number;
|
|
||||||
top: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IContextMenuState {
|
|
||||||
isOpen: boolean;
|
|
||||||
isDarkTheme: boolean;
|
|
||||||
menu?: JSX.Element;
|
|
||||||
offset?: IOffset;
|
|
||||||
onClose?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const POPPER_MODIFIERS = {
|
|
||||||
preventOverflow: { boundariesElement: "viewport" },
|
|
||||||
};
|
|
||||||
const TRANSITION_DURATION = 100;
|
|
||||||
|
|
||||||
// type IContextMenuProps = IOverlayLifecycleProps;
|
|
||||||
|
|
||||||
/* istanbul ignore next */
|
|
||||||
|
|
||||||
export default class ContextMenu extends React.PureComponent {
|
|
||||||
public state: IContextMenuState = {
|
|
||||||
isDarkTheme: false,
|
|
||||||
isOpen: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
public render() {
|
|
||||||
// prevent right-clicking in a context menu
|
|
||||||
const content = <div onContextMenu={this.cancelContextMenu}>{this.state.menu}</div>;
|
|
||||||
const popoverClassName = {};
|
|
||||||
|
|
||||||
// HACKHACK: workaround until we have access to Popper#scheduleUpdate().
|
|
||||||
// https://github.com/palantir/blueprint/issues/692
|
|
||||||
// Generate key based on offset so a new Popover instance is created
|
|
||||||
// when offset changes, to force recomputing position.
|
|
||||||
const key = this.state.offset === undefined ? "" : `${this.state.offset.left}x${this.state.offset.top}`;
|
|
||||||
|
|
||||||
// wrap the popover in a positioned div to make sure it is properly
|
|
||||||
// offset on the screen.
|
|
||||||
return (
|
|
||||||
<div className={Classes.CONTEXT_MENU_POPOVER_TARGET} style={this.state.offset}>
|
|
||||||
<Popover
|
|
||||||
{...this.props}
|
|
||||||
backdropProps={{ onContextMenu: this.handleBackdropContextMenu }}
|
|
||||||
content={content}
|
|
||||||
enforceFocus={false}
|
|
||||||
key={key}
|
|
||||||
hasBackdrop={true}
|
|
||||||
isOpen={this.state.isOpen}
|
|
||||||
minimal={true}
|
|
||||||
// modifiers={POPPER_MODIFIERS}
|
|
||||||
onInteraction={this.handlePopoverInteraction}
|
|
||||||
position={Position.RIGHT_TOP}
|
|
||||||
// popoverClassName={popoverClassName}
|
|
||||||
target={<div />}
|
|
||||||
transitionDuration={TRANSITION_DURATION}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public show(menu: JSX.Element, offset: IOffset, onClose?: () => void, isDarkTheme = false) {
|
|
||||||
this.setState({ isOpen: true, menu, offset, onClose, isDarkTheme });
|
|
||||||
}
|
|
||||||
|
|
||||||
public hide() {
|
|
||||||
this.state.onClose?.();
|
|
||||||
this.setState({ isOpen: false, onClose: undefined });
|
|
||||||
}
|
|
||||||
|
|
||||||
private cancelContextMenu = (e: React.SyntheticEvent<HTMLDivElement>) => e.preventDefault();
|
|
||||||
|
|
||||||
private handleBackdropContextMenu = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
||||||
// React function to remove from the event pool, useful when using a event within a callback
|
|
||||||
e.persist();
|
|
||||||
e.preventDefault();
|
|
||||||
// wait for backdrop to disappear so we can find the "real" element at event coordinates.
|
|
||||||
// timeout duration is equivalent to transition duration so we know it's animated out.
|
|
||||||
setTimeout(() => {
|
|
||||||
// retrigger context menu event at the element beneath the backdrop.
|
|
||||||
// if it has a `contextmenu` event handler then it'll be invoked.
|
|
||||||
// if it doesn't, no native menu will show (at least on OSX) :(
|
|
||||||
const newTarget = document.elementFromPoint(e.clientX, e.clientY);
|
|
||||||
const { view, ...newEventInit } = e;
|
|
||||||
newTarget?.dispatchEvent(new MouseEvent("contextmenu", newEventInit));
|
|
||||||
}, TRANSITION_DURATION);
|
|
||||||
};
|
|
||||||
|
|
||||||
private handlePopoverInteraction = (nextOpenState: boolean) => {
|
|
||||||
if (!nextOpenState) {
|
|
||||||
// delay the actual hiding till the event queue clears
|
|
||||||
// to avoid flicker of opening twice
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -10,22 +10,20 @@ import {
|
|||||||
Position,
|
Position,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
import { useHistory } from 'react-router-dom';
|
|
||||||
import { Icon } from 'components';
|
import { Icon } from 'components';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dashboard action views list.
|
||||||
|
*/
|
||||||
export default function DashboardActionViewsList({
|
export default function DashboardActionViewsList({
|
||||||
resourceName,
|
resourceName,
|
||||||
views,
|
views,
|
||||||
onChange,
|
onChange,
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
const handleClickViewItem = (view) => {
|
const handleClickViewItem = (view) => {
|
||||||
history.push(
|
|
||||||
view ? `/${resourceName}/${view.id}/custom_view` : '/accounts',
|
|
||||||
);
|
|
||||||
onChange && onChange(view);
|
onChange && onChange(view);
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewsMenuItems = views.map((view) => {
|
const viewsMenuItems = views.map((view) => {
|
||||||
return (
|
return (
|
||||||
<MenuItem onClick={() => handleClickViewItem(view)} text={view.name} />
|
<MenuItem onClick={() => handleClickViewItem(view)} text={view.name} />
|
||||||
|
|||||||
@@ -19,13 +19,14 @@ import { Icon, Hint, If } from 'components';
|
|||||||
import withSearch from 'containers/GeneralSearch/withSearch';
|
import withSearch from 'containers/GeneralSearch/withSearch';
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
import withDashboard from 'containers/Dashboard/withDashboard';
|
import withDashboard from 'containers/Dashboard/withDashboard';
|
||||||
|
import withSettings from 'containers/Settings/withSettings';
|
||||||
|
|
||||||
import QuickNewDropdown from 'containers/QuickNewDropdown/QuickNewDropdown';
|
import QuickNewDropdown from 'containers/QuickNewDropdown/QuickNewDropdown';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
function DashboardTopbar({
|
function DashboardTopbar({
|
||||||
// #withDashboard
|
// #withDashboard
|
||||||
pageTitle,
|
pageTitle,
|
||||||
pageSubtitle,
|
|
||||||
editViewId,
|
editViewId,
|
||||||
|
|
||||||
// #withDashboardActions
|
// #withDashboardActions
|
||||||
@@ -35,6 +36,9 @@ function DashboardTopbar({
|
|||||||
// #withDashboard
|
// #withDashboard
|
||||||
sidebarExpended,
|
sidebarExpended,
|
||||||
|
|
||||||
|
// #withSettings
|
||||||
|
organizationName,
|
||||||
|
|
||||||
// #withGlobalSearch
|
// #withGlobalSearch
|
||||||
openGlobalSearch,
|
openGlobalSearch,
|
||||||
}) {
|
}) {
|
||||||
@@ -100,11 +104,7 @@ function DashboardTopbar({
|
|||||||
</div>
|
</div>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
<If condition={pageSubtitle}>
|
<If condition={editViewId}>
|
||||||
<h3>{pageSubtitle}</h3>
|
|
||||||
</If>
|
|
||||||
|
|
||||||
<If condition={pageSubtitle && editViewId}>
|
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL + ' button--view-edit'}
|
className={Classes.MINIMAL + ' button--view-edit'}
|
||||||
icon={<Icon icon="pen" iconSize={13} />}
|
icon={<Icon icon="pen" iconSize={13} />}
|
||||||
@@ -117,6 +117,10 @@ function DashboardTopbar({
|
|||||||
<DashboardBreadcrumbs />
|
<DashboardBreadcrumbs />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* <div class="dashboard__organization-name">
|
||||||
|
{ organizationName }
|
||||||
|
</div> */}
|
||||||
|
|
||||||
<DashboardBackLink />
|
<DashboardBackLink />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -158,11 +162,13 @@ function DashboardTopbar({
|
|||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
withSearch,
|
withSearch,
|
||||||
withDashboard(({ pageTitle, pageSubtitle, editViewId, sidebarExpended }) => ({
|
withDashboard(({ pageTitle, editViewId, sidebarExpended }) => ({
|
||||||
pageTitle,
|
pageTitle,
|
||||||
pageSubtitle,
|
|
||||||
editViewId,
|
editViewId,
|
||||||
sidebarExpended,
|
sidebarExpended,
|
||||||
})),
|
})),
|
||||||
|
withSettings(({ organizationSettings }) => ({
|
||||||
|
organizationName: organizationSettings.name,
|
||||||
|
})),
|
||||||
withDashboardActions,
|
withDashboardActions,
|
||||||
)(DashboardTopbar);
|
)(DashboardTopbar);
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
import React, { useState, useRef, useMemo } from 'react';
|
import React, { useRef, useState, useEffect } from 'react';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Button, Tabs, Tab, Tooltip, Position } from '@blueprintjs/core';
|
import { Button, Tabs, Tab, Tooltip, Position } from '@blueprintjs/core';
|
||||||
import { debounce } from 'lodash';
|
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
|
import { debounce } from 'lodash';
|
||||||
import { If, Icon } from 'components';
|
import { If, Icon } from 'components';
|
||||||
import { saveInvoke } from 'utils';
|
import { saveInvoke } from 'utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dashboard views tabs.
|
||||||
|
*
|
||||||
|
*/
|
||||||
export default function DashboardViewsTabs({
|
export default function DashboardViewsTabs({
|
||||||
initialViewId = 0,
|
initialViewId = 0,
|
||||||
viewId,
|
currentViewId,
|
||||||
tabs,
|
tabs,
|
||||||
defaultTabText = <T id={'all'} />,
|
defaultTabText = <T id={'all'} />,
|
||||||
allTab = true,
|
allTab = true,
|
||||||
@@ -17,41 +21,38 @@ export default function DashboardViewsTabs({
|
|||||||
resourceName,
|
resourceName,
|
||||||
onNewViewTabClick,
|
onNewViewTabClick,
|
||||||
onChange,
|
onChange,
|
||||||
onTabClick,
|
OnThrottledChange,
|
||||||
|
throttleTime = 250,
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [currentView, setCurrentView] = useState(initialViewId || 0);
|
const [currentView, setCurrentView] = useState(initialViewId || 0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof currentViewId !== 'undefined' && currentViewId !== currentView) {
|
||||||
|
setCurrentView(currentViewId || 0);
|
||||||
|
}
|
||||||
|
}, [currentView, setCurrentView, currentViewId]);
|
||||||
|
|
||||||
|
const throttledOnChange = useRef(
|
||||||
|
debounce((viewId) => saveInvoke(OnThrottledChange, viewId), throttleTime),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Trigger `onChange` and `onThrottledChange` events.
|
||||||
|
const triggerOnChange = (viewId) => {
|
||||||
|
saveInvoke(onChange, viewId);
|
||||||
|
throttledOnChange.current(viewId);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handles click a new view.
|
||||||
const handleClickNewView = () => {
|
const handleClickNewView = () => {
|
||||||
history.push(`/custom_views/${resourceName}/new`);
|
history.push(`/custom_views/${resourceName}/new`);
|
||||||
onNewViewTabClick && onNewViewTabClick();
|
onNewViewTabClick && onNewViewTabClick();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTabClick = (viewId) => {
|
// Handle tabs change.
|
||||||
saveInvoke(onTabClick, viewId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const mappedTabs = useMemo(
|
|
||||||
() => tabs.map((tab) => ({ ...tab, onTabClick: handleTabClick })),
|
|
||||||
[tabs, handleTabClick],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleViewLinkClick = () => {
|
|
||||||
saveInvoke(onNewViewTabClick);
|
|
||||||
};
|
|
||||||
|
|
||||||
const debounceChangeHistory = useRef(
|
|
||||||
debounce((toUrl) => {
|
|
||||||
history.push(toUrl);
|
|
||||||
}, 250),
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleTabsChange = (viewId) => {
|
const handleTabsChange = (viewId) => {
|
||||||
const toPath = viewId ? `${viewId}/custom_view` : '';
|
|
||||||
debounceChangeHistory.current(`/${resourceName}/${toPath}`);
|
|
||||||
|
|
||||||
setCurrentView(viewId);
|
setCurrentView(viewId);
|
||||||
saveInvoke(onChange, viewId);
|
triggerOnChange(viewId)
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -62,13 +63,11 @@ export default function DashboardViewsTabs({
|
|||||||
className="tabs--dashboard-views"
|
className="tabs--dashboard-views"
|
||||||
onChange={handleTabsChange}
|
onChange={handleTabsChange}
|
||||||
>
|
>
|
||||||
{allTab && (
|
{allTab && <Tab id={0} title={defaultTabText} />}
|
||||||
<Tab id={0} title={defaultTabText} onClick={handleViewLinkClick} />
|
|
||||||
)}
|
|
||||||
{mappedTabs.map((tab) => (
|
|
||||||
<Tab id={tab.id} title={tab.name} onClick={handleTabClick} />
|
|
||||||
))}
|
|
||||||
|
|
||||||
|
{tabs.map((tab) => (
|
||||||
|
<Tab id={tab.id} title={tab.name} />
|
||||||
|
))}
|
||||||
<If condition={newViewTab}>
|
<If condition={newViewTab}>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={<T id={'create_a_new_view'} />}
|
content={<T id={'create_a_new_view'} />}
|
||||||
@@ -93,5 +92,6 @@ DashboardViewsTabs.propTypes = {
|
|||||||
|
|
||||||
onNewViewTabClick: PropTypes.func,
|
onNewViewTabClick: PropTypes.func,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
onTabClick: PropTypes.func,
|
OnThrottledChange: PropTypes.func,
|
||||||
|
throttleTime: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ export default function DataTable(props) {
|
|||||||
TableWrapperRenderer,
|
TableWrapperRenderer,
|
||||||
TableTBodyRenderer,
|
TableTBodyRenderer,
|
||||||
TablePaginationRenderer,
|
TablePaginationRenderer,
|
||||||
|
|
||||||
|
...restProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const selectionColumnObj = {
|
const selectionColumnObj = {
|
||||||
@@ -117,6 +119,8 @@ export default function DataTable(props) {
|
|||||||
autoResetSortBy,
|
autoResetSortBy,
|
||||||
autoResetFilters,
|
autoResetFilters,
|
||||||
autoResetRowState,
|
autoResetRowState,
|
||||||
|
|
||||||
|
...restProps
|
||||||
},
|
},
|
||||||
useSortBy,
|
useSortBy,
|
||||||
useExpanded,
|
useExpanded,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import classNames from 'classnames';
|
|||||||
import { ScrollSyncPane } from 'react-scroll-sync';
|
import { ScrollSyncPane } from 'react-scroll-sync';
|
||||||
import { If } from 'components';
|
import { If } from 'components';
|
||||||
import TableContext from './TableContext';
|
import TableContext from './TableContext';
|
||||||
|
import MaterialProgressBar from 'components/MaterialProgressBar';
|
||||||
|
|
||||||
function TableHeaderCell({ column, index }) {
|
function TableHeaderCell({ column, index }) {
|
||||||
const {
|
const {
|
||||||
@@ -77,7 +78,7 @@ function TableHeaderGroup({ headerGroup }) {
|
|||||||
export default function TableHeader() {
|
export default function TableHeader() {
|
||||||
const {
|
const {
|
||||||
table: { headerGroups, page },
|
table: { headerGroups, page },
|
||||||
props: { TableHeaderSkeletonRenderer, headerLoading },
|
props: { TableHeaderSkeletonRenderer, headerLoading, progressBarLoading },
|
||||||
} = useContext(TableContext);
|
} = useContext(TableContext);
|
||||||
|
|
||||||
if (headerLoading && TableHeaderSkeletonRenderer) {
|
if (headerLoading && TableHeaderSkeletonRenderer) {
|
||||||
@@ -89,6 +90,9 @@ export default function TableHeader() {
|
|||||||
{headerGroups.map((headerGroup) => (
|
{headerGroups.map((headerGroup) => (
|
||||||
<TableHeaderGroup headerGroup={headerGroup} />
|
<TableHeaderGroup headerGroup={headerGroup} />
|
||||||
))}
|
))}
|
||||||
|
<If condition={progressBarLoading}>
|
||||||
|
<MaterialProgressBar />
|
||||||
|
</If>
|
||||||
</div>
|
</div>
|
||||||
</ScrollSyncPane>
|
</ScrollSyncPane>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import React, { useCallback, useContext } from 'react';
|
import React, { useCallback, useContext } from 'react';
|
||||||
import { If, Pagination } from 'components';
|
import { If, Pagination } from 'components';
|
||||||
import TableContext from './TableContext';
|
import TableContext from './TableContext';
|
||||||
|
import { saveInvoke } from 'utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table pagination.
|
* Table pagination.
|
||||||
*/
|
*/
|
||||||
export default function TablePagination({}) {
|
export default function TablePagination() {
|
||||||
const {
|
const {
|
||||||
table: {
|
table: {
|
||||||
gotoPage,
|
gotoPage,
|
||||||
@@ -13,28 +14,39 @@ export default function TablePagination({}) {
|
|||||||
pageCount,
|
pageCount,
|
||||||
state: { pageIndex, pageSize },
|
state: { pageIndex, pageSize },
|
||||||
},
|
},
|
||||||
props: { pagination, loading },
|
props: { pagination, loading, onPaginationChange },
|
||||||
} = useContext(TableContext);
|
} = useContext(TableContext);
|
||||||
|
|
||||||
|
const triggerOnPaginationChange = useCallback((payload) => {
|
||||||
|
saveInvoke(onPaginationChange, payload)
|
||||||
|
}, [onPaginationChange]);
|
||||||
|
|
||||||
|
// Handles the page changing.
|
||||||
const handlePageChange = useCallback(
|
const handlePageChange = useCallback(
|
||||||
(currentPage) => {
|
({ page, pageSize }) => {
|
||||||
gotoPage(currentPage - 1);
|
const pageIndex = page - 1;
|
||||||
|
|
||||||
|
gotoPage(pageIndex);
|
||||||
|
triggerOnPaginationChange({ page, pageSize });
|
||||||
},
|
},
|
||||||
[gotoPage],
|
[gotoPage, triggerOnPaginationChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Handles the page size changing.
|
||||||
const handlePageSizeChange = useCallback(
|
const handlePageSizeChange = useCallback(
|
||||||
(pageSize, currentPage) => {
|
({ pageSize, page }) => {
|
||||||
gotoPage(0);
|
gotoPage(0);
|
||||||
setPageSize(pageSize);
|
setPageSize(pageSize);
|
||||||
|
|
||||||
|
triggerOnPaginationChange({ page, pageSize });
|
||||||
},
|
},
|
||||||
[gotoPage, setPageSize],
|
[gotoPage, setPageSize, triggerOnPaginationChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<If condition={pagination && !loading}>
|
<If condition={pagination && !loading}>
|
||||||
<Pagination
|
<Pagination
|
||||||
initialPage={pageIndex + 1}
|
currentPage={pageIndex + 1}
|
||||||
total={pageSize * pageCount}
|
total={pageSize * pageCount}
|
||||||
size={pageSize}
|
size={pageSize}
|
||||||
onPageChange={handlePageChange}
|
onPageChange={handlePageChange}
|
||||||
|
|||||||
@@ -1,32 +1,36 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { ContextMenu } from '@blueprintjs/core';
|
import useContextMenu from 'react-use-context-menu';
|
||||||
|
|
||||||
import TableContext from './TableContext';
|
import TableContext from './TableContext';
|
||||||
import { saveInvoke } from 'utils';
|
import { saveInvoke } from 'utils';
|
||||||
|
import { ContextMenu } from 'components';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table row.
|
* Table row.
|
||||||
*/
|
*/
|
||||||
export default function TableRow({ row, className, style }) {
|
export default function TableRow({ row, className, style }) {
|
||||||
const {
|
const {
|
||||||
props: { TableCellRenderer, rowContextMenu, rowClassNames },
|
props: {
|
||||||
|
TableCellRenderer,
|
||||||
|
rowContextMenu,
|
||||||
|
rowClassNames,
|
||||||
|
ContextMenu: ContextMenuContent,
|
||||||
|
},
|
||||||
|
table,
|
||||||
} = useContext(TableContext);
|
} = useContext(TableContext);
|
||||||
|
|
||||||
// Handle rendering row context menu.
|
const [
|
||||||
const handleRowContextMenu = (row) => (e) => {
|
bindMenu,
|
||||||
if (typeof rowContextMenu === 'function') {
|
bindMenuItem,
|
||||||
e.preventDefault();
|
useContextTrigger,
|
||||||
const tr = e.currentTarget.closest('.tr');
|
{ coords, setVisible, isVisible },
|
||||||
tr.classList.add('is-context-menu-active');
|
] = useContextMenu();
|
||||||
|
|
||||||
const DropdownEl = rowContextMenu({ row });
|
const [bindTrigger] = useContextTrigger({
|
||||||
|
collect: () => 'Title',
|
||||||
ContextMenu.show(DropdownEl, { left: e.clientX, top: e.clientY }, () => {
|
|
||||||
tr.classList.remove('is-context-menu-active');
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -40,12 +44,21 @@ export default function TableRow({ row, className, style }) {
|
|||||||
className,
|
className,
|
||||||
),
|
),
|
||||||
style,
|
style,
|
||||||
onContextMenu: handleRowContextMenu(row)
|
|
||||||
})}
|
})}
|
||||||
|
{...bindTrigger}
|
||||||
>
|
>
|
||||||
{row.cells.map((cell, index) => (
|
{row.cells.map((cell, index) => (
|
||||||
<TableCellRenderer cell={cell} row={row} index={index + 1} />
|
<TableCellRenderer cell={cell} row={row} index={index + 1} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
<ContextMenu
|
||||||
|
bindMenu={bindMenu}
|
||||||
|
isOpen={isVisible}
|
||||||
|
coords={coords}
|
||||||
|
onClosed={() => setVisible(false)}
|
||||||
|
>
|
||||||
|
<ContextMenuContent {...table} row={row} />
|
||||||
|
</ContextMenu>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
13
client/src/components/MaterialProgressBar.js
Normal file
13
client/src/components/MaterialProgressBar.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import 'style/components/MaterialProgressBar.scss';
|
||||||
|
|
||||||
|
export default function MaterialProgressBar() {
|
||||||
|
return (
|
||||||
|
<div class="progress-container">
|
||||||
|
<div class="progress-materializecss">
|
||||||
|
<div class="indeterminate"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useReducer, useEffect } from 'react';
|
import React, { useReducer, useEffect } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Button, ButtonGroup, Intent, HTMLSelect, } from '@blueprintjs/core';
|
import { Button, ButtonGroup, Intent, HTMLSelect } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { range } from 'lodash';
|
import { range } from 'lodash';
|
||||||
@@ -8,6 +8,12 @@ import { Icon } from 'components';
|
|||||||
|
|
||||||
import 'style/components/DataTable/Pagination.scss';
|
import 'style/components/DataTable/Pagination.scss';
|
||||||
|
|
||||||
|
const TYPE = {
|
||||||
|
PAGE_CHANGE: 'PAGE_CHANGE',
|
||||||
|
PAGE_SIZE_CHANGE: 'PAGE_SIZE_CHANGE',
|
||||||
|
INITIALIZE: 'INITIALIZE',
|
||||||
|
};
|
||||||
|
|
||||||
const getState = ({ currentPage, size, total }) => {
|
const getState = ({ currentPage, size, total }) => {
|
||||||
const totalPages = Math.ceil(total / size);
|
const totalPages = Math.ceil(total / size);
|
||||||
const visibleItems = 5;
|
const visibleItems = 5;
|
||||||
@@ -50,11 +56,6 @@ const getState = ({ currentPage, size, total }) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const TYPE = {
|
|
||||||
PAGE_CHANGE: 'PAGE_CHANGE',
|
|
||||||
PAGE_SIZE_CHANGE: 'PAGE_SIZE_CHANGE',
|
|
||||||
INITIALIZE: 'INITIALIZE',
|
|
||||||
};
|
|
||||||
const reducer = (state, action) => {
|
const reducer = (state, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case TYPE.PAGE_CHANGE:
|
case TYPE.PAGE_CHANGE:
|
||||||
@@ -71,7 +72,7 @@ const reducer = (state, action) => {
|
|||||||
});
|
});
|
||||||
case TYPE.INITIALIZE:
|
case TYPE.INITIALIZE:
|
||||||
return getState({
|
return getState({
|
||||||
currentPage: state.currentPage,
|
currentPage: action.page,
|
||||||
size: action.size,
|
size: action.size,
|
||||||
total: action.total,
|
total: action.total,
|
||||||
});
|
});
|
||||||
@@ -80,43 +81,48 @@ const reducer = (state, action) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Pagination = ({
|
function Pagination({
|
||||||
initialPage,
|
currentPage,
|
||||||
total,
|
total,
|
||||||
size,
|
size,
|
||||||
pageSizesOptions = [5, 12, 20, 30, 50, 75, 100, 150],
|
pageSizesOptions = [5, 12, 20, 30, 50, 75, 100, 150],
|
||||||
onPageChange,
|
onPageChange,
|
||||||
onPageSizeChange,
|
onPageSizeChange,
|
||||||
}) => {
|
}) {
|
||||||
const [state, dispatch] = useReducer(
|
const [state, dispatch] = useReducer(
|
||||||
reducer,
|
reducer,
|
||||||
{ currentPage: initialPage, total, size },
|
{ currentPage, total, size },
|
||||||
getState,
|
getState,
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'INITIALIZE',
|
type: TYPE.INITIALIZE,
|
||||||
total,
|
total,
|
||||||
size,
|
size,
|
||||||
|
page: currentPage,
|
||||||
});
|
});
|
||||||
}, [total, size]);
|
}, [total, size, currentPage]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="pagination">
|
<div class="pagination">
|
||||||
<div class="pagination__buttons-group">
|
<div class="pagination__buttons-group">
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<Button
|
<Button
|
||||||
disabled={state.currentPage === 1}
|
disabled={state.currentPage <= 1}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch({ type: 'PAGE_CHANGE', page: state.currentPage - 1 });
|
dispatch({ type: 'PAGE_CHANGE', page: state.currentPage - 1 });
|
||||||
onPageChange(state.currentPage - 1);
|
|
||||||
|
const page = state.currentPage - 1;
|
||||||
|
const { size: pageSize } = state;
|
||||||
|
|
||||||
|
onPageChange({ page, pageSize });
|
||||||
}}
|
}}
|
||||||
minimal={true}
|
minimal={true}
|
||||||
className={'pagination__item pagination__item--previous'}
|
className={'pagination__item pagination__item--previous'}
|
||||||
icon={<Icon icon={'arrow-back-24'} iconSize={12} />}
|
icon={<Icon icon={'arrow-back-24'} iconSize={12} />}
|
||||||
>
|
>
|
||||||
<T id='previous' />
|
<T id="previous" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{state.pages.map((page) => (
|
{state.pages.map((page) => (
|
||||||
@@ -126,7 +132,9 @@ const Pagination = ({
|
|||||||
disabled={state.currentPage === page}
|
disabled={state.currentPage === page}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch({ type: 'PAGE_CHANGE', page });
|
dispatch({ type: 'PAGE_CHANGE', page });
|
||||||
onPageChange(page);
|
const { size: pageSize } = state;
|
||||||
|
|
||||||
|
onPageChange({ page, pageSize });
|
||||||
}}
|
}}
|
||||||
minimal={true}
|
minimal={true}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
@@ -134,7 +142,7 @@ const Pagination = ({
|
|||||||
'pagination__item--page',
|
'pagination__item--page',
|
||||||
{
|
{
|
||||||
'is-active': state.currentPage === page,
|
'is-active': state.currentPage === page,
|
||||||
}
|
},
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{page}
|
{page}
|
||||||
@@ -145,15 +153,18 @@ const Pagination = ({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'PAGE_CHANGE',
|
type: 'PAGE_CHANGE',
|
||||||
page: state.currentPage + 1
|
page: state.currentPage + 1,
|
||||||
});
|
});
|
||||||
onPageChange(state.currentPage + 1);
|
const page = state.currentPage + 1;
|
||||||
|
const { size: pageSize } = state;
|
||||||
|
|
||||||
|
onPageChange({ page, pageSize });
|
||||||
}}
|
}}
|
||||||
minimal={true}
|
minimal={true}
|
||||||
className={'pagination__item pagination__item--next'}
|
className={'pagination__item pagination__item--next'}
|
||||||
icon={<Icon icon={'arrow-forward-24'} iconSize={12} />}
|
icon={<Icon icon={'arrow-forward-24'} iconSize={12} />}
|
||||||
>
|
>
|
||||||
<T id='next' />
|
<T id="next" />
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</div>
|
</div>
|
||||||
@@ -167,11 +178,11 @@ const Pagination = ({
|
|||||||
value={state.currentPage}
|
value={state.currentPage}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
const page = parseInt(event.currentTarget.value, 10);
|
const page = parseInt(event.currentTarget.value, 10);
|
||||||
|
const { size: pageSize } = state;
|
||||||
|
|
||||||
dispatch({ type: 'PAGE_CHANGE', page });
|
dispatch({ type: 'PAGE_CHANGE', page });
|
||||||
onPageChange(page);
|
onPageChange({ page, pageSize });
|
||||||
}}
|
}}
|
||||||
minimal={true}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -186,26 +197,28 @@ const Pagination = ({
|
|||||||
dispatch({ type: 'PAGE_SIZE_CHANGE', size: pageSize });
|
dispatch({ type: 'PAGE_SIZE_CHANGE', size: pageSize });
|
||||||
dispatch({ type: 'PAGE_CHANGE', page: 1 });
|
dispatch({ type: 'PAGE_CHANGE', page: 1 });
|
||||||
|
|
||||||
onPageSizeChange(pageSize, 1);
|
onPageSizeChange({ pageSize, page: 1 });
|
||||||
}}
|
}}
|
||||||
minimal={true}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pagination__info">
|
<div class="pagination__info">
|
||||||
<T id={'showing_current_page_to_total'} values={{
|
<T
|
||||||
|
id={'showing_current_page_to_total'}
|
||||||
|
values={{
|
||||||
currentPage: state.currentPage,
|
currentPage: state.currentPage,
|
||||||
totalPages: state.totalPages,
|
totalPages: state.totalPages,
|
||||||
total: total,
|
total: total,
|
||||||
}} />
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
Pagination.propTypes = {
|
Pagination.propTypes = {
|
||||||
initialPage: PropTypes.number.isRequired,
|
currentPage: PropTypes.number.isRequired,
|
||||||
size: PropTypes.number.isRequired,
|
size: PropTypes.number.isRequired,
|
||||||
total: PropTypes.number.isRequired,
|
total: PropTypes.number.isRequired,
|
||||||
onPageChange: PropTypes.func,
|
onPageChange: PropTypes.func,
|
||||||
@@ -213,7 +226,7 @@ Pagination.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Pagination.defaultProps = {
|
Pagination.defaultProps = {
|
||||||
initialPage: 1,
|
currentPage: 1,
|
||||||
size: 25,
|
size: 25,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import PageFormBigNumber from './PageFormBigNumber';
|
|||||||
import AccountsMultiSelect from './AccountsMultiSelect';
|
import AccountsMultiSelect from './AccountsMultiSelect';
|
||||||
import CustomersMultiSelect from './CustomersMultiSelect';
|
import CustomersMultiSelect from './CustomersMultiSelect';
|
||||||
import Skeleton from './Skeleton'
|
import Skeleton from './Skeleton'
|
||||||
|
import ContextMenu from './ContextMenu'
|
||||||
import TableFastCell from './Datatable/TableFastCell';
|
import TableFastCell from './Datatable/TableFastCell';
|
||||||
|
|
||||||
const Hint = FieldHint;
|
const Hint = FieldHint;
|
||||||
@@ -99,4 +99,5 @@ export {
|
|||||||
CustomersMultiSelect,
|
CustomersMultiSelect,
|
||||||
TableFastCell,
|
TableFastCell,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
|
ContextMenu
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
FormattedMessage as T,
|
FormattedMessage as T,
|
||||||
FormattedHTMLMessage,
|
FormattedHTMLMessage,
|
||||||
useIntl,
|
useIntl,
|
||||||
} from 'react-intl';
|
} from 'react-intl';
|
||||||
import { Intent, Alert } from '@blueprintjs/core';
|
import { Intent, Alert } from '@blueprintjs/core';
|
||||||
import { queryCache } from 'react-query';
|
|
||||||
import { AppToaster } from 'components';
|
import { AppToaster } from 'components';
|
||||||
|
|
||||||
import { handleDeleteErrors } from 'containers/Items/utils';
|
import { handleDeleteErrors } from 'containers/Items/utils';
|
||||||
|
import { useDeleteItem } from 'hooks/query';
|
||||||
|
|
||||||
import {
|
|
||||||
useDeleteItem
|
|
||||||
} from 'hooks/query';
|
|
||||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||||
|
import withItemsActions from 'containers/Items/withItemsActions';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
@@ -30,15 +28,19 @@ function ItemDeleteAlert({
|
|||||||
|
|
||||||
// #withAlertActions
|
// #withAlertActions
|
||||||
closeAlert,
|
closeAlert,
|
||||||
|
|
||||||
|
// #withItemsActions
|
||||||
|
addItemsTableQueries
|
||||||
}) {
|
}) {
|
||||||
const { mutateAsync: deleteItem, isLoading } = useDeleteItem();
|
const { mutateAsync: deleteItem, isLoading } = useDeleteItem();
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
// handle cancel delete item alert.
|
// Handle cancel delete item alert.
|
||||||
const handleCancelItemDelete = () => {
|
const handleCancelItemDelete = () => {
|
||||||
closeAlert(name);
|
closeAlert(name);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle confirm delete item.
|
||||||
const handleConfirmDeleteItem = () => {
|
const handleConfirmDeleteItem = () => {
|
||||||
deleteItem(itemId)
|
deleteItem(itemId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -48,6 +50,8 @@ function ItemDeleteAlert({
|
|||||||
}),
|
}),
|
||||||
intent: Intent.SUCCESS,
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
|
// Reset to page number one.
|
||||||
|
addItemsTableQueries({ page: 1 });
|
||||||
})
|
})
|
||||||
.catch(({ errors }) => {
|
.catch(({ errors }) => {
|
||||||
handleDeleteErrors(errors);
|
handleDeleteErrors(errors);
|
||||||
@@ -80,4 +84,5 @@ function ItemDeleteAlert({
|
|||||||
export default compose(
|
export default compose(
|
||||||
withAlertStoreConnect(),
|
withAlertStoreConnect(),
|
||||||
withAlertActions,
|
withAlertActions,
|
||||||
|
withItemsActions
|
||||||
)(ItemDeleteAlert);
|
)(ItemDeleteAlert);
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ function InventoryAdjustmentDataTable({
|
|||||||
autoResetSortBy={false}
|
autoResetSortBy={false}
|
||||||
autoResetPage={false}
|
autoResetPage={false}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
noResults={'There is no inventory adjustments transactions yet.'}
|
||||||
// pagesCount={inventoryAdjustmentsPagination.pagesCount}
|
// pagesCount={inventoryAdjustmentsPagination.pagesCount}
|
||||||
// initialPageSize={inventoryAdjustmentsPagination.pageSize}
|
// initialPageSize={inventoryAdjustmentsPagination.pageSize}
|
||||||
// initialPageIndex={inventoryAdjustmentsPagination.page - 1}
|
// initialPageIndex={inventoryAdjustmentsPagination.page - 1}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { ItemFormProvider } from './ItemFormProvider';
|
import { ItemFormProvider } from './ItemFormProvider';
|
||||||
@@ -12,9 +12,22 @@ import { compose } from 'utils';
|
|||||||
/**
|
/**
|
||||||
* Item form page.
|
* Item form page.
|
||||||
*/
|
*/
|
||||||
function ItemFormPage() {
|
function ItemFormPage({
|
||||||
|
// #withDashboardActions
|
||||||
|
setDashboardBackLink
|
||||||
|
}) {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Show the back link on dashboard topbar.
|
||||||
|
setDashboardBackLink(true);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
// Hide the back link on dashboard topbar.
|
||||||
|
setDashboardBackLink(false);
|
||||||
|
};
|
||||||
|
}, [setDashboardBackLink]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ItemFormProvider itemId={id}>
|
<ItemFormProvider itemId={id}>
|
||||||
<DashboardCard page>
|
<DashboardCard page>
|
||||||
@@ -24,4 +37,6 @@ function ItemFormPage() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(withDashboardActions)(ItemFormPage);
|
export default compose(
|
||||||
|
withDashboardActions,
|
||||||
|
)(ItemFormPage);
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ function ItemsActionsBar({
|
|||||||
// Handle tab changing.
|
// Handle tab changing.
|
||||||
const handleTabChange = (viewId) => {
|
const handleTabChange = (viewId) => {
|
||||||
addItemsTableQueries({
|
addItemsTableQueries({
|
||||||
custom_view_id: viewId.id || null,
|
customViewId: viewId.id || null,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ function ItemsActionsBar({
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||||
text={`${formatMessage({ id: 'filters_applied' })}`}
|
text={`${formatMessage({ id: 'filter' })}`}
|
||||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||||
/>
|
/>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
CostPriceCell,
|
CostPriceCell,
|
||||||
ItemTypeAccessor,
|
ItemTypeAccessor,
|
||||||
ItemsActionsTableCell,
|
ItemsActionsTableCell,
|
||||||
|
ItemsActionMenuList
|
||||||
} from './components';
|
} from './components';
|
||||||
|
|
||||||
// Items datatable.
|
// Items datatable.
|
||||||
@@ -118,11 +119,14 @@ function ItemsDataTable({
|
|||||||
manualSortBy={true}
|
manualSortBy={true}
|
||||||
pagesCount={1}
|
pagesCount={1}
|
||||||
autoResetSortBy={false}
|
autoResetSortBy={false}
|
||||||
autoResetPage={false}
|
autoResetPage={true}
|
||||||
|
manualPagination={true}
|
||||||
TableLoadingRenderer={TableSkeletonRows}
|
TableLoadingRenderer={TableSkeletonRows}
|
||||||
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||||
initialPageSize={pagination.pageSize}
|
|
||||||
initialPageIndex={pagination.page}
|
pageSize={pagination.pageSize}
|
||||||
|
pageIndex={pagination.page - 1}
|
||||||
|
ContextMenu={ItemsActionMenuList}
|
||||||
{...tableProps}
|
{...tableProps}
|
||||||
/>
|
/>
|
||||||
</Choose.Otherwise>
|
</Choose.Otherwise>
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
import React, { useEffect, createContext } from 'react';
|
import React, { useEffect, createContext } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { isEmpty } from 'lodash';
|
|
||||||
|
import { transformTableQueryToParams, isTableEmptyStatus } from 'utils';
|
||||||
|
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
import { useResourceViews, useResourceFields, useItems } from 'hooks/query';
|
import { useResourceViews, useResourceFields, useItems } from 'hooks/query';
|
||||||
import { useDashboardPageTitle } from 'hooks/state';
|
import { useDashboardPageTitle } from 'hooks/state';
|
||||||
|
|
||||||
const ItemsContext = createContext();
|
const ItemsContext = createContext();
|
||||||
|
|
||||||
function ItemsListProvider({ query, ...props }) {
|
/**
|
||||||
|
* Items list provider.
|
||||||
|
*/
|
||||||
|
function ItemsListProvider({
|
||||||
|
query,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
// Fetch accounts resource views and fields.
|
// Fetch accounts resource views and fields.
|
||||||
const { data: itemsViews, isFetching: isViewsLoading } = useResourceViews(
|
const { data: itemsViews, isFetching: isViewsLoading } = useResourceViews(
|
||||||
'items',
|
'items',
|
||||||
@@ -21,11 +29,16 @@ function ItemsListProvider({ query, ...props }) {
|
|||||||
// Handle fetching the items table based on the given query.
|
// Handle fetching the items table based on the given query.
|
||||||
const {
|
const {
|
||||||
data: { items, pagination, filterMeta },
|
data: { items, pagination, filterMeta },
|
||||||
isFetching: isItemsLoading,
|
isFetching: isItemsFetching,
|
||||||
} = useItems(query);
|
isLoading: isItemsLoading,
|
||||||
|
} = useItems({
|
||||||
|
...transformTableQueryToParams(query)
|
||||||
|
}, { keepPreviousData: true });
|
||||||
|
|
||||||
// Detarmines the datatable empty status.
|
// Detarmines the datatable empty status.
|
||||||
const isEmptyStatus = isEmpty(items) && !isItemsLoading && !filterMeta.view;
|
const isEmptyStatus = isTableEmptyStatus({
|
||||||
|
data: items, pagination, filterMeta,
|
||||||
|
}) && !isItemsFetching;
|
||||||
|
|
||||||
// Format message intl.
|
// Format message intl.
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
@@ -42,15 +55,15 @@ function ItemsListProvider({ query, ...props }) {
|
|||||||
itemsFields,
|
itemsFields,
|
||||||
items,
|
items,
|
||||||
pagination,
|
pagination,
|
||||||
|
|
||||||
isViewsLoading,
|
isViewsLoading,
|
||||||
isItemsLoading,
|
isItemsLoading,
|
||||||
isEmptyStatus: false,
|
isItemsFetching: isItemsFetching,
|
||||||
|
isEmptyStatus,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider
|
<DashboardInsider
|
||||||
loading={isFieldsLoading || isViewsLoading}
|
loading={isFieldsLoading}
|
||||||
name={'items-list'}
|
name={'items-list'}
|
||||||
>
|
>
|
||||||
<ItemsContext.Provider value={state} {...props} />
|
<ItemsContext.Provider value={state} {...props} />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { Switch, Route, useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import ItemsViewsTabs from './ItemsViewsTabs';
|
import ItemsViewsTabs from './ItemsViewsTabs';
|
||||||
import ItemsDataTable from './ItemsDataTable';
|
import ItemsDataTable from './ItemsDataTable';
|
||||||
@@ -7,9 +7,39 @@ import ItemsDataTable from './ItemsDataTable';
|
|||||||
import withItemsActions from 'containers/Items/withItemsActions';
|
import withItemsActions from 'containers/Items/withItemsActions';
|
||||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
|
import withItems from 'containers/Items/withItems';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
import { useItemsListContext } from './ItemsListProvider';
|
||||||
|
|
||||||
|
function transformPaginationToProps(pagination) {
|
||||||
|
const { page, pageSize, total } = pagination;
|
||||||
|
|
||||||
|
return {
|
||||||
|
initialPageIndex: Math.max(page - 1, 0),
|
||||||
|
initialPageSize: pageSize,
|
||||||
|
pagesCount: Math.ceil(total / pageSize),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformPaginationToQuery(query) {
|
||||||
|
const { pageSize, pageIndex, sortBy } = query;
|
||||||
|
|
||||||
|
return {
|
||||||
|
page_size: pageSize,
|
||||||
|
page: pageIndex + 1,
|
||||||
|
...(sortBy.length > 0
|
||||||
|
? {
|
||||||
|
column_sort_by: sortBy[0].id,
|
||||||
|
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Items view page.
|
||||||
|
*/
|
||||||
function ItemsViewPage({
|
function ItemsViewPage({
|
||||||
// #withAlertsActions.
|
// #withAlertsActions.
|
||||||
openAlert,
|
openAlert,
|
||||||
@@ -19,10 +49,14 @@ function ItemsViewPage({
|
|||||||
|
|
||||||
// #withItemsActions.
|
// #withItemsActions.
|
||||||
setSelectedRowsItems,
|
setSelectedRowsItems,
|
||||||
addItemsTableQueries
|
addItemsTableQueries,
|
||||||
|
|
||||||
|
itemsTableQuery,
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
|
const { pagination, isItemsFetching } = useItemsListContext();
|
||||||
|
|
||||||
// Handle delete action Item.
|
// Handle delete action Item.
|
||||||
const handleDeleteItem = ({ id }) => {
|
const handleDeleteItem = ({ id }) => {
|
||||||
openAlert('item-delete', { itemId: id });
|
openAlert('item-delete', { itemId: id });
|
||||||
@@ -52,28 +86,24 @@ function ItemsViewPage({
|
|||||||
// Handle item make adjustment.
|
// Handle item make adjustment.
|
||||||
const handleMakeAdjustment = ({ id }) => {
|
const handleMakeAdjustment = ({ id }) => {
|
||||||
openDialog('inventory-adjustment', { itemId: id });
|
openDialog('inventory-adjustment', { itemId: id });
|
||||||
}
|
};
|
||||||
|
|
||||||
// Handle fetch data once the page index, size or sort by of the table change.
|
// Handle fetch data once the page index, size or sort by of the table change.
|
||||||
const handleFetchData = ({ pageIndex, pageSize, sortBy }) => {
|
const handlePaginationChange = ({ pageSize, page }) => {
|
||||||
addItemsTableQueries({
|
addItemsTableQueries({
|
||||||
page_size: pageSize,
|
// ...transformPaginationToQuery(query),
|
||||||
page: pageIndex,
|
page,
|
||||||
...(sortBy.length > 0
|
pageSize,
|
||||||
? {
|
|
||||||
column_sort_by: sortBy[0].id,
|
|
||||||
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const controlledState = (state) => ({
|
||||||
|
...state,
|
||||||
|
pageIndex: itemsTableQuery.page - 1,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<>
|
||||||
<Route
|
|
||||||
exact={true}
|
|
||||||
path={['/items/:custom_view_id/custom_view', '/items']}
|
|
||||||
>
|
|
||||||
<ItemsViewsTabs />
|
<ItemsViewsTabs />
|
||||||
<ItemsDataTable
|
<ItemsDataTable
|
||||||
tableProps={{
|
tableProps={{
|
||||||
@@ -82,19 +112,23 @@ function ItemsViewPage({
|
|||||||
onEditItem: handleEditItem,
|
onEditItem: handleEditItem,
|
||||||
onInactivateItem: handleInactiveItem,
|
onInactivateItem: handleInactiveItem,
|
||||||
onActivateItem: handleActivateItem,
|
onActivateItem: handleActivateItem,
|
||||||
onMakeAdjustment: handleMakeAdjustment
|
onMakeAdjustment: handleMakeAdjustment,
|
||||||
},
|
},
|
||||||
onFetchData: handleFetchData
|
...transformPaginationToProps(pagination),
|
||||||
|
onPaginationChange: handlePaginationChange,
|
||||||
|
progressBarLoading: isItemsFetching,
|
||||||
|
// useControlledState: controlledState
|
||||||
|
// progressBarLoading: true
|
||||||
}}
|
}}
|
||||||
onSelectedRowsChange={handleSelectedRowsChange}
|
onSelectedRowsChange={handleSelectedRowsChange}
|
||||||
/>
|
/>
|
||||||
</Route>
|
</>
|
||||||
</Switch>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
withAlertsActions,
|
withAlertsActions,
|
||||||
withItemsActions,
|
withItemsActions,
|
||||||
withDialogActions
|
withDialogActions,
|
||||||
|
withItems(({ itemsTableQuery }) => ({ itemsTableQuery })),
|
||||||
)(ItemsViewPage);
|
)(ItemsViewPage);
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
|
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import { DashboardViewsTabs } from 'components';
|
import { DashboardViewsTabs } from 'components';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
|
import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import withItemsActions from 'containers/Items/withItemsActions';
|
import withItemsActions from 'containers/Items/withItemsActions';
|
||||||
|
import withItems from 'containers/Items/withItems';
|
||||||
|
|
||||||
import { useItemsListContext } from './ItemsListProvider';
|
import { useItemsListContext } from './ItemsListProvider';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,17 +16,22 @@ import { useItemsListContext } from './ItemsListProvider';
|
|||||||
function ItemsViewsTabs({
|
function ItemsViewsTabs({
|
||||||
// #withItemsActions
|
// #withItemsActions
|
||||||
addItemsTableQueries,
|
addItemsTableQueries,
|
||||||
|
|
||||||
|
// #withItems
|
||||||
|
itemsTableQuery
|
||||||
}) {
|
}) {
|
||||||
const { custom_view_id: customViewId = null } = useParams();
|
|
||||||
const { itemsViews } = useItemsListContext();
|
const { itemsViews } = useItemsListContext();
|
||||||
|
|
||||||
|
// Mapped items views.
|
||||||
const tabs = itemsViews.map((view) => ({
|
const tabs = itemsViews.map((view) => ({
|
||||||
...pick(view, ['name', 'id']),
|
...pick(view, ['name', 'id']),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Handles the active tab change.
|
||||||
const handleTabChange = (viewId) => {
|
const handleTabChange = (viewId) => {
|
||||||
addItemsTableQueries({
|
addItemsTableQueries({
|
||||||
custom_view_id: viewId || null,
|
page: 1,
|
||||||
|
customViewId: viewId || null,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,7 +39,7 @@ function ItemsViewsTabs({
|
|||||||
<Navbar className="navbar--dashboard-views">
|
<Navbar className="navbar--dashboard-views">
|
||||||
<NavbarGroup align={Alignment.LEFT}>
|
<NavbarGroup align={Alignment.LEFT}>
|
||||||
<DashboardViewsTabs
|
<DashboardViewsTabs
|
||||||
initialViewId={customViewId}
|
currentViewId={itemsTableQuery.customViewId}
|
||||||
resourceName={'items'}
|
resourceName={'items'}
|
||||||
tabs={tabs}
|
tabs={tabs}
|
||||||
onChange={handleTabChange}
|
onChange={handleTabChange}
|
||||||
@@ -42,7 +49,8 @@ function ItemsViewsTabs({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
|
withRouter,
|
||||||
|
withItems(({ itemsTableQuery }) => ({ itemsTableQuery })),
|
||||||
withItemsActions,
|
withItemsActions,
|
||||||
)(ItemsViewsTabs);
|
)(ItemsViewsTabs);
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export const ItemTypeAccessor = (row) => {
|
|||||||
) : null;
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ItemsActionMenuList = ({
|
export function ItemsActionMenuList({
|
||||||
row: { original },
|
row: { original },
|
||||||
payload: {
|
payload: {
|
||||||
onEditItem,
|
onEditItem,
|
||||||
@@ -80,7 +80,7 @@ export const ItemsActionMenuList = ({
|
|||||||
onMakeAdjustment,
|
onMakeAdjustment,
|
||||||
onDeleteItem,
|
onDeleteItem,
|
||||||
},
|
},
|
||||||
}) => {
|
}) {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -112,6 +112,7 @@ export const ItemsActionMenuList = ({
|
|||||||
<If condition={original.type === 'inventory'}>
|
<If condition={original.type === 'inventory'}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text={formatMessage({ id: 'make_adjustment' })}
|
text={formatMessage({ id: 'make_adjustment' })}
|
||||||
|
icon={<Icon icon={'swap-vert'} iconSize={16} />}
|
||||||
onClick={safeCallback(onMakeAdjustment, original)}
|
onClick={safeCallback(onMakeAdjustment, original)}
|
||||||
/>
|
/>
|
||||||
</If>
|
</If>
|
||||||
|
|||||||
@@ -1,30 +1,15 @@
|
|||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
import {
|
import {
|
||||||
getResourceViews,
|
|
||||||
} from 'store/customViews/customViews.selectors'
|
|
||||||
import {
|
|
||||||
getItemsCurrentPageFactory,
|
|
||||||
getItemsPaginationMetaFactory,
|
|
||||||
getItemsTableQueryFactory,
|
getItemsTableQueryFactory,
|
||||||
getItemsCurrentViewIdFactory
|
|
||||||
} from 'store/items/items.selectors';
|
} from 'store/items/items.selectors';
|
||||||
|
|
||||||
export default (mapState) => {
|
export default (mapState) => {
|
||||||
const getItemsCurrentPage = getItemsCurrentPageFactory();
|
|
||||||
const getItemsPaginationMeta = getItemsPaginationMetaFactory();
|
|
||||||
const getItemsTableQuery = getItemsTableQueryFactory();
|
const getItemsTableQuery = getItemsTableQueryFactory();
|
||||||
const getItemsCurrentViewId = getItemsCurrentViewIdFactory();
|
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => {
|
const mapStateToProps = (state, props) => {
|
||||||
const mapped = {
|
const mapped = {
|
||||||
itemsViews: getResourceViews(state, props, 'items'),
|
|
||||||
itemsCurrentPage: getItemsCurrentPage(state, props),
|
|
||||||
itemsBulkSelected: state.items.bulkActions,
|
|
||||||
itemsTableLoading: state.items.loading,
|
|
||||||
itemsSelectedRows: state.items.selectedRows,
|
itemsSelectedRows: state.items.selectedRows,
|
||||||
itemsTableQuery: getItemsTableQuery(state, props),
|
itemsTableQuery: getItemsTableQuery(state, props),
|
||||||
itemsPagination: getItemsPaginationMeta(state, props),
|
|
||||||
itemsCurrentViewId: getItemsCurrentViewId(state, props),
|
|
||||||
};
|
};
|
||||||
return mapState ? mapState(mapped, state, props) : mapped;
|
return mapState ? mapState(mapped, state, props) : mapped;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,35 +1,7 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
|
||||||
fetchItems,
|
|
||||||
fetchItem,
|
|
||||||
deleteItem,
|
|
||||||
submitItem,
|
|
||||||
editItem,
|
|
||||||
deleteBulkItems,
|
|
||||||
activateItem,
|
|
||||||
inactiveItem,
|
|
||||||
} from 'store/items/items.actions';
|
|
||||||
import t from 'store/types';
|
import t from 'store/types';
|
||||||
|
|
||||||
export const mapDispatchToProps = (dispatch) => ({
|
export const mapDispatchToProps = (dispatch) => ({
|
||||||
requestFetchItems: (query) => dispatch(fetchItems({ query })),
|
|
||||||
requestFetchItem: (id) => dispatch(fetchItem({ id })),
|
|
||||||
requestDeleteItem: (id) => dispatch(deleteItem({ id })),
|
|
||||||
requestDeleteBulkItems: (ids) => dispatch(deleteBulkItems({ ids })),
|
|
||||||
requestSubmitItem: (form) => dispatch(submitItem({ form })),
|
|
||||||
requestEditItem: (id, form) => dispatch(editItem(id, form)),
|
|
||||||
requestInactiveItem: (id) => dispatch(inactiveItem({ id })),
|
|
||||||
requestActivateItem: (id) => dispatch(activateItem({ id })),
|
|
||||||
addBulkActionItem: (id) =>
|
|
||||||
dispatch({
|
|
||||||
type: t.ITEM_BULK_ACTION_ADD,
|
|
||||||
itemId: id,
|
|
||||||
}),
|
|
||||||
removeBulkActionItem: (id) =>
|
|
||||||
dispatch({
|
|
||||||
type: t.ITEM_BULK_ACTION_REMOVE,
|
|
||||||
itemId: id,
|
|
||||||
}),
|
|
||||||
setItemsTableQuery: (key, value) =>
|
setItemsTableQuery: (key, value) =>
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.ITEMS_TABLE_QUERY_SET,
|
type: t.ITEMS_TABLE_QUERY_SET,
|
||||||
@@ -41,12 +13,6 @@ export const mapDispatchToProps = (dispatch) => ({
|
|||||||
type: t.ITEMS_TABLE_QUERIES_ADD,
|
type: t.ITEMS_TABLE_QUERIES_ADD,
|
||||||
payload: { queries },
|
payload: { queries },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
changeItemsCurrentView: (id) =>
|
|
||||||
dispatch({
|
|
||||||
type: t.ITEMS_SET_CURRENT_VIEW,
|
|
||||||
currentViewId: parseInt(id, 10),
|
|
||||||
}),
|
|
||||||
setSelectedRowsItems: (selectedRows) =>
|
setSelectedRowsItems: (selectedRows) =>
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.ITEM_SELECTED_ROWS_SET,
|
type: t.ITEM_SELECTED_ROWS_SET,
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export default function ItemsCategoryTable({
|
|||||||
sticky={true}
|
sticky={true}
|
||||||
selectionColumn={true}
|
selectionColumn={true}
|
||||||
TableLoadingRenderer={TableSkeletonRows}
|
TableLoadingRenderer={TableSkeletonRows}
|
||||||
{...tableProps}
|
noResults={'There is no items categories in table yet.'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
||||||
|
import { defaultTo } from 'lodash';
|
||||||
import ApiService from 'services/ApiService';
|
import ApiService from 'services/ApiService';
|
||||||
|
|
||||||
|
// Transform the account.
|
||||||
const transformAccount = (response) => {
|
const transformAccount = (response) => {
|
||||||
return response.data.account;
|
return response.data.account;
|
||||||
};
|
};
|
||||||
@@ -9,17 +11,18 @@ const transformAccount = (response) => {
|
|||||||
* Retrieve accounts list.
|
* Retrieve accounts list.
|
||||||
*/
|
*/
|
||||||
export function useAccounts(query, props) {
|
export function useAccounts(query, props) {
|
||||||
return useQuery(
|
const states = useQuery(
|
||||||
['ACCOUNTS', query],
|
['ACCOUNTS', query],
|
||||||
() =>
|
() =>
|
||||||
ApiService.get('accounts', { params: query }).then(
|
ApiService.get('accounts', { params: query }).then(
|
||||||
(response) => response.data.accounts,
|
(response) => response.data.accounts,
|
||||||
),
|
),
|
||||||
{
|
props,
|
||||||
initialData: [],
|
|
||||||
...props
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
return {
|
||||||
|
...states,
|
||||||
|
data: defaultTo(states.data, []),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,23 +30,30 @@ export function useAccounts(query, props) {
|
|||||||
* @param {number} id -
|
* @param {number} id -
|
||||||
*/
|
*/
|
||||||
export function useAccount(id, props) {
|
export function useAccount(id, props) {
|
||||||
return useQuery(
|
const states = useQuery(
|
||||||
['ACCOUNT', id],
|
['ACCOUNT', id],
|
||||||
() => ApiService.get(`accounts/${id}`).then(transformAccount),
|
() => ApiService.get(`accounts/${id}`).then(transformAccount),
|
||||||
{
|
props,
|
||||||
initialData: {},
|
|
||||||
...props,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
return {
|
||||||
|
...states,
|
||||||
|
data: defaultTo(states.data, {}),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve accounts types list.
|
* Retrieve accounts types list.
|
||||||
*/
|
*/
|
||||||
export function useAccountsTypes() {
|
export function useAccountsTypes(props) {
|
||||||
return useQuery(['ACCOUNTS_TYPES'], () => ApiService.get('account_types'), {
|
const states = useQuery(
|
||||||
initialData: [],
|
['ACCOUNTS_TYPES'],
|
||||||
});
|
() => ApiService.get('account_types'),
|
||||||
|
props,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...states,
|
||||||
|
data: defaultTo(states.data, {}),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,7 +66,7 @@ export function useCreateAccount(props) {
|
|||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
client.invalidateQueries('ACCOUNTS');
|
client.invalidateQueries('ACCOUNTS');
|
||||||
},
|
},
|
||||||
...props
|
...props,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +82,7 @@ export function useEditAccount(props) {
|
|||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
query.invalidateQueries('ACCOUNTS');
|
query.invalidateQueries('ACCOUNTS');
|
||||||
},
|
},
|
||||||
...props
|
...props,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -103,15 +113,10 @@ export function useDeleteAccount(props) {
|
|||||||
export function useActivateAccount(props) {
|
export function useActivateAccount(props) {
|
||||||
const query = useQueryClient();
|
const query = useQueryClient();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation((id) => ApiService.post(`accounts/${id}/activate`), {
|
||||||
(id) => ApiService.post(`accounts/${id}/activate`),
|
onSuccess: () => {},
|
||||||
{
|
...props,
|
||||||
onSuccess: () => {
|
});
|
||||||
|
|
||||||
},
|
|
||||||
...props
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,13 +125,8 @@ export function useActivateAccount(props) {
|
|||||||
export function useInactivateAccount(props) {
|
export function useInactivateAccount(props) {
|
||||||
const query = useQueryClient();
|
const query = useQueryClient();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation((id) => ApiService.post(`accounts/${id}/inactivate`), {
|
||||||
(id) => ApiService.post(`accounts/${id}/inactivate`),
|
onSuccess: () => {},
|
||||||
{
|
...props,
|
||||||
onSuccess: () => {
|
});
|
||||||
|
|
||||||
},
|
|
||||||
...props
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,55 @@
|
|||||||
import { useMutation, useQuery } from 'react-query';
|
import { useMutation, useQueryClient, useQuery } from 'react-query';
|
||||||
|
import { defaultTo } from 'lodash';
|
||||||
import ApiService from 'services/ApiService';
|
import ApiService from 'services/ApiService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new currency.
|
* Create a new currency.
|
||||||
*/
|
*/
|
||||||
export function useCreateCurrency() {
|
export function useCreateCurrency(props) {
|
||||||
return useMutation((values) => ApiService.post('currencies', values));
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(values) => ApiService.post('currencies', values),
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries('CURRENCIES');
|
||||||
|
},
|
||||||
|
...props,
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edits the given currency code.
|
* Edits the given currency code.
|
||||||
*/
|
*/
|
||||||
export function useEditCurrency() {
|
export function useEditCurrency(props) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation((currencyCode, values) =>
|
return useMutation((currencyCode, values) =>
|
||||||
ApiService.post(`currencies/${currencyCode}`, values),
|
ApiService.post(`currencies/${currencyCode}`, values),
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries('CURRENCIES');
|
||||||
|
},
|
||||||
|
...props,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the given currency.
|
* Deletes the given currency.
|
||||||
*/
|
*/
|
||||||
export function useDeleteCurrency() {
|
export function useDeleteCurrency(props) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation((currencyCode) =>
|
return useMutation((currencyCode) =>
|
||||||
ApiService.delete(`currencies/${currencyCode}`),
|
ApiService.delete(`currencies/${currencyCode}`),
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries('CURRENCIES');
|
||||||
|
},
|
||||||
|
...props
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,12 +57,14 @@ export function useDeleteCurrency() {
|
|||||||
* Retrieve the currencies list.
|
* Retrieve the currencies list.
|
||||||
*/
|
*/
|
||||||
export function useCurrencies(props) {
|
export function useCurrencies(props) {
|
||||||
return useQuery(
|
const states = useQuery(
|
||||||
['CURRENCIES'],
|
['CURRENCIES'],
|
||||||
() => ApiService.get('currencies').then(res => res.data.currencies),
|
() => ApiService.get('currencies').then(res => res.data.currencies),
|
||||||
{
|
props,
|
||||||
initialData: [],
|
|
||||||
...props,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...states,
|
||||||
|
data: defaultTo(states.data, []),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
||||||
|
import { defaultTo } from 'lodash';
|
||||||
import ApiService from 'services/ApiService';
|
import ApiService from 'services/ApiService';
|
||||||
import { transformResponse } from 'utils';
|
import { transformResponse } from 'utils';
|
||||||
|
|
||||||
@@ -12,7 +13,14 @@ const defaultPagination = {
|
|||||||
* Creates a new item.
|
* Creates a new item.
|
||||||
*/
|
*/
|
||||||
export function useCreateItem(props) {
|
export function useCreateItem(props) {
|
||||||
return useMutation((values) => ApiService.post('items', values), props);
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation((values) => ApiService.post('items', values), {
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries('ITEMS');
|
||||||
|
},
|
||||||
|
...props,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,19 +66,21 @@ const transformItemsResponse = (response) => {
|
|||||||
* Retrieves items list.
|
* Retrieves items list.
|
||||||
*/
|
*/
|
||||||
export function useItems(query, props) {
|
export function useItems(query, props) {
|
||||||
return useQuery(
|
const result = useQuery(
|
||||||
['ITEMS', query],
|
['ITEMS', query],
|
||||||
() =>
|
() =>
|
||||||
ApiService.get(`items`, { params: query }).then(transformItemsResponse),
|
ApiService.get(`items`, { params: query }).then(transformItemsResponse),
|
||||||
{
|
props,
|
||||||
initialData: {
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
data: defaultTo(result.data, {
|
||||||
items: [],
|
items: [],
|
||||||
pagination: defaultPagination,
|
pagination: defaultPagination,
|
||||||
filterMeta: {},
|
filterMeta: {},
|
||||||
},
|
}),
|
||||||
...props,
|
};
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
|
import { defaultTo } from 'lodash';
|
||||||
import ApiService from "services/ApiService";
|
import ApiService from "services/ApiService";
|
||||||
|
|
||||||
// export function useSaveView(values) {
|
// export function useSaveView(values) {
|
||||||
// return ApiService.post('views', form);
|
// return ApiService.post('views', form);
|
||||||
// }
|
// }
|
||||||
@@ -18,14 +18,16 @@ import ApiService from "services/ApiService";
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
export function useResourceViews(resourceSlug) {
|
export function useResourceViews(resourceSlug) {
|
||||||
return useQuery(
|
const states = useQuery(
|
||||||
['RESOURCE_VIEW', resourceSlug],
|
['RESOURCE_VIEW', resourceSlug],
|
||||||
() => ApiService.get(`views/resource/${resourceSlug}`)
|
() => ApiService.get(`views/resource/${resourceSlug}`)
|
||||||
.then((response) => response.data.views),
|
.then((response) => response.data.views),
|
||||||
{
|
|
||||||
initialData: [],
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...states,
|
||||||
|
data: defaultTo(states.data, []),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -39,13 +41,16 @@ export function useResourceColumns(resourceSlug) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useResourceFields(resourceSlug) {
|
export function useResourceFields(resourceSlug, props) {
|
||||||
return useQuery(
|
const states = useQuery(
|
||||||
['RESOURCE_FIELDS', resourceSlug],
|
['RESOURCE_FIELDS', resourceSlug],
|
||||||
() => ApiService.get(`resources/${resourceSlug}/fields`)
|
() => ApiService.get(`resources/${resourceSlug}/fields`)
|
||||||
.then((res) => res.data.resource_fields),
|
.then((res) => res.data.resource_fields),
|
||||||
{
|
props
|
||||||
initialData: [],
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...states,
|
||||||
|
data: defaultTo(states.data, []),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,27 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { dashboardPageTitle } from 'store/dashboard/dashboard.actions';
|
import { dashboardPageTitle } from 'store/dashboard/dashboard.actions';
|
||||||
|
|
||||||
export const useDashboardPageTitle = () => {
|
export const useDispatchAction = (action) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
return (pageTitle) => {
|
return useCallback(
|
||||||
dispatch(dashboardPageTitle(pageTitle));
|
(payload) => {
|
||||||
}
|
dispatch(action(payload));
|
||||||
|
},
|
||||||
|
[dispatch, action],
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useDashboardPageTitle = () => {
|
||||||
|
return useDispatchAction(dashboardPageTitle);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSetAccountsTableQuery = () => {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAccountsTableQuery = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -367,5 +367,11 @@ export default {
|
|||||||
'M10 20C4.48 20 0 15.52 0 10S4.48 0 10 0s10 4.48 10 10-4.48 10-10 10zm5-14c-.28 0-.53.11-.71.29L8 12.59l-2.29-2.3a1.003 1.003 0 00-1.42 1.42l3 3c.18.18.43.29.71.29.28 0 .53-.11.71-.29l7-7A1.003 1.003 0 0015 6z'
|
'M10 20C4.48 20 0 15.52 0 10S4.48 0 10 0s10 4.48 10 10-4.48 10-10 10zm5-14c-.28 0-.53.11-.71.29L8 12.59l-2.29-2.3a1.003 1.003 0 00-1.42 1.42l3 3c.18.18.43.29.71.29.28 0 .53-.11.71-.29l7-7A1.003 1.003 0 0015 6z'
|
||||||
],
|
],
|
||||||
viewBox: '0 0 20 20'
|
viewBox: '0 0 20 20'
|
||||||
|
},
|
||||||
|
'swap-vert': {
|
||||||
|
path: [
|
||||||
|
'M10.6,10.9V5.4H9v5.5H6.7L9.8,14l3.1-3.1ZM5.1,0,2,3.1H4.3V8.6H5.9V3.1H8.2Z',
|
||||||
|
],
|
||||||
|
viewBox: '0 0 14 14'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,139 +0,0 @@
|
|||||||
import ApiService from 'services/ApiService';
|
|
||||||
import t from 'store/types';
|
|
||||||
|
|
||||||
export const submitItem = ({ form }) => {
|
|
||||||
return (dispatch) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
ApiService.post('items', form)
|
|
||||||
.then((response) => {
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
const { response } = error;
|
|
||||||
const { data } = response;
|
|
||||||
|
|
||||||
reject(data?.errors);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const editItem = (id, form) => {
|
|
||||||
return (dispatch) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
ApiService.post(`items/${id}`, form)
|
|
||||||
.then((response) => {
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
const { response } = error;
|
|
||||||
const { data } = response;
|
|
||||||
|
|
||||||
reject(data?.errors);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchItems = ({ query }) => {
|
|
||||||
return (dispatch, getState) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
let pageQuery = getState().items.tableQuery;
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: t.ITEMS_TABLE_LOADING,
|
|
||||||
payload: { loading: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
ApiService.get(`items`, { params: { ...pageQuery, ...query } })
|
|
||||||
.then((response) => {
|
|
||||||
dispatch({
|
|
||||||
type: t.ITEMS_SET,
|
|
||||||
items: response.data.items,
|
|
||||||
});
|
|
||||||
dispatch({
|
|
||||||
type: t.ITEMS_PAGE_SET,
|
|
||||||
items: response.data.items,
|
|
||||||
customViewId:
|
|
||||||
response.data?.filter_meta?.view?.custom_view_id || -1,
|
|
||||||
paginationMeta: response.data.pagination,
|
|
||||||
});
|
|
||||||
dispatch({
|
|
||||||
type: t.ITEMS_PAGINATION_SET,
|
|
||||||
payload: {
|
|
||||||
pagination: response.data.pagination,
|
|
||||||
customViewId:
|
|
||||||
response.data?.filter_meta?.view?.custom_view_id || -1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
dispatch({
|
|
||||||
type: t.ITEMS_TABLE_LOADING,
|
|
||||||
payload: { loading: false },
|
|
||||||
});
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchItem = ({ id }) => {
|
|
||||||
return (dispatch) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
ApiService.get(`items/${id}`)
|
|
||||||
.then((response) => {
|
|
||||||
dispatch({
|
|
||||||
type: t.ITEM_SET,
|
|
||||||
payload: {
|
|
||||||
id,
|
|
||||||
item: response.data.item,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteItem = ({ id }) => {
|
|
||||||
return (dispatch) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
ApiService.delete(`items/${id}`)
|
|
||||||
.then((response) => {
|
|
||||||
dispatch({
|
|
||||||
type: t.ITEM_DELETE,
|
|
||||||
payload: { id },
|
|
||||||
});
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
reject(error?.response?.data);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteBulkItems = ({ ids }) => {
|
|
||||||
return (dispatch) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
ApiService.delete('items', { params: { ids } })
|
|
||||||
.then((response) => {
|
|
||||||
dispatch({
|
|
||||||
type: t.ITEMS_BULK_DELETE,
|
|
||||||
payload: { ids },
|
|
||||||
});
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
reject(error.response.data.errors || []);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const activateItem = ({ id }) => {
|
|
||||||
return (dispatch) => ApiService.post(`items/${id}/activate`);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const inactiveItem = ({ id }) => {
|
|
||||||
return (dispatch) => ApiService.post(`items/${id}/inactivate`);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,111 +1,16 @@
|
|||||||
import t from 'store/types';
|
|
||||||
import { createReducer } from '@reduxjs/toolkit';
|
import { createReducer } from '@reduxjs/toolkit';
|
||||||
import { getItemsViewPages } from 'store/items/items.selectors';
|
|
||||||
import {
|
import {
|
||||||
viewPaginationSetReducer,
|
|
||||||
createTableQueryReducers,
|
createTableQueryReducers,
|
||||||
} from 'store/journalNumber.reducer';
|
} from 'store/journalNumber.reducer';
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
items: {},
|
|
||||||
views: {},
|
|
||||||
itemsRelation: {},
|
|
||||||
currentPage: 1,
|
|
||||||
currentViewId: -1,
|
|
||||||
bulkActions: {},
|
|
||||||
loading: false,
|
|
||||||
tableQuery: {
|
tableQuery: {
|
||||||
page_size: 12,
|
pageSize: 12,
|
||||||
page: 1,
|
page: 1,
|
||||||
},
|
},
|
||||||
selectedRows: [],
|
selectedRows: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export default createReducer(initialState, {
|
export default createReducer(initialState, {
|
||||||
[t.ITEMS_SET]: (state, action) => {
|
|
||||||
const _items = {};
|
|
||||||
|
|
||||||
action.items.forEach((item) => {
|
|
||||||
_items[item.id] = item;
|
|
||||||
});
|
|
||||||
state.items = {
|
|
||||||
...state.items,
|
|
||||||
..._items,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
[t.ITEM_SET]: (state, action) => {
|
|
||||||
const { id, item } = action.payload;
|
|
||||||
state.items[id] = { ...item };
|
|
||||||
},
|
|
||||||
|
|
||||||
[t.ITEMS_PAGE_SET]: (state, action) => {
|
|
||||||
const { items, customViewId, paginationMeta } = action;
|
|
||||||
|
|
||||||
const viewId = customViewId || -1;
|
|
||||||
const view = state.views[viewId] || {};
|
|
||||||
|
|
||||||
state.views[viewId] = {
|
|
||||||
...view,
|
|
||||||
pages: {
|
|
||||||
...(state.views?.[viewId]?.pages || {}),
|
|
||||||
[paginationMeta.page]: {
|
|
||||||
ids: items.map((item) => item.id),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
[t.ITEM_BULK_ACTION_ADD]: (state, action) => {
|
|
||||||
state.bulkActions[action.itemId] = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
[t.ITEM_SELECTED_ROWS_SET]: (state, action) => {
|
|
||||||
const { selectedRows } = action.payload;
|
|
||||||
state.selectedRows = selectedRows;
|
|
||||||
},
|
|
||||||
|
|
||||||
[t.ITEM_BULK_ACTION_REMOVE]: (state, action) => {
|
|
||||||
delete state.bulkActions[action.itemId];
|
|
||||||
},
|
|
||||||
|
|
||||||
[t.ITEM_DELETE]: (state, action) => {
|
|
||||||
const { id } = action.payload;
|
|
||||||
const items = { ...state.items };
|
|
||||||
|
|
||||||
if (items[id]) {
|
|
||||||
const item = items[id];
|
|
||||||
|
|
||||||
delete items[id];
|
|
||||||
state.items = items;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
[t.ITEMS_TABLE_LOADING]: (state, action) => {
|
|
||||||
const { loading } = action.payload;
|
|
||||||
state.loading = !!loading;
|
|
||||||
},
|
|
||||||
|
|
||||||
[t.ITEMS_SET_CURRENT_VIEW]: (state, action) => {
|
|
||||||
state.currentViewId = action.currentViewId;
|
|
||||||
},
|
|
||||||
|
|
||||||
[t.ITEMS_BULK_DELETE]: (state, action) => {
|
|
||||||
const { ids } = action.payload;
|
|
||||||
const items = { ...state.items };
|
|
||||||
|
|
||||||
ids.forEach((id) => {
|
|
||||||
if (typeof items[id] !== 'undefined') {
|
|
||||||
delete items[id];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
state.items = items;
|
|
||||||
},
|
|
||||||
|
|
||||||
...viewPaginationSetReducer(t.ITEMS_PAGINATION_SET),
|
|
||||||
...createTableQueryReducers('ITEMS'),
|
...createTableQueryReducers('ITEMS'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getItemById = (state, id) => {
|
|
||||||
return state.items.items[id];
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,28 +1,11 @@
|
|||||||
import { paginationLocationQuery } from 'store/selectors';
|
import { paginationLocationQuery } from 'store/selectors';
|
||||||
import { createSelector } from 'reselect';
|
import { createDeepEqualSelector } from 'utils';
|
||||||
import { pickItemsFromIds, defaultPaginationMeta } from 'store/selectors';
|
|
||||||
|
|
||||||
const itemsTableQuerySelector = (state) => state.items.tableQuery;
|
const itemsTableQuerySelector = (state) => state.items.tableQuery;
|
||||||
|
|
||||||
const itemsCurrentPageSelector = (state, props) => {
|
|
||||||
const currentViewId = state.items.currentViewId;
|
|
||||||
const currentView = state.items.views?.[currentViewId];
|
|
||||||
const currentPageId = currentView?.paginationMeta?.page;
|
|
||||||
|
|
||||||
return currentView?.pages?.[currentPageId];
|
|
||||||
};
|
|
||||||
const itemsDataSelector = (state) => state.items.items;
|
|
||||||
|
|
||||||
const itemsPaginationSelector = (state, props) => {
|
|
||||||
const viewId = state.items.currentViewId;
|
|
||||||
return state.items.views?.[viewId]?.paginationMeta;
|
|
||||||
};
|
|
||||||
const itemsCurrentViewIdSelector = (state) => {
|
|
||||||
return state.items.currentViewId;
|
|
||||||
};
|
|
||||||
// Get items table query marged with location query.
|
// Get items table query marged with location query.
|
||||||
export const getItemsTableQueryFactory = () =>
|
export const getItemsTableQueryFactory = () =>
|
||||||
createSelector(
|
createDeepEqualSelector(
|
||||||
paginationLocationQuery,
|
paginationLocationQuery,
|
||||||
itemsTableQuerySelector,
|
itemsTableQuerySelector,
|
||||||
(locationQuery, tableQuery) => {
|
(locationQuery, tableQuery) => {
|
||||||
@@ -32,30 +15,3 @@ export const getItemsTableQueryFactory = () =>
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Retrieve items current page and view.
|
|
||||||
export const getItemsCurrentPageFactory = () =>
|
|
||||||
createSelector(
|
|
||||||
itemsDataSelector,
|
|
||||||
itemsCurrentPageSelector,
|
|
||||||
(items, itemsIdsCurrentPage) => {
|
|
||||||
return typeof itemsIdsCurrentPage === 'object'
|
|
||||||
? pickItemsFromIds(items, itemsIdsCurrentPage.ids) || []
|
|
||||||
: [];
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Retrieve items pagination meta.
|
|
||||||
export const getItemsPaginationMetaFactory = () =>
|
|
||||||
createSelector(itemsPaginationSelector, (itemsPagination) => {
|
|
||||||
return {
|
|
||||||
...defaultPaginationMeta(),
|
|
||||||
...itemsPagination,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Retrieve items current view id.
|
|
||||||
export const getItemsCurrentViewIdFactory = () =>
|
|
||||||
createSelector(itemsCurrentViewIdSelector, (currentViewId) => {
|
|
||||||
return currentViewId;
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const paginationLocationQuery = (state, props) => {
|
|||||||
? new URLSearchParams(props.location.search)
|
? new URLSearchParams(props.location.search)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const queryParamsKeys = ['page_size', 'page'];
|
const queryParamsKeys = ['page_size', 'page', 'custom_view_id'];
|
||||||
|
|
||||||
return queryParams
|
return queryParams
|
||||||
? mapValues(pick(Object.fromEntries(queryParams), queryParamsKeys), (v) =>
|
? mapValues(pick(Object.fromEntries(queryParams), queryParamsKeys), (v) =>
|
||||||
|
|||||||
@@ -44,14 +44,7 @@ body.hide-scrollbar .Pane2{
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.select-list--fill-popover {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.bp3-transition-container,
|
|
||||||
.bp3-popover {
|
|
||||||
min-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bp3-fill{
|
.bp3-fill{
|
||||||
.bp3-popover-wrapper,
|
.bp3-popover-wrapper,
|
||||||
@@ -91,12 +84,6 @@ body.hide-scrollbar .Pane2{
|
|||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group--select-list{
|
|
||||||
button{
|
|
||||||
justify-content: start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bp3-timezone-picker{
|
.bp3-timezone-picker{
|
||||||
|
|
||||||
.bp3-button{
|
.bp3-button{
|
||||||
@@ -106,3 +93,5 @@ body.hide-scrollbar .Pane2{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,10 @@
|
|||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bp3-context-menu-popover-target{
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.th,
|
.th,
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
|
|
||||||
.pagination{
|
.pagination{
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 25px 14px;
|
padding: 28px 14px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|
||||||
.bp3-button{
|
.bp3-button{
|
||||||
background: transparent;
|
background: transparent;
|
||||||
padding: 4px 4px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__item{
|
&__item{
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
&:not([class*="bp3-intent-"]){
|
&:not([class*="bp3-intent-"]){
|
||||||
color: #666666;
|
color: #666666;
|
||||||
border-radius: 3px;
|
border-radius: 5px;
|
||||||
|
|
||||||
&:hover{
|
&:hover{
|
||||||
background-color: #E6EFFB;
|
background-color: #E6EFFB;
|
||||||
@@ -28,14 +28,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.pagination .pagination__buttons-group .bp3-button-group &{
|
.pagination .pagination__buttons-group .bp3-button-group &{
|
||||||
border-radius: 3px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
&--next,
|
&--next,
|
||||||
&--previous{
|
&--previous{
|
||||||
|
|
||||||
&.bp3-button{
|
&.bp3-button{
|
||||||
padding-left: 6px;
|
padding-left: 10px;
|
||||||
padding-right: 6px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,20 +63,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__info{
|
&__info{
|
||||||
padding-top: 6px;
|
color: #888;
|
||||||
color: #999;
|
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__controls{
|
&__controls{
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
|
||||||
.bp3-html-select{
|
.bp3-html-select{
|
||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
|
|
||||||
select{
|
select{
|
||||||
height: 20px;
|
height: 24px;
|
||||||
width: auto;
|
width: auto;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
padding-right: 0px;
|
padding-right: 0px;
|
||||||
@@ -103,7 +105,6 @@
|
|||||||
|
|
||||||
&__pagesize-control{
|
&__pagesize-control{
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
padding-top: 4px;
|
color: #888;
|
||||||
color: #777;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
101
client/src/style/components/MaterialProgressBar.scss
Normal file
101
client/src/style/components/MaterialProgressBar.scss
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// IONIC DEFAULT THEME COLORS
|
||||||
|
$colors: (
|
||||||
|
primary: #387ef5,
|
||||||
|
secondary: #32db64,
|
||||||
|
danger: #f53d3d,
|
||||||
|
light: #f4f4f4,
|
||||||
|
dark: #222
|
||||||
|
);
|
||||||
|
|
||||||
|
// https://css-tricks.com/html5-progress-element/
|
||||||
|
.progress, progress[value] {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
margin: 5px 0;
|
||||||
|
height: 5px;
|
||||||
|
display: block;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
&::-webkit-progress-bar {
|
||||||
|
background-color: lighten( map-get($colors, primary), 35% );
|
||||||
|
}
|
||||||
|
&::-webkit-progress-value {
|
||||||
|
background-color: map-get($colors, primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://materializecss.com/preloader.html
|
||||||
|
// https://github.com/Dogfalo/materialize/blob/master/dist/css/materialize.css
|
||||||
|
.progress-materializecss {
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
height: 2px;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
background-color: transparent;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.indeterminate {
|
||||||
|
background-color: #002fff;
|
||||||
|
|
||||||
|
&:before{
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
background-color: inherit;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
will-change: left, right;
|
||||||
|
animation: indeterminate 3.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
background-color: inherit;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
will-change: left, right;
|
||||||
|
animation: indeterminate-short 3.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
|
||||||
|
animation-delay: 2.15s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes indeterminate {
|
||||||
|
0% {
|
||||||
|
left: -35%;
|
||||||
|
right: 100%;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
left: 100%;
|
||||||
|
right: -90%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
left: 100%;
|
||||||
|
right: -90%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes indeterminate-short {
|
||||||
|
0% {
|
||||||
|
left: -200%;
|
||||||
|
right: 100%;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
left: 107%;
|
||||||
|
right: -8%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
left: 107%;
|
||||||
|
right: -8%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.progress-container{
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
}
|
||||||
|
// IRRELEVANTS STYLES //
|
||||||
|
/////////////////////////
|
||||||
@@ -1,30 +1,4 @@
|
|||||||
.form-group--select-list {
|
|
||||||
.bp3-popover-target {
|
|
||||||
.bp3-icon {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
margin: 7px;
|
|
||||||
|
|
||||||
+ .bp3-button-text {
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bp3-button {
|
|
||||||
padding-left: 10px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group--select-list {
|
|
||||||
.bp3-popover-open {
|
|
||||||
.bp3-button {
|
|
||||||
border-color: #80bdff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bp3-button {
|
.bp3-button {
|
||||||
min-width: 32px;
|
min-width: 32px;
|
||||||
@@ -41,24 +15,6 @@
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
min-height: 29px;
|
min-height: 29px;
|
||||||
}
|
}
|
||||||
.form-group--select-list & {
|
|
||||||
border-radius: 2px;
|
|
||||||
|
|
||||||
&,
|
|
||||||
&:hover {
|
|
||||||
box-shadow: 0 0 0 transparent;
|
|
||||||
border: 1px solid #ced4da;
|
|
||||||
|
|
||||||
&:not(:disabled) {
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.form-group--select-list.bp3-intent-danger & {
|
|
||||||
& {
|
|
||||||
border-color: #db3737;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bp3-button-group.bp3-minimal .bp3-button {
|
.bp3-button-group.bp3-minimal .bp3-button {
|
||||||
|
|||||||
@@ -167,16 +167,70 @@ label.bp3-label {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.form-group--select-list {
|
.form-group--select-list {
|
||||||
|
|
||||||
|
.form-group--select-list.bp3-intent-danger & {
|
||||||
|
& {
|
||||||
|
border-color: #db3737;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.#{$ns}-button {
|
.#{$ns}-button {
|
||||||
color: #8d8d8d;
|
color: #8d8d8d;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-right: 25px;
|
padding-right: 25px;
|
||||||
|
justify-content: start;
|
||||||
|
|
||||||
|
&:not([class*="bp3-intent-"]):not(.bp3-minimal){
|
||||||
|
&,
|
||||||
|
&:hover {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 transparent;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
|
||||||
|
&:not(:disabled) {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:focus{
|
||||||
|
box-shadow: 0 0 0 1px #116cd0;
|
||||||
|
border-color: #116cd0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.bp3-fill {
|
&.bp3-fill {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bp3-popover-target.bp3-popover-open{
|
||||||
|
|
||||||
|
.#{$ns}-button {
|
||||||
|
|
||||||
|
&:not([class*="bp3-intent-"]):not(.bp3-minimal):not(:disabled),
|
||||||
|
&:not([class*="bp3-intent-"]):not(.bp3-minimal):hover:not(:disabled){
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-popover-target {
|
||||||
|
.bp3-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
margin: 7px;
|
||||||
|
|
||||||
|
+ .bp3-button-text {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-button {
|
||||||
|
padding-left: 10px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.bp3-intent-danger {
|
&.bp3-intent-danger {
|
||||||
.bp3-button:not(.bp3-minimal) {
|
.bp3-button:not(.bp3-minimal) {
|
||||||
border-color: #db3737;
|
border-color: #db3737;
|
||||||
@@ -184,6 +238,15 @@ label.bp3-label {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.select-list--fill-popover {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.bp3-transition-container,
|
||||||
|
.bp3-popover {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.select-list--tooltip-items .bp3-popover-target {
|
.select-list--tooltip-items .bp3-popover-target {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,19 +79,18 @@ $dashboard-views-bar-height: 45px;
|
|||||||
.bp3-button:not([class*='bp3-intent-']):not(.bp3-minimal) {
|
.bp3-button:not([class*='bp3-intent-']):not(.bp3-minimal) {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-color: #eed1f2;
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
height: 35px;
|
height: 32px;
|
||||||
width: 35px;
|
width: 32px;
|
||||||
|
|
||||||
.user-text {
|
.user-text {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #804F87;
|
color: #fff;
|
||||||
}
|
}
|
||||||
&,
|
&,
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
background-color: #EEB1F7;
|
background-color: #cb20e5;
|
||||||
border: 0;
|
border: 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
@@ -120,7 +119,7 @@ $dashboard-views-bar-height: 45px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__back-link {
|
&__back-link {
|
||||||
margin-left: 24px;
|
margin-left: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@@ -128,6 +127,15 @@ $dashboard-views-bar-height: 45px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__organization-name{
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #818ca9;
|
||||||
|
margin: 0 0 0 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
&__actions-bar {
|
&__actions-bar {
|
||||||
border-bottom: 2px solid #eaeaea;
|
border-bottom: 2px solid #eaeaea;
|
||||||
|
|
||||||
@@ -224,14 +232,7 @@ $dashboard-views-bar-height: 45px;
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
h3 {
|
h3 {
|
||||||
border-left: 1px solid #d9d9d9;
|
|
||||||
padding-left: 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 400;
|
|
||||||
color: #76768b;
|
|
||||||
margin: 0 0 0 12px;
|
|
||||||
padding-top: 4px;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.button--view-edit {
|
.button--view-edit {
|
||||||
|
|||||||
@@ -32,17 +32,17 @@
|
|||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-color: #eed1f2;
|
background-color: #eed1f2;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
height: 35px;
|
height: 32px;
|
||||||
width: 35px;
|
width: 32px;
|
||||||
|
|
||||||
.user-text {
|
.user-text {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #804f87;
|
color: #fff;
|
||||||
}
|
}
|
||||||
&,
|
&,
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
background-color: #eed1f2;
|
background-color: #cb20e5;
|
||||||
border: 0;
|
border: 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import Currency from 'js-money/lib/currency';
|
|||||||
import PProgress from 'p-progress';
|
import PProgress from 'p-progress';
|
||||||
import accounting from 'accounting';
|
import accounting from 'accounting';
|
||||||
import deepMapKeys from 'deep-map-keys';
|
import deepMapKeys from 'deep-map-keys';
|
||||||
|
import { createSelectorCreator, defaultMemoize } from 'reselect';
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
export function removeEmptyFromObject(obj) {
|
export function removeEmptyFromObject(obj) {
|
||||||
obj = Object.assign({}, obj);
|
obj = Object.assign({}, obj);
|
||||||
@@ -411,6 +412,10 @@ export const transfromToSnakeCase = (object) => {
|
|||||||
return deepMapKeys(object, (key) => _.snakeCase(key));
|
return deepMapKeys(object, (key) => _.snakeCase(key));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const transformTableQueryToParams = (object) => {
|
||||||
|
return transfromToSnakeCase(object);
|
||||||
|
};
|
||||||
|
|
||||||
export function flatObject(obj) {
|
export function flatObject(obj) {
|
||||||
const flatObject = {};
|
const flatObject = {};
|
||||||
const path = []; // current path
|
const path = []; // current path
|
||||||
@@ -467,3 +472,19 @@ export function transactionNumber(prefix, number) {
|
|||||||
export function safeCallback(callback, ...args) {
|
export function safeCallback(callback, ...args) {
|
||||||
return () => callback && callback(...args);
|
return () => callback && callback(...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const createDeepEqualSelector = createSelectorCreator(
|
||||||
|
defaultMemoize,
|
||||||
|
isEqual
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines whether the table has empty status.
|
||||||
|
*/
|
||||||
|
export const isTableEmptyStatus = ({ data, pagination, filterMeta }) => {
|
||||||
|
return [
|
||||||
|
_.isEmpty(data),
|
||||||
|
_.isEmpty(filterMeta.view),
|
||||||
|
pagination.page === 1,
|
||||||
|
].every(cond => cond === true)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user