diff --git a/client/src/components/ContextMenu.js b/client/src/components/ContextMenu.js new file mode 100644 index 000000000..10dca8452 --- /dev/null +++ b/client/src/components/ContextMenu.js @@ -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 ( +
+ } + usePortal={false} + onInteraction={handleInteraction} + {...popoverProps} + /> +
+ ); +} + +export default memo(ContextMenu, (prevProps, nextProps) => { + if ( + prevProps.isOpen === nextProps.isOpen && + prevProps.bindMenu.style === nextProps.bindMenu.style + ) { + return true; + } else { + return false; + } +}); diff --git a/client/src/components/ContextMenu.tsx b/client/src/components/ContextMenu.tsx deleted file mode 100644 index 94ec49b83..000000000 --- a/client/src/components/ContextMenu.tsx +++ /dev/null @@ -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 =
{this.state.menu}
; - 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 ( -
- } - transitionDuration={TRANSITION_DURATION} - /> -
- ); - - } - - 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) => e.preventDefault(); - - private handleBackdropContextMenu = (e: React.MouseEvent) => { - // 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(); - } - }; -} \ No newline at end of file diff --git a/client/src/components/Dashboard/DashboardActionViewsList.js b/client/src/components/Dashboard/DashboardActionViewsList.js index 17a3f0d98..0e42a8b9c 100644 --- a/client/src/components/Dashboard/DashboardActionViewsList.js +++ b/client/src/components/Dashboard/DashboardActionViewsList.js @@ -10,22 +10,20 @@ import { Position, } from '@blueprintjs/core'; import { FormattedMessage as T } from 'react-intl'; -import { useHistory } from 'react-router-dom'; import { Icon } from 'components'; +/** + * Dashboard action views list. + */ export default function DashboardActionViewsList({ resourceName, views, onChange, }) { - const history = useHistory(); - const handleClickViewItem = (view) => { - history.push( - view ? `/${resourceName}/${view.id}/custom_view` : '/accounts', - ); onChange && onChange(view); }; + const viewsMenuItems = views.map((view) => { return ( handleClickViewItem(view)} text={view.name} /> diff --git a/client/src/components/Dashboard/DashboardTopbar.js b/client/src/components/Dashboard/DashboardTopbar.js index 0aa03bc72..24eb0396f 100644 --- a/client/src/components/Dashboard/DashboardTopbar.js +++ b/client/src/components/Dashboard/DashboardTopbar.js @@ -19,13 +19,14 @@ import { Icon, Hint, If } from 'components'; import withSearch from 'containers/GeneralSearch/withSearch'; import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withDashboard from 'containers/Dashboard/withDashboard'; +import withSettings from 'containers/Settings/withSettings'; + import QuickNewDropdown from 'containers/QuickNewDropdown/QuickNewDropdown'; import { compose } from 'utils'; function DashboardTopbar({ // #withDashboard pageTitle, - pageSubtitle, editViewId, // #withDashboardActions @@ -35,6 +36,9 @@ function DashboardTopbar({ // #withDashboard sidebarExpended, + // #withSettings + organizationName, + // #withGlobalSearch openGlobalSearch, }) { @@ -100,11 +104,7 @@ function DashboardTopbar({ - -

{pageSubtitle}

-
- - + - - {state.pages.map((page) => ( + - ))} - - + + {state.pages.map((page) => ( + + ))} + +
@@ -167,11 +178,11 @@ const Pagination = ({ value={state.currentPage} onChange={(event) => { const page = parseInt(event.currentTarget.value, 10); + const { size: pageSize } = state; dispatch({ type: 'PAGE_CHANGE', page }); - onPageChange(page); + onPageChange({ page, pageSize }); }} - minimal={true} />
@@ -186,26 +197,28 @@ const Pagination = ({ dispatch({ type: 'PAGE_SIZE_CHANGE', size: pageSize }); dispatch({ type: 'PAGE_CHANGE', page: 1 }); - onPageSizeChange(pageSize, 1); + onPageSizeChange({ pageSize, page: 1 }); }} - minimal={true} />
- +
); -}; +} Pagination.propTypes = { - initialPage: PropTypes.number.isRequired, + currentPage: PropTypes.number.isRequired, size: PropTypes.number.isRequired, total: PropTypes.number.isRequired, onPageChange: PropTypes.func, @@ -213,7 +226,7 @@ Pagination.propTypes = { }; Pagination.defaultProps = { - initialPage: 1, + currentPage: 1, size: 25, }; diff --git a/client/src/components/index.js b/client/src/components/index.js index 3084f8c9b..c97be1dfa 100644 --- a/client/src/components/index.js +++ b/client/src/components/index.js @@ -45,7 +45,7 @@ import PageFormBigNumber from './PageFormBigNumber'; import AccountsMultiSelect from './AccountsMultiSelect'; import CustomersMultiSelect from './CustomersMultiSelect'; import Skeleton from './Skeleton' - +import ContextMenu from './ContextMenu' import TableFastCell from './Datatable/TableFastCell'; const Hint = FieldHint; @@ -99,4 +99,5 @@ export { CustomersMultiSelect, TableFastCell, Skeleton, + ContextMenu }; diff --git a/client/src/containers/Alerts/Items/ItemDeleteAlert.js b/client/src/containers/Alerts/Items/ItemDeleteAlert.js index 16a9c25cc..93e6a2a96 100644 --- a/client/src/containers/Alerts/Items/ItemDeleteAlert.js +++ b/client/src/containers/Alerts/Items/ItemDeleteAlert.js @@ -1,20 +1,18 @@ -import React, { useState } from 'react'; +import React from 'react'; import { FormattedMessage as T, FormattedHTMLMessage, useIntl, } from 'react-intl'; import { Intent, Alert } from '@blueprintjs/core'; -import { queryCache } from 'react-query'; import { AppToaster } from 'components'; import { handleDeleteErrors } from 'containers/Items/utils'; +import { useDeleteItem } from 'hooks/query'; -import { - useDeleteItem -} from 'hooks/query'; import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect'; import withAlertActions from 'containers/Alert/withAlertActions'; +import withItemsActions from 'containers/Items/withItemsActions'; import { compose } from 'utils'; @@ -30,15 +28,19 @@ function ItemDeleteAlert({ // #withAlertActions closeAlert, + + // #withItemsActions + addItemsTableQueries }) { const { mutateAsync: deleteItem, isLoading } = useDeleteItem(); const { formatMessage } = useIntl(); - // handle cancel delete item alert. + // Handle cancel delete item alert. const handleCancelItemDelete = () => { closeAlert(name); }; + // Handle confirm delete item. const handleConfirmDeleteItem = () => { deleteItem(itemId) .then(() => { @@ -48,6 +50,8 @@ function ItemDeleteAlert({ }), intent: Intent.SUCCESS, }); + // Reset to page number one. + addItemsTableQueries({ page: 1 }); }) .catch(({ errors }) => { handleDeleteErrors(errors); @@ -80,4 +84,5 @@ function ItemDeleteAlert({ export default compose( withAlertStoreConnect(), withAlertActions, + withItemsActions )(ItemDeleteAlert); diff --git a/client/src/containers/InventoryAdjustments/InventoryAdjustmentDataTable.js b/client/src/containers/InventoryAdjustments/InventoryAdjustmentDataTable.js index ad06fd434..4a2ce1330 100644 --- a/client/src/containers/InventoryAdjustments/InventoryAdjustmentDataTable.js +++ b/client/src/containers/InventoryAdjustments/InventoryAdjustmentDataTable.js @@ -179,6 +179,7 @@ function InventoryAdjustmentDataTable({ autoResetSortBy={false} autoResetPage={false} isLoading={isLoading} + noResults={'There is no inventory adjustments transactions yet.'} // pagesCount={inventoryAdjustmentsPagination.pagesCount} // initialPageSize={inventoryAdjustmentsPagination.pageSize} // initialPageIndex={inventoryAdjustmentsPagination.page - 1} diff --git a/client/src/containers/Items/ItemFormPage.js b/client/src/containers/Items/ItemFormPage.js index 50d4399bd..ab2098229 100644 --- a/client/src/containers/Items/ItemFormPage.js +++ b/client/src/containers/Items/ItemFormPage.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { useParams } from 'react-router-dom'; import { ItemFormProvider } from './ItemFormProvider'; @@ -12,8 +12,21 @@ import { compose } from 'utils'; /** * Item form page. */ -function ItemFormPage() { +function ItemFormPage({ + // #withDashboardActions + setDashboardBackLink +}) { 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 ( @@ -24,4 +37,6 @@ function ItemFormPage() { ); } -export default compose(withDashboardActions)(ItemFormPage); +export default compose( + withDashboardActions, +)(ItemFormPage); diff --git a/client/src/containers/Items/ItemsActionsBar.js b/client/src/containers/Items/ItemsActionsBar.js index 7d2b41f13..fadd462d7 100644 --- a/client/src/containers/Items/ItemsActionsBar.js +++ b/client/src/containers/Items/ItemsActionsBar.js @@ -53,7 +53,7 @@ function ItemsActionsBar({ // Handle tab changing. const handleTabChange = (viewId) => { addItemsTableQueries({ - custom_view_id: viewId.id || null, + customViewId: viewId.id || null, }); }; @@ -87,7 +87,7 @@ function ItemsActionsBar({ >