From 72ed9d572f86dd57befed3d6f0b280841a0a500a Mon Sep 17 00:00:00 2001 From: Wojciech Mista Date: Mon, 14 Oct 2024 12:07:57 +0200 Subject: [PATCH] Back links with pagination and filters (#5200) * improve backlink in order details * fix tests * pass navigator opts to datagrid * make hook generic * fix ts * fix test * back links with pagination and filters * back links with pagination and filters --- .changeset/pretty-cobras-remember.md | 5 +++++ .../AttributeListDatagrid/AttributeListDatagrid.tsx | 8 +++++++- .../components/AttributePage/AttributePage.tsx | 11 ++++++++--- .../CategoryListDatagrid/CategoryListDatagrid.tsx | 7 +++++++ .../CategoryUpdatePage/CategoryUpdatePage.tsx | 10 ++++++++-- .../CollectionDetailsPage/CollectionDetailsPage.tsx | 11 ++++++++--- .../CollectionListPage/CollectionListPage.tsx | 8 +++++++- src/components/TableRowLink/TableRowLink.tsx | 8 +++++--- .../CustomerDetailsPage/CustomerDetailsPage.tsx | 13 +++++++++---- .../CustomerListDatagrid/CustomerListDatagrid.tsx | 7 +++++++ .../DiscountDetailsPage/DiscountDetailsPage.tsx | 5 +++-- .../DiscountListPage/DiscountListPage.tsx | 8 +++++++- .../components/SaleDetailsPage/SaleDetailsPage.tsx | 9 +++++++-- .../components/SaleListPage/SaleListPage.tsx | 8 +++++++- .../VoucherDetailsPage/VoucherDetailsPage.tsx | 11 ++++++++--- .../VoucherListDatagrid/VoucherListDatagrid.tsx | 8 +++++++- src/discounts/discountsUrls.ts | 2 ++ .../views/DiscountDetails/DiscountDetails.tsx | 9 +++++++-- .../GiftCardUpdatePageHeader.tsx | 6 +++++- .../GiftCardsListDatagrid/GiftCardsListDatagrid.tsx | 7 +++++++ src/hooks/useBackLinkWithState.ts | 4 +++- .../PageTypeDetailsPage/PageTypeDetailsPage.tsx | 11 ++++++++--- .../components/PageTypeList/PageTypeList.tsx | 11 ++++++++++- .../components/PageDetailsPage/PageDetailsPage.tsx | 11 ++++++++--- src/pages/components/PageListPage/PageListPage.tsx | 10 +++++++++- .../PermissionGroupDetailsPage.tsx | 11 ++++++++--- .../PermissionGroupListDatagrid.tsx | 8 +++++++- .../ProductTypeDetailsPage.tsx | 10 +++++++--- .../components/ProductTypeList/ProductTypeList.tsx | 11 ++++++++++- .../components/ProductListPage/ProductListPage.tsx | 8 +++++++- .../ProductUpdatePage/ProductUpdatePage.tsx | 8 ++++++-- .../ShippingZoneDetailsPage.tsx | 11 ++++++++--- .../ShippingZonesListDatagrid.tsx | 7 +++++++ .../StaffDetailsPage/StaffDetailsPage.tsx | 11 ++++++++--- .../StaffListDatagrid/StaffListDatagrid.tsx | 6 +++++- .../WarehouseDetailsPage/WarehouseDetailsPage.tsx | 11 ++++++++--- .../components/WarehouseList/WarehouseList.tsx | 11 ++++++++++- 37 files changed, 260 insertions(+), 61 deletions(-) create mode 100644 .changeset/pretty-cobras-remember.md diff --git a/.changeset/pretty-cobras-remember.md b/.changeset/pretty-cobras-remember.md new file mode 100644 index 00000000000..6a69551c8fc --- /dev/null +++ b/.changeset/pretty-cobras-remember.md @@ -0,0 +1,5 @@ +--- +"saleor-dashboard": patch +--- + +Back buttons now navigate to the list with previously used filters and pagination applied. diff --git a/src/attributes/components/AttributeListDatagrid/AttributeListDatagrid.tsx b/src/attributes/components/AttributeListDatagrid/AttributeListDatagrid.tsx index 826f16d8e2c..16b61903a88 100644 --- a/src/attributes/components/AttributeListDatagrid/AttributeListDatagrid.tsx +++ b/src/attributes/components/AttributeListDatagrid/AttributeListDatagrid.tsx @@ -14,6 +14,7 @@ import { Item } from "@glideapps/glide-data-grid"; import { Box } from "@saleor/macaw-ui-next"; import React, { useCallback, useMemo } from "react"; import { useIntl } from "react-intl"; +import { useLocation } from "react-router"; import { attributesListStaticColumnsAdapter, createGetCellContent } from "./datagrid"; import { messages } from "./messages"; @@ -33,6 +34,7 @@ export const AttributeListDatagrid = ({ onUpdateListSettings, }: AttributeListDatagridProps) => { const datagridState = useDatagridChangeState(); + const location = useLocation(); const navigate = useNavigator(); const intl = useIntl(); const attributesListStaticColumns = useMemo( @@ -66,7 +68,11 @@ export const AttributeListDatagrid = ({ const rowData: AttributeFragment = attributes[row]; if (rowData) { - navigate(attributeUrl(rowData.id)); + navigate(attributeUrl(rowData.id), { + state: { + prevLocation: location, + }, + }); } }, [attributes], diff --git a/src/attributes/components/AttributePage/AttributePage.tsx b/src/attributes/components/AttributePage/AttributePage.tsx index e141fa7b149..e9fc36838ad 100644 --- a/src/attributes/components/AttributePage/AttributePage.tsx +++ b/src/attributes/components/AttributePage/AttributePage.tsx @@ -1,4 +1,4 @@ -import { attributeListUrl } from "@dashboard/attributes/urls"; +import { attributeListPath } from "@dashboard/attributes/urls"; import { ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES } from "@dashboard/attributes/utils/data"; import { TopNav } from "@dashboard/components/AppLayout/TopNav"; import CardSpacer from "@dashboard/components/CardSpacer"; @@ -18,6 +18,7 @@ import { AttributeTypeEnum, MeasurementUnitsEnum, } from "@dashboard/graphql"; +import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState"; import { SubmitPromise } from "@dashboard/hooks/useForm"; import useNavigator from "@dashboard/hooks/useNavigator"; import { ListSettings, ReorderAction } from "@dashboard/types"; @@ -135,6 +136,10 @@ const AttributePage: React.FC = ({ }); }; + const attributePageBackLink = useBackLinkWithState({ + path: attributeListPath, + }); + return (
{({ change, set, data, isSaveDisabled, submit, errors, setError, clearErrors }) => { @@ -144,7 +149,7 @@ const AttributePage: React.FC = ({ <> = ({ {attribute !== null && } - navigate(attributeListUrl())} /> + navigate(attributePageBackLink)} /> { + const location = useLocation(); const datagridState = useDatagridChangeState(); const intl = useIntl(); const memoizedStaticColumns = useMemo( @@ -103,6 +105,11 @@ export const CategoryListDatagrid = ({ staticColumns={staticColumns} /> )} + navigatorOpts={{ + state: { + prevLocation: location, + }, + }} /> diff --git a/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.tsx b/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.tsx index 99fe426e124..112259fa674 100644 --- a/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.tsx +++ b/src/categories/components/CategoryUpdatePage/CategoryUpdatePage.tsx @@ -1,4 +1,4 @@ -import { categoryListUrl, categoryUrl } from "@dashboard/categories/urls"; +import { categoryListPath, categoryUrl } from "@dashboard/categories/urls"; import { TopNav } from "@dashboard/components/AppLayout/TopNav"; import { CardSpacer } from "@dashboard/components/CardSpacer"; import { ConfirmButtonTransitionState } from "@dashboard/components/ConfirmButton"; @@ -8,6 +8,7 @@ import { Savebar } from "@dashboard/components/Savebar"; import { SeoForm } from "@dashboard/components/SeoForm"; import { Tab, TabContainer } from "@dashboard/components/Tab"; import { CategoryDetailsQuery, ProductErrorFragment } from "@dashboard/graphql"; +import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState"; import { SubmitPromise } from "@dashboard/hooks/useForm"; import useNavigator from "@dashboard/hooks/useNavigator"; import { sprinkles } from "@saleor/macaw-ui-next"; @@ -74,7 +75,12 @@ export const CategoryUpdatePage: React.FC = ({ }: CategoryUpdatePageProps) => { const intl = useIntl(); const navigate = useNavigator(); - const backHref = category?.parent?.id ? categoryUrl(category?.parent?.id) : categoryListUrl(); + + const categoryBackListUrl = useBackLinkWithState({ + path: categoryListPath, + }); + + const backHref = category?.parent?.id ? categoryUrl(category?.parent?.id) : categoryBackListUrl; return ( diff --git a/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx b/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx index b56067800da..d7476afd192 100644 --- a/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx +++ b/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx @@ -1,6 +1,6 @@ // @ts-strict-ignore import { ChannelCollectionData } from "@dashboard/channels/utils"; -import { collectionListUrl } from "@dashboard/collections/urls"; +import { collectionListPath } from "@dashboard/collections/urls"; import { TopNav } from "@dashboard/components/AppLayout/TopNav"; import { CardSpacer } from "@dashboard/components/CardSpacer"; import ChannelsAvailabilityCard from "@dashboard/components/ChannelsAvailabilityCard"; @@ -15,6 +15,7 @@ import { CollectionErrorFragment, PermissionEnum, } from "@dashboard/graphql"; +import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState"; import { SubmitPromise } from "@dashboard/hooks/useForm"; import useNavigator from "@dashboard/hooks/useNavigator"; import React from "react"; @@ -62,6 +63,10 @@ const CollectionDetailsPage: React.FC = ({ const intl = useIntl(); const navigate = useNavigator(); + const collectionListBackLink = useBackLinkWithState({ + path: collectionListPath, + }); + return ( = ({ > {({ change, data, handlers, submit, isSaveDisabled }) => ( - + @@ -138,7 +143,7 @@ const CollectionDetailsPage: React.FC = ({ - navigate(collectionListUrl())} /> + navigate(collectionListBackLink)} /> = ({ ...listProps }) => { const intl = useIntl(); + const location = useLocation(); const navigate = useNavigator(); const filterStructure = createFilterStructure(intl, filterOpts); const [isFilterPresetOpen, setFilterPresetOpen] = useState(false); @@ -140,7 +142,11 @@ const CollectionListPage: React.FC = ({ selectedChannelId={selectedChannelId} filterDependency={filterDependency} onRowClick={id => { - navigate(collectionUrl(id)); + navigate(collectionUrl(id), { + state: { + prevLocation: location, + }, + }); }} hasRowHover={!isFilterPresetOpen} rowAnchor={collectionUrl} diff --git a/src/components/TableRowLink/TableRowLink.tsx b/src/components/TableRowLink/TableRowLink.tsx index ce7cf799009..3eed8ca40a7 100644 --- a/src/components/TableRowLink/TableRowLink.tsx +++ b/src/components/TableRowLink/TableRowLink.tsx @@ -3,13 +3,15 @@ import { TableRow, TableRowTypeMap } from "@material-ui/core"; import { makeStyles } from "@saleor/macaw-ui"; import clsx from "clsx"; import React, { forwardRef } from "react"; -import { Link } from "react-router-dom"; +import { Link, LinkProps } from "react-router-dom"; type MaterialTableRowPropsType = TableRowTypeMap["props"]; +type LocationDescriptor = LinkProps["to"]; + export interface TableRowLinkProps extends MaterialTableRowPropsType { children: React.ReactNode; - href?: string; + href?: string | LocationDescriptor; className?: string; linkClassName?: string; onClick?: () => void; @@ -28,7 +30,7 @@ const TableRowLink = forwardRef((props, const { href, children, linkClassName, onClick, ...restProps } = props; const classes = useStyles(); - if (!href || isExternalURL(href)) { + if (!href || (typeof href === "string" && isExternalURL(href))) { return ( {children} diff --git a/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx b/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx index f136b13c012..8115a2c38fb 100644 --- a/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx +++ b/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx @@ -15,9 +15,10 @@ import { Metadata } from "@dashboard/components/Metadata/Metadata"; import { MetadataFormData } from "@dashboard/components/Metadata/types"; import RequirePermissions from "@dashboard/components/RequirePermissions"; import { Savebar } from "@dashboard/components/Savebar"; -import { customerAddressesUrl, customerListUrl } from "@dashboard/customers/urls"; +import { customerAddressesUrl, customerListPath } from "@dashboard/customers/urls"; import CustomerGiftCardsCard from "@dashboard/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCard"; import { AccountErrorFragment, CustomerDetailsQuery, PermissionEnum } from "@dashboard/graphql"; +import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState"; import { SubmitPromise } from "@dashboard/hooks/useForm"; import useNavigator from "@dashboard/hooks/useNavigator"; import { sectionNames } from "@dashboard/intl"; @@ -81,6 +82,10 @@ const CustomerDetailsPage: React.FC = ({ customerId, ); + const customerBackLink = useBackLinkWithState({ + path: customerListPath, + }); + return ( {({ change, data, isSaveDisabled, submit }) => { @@ -88,11 +93,11 @@ const CustomerDetailsPage: React.FC = ({ return ( - + {extensionMenuItems.length > 0 && } - + {intl.formatMessage(sectionNames.customers)} = ({ - navigate(customerListUrl())} /> + navigate(customerBackLink)} /> { const intl = useIntl(); + const location = useLocation(); const datagrid = useDatagridChangeState(); const userPermissions = useUserPermissions(); const hasManageOrdersPermission = @@ -135,6 +137,11 @@ export const CustomerListDatagrid = ({ onToggle={handlers.onToggle} /> )} + navigatorOpts={{ + state: { + prevLocation: location, + }, + }} /> diff --git a/src/discounts/components/DiscountDetailsPage/DiscountDetailsPage.tsx b/src/discounts/components/DiscountDetailsPage/DiscountDetailsPage.tsx index b56b1fb4a32..a73775aeccb 100644 --- a/src/discounts/components/DiscountDetailsPage/DiscountDetailsPage.tsx +++ b/src/discounts/components/DiscountDetailsPage/DiscountDetailsPage.tsx @@ -1,7 +1,6 @@ import { TopNav } from "@dashboard/components/AppLayout"; import { ConfirmButtonTransitionState } from "@dashboard/components/ConfirmButton"; import { DetailPageLayout } from "@dashboard/components/Layouts"; -import { discountListUrl } from "@dashboard/discounts/discountsUrls"; import { Rule } from "@dashboard/discounts/models"; import { DiscoutFormData } from "@dashboard/discounts/types"; import { @@ -38,6 +37,7 @@ export interface DiscountDetailsPageProps { onRuleDeleteSubmit: (id: string) => void; ruleDeleteButtonState: ConfirmButtonTransitionState; onBack: () => void; + backLinkHref: string; } export const DiscountDetailsPage = ({ @@ -55,13 +55,14 @@ export const DiscountDetailsPage = ({ ruleCreateButtonState, ruleUpdateButtonState, ruleDeleteButtonState, + backLinkHref, }: DiscountDetailsPageProps) => { const intl = useIntl(); const formErrors = getFormErrors(["name"], errors); return ( - + = ({ }) => { const intl = useIntl(); const navigation = useNavigator(); + const location = useLocation(); const [isFilterPresetOpen, setFilterPresetOpen] = useState(false); const handleRowClick = (id: string) => { - navigation(discountUrl(id)); + navigation(discountUrl(id), { + state: { + prevLocation: location, + }, + }); }; return ( diff --git a/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx b/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx index f3df8fbb391..b5c92b6c650 100644 --- a/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx +++ b/src/discounts/components/SaleDetailsPage/SaleDetailsPage.tsx @@ -14,7 +14,7 @@ import { createSaleUpdateHandler, } from "@dashboard/discounts/handlers"; import { itemsQuantityMessages } from "@dashboard/discounts/translations"; -import { saleListUrl } from "@dashboard/discounts/urls"; +import { saleListPath, saleListUrl } from "@dashboard/discounts/urls"; import { SALE_UPDATE_FORM_ID } from "@dashboard/discounts/views/SaleDetails/types"; import { DiscountErrorFragment, @@ -22,6 +22,7 @@ import { SaleDetailsFragment, SaleType as SaleTypeEnum, } from "@dashboard/graphql"; +import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState"; import { SubmitPromise } from "@dashboard/hooks/useForm"; import useNavigator from "@dashboard/hooks/useNavigator"; import { mapMetadataItemToInput } from "@dashboard/utils/maps"; @@ -149,6 +150,10 @@ const SaleDetailsPage: React.FC = ({ const checkIfSaveIsDisabled = (data: SaleDetailsPageFormData) => data.channelListings?.some(channel => validateSalePrice(data, channel)) || disabled; + const saleListBackLink = useBackLinkWithState({ + path: saleListPath, + }); + return ( = ({ return ( - + diff --git a/src/discounts/components/SaleListPage/SaleListPage.tsx b/src/discounts/components/SaleListPage/SaleListPage.tsx index 265bf253f6d..e854b7eba7f 100644 --- a/src/discounts/components/SaleListPage/SaleListPage.tsx +++ b/src/discounts/components/SaleListPage/SaleListPage.tsx @@ -18,6 +18,7 @@ import { import { Box, Button, ChevronRightIcon } from "@saleor/macaw-ui-next"; import React, { useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; +import { useLocation } from "react-router"; import { SaleListDatagrid } from "../SaleListDatagrid"; import { createFilterStructure, SaleFilterKeys, SaleListFilterOpts } from "./filters"; @@ -52,12 +53,17 @@ const SaleListPage: React.FC = ({ ...listProps }) => { const intl = useIntl(); + const location = useLocation(); const navigation = useNavigator(); const structure = createFilterStructure(intl, filterOpts); const [isFilterPresetOpen, setFilterPresetOpen] = useState(false); const filterDependency = structure.find(getByName("channel")); const handleRowClick = (id: string) => { - navigation(saleUrl(id)); + navigation(saleUrl(id), { + state: { + prevLocation: location, + }, + }); }; return ( diff --git a/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx b/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx index 65684f71730..3c5790fcd43 100644 --- a/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx +++ b/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.tsx @@ -17,7 +17,7 @@ import { } from "@dashboard/discounts/handlers"; import { itemsQuantityMessages } from "@dashboard/discounts/translations"; import { DiscountTypeEnum, RequirementsPicker } from "@dashboard/discounts/types"; -import { voucherListUrl } from "@dashboard/discounts/urls"; +import { voucherListPath } from "@dashboard/discounts/urls"; import { DiscountErrorFragment, DiscountValueTypeEnum, @@ -25,6 +25,7 @@ import { VoucherDetailsFragment, VoucherTypeEnum, } from "@dashboard/graphql"; +import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState"; import { UseListSettings } from "@dashboard/hooks/useListSettings"; import { LocalPagination } from "@dashboard/hooks/useLocalPaginator"; import useNavigator from "@dashboard/hooks/useNavigator"; @@ -209,6 +210,10 @@ const VoucherDetailsPage: React.FC = ({ privateMetadata: voucher?.privateMetadata.map(mapMetadataItemToInput), }; + const voucherListBackLink = useBackLinkWithState({ + path: voucherListPath, + }); + return ( {({ change, data, submit, triggerChange, set }) => { @@ -224,7 +229,7 @@ const VoucherDetailsPage: React.FC = ({ return ( - + = ({ - navigate(voucherListUrl())} /> + navigate(voucherListBackLink)} /> handleSubmit(data)} diff --git a/src/discounts/components/VoucherListDatagrid/VoucherListDatagrid.tsx b/src/discounts/components/VoucherListDatagrid/VoucherListDatagrid.tsx index 17faa536b3c..358530e061f 100644 --- a/src/discounts/components/VoucherListDatagrid/VoucherListDatagrid.tsx +++ b/src/discounts/components/VoucherListDatagrid/VoucherListDatagrid.tsx @@ -17,6 +17,7 @@ import { Item } from "@glideapps/glide-data-grid"; import { Box } from "@saleor/macaw-ui-next"; import React, { useCallback, useMemo } from "react"; import { useIntl } from "react-intl"; +import { useLocation } from "react-router"; import { createGetCellContent, vouchersListStaticColumnsAdapter } from "./datagrid"; import { messages } from "./messages"; @@ -41,6 +42,7 @@ export const VoucherListDatagrid = ({ onUpdateListSettings, }: VoucherListDatagridProps) => { const datagridState = useDatagridChangeState(); + const location = useLocation(); const navigate = useNavigator(); const { locale } = useLocale(); const intl = useIntl(); @@ -77,7 +79,11 @@ export const VoucherListDatagrid = ({ const rowData: VoucherFragment = vouchers[row]; if (rowData) { - navigate(voucherUrl(rowData.id)); + navigate(voucherUrl(rowData.id), { + state: { + prevLocation: location, + }, + }); } }, [vouchers], diff --git a/src/discounts/discountsUrls.ts b/src/discounts/discountsUrls.ts index 2ee8175cdc3..2f4992ebc27 100644 --- a/src/discounts/discountsUrls.ts +++ b/src/discounts/discountsUrls.ts @@ -10,6 +10,8 @@ export enum DiscountListUrlSortField { const discountSection = "/discounts/sales"; +export const discountSalesListPath = discountSection; + export type DiscountListUrlDialog = TabActionDialog; export type DiscountListUrlSort = Sort; diff --git a/src/discounts/views/DiscountDetails/DiscountDetails.tsx b/src/discounts/views/DiscountDetails/DiscountDetails.tsx index 6042f9c4411..fb99bf7999d 100644 --- a/src/discounts/views/DiscountDetails/DiscountDetails.tsx +++ b/src/discounts/views/DiscountDetails/DiscountDetails.tsx @@ -2,7 +2,8 @@ import useAppChannel from "@dashboard/components/AppLayout/AppChannelContext"; import { WindowTitle } from "@dashboard/components/WindowTitle"; import { DiscountDeleteModal } from "@dashboard/discounts/components/DiscountDeleteModal"; import { DiscountDetailsPage } from "@dashboard/discounts/components/DiscountDetailsPage"; -import { discountListUrl, DiscountUrlQueryParams } from "@dashboard/discounts/discountsUrls"; +import { discountSalesListPath, DiscountUrlQueryParams } from "@dashboard/discounts/discountsUrls"; +import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState"; import useNavigator from "@dashboard/hooks/useNavigator"; import { commonMessages } from "@dashboard/intl"; import { getMutationErrors } from "@dashboard/misc"; @@ -34,6 +35,9 @@ export const DiscountDetails = ({ id }: DiscountDetailsProps) => { const { promotionRuleUpdate, promotionRuleUpdateOpts } = usePromotionRuleUpdate(id); const { promotionRuleCreate, promotionRuleCreateOpts } = usePromotionRuleCreate(id); const { promotionRuleDelete, promotionRuleDeleteOpts } = usePromotionRuleDelete(id); + const discountListBackLink = useBackLinkWithState({ + path: discountSalesListPath, + }); const onSubmit = createUpdateHandler(promotionData?.promotion, variables => promotionUpdate({ variables }), ); @@ -74,7 +78,7 @@ export const DiscountDetails = ({ id }: DiscountDetailsProps) => { promotionRuleDeleteOpts.loading } onBack={() => { - navigate(discountListUrl()); + navigate(discountListBackLink); }} channels={availableChannels} onSubmit={onSubmit} @@ -86,6 +90,7 @@ export const DiscountDetails = ({ id }: DiscountDetailsProps) => { ruleCreateButtonState={promotionRuleCreateOpts.status} onRuleDeleteSubmit={onRuleDeleteSubmit} ruleDeleteButtonState={promotionRuleDeleteOpts.status} + backLinkHref={discountListBackLink} /> { const classes = useStyles(); + const giftCardBackLink = useBackLinkWithState({ + path: giftCardsListPath, + }); const intl = useIntl(); const { canManageChannels } = useGiftCardPermissions(); const { giftCard } = useGiftCardDetails(); @@ -36,7 +40,7 @@ const GiftCardUpdatePageHeader: React.FC = () => { return ( <> {title} diff --git a/src/giftCards/GiftCardsList/GiftCardsListDatagrid/GiftCardsListDatagrid.tsx b/src/giftCards/GiftCardsList/GiftCardsListDatagrid/GiftCardsListDatagrid.tsx index ab800a311dd..a6790f5b3e6 100644 --- a/src/giftCards/GiftCardsList/GiftCardsListDatagrid/GiftCardsListDatagrid.tsx +++ b/src/giftCards/GiftCardsList/GiftCardsListDatagrid/GiftCardsListDatagrid.tsx @@ -15,6 +15,7 @@ import { Box, useTheme } from "@saleor/macaw-ui-next"; import isEqual from "lodash/isEqual"; import React, { useCallback, useEffect, useMemo } from "react"; import { useIntl } from "react-intl"; +import { useLocation } from "react-router"; import { messages as filterLabels } from "../filters"; import { useGiftCardList } from "../providers/GiftCardListProvider"; @@ -26,6 +27,7 @@ import { messages } from "./messages"; export const GiftCardsListDatagrid = () => { const datagridState = useDatagridChangeState(); const navigate = useNavigator(); + const location = useLocation(); const intl = useIntl(); const { loading, @@ -169,6 +171,11 @@ export const GiftCardsListDatagrid = () => { onToggle={handlers.onToggle} /> )} + navigatorOpts={{ + state: { + prevLocation: location, + }, + }} /> diff --git a/src/hooks/useBackLinkWithState.ts b/src/hooks/useBackLinkWithState.ts index 0ab46203920..52e40e65eaf 100644 --- a/src/hooks/useBackLinkWithState.ts +++ b/src/hooks/useBackLinkWithState.ts @@ -22,6 +22,8 @@ interface UseBackLinkWithState { path: string; } +const getPath = (path: string) => (path.endsWith("/") ? path.substring(0, path.length - 1) : path); + export const useBackLinkWithState = ({ path }: UseBackLinkWithState) => { const location = useLocation(); const [backLink, setBackLink] = useState(path); @@ -31,7 +33,7 @@ export const useBackLinkWithState = ({ path }: UseBackLinkWithState) => { const previousUrl = getPreviousUrl(location as LocationWithState); // Prevent other links from being set as back link - const isCorrectPath = previousUrl?.includes(path); + const isCorrectPath = previousUrl?.includes(getPath(path)); if (isCorrectPath && previousUrl) { setBackLink(previousUrl); diff --git a/src/pageTypes/components/PageTypeDetailsPage/PageTypeDetailsPage.tsx b/src/pageTypes/components/PageTypeDetailsPage/PageTypeDetailsPage.tsx index 921a86e0527..f72ad1c8e05 100644 --- a/src/pageTypes/components/PageTypeDetailsPage/PageTypeDetailsPage.tsx +++ b/src/pageTypes/components/PageTypeDetailsPage/PageTypeDetailsPage.tsx @@ -9,9 +9,10 @@ import { Metadata } from "@dashboard/components/Metadata"; import { MetadataFormData } from "@dashboard/components/Metadata/types"; import { Savebar } from "@dashboard/components/Savebar"; import { AttributeTypeEnum, PageErrorFragment, PageTypeDetailsFragment } from "@dashboard/graphql"; +import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState"; import useNavigator from "@dashboard/hooks/useNavigator"; import { commonMessages } from "@dashboard/intl"; -import { pageTypeListUrl } from "@dashboard/pageTypes/urls"; +import { pageTypeListPath } from "@dashboard/pageTypes/urls"; import { ListActions, ReorderEvent } from "@dashboard/types"; import { mapMetadataItemToInput } from "@dashboard/utils/maps"; import useMetadataChangeTrigger from "@dashboard/utils/metadata/useMetadataChangeTrigger"; @@ -96,6 +97,10 @@ const PageTypeDetailsPage: React.FC = props => { }); }; + const pageTypeListBackLink = useBackLinkWithState({ + path: pageTypeListPath, + }); + return ( {({ change, data, isSaveDisabled, submit }) => { @@ -103,7 +108,7 @@ const PageTypeDetailsPage: React.FC = props => { return ( - + = props => { - navigate(pageTypeListUrl())} /> + navigate(pageTypeListBackLink)} /> = props => { const { disabled, pageTypes, onSort, isChecked, selected, sort, toggle, toggleAll, toolbar } = props; + const location = useLocation(); const classes = useStyles(props); const numberOfColumns = pageTypes?.length === 0 ? 1 : 2; @@ -80,7 +82,14 @@ const PageTypeList: React.FC = props => { className={pageType ? classes.link : undefined} hover={!!pageType} key={pageType ? pageType.id : "skeleton"} - href={pageType && pageTypeUrl(pageType.id)} + href={ + pageType + ? { + pathname: pageTypeUrl(pageType.id), + state: { prevLocation: location }, + } + : undefined + } selected={isSelected} data-test-id={"id-" + pageType?.id} > diff --git a/src/pages/components/PageDetailsPage/PageDetailsPage.tsx b/src/pages/components/PageDetailsPage/PageDetailsPage.tsx index 1b089457dfb..95ffc514ce3 100644 --- a/src/pages/components/PageDetailsPage/PageDetailsPage.tsx +++ b/src/pages/components/PageDetailsPage/PageDetailsPage.tsx @@ -22,10 +22,11 @@ import { SearchPageTypesQuery, SearchProductsQuery, } from "@dashboard/graphql"; +import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState"; import useDateLocalize from "@dashboard/hooks/useDateLocalize"; import { SubmitPromise } from "@dashboard/hooks/useForm"; import useNavigator from "@dashboard/hooks/useNavigator"; -import { pageListUrl } from "@dashboard/pages/urls"; +import { pagesSection } from "@dashboard/pages/urls"; import { FetchMoreProps, RelayToFlat } from "@dashboard/types"; import { mapNodeToChoice } from "@dashboard/utils/maps"; import React from "react"; @@ -118,6 +119,10 @@ const PageDetailsPage: React.FC = ({ const handleSelectPageType = (pageTypeId: string) => onSelectPageType && onSelectPageType(pageTypeId); + const pageListBackLink = useBackLinkWithState({ + path: pagesSection, + }); + return ( = ({ return ( @@ -214,7 +219,7 @@ const PageDetailsPage: React.FC = ({ {page !== null && } - navigate(pageListUrl())} /> + navigate(pageListBackLink)} /> = ({ ...listProps }) => { const intl = useIntl(); + const location = useLocation(); const navigate = useNavigator(); const structure = createFilterStructure(intl, filterOpts); const [isFilterPresetOpen, setFilterPresetOpen] = React.useState(false); @@ -129,7 +131,13 @@ const PageListPage: React.FC = ({ {...listProps} hasRowHover={!isFilterPresetOpen} rowAnchor={pageUrl} - onRowClick={id => navigate(pageUrl(id))} + onRowClick={id => + navigate(pageUrl(id), { + state: { + prevLocation: location, + }, + }) + } /> diff --git a/src/permissionGroups/components/PermissionGroupDetailsPage/PermissionGroupDetailsPage.tsx b/src/permissionGroups/components/PermissionGroupDetailsPage/PermissionGroupDetailsPage.tsx index 33806602044..6d023b249c8 100644 --- a/src/permissionGroups/components/PermissionGroupDetailsPage/PermissionGroupDetailsPage.tsx +++ b/src/permissionGroups/components/PermissionGroupDetailsPage/PermissionGroupDetailsPage.tsx @@ -13,10 +13,11 @@ import { PermissionGroupErrorFragment, UserPermissionFragment, } from "@dashboard/graphql"; +import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState"; import { FormChange, SubmitPromise } from "@dashboard/hooks/useForm"; import useNavigator from "@dashboard/hooks/useNavigator"; import { buttonMessages } from "@dashboard/intl"; -import { MembersListUrlSortField, permissionGroupListUrl } from "@dashboard/permissionGroups/urls"; +import { MembersListUrlSortField, permissionGroupListPath } from "@dashboard/permissionGroups/urls"; import { ListActions, SortPage } from "@dashboard/types"; import { getFormErrors } from "@dashboard/utils/errors"; import getPermissionGroupErrorMessage from "@dashboard/utils/errors/permissionGroups"; @@ -98,6 +99,10 @@ export const PermissionGroupDetailsPage: React.FC {({ data, change, submit }) => { @@ -120,7 +125,7 @@ export const PermissionGroupDetailsPage: React.FC - + - navigate(permissionGroupListUrl())} /> + navigate(permissionGroupListBackLink)} /> { const intl = useIntl(); + const location = useLocation(); const datagridState = useDatagridChangeState(); const navigate = useNavigator(); const emptyColumn = useEmptyColumn(); @@ -69,7 +71,11 @@ export const PermissionGroupListDatagrid = ({ const rowData: PermissionGroupFragment = permissionGroups[row]; if (rowData) { - navigate(permissionGroupDetailsUrl(rowData.id)); + navigate(permissionGroupDetailsUrl(rowData.id), { + state: { + prevLocation: location, + }, + }); } }, [permissionGroups], diff --git a/src/productTypes/components/ProductTypeDetailsPage/ProductTypeDetailsPage.tsx b/src/productTypes/components/ProductTypeDetailsPage/ProductTypeDetailsPage.tsx index 4c9f73fef3e..728de0df46f 100644 --- a/src/productTypes/components/ProductTypeDetailsPage/ProductTypeDetailsPage.tsx +++ b/src/productTypes/components/ProductTypeDetailsPage/ProductTypeDetailsPage.tsx @@ -15,12 +15,13 @@ import { TaxClassBaseFragment, WeightUnitsEnum, } from "@dashboard/graphql"; +import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState"; import { SubmitPromise } from "@dashboard/hooks/useForm"; import useNavigator from "@dashboard/hooks/useNavigator"; import useStateFromProps from "@dashboard/hooks/useStateFromProps"; import { maybe } from "@dashboard/misc"; import { handleTaxClassChange } from "@dashboard/productTypes/handlers"; -import { productTypeListUrl } from "@dashboard/productTypes/urls"; +import { productTypeListPath } from "@dashboard/productTypes/urls"; import { FetchMoreProps, ListActions, ReorderEvent, UserError } from "@dashboard/types"; import { mapMetadataItemToInput } from "@dashboard/utils/maps"; import useMetadataChangeTrigger from "@dashboard/utils/metadata/useMetadataChangeTrigger"; @@ -93,6 +94,9 @@ const ProductTypeDetailsPage: React.FC = ({ }) => { const intl = useIntl(); const navigate = useNavigator(); + const productTypeListBackLink = useBackLinkWithState({ + path: productTypeListPath, + }); const { isMetadataModified, isPrivateMetadataModified, @@ -147,7 +151,7 @@ const ProductTypeDetailsPage: React.FC = ({ return ( - + = ({ - navigate(productTypeListUrl())} /> + navigate(productTypeListBackLink)} /> = props => { const { disabled, productTypes, onSort, isChecked, selected, sort, toggle, toggleAll, toolbar } = props; const classes = useStyles(props); + const location = useLocation(); return ( @@ -116,7 +118,14 @@ const ProductTypeList: React.FC = props => { className={productType ? classes.link : undefined} hover={!!productType} key={productType ? productType.id : "skeleton"} - href={productType && productTypeUrl(productType.id)} + href={ + productType + ? { + pathname: productTypeUrl(productType.id), + state: { prevLocation: location }, + } + : undefined + } selected={isSelected} data-test-id={"id-" + maybe(() => productType.id)} > diff --git a/src/products/components/ProductListPage/ProductListPage.tsx b/src/products/components/ProductListPage/ProductListPage.tsx index 1acb2c33f83..86dd93f1b6b 100644 --- a/src/products/components/ProductListPage/ProductListPage.tsx +++ b/src/products/components/ProductListPage/ProductListPage.tsx @@ -38,6 +38,7 @@ import { hasLimits, isLimitReached } from "@dashboard/utils/limits"; import { Box, Button, ChevronRightIcon, Text } from "@saleor/macaw-ui-next"; import React, { useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; +import { useLocation } from "react-router"; import { ProductListUrlSortField, productUrl } from "../../urls"; import { ProductListDatagrid } from "../ProductListDatagrid"; @@ -107,6 +108,7 @@ export const ProductListPage: React.FC = props => { ...listProps } = props; const intl = useIntl(); + const location = useLocation(); const navigate = useNavigator(); const [isFilterPresetOpen, setFilterPresetOpen] = useState(false); const limitReached = isLimitReached(limits, "productVariants"); @@ -274,7 +276,11 @@ export const ProductListPage: React.FC = props => { onUpdateListSettings={onUpdateListSettings} rowAnchor={productUrl} onRowClick={id => { - navigate(productUrl(id)); + navigate(productUrl(id), { + state: { + prevLocation: location, + }, + }); }} /> ) : ( diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index fa8295158a4..04bb508ad12 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -37,6 +37,7 @@ import { SearchProductsQuery, TaxClassBaseFragment, } from "@dashboard/graphql"; +import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState"; import { SubmitPromise } from "@dashboard/hooks/useForm"; import useNavigator from "@dashboard/hooks/useNavigator"; import useStateFromProps from "@dashboard/hooks/useStateFromProps"; @@ -45,7 +46,7 @@ import ProductExternalMediaDialog from "@dashboard/products/components/ProductEx import { ProductOrganization } from "@dashboard/products/components/ProductOrganization/ProductOrganization"; import { mapByChannel } from "@dashboard/products/components/ProductUpdatePage/utils"; import { defaultGraphiQLQuery } from "@dashboard/products/queries"; -import { productImageUrl, productListUrl } from "@dashboard/products/urls"; +import { productImageUrl, productListPath, productListUrl } from "@dashboard/products/urls"; import { ChoiceWithAncestors, getChoicesWithAncestors } from "@dashboard/products/utils/utils"; import { ProductVariantListError } from "@dashboard/products/views/ProductUpdate/handlers/errors"; import { UseProductUpdateHandlerError } from "@dashboard/products/views/ProductUpdate/handlers/useProductUpdateHandler"; @@ -223,6 +224,9 @@ export const ProductUpdatePage: React.FC = ({ context.setVariables(`{ "id": "${product?.id}" }`); context.setDevModeVisibility(true); }; + const backLinkProductUrl = useBackLinkWithState({ + path: productListPath, + }); return ( = ({ return ( - + = ({ const warehouseChoices = warehouses.map(warehouseToChoice); const { makeChangeHandler: makeMetadataChangeHandler } = useMetadataChangeTrigger(); + const shippingZonesListBackLink = useBackLinkWithState({ + path: shippingZonesListPath, + }); + return ( {({ change, data, isSaveDisabled, submit }) => { @@ -109,7 +114,7 @@ const ShippingZoneDetailsPage: React.FC = ({ return ( - + @@ -169,7 +174,7 @@ const ShippingZoneDetailsPage: React.FC = ({ - navigate(shippingZonesListUrl())} /> + navigate(shippingZonesListBackLink)} /> shippingZonesListStaticColumnsAdapter(intl), @@ -100,6 +102,11 @@ export const ShippingZoneListDatagrid = ({ onRowClick={handleRowClick} rowAnchor={handleRowAnchor} recentlyAddedColumn={recentlyAddedColumn} + navigatorOpts={{ + state: { + prevLocation: location, + }, + }} /> diff --git a/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx b/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx index 2fdef34f3b9..e5a439ccdba 100644 --- a/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx +++ b/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx @@ -13,12 +13,13 @@ import { StaffMemberDetailsFragment, UserFragment, } from "@dashboard/graphql"; +import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState"; import { SubmitPromise } from "@dashboard/hooks/useForm"; import useLocale from "@dashboard/hooks/useLocale"; import useNavigator from "@dashboard/hooks/useNavigator"; import { getUserName } from "@dashboard/misc"; import UserStatus from "@dashboard/staff/components/UserStatus"; -import { staffListUrl } from "@dashboard/staff/urls"; +import { staffListPath } from "@dashboard/staff/urls"; import { getMemberPermissionGroups, isMemberActive } from "@dashboard/staff/utils"; import { FetchMoreProps, RelayToFlat, SearchPageProps } from "@dashboard/types"; import { Option, Text } from "@saleor/macaw-ui-next"; @@ -81,6 +82,10 @@ const StaffDetailsPage: React.FC = ({ const isActive = isMemberActive(staffMember); const permissionGroups = getMemberPermissionGroups(staffMember); + const staffListBackLink = useBackLinkWithState({ + path: staffListPath, + }); + const initialForm: StaffDetailsFormData = { email: staffMember?.email || "", firstName: staffMember?.firstName || "", @@ -94,7 +99,7 @@ const StaffDetailsPage: React.FC = ({ {({ data: formData, change, isSaveDisabled, submit }) => { return ( - + = ({ {canRemove && } - navigate(staffListUrl())} /> + navigate(staffListBackLink)} /> { const datagridState = useDatagridChangeState(); const navigate = useNavigator(); + const location = useLocation(); const intl = useIntl(); const { theme: currentTheme } = useTheme(); const emptyColumn = useEmptyColumn(); @@ -66,7 +68,9 @@ export const StaffListDatagrid = ({ const rowData: StaffMember = staffMembers[row]; if (rowData) { - navigate(staffMemberDetailsUrl(rowData?.id)); + navigate(staffMemberDetailsUrl(rowData?.id), { + state: { prevLocation: location }, + }); } }, [staffMembers], diff --git a/src/warehouses/components/WarehouseDetailsPage/WarehouseDetailsPage.tsx b/src/warehouses/components/WarehouseDetailsPage/WarehouseDetailsPage.tsx index 663c48467d9..495644dd184 100644 --- a/src/warehouses/components/WarehouseDetailsPage/WarehouseDetailsPage.tsx +++ b/src/warehouses/components/WarehouseDetailsPage/WarehouseDetailsPage.tsx @@ -15,12 +15,13 @@ import { WarehouseErrorFragment, } from "@dashboard/graphql"; import useAddressValidation from "@dashboard/hooks/useAddressValidation"; +import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState"; import { SubmitPromise } from "@dashboard/hooks/useForm"; import useNavigator from "@dashboard/hooks/useNavigator"; import useStateFromProps from "@dashboard/hooks/useStateFromProps"; import createSingleAutocompleteSelectHandler from "@dashboard/utils/handlers/singleAutocompleteSelectChangeHandler"; import { mapCountriesToChoices, mapEdgesToItems } from "@dashboard/utils/maps"; -import { warehouseListUrl } from "@dashboard/warehouses/urls"; +import { warehouseListPath } from "@dashboard/warehouses/urls"; import React from "react"; import { useIntl } from "react-intl"; @@ -72,6 +73,10 @@ const WarehouseDetailsPage: React.FC = ({ streetAddress2: warehouse?.address.streetAddress2 ?? "", }; + const warehouseListBackLink = useBackLinkWithState({ + path: warehouseListPath, + }); + return ( {({ change, data, isSaveDisabled, submit, set }) => { @@ -85,7 +90,7 @@ const WarehouseDetailsPage: React.FC = ({ return ( - + @@ -116,7 +121,7 @@ const WarehouseDetailsPage: React.FC = ({ - navigate(warehouseListUrl())} /> + navigate(warehouseListBackLink)} /> ({ @@ -62,6 +63,7 @@ const numberOfColumns = 3; const WarehouseList: React.FC = props => { const { warehouses, disabled, settings, sort, onUpdateListSettings, onRemove, onSort } = props; const classes = useStyles(props); + const location = useLocation(); return ( @@ -102,7 +104,14 @@ const WarehouseList: React.FC = props => { warehouses, warehouse => (