Add global channel picker (#841)

* Move theme switch to user menu

* Add global channel picker

* Fix picker styles

* Use app channel state

* Improve prop naming to indicate id vs slug

* Disable picker if no reason to pick channel

* Remove settings modal leftovers

* Remove channel settings dialog

* Remove unused props

* Skip channel fetching if user is not authenticated

* Remove channel selection from components

* Update messages

* Fix e2e tests

* Remove channel picker leftover

* Revert ChannelSettingsDialog deletion

* Update snapshots

* Update messages
This commit is contained in:
Dominik Żegleń 2020-11-23 10:39:24 +01:00 committed by GitHub
parent 607eba6a10
commit a175fb9497
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
88 changed files with 501 additions and 1809 deletions

View file

@ -1,6 +1,5 @@
import { LEFT_MENU_SELECTORS } from "../elements/account/left-menu/left-menu-selectors";
import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors";
import { BUTTON_SELECTORS } from "../elements/shared/button-selectors";
// <reference types="cypress" />
describe("Products", () => {
@ -14,8 +13,6 @@ describe("Products", () => {
.click()
.get(PRODUCTS_SELECTORS.products)
.click()
.get(BUTTON_SELECTORS.submit)
.click()
.get(PRODUCTS_SELECTORS.createProductBtn)
.click()
.get(PRODUCTS_SELECTORS.productNameInput)

View file

@ -1325,10 +1325,6 @@
"context": "field is optional",
"string": "(Optional)"
},
"src_dot_collections_dot_components_dot_CollectionListPage_dot_1391686013": {
"context": "button",
"string": "Settings"
},
"src_dot_collections_dot_components_dot_CollectionListPage_dot_1631917001": {
"context": "tab name",
"string": "All Collections"
@ -1651,10 +1647,6 @@
"context": "section header button",
"string": "Manage"
},
"src_dot_components_dot_ChannelsSelect_dot_4183335632": {
"context": "channel select label",
"string": "Channel:"
},
"src_dot_components_dot_ColumnPicker_dot_1483881697": {
"context": "button",
"string": "Reset"
@ -2062,6 +2054,10 @@
"context": "button",
"string": "Account Settings"
},
"src_dot_components_dot_UserChip_dot_85001470": {
"context": "button",
"string": "Enable Dark Mode"
},
"src_dot_components_dot_VisibilityCard_dot_1311467573": {
"string": "Show in product listings"
},
@ -2496,10 +2492,6 @@
"context": "sale name",
"string": "Name"
},
"src_dot_discounts_dot_components_dot_SaleListPage_dot_1391686013": {
"context": "button",
"string": "Settings"
},
"src_dot_discounts_dot_components_dot_SaleListPage_dot_1866913828": {
"string": "Search Sale"
},
@ -2660,10 +2652,6 @@
"context": "tab name",
"string": "All Vouchers"
},
"src_dot_discounts_dot_components_dot_VoucherListPage_dot_1391686013": {
"context": "button",
"string": "Settings"
},
"src_dot_discounts_dot_components_dot_VoucherListPage_dot_1930485532": {
"string": "Search Voucher"
},
@ -3004,9 +2992,6 @@
"src_dot_home_dot_components_dot_HomeActivityCard_dot_placed": {
"string": "Order #{orderId} was placed"
},
"src_dot_home_dot_components_dot_HomeHeader_dot_4019698341": {
"string": "Channel"
},
"src_dot_hooks_dot_3382262667": {
"string": "Variant {name} has been set as default."
},
@ -3163,10 +3148,6 @@
"context": "button",
"string": "Add products"
},
"src_dot_orders_dot_components_dot_OrderDraftListPage_dot_1391686013": {
"context": "button",
"string": "Settings"
},
"src_dot_orders_dot_components_dot_OrderDraftListPage_dot_2826235371": {
"context": "button",
"string": "Create order"
@ -3491,10 +3472,6 @@
"context": "generate invoice button",
"string": "Generate"
},
"src_dot_orders_dot_components_dot_OrderListPage_dot_1391686013": {
"context": "button",
"string": "Settings"
},
"src_dot_orders_dot_components_dot_OrderListPage_dot_2826235371": {
"context": "button",
"string": "Create order"
@ -4561,10 +4538,6 @@
"context": "product price",
"string": "Price"
},
"src_dot_products_dot_components_dot_ProductListPage_dot_1391686013": {
"context": "button",
"string": "Settings"
},
"src_dot_products_dot_components_dot_ProductListPage_dot_1542417144": {
"context": "button",
"string": "Create Product"
@ -5296,10 +5269,6 @@
"context": "header",
"string": "Shipping"
},
"src_dot_shipping_dot_components_dot_ShippingZonesListPage_dot_1391686013": {
"context": "button",
"string": "Settings"
},
"src_dot_shipping_dot_components_dot_ShippingZonesList_dot_120574110": {
"context": "sort shipping methods by zone, section header",
"string": "Shipping By Zone"

View file

@ -14,7 +14,7 @@ import TableCellAvatar, {
import TableHead from "@saleor/components/TableHead";
import TablePagination from "@saleor/components/TablePagination";
import { maybe, renderCollection } from "@saleor/misc";
import { ListActions, ListProps } from "@saleor/types";
import { ChannelProps, ListActions, ListProps } from "@saleor/types";
import React from "react";
import { FormattedMessage } from "react-intl";
@ -70,9 +70,11 @@ const useStyles = makeStyles(
}
);
interface CategoryProductListProps extends ListProps, ListActions {
interface CategoryProductListProps
extends ListProps,
ListActions,
ChannelProps {
channelsCount: number;
selectedChannel: string;
products: CategoryDetails_category_products_edges_node[];
}
@ -90,7 +92,7 @@ export const CategoryProductList: React.FC<CategoryProductListProps> = props =>
onNextPage,
onPreviousPage,
onRowClick,
selectedChannel
selectedChannelId
} = props;
const classes = useStyles(props);
@ -158,7 +160,7 @@ export const CategoryProductList: React.FC<CategoryProductListProps> = props =>
product => {
const isSelected = product ? isChecked(product.id) : false;
const channel = product?.channelListings.find(
listing => listing.channel.id === selectedChannel
listing => listing.channel.id === selectedChannelId
);
return (

View file

@ -1,36 +1,25 @@
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import { makeStyles } from "@material-ui/core/styles";
import CardTitle from "@saleor/components/CardTitle";
import { ChannelsSelect } from "@saleor/components/ChannelsSelect";
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
import useStateFromProps from "@saleor/hooks/useStateFromProps";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { ListActions, PageListProps } from "../../../types";
import { ChannelProps, ListActions, PageListProps } from "../../../types";
import { CategoryDetails_category_products_edges_node } from "../../types/CategoryDetails";
import CategoryProductList from "../CategoryProductList";
interface CategoryProductsProps extends PageListProps, ListActions {
interface CategoryProductsProps
extends PageListProps,
ListActions,
ChannelProps {
products: CategoryDetails_category_products_edges_node[];
channelChoices: SingleAutocompleteChoiceType[];
channelsCount: number;
categoryName: string;
}
const useStyles = makeStyles(
theme => ({
channelsSelectContainer: {
paddingTop: theme.spacing(2)
}
}),
{ name: "CategoryProducts" }
);
export const CategoryProducts: React.FC<CategoryProductsProps> = ({
channelChoices,
channelsCount,
products,
disabled,
@ -42,16 +31,12 @@ export const CategoryProducts: React.FC<CategoryProductsProps> = ({
categoryName,
isChecked,
selected,
selectedChannelId,
toggle,
toggleAll,
toolbar
}) => {
const intl = useIntl();
const classes = useStyles({});
const [channelChoice, setChannelChoice] = useStateFromProps(
channelChoices?.length ? channelChoices[0]?.value : ""
);
return (
<Card>
@ -72,16 +57,9 @@ export const CategoryProducts: React.FC<CategoryProductsProps> = ({
</Button>
}
/>
<CardContent className={classes.channelsSelectContainer}>
<ChannelsSelect
channelChoice={channelChoice}
channelChoices={channelChoices}
setChannelChoice={setChannelChoice}
/>
</CardContent>
<CategoryProductList
channelsCount={channelsCount}
selectedChannel={channelChoice}
selectedChannelId={selectedChannelId}
products={products}
disabled={disabled}
pageInfo={pageInfo}

View file

@ -18,7 +18,7 @@ import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { maybe } from "../../../misc";
import { TabListActions } from "../../../types";
import { ChannelProps, TabListActions } from "../../../types";
import CategoryDetailsForm from "../../components/CategoryDetailsForm";
import CategoryList from "../../components/CategoryList";
import {
@ -36,7 +36,8 @@ export enum CategoryPageTab {
}
export interface CategoryUpdatePageProps
extends TabListActions<"productListToolbar" | "subcategoryListToolbar"> {
extends TabListActions<"productListToolbar" | "subcategoryListToolbar">,
ChannelProps {
changeTab: (index: CategoryPageTab) => void;
currentTab: CategoryPageTab;
errors: ProductErrorFragment[];
@ -93,6 +94,7 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
isChecked,
productListToolbar,
selected,
selectedChannelId,
subcategoryListToolbar,
toggle,
toggleAll
@ -216,6 +218,7 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
toggle={toggle}
toggleAll={toggleAll}
selected={selected}
selectedChannelId={selectedChannelId}
isChecked={isChecked}
toolbar={productListToolbar}
/>

View file

@ -1,8 +1,8 @@
import DialogContentText from "@material-ui/core/DialogContentText";
import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import { useChannelsList } from "@saleor/channels/queries";
import ActionDialog from "@saleor/components/ActionDialog";
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
import NotFoundPage from "@saleor/components/NotFoundPage";
import { WindowTitle } from "@saleor/components/WindowTitle";
import useBulkActions from "@saleor/hooks/useBulkActions";
@ -14,6 +14,7 @@ import usePaginator, {
import { commonMessages } from "@saleor/intl";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler";
import { mapNodeToChoice } from "@saleor/utils/maps";
import {
useMetadataUpdate,
usePrivateMetadataUpdate
@ -79,12 +80,9 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
variables: { ...paginationState, id }
});
const { data: channelsData } = useChannelsList({});
const { availableChannels, channel } = useAppChannel();
const channelChoices = channelsData?.channels?.map(channel => ({
label: channel.name,
value: channel.id
}));
const channelChoices = mapNodeToChoice(availableChannels);
const category = data?.category;
@ -213,7 +211,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
<>
<WindowTitle title={maybe(() => data.category.name)} />
<CategoryUpdatePage
channelsCount={channelsData?.channels?.length}
channelsCount={availableChannels.length}
channelChoices={channelChoices}
changeTab={changeTab}
currentTab={params.activeTab}
@ -258,6 +256,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
data.category.products.edges.map(edge => edge.node)
)}
saveButtonBarState={updateResult.status}
selectedChannelId={channel.id}
subcategories={maybe(() =>
data.category.children.edges.map(edge => edge.node)
)}

View file

@ -8,10 +8,10 @@
export interface Channel_channel {
__typename: "Channel";
id: string;
isActive: boolean;
name: string;
slug: string;
id: string;
isActive: boolean;
currencyCode: string;
}

View file

@ -10,10 +10,10 @@ import { ChannelErrorCode } from "./../../types/globalTypes";
export interface ChannelActivate_channelActivate_channel {
__typename: "Channel";
id: string;
isActive: boolean;
name: string;
slug: string;
id: string;
isActive: boolean;
currencyCode: string;
}

View file

@ -10,10 +10,10 @@ import { ChannelCreateInput, ChannelErrorCode } from "./../../types/globalTypes"
export interface ChannelCreate_channelCreate_channel {
__typename: "Channel";
id: string;
isActive: boolean;
name: string;
slug: string;
id: string;
isActive: boolean;
currencyCode: string;
}

View file

@ -10,10 +10,10 @@ import { ChannelErrorCode } from "./../../types/globalTypes";
export interface ChannelDeactivate_channelDeactivate_channel {
__typename: "Channel";
id: string;
isActive: boolean;
name: string;
slug: string;
id: string;
isActive: boolean;
currencyCode: string;
}

View file

@ -10,10 +10,10 @@ import { ChannelDeleteInput, ChannelErrorCode } from "./../../types/globalTypes"
export interface ChannelDelete_channelDelete_channel {
__typename: "Channel";
id: string;
isActive: boolean;
name: string;
slug: string;
id: string;
isActive: boolean;
currencyCode: string;
}

View file

@ -10,10 +10,10 @@ import { ChannelUpdateInput, ChannelErrorCode } from "./../../types/globalTypes"
export interface ChannelUpdate_channelUpdate_channel {
__typename: "Channel";
id: string;
isActive: boolean;
name: string;
slug: string;
id: string;
isActive: boolean;
currencyCode: string;
}

View file

@ -8,10 +8,10 @@
export interface Channels_channels {
__typename: "Channel";
id: string;
isActive: boolean;
name: string;
slug: string;
id: string;
isActive: boolean;
currencyCode: string;
}

View file

@ -6,7 +6,7 @@ import { RequireOnlyOne } from "@saleor/misc";
import { ProductDetails_product } from "@saleor/products/types/ProductDetails";
import { ProductVariantDetails_productVariant } from "@saleor/products/types/ProductVariantDetails";
import { ShippingZone_shippingZone_shippingMethods_channelListings } from "@saleor/shipping/types/ShippingZone";
import { uniqBy } from "lodash";
import uniqBy from "lodash-es/uniqBy";
export interface Channel {
id: string;

View file

@ -16,14 +16,17 @@ import { sectionNames } from "@saleor/intl";
import React from "react";
import { useIntl } from "react-intl";
import { ListActions, PageListProps } from "../../../types";
import { ChannelProps, ListActions, PageListProps } from "../../../types";
import { CollectionDetails_collection } from "../../types/CollectionDetails";
import CollectionDetails from "../CollectionDetails/CollectionDetails";
import { CollectionImage } from "../CollectionImage/CollectionImage";
import CollectionProducts from "../CollectionProducts/CollectionProducts";
import CollectionUpdateForm, { CollectionUpdateData } from "./form";
export interface CollectionDetailsPageProps extends PageListProps, ListActions {
export interface CollectionDetailsPageProps
extends PageListProps,
ListActions,
ChannelProps {
channelsCount: number;
channelsErrors: CollectionChannelListingErrorFragment[];
collection: CollectionDetails_collection;
@ -31,7 +34,6 @@ export interface CollectionDetailsPageProps extends PageListProps, ListActions {
errors: CollectionErrorFragment[];
hasChannelChanged: boolean;
saveButtonBarState: ConfirmButtonTransitionState;
selectedChannel: string;
onBack: () => void;
onCollectionRemove: () => void;
onImageDelete: () => void;
@ -51,7 +53,7 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
errors,
hasChannelChanged,
saveButtonBarState,
selectedChannel,
selectedChannelId,
onBack,
onCollectionRemove,
onImageDelete,
@ -99,7 +101,7 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
<CollectionProducts
disabled={disabled}
channelsCount={channelsCount}
selectedChannel={selectedChannel}
selectedChannelId={selectedChannelId}
collection={collection}
{...collectionProductsProps}
/>

View file

@ -12,7 +12,7 @@ import TableCellHeader from "@saleor/components/TableCellHeader";
import TableHead from "@saleor/components/TableHead";
import TablePagination from "@saleor/components/TablePagination";
import { maybe, renderCollection } from "@saleor/misc";
import { ListActions, ListProps, SortPage } from "@saleor/types";
import { ChannelProps, ListActions, ListProps, SortPage } from "@saleor/types";
import { getArrowDirection } from "@saleor/utils/sort";
import React from "react";
import { FormattedMessage } from "react-intl";
@ -47,10 +47,10 @@ const useStyles = makeStyles(
interface CollectionListProps
extends ListProps,
ListActions,
SortPage<CollectionListUrlSortField> {
SortPage<CollectionListUrlSortField>,
ChannelProps {
collections: CollectionList_collections_edges_node[];
channelsCount: number;
selectedChannel: string;
}
const numberOfColumns = 4;
@ -70,7 +70,7 @@ const CollectionList: React.FC<CollectionListProps> = props => {
pageInfo,
isChecked,
selected,
selectedChannel,
selectedChannelId,
toggle,
toggleAll,
toolbar
@ -147,7 +147,7 @@ const CollectionList: React.FC<CollectionListProps> = props => {
collection => {
const isSelected = collection ? isChecked(collection.id) : false;
const channel = collection?.channelListings.find(
listing => listing.channel.id === selectedChannel
listing => listing.channel.id === selectedChannelId
);
return (
<TableRow

View file

@ -1,13 +1,12 @@
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import makeStyles from "@material-ui/core/styles/makeStyles";
import { CollectionListUrlSortField } from "@saleor/collections/urls";
import CardMenu from "@saleor/components/CardMenu";
import { Container } from "@saleor/components/Container";
import PageHeader from "@saleor/components/PageHeader";
import SearchBar from "@saleor/components/SearchBar";
import { sectionNames } from "@saleor/intl";
import {
ChannelProps,
ListActions,
PageListProps,
SearchPageProps,
@ -25,22 +24,12 @@ export interface CollectionListPageProps
ListActions,
SearchPageProps,
SortPage<CollectionListUrlSortField>,
TabPageProps {
TabPageProps,
ChannelProps {
collections: CollectionList_collections_edges_node[];
channelsCount: number;
selectedChannel: string;
onSettingsOpen?: () => void;
}
const useStyles = makeStyles(
theme => ({
settings: {
marginRight: theme.spacing(2)
}
}),
{ name: "CollectionListPage" }
);
const CollectionListPage: React.FC<CollectionListPageProps> = ({
channelsCount,
currentTab,
@ -49,34 +38,18 @@ const CollectionListPage: React.FC<CollectionListPageProps> = ({
onAdd,
onAll,
onSearchChange,
onSettingsOpen,
onTabChange,
onTabDelete,
onTabSave,
selectedChannel,
selectedChannelId,
tabs,
...listProps
}) => {
const intl = useIntl();
const classes = useStyles({});
return (
<Container>
<PageHeader title={intl.formatMessage(sectionNames.collections)}>
{!!onSettingsOpen && (
<CardMenu
className={classes.settings}
menuItems={[
{
label: intl.formatMessage({
defaultMessage: "Settings",
description: "button"
}),
onSelect: onSettingsOpen
}
]}
/>
)}
<Button
color="primary"
disabled={disabled}
@ -110,7 +83,7 @@ const CollectionListPage: React.FC<CollectionListPageProps> = ({
<CollectionList
disabled={disabled}
channelsCount={channelsCount}
selectedChannel={selectedChannel}
selectedChannelId={selectedChannelId}
{...listProps}
/>
</Card>

View file

@ -21,7 +21,7 @@ import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { maybe, renderCollection } from "../../../misc";
import { ListActions, PageListProps } from "../../../types";
import { ChannelProps, ListActions, PageListProps } from "../../../types";
import { CollectionDetails_collection } from "../../types/CollectionDetails";
const useStyles = makeStyles(
@ -55,10 +55,12 @@ const useStyles = makeStyles(
{ name: "CollectionProducts" }
);
export interface CollectionProductsProps extends PageListProps, ListActions {
export interface CollectionProductsProps
extends PageListProps,
ListActions,
ChannelProps {
collection: CollectionDetails_collection;
channelsCount: number;
selectedChannel: string;
onProductUnassign: (id: string, event: React.MouseEvent<any>) => void;
}
@ -75,7 +77,7 @@ const CollectionProducts: React.FC<CollectionProductsProps> = props => {
onProductUnassign,
onRowClick,
pageInfo,
selectedChannel,
selectedChannelId,
isChecked,
selected,
toggle,
@ -167,7 +169,7 @@ const CollectionProducts: React.FC<CollectionProductsProps> = props => {
const isSelected = product ? isChecked(product.id) : false;
const channel =
product?.channelListings.find(
listing => listing.channel.id === selectedChannel
listing => listing.channel.id === selectedChannelId
) || product?.channelListings[0];
return (

View file

@ -19,7 +19,7 @@ export enum CollectionListUrlFiltersEnum {
query = "query"
}
export type CollectionListUrlFilters = Filters<CollectionListUrlFiltersEnum>;
export type CollectionListUrlDialog = "remove" | "settings" | TabActionDialog;
export type CollectionListUrlDialog = "remove" | TabActionDialog;
export enum CollectionListUrlSortField {
name = "name",
available = "available",

View file

@ -336,7 +336,7 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({
collectionChannelsChoices?.length !== currentChannels?.length
}
channelsCount={channelsData?.channels?.length}
selectedChannel={selectedChannel}
selectedChannelId={selectedChannel}
openChannelsModal={handleChannelsModalOpen}
onChannelsChange={setCurrentChannels}
/>

View file

@ -1,14 +1,13 @@
import DialogContentText from "@material-ui/core/DialogContentText";
import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog";
import ActionDialog from "@saleor/components/ActionDialog";
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
import SaveFilterTabDialog, {
SaveFilterTabDialogFormData
} from "@saleor/components/SaveFilterTabDialog";
import useBulkActions from "@saleor/hooks/useBulkActions";
import useChannelsSettings from "@saleor/hooks/useChannelsSettings";
import useListSettings from "@saleor/hooks/useListSettings";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
@ -90,6 +89,9 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
}
}
});
const { availableChannels, channel } = useAppChannel();
const tabs = getFilterTabs();
const currentTab =
@ -114,12 +116,6 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
CollectionListUrlQueryParams
>(navigate, collectionListUrl, params);
const {
channelChoices,
handleChannelSelectConfirm,
selectedChannel
} = useChannelsSettings("collectionListChannel", { closeModal, openModal });
const handleTabChange = (tab: number) => {
reset();
navigate(
@ -151,16 +147,6 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
return (
<>
{!!channelChoices?.length && (
<ChannelSettingsDialog
channelsChoices={channelChoices}
defaultChoice={selectedChannel}
open={params.action === "settings"}
confirmButtonState="default"
onClose={closeModal}
onConfirm={handleChannelSelectConfirm}
/>
)}
<CollectionListPage
currentTab={currentTab}
initialSearch={params.query || ""}
@ -197,11 +183,8 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
selected={listElements.length}
toggle={toggle}
toggleAll={toggleAll}
channelsCount={channelChoices?.length}
selectedChannel={selectedChannel}
onSettingsOpen={
!!channelChoices?.length ? () => openModal("settings") : undefined
}
channelsCount={availableChannels?.length}
selectedChannelId={channel.id}
/>
<ActionDialog
open={params.action === "remove" && maybe(() => params.ids.length > 0)}

View file

@ -0,0 +1,75 @@
import { useAuth } from "@saleor/auth/AuthProvider";
import { useChannelsList } from "@saleor/channels/queries";
import { ChannelDetailsFragment } from "@saleor/fragments/types/ChannelDetailsFragment";
import useLocalStorage from "@saleor/hooks/useLocalStorage";
import React from "react";
interface UseAppChannel {
availableChannels: ChannelDetailsFragment[];
channel: ChannelDetailsFragment;
isPickerActive: boolean;
refreshChannels: () => void;
setChannel: (id: string) => void;
}
export interface AppChannelContextData extends UseAppChannel {
setPickerActive: (isActive: boolean) => void;
}
const AppChannelContext = React.createContext<AppChannelContextData>({
availableChannels: [],
channel: undefined,
isPickerActive: false,
refreshChannels: () => undefined,
setChannel: () => undefined,
setPickerActive: () => undefined
});
export const AppChannelProvider: React.FC = ({ children }) => {
const { isAuthenticated } = useAuth();
const [selectedChannel, setSelectedChannel] = useLocalStorage("channel", "");
const { data: channelData, refetch } = useChannelsList({
skip: !isAuthenticated
});
const [isPickerActive, setPickerActive] = React.useState(false);
React.useEffect(() => {
if (!selectedChannel) {
setSelectedChannel(channelData?.channels[0].id);
}
}, [channelData]);
const availableChannels = channelData?.channels || [];
const channel = availableChannels.find(
channel => channel.id === selectedChannel
);
return (
<AppChannelContext.Provider
value={{
availableChannels,
channel,
isPickerActive,
refreshChannels: refetch,
setChannel: setSelectedChannel,
setPickerActive
}}
>
{children}
</AppChannelContext.Provider>
);
};
AppChannelProvider.displayName = "AppChannelProvider";
function useAppChannel(enablePicker = true): UseAppChannel {
const { setPickerActive, ...data } = React.useContext(AppChannelContext);
React.useEffect(() => {
if (enablePicker) {
setPickerActive(true);
}
return () => setPickerActive(false);
}, [enablePicker]);
return data;
}
export default useAppChannel;

View file

@ -0,0 +1,51 @@
import makeStyles from "@material-ui/core/styles/makeStyles";
import { ChannelDetailsFragment } from "@saleor/fragments/types/ChannelDetailsFragment";
import { ChannelProps } from "@saleor/types";
import { mapNodeToChoice } from "@saleor/utils/maps";
import React from "react";
import SingleSelectField from "../SingleSelectField";
const useStyles = makeStyles(
theme => ({
root: {
"&& fieldset": {
borderColor: theme.palette.divider
},
marginRight: theme.spacing(2),
width: 192
}
}),
{
name: "AppChannelSelect"
}
);
export interface AppChannelSelectProps extends ChannelProps {
channels: ChannelDetailsFragment[];
disabled: boolean;
onChannelSelect: (id: string) => void;
}
const AppChannelSelect: React.FC<AppChannelSelectProps> = ({
channels,
disabled,
onChannelSelect,
selectedChannelId
}) => {
const classes = useStyles({});
return (
<div className={classes.root}>
<SingleSelectField
choices={mapNodeToChoice(channels)}
disabled={disabled}
value={selectedChannelId}
onChange={event => onChannelSelect(event.target.value)}
/>
</div>
);
};
AppChannelSelect.displayName = "AppChannelSelect";
export default AppChannelSelect;

View file

@ -20,10 +20,11 @@ import SideBar from "../SideBar";
import SideBarDrawer from "../SideBarDrawer/SideBarDrawer";
import UserChip from "../UserChip";
import AppActionContext from "./AppActionContext";
import useAppChannel from "./AppChannelContext";
import AppChannelSelect from "./AppChannelSelect";
import AppHeaderContext from "./AppHeaderContext";
import { appLoaderHeight } from "./consts";
import createMenuStructure from "./menuStructure";
import ThemeSwitch from "./ThemeSwitch";
const useStyles = makeStyles(
theme => ({
@ -128,6 +129,12 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
const [isNavigatorVisible, setNavigatorVisibility] = React.useState(false);
const isMdUp = useMediaQuery((theme: Theme) => theme.breakpoints.up("md"));
const [docked, setDocked] = React.useState(true);
const {
availableChannels,
channel,
isPickerActive,
setChannel
} = useAppChannel(false);
const menuStructure = createMenuStructure(intl);
const configurationMenu = createConfigurationMenu(intl);
@ -202,23 +209,26 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
)}
<div className={classes.spacer} />
<div className={classes.userBar}>
<ThemeSwitch
className={classes.darkThemeSwitch}
checked={isDark}
onClick={toggleTheme}
/>
<NavigatorButton
isMac={navigator.platform
.toLowerCase()
.includes("mac")}
onClick={() => setNavigatorVisibility(true)}
/>
<AppChannelSelect
channels={availableChannels}
disabled={!isPickerActive}
selectedChannelId={channel.id}
onChannelSelect={setChannel}
/>
<UserChip
isDarkThemeEnabled={isDark}
user={user}
onLogout={logout}
onProfileClick={() =>
navigate(staffMemberDetailsUrl(user.id))
}
user={user}
onThemeToggle={toggleTheme}
/>
</div>
</div>

View file

@ -1,51 +0,0 @@
import { makeStyles } from "@material-ui/core/styles";
import Switch, { SwitchProps } from "@material-ui/core/Switch";
import React from "react";
import MoonIcon from "../../icons/Moon";
import SunIcon from "../../icons/Sun";
const useStyles = makeStyles(
theme => ({
checked: {
"& svg": {
background: theme.palette.primary.main,
color: theme.palette.background.paper
}
},
colorPrimary: {},
root: {
"& svg": {
background: theme.palette.primary.main,
borderRadius: "100%",
height: 20,
width: 20
}
},
track: {
"$colorPrimary$checked + &": {
backgroundColor: theme.palette.background.paper
},
background: theme.palette.background.paper
}
}),
{
name: "ThemeSwitch"
}
);
const ThemeSwitch: React.FC<SwitchProps> = props => {
const classes = useStyles(props);
return (
<Switch
{...props}
classes={classes}
color="primary"
icon={<SunIcon />}
checkedIcon={<MoonIcon />}
/>
);
};
ThemeSwitch.displayName = "ThemeSwitch";
export default ThemeSwitch;

View file

@ -1,22 +0,0 @@
import { product as productFixture } from "@saleor/products/fixtures";
import Decorator from "@saleor/storybook/Decorator";
import { storiesOf } from "@storybook/react";
import React from "react";
import ChannelsSelect, { ChannelsSelectProps } from "./ChannelsSelect";
const product = productFixture("");
const channelChoices = product.channelListings.map(listing => ({
label: listing.channel.name,
value: listing.channel.id
}));
const props: ChannelsSelectProps = {
channelChoice: channelChoices[0].value,
channelChoices,
setChannelChoice: () => undefined
};
storiesOf("Generics / ChannelsSelect", module)
.addDecorator(Decorator)
.add("default", () => <ChannelsSelect {...props} />);

View file

@ -1,53 +0,0 @@
import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import LinkChoice from "@saleor/components/LinkChoice";
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
import { FormChange } from "@saleor/hooks/useForm";
import React from "react";
import { FormattedMessage } from "react-intl";
export interface ChannelsSelectProps {
channelChoices: SingleAutocompleteChoiceType[];
channelChoice: string;
setChannelChoice: FormChange;
}
const useStyles = makeStyles(
theme => ({
label: {
display: "inline-block",
marginRight: theme.spacing(1)
},
select: {
display: "inline-block"
}
}),
{ name: "ChannelsSelect" }
);
export const ChannelsSelect: React.FC<ChannelsSelectProps> = ({
channelChoice,
channelChoices,
setChannelChoice
}) => {
const classes = useStyles({});
return channelChoices?.length ? (
<>
<Typography className={classes.label}>
<FormattedMessage
defaultMessage="Channel:"
description="channel select label"
/>
</Typography>
<LinkChoice
className={classes.select}
choices={channelChoices}
name="channels"
value={channelChoice}
onChange={event => setChannelChoice(event.target.value)}
/>
</>
) : null;
};
export default ChannelsSelect;

View file

@ -1,2 +0,0 @@
export * from "./ChannelsSelect";
export { default } from "./ChannelsSelect";

View file

@ -1,6 +1,7 @@
import Avatar from "@material-ui/core/Avatar";
import Chip from "@material-ui/core/Chip";
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Grow from "@material-ui/core/Grow";
import Hidden from "@material-ui/core/Hidden";
import MenuItem from "@material-ui/core/MenuItem";
@ -8,12 +9,13 @@ import Menu from "@material-ui/core/MenuList";
import Paper from "@material-ui/core/Paper";
import Popper from "@material-ui/core/Popper";
import makeStyles from "@material-ui/core/styles/makeStyles";
import Switch from "@material-ui/core/Switch";
import { User } from "@saleor/fragments/types/User";
import ArrowDropdown from "@saleor/icons/ArrowDropdown";
import { getUserInitials, getUserName } from "@saleor/misc";
import classNames from "classnames";
import React from "react";
import { FormattedMessage } from "react-intl";
import { FormattedMessage, useIntl } from "react-intl";
const useStyles = makeStyles(
theme => ({
@ -46,11 +48,16 @@ const useStyles = makeStyles(
},
popover: {
marginTop: theme.spacing(2),
zIndex: 1
zIndex: 10
},
rotate: {
transform: "rotate(180deg)"
},
switch: {
"&&:hover": {
background: "transparent"
}
},
userChip: {
[theme.breakpoints.down("sm")]: {
height: 48
@ -74,19 +81,24 @@ const useStyles = makeStyles(
);
export interface UserChipProps {
isDarkThemeEnabled: boolean;
user: User;
onLogout: () => void;
onProfileClick: () => void;
onThemeToggle: () => void;
}
const UserChip: React.FC<UserChipProps> = ({
isDarkThemeEnabled,
user,
onLogout,
onProfileClick
onProfileClick,
onThemeToggle
}) => {
const classes = useStyles({});
const [isMenuOpened, setMenuState] = React.useState(false);
const anchor = React.useRef<HTMLDivElement>();
const intl = useIntl();
const handleLogout = () => {
setMenuState(false);
@ -170,6 +182,29 @@ const UserChip: React.FC<UserChipProps> = ({
description="button"
/>
</MenuItem>
<MenuItem
className={classes.userMenuItem}
data-test="themeSwitch"
data-test-is-dark={isDarkThemeEnabled}
>
<FormControlLabel
control={
<Switch
classes={{
switchBase: classes.switch
}}
checked={isDarkThemeEnabled}
color="primary"
disableRipple
/>
}
label={intl.formatMessage({
defaultMessage: "Enable Dark Mode",
description: "button"
})}
onChange={onThemeToggle}
/>
</MenuItem>
</Menu>
</ClickAwayListener>
</Paper>

View file

@ -21,14 +21,16 @@ import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { maybe, renderCollection } from "../../../misc";
import { ListActions, ListProps } from "../../../types";
import { ChannelProps, ListActions, ListProps } from "../../../types";
import { SaleDetails_sale } from "../../types/SaleDetails";
import { VoucherDetails_voucher } from "../../types/VoucherDetails";
export interface SaleProductsProps extends ListProps, ListActions {
export interface SaleProductsProps
extends ListProps,
ListActions,
ChannelProps {
discount: SaleDetails_sale | VoucherDetails_voucher;
channelsCount: number;
selectedChannel: string;
onProductAssign: () => void;
onProductUnassign: (id: string) => void;
}
@ -79,7 +81,7 @@ const DiscountProducts: React.FC<SaleProductsProps> = props => {
onNextPage,
isChecked,
selected,
selectedChannel,
selectedChannelId,
toggle,
toggleAll,
toolbar
@ -156,7 +158,7 @@ const DiscountProducts: React.FC<SaleProductsProps> = props => {
const isSelected = product ? isChecked(product.id) : false;
const channel =
product?.channelListings.find(
listing => listing.channel.id === selectedChannel
listing => listing.channel.id === selectedChannelId
) || product?.channelListings[0];
return (
<TableRow

View file

@ -17,7 +17,7 @@ import React from "react";
import { useIntl } from "react-intl";
import { maybe, splitDateTime } from "../../../misc";
import { ListProps, TabListActions } from "../../../types";
import { ChannelProps, ListProps, TabListActions } from "../../../types";
import { SaleType as SaleTypeEnum } from "../../../types/globalTypes";
import { SaleDetails_sale } from "../../types/SaleDetails";
import DiscountCategories from "../DiscountCategories";
@ -57,11 +57,11 @@ export interface SaleDetailsPageProps
extends Pick<ListProps, Exclude<keyof ListProps, "onRowClick">>,
TabListActions<
"categoryListToolbar" | "collectionListToolbar" | "productListToolbar"
> {
>,
ChannelProps {
activeTab: SaleDetailsPageTab;
errors: DiscountErrorFragment[];
sale: SaleDetails_sale;
selectedChannel: string;
allChannelsCount: number;
channelListings: ChannelSaleData[];
hasChannelChanged: boolean;
@ -119,7 +119,7 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
productListToolbar,
isChecked,
selected,
selectedChannel,
selectedChannelId,
toggle,
toggleAll
}) => {
@ -270,7 +270,7 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
pageInfo={pageInfo}
discount={sale}
channelsCount={allChannelsCount}
selectedChannel={selectedChannel}
selectedChannelId={selectedChannelId}
isChecked={isChecked}
selected={selected}
toggle={toggle}
@ -287,7 +287,10 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
/>
</div>
<div>
<SaleSummary selectedChannel={selectedChannel} sale={sale} />
<SaleSummary
selectedChannelId={selectedChannelId}
sale={sale}
/>
<CardSpacer />
<ChannelsAvailability
selectedChannelsCount={data.channelListings.length}

View file

@ -14,7 +14,7 @@ import TableHead from "@saleor/components/TableHead";
import TablePagination from "@saleor/components/TablePagination";
import { SaleListUrlSortField } from "@saleor/discounts/urls";
import { maybe, renderCollection } from "@saleor/misc";
import { ListActions, ListProps, SortPage } from "@saleor/types";
import { ChannelProps, ListActions, ListProps, SortPage } from "@saleor/types";
import { SaleType } from "@saleor/types/globalTypes";
import { getArrowDirection } from "@saleor/utils/sort";
import React from "react";
@ -25,9 +25,9 @@ import { SaleList_sales_edges_node } from "../../types/SaleList";
export interface SaleListProps
extends ListProps,
ListActions,
SortPage<SaleListUrlSortField> {
SortPage<SaleListUrlSortField>,
ChannelProps {
sales: SaleList_sales_edges_node[];
selectedChannel: string;
}
const useStyles = makeStyles(
@ -76,7 +76,7 @@ const SaleList: React.FC<SaleListProps> = props => {
onSort,
pageInfo,
sales,
selectedChannel,
selectedChannelId,
isChecked,
selected,
sort,
@ -170,7 +170,7 @@ const SaleList: React.FC<SaleListProps> = props => {
sale => {
const isSelected = sale ? isChecked(sale.id) : false;
const channel = sale?.channelListings?.find(
lisiting => lisiting.channel.id === selectedChannel
lisiting => lisiting.channel.id === selectedChannelId
);
return (
<TableRow

View file

@ -1,13 +1,12 @@
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import makeStyles from "@material-ui/core/styles/makeStyles";
import CardMenu from "@saleor/components/CardMenu";
import Container from "@saleor/components/Container";
import FilterBar from "@saleor/components/FilterBar";
import PageHeader from "@saleor/components/PageHeader";
import { SaleListUrlSortField } from "@saleor/discounts/urls";
import { sectionNames } from "@saleor/intl";
import {
ChannelProps,
FilterPageProps,
ListActions,
PageListProps,
@ -30,19 +29,10 @@ export interface SaleListPageProps
ListActions,
FilterPageProps<SaleFilterKeys, SaleListFilterOpts>,
SortPage<SaleListUrlSortField>,
TabPageProps {
TabPageProps,
ChannelProps {
sales: SaleList_sales_edges_node[];
selectedChannel: string;
onSettingsOpen?: () => void;
}
const useStyles = makeStyles(
theme => ({
settings: {
marginRight: theme.spacing(2)
}
}),
{ name: "SaleListPage" }
);
const SaleListPage: React.FC<SaleListPageProps> = ({
currentTab,
@ -52,7 +42,6 @@ const SaleListPage: React.FC<SaleListPageProps> = ({
onAll,
onFilterChange,
onSearchChange,
onSettingsOpen,
onTabChange,
onTabDelete,
onTabSave,
@ -60,26 +49,11 @@ const SaleListPage: React.FC<SaleListPageProps> = ({
...listProps
}) => {
const intl = useIntl();
const classes = useStyles({});
const structure = createFilterStructure(intl, filterOpts);
return (
<Container>
<PageHeader title={intl.formatMessage(sectionNames.sales)}>
{!!onSettingsOpen && (
<CardMenu
className={classes.settings}
menuItems={[
{
label: intl.formatMessage({
defaultMessage: "Settings",
description: "button"
}),
onSelect: onSettingsOpen
}
]}
/>
)}
<Button onClick={onAdd} variant="contained" color="primary">
<FormattedMessage defaultMessage="Create Sale" description="button" />
</Button>

View file

@ -10,6 +10,7 @@ import Money from "@saleor/components/Money";
import Percent from "@saleor/components/Percent";
import Skeleton from "@saleor/components/Skeleton";
import { commonMessages } from "@saleor/intl";
import { ChannelProps } from "@saleor/types";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
@ -17,16 +18,18 @@ import { maybe } from "../../../misc";
import { SaleType } from "../../../types/globalTypes";
import { SaleDetails_sale } from "../../types/SaleDetails";
export interface SaleSummaryProps {
selectedChannel: string;
export interface SaleSummaryProps extends ChannelProps {
sale: SaleDetails_sale;
}
const SaleSummary: React.FC<SaleSummaryProps> = ({ selectedChannel, sale }) => {
const SaleSummary: React.FC<SaleSummaryProps> = ({
selectedChannelId,
sale
}) => {
const intl = useIntl();
const channel = sale?.channelListings?.find(
listing => listing.channel.id === selectedChannel
listing => listing.channel.id === selectedChannelId
);
return (
<Card>

View file

@ -20,7 +20,7 @@ import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { maybe, splitDateTime } from "../../../misc";
import { ListProps, TabListActions } from "../../../types";
import { ChannelProps, ListProps, TabListActions } from "../../../types";
import {
DiscountValueTypeEnum,
VoucherTypeEnum
@ -73,12 +73,12 @@ export interface VoucherDetailsPageProps
extends Pick<ListProps, Exclude<keyof ListProps, "onRowClick">>,
TabListActions<
"categoryListToolbar" | "collectionListToolbar" | "productListToolbar"
> {
>,
ChannelProps {
activeTab: VoucherDetailsPageTab;
errors: DiscountErrorFragment[];
saveButtonBarState: ConfirmButtonTransitionState;
voucher: VoucherDetails_voucher;
selectedChannel: string;
allChannelsCount: number;
channelListings: ChannelVoucherData[];
hasChannelChanged: boolean;
@ -137,7 +137,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
toggle,
toggleAll,
selected,
selectedChannel,
selectedChannelId,
isChecked,
categoryListToolbar,
collectionListToolbar,
@ -145,7 +145,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
}) => {
const intl = useIntl();
const channel = voucher?.channelListings?.find(
listing => listing.channel.id === selectedChannel
listing => listing.channel.id === selectedChannelId
);
let requirementsPickerInitValue;
if (voucher?.minCheckoutItemsQuantity > 0) {
@ -331,7 +331,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
onRowClick={onProductClick}
pageInfo={pageInfo}
discount={voucher}
selectedChannel={selectedChannel}
selectedChannelId={selectedChannelId}
channelsCount={allChannelsCount}
isChecked={isChecked}
selected={selected}
@ -391,7 +391,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
<div>
<VoucherSummary
voucher={voucher}
selectedChannel={selectedChannel}
selectedChannelId={selectedChannelId}
/>
<CardSpacer />
<ChannelsAvailability

View file

@ -14,7 +14,7 @@ import TableHead from "@saleor/components/TableHead";
import TablePagination from "@saleor/components/TablePagination";
import { VoucherListUrlSortField } from "@saleor/discounts/urls";
import { maybe, renderCollection } from "@saleor/misc";
import { ListActions, ListProps, SortPage } from "@saleor/types";
import { ChannelProps, ListActions, ListProps, SortPage } from "@saleor/types";
import { DiscountValueTypeEnum } from "@saleor/types/globalTypes";
import { getArrowDirection } from "@saleor/utils/sort";
import { getFooterColSpanWithBulkActions } from "@saleor/utils/tables";
@ -26,9 +26,9 @@ import { VoucherList_vouchers_edges_node } from "../../types/VoucherList";
export interface VoucherListProps
extends ListProps,
ListActions,
SortPage<VoucherListUrlSortField> {
SortPage<VoucherListUrlSortField>,
ChannelProps {
vouchers: VoucherList_vouchers_edges_node[];
selectedChannel: string;
}
const useStyles = makeStyles(
@ -94,7 +94,7 @@ const VoucherList: React.FC<VoucherListProps> = props => {
vouchers,
isChecked,
selected,
selectedChannel,
selectedChannelId,
sort,
toggle,
toggleAll,
@ -219,7 +219,7 @@ const VoucherList: React.FC<VoucherListProps> = props => {
voucher => {
const isSelected = voucher ? isChecked(voucher.id) : false;
const channel = voucher?.channelListings?.find(
listing => listing.channel.id === selectedChannel
listing => listing.channel.id === selectedChannelId
);
const hasChannelsLoaded = voucher?.channelListings?.length;

View file

@ -1,13 +1,12 @@
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import makeStyles from "@material-ui/core/styles/makeStyles";
import CardMenu from "@saleor/components/CardMenu";
import Container from "@saleor/components/Container";
import FilterBar from "@saleor/components/FilterBar";
import PageHeader from "@saleor/components/PageHeader";
import { VoucherListUrlSortField } from "@saleor/discounts/urls";
import { sectionNames } from "@saleor/intl";
import {
ChannelProps,
FilterPageProps,
ListActions,
PageListProps,
@ -30,20 +29,10 @@ export interface VoucherListPageProps
ListActions,
FilterPageProps<VoucherFilterKeys, VoucherListFilterOpts>,
SortPage<VoucherListUrlSortField>,
TabPageProps {
TabPageProps,
ChannelProps {
vouchers: VoucherList_vouchers_edges_node[];
selectedChannel: string;
onSettingsOpen?: () => void;
}
const useStyles = makeStyles(
theme => ({
settings: {
marginRight: theme.spacing(2)
}
}),
{ name: "VoucherListPage" }
);
const VoucherListPage: React.FC<VoucherListPageProps> = ({
currentTab,
filterOpts,
@ -52,7 +41,6 @@ const VoucherListPage: React.FC<VoucherListPageProps> = ({
onAll,
onFilterChange,
onSearchChange,
onSettingsOpen,
onTabChange,
onTabDelete,
onTabSave,
@ -60,26 +48,11 @@ const VoucherListPage: React.FC<VoucherListPageProps> = ({
...listProps
}) => {
const intl = useIntl();
const classes = useStyles({});
const structure = createFilterStructure(intl, filterOpts);
return (
<Container>
<PageHeader title={intl.formatMessage(sectionNames.vouchers)}>
{onSettingsOpen && (
<CardMenu
className={classes.settings}
menuItems={[
{
label: intl.formatMessage({
defaultMessage: "Settings",
description: "button"
}),
onSelect: onSettingsOpen
}
]}
/>
)}
<Button onClick={onAdd} variant="contained" color="primary">
<FormattedMessage
defaultMessage="Create voucher"

View file

@ -10,6 +10,7 @@ import Money from "@saleor/components/Money";
import Percent from "@saleor/components/Percent";
import Skeleton from "@saleor/components/Skeleton";
import { commonMessages } from "@saleor/intl";
import { ChannelProps } from "@saleor/types";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
@ -18,20 +19,19 @@ import { DiscountValueTypeEnum } from "../../../types/globalTypes";
import { translateVoucherTypes } from "../../translations";
import { VoucherDetails_voucher } from "../../types/VoucherDetails";
export interface VoucherSummaryProps {
export interface VoucherSummaryProps extends ChannelProps {
voucher: VoucherDetails_voucher;
selectedChannel: string;
}
const VoucherSummary: React.FC<VoucherSummaryProps> = ({
selectedChannel,
selectedChannelId,
voucher
}) => {
const intl = useIntl();
const translatedVoucherTypes = translateVoucherTypes(intl);
const channel = voucher?.channelListings?.find(
listing => listing.channel.id === selectedChannel
listing => listing.channel.id === selectedChannelId
);
return (

View file

@ -29,7 +29,7 @@ export enum SaleListUrlFiltersWithMultipleValues {
}
export type SaleListUrlFilters = Filters<SaleListUrlFiltersEnum> &
FiltersWithMultipleValues<SaleListUrlFiltersWithMultipleValues>;
export type SaleListUrlDialog = "remove" | "settings" | TabActionDialog;
export type SaleListUrlDialog = "remove" | TabActionDialog;
export enum SaleListUrlSortField {
name = "name",
endDate = "end-date",
@ -79,7 +79,7 @@ export enum VoucherListUrlFiltersWithMultipleValues {
}
export type VoucherListUrlFilters = Filters<VoucherListUrlFiltersEnum> &
FiltersWithMultipleValues<VoucherListUrlFiltersWithMultipleValues>;
export type VoucherListUrlDialog = "remove" | "settings" | TabActionDialog;
export type VoucherListUrlDialog = "remove" | TabActionDialog;
export enum VoucherListUrlSortField {
code = "code",
endDate = "end-date",

View file

@ -279,7 +279,7 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
...(updateChannelsOpts.data
?.saleChannelListingUpdate.errors || [])
]}
selectedChannel={selectedChannel}
selectedChannelId={selectedChannel}
pageInfo={pageInfo}
openChannelsModal={handleChannelsModalOpen}
onChannelsChange={setCurrentChannels}

View file

@ -1,15 +1,14 @@
import DialogContentText from "@material-ui/core/DialogContentText";
import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog";
import ActionDialog from "@saleor/components/ActionDialog";
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
import SaveFilterTabDialog, {
SaveFilterTabDialogFormData
} from "@saleor/components/SaveFilterTabDialog";
import { WindowTitle } from "@saleor/components/WindowTitle";
import useBulkActions from "@saleor/hooks/useBulkActions";
import useChannelsSettings from "@saleor/hooks/useChannelsSettings";
import useListSettings from "@saleor/hooks/useListSettings";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
@ -64,18 +63,13 @@ export const SaleList: React.FC<SaleListProps> = ({ params }) => {
ListViews.SALES_LIST
);
const intl = useIntl();
const { channel } = useAppChannel();
const [openModal, closeModal] = createDialogActionHandlers<
SaleListUrlDialog,
SaleListUrlQueryParams
>(navigate, saleListUrl, params);
const {
channelChoices,
handleChannelSelectConfirm,
selectedChannel
} = useChannelsSettings("salesListChannel", { closeModal, openModal });
const paginationState = createPaginationState(settings.rowNumber, params);
const queryVariables = React.useMemo(
() => ({
@ -167,16 +161,6 @@ export const SaleList: React.FC<SaleListProps> = ({ params }) => {
return (
<>
<WindowTitle title={intl.formatMessage(sectionNames.sales)} />
{!!channelChoices?.length && (
<ChannelSettingsDialog
channelsChoices={channelChoices}
defaultChoice={selectedChannel}
open={params.action === "settings"}
confirmButtonState="default"
onClose={closeModal}
onConfirm={handleChannelSelectConfirm}
/>
)}
<SaleListPage
currentTab={currentTab}
filterOpts={getFilterOpts(params)}
@ -215,12 +199,7 @@ export const SaleList: React.FC<SaleListProps> = ({ params }) => {
<DeleteIcon />
</IconButton>
}
selectedChannel={selectedChannel}
onSettingsOpen={
!!channelChoices?.length
? () => openModal("settings")
: undefined
}
selectedChannelId={channel.id}
/>
<ActionDialog
confirmButtonState={saleBulkDeleteOpts.status}

View file

@ -7,6 +7,7 @@ import {
createSortedChannelsDataFromVoucher
} from "@saleor/channels/utils";
import ActionDialog from "@saleor/components/ActionDialog";
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
import AssignCategoriesDialog from "@saleor/components/AssignCategoryDialog";
import AssignCollectionDialog from "@saleor/components/AssignCollectionDialog";
import AssignProductDialog from "@saleor/components/AssignProductDialog";
@ -37,7 +38,6 @@ import {
} from "@saleor/discounts/urls";
import useBulkActions from "@saleor/hooks/useBulkActions";
import useChannels from "@saleor/hooks/useChannels";
import useLocalStorage from "@saleor/hooks/useLocalStorage";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import usePaginator, {
@ -145,8 +145,7 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
const [updateChannels, updateChannelsOpts] = useVoucherChannelListingUpdate(
{}
);
const [selectedChannel] = useLocalStorage("vouchersListChannel", "");
const { channel } = useAppChannel();
const handleVoucherDelete = (data: VoucherDelete) => {
if (data.voucherDelete.errors.length === 0) {
@ -295,7 +294,7 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
...(updateChannelsOpts.data
?.voucherChannelListingUpdate.errors || [])
]}
selectedChannel={selectedChannel}
selectedChannelId={channel.id}
pageInfo={pageInfo}
onNextPage={loadNextPage}
onPreviousPage={loadPreviousPage}

View file

@ -1,15 +1,14 @@
import DialogContentText from "@material-ui/core/DialogContentText";
import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog";
import ActionDialog from "@saleor/components/ActionDialog";
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
import SaveFilterTabDialog, {
SaveFilterTabDialogFormData
} from "@saleor/components/SaveFilterTabDialog";
import { WindowTitle } from "@saleor/components/WindowTitle";
import useBulkActions from "@saleor/hooks/useBulkActions";
import useChannelsSettings from "@saleor/hooks/useChannelsSettings";
import useListSettings from "@saleor/hooks/useListSettings";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
@ -65,17 +64,13 @@ export const VoucherList: React.FC<VoucherListProps> = ({ params }) => {
);
const intl = useIntl();
const { channel } = useAppChannel();
const [openModal, closeModal] = createDialogActionHandlers<
VoucherListUrlDialog,
VoucherListUrlQueryParams
>(navigate, voucherListUrl, params);
const {
channelChoices,
handleChannelSelectConfirm,
selectedChannel
} = useChannelsSettings("vouchersListChannel", { closeModal, openModal });
const paginationState = createPaginationState(settings.rowNumber, params);
const queryVariables = React.useMemo(
() => ({
@ -167,16 +162,6 @@ export const VoucherList: React.FC<VoucherListProps> = ({ params }) => {
return (
<>
<WindowTitle title={intl.formatMessage(sectionNames.vouchers)} />
{!!channelChoices?.length && (
<ChannelSettingsDialog
channelsChoices={channelChoices}
defaultChoice={selectedChannel}
open={params.action === "settings"}
confirmButtonState="default"
onClose={closeModal}
onConfirm={handleChannelSelectConfirm}
/>
)}
<VoucherListPage
currentTab={currentTab}
filterOpts={getFilterOpts(params)}
@ -215,12 +200,7 @@ export const VoucherList: React.FC<VoucherListProps> = ({ params }) => {
<DeleteIcon />
</IconButton>
}
selectedChannel={selectedChannel}
onSettingsOpen={
!!channelChoices?.length
? () => openModal("settings")
: undefined
}
selectedChannelId={channel.id}
/>
<ActionDialog
confirmButtonState={voucherBulkDeleteOpts.status}

View file

@ -1,11 +1,8 @@
import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import SingleSelectField, {
Choices
} from "@saleor/components/SingleSelectField";
import Skeleton from "@saleor/components/Skeleton";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { FormattedMessage } from "react-intl";
const useStyles = makeStyles(
theme => ({
@ -27,16 +24,12 @@ const useStyles = makeStyles(
interface HomeOrdersCardProps {
userName: string;
channelChoices: Choices;
channelValue: string;
onChannelChange: (value: string) => void;
}
const HomeOrdersCard: React.FC<HomeOrdersCardProps> = props => {
const { userName, channelChoices, channelValue, onChannelChange } = props;
const { userName } = props;
const classes = useStyles(props);
const intl = useIntl();
return (
<div className={classes.headerContainer} data-test="home-header">
@ -71,20 +64,6 @@ const HomeOrdersCard: React.FC<HomeOrdersCardProps> = props => {
)}
</Typography>
</div>
<div>
{!!channelChoices?.length && (
<SingleSelectField
name="channel"
label={intl.formatMessage({
defaultMessage: "Channel"
})}
choices={channelChoices}
value={channelValue}
onChange={e => onChannelChange(e.target.value)}
data-test="channel-select"
/>
)}
</div>
</div>
);
};

View file

@ -4,7 +4,6 @@ import Container from "@saleor/components/Container";
import Grid from "@saleor/components/Grid";
import Money from "@saleor/components/Money";
import RequirePermissions from "@saleor/components/RequirePermissions";
import { Choices } from "@saleor/components/SingleSelectField";
import Skeleton from "@saleor/components/Skeleton";
import { UserPermissionProps } from "@saleor/types";
import { PermissionEnum } from "@saleor/types/globalTypes";
@ -54,9 +53,6 @@ export interface HomePageProps extends UserPermissionProps {
sales: Home_salesToday_gross;
topProducts: Home_productTopToday_edges_node[];
userName: string;
channelChoices: Choices;
channelValue: string;
onChannelChange: (value: string) => void;
onOrdersToCaptureClick: () => void;
onOrdersToFulfillClick: () => void;
onProductClick: (productId: string, variantId: string) => void;
@ -65,8 +61,6 @@ export interface HomePageProps extends UserPermissionProps {
const HomePage: React.FC<HomePageProps> = props => {
const {
channelChoices,
channelValue,
userName,
orders,
sales,
@ -78,7 +72,6 @@ const HomePage: React.FC<HomePageProps> = props => {
onProductsOutOfStockClick,
ordersToCapture,
ordersToFulfill,
onChannelChange,
productsOutOfStock,
userPermissions
} = props;
@ -87,12 +80,7 @@ const HomePage: React.FC<HomePageProps> = props => {
return (
<Container>
<HomeHeader
userName={userName}
channelValue={channelValue}
channelChoices={channelChoices}
onChannelChange={onChannelChange}
/>
<HomeHeader userName={userName} />
<CardSpacer />
<Grid>
<div>

View file

@ -1,8 +1,7 @@
import { useChannelsList } from "@saleor/channels/queries";
import useLocalStorage from "@saleor/hooks/useLocalStorage";
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
import useNavigator from "@saleor/hooks/useNavigator";
import useUser from "@saleor/hooks/useUser";
import React, { useCallback, useEffect } from "react";
import React from "react";
import { getUserName, maybe } from "../../misc";
import { orderListUrl } from "../../orders/urls";
@ -14,29 +13,10 @@ import { HomePageQuery } from "../queries";
const HomeSection = () => {
const navigate = useNavigator();
const { user } = useUser();
const { data: channelsData } = useChannelsList({});
const channelChoices =
channelsData?.channels?.map(channel => ({
label: channel.name,
value: channel.slug
})) || [];
const [channelChoice, setChannelChoice] = useLocalStorage(
"homepageChannelChoice",
channelChoices?.length ? channelChoices[0]?.value : ""
);
useEffect(() => {
if (!channelChoice && channelChoices[0]) {
setChannelChoice(channelChoices[0].value);
}
}, [channelChoices]);
const handleChannelChange = useCallback(value => setChannelChoice(value), []);
const { channel } = useAppChannel();
return (
<HomePageQuery displayLoader variables={{ channel: channelChoice }}>
<HomePageQuery displayLoader variables={{ channel: channel.slug }}>
{({ data }) => (
<HomePage
activities={maybe(() =>
@ -47,9 +27,6 @@ const HomeSection = () => {
topProducts={maybe(() =>
data.productTopToday.edges.map(edge => edge.node)
)}
channelChoices={channelChoices}
channelValue={channelChoice}
onChannelChange={handleChannelChange}
onProductClick={(productId, variantId) =>
navigate(productVariantEditUrl(productId, variantId))
}

View file

@ -1,44 +0,0 @@
import { useChannelsList } from "@saleor/channels/queries";
import useLocalStorage from "@saleor/hooks/useLocalStorage";
import { useEffect } from "react";
interface Actions {
openModal: (value: string) => void;
closeModal: () => void;
}
function useChannelsSettings(key: string, { openModal, closeModal }: Actions) {
const { data: channelsData } = useChannelsList({});
const channelChoices = channelsData?.channels?.map(channel => ({
label: channel.name,
value: channel.id
}));
const [selectedChannel, setSelectedChannel] = useLocalStorage(key, "");
const handleChannelSelectConfirm = (channel: string) => {
setSelectedChannel(channel);
closeModal();
};
useEffect(() => {
if (!selectedChannel) {
openModal("settings");
}
}, [selectedChannel]);
const channel = channelsData?.channels?.find(
channel => channel.id === selectedChannel
);
return {
channel,
channelChoices,
channels: channelsData?.channels,
handleChannelSelectConfirm,
selectedChannel,
slug: channel?.slug
};
}
export default useChannelsSettings;

View file

@ -7,14 +7,26 @@ export default function useLocalStorage<T>(
initialValue: T
): [T, SetLocalStorage<T>] {
const [storedValue, setStoredValue] = useState<T>(() => {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
let result: T;
try {
const item = window.localStorage.getItem(key);
result = item ? JSON.parse(item) : initialValue;
} catch {
result = initialValue;
}
return result;
});
const setValue = (value: SetLocalStorageValue<T>) => {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
try {
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch {
console.warn(`Could not save ${key} to localStorage`);
}
};
return [storedValue, setValue];

View file

@ -27,6 +27,9 @@ import ChannelsSection from "./channels";
import { channelsSection } from "./channels/urls";
import CollectionSection from "./collections";
import AppLayout from "./components/AppLayout";
import useAppChannel, {
AppChannelProvider
} from "./components/AppLayout/AppChannelContext";
import { DateProvider } from "./components/Date";
import { LocaleProvider } from "./components/Locale";
import MessageManagerProvider from "./components/messages";
@ -60,7 +63,7 @@ import { PermissionEnum } from "./types/globalTypes";
import WarehouseSection from "./warehouses";
import { warehouseSection } from "./warehouses/urls";
if (process.env.GTM_ID !== undefined) {
if (process.env.GTM_ID) {
TagManager.initialize({ gtmId: GTM_ID });
}
@ -111,7 +114,9 @@ const App: React.FC = () => {
<AppStateProvider>
<ShopProvider>
<AuthProvider>
<Routes />
<AppChannelProvider>
<Routes />
</AppChannelProvider>
</AuthProvider>
</ShopProvider>
</AppStateProvider>
@ -135,11 +140,15 @@ const Routes: React.FC = () => {
tokenVerifyLoading,
user
} = useAuth();
const { channel } = useAppChannel(false);
return (
<>
<WindowTitle title={intl.formatMessage(commonMessages.dashboard)} />
{isAuthenticated && !tokenAuthLoading && !tokenVerifyLoading ? (
{channel &&
isAuthenticated &&
!tokenAuthLoading &&
!tokenVerifyLoading ? (
<AppLayout>
<ErrorBoundary
onError={() =>
@ -271,7 +280,7 @@ const Routes: React.FC = () => {
</Switch>
</ErrorBoundary>
</AppLayout>
) : hasToken && tokenVerifyLoading ? (
) : (isAuthenticated && !channel) || (hasToken && tokenVerifyLoading) ? (
<LoginLoading />
) : (
<Auth />

View file

@ -1,7 +1,5 @@
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import makeStyles from "@material-ui/core/styles/makeStyles";
import CardMenu from "@saleor/components/CardMenu";
import Container from "@saleor/components/Container";
import FilterBar from "@saleor/components/FilterBar";
import PageHeader from "@saleor/components/PageHeader";
@ -32,18 +30,8 @@ export interface OrderDraftListPageProps
SortPage<OrderDraftListUrlSortField>,
TabPageProps {
orders: OrderDraftList_draftOrders_edges_node[];
onSettingsOpen?: () => void;
}
const useStyles = makeStyles(
theme => ({
settings: {
marginRight: theme.spacing(2)
}
}),
{ name: "OrderDraftListPage" }
);
const OrderDraftListPage: React.FC<OrderDraftListPageProps> = ({
currentTab,
disabled,
@ -53,7 +41,6 @@ const OrderDraftListPage: React.FC<OrderDraftListPageProps> = ({
onAll,
onFilterChange,
onSearchChange,
onSettingsOpen,
onTabChange,
onTabDelete,
onTabSave,
@ -61,26 +48,11 @@ const OrderDraftListPage: React.FC<OrderDraftListPageProps> = ({
...listProps
}) => {
const intl = useIntl();
const classes = useStyles({});
const structure = createFilterStructure(intl, filterOpts);
return (
<Container>
<PageHeader title={intl.formatMessage(sectionNames.draftOrders)}>
{!!onSettingsOpen && (
<CardMenu
className={classes.settings}
menuItems={[
{
label: intl.formatMessage({
defaultMessage: "Settings",
description: "button"
}),
onSelect: onSettingsOpen
}
]}
/>
)}
<Button
color="primary"
variant="contained"

View file

@ -1,7 +1,5 @@
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import makeStyles from "@material-ui/core/styles/makeStyles";
import CardMenu from "@saleor/components/CardMenu";
import Container from "@saleor/components/Container";
import FilterBar from "@saleor/components/FilterBar";
import PageHeader from "@saleor/components/PageHeader";
@ -24,18 +22,8 @@ export interface OrderListPageProps
FilterPageProps<OrderFilterKeys, OrderListFilterOpts>,
SortPage<OrderListUrlSortField> {
orders: OrderList_orders_edges_node[];
onSettingsOpen?: () => void;
}
const useStyles = makeStyles(
theme => ({
settings: {
marginRight: theme.spacing(2)
}
}),
{ name: "OrderListPage" }
);
const OrderListPage: React.FC<OrderListPageProps> = ({
currentTab,
initialSearch,
@ -44,7 +32,6 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
onAdd,
onAll,
onSearchChange,
onSettingsOpen,
onFilterChange,
onTabChange,
onTabDelete,
@ -52,26 +39,11 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
...listProps
}) => {
const intl = useIntl();
const classes = useStyles({});
const filterStructure = createFilterStructure(intl, filterOpts);
return (
<Container>
<PageHeader title={intl.formatMessage(sectionNames.orders)}>
{!!onSettingsOpen && (
<CardMenu
className={classes.settings}
menuItems={[
{
label: intl.formatMessage({
defaultMessage: "Settings",
description: "button"
}),
onSelect: onSettingsOpen
}
]}
/>
)}
<Button color="primary" variant="contained" onClick={onAdd}>
<FormattedMessage
defaultMessage="Create order"

View file

@ -24,7 +24,7 @@ import useModalDialogOpen from "@saleor/hooks/useModalDialogOpen";
import useSearchQuery from "@saleor/hooks/useSearchQuery";
import { buttonMessages } from "@saleor/intl";
import { maybe, renderCollection } from "@saleor/misc";
import { FetchMoreProps } from "@saleor/types";
import { ChannelProps, FetchMoreProps } from "@saleor/types";
import getOrderErrorMessage from "@saleor/utils/errors/order";
import React from "react";
import InfiniteScroll from "react-infinite-scroller";
@ -87,12 +87,13 @@ type SetVariantsAction = (
data: SearchOrderVariant_search_edges_node_variants[]
) => void;
export interface OrderProductAddDialogProps extends FetchMoreProps {
export interface OrderProductAddDialogProps
extends FetchMoreProps,
ChannelProps {
confirmButtonState: ConfirmButtonTransitionState;
errors: OrderErrorFragment[];
open: boolean;
products: SearchOrderVariant_search_edges_node[];
selectedChannel: string;
onClose: () => void;
onFetch: (query: string) => void;
onSubmit: (data: SearchOrderVariant_search_edges_node_variants[]) => void;
@ -169,7 +170,7 @@ const OrderProductAddDialog: React.FC<OrderProductAddDialogProps> = props => {
loading,
hasMore,
products,
selectedChannel,
selectedChannelId,
onFetch,
onFetchMore,
onClose,
@ -257,7 +258,7 @@ const OrderProductAddDialog: React.FC<OrderProductAddDialogProps> = props => {
(product, productIndex) =>
product.variants.some(variant =>
variant.channelListings.some(
listing => listing.channel.id === selectedChannel
listing => listing.channel.id === selectedChannelId
)
) ? (
<React.Fragment key={product ? product.id : "skeleton"}>
@ -293,7 +294,7 @@ const OrderProductAddDialog: React.FC<OrderProductAddDialogProps> = props => {
{maybe(() => product.variants, []).map(
(variant, variantIndex) =>
variant.channelListings.some(
listing => listing.channel.id === selectedChannel
listing => listing.channel.id === selectedChannelId
) ? (
<TableRow key={variant.id}>
<TableCell />

View file

@ -31,7 +31,10 @@ import {
OrderDraftCancel,
OrderDraftCancelVariables
} from "./types/OrderDraftCancel";
import { OrderDraftCreate } from "./types/OrderDraftCreate";
import {
OrderDraftCreate,
OrderDraftCreateVariables
} from "./types/OrderDraftCreate";
import {
OrderDraftFinalize,
OrderDraftFinalizeVariables
@ -371,9 +374,10 @@ const orderDraftCreateMutation = gql`
}
}
`;
export const useOrderDraftCreateMutation = makeMutation<OrderDraftCreate, {}>(
orderDraftCreateMutation
);
export const useOrderDraftCreateMutation = makeMutation<
OrderDraftCreate,
OrderDraftCreateVariables
>(orderDraftCreateMutation);
const orderLineDeleteMutation = gql`
${fragmentOrderDetails}

View file

@ -28,7 +28,7 @@ export enum OrderListUrlFiltersWithMultipleValuesEnum {
}
export type OrderListUrlFilters = Filters<OrderListUrlFiltersEnum> &
FiltersWithMultipleValues<OrderListUrlFiltersWithMultipleValuesEnum>;
export type OrderListUrlDialog = "cancel" | "settings" | TabActionDialog;
export type OrderListUrlDialog = "cancel" | TabActionDialog;
export enum OrderListUrlSortField {
number = "number",
customer = "customer",
@ -61,7 +61,7 @@ export enum OrderDraftListUrlFiltersEnum {
query = "query"
}
export type OrderDraftListUrlFilters = Filters<OrderDraftListUrlFiltersEnum>;
export type OrderDraftListUrlDialog = "remove" | "settings" | TabActionDialog;
export type OrderDraftListUrlDialog = "remove" | TabActionDialog;
export enum OrderDraftListUrlSortField {
number = "number",
customer = "customer",

View file

@ -561,7 +561,7 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
products={variantSearchOpts.data?.search.edges.map(
edge => edge.node
)}
selectedChannel={order?.channel?.id}
selectedChannelId={order?.channel?.id}
onClose={closeModal}
onFetch={variantSearch}
onFetchMore={loadMore}

View file

@ -1,14 +1,13 @@
import DialogContentText from "@material-ui/core/DialogContentText";
import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog";
import ActionDialog from "@saleor/components/ActionDialog";
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
import SaveFilterTabDialog, {
SaveFilterTabDialogFormData
} from "@saleor/components/SaveFilterTabDialog";
import useBulkActions from "@saleor/hooks/useBulkActions";
import useChannelsSettings from "@saleor/hooks/useChannelsSettings";
import useListSettings from "@saleor/hooks/useListSettings";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
@ -80,6 +79,8 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = ({ params }) => {
onCompleted: handleCreateOrderCreateSuccess
});
const { channel } = useAppChannel();
const tabs = getFilterTabs();
const currentTab =
@ -106,12 +107,6 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = ({ params }) => {
OrderDraftListUrlQueryParams
>(navigate, orderDraftListUrl, params);
const {
channelChoices,
handleChannelSelectConfirm,
selectedChannel
} = useChannelsSettings("ordersDraftListChannel", { closeModal, openModal });
const handleTabChange = (tab: number) => {
reset();
navigate(
@ -171,16 +166,6 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = ({ params }) => {
return (
<>
{!!channelChoices?.length && (
<ChannelSettingsDialog
channelsChoices={channelChoices}
defaultChoice={selectedChannel}
open={params.action === "settings"}
confirmButtonState="default"
onClose={closeModal}
onConfirm={handleChannelSelectConfirm}
/>
)}
<TypedOrderDraftBulkCancelMutation
onCompleted={handleOrderDraftBulkCancel}
>
@ -214,7 +199,7 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = ({ params }) => {
onAdd={() =>
createOrder({
variables: {
input: { channel: selectedChannel }
input: { channel: channel.id }
}
})
}
@ -240,11 +225,6 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = ({ params }) => {
<DeleteIcon />
</IconButton>
}
onSettingsOpen={
!!channelChoices?.length
? () => openModal("settings")
: undefined
}
/>
<ActionDialog
confirmButtonState={orderDraftBulkDeleteOpts.status}

View file

@ -1,9 +1,8 @@
import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog";
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
import SaveFilterTabDialog, {
SaveFilterTabDialogFormData
} from "@saleor/components/SaveFilterTabDialog";
import useChannelsSettings from "@saleor/hooks/useChannelsSettings";
import useListSettings from "@saleor/hooks/useListSettings";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
@ -68,6 +67,8 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
onCompleted: handleCreateOrderCreateSuccess
});
const { channel } = useAppChannel();
const tabs = getFilterTabs();
const currentTab =
@ -93,12 +94,6 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
OrderListUrlQueryParams
>(navigate, orderListUrl, params);
const {
channelChoices,
handleChannelSelectConfirm,
selectedChannel
} = useChannelsSettings("ordersListChannel", { closeModal, openModal });
const handleTabChange = (tab: number) =>
navigate(
orderListUrl({
@ -142,16 +137,6 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
return (
<>
{!!channelChoices?.length && (
<ChannelSettingsDialog
channelsChoices={channelChoices}
defaultChoice={selectedChannel}
open={params.action === "settings"}
confirmButtonState="default"
onClose={closeModal}
onConfirm={handleChannelSelectConfirm}
/>
)}
<OrderListPage
settings={settings}
currentTab={currentTab}
@ -163,7 +148,7 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
onAdd={() =>
createOrder({
variables: {
input: { channel: selectedChannel }
input: { channel: channel.id }
}
})
}
@ -180,9 +165,6 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
initialSearch={params.query || ""}
tabs={getFilterTabs().map(tab => tab.name)}
onAll={resetFilters}
onSettingsOpen={
!!channelChoices?.length ? () => openModal("settings") : undefined
}
/>
<SaveFilterTabDialog
open={params.action === "save-search"}

View file

@ -24,7 +24,7 @@ import {
import { GridAttributes_grid_edges_node } from "@saleor/products/types/GridAttributes";
import { ProductList_products_edges_node } from "@saleor/products/types/ProductList";
import { ProductListUrlSortField } from "@saleor/products/urls";
import { ListActions, ListProps, SortPage } from "@saleor/types";
import { ChannelProps, ListActions, ListProps, SortPage } from "@saleor/types";
import TDisplayColumn, {
DisplayColumnProps
} from "@saleor/utils/columns/DisplayColumn";
@ -99,12 +99,12 @@ const DisplayColumn = TDisplayColumn as React.FunctionComponent<
interface ProductListProps
extends ListProps<ProductListColumns>,
ListActions,
SortPage<ProductListUrlSortField> {
SortPage<ProductListUrlSortField>,
ChannelProps {
activeAttributeSortId: string;
gridAttributes: GridAttributes_grid_edges_node[];
products: ProductList_products_edges_node[];
loading: boolean;
selectedChannel: string;
channelsCount: number;
}
@ -128,7 +128,7 @@ export const ProductList: React.FC<ProductListProps> = props => {
onUpdateListSettings,
onRowClick,
onSort,
selectedChannel
selectedChannelId
} = props;
const classes = useStyles(props);
@ -286,7 +286,7 @@ export const ProductList: React.FC<ProductListProps> = props => {
product => {
const isSelected = product ? isChecked(product.id) : false;
const channel = product?.channelListings.find(
listing => listing.channel.id === selectedChannel
listing => listing.channel.id === selectedChannelId
);
return (
@ -362,9 +362,7 @@ export const ProductList: React.FC<ProductListProps> = props => {
) : product?.channelListings !== undefined ? (
<ChannelsAvailabilityDropdown
allChannelsCount={channelsCount}
currentChannel={
channel || product?.channelListings[0]
}
currentChannel={channel}
channels={product?.channelListings}
/>
) : (

View file

@ -16,6 +16,7 @@ import {
} from "@saleor/products/types/GridAttributes";
import { ProductList_products_edges_node } from "@saleor/products/types/ProductList";
import {
ChannelProps,
FetchMoreProps,
FilterPageProps,
ListActions,
@ -38,7 +39,8 @@ export interface ProductListPageProps
ListActions,
FilterPageProps<ProductFilterKeys, ProductListFilterOpts>,
FetchMoreProps,
SortPage<ProductListUrlSortField> {
SortPage<ProductListUrlSortField>,
ChannelProps {
activeAttributeSortId: string;
availableInGridAttributes: GridAttributes_availableInGrid_edges_node[];
channelsCount: number;
@ -47,8 +49,6 @@ export interface ProductListPageProps
totalGridAttributes: number;
products: ProductList_products_edges_node[];
onExport: () => void;
selectedChannel: string;
onSettingsOpen?: () => void;
}
const useStyles = makeStyles(
@ -84,12 +84,11 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
onFetchMore,
onFilterChange,
onSearchChange,
onSettingsOpen,
onTabChange,
onTabDelete,
onTabSave,
onUpdateListSettings,
selectedChannel,
selectedChannelId,
...listProps
} = props;
const intl = useIntl();
@ -134,13 +133,6 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
}),
onSelect: onExport,
testId: "export"
},
onSettingsOpen && {
label: intl.formatMessage({
defaultMessage: "Settings",
description: "button"
}),
onSelect: onSettingsOpen
}
]}
data-test="menu"
@ -199,7 +191,7 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
gridAttributes={gridAttributes}
settings={settings}
channelsCount={channelsCount}
selectedChannel={selectedChannel}
selectedChannelId={selectedChannelId}
onUpdateListSettings={onUpdateListSettings}
/>
</Card>

View file

@ -58,6 +58,7 @@ const props: ProductUpdatePageProps = {
placeholderImage,
product,
saveButtonBarState: "default",
selectedChannelId: "123",
taxTypes,
variants: product.variants,
warehouses: warehouseList

View file

@ -22,7 +22,12 @@ import { maybe } from "@saleor/misc";
import ProductVariantPrice from "@saleor/products/components/ProductVariantPrice";
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections";
import { FetchMoreProps, ListActions, ReorderAction } from "@saleor/types";
import {
ChannelProps,
FetchMoreProps,
ListActions,
ReorderAction
} from "@saleor/types";
import React from "react";
import { useIntl } from "react-intl";
@ -42,7 +47,7 @@ import ProductTaxes from "../ProductTaxes";
import ProductVariants from "../ProductVariants";
import ProductUpdateForm from "./form";
export interface ProductUpdatePageProps extends ListActions {
export interface ProductUpdatePageProps extends ListActions, ChannelProps {
defaultWeightUnit: string;
errors: ProductErrorWithAttributesFragment[];
channelsErrors: ProductChannelListingErrorFragment[];
@ -83,7 +88,9 @@ export interface ProductUpdatePageProps extends ListActions {
onWarehouseConfigure();
}
export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData {
export interface ProductUpdatePageSubmitData
extends ProductUpdatePageFormData,
ChannelProps {
addStocks: ProductStockInput[];
attributes: ProductAttributeInput[];
collections: string[];
@ -96,7 +103,6 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
defaultWeightUnit,
disabled,
categories: categoryChoiceList,
channelChoices,
channelsErrors,
allChannelsCount,
currentChannels = [],
@ -133,6 +139,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
onWarehouseConfigure,
isChecked,
selected,
selectedChannelId,
toggle,
toggleAll,
toolbar
@ -235,7 +242,6 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
disabled={disabled}
variants={variants}
product={product}
channelChoices={channelChoices}
onRowClick={onVariantShow}
onVariantAdd={onVariantAdd}
onVariantsAdd={onVariantsAdd}
@ -244,6 +250,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
toolbar={toolbar}
isChecked={isChecked}
selected={selected}
selectedChannelId={selectedChannelId}
toggle={toggle}
toggleAll={toggleAll}
/>

View file

@ -7,7 +7,6 @@ import { fade } from "@material-ui/core/styles/colorManipulator";
import TableCell from "@material-ui/core/TableCell";
import Typography from "@material-ui/core/Typography";
import CardTitle from "@saleor/components/CardTitle";
import { ChannelsSelect } from "@saleor/components/ChannelsSelect";
import Checkbox from "@saleor/components/Checkbox";
import LinkChoice from "@saleor/components/LinkChoice";
import Money from "@saleor/components/Money";
@ -19,12 +18,11 @@ import {
SortableTableRow
} from "@saleor/components/SortableTable";
import TableHead from "@saleor/components/TableHead";
import useStateFromProps from "@saleor/hooks/useStateFromProps";
import React from "react";
import { FormattedMessage, IntlShape, useIntl } from "react-intl";
import { maybe, renderCollection } from "../../../misc";
import { ListActions, ReorderAction } from "../../../types";
import { ChannelProps, ListActions, ReorderAction } from "../../../types";
import {
ProductDetails_product,
ProductDetails_product_variants,
@ -84,9 +82,6 @@ const useStyles = makeStyles(
width: 200
}
},
channelSelect: {
marginRight: theme.spacing(1)
},
colGrab: {
width: 60
},
@ -183,12 +178,11 @@ function getAvailabilityLabel(
}
}
interface ProductVariantsProps extends ListActions {
interface ProductVariantsProps extends ListActions, ChannelProps {
disabled: boolean;
product: ProductDetails_product;
variants: ProductDetails_product_variants[];
onVariantReorder: ReorderAction;
channelChoices: SingleAutocompleteChoiceType[];
onRowClick: (id: string) => () => void;
onSetDefaultVariant(variant: ProductDetails_product_variants);
onVariantAdd?();
@ -199,7 +193,6 @@ const numberOfColumns = 7;
export const ProductVariants: React.FC<ProductVariantsProps> = props => {
const {
channelChoices,
disabled,
variants,
product,
@ -210,6 +203,7 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
onSetDefaultVariant,
isChecked,
selected,
selectedChannelId,
toggle,
toggleAll,
toolbar
@ -218,9 +212,6 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
const intl = useIntl();
const [warehouse, setWarehouse] = React.useState<string>(null);
const [channelChoice, setChannelChoice] = useStateFromProps(
channelChoices[0]?.value
);
const hasVariants = maybe(() => variants.length > 0, true);
return (
@ -261,11 +252,6 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
{variants.length > 0 ? (
<CardContent className={classes.warehouseSelectContainer}>
<ChannelsSelect
channelChoice={channelChoice}
channelChoices={channelChoices}
setChannelChoice={setChannelChoice}
/>
<Typography className={classes.warehouseLabel}>
<FormattedMessage
defaultMessage="Available inventory at:"
@ -345,7 +331,7 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
)
: null;
const channel = variant.channelListings.find(
listing => listing.channel.id === channelChoice
listing => listing.channel.id === selectedChannelId
);
return (

View file

@ -19,11 +19,7 @@ export const productAddPath = urlJoin(productSection, "add");
export const productAddUrl = productAddPath;
export const productListPath = productSection;
export type ProductListUrlDialog =
| "settings"
| "delete"
| "export"
| TabActionDialog;
export type ProductListUrlDialog = "delete" | "export" | TabActionDialog;
export enum ProductListUrlFiltersEnum {
priceFrom = "priceFrom",
priceTo = "priceTo",

View file

@ -1,8 +1,8 @@
import DialogContentText from "@material-ui/core/DialogContentText";
import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog";
import ActionDialog from "@saleor/components/ActionDialog";
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
import SaveFilterTabDialog, {
SaveFilterTabDialogFormData
@ -16,7 +16,6 @@ import {
import { Task } from "@saleor/containers/BackgroundTasks/types";
import useBackgroundTask from "@saleor/hooks/useBackgroundTask";
import useBulkActions from "@saleor/hooks/useBulkActions";
import useChannelsSettings from "@saleor/hooks/useChannelsSettings";
import useListSettings from "@saleor/hooks/useListSettings";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
@ -126,33 +125,25 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
first: 100
}
});
const { availableChannels, channel } = useAppChannel();
const [openModal, closeModal] = createDialogActionHandlers<
ProductListUrlDialog,
ProductListUrlQueryParams
>(navigate, productListUrl, params);
const {
channel,
channels,
channelChoices,
handleChannelSelectConfirm,
selectedChannel,
slug
} = useChannelsSettings("productsListChannel", { closeModal, openModal });
React.useEffect(() => {
const action = selectedChannel
? {}
: { action: "settings" as ProductListUrlDialog };
navigate(
productListUrl({
...{ ...params, ...action },
...DEFAULT_INITIAL_PAGINATION_DATA
}),
true
);
}, [settings.rowNumber]);
// Reset pagination
React.useEffect(
() =>
navigate(
productListUrl({
...params,
...DEFAULT_INITIAL_PAGINATION_DATA
}),
true
),
[settings.rowNumber]
);
const tabs = getFilterTabs();
@ -231,13 +222,13 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
);
const paginationState = createPaginationState(settings.rowNumber, params);
const filter = getFilterVariables(params, slug);
const sort = getSortQueryVariables(params, slug);
const filter = getFilterVariables(params, channel.slug);
const sort = getSortQueryVariables(params, channel.slug);
const queryVariables = React.useMemo<ProductListVariables>(
() => ({
...paginationState,
filter,
...(slug ? { sort } : {})
sort
}),
[params, settings.rowNumber]
);
@ -388,22 +379,9 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
initialSearch={params.query || ""}
tabs={getFilterTabs().map(tab => tab.name)}
onExport={() => openModal("export")}
channelsCount={channelChoices?.length}
selectedChannel={selectedChannel}
onSettingsOpen={
!!channelChoices?.length ? () => openModal("settings") : undefined
}
channelsCount={availableChannels?.length}
selectedChannelId={channel.id}
/>
{!!channelChoices?.length && (
<ChannelSettingsDialog
channelsChoices={channelChoices}
defaultChoice={selectedChannel}
open={params.action === "settings"}
confirmButtonState="default"
onClose={closeModal}
onConfirm={handleChannelSelectConfirm}
/>
)}
<ActionDialog
open={params.action === "delete"}
confirmButtonState={productBulkDeleteOpts.status}
@ -449,7 +427,7 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
warehouses={
warehouses.data?.warehouses.edges.map(edge => edge.node) || []
}
channels={channels}
channels={availableChannels}
onClose={closeModal}
onSubmit={data =>
exportProducts({

View file

@ -9,6 +9,7 @@ import {
createSortedChannelsDataFromProduct
} from "@saleor/channels/utils";
import ActionDialog from "@saleor/components/ActionDialog";
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog";
import NotFoundPage from "@saleor/components/NotFoundPage";
import { WindowTitle } from "@saleor/components/WindowTitle";
@ -114,6 +115,7 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
displayLoader: true,
variables: { id }
});
const { channel } = useAppChannel();
const handleUpdate = (data: ProductUpdateMutationResult) => {
if (data.productUpdate.errors.length === 0) {
@ -404,6 +406,7 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
loading: searchCollectionsOpts.loading,
onFetchMore: loadMoreCollections
}}
selectedChannelId={channel.id}
openChannelsModal={handleChannelsModalOpen}
onChannelsChange={setCurrentChannels}
/>

View file

@ -21,7 +21,7 @@ import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { getStringOrPlaceholder } from "../../../misc";
import { FetchMoreProps, SearchProps } from "../../../types";
import { ChannelProps, FetchMoreProps, SearchProps } from "../../../types";
import { ShippingMethodTypeEnum } from "../../../types/globalTypes";
import ShippingZoneInfo from "../ShippingZoneInfo";
import ShippingZoneRates from "../ShippingZoneRates";
@ -34,11 +34,11 @@ export interface FormData {
export interface ShippingZoneDetailsPageProps
extends FetchMoreProps,
SearchProps {
SearchProps,
ChannelProps {
disabled: boolean;
errors: ShippingErrorFragment[];
saveButtonBarState: ConfirmButtonTransitionState;
selectedChannel: string;
shippingZone: ShippingZoneDetailsFragment;
warehouses: ShippingZoneDetailsFragment_warehouses[];
onBack: () => void;
@ -82,7 +82,7 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
onWeightRateAdd,
onWeightRateEdit,
saveButtonBarState,
selectedChannel,
selectedChannelId,
shippingZone,
warehouses
}) => {
@ -160,7 +160,7 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
method => method.type === ShippingMethodTypeEnum.PRICE
)}
variant="price"
selectedChannel={selectedChannel}
selectedChannelId={selectedChannelId}
/>
<CardSpacer />
<ShippingZoneRates
@ -172,7 +172,7 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
method => method.type === ShippingMethodTypeEnum.WEIGHT
)}
variant="weight"
selectedChannel={selectedChannel}
selectedChannelId={selectedChannelId}
/>
</div>
<div>

View file

@ -15,16 +15,16 @@ import ResponsiveTable from "@saleor/components/ResponsiveTable";
import Skeleton from "@saleor/components/Skeleton";
import WeightRange from "@saleor/components/WeightRange";
import { ShippingZoneDetailsFragment_shippingMethods } from "@saleor/fragments/types/ShippingZoneDetailsFragment";
import { ChannelProps } from "@saleor/types";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { maybe, renderCollection } from "../../../misc";
import { ICONBUTTON_SIZE } from "../../../theme";
export interface ShippingZoneRatesProps {
export interface ShippingZoneRatesProps extends ChannelProps {
disabled: boolean;
rates: ShippingZoneDetailsFragment_shippingMethods[];
selectedChannel: string;
variant: "price" | "weight";
onRateAdd: () => void;
onRateEdit: (id: string) => void;
@ -56,7 +56,7 @@ const ShippingZoneRates: React.FC<ShippingZoneRatesProps> = props => {
onRateEdit,
onRateRemove,
rates,
selectedChannel,
selectedChannelId,
variant
} = props;
@ -122,7 +122,7 @@ const ShippingZoneRates: React.FC<ShippingZoneRatesProps> = props => {
rates,
rate => {
const channel = rate?.channelListings?.find(
listing => listing.channel.id === selectedChannel
listing => listing.channel.id === selectedChannelId
);
return (
<TableRow

View file

@ -1,5 +1,4 @@
import AppHeader from "@saleor/components/AppHeader";
import CardMenu from "@saleor/components/CardMenu";
import Container from "@saleor/components/Container";
import Grid from "@saleor/components/Grid";
import PageHeader from "@saleor/components/PageHeader";
@ -23,8 +22,6 @@ export interface ShippingZonesListPageProps
onBack: () => void;
onRemove: (id: string) => void;
onSubmit: (unit: WeightUnitsEnum) => void;
selectedChannel: string;
onSettingsOpen: () => void;
}
const ShippingZonesListPage: React.FC<ShippingZonesListPageProps> = ({
@ -32,7 +29,6 @@ const ShippingZonesListPage: React.FC<ShippingZonesListPageProps> = ({
disabled,
userPermissions,
onBack,
onSettingsOpen,
onSubmit,
...listProps
}) => {
@ -48,19 +44,7 @@ const ShippingZonesListPage: React.FC<ShippingZonesListPageProps> = ({
defaultMessage: "Shipping",
description: "header"
})}
>
<CardMenu
menuItems={[
{
label: intl.formatMessage({
defaultMessage: "Settings",
description: "button"
}),
onSelect: onSettingsOpen
}
]}
/>
</PageHeader>
/>
<Grid>
<div>
<ShippingZonesList disabled={disabled} {...listProps} />

View file

@ -7,7 +7,7 @@ import { ShippingMethodTypeEnum } from "../types/globalTypes";
export const shippingSection = "/shipping/";
export const shippingZonesListPath = shippingSection;
export type ShippingZonesListUrlDialog = "remove" | "remove-many" | "settings";
export type ShippingZonesListUrlDialog = "remove" | "remove-many";
export type ShippingZonesListUrlQueryParams = BulkAction &
Dialog<ShippingZonesListUrlDialog> &
Pagination &

View file

@ -1,8 +1,8 @@
import DialogContentText from "@material-ui/core/DialogContentText";
import ActionDialog from "@saleor/components/ActionDialog";
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
import NotFoundPage from "@saleor/components/NotFoundPage";
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
import useLocalStorage from "@saleor/hooks/useLocalStorage";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import useShop from "@saleor/hooks/useShop";
@ -63,8 +63,7 @@ const ShippingZoneDetails: React.FC<ShippingZoneDetailsProps> = ({
displayLoader: true,
variables: { id }
});
const [selectedChannel] = useLocalStorage("shippingListChannel", "");
const { channel } = useAppChannel();
const [openModal, closeModal] = createDialogActionHandlers<
ShippingZoneUrlDialog,
@ -183,7 +182,7 @@ const ShippingZoneDetails: React.FC<ShippingZoneDetailsProps> = ({
loading={searchWarehousesOpts.loading}
onFetchMore={loadMore}
onSearchChange={search}
selectedChannel={selectedChannel}
selectedChannelId={channel.id}
/>
<DeleteShippingRateDialog
confirmButtonState={deleteShippingRateOpts.status}

View file

@ -1,11 +1,9 @@
import DialogContentText from "@material-ui/core/DialogContentText";
import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog";
import ActionDialog from "@saleor/components/ActionDialog";
import { configurationMenuUrl } from "@saleor/configuration";
import useBulkActions from "@saleor/hooks/useBulkActions";
import useChannelsSettings from "@saleor/hooks/useChannelsSettings";
import useListSettings from "@saleor/hooks/useListSettings";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
@ -63,12 +61,6 @@ export const ShippingZonesList: React.FC<ShippingZonesListProps> = ({
ShippingZonesListUrlQueryParams
>(navigate, shippingZonesListUrl, params);
const {
channelChoices,
handleChannelSelectConfirm,
selectedChannel
} = useChannelsSettings("shippingListChannel", { closeModal, openModal });
const { data, loading, refetch } = useShippingZoneList({
displayLoader: true,
variables: paginationState
@ -125,14 +117,6 @@ export const ShippingZonesList: React.FC<ShippingZonesListProps> = ({
);
return (
<>
<ChannelSettingsDialog
channelsChoices={channelChoices}
defaultChoice={selectedChannel}
open={params.action === "settings"}
confirmButtonState="default"
onClose={closeModal}
onConfirm={handleChannelSelectConfirm}
/>
<ShippingZonesListPage
defaultWeightUnit={shop?.defaultWeightUnit}
settings={settings}
@ -178,8 +162,6 @@ export const ShippingZonesList: React.FC<ShippingZonesListProps> = ({
</IconButton>
}
userPermissions={user?.userPermissions || []}
selectedChannel={selectedChannel}
onSettingsOpen={() => openModal("settings")}
/>
<ActionDialog

File diff suppressed because it is too large Load diff

View file

@ -47,6 +47,7 @@ const updateProps: Omit<CategoryUpdatePageProps, "classes"> = {
productListToolbar: null,
products: category.products.edges.map(edge => edge.node),
saveButtonBarState: "default",
selectedChannelId: "123",
subcategories: category.children.edges.map(edge => edge.node),
subcategoryListToolbar: null,
...listActionsProps

View file

@ -38,7 +38,7 @@ const props: Omit<CollectionDetailsPageProps, "classes"> = {
onSubmit: () => undefined,
openChannelsModal: () => undefined,
saveButtonBarState: "default",
selectedChannel: "123"
selectedChannelId: "123"
};
storiesOf("Views / Collections / Collection details", module)

View file

@ -27,7 +27,7 @@ const props: CollectionListPageProps = {
},
...tabPageProps,
collections,
selectedChannel: "123"
selectedChannelId: "123"
};
storiesOf("Views / Collections / Collection list", module)

View file

@ -47,7 +47,7 @@ const props: SaleDetailsPageProps = {
productListToolbar: null,
sale,
saveButtonBarState: "default",
selectedChannel: "123",
selectedChannelId: "123",
...listActionsProps
};

View file

@ -42,9 +42,8 @@ const props: SaleListPageProps = {
value: [DiscountStatusEnum.ACTIVE]
}
},
onSettingsOpen: () => undefined,
sales: saleList,
selectedChannel: "123",
selectedChannelId: "123",
sort: {
...sortPageProps.sort,
sort: SaleListUrlSortField.name
@ -60,7 +59,6 @@ storiesOf("Views / Discounts / Sale list", module)
<SaleListPage
{...props}
sales={saleList.map(sale => ({ ...sale, channelListings: [] }))}
selectedChannel=""
onSettingsOpen={undefined}
selectedChannelId=""
/>
));

View file

@ -47,7 +47,7 @@ const props: VoucherDetailsPageProps = {
openChannelsModal: () => undefined,
productListToolbar: null,
saveButtonBarState: "default",
selectedChannel: "123",
selectedChannelId: "123",
voucher: voucherDetails
};

View file

@ -51,8 +51,7 @@ const props: VoucherListPageProps = {
}
}
},
onSettingsOpen: () => undefined,
selectedChannel: "123",
selectedChannelId: "123",
sort: {
...sortPageProps.sort,
sort: VoucherListUrlSortField.code
@ -68,8 +67,7 @@ storiesOf("Views / Discounts / Voucher list", module)
.add("no channels", () => (
<VoucherListPage
{...props}
selectedChannel=""
onSettingsOpen={undefined}
selectedChannelId=""
vouchers={voucherList.map(voucher => ({
...voucher,
channelListings: []

View file

@ -1,6 +1,5 @@
import placeholderImage from "@assets/images/placeholder60x60.png";
import { Omit } from "@material-ui/core";
import { channelsList } from "@saleor/channels/fixtures";
import { adminUserPermissions } from "@saleor/fixtures";
import { PermissionEnum } from "@saleor/types/globalTypes";
import { storiesOf } from "@storybook/react";
@ -11,16 +10,9 @@ import { shop as shopFixture } from "../../../home/fixtures";
import Decorator from "../../Decorator";
const shop = shopFixture(placeholderImage);
const channelChoices = channelsList.map(channel => ({
label: channel.name,
value: channel.slug
}));
const homePageProps: Omit<HomePageProps, "classes"> = {
activities: shop.activities.edges.map(edge => edge.node),
channelChoices,
channelValue: channelChoices[0].value,
onChannelChange: () => undefined,
onOrdersToCaptureClick: () => undefined,
onOrdersToFulfillClick: () => undefined,
onProductClick: () => undefined,

View file

@ -37,7 +37,6 @@ const props: OrderDraftListPageProps = {
}
},
onAdd: () => undefined,
onSettingsOpen: () => undefined,
orders,
sort: {
...sortPageProps.sort,

View file

@ -21,7 +21,7 @@ const props: OrderProductAddDialogProps = {
onSubmit: () => undefined,
open: true,
products,
selectedChannel: products[0].variants[0].channelListings[0].channel.id
selectedChannelId: products[0].variants[0].channelListings[0].channel.id
};
storiesOf("Orders / OrderProductAddDialog", module)

View file

@ -42,9 +42,8 @@ const props: ProductListPageProps = {
filterOpts: productListFilterOpts,
gridAttributes: attributes,
onExport: () => undefined,
onSettingsOpen: () => undefined,
products,
selectedChannel: "123",
selectedChannelId: "123",
settings: {
...pageListProps.default.settings,
columns: ["availability", "productType", "price"]
@ -69,8 +68,7 @@ storiesOf("Views / Products / Product list", module)
<ProductListPage
{...props}
channelsCount={0}
onSettingsOpen={undefined}
selectedChannel={""}
selectedChannelId={""}
products={products.map(product => ({ ...product, channelListings: [] }))}
/>
));

View file

@ -58,6 +58,7 @@ const props: ProductUpdatePageProps = {
placeholderImage,
product,
saveButtonBarState: "default",
selectedChannelId: "123",
taxTypes,
variants: product.variants,
warehouses: warehouseList

View file

@ -27,7 +27,7 @@ const props: ShippingZoneDetailsPageProps = {
onWeightRateAdd: () => undefined,
onWeightRateEdit: () => undefined,
saveButtonBarState: "default",
selectedChannel: "12345",
selectedChannelId: "12345",
shippingZone,
warehouses: warehouseList
};

View file

@ -20,9 +20,7 @@ const props: ShippingZonesListPageProps = {
onAdd: () => undefined,
onBack: () => undefined,
onRemove: () => undefined,
onSettingsOpen: () => undefined,
onSubmit: () => undefined,
selectedChannel: "123",
shippingZones,
userPermissions: adminUserPermissions
};

View file

@ -117,6 +117,10 @@ export interface TabPageProps {
onTabSave: () => void;
}
export interface ChannelProps {
selectedChannelId: string;
}
export interface PartialMutationProviderOutput<
TData extends {} = {},
TVariables extends {} = {}

View file

@ -1,21 +1,32 @@
import { User } from "@saleor/fragments/types/User";
export const isSupported =
navigator.credentials && navigator.credentials.preventSilentAccess;
export const isSupported = !!(
navigator?.credentials?.preventSilentAccess && PasswordCredential
);
export function login<T>(loginFn: (id: string, password: string) => T): T {
if (isSupported) {
navigator.credentials.get({ password: true }).then(credential => {
if (credential instanceof PasswordCredential) {
return loginFn(credential.id, credential.password);
}
});
export async function login<T>(
loginFn: (id: string, password: string) => T
): Promise<T | null> {
let result: T;
try {
const credential = await navigator.credentials.get({ password: true });
if (credential instanceof PasswordCredential) {
result = loginFn(credential.id, credential.password);
}
} catch {
result = null;
}
return null;
return result;
}
export function saveCredentials(user: User, password: string) {
export function saveCredentials(
user: User,
password: string
): Promise<CredentialType | null> {
let result: Promise<CredentialType | null>;
if (isSupported) {
const cred = new PasswordCredential({
iconURL: user.avatar ? user.avatar.url : undefined,
@ -23,6 +34,14 @@ export function saveCredentials(user: User, password: string) {
name: user.firstName ? `${user.firstName} ${user.lastName}` : undefined,
password
});
navigator.credentials.store(cred);
try {
result = navigator.credentials.store(cred);
} catch {
result = null;
}
} else {
result = null;
}
return result;
}