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:
parent
607eba6a10
commit
a175fb9497
88 changed files with 501 additions and 1809 deletions
|
@ -1,6 +1,5 @@
|
||||||
import { LEFT_MENU_SELECTORS } from "../elements/account/left-menu/left-menu-selectors";
|
import { LEFT_MENU_SELECTORS } from "../elements/account/left-menu/left-menu-selectors";
|
||||||
import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors";
|
import { PRODUCTS_SELECTORS } from "../elements/catalog/product-selectors";
|
||||||
import { BUTTON_SELECTORS } from "../elements/shared/button-selectors";
|
|
||||||
|
|
||||||
// <reference types="cypress" />
|
// <reference types="cypress" />
|
||||||
describe("Products", () => {
|
describe("Products", () => {
|
||||||
|
@ -14,8 +13,6 @@ describe("Products", () => {
|
||||||
.click()
|
.click()
|
||||||
.get(PRODUCTS_SELECTORS.products)
|
.get(PRODUCTS_SELECTORS.products)
|
||||||
.click()
|
.click()
|
||||||
.get(BUTTON_SELECTORS.submit)
|
|
||||||
.click()
|
|
||||||
.get(PRODUCTS_SELECTORS.createProductBtn)
|
.get(PRODUCTS_SELECTORS.createProductBtn)
|
||||||
.click()
|
.click()
|
||||||
.get(PRODUCTS_SELECTORS.productNameInput)
|
.get(PRODUCTS_SELECTORS.productNameInput)
|
||||||
|
|
|
@ -1325,10 +1325,6 @@
|
||||||
"context": "field is optional",
|
"context": "field is optional",
|
||||||
"string": "(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": {
|
"src_dot_collections_dot_components_dot_CollectionListPage_dot_1631917001": {
|
||||||
"context": "tab name",
|
"context": "tab name",
|
||||||
"string": "All Collections"
|
"string": "All Collections"
|
||||||
|
@ -1651,10 +1647,6 @@
|
||||||
"context": "section header button",
|
"context": "section header button",
|
||||||
"string": "Manage"
|
"string": "Manage"
|
||||||
},
|
},
|
||||||
"src_dot_components_dot_ChannelsSelect_dot_4183335632": {
|
|
||||||
"context": "channel select label",
|
|
||||||
"string": "Channel:"
|
|
||||||
},
|
|
||||||
"src_dot_components_dot_ColumnPicker_dot_1483881697": {
|
"src_dot_components_dot_ColumnPicker_dot_1483881697": {
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Reset"
|
"string": "Reset"
|
||||||
|
@ -2062,6 +2054,10 @@
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Account Settings"
|
"string": "Account Settings"
|
||||||
},
|
},
|
||||||
|
"src_dot_components_dot_UserChip_dot_85001470": {
|
||||||
|
"context": "button",
|
||||||
|
"string": "Enable Dark Mode"
|
||||||
|
},
|
||||||
"src_dot_components_dot_VisibilityCard_dot_1311467573": {
|
"src_dot_components_dot_VisibilityCard_dot_1311467573": {
|
||||||
"string": "Show in product listings"
|
"string": "Show in product listings"
|
||||||
},
|
},
|
||||||
|
@ -2496,10 +2492,6 @@
|
||||||
"context": "sale name",
|
"context": "sale name",
|
||||||
"string": "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": {
|
"src_dot_discounts_dot_components_dot_SaleListPage_dot_1866913828": {
|
||||||
"string": "Search Sale"
|
"string": "Search Sale"
|
||||||
},
|
},
|
||||||
|
@ -2660,10 +2652,6 @@
|
||||||
"context": "tab name",
|
"context": "tab name",
|
||||||
"string": "All Vouchers"
|
"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": {
|
"src_dot_discounts_dot_components_dot_VoucherListPage_dot_1930485532": {
|
||||||
"string": "Search Voucher"
|
"string": "Search Voucher"
|
||||||
},
|
},
|
||||||
|
@ -3004,9 +2992,6 @@
|
||||||
"src_dot_home_dot_components_dot_HomeActivityCard_dot_placed": {
|
"src_dot_home_dot_components_dot_HomeActivityCard_dot_placed": {
|
||||||
"string": "Order #{orderId} was placed"
|
"string": "Order #{orderId} was placed"
|
||||||
},
|
},
|
||||||
"src_dot_home_dot_components_dot_HomeHeader_dot_4019698341": {
|
|
||||||
"string": "Channel"
|
|
||||||
},
|
|
||||||
"src_dot_hooks_dot_3382262667": {
|
"src_dot_hooks_dot_3382262667": {
|
||||||
"string": "Variant {name} has been set as default."
|
"string": "Variant {name} has been set as default."
|
||||||
},
|
},
|
||||||
|
@ -3163,10 +3148,6 @@
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Add products"
|
"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": {
|
"src_dot_orders_dot_components_dot_OrderDraftListPage_dot_2826235371": {
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Create order"
|
"string": "Create order"
|
||||||
|
@ -3491,10 +3472,6 @@
|
||||||
"context": "generate invoice button",
|
"context": "generate invoice button",
|
||||||
"string": "Generate"
|
"string": "Generate"
|
||||||
},
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderListPage_dot_1391686013": {
|
|
||||||
"context": "button",
|
|
||||||
"string": "Settings"
|
|
||||||
},
|
|
||||||
"src_dot_orders_dot_components_dot_OrderListPage_dot_2826235371": {
|
"src_dot_orders_dot_components_dot_OrderListPage_dot_2826235371": {
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Create order"
|
"string": "Create order"
|
||||||
|
@ -4561,10 +4538,6 @@
|
||||||
"context": "product price",
|
"context": "product price",
|
||||||
"string": "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": {
|
"src_dot_products_dot_components_dot_ProductListPage_dot_1542417144": {
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Create Product"
|
"string": "Create Product"
|
||||||
|
@ -5296,10 +5269,6 @@
|
||||||
"context": "header",
|
"context": "header",
|
||||||
"string": "Shipping"
|
"string": "Shipping"
|
||||||
},
|
},
|
||||||
"src_dot_shipping_dot_components_dot_ShippingZonesListPage_dot_1391686013": {
|
|
||||||
"context": "button",
|
|
||||||
"string": "Settings"
|
|
||||||
},
|
|
||||||
"src_dot_shipping_dot_components_dot_ShippingZonesList_dot_120574110": {
|
"src_dot_shipping_dot_components_dot_ShippingZonesList_dot_120574110": {
|
||||||
"context": "sort shipping methods by zone, section header",
|
"context": "sort shipping methods by zone, section header",
|
||||||
"string": "Shipping By Zone"
|
"string": "Shipping By Zone"
|
||||||
|
|
|
@ -14,7 +14,7 @@ import TableCellAvatar, {
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import { maybe, renderCollection } from "@saleor/misc";
|
import { maybe, renderCollection } from "@saleor/misc";
|
||||||
import { ListActions, ListProps } from "@saleor/types";
|
import { ChannelProps, ListActions, ListProps } from "@saleor/types";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
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;
|
channelsCount: number;
|
||||||
selectedChannel: string;
|
|
||||||
products: CategoryDetails_category_products_edges_node[];
|
products: CategoryDetails_category_products_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +92,7 @@ export const CategoryProductList: React.FC<CategoryProductListProps> = props =>
|
||||||
onNextPage,
|
onNextPage,
|
||||||
onPreviousPage,
|
onPreviousPage,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
selectedChannel
|
selectedChannelId
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
|
@ -158,7 +160,7 @@ export const CategoryProductList: React.FC<CategoryProductListProps> = props =>
|
||||||
product => {
|
product => {
|
||||||
const isSelected = product ? isChecked(product.id) : false;
|
const isSelected = product ? isChecked(product.id) : false;
|
||||||
const channel = product?.channelListings.find(
|
const channel = product?.channelListings.find(
|
||||||
listing => listing.channel.id === selectedChannel
|
listing => listing.channel.id === selectedChannelId
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,36 +1,25 @@
|
||||||
import Button from "@material-ui/core/Button";
|
import Button from "@material-ui/core/Button";
|
||||||
import Card from "@material-ui/core/Card";
|
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 CardTitle from "@saleor/components/CardTitle";
|
||||||
import { ChannelsSelect } from "@saleor/components/ChannelsSelect";
|
|
||||||
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
|
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
|
||||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
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 { CategoryDetails_category_products_edges_node } from "../../types/CategoryDetails";
|
||||||
import CategoryProductList from "../CategoryProductList";
|
import CategoryProductList from "../CategoryProductList";
|
||||||
|
|
||||||
interface CategoryProductsProps extends PageListProps, ListActions {
|
interface CategoryProductsProps
|
||||||
|
extends PageListProps,
|
||||||
|
ListActions,
|
||||||
|
ChannelProps {
|
||||||
products: CategoryDetails_category_products_edges_node[];
|
products: CategoryDetails_category_products_edges_node[];
|
||||||
channelChoices: SingleAutocompleteChoiceType[];
|
channelChoices: SingleAutocompleteChoiceType[];
|
||||||
channelsCount: number;
|
channelsCount: number;
|
||||||
categoryName: string;
|
categoryName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
|
||||||
theme => ({
|
|
||||||
channelsSelectContainer: {
|
|
||||||
paddingTop: theme.spacing(2)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
{ name: "CategoryProducts" }
|
|
||||||
);
|
|
||||||
|
|
||||||
export const CategoryProducts: React.FC<CategoryProductsProps> = ({
|
export const CategoryProducts: React.FC<CategoryProductsProps> = ({
|
||||||
channelChoices,
|
|
||||||
channelsCount,
|
channelsCount,
|
||||||
products,
|
products,
|
||||||
disabled,
|
disabled,
|
||||||
|
@ -42,16 +31,12 @@ export const CategoryProducts: React.FC<CategoryProductsProps> = ({
|
||||||
categoryName,
|
categoryName,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
|
selectedChannelId,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const classes = useStyles({});
|
|
||||||
|
|
||||||
const [channelChoice, setChannelChoice] = useStateFromProps(
|
|
||||||
channelChoices?.length ? channelChoices[0]?.value : ""
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
|
@ -72,16 +57,9 @@ export const CategoryProducts: React.FC<CategoryProductsProps> = ({
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<CardContent className={classes.channelsSelectContainer}>
|
|
||||||
<ChannelsSelect
|
|
||||||
channelChoice={channelChoice}
|
|
||||||
channelChoices={channelChoices}
|
|
||||||
setChannelChoice={setChannelChoice}
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
<CategoryProductList
|
<CategoryProductList
|
||||||
channelsCount={channelsCount}
|
channelsCount={channelsCount}
|
||||||
selectedChannel={channelChoice}
|
selectedChannelId={selectedChannelId}
|
||||||
products={products}
|
products={products}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
pageInfo={pageInfo}
|
pageInfo={pageInfo}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { maybe } from "../../../misc";
|
import { maybe } from "../../../misc";
|
||||||
import { TabListActions } from "../../../types";
|
import { ChannelProps, TabListActions } from "../../../types";
|
||||||
import CategoryDetailsForm from "../../components/CategoryDetailsForm";
|
import CategoryDetailsForm from "../../components/CategoryDetailsForm";
|
||||||
import CategoryList from "../../components/CategoryList";
|
import CategoryList from "../../components/CategoryList";
|
||||||
import {
|
import {
|
||||||
|
@ -36,7 +36,8 @@ export enum CategoryPageTab {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CategoryUpdatePageProps
|
export interface CategoryUpdatePageProps
|
||||||
extends TabListActions<"productListToolbar" | "subcategoryListToolbar"> {
|
extends TabListActions<"productListToolbar" | "subcategoryListToolbar">,
|
||||||
|
ChannelProps {
|
||||||
changeTab: (index: CategoryPageTab) => void;
|
changeTab: (index: CategoryPageTab) => void;
|
||||||
currentTab: CategoryPageTab;
|
currentTab: CategoryPageTab;
|
||||||
errors: ProductErrorFragment[];
|
errors: ProductErrorFragment[];
|
||||||
|
@ -93,6 +94,7 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
|
||||||
isChecked,
|
isChecked,
|
||||||
productListToolbar,
|
productListToolbar,
|
||||||
selected,
|
selected,
|
||||||
|
selectedChannelId,
|
||||||
subcategoryListToolbar,
|
subcategoryListToolbar,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll
|
toggleAll
|
||||||
|
@ -216,6 +218,7 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
|
||||||
toggle={toggle}
|
toggle={toggle}
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
|
selectedChannelId={selectedChannelId}
|
||||||
isChecked={isChecked}
|
isChecked={isChecked}
|
||||||
toolbar={productListToolbar}
|
toolbar={productListToolbar}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import DeleteIcon from "@material-ui/icons/Delete";
|
import DeleteIcon from "@material-ui/icons/Delete";
|
||||||
import { useChannelsList } from "@saleor/channels/queries";
|
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
|
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
|
||||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
|
@ -14,6 +14,7 @@ import usePaginator, {
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||||
import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler";
|
import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler";
|
||||||
|
import { mapNodeToChoice } from "@saleor/utils/maps";
|
||||||
import {
|
import {
|
||||||
useMetadataUpdate,
|
useMetadataUpdate,
|
||||||
usePrivateMetadataUpdate
|
usePrivateMetadataUpdate
|
||||||
|
@ -79,12 +80,9 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
||||||
variables: { ...paginationState, id }
|
variables: { ...paginationState, id }
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: channelsData } = useChannelsList({});
|
const { availableChannels, channel } = useAppChannel();
|
||||||
|
|
||||||
const channelChoices = channelsData?.channels?.map(channel => ({
|
const channelChoices = mapNodeToChoice(availableChannels);
|
||||||
label: channel.name,
|
|
||||||
value: channel.id
|
|
||||||
}));
|
|
||||||
|
|
||||||
const category = data?.category;
|
const category = data?.category;
|
||||||
|
|
||||||
|
@ -213,7 +211,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
||||||
<>
|
<>
|
||||||
<WindowTitle title={maybe(() => data.category.name)} />
|
<WindowTitle title={maybe(() => data.category.name)} />
|
||||||
<CategoryUpdatePage
|
<CategoryUpdatePage
|
||||||
channelsCount={channelsData?.channels?.length}
|
channelsCount={availableChannels.length}
|
||||||
channelChoices={channelChoices}
|
channelChoices={channelChoices}
|
||||||
changeTab={changeTab}
|
changeTab={changeTab}
|
||||||
currentTab={params.activeTab}
|
currentTab={params.activeTab}
|
||||||
|
@ -258,6 +256,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
||||||
data.category.products.edges.map(edge => edge.node)
|
data.category.products.edges.map(edge => edge.node)
|
||||||
)}
|
)}
|
||||||
saveButtonBarState={updateResult.status}
|
saveButtonBarState={updateResult.status}
|
||||||
|
selectedChannelId={channel.id}
|
||||||
subcategories={maybe(() =>
|
subcategories={maybe(() =>
|
||||||
data.category.children.edges.map(edge => edge.node)
|
data.category.children.edges.map(edge => edge.node)
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
|
|
||||||
export interface Channel_channel {
|
export interface Channel_channel {
|
||||||
__typename: "Channel";
|
__typename: "Channel";
|
||||||
id: string;
|
|
||||||
isActive: boolean;
|
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
id: string;
|
||||||
|
isActive: boolean;
|
||||||
currencyCode: string;
|
currencyCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,10 @@ import { ChannelErrorCode } from "./../../types/globalTypes";
|
||||||
|
|
||||||
export interface ChannelActivate_channelActivate_channel {
|
export interface ChannelActivate_channelActivate_channel {
|
||||||
__typename: "Channel";
|
__typename: "Channel";
|
||||||
id: string;
|
|
||||||
isActive: boolean;
|
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
id: string;
|
||||||
|
isActive: boolean;
|
||||||
currencyCode: string;
|
currencyCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,10 @@ import { ChannelCreateInput, ChannelErrorCode } from "./../../types/globalTypes"
|
||||||
|
|
||||||
export interface ChannelCreate_channelCreate_channel {
|
export interface ChannelCreate_channelCreate_channel {
|
||||||
__typename: "Channel";
|
__typename: "Channel";
|
||||||
id: string;
|
|
||||||
isActive: boolean;
|
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
id: string;
|
||||||
|
isActive: boolean;
|
||||||
currencyCode: string;
|
currencyCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,10 @@ import { ChannelErrorCode } from "./../../types/globalTypes";
|
||||||
|
|
||||||
export interface ChannelDeactivate_channelDeactivate_channel {
|
export interface ChannelDeactivate_channelDeactivate_channel {
|
||||||
__typename: "Channel";
|
__typename: "Channel";
|
||||||
id: string;
|
|
||||||
isActive: boolean;
|
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
id: string;
|
||||||
|
isActive: boolean;
|
||||||
currencyCode: string;
|
currencyCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,10 @@ import { ChannelDeleteInput, ChannelErrorCode } from "./../../types/globalTypes"
|
||||||
|
|
||||||
export interface ChannelDelete_channelDelete_channel {
|
export interface ChannelDelete_channelDelete_channel {
|
||||||
__typename: "Channel";
|
__typename: "Channel";
|
||||||
id: string;
|
|
||||||
isActive: boolean;
|
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
id: string;
|
||||||
|
isActive: boolean;
|
||||||
currencyCode: string;
|
currencyCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,10 @@ import { ChannelUpdateInput, ChannelErrorCode } from "./../../types/globalTypes"
|
||||||
|
|
||||||
export interface ChannelUpdate_channelUpdate_channel {
|
export interface ChannelUpdate_channelUpdate_channel {
|
||||||
__typename: "Channel";
|
__typename: "Channel";
|
||||||
id: string;
|
|
||||||
isActive: boolean;
|
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
id: string;
|
||||||
|
isActive: boolean;
|
||||||
currencyCode: string;
|
currencyCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
|
|
||||||
export interface Channels_channels {
|
export interface Channels_channels {
|
||||||
__typename: "Channel";
|
__typename: "Channel";
|
||||||
id: string;
|
|
||||||
isActive: boolean;
|
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
id: string;
|
||||||
|
isActive: boolean;
|
||||||
currencyCode: string;
|
currencyCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { RequireOnlyOne } from "@saleor/misc";
|
||||||
import { ProductDetails_product } from "@saleor/products/types/ProductDetails";
|
import { ProductDetails_product } from "@saleor/products/types/ProductDetails";
|
||||||
import { ProductVariantDetails_productVariant } from "@saleor/products/types/ProductVariantDetails";
|
import { ProductVariantDetails_productVariant } from "@saleor/products/types/ProductVariantDetails";
|
||||||
import { ShippingZone_shippingZone_shippingMethods_channelListings } from "@saleor/shipping/types/ShippingZone";
|
import { ShippingZone_shippingZone_shippingMethods_channelListings } from "@saleor/shipping/types/ShippingZone";
|
||||||
import { uniqBy } from "lodash";
|
import uniqBy from "lodash-es/uniqBy";
|
||||||
|
|
||||||
export interface Channel {
|
export interface Channel {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
@ -16,14 +16,17 @@ import { sectionNames } from "@saleor/intl";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import { ListActions, PageListProps } from "../../../types";
|
import { ChannelProps, ListActions, PageListProps } from "../../../types";
|
||||||
import { CollectionDetails_collection } from "../../types/CollectionDetails";
|
import { CollectionDetails_collection } from "../../types/CollectionDetails";
|
||||||
import CollectionDetails from "../CollectionDetails/CollectionDetails";
|
import CollectionDetails from "../CollectionDetails/CollectionDetails";
|
||||||
import { CollectionImage } from "../CollectionImage/CollectionImage";
|
import { CollectionImage } from "../CollectionImage/CollectionImage";
|
||||||
import CollectionProducts from "../CollectionProducts/CollectionProducts";
|
import CollectionProducts from "../CollectionProducts/CollectionProducts";
|
||||||
import CollectionUpdateForm, { CollectionUpdateData } from "./form";
|
import CollectionUpdateForm, { CollectionUpdateData } from "./form";
|
||||||
|
|
||||||
export interface CollectionDetailsPageProps extends PageListProps, ListActions {
|
export interface CollectionDetailsPageProps
|
||||||
|
extends PageListProps,
|
||||||
|
ListActions,
|
||||||
|
ChannelProps {
|
||||||
channelsCount: number;
|
channelsCount: number;
|
||||||
channelsErrors: CollectionChannelListingErrorFragment[];
|
channelsErrors: CollectionChannelListingErrorFragment[];
|
||||||
collection: CollectionDetails_collection;
|
collection: CollectionDetails_collection;
|
||||||
|
@ -31,7 +34,6 @@ export interface CollectionDetailsPageProps extends PageListProps, ListActions {
|
||||||
errors: CollectionErrorFragment[];
|
errors: CollectionErrorFragment[];
|
||||||
hasChannelChanged: boolean;
|
hasChannelChanged: boolean;
|
||||||
saveButtonBarState: ConfirmButtonTransitionState;
|
saveButtonBarState: ConfirmButtonTransitionState;
|
||||||
selectedChannel: string;
|
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
onCollectionRemove: () => void;
|
onCollectionRemove: () => void;
|
||||||
onImageDelete: () => void;
|
onImageDelete: () => void;
|
||||||
|
@ -51,7 +53,7 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
|
||||||
errors,
|
errors,
|
||||||
hasChannelChanged,
|
hasChannelChanged,
|
||||||
saveButtonBarState,
|
saveButtonBarState,
|
||||||
selectedChannel,
|
selectedChannelId,
|
||||||
onBack,
|
onBack,
|
||||||
onCollectionRemove,
|
onCollectionRemove,
|
||||||
onImageDelete,
|
onImageDelete,
|
||||||
|
@ -99,7 +101,7 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
|
||||||
<CollectionProducts
|
<CollectionProducts
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
channelsCount={channelsCount}
|
channelsCount={channelsCount}
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={selectedChannelId}
|
||||||
collection={collection}
|
collection={collection}
|
||||||
{...collectionProductsProps}
|
{...collectionProductsProps}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -12,7 +12,7 @@ import TableCellHeader from "@saleor/components/TableCellHeader";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import { maybe, renderCollection } from "@saleor/misc";
|
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 { getArrowDirection } from "@saleor/utils/sort";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
@ -47,10 +47,10 @@ const useStyles = makeStyles(
|
||||||
interface CollectionListProps
|
interface CollectionListProps
|
||||||
extends ListProps,
|
extends ListProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
SortPage<CollectionListUrlSortField> {
|
SortPage<CollectionListUrlSortField>,
|
||||||
|
ChannelProps {
|
||||||
collections: CollectionList_collections_edges_node[];
|
collections: CollectionList_collections_edges_node[];
|
||||||
channelsCount: number;
|
channelsCount: number;
|
||||||
selectedChannel: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const numberOfColumns = 4;
|
const numberOfColumns = 4;
|
||||||
|
@ -70,7 +70,7 @@ const CollectionList: React.FC<CollectionListProps> = props => {
|
||||||
pageInfo,
|
pageInfo,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
selectedChannel,
|
selectedChannelId,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar
|
||||||
|
@ -147,7 +147,7 @@ const CollectionList: React.FC<CollectionListProps> = props => {
|
||||||
collection => {
|
collection => {
|
||||||
const isSelected = collection ? isChecked(collection.id) : false;
|
const isSelected = collection ? isChecked(collection.id) : false;
|
||||||
const channel = collection?.channelListings.find(
|
const channel = collection?.channelListings.find(
|
||||||
listing => listing.channel.id === selectedChannel
|
listing => listing.channel.id === selectedChannelId
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import Button from "@material-ui/core/Button";
|
import Button from "@material-ui/core/Button";
|
||||||
import Card from "@material-ui/core/Card";
|
import Card from "@material-ui/core/Card";
|
||||||
import makeStyles from "@material-ui/core/styles/makeStyles";
|
|
||||||
import { CollectionListUrlSortField } from "@saleor/collections/urls";
|
import { CollectionListUrlSortField } from "@saleor/collections/urls";
|
||||||
import CardMenu from "@saleor/components/CardMenu";
|
|
||||||
import { Container } from "@saleor/components/Container";
|
import { Container } from "@saleor/components/Container";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import SearchBar from "@saleor/components/SearchBar";
|
import SearchBar from "@saleor/components/SearchBar";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import {
|
import {
|
||||||
|
ChannelProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
PageListProps,
|
PageListProps,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
|
@ -25,22 +24,12 @@ export interface CollectionListPageProps
|
||||||
ListActions,
|
ListActions,
|
||||||
SearchPageProps,
|
SearchPageProps,
|
||||||
SortPage<CollectionListUrlSortField>,
|
SortPage<CollectionListUrlSortField>,
|
||||||
TabPageProps {
|
TabPageProps,
|
||||||
|
ChannelProps {
|
||||||
collections: CollectionList_collections_edges_node[];
|
collections: CollectionList_collections_edges_node[];
|
||||||
channelsCount: number;
|
channelsCount: number;
|
||||||
selectedChannel: string;
|
|
||||||
onSettingsOpen?: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
|
||||||
theme => ({
|
|
||||||
settings: {
|
|
||||||
marginRight: theme.spacing(2)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
{ name: "CollectionListPage" }
|
|
||||||
);
|
|
||||||
|
|
||||||
const CollectionListPage: React.FC<CollectionListPageProps> = ({
|
const CollectionListPage: React.FC<CollectionListPageProps> = ({
|
||||||
channelsCount,
|
channelsCount,
|
||||||
currentTab,
|
currentTab,
|
||||||
|
@ -49,34 +38,18 @@ const CollectionListPage: React.FC<CollectionListPageProps> = ({
|
||||||
onAdd,
|
onAdd,
|
||||||
onAll,
|
onAll,
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
onSettingsOpen,
|
|
||||||
onTabChange,
|
onTabChange,
|
||||||
onTabDelete,
|
onTabDelete,
|
||||||
onTabSave,
|
onTabSave,
|
||||||
selectedChannel,
|
selectedChannelId,
|
||||||
tabs,
|
tabs,
|
||||||
...listProps
|
...listProps
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const classes = useStyles({});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<PageHeader title={intl.formatMessage(sectionNames.collections)}>
|
<PageHeader title={intl.formatMessage(sectionNames.collections)}>
|
||||||
{!!onSettingsOpen && (
|
|
||||||
<CardMenu
|
|
||||||
className={classes.settings}
|
|
||||||
menuItems={[
|
|
||||||
{
|
|
||||||
label: intl.formatMessage({
|
|
||||||
defaultMessage: "Settings",
|
|
||||||
description: "button"
|
|
||||||
}),
|
|
||||||
onSelect: onSettingsOpen
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -110,7 +83,7 @@ const CollectionListPage: React.FC<CollectionListPageProps> = ({
|
||||||
<CollectionList
|
<CollectionList
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
channelsCount={channelsCount}
|
channelsCount={channelsCount}
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={selectedChannelId}
|
||||||
{...listProps}
|
{...listProps}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
@ -21,7 +21,7 @@ import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { maybe, renderCollection } from "../../../misc";
|
import { maybe, renderCollection } from "../../../misc";
|
||||||
import { ListActions, PageListProps } from "../../../types";
|
import { ChannelProps, ListActions, PageListProps } from "../../../types";
|
||||||
import { CollectionDetails_collection } from "../../types/CollectionDetails";
|
import { CollectionDetails_collection } from "../../types/CollectionDetails";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
|
@ -55,10 +55,12 @@ const useStyles = makeStyles(
|
||||||
{ name: "CollectionProducts" }
|
{ name: "CollectionProducts" }
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface CollectionProductsProps extends PageListProps, ListActions {
|
export interface CollectionProductsProps
|
||||||
|
extends PageListProps,
|
||||||
|
ListActions,
|
||||||
|
ChannelProps {
|
||||||
collection: CollectionDetails_collection;
|
collection: CollectionDetails_collection;
|
||||||
channelsCount: number;
|
channelsCount: number;
|
||||||
selectedChannel: string;
|
|
||||||
onProductUnassign: (id: string, event: React.MouseEvent<any>) => void;
|
onProductUnassign: (id: string, event: React.MouseEvent<any>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +77,7 @@ const CollectionProducts: React.FC<CollectionProductsProps> = props => {
|
||||||
onProductUnassign,
|
onProductUnassign,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
selectedChannel,
|
selectedChannelId,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
toggle,
|
toggle,
|
||||||
|
@ -167,7 +169,7 @@ const CollectionProducts: React.FC<CollectionProductsProps> = props => {
|
||||||
const isSelected = product ? isChecked(product.id) : false;
|
const isSelected = product ? isChecked(product.id) : false;
|
||||||
const channel =
|
const channel =
|
||||||
product?.channelListings.find(
|
product?.channelListings.find(
|
||||||
listing => listing.channel.id === selectedChannel
|
listing => listing.channel.id === selectedChannelId
|
||||||
) || product?.channelListings[0];
|
) || product?.channelListings[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -19,7 +19,7 @@ export enum CollectionListUrlFiltersEnum {
|
||||||
query = "query"
|
query = "query"
|
||||||
}
|
}
|
||||||
export type CollectionListUrlFilters = Filters<CollectionListUrlFiltersEnum>;
|
export type CollectionListUrlFilters = Filters<CollectionListUrlFiltersEnum>;
|
||||||
export type CollectionListUrlDialog = "remove" | "settings" | TabActionDialog;
|
export type CollectionListUrlDialog = "remove" | TabActionDialog;
|
||||||
export enum CollectionListUrlSortField {
|
export enum CollectionListUrlSortField {
|
||||||
name = "name",
|
name = "name",
|
||||||
available = "available",
|
available = "available",
|
||||||
|
|
|
@ -336,7 +336,7 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({
|
||||||
collectionChannelsChoices?.length !== currentChannels?.length
|
collectionChannelsChoices?.length !== currentChannels?.length
|
||||||
}
|
}
|
||||||
channelsCount={channelsData?.channels?.length}
|
channelsCount={channelsData?.channels?.length}
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={selectedChannel}
|
||||||
openChannelsModal={handleChannelsModalOpen}
|
openChannelsModal={handleChannelsModalOpen}
|
||||||
onChannelsChange={setCurrentChannels}
|
onChannelsChange={setCurrentChannels}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import DeleteIcon from "@material-ui/icons/Delete";
|
import DeleteIcon from "@material-ui/icons/Delete";
|
||||||
import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog";
|
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
|
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
|
||||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||||
import SaveFilterTabDialog, {
|
import SaveFilterTabDialog, {
|
||||||
SaveFilterTabDialogFormData
|
SaveFilterTabDialogFormData
|
||||||
} from "@saleor/components/SaveFilterTabDialog";
|
} from "@saleor/components/SaveFilterTabDialog";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
import useChannelsSettings from "@saleor/hooks/useChannelsSettings";
|
|
||||||
import useListSettings from "@saleor/hooks/useListSettings";
|
import useListSettings from "@saleor/hooks/useListSettings";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
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 tabs = getFilterTabs();
|
||||||
|
|
||||||
const currentTab =
|
const currentTab =
|
||||||
|
@ -114,12 +116,6 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
|
||||||
CollectionListUrlQueryParams
|
CollectionListUrlQueryParams
|
||||||
>(navigate, collectionListUrl, params);
|
>(navigate, collectionListUrl, params);
|
||||||
|
|
||||||
const {
|
|
||||||
channelChoices,
|
|
||||||
handleChannelSelectConfirm,
|
|
||||||
selectedChannel
|
|
||||||
} = useChannelsSettings("collectionListChannel", { closeModal, openModal });
|
|
||||||
|
|
||||||
const handleTabChange = (tab: number) => {
|
const handleTabChange = (tab: number) => {
|
||||||
reset();
|
reset();
|
||||||
navigate(
|
navigate(
|
||||||
|
@ -151,16 +147,6 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!!channelChoices?.length && (
|
|
||||||
<ChannelSettingsDialog
|
|
||||||
channelsChoices={channelChoices}
|
|
||||||
defaultChoice={selectedChannel}
|
|
||||||
open={params.action === "settings"}
|
|
||||||
confirmButtonState="default"
|
|
||||||
onClose={closeModal}
|
|
||||||
onConfirm={handleChannelSelectConfirm}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<CollectionListPage
|
<CollectionListPage
|
||||||
currentTab={currentTab}
|
currentTab={currentTab}
|
||||||
initialSearch={params.query || ""}
|
initialSearch={params.query || ""}
|
||||||
|
@ -197,11 +183,8 @@ export const CollectionList: React.FC<CollectionListProps> = ({ params }) => {
|
||||||
selected={listElements.length}
|
selected={listElements.length}
|
||||||
toggle={toggle}
|
toggle={toggle}
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
channelsCount={channelChoices?.length}
|
channelsCount={availableChannels?.length}
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={channel.id}
|
||||||
onSettingsOpen={
|
|
||||||
!!channelChoices?.length ? () => openModal("settings") : undefined
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<ActionDialog
|
<ActionDialog
|
||||||
open={params.action === "remove" && maybe(() => params.ids.length > 0)}
|
open={params.action === "remove" && maybe(() => params.ids.length > 0)}
|
||||||
|
|
75
src/components/AppLayout/AppChannelContext.tsx
Normal file
75
src/components/AppLayout/AppChannelContext.tsx
Normal 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;
|
51
src/components/AppLayout/AppChannelSelect.tsx
Normal file
51
src/components/AppLayout/AppChannelSelect.tsx
Normal 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;
|
|
@ -20,10 +20,11 @@ import SideBar from "../SideBar";
|
||||||
import SideBarDrawer from "../SideBarDrawer/SideBarDrawer";
|
import SideBarDrawer from "../SideBarDrawer/SideBarDrawer";
|
||||||
import UserChip from "../UserChip";
|
import UserChip from "../UserChip";
|
||||||
import AppActionContext from "./AppActionContext";
|
import AppActionContext from "./AppActionContext";
|
||||||
|
import useAppChannel from "./AppChannelContext";
|
||||||
|
import AppChannelSelect from "./AppChannelSelect";
|
||||||
import AppHeaderContext from "./AppHeaderContext";
|
import AppHeaderContext from "./AppHeaderContext";
|
||||||
import { appLoaderHeight } from "./consts";
|
import { appLoaderHeight } from "./consts";
|
||||||
import createMenuStructure from "./menuStructure";
|
import createMenuStructure from "./menuStructure";
|
||||||
import ThemeSwitch from "./ThemeSwitch";
|
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
theme => ({
|
theme => ({
|
||||||
|
@ -128,6 +129,12 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
||||||
const [isNavigatorVisible, setNavigatorVisibility] = React.useState(false);
|
const [isNavigatorVisible, setNavigatorVisibility] = React.useState(false);
|
||||||
const isMdUp = useMediaQuery((theme: Theme) => theme.breakpoints.up("md"));
|
const isMdUp = useMediaQuery((theme: Theme) => theme.breakpoints.up("md"));
|
||||||
const [docked, setDocked] = React.useState(true);
|
const [docked, setDocked] = React.useState(true);
|
||||||
|
const {
|
||||||
|
availableChannels,
|
||||||
|
channel,
|
||||||
|
isPickerActive,
|
||||||
|
setChannel
|
||||||
|
} = useAppChannel(false);
|
||||||
|
|
||||||
const menuStructure = createMenuStructure(intl);
|
const menuStructure = createMenuStructure(intl);
|
||||||
const configurationMenu = createConfigurationMenu(intl);
|
const configurationMenu = createConfigurationMenu(intl);
|
||||||
|
@ -202,23 +209,26 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
||||||
)}
|
)}
|
||||||
<div className={classes.spacer} />
|
<div className={classes.spacer} />
|
||||||
<div className={classes.userBar}>
|
<div className={classes.userBar}>
|
||||||
<ThemeSwitch
|
|
||||||
className={classes.darkThemeSwitch}
|
|
||||||
checked={isDark}
|
|
||||||
onClick={toggleTheme}
|
|
||||||
/>
|
|
||||||
<NavigatorButton
|
<NavigatorButton
|
||||||
isMac={navigator.platform
|
isMac={navigator.platform
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes("mac")}
|
.includes("mac")}
|
||||||
onClick={() => setNavigatorVisibility(true)}
|
onClick={() => setNavigatorVisibility(true)}
|
||||||
/>
|
/>
|
||||||
|
<AppChannelSelect
|
||||||
|
channels={availableChannels}
|
||||||
|
disabled={!isPickerActive}
|
||||||
|
selectedChannelId={channel.id}
|
||||||
|
onChannelSelect={setChannel}
|
||||||
|
/>
|
||||||
<UserChip
|
<UserChip
|
||||||
|
isDarkThemeEnabled={isDark}
|
||||||
|
user={user}
|
||||||
onLogout={logout}
|
onLogout={logout}
|
||||||
onProfileClick={() =>
|
onProfileClick={() =>
|
||||||
navigate(staffMemberDetailsUrl(user.id))
|
navigate(staffMemberDetailsUrl(user.id))
|
||||||
}
|
}
|
||||||
user={user}
|
onThemeToggle={toggleTheme}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -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;
|
|
|
@ -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} />);
|
|
|
@ -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;
|
|
|
@ -1,2 +0,0 @@
|
||||||
export * from "./ChannelsSelect";
|
|
||||||
export { default } from "./ChannelsSelect";
|
|
|
@ -1,6 +1,7 @@
|
||||||
import Avatar from "@material-ui/core/Avatar";
|
import Avatar from "@material-ui/core/Avatar";
|
||||||
import Chip from "@material-ui/core/Chip";
|
import Chip from "@material-ui/core/Chip";
|
||||||
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
|
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
|
||||||
|
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||||
import Grow from "@material-ui/core/Grow";
|
import Grow from "@material-ui/core/Grow";
|
||||||
import Hidden from "@material-ui/core/Hidden";
|
import Hidden from "@material-ui/core/Hidden";
|
||||||
import MenuItem from "@material-ui/core/MenuItem";
|
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 Paper from "@material-ui/core/Paper";
|
||||||
import Popper from "@material-ui/core/Popper";
|
import Popper from "@material-ui/core/Popper";
|
||||||
import makeStyles from "@material-ui/core/styles/makeStyles";
|
import makeStyles from "@material-ui/core/styles/makeStyles";
|
||||||
|
import Switch from "@material-ui/core/Switch";
|
||||||
import { User } from "@saleor/fragments/types/User";
|
import { User } from "@saleor/fragments/types/User";
|
||||||
import ArrowDropdown from "@saleor/icons/ArrowDropdown";
|
import ArrowDropdown from "@saleor/icons/ArrowDropdown";
|
||||||
import { getUserInitials, getUserName } from "@saleor/misc";
|
import { getUserInitials, getUserName } from "@saleor/misc";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
theme => ({
|
theme => ({
|
||||||
|
@ -46,11 +48,16 @@ const useStyles = makeStyles(
|
||||||
},
|
},
|
||||||
popover: {
|
popover: {
|
||||||
marginTop: theme.spacing(2),
|
marginTop: theme.spacing(2),
|
||||||
zIndex: 1
|
zIndex: 10
|
||||||
},
|
},
|
||||||
rotate: {
|
rotate: {
|
||||||
transform: "rotate(180deg)"
|
transform: "rotate(180deg)"
|
||||||
},
|
},
|
||||||
|
switch: {
|
||||||
|
"&&:hover": {
|
||||||
|
background: "transparent"
|
||||||
|
}
|
||||||
|
},
|
||||||
userChip: {
|
userChip: {
|
||||||
[theme.breakpoints.down("sm")]: {
|
[theme.breakpoints.down("sm")]: {
|
||||||
height: 48
|
height: 48
|
||||||
|
@ -74,19 +81,24 @@ const useStyles = makeStyles(
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface UserChipProps {
|
export interface UserChipProps {
|
||||||
|
isDarkThemeEnabled: boolean;
|
||||||
user: User;
|
user: User;
|
||||||
onLogout: () => void;
|
onLogout: () => void;
|
||||||
onProfileClick: () => void;
|
onProfileClick: () => void;
|
||||||
|
onThemeToggle: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserChip: React.FC<UserChipProps> = ({
|
const UserChip: React.FC<UserChipProps> = ({
|
||||||
|
isDarkThemeEnabled,
|
||||||
user,
|
user,
|
||||||
onLogout,
|
onLogout,
|
||||||
onProfileClick
|
onProfileClick,
|
||||||
|
onThemeToggle
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles({});
|
const classes = useStyles({});
|
||||||
const [isMenuOpened, setMenuState] = React.useState(false);
|
const [isMenuOpened, setMenuState] = React.useState(false);
|
||||||
const anchor = React.useRef<HTMLDivElement>();
|
const anchor = React.useRef<HTMLDivElement>();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
setMenuState(false);
|
setMenuState(false);
|
||||||
|
@ -170,6 +182,29 @@ const UserChip: React.FC<UserChipProps> = ({
|
||||||
description="button"
|
description="button"
|
||||||
/>
|
/>
|
||||||
</MenuItem>
|
</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>
|
</Menu>
|
||||||
</ClickAwayListener>
|
</ClickAwayListener>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
|
@ -21,14 +21,16 @@ import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { maybe, renderCollection } from "../../../misc";
|
import { maybe, renderCollection } from "../../../misc";
|
||||||
import { ListActions, ListProps } from "../../../types";
|
import { ChannelProps, ListActions, ListProps } from "../../../types";
|
||||||
import { SaleDetails_sale } from "../../types/SaleDetails";
|
import { SaleDetails_sale } from "../../types/SaleDetails";
|
||||||
import { VoucherDetails_voucher } from "../../types/VoucherDetails";
|
import { VoucherDetails_voucher } from "../../types/VoucherDetails";
|
||||||
|
|
||||||
export interface SaleProductsProps extends ListProps, ListActions {
|
export interface SaleProductsProps
|
||||||
|
extends ListProps,
|
||||||
|
ListActions,
|
||||||
|
ChannelProps {
|
||||||
discount: SaleDetails_sale | VoucherDetails_voucher;
|
discount: SaleDetails_sale | VoucherDetails_voucher;
|
||||||
channelsCount: number;
|
channelsCount: number;
|
||||||
selectedChannel: string;
|
|
||||||
onProductAssign: () => void;
|
onProductAssign: () => void;
|
||||||
onProductUnassign: (id: string) => void;
|
onProductUnassign: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
@ -79,7 +81,7 @@ const DiscountProducts: React.FC<SaleProductsProps> = props => {
|
||||||
onNextPage,
|
onNextPage,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
selectedChannel,
|
selectedChannelId,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar
|
||||||
|
@ -156,7 +158,7 @@ const DiscountProducts: React.FC<SaleProductsProps> = props => {
|
||||||
const isSelected = product ? isChecked(product.id) : false;
|
const isSelected = product ? isChecked(product.id) : false;
|
||||||
const channel =
|
const channel =
|
||||||
product?.channelListings.find(
|
product?.channelListings.find(
|
||||||
listing => listing.channel.id === selectedChannel
|
listing => listing.channel.id === selectedChannelId
|
||||||
) || product?.channelListings[0];
|
) || product?.channelListings[0];
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
|
|
|
@ -17,7 +17,7 @@ import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import { maybe, splitDateTime } from "../../../misc";
|
import { maybe, splitDateTime } from "../../../misc";
|
||||||
import { ListProps, TabListActions } from "../../../types";
|
import { ChannelProps, ListProps, TabListActions } from "../../../types";
|
||||||
import { SaleType as SaleTypeEnum } from "../../../types/globalTypes";
|
import { SaleType as SaleTypeEnum } from "../../../types/globalTypes";
|
||||||
import { SaleDetails_sale } from "../../types/SaleDetails";
|
import { SaleDetails_sale } from "../../types/SaleDetails";
|
||||||
import DiscountCategories from "../DiscountCategories";
|
import DiscountCategories from "../DiscountCategories";
|
||||||
|
@ -57,11 +57,11 @@ export interface SaleDetailsPageProps
|
||||||
extends Pick<ListProps, Exclude<keyof ListProps, "onRowClick">>,
|
extends Pick<ListProps, Exclude<keyof ListProps, "onRowClick">>,
|
||||||
TabListActions<
|
TabListActions<
|
||||||
"categoryListToolbar" | "collectionListToolbar" | "productListToolbar"
|
"categoryListToolbar" | "collectionListToolbar" | "productListToolbar"
|
||||||
> {
|
>,
|
||||||
|
ChannelProps {
|
||||||
activeTab: SaleDetailsPageTab;
|
activeTab: SaleDetailsPageTab;
|
||||||
errors: DiscountErrorFragment[];
|
errors: DiscountErrorFragment[];
|
||||||
sale: SaleDetails_sale;
|
sale: SaleDetails_sale;
|
||||||
selectedChannel: string;
|
|
||||||
allChannelsCount: number;
|
allChannelsCount: number;
|
||||||
channelListings: ChannelSaleData[];
|
channelListings: ChannelSaleData[];
|
||||||
hasChannelChanged: boolean;
|
hasChannelChanged: boolean;
|
||||||
|
@ -119,7 +119,7 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
|
||||||
productListToolbar,
|
productListToolbar,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
selectedChannel,
|
selectedChannelId,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll
|
toggleAll
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -270,7 +270,7 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
|
||||||
pageInfo={pageInfo}
|
pageInfo={pageInfo}
|
||||||
discount={sale}
|
discount={sale}
|
||||||
channelsCount={allChannelsCount}
|
channelsCount={allChannelsCount}
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={selectedChannelId}
|
||||||
isChecked={isChecked}
|
isChecked={isChecked}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
toggle={toggle}
|
toggle={toggle}
|
||||||
|
@ -287,7 +287,10 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<SaleSummary selectedChannel={selectedChannel} sale={sale} />
|
<SaleSummary
|
||||||
|
selectedChannelId={selectedChannelId}
|
||||||
|
sale={sale}
|
||||||
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
<ChannelsAvailability
|
<ChannelsAvailability
|
||||||
selectedChannelsCount={data.channelListings.length}
|
selectedChannelsCount={data.channelListings.length}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import { SaleListUrlSortField } from "@saleor/discounts/urls";
|
import { SaleListUrlSortField } from "@saleor/discounts/urls";
|
||||||
import { maybe, renderCollection } from "@saleor/misc";
|
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 { SaleType } from "@saleor/types/globalTypes";
|
||||||
import { getArrowDirection } from "@saleor/utils/sort";
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
@ -25,9 +25,9 @@ import { SaleList_sales_edges_node } from "../../types/SaleList";
|
||||||
export interface SaleListProps
|
export interface SaleListProps
|
||||||
extends ListProps,
|
extends ListProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
SortPage<SaleListUrlSortField> {
|
SortPage<SaleListUrlSortField>,
|
||||||
|
ChannelProps {
|
||||||
sales: SaleList_sales_edges_node[];
|
sales: SaleList_sales_edges_node[];
|
||||||
selectedChannel: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
|
@ -76,7 +76,7 @@ const SaleList: React.FC<SaleListProps> = props => {
|
||||||
onSort,
|
onSort,
|
||||||
pageInfo,
|
pageInfo,
|
||||||
sales,
|
sales,
|
||||||
selectedChannel,
|
selectedChannelId,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
sort,
|
sort,
|
||||||
|
@ -170,7 +170,7 @@ const SaleList: React.FC<SaleListProps> = props => {
|
||||||
sale => {
|
sale => {
|
||||||
const isSelected = sale ? isChecked(sale.id) : false;
|
const isSelected = sale ? isChecked(sale.id) : false;
|
||||||
const channel = sale?.channelListings?.find(
|
const channel = sale?.channelListings?.find(
|
||||||
lisiting => lisiting.channel.id === selectedChannel
|
lisiting => lisiting.channel.id === selectedChannelId
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import Button from "@material-ui/core/Button";
|
import Button from "@material-ui/core/Button";
|
||||||
import Card from "@material-ui/core/Card";
|
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 Container from "@saleor/components/Container";
|
||||||
import FilterBar from "@saleor/components/FilterBar";
|
import FilterBar from "@saleor/components/FilterBar";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import { SaleListUrlSortField } from "@saleor/discounts/urls";
|
import { SaleListUrlSortField } from "@saleor/discounts/urls";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import {
|
import {
|
||||||
|
ChannelProps,
|
||||||
FilterPageProps,
|
FilterPageProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
PageListProps,
|
PageListProps,
|
||||||
|
@ -30,19 +29,10 @@ export interface SaleListPageProps
|
||||||
ListActions,
|
ListActions,
|
||||||
FilterPageProps<SaleFilterKeys, SaleListFilterOpts>,
|
FilterPageProps<SaleFilterKeys, SaleListFilterOpts>,
|
||||||
SortPage<SaleListUrlSortField>,
|
SortPage<SaleListUrlSortField>,
|
||||||
TabPageProps {
|
TabPageProps,
|
||||||
|
ChannelProps {
|
||||||
sales: SaleList_sales_edges_node[];
|
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> = ({
|
const SaleListPage: React.FC<SaleListPageProps> = ({
|
||||||
currentTab,
|
currentTab,
|
||||||
|
@ -52,7 +42,6 @@ const SaleListPage: React.FC<SaleListPageProps> = ({
|
||||||
onAll,
|
onAll,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
onSettingsOpen,
|
|
||||||
onTabChange,
|
onTabChange,
|
||||||
onTabDelete,
|
onTabDelete,
|
||||||
onTabSave,
|
onTabSave,
|
||||||
|
@ -60,26 +49,11 @@ const SaleListPage: React.FC<SaleListPageProps> = ({
|
||||||
...listProps
|
...listProps
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const classes = useStyles({});
|
|
||||||
const structure = createFilterStructure(intl, filterOpts);
|
const structure = createFilterStructure(intl, filterOpts);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<PageHeader title={intl.formatMessage(sectionNames.sales)}>
|
<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">
|
<Button onClick={onAdd} variant="contained" color="primary">
|
||||||
<FormattedMessage defaultMessage="Create Sale" description="button" />
|
<FormattedMessage defaultMessage="Create Sale" description="button" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -10,6 +10,7 @@ import Money from "@saleor/components/Money";
|
||||||
import Percent from "@saleor/components/Percent";
|
import Percent from "@saleor/components/Percent";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
|
import { ChannelProps } from "@saleor/types";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
@ -17,16 +18,18 @@ import { maybe } from "../../../misc";
|
||||||
import { SaleType } from "../../../types/globalTypes";
|
import { SaleType } from "../../../types/globalTypes";
|
||||||
import { SaleDetails_sale } from "../../types/SaleDetails";
|
import { SaleDetails_sale } from "../../types/SaleDetails";
|
||||||
|
|
||||||
export interface SaleSummaryProps {
|
export interface SaleSummaryProps extends ChannelProps {
|
||||||
selectedChannel: string;
|
|
||||||
sale: SaleDetails_sale;
|
sale: SaleDetails_sale;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SaleSummary: React.FC<SaleSummaryProps> = ({ selectedChannel, sale }) => {
|
const SaleSummary: React.FC<SaleSummaryProps> = ({
|
||||||
|
selectedChannelId,
|
||||||
|
sale
|
||||||
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const channel = sale?.channelListings?.find(
|
const channel = sale?.channelListings?.find(
|
||||||
listing => listing.channel.id === selectedChannel
|
listing => listing.channel.id === selectedChannelId
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
|
|
|
@ -20,7 +20,7 @@ import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { maybe, splitDateTime } from "../../../misc";
|
import { maybe, splitDateTime } from "../../../misc";
|
||||||
import { ListProps, TabListActions } from "../../../types";
|
import { ChannelProps, ListProps, TabListActions } from "../../../types";
|
||||||
import {
|
import {
|
||||||
DiscountValueTypeEnum,
|
DiscountValueTypeEnum,
|
||||||
VoucherTypeEnum
|
VoucherTypeEnum
|
||||||
|
@ -73,12 +73,12 @@ export interface VoucherDetailsPageProps
|
||||||
extends Pick<ListProps, Exclude<keyof ListProps, "onRowClick">>,
|
extends Pick<ListProps, Exclude<keyof ListProps, "onRowClick">>,
|
||||||
TabListActions<
|
TabListActions<
|
||||||
"categoryListToolbar" | "collectionListToolbar" | "productListToolbar"
|
"categoryListToolbar" | "collectionListToolbar" | "productListToolbar"
|
||||||
> {
|
>,
|
||||||
|
ChannelProps {
|
||||||
activeTab: VoucherDetailsPageTab;
|
activeTab: VoucherDetailsPageTab;
|
||||||
errors: DiscountErrorFragment[];
|
errors: DiscountErrorFragment[];
|
||||||
saveButtonBarState: ConfirmButtonTransitionState;
|
saveButtonBarState: ConfirmButtonTransitionState;
|
||||||
voucher: VoucherDetails_voucher;
|
voucher: VoucherDetails_voucher;
|
||||||
selectedChannel: string;
|
|
||||||
allChannelsCount: number;
|
allChannelsCount: number;
|
||||||
channelListings: ChannelVoucherData[];
|
channelListings: ChannelVoucherData[];
|
||||||
hasChannelChanged: boolean;
|
hasChannelChanged: boolean;
|
||||||
|
@ -137,7 +137,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
selected,
|
selected,
|
||||||
selectedChannel,
|
selectedChannelId,
|
||||||
isChecked,
|
isChecked,
|
||||||
categoryListToolbar,
|
categoryListToolbar,
|
||||||
collectionListToolbar,
|
collectionListToolbar,
|
||||||
|
@ -145,7 +145,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const channel = voucher?.channelListings?.find(
|
const channel = voucher?.channelListings?.find(
|
||||||
listing => listing.channel.id === selectedChannel
|
listing => listing.channel.id === selectedChannelId
|
||||||
);
|
);
|
||||||
let requirementsPickerInitValue;
|
let requirementsPickerInitValue;
|
||||||
if (voucher?.minCheckoutItemsQuantity > 0) {
|
if (voucher?.minCheckoutItemsQuantity > 0) {
|
||||||
|
@ -331,7 +331,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
|
||||||
onRowClick={onProductClick}
|
onRowClick={onProductClick}
|
||||||
pageInfo={pageInfo}
|
pageInfo={pageInfo}
|
||||||
discount={voucher}
|
discount={voucher}
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={selectedChannelId}
|
||||||
channelsCount={allChannelsCount}
|
channelsCount={allChannelsCount}
|
||||||
isChecked={isChecked}
|
isChecked={isChecked}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
|
@ -391,7 +391,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<VoucherSummary
|
<VoucherSummary
|
||||||
voucher={voucher}
|
voucher={voucher}
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={selectedChannelId}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
<ChannelsAvailability
|
<ChannelsAvailability
|
||||||
|
|
|
@ -14,7 +14,7 @@ import TableHead from "@saleor/components/TableHead";
|
||||||
import TablePagination from "@saleor/components/TablePagination";
|
import TablePagination from "@saleor/components/TablePagination";
|
||||||
import { VoucherListUrlSortField } from "@saleor/discounts/urls";
|
import { VoucherListUrlSortField } from "@saleor/discounts/urls";
|
||||||
import { maybe, renderCollection } from "@saleor/misc";
|
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 { DiscountValueTypeEnum } from "@saleor/types/globalTypes";
|
||||||
import { getArrowDirection } from "@saleor/utils/sort";
|
import { getArrowDirection } from "@saleor/utils/sort";
|
||||||
import { getFooterColSpanWithBulkActions } from "@saleor/utils/tables";
|
import { getFooterColSpanWithBulkActions } from "@saleor/utils/tables";
|
||||||
|
@ -26,9 +26,9 @@ import { VoucherList_vouchers_edges_node } from "../../types/VoucherList";
|
||||||
export interface VoucherListProps
|
export interface VoucherListProps
|
||||||
extends ListProps,
|
extends ListProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
SortPage<VoucherListUrlSortField> {
|
SortPage<VoucherListUrlSortField>,
|
||||||
|
ChannelProps {
|
||||||
vouchers: VoucherList_vouchers_edges_node[];
|
vouchers: VoucherList_vouchers_edges_node[];
|
||||||
selectedChannel: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
|
@ -94,7 +94,7 @@ const VoucherList: React.FC<VoucherListProps> = props => {
|
||||||
vouchers,
|
vouchers,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
selectedChannel,
|
selectedChannelId,
|
||||||
sort,
|
sort,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
|
@ -219,7 +219,7 @@ const VoucherList: React.FC<VoucherListProps> = props => {
|
||||||
voucher => {
|
voucher => {
|
||||||
const isSelected = voucher ? isChecked(voucher.id) : false;
|
const isSelected = voucher ? isChecked(voucher.id) : false;
|
||||||
const channel = voucher?.channelListings?.find(
|
const channel = voucher?.channelListings?.find(
|
||||||
listing => listing.channel.id === selectedChannel
|
listing => listing.channel.id === selectedChannelId
|
||||||
);
|
);
|
||||||
const hasChannelsLoaded = voucher?.channelListings?.length;
|
const hasChannelsLoaded = voucher?.channelListings?.length;
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import Button from "@material-ui/core/Button";
|
import Button from "@material-ui/core/Button";
|
||||||
import Card from "@material-ui/core/Card";
|
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 Container from "@saleor/components/Container";
|
||||||
import FilterBar from "@saleor/components/FilterBar";
|
import FilterBar from "@saleor/components/FilterBar";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import { VoucherListUrlSortField } from "@saleor/discounts/urls";
|
import { VoucherListUrlSortField } from "@saleor/discounts/urls";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import {
|
import {
|
||||||
|
ChannelProps,
|
||||||
FilterPageProps,
|
FilterPageProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
PageListProps,
|
PageListProps,
|
||||||
|
@ -30,20 +29,10 @@ export interface VoucherListPageProps
|
||||||
ListActions,
|
ListActions,
|
||||||
FilterPageProps<VoucherFilterKeys, VoucherListFilterOpts>,
|
FilterPageProps<VoucherFilterKeys, VoucherListFilterOpts>,
|
||||||
SortPage<VoucherListUrlSortField>,
|
SortPage<VoucherListUrlSortField>,
|
||||||
TabPageProps {
|
TabPageProps,
|
||||||
|
ChannelProps {
|
||||||
vouchers: VoucherList_vouchers_edges_node[];
|
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> = ({
|
const VoucherListPage: React.FC<VoucherListPageProps> = ({
|
||||||
currentTab,
|
currentTab,
|
||||||
filterOpts,
|
filterOpts,
|
||||||
|
@ -52,7 +41,6 @@ const VoucherListPage: React.FC<VoucherListPageProps> = ({
|
||||||
onAll,
|
onAll,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
onSettingsOpen,
|
|
||||||
onTabChange,
|
onTabChange,
|
||||||
onTabDelete,
|
onTabDelete,
|
||||||
onTabSave,
|
onTabSave,
|
||||||
|
@ -60,26 +48,11 @@ const VoucherListPage: React.FC<VoucherListPageProps> = ({
|
||||||
...listProps
|
...listProps
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const classes = useStyles({});
|
|
||||||
const structure = createFilterStructure(intl, filterOpts);
|
const structure = createFilterStructure(intl, filterOpts);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<PageHeader title={intl.formatMessage(sectionNames.vouchers)}>
|
<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">
|
<Button onClick={onAdd} variant="contained" color="primary">
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Create voucher"
|
defaultMessage="Create voucher"
|
||||||
|
|
|
@ -10,6 +10,7 @@ import Money from "@saleor/components/Money";
|
||||||
import Percent from "@saleor/components/Percent";
|
import Percent from "@saleor/components/Percent";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
|
import { ChannelProps } from "@saleor/types";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
@ -18,20 +19,19 @@ import { DiscountValueTypeEnum } from "../../../types/globalTypes";
|
||||||
import { translateVoucherTypes } from "../../translations";
|
import { translateVoucherTypes } from "../../translations";
|
||||||
import { VoucherDetails_voucher } from "../../types/VoucherDetails";
|
import { VoucherDetails_voucher } from "../../types/VoucherDetails";
|
||||||
|
|
||||||
export interface VoucherSummaryProps {
|
export interface VoucherSummaryProps extends ChannelProps {
|
||||||
voucher: VoucherDetails_voucher;
|
voucher: VoucherDetails_voucher;
|
||||||
selectedChannel: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const VoucherSummary: React.FC<VoucherSummaryProps> = ({
|
const VoucherSummary: React.FC<VoucherSummaryProps> = ({
|
||||||
selectedChannel,
|
selectedChannelId,
|
||||||
voucher
|
voucher
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const translatedVoucherTypes = translateVoucherTypes(intl);
|
const translatedVoucherTypes = translateVoucherTypes(intl);
|
||||||
const channel = voucher?.channelListings?.find(
|
const channel = voucher?.channelListings?.find(
|
||||||
listing => listing.channel.id === selectedChannel
|
listing => listing.channel.id === selectedChannelId
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -29,7 +29,7 @@ export enum SaleListUrlFiltersWithMultipleValues {
|
||||||
}
|
}
|
||||||
export type SaleListUrlFilters = Filters<SaleListUrlFiltersEnum> &
|
export type SaleListUrlFilters = Filters<SaleListUrlFiltersEnum> &
|
||||||
FiltersWithMultipleValues<SaleListUrlFiltersWithMultipleValues>;
|
FiltersWithMultipleValues<SaleListUrlFiltersWithMultipleValues>;
|
||||||
export type SaleListUrlDialog = "remove" | "settings" | TabActionDialog;
|
export type SaleListUrlDialog = "remove" | TabActionDialog;
|
||||||
export enum SaleListUrlSortField {
|
export enum SaleListUrlSortField {
|
||||||
name = "name",
|
name = "name",
|
||||||
endDate = "end-date",
|
endDate = "end-date",
|
||||||
|
@ -79,7 +79,7 @@ export enum VoucherListUrlFiltersWithMultipleValues {
|
||||||
}
|
}
|
||||||
export type VoucherListUrlFilters = Filters<VoucherListUrlFiltersEnum> &
|
export type VoucherListUrlFilters = Filters<VoucherListUrlFiltersEnum> &
|
||||||
FiltersWithMultipleValues<VoucherListUrlFiltersWithMultipleValues>;
|
FiltersWithMultipleValues<VoucherListUrlFiltersWithMultipleValues>;
|
||||||
export type VoucherListUrlDialog = "remove" | "settings" | TabActionDialog;
|
export type VoucherListUrlDialog = "remove" | TabActionDialog;
|
||||||
export enum VoucherListUrlSortField {
|
export enum VoucherListUrlSortField {
|
||||||
code = "code",
|
code = "code",
|
||||||
endDate = "end-date",
|
endDate = "end-date",
|
||||||
|
|
|
@ -279,7 +279,7 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
||||||
...(updateChannelsOpts.data
|
...(updateChannelsOpts.data
|
||||||
?.saleChannelListingUpdate.errors || [])
|
?.saleChannelListingUpdate.errors || [])
|
||||||
]}
|
]}
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={selectedChannel}
|
||||||
pageInfo={pageInfo}
|
pageInfo={pageInfo}
|
||||||
openChannelsModal={handleChannelsModalOpen}
|
openChannelsModal={handleChannelsModalOpen}
|
||||||
onChannelsChange={setCurrentChannels}
|
onChannelsChange={setCurrentChannels}
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import DeleteIcon from "@material-ui/icons/Delete";
|
import DeleteIcon from "@material-ui/icons/Delete";
|
||||||
import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog";
|
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
|
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
|
||||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||||
import SaveFilterTabDialog, {
|
import SaveFilterTabDialog, {
|
||||||
SaveFilterTabDialogFormData
|
SaveFilterTabDialogFormData
|
||||||
} from "@saleor/components/SaveFilterTabDialog";
|
} from "@saleor/components/SaveFilterTabDialog";
|
||||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
import useChannelsSettings from "@saleor/hooks/useChannelsSettings";
|
|
||||||
import useListSettings from "@saleor/hooks/useListSettings";
|
import useListSettings from "@saleor/hooks/useListSettings";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
|
@ -64,18 +63,13 @@ export const SaleList: React.FC<SaleListProps> = ({ params }) => {
|
||||||
ListViews.SALES_LIST
|
ListViews.SALES_LIST
|
||||||
);
|
);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const { channel } = useAppChannel();
|
||||||
|
|
||||||
const [openModal, closeModal] = createDialogActionHandlers<
|
const [openModal, closeModal] = createDialogActionHandlers<
|
||||||
SaleListUrlDialog,
|
SaleListUrlDialog,
|
||||||
SaleListUrlQueryParams
|
SaleListUrlQueryParams
|
||||||
>(navigate, saleListUrl, params);
|
>(navigate, saleListUrl, params);
|
||||||
|
|
||||||
const {
|
|
||||||
channelChoices,
|
|
||||||
handleChannelSelectConfirm,
|
|
||||||
selectedChannel
|
|
||||||
} = useChannelsSettings("salesListChannel", { closeModal, openModal });
|
|
||||||
|
|
||||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||||
const queryVariables = React.useMemo(
|
const queryVariables = React.useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
@ -167,16 +161,6 @@ export const SaleList: React.FC<SaleListProps> = ({ params }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<WindowTitle title={intl.formatMessage(sectionNames.sales)} />
|
<WindowTitle title={intl.formatMessage(sectionNames.sales)} />
|
||||||
{!!channelChoices?.length && (
|
|
||||||
<ChannelSettingsDialog
|
|
||||||
channelsChoices={channelChoices}
|
|
||||||
defaultChoice={selectedChannel}
|
|
||||||
open={params.action === "settings"}
|
|
||||||
confirmButtonState="default"
|
|
||||||
onClose={closeModal}
|
|
||||||
onConfirm={handleChannelSelectConfirm}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<SaleListPage
|
<SaleListPage
|
||||||
currentTab={currentTab}
|
currentTab={currentTab}
|
||||||
filterOpts={getFilterOpts(params)}
|
filterOpts={getFilterOpts(params)}
|
||||||
|
@ -215,12 +199,7 @@ export const SaleList: React.FC<SaleListProps> = ({ params }) => {
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
}
|
}
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={channel.id}
|
||||||
onSettingsOpen={
|
|
||||||
!!channelChoices?.length
|
|
||||||
? () => openModal("settings")
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<ActionDialog
|
<ActionDialog
|
||||||
confirmButtonState={saleBulkDeleteOpts.status}
|
confirmButtonState={saleBulkDeleteOpts.status}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
createSortedChannelsDataFromVoucher
|
createSortedChannelsDataFromVoucher
|
||||||
} from "@saleor/channels/utils";
|
} from "@saleor/channels/utils";
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
|
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
|
||||||
import AssignCategoriesDialog from "@saleor/components/AssignCategoryDialog";
|
import AssignCategoriesDialog from "@saleor/components/AssignCategoryDialog";
|
||||||
import AssignCollectionDialog from "@saleor/components/AssignCollectionDialog";
|
import AssignCollectionDialog from "@saleor/components/AssignCollectionDialog";
|
||||||
import AssignProductDialog from "@saleor/components/AssignProductDialog";
|
import AssignProductDialog from "@saleor/components/AssignProductDialog";
|
||||||
|
@ -37,7 +38,6 @@ import {
|
||||||
} from "@saleor/discounts/urls";
|
} from "@saleor/discounts/urls";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
import useChannels from "@saleor/hooks/useChannels";
|
import useChannels from "@saleor/hooks/useChannels";
|
||||||
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
import usePaginator, {
|
import usePaginator, {
|
||||||
|
@ -145,8 +145,7 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
|
||||||
const [updateChannels, updateChannelsOpts] = useVoucherChannelListingUpdate(
|
const [updateChannels, updateChannelsOpts] = useVoucherChannelListingUpdate(
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
const { channel } = useAppChannel();
|
||||||
const [selectedChannel] = useLocalStorage("vouchersListChannel", "");
|
|
||||||
|
|
||||||
const handleVoucherDelete = (data: VoucherDelete) => {
|
const handleVoucherDelete = (data: VoucherDelete) => {
|
||||||
if (data.voucherDelete.errors.length === 0) {
|
if (data.voucherDelete.errors.length === 0) {
|
||||||
|
@ -295,7 +294,7 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
|
||||||
...(updateChannelsOpts.data
|
...(updateChannelsOpts.data
|
||||||
?.voucherChannelListingUpdate.errors || [])
|
?.voucherChannelListingUpdate.errors || [])
|
||||||
]}
|
]}
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={channel.id}
|
||||||
pageInfo={pageInfo}
|
pageInfo={pageInfo}
|
||||||
onNextPage={loadNextPage}
|
onNextPage={loadNextPage}
|
||||||
onPreviousPage={loadPreviousPage}
|
onPreviousPage={loadPreviousPage}
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import DeleteIcon from "@material-ui/icons/Delete";
|
import DeleteIcon from "@material-ui/icons/Delete";
|
||||||
import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog";
|
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
|
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
|
||||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||||
import SaveFilterTabDialog, {
|
import SaveFilterTabDialog, {
|
||||||
SaveFilterTabDialogFormData
|
SaveFilterTabDialogFormData
|
||||||
} from "@saleor/components/SaveFilterTabDialog";
|
} from "@saleor/components/SaveFilterTabDialog";
|
||||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
import useChannelsSettings from "@saleor/hooks/useChannelsSettings";
|
|
||||||
import useListSettings from "@saleor/hooks/useListSettings";
|
import useListSettings from "@saleor/hooks/useListSettings";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
|
@ -65,17 +64,13 @@ export const VoucherList: React.FC<VoucherListProps> = ({ params }) => {
|
||||||
);
|
);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const { channel } = useAppChannel();
|
||||||
|
|
||||||
const [openModal, closeModal] = createDialogActionHandlers<
|
const [openModal, closeModal] = createDialogActionHandlers<
|
||||||
VoucherListUrlDialog,
|
VoucherListUrlDialog,
|
||||||
VoucherListUrlQueryParams
|
VoucherListUrlQueryParams
|
||||||
>(navigate, voucherListUrl, params);
|
>(navigate, voucherListUrl, params);
|
||||||
|
|
||||||
const {
|
|
||||||
channelChoices,
|
|
||||||
handleChannelSelectConfirm,
|
|
||||||
selectedChannel
|
|
||||||
} = useChannelsSettings("vouchersListChannel", { closeModal, openModal });
|
|
||||||
|
|
||||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||||
const queryVariables = React.useMemo(
|
const queryVariables = React.useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
@ -167,16 +162,6 @@ export const VoucherList: React.FC<VoucherListProps> = ({ params }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<WindowTitle title={intl.formatMessage(sectionNames.vouchers)} />
|
<WindowTitle title={intl.formatMessage(sectionNames.vouchers)} />
|
||||||
{!!channelChoices?.length && (
|
|
||||||
<ChannelSettingsDialog
|
|
||||||
channelsChoices={channelChoices}
|
|
||||||
defaultChoice={selectedChannel}
|
|
||||||
open={params.action === "settings"}
|
|
||||||
confirmButtonState="default"
|
|
||||||
onClose={closeModal}
|
|
||||||
onConfirm={handleChannelSelectConfirm}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<VoucherListPage
|
<VoucherListPage
|
||||||
currentTab={currentTab}
|
currentTab={currentTab}
|
||||||
filterOpts={getFilterOpts(params)}
|
filterOpts={getFilterOpts(params)}
|
||||||
|
@ -215,12 +200,7 @@ export const VoucherList: React.FC<VoucherListProps> = ({ params }) => {
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
}
|
}
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={channel.id}
|
||||||
onSettingsOpen={
|
|
||||||
!!channelChoices?.length
|
|
||||||
? () => openModal("settings")
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<ActionDialog
|
<ActionDialog
|
||||||
confirmButtonState={voucherBulkDeleteOpts.status}
|
confirmButtonState={voucherBulkDeleteOpts.status}
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import SingleSelectField, {
|
|
||||||
Choices
|
|
||||||
} from "@saleor/components/SingleSelectField";
|
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
theme => ({
|
theme => ({
|
||||||
|
@ -27,16 +24,12 @@ const useStyles = makeStyles(
|
||||||
|
|
||||||
interface HomeOrdersCardProps {
|
interface HomeOrdersCardProps {
|
||||||
userName: string;
|
userName: string;
|
||||||
channelChoices: Choices;
|
|
||||||
channelValue: string;
|
|
||||||
onChannelChange: (value: string) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const HomeOrdersCard: React.FC<HomeOrdersCardProps> = props => {
|
const HomeOrdersCard: React.FC<HomeOrdersCardProps> = props => {
|
||||||
const { userName, channelChoices, channelValue, onChannelChange } = props;
|
const { userName } = props;
|
||||||
|
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
const intl = useIntl();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.headerContainer} data-test="home-header">
|
<div className={classes.headerContainer} data-test="home-header">
|
||||||
|
@ -71,20 +64,6 @@ const HomeOrdersCard: React.FC<HomeOrdersCardProps> = props => {
|
||||||
)}
|
)}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,6 @@ import Container from "@saleor/components/Container";
|
||||||
import Grid from "@saleor/components/Grid";
|
import Grid from "@saleor/components/Grid";
|
||||||
import Money from "@saleor/components/Money";
|
import Money from "@saleor/components/Money";
|
||||||
import RequirePermissions from "@saleor/components/RequirePermissions";
|
import RequirePermissions from "@saleor/components/RequirePermissions";
|
||||||
import { Choices } from "@saleor/components/SingleSelectField";
|
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import { UserPermissionProps } from "@saleor/types";
|
import { UserPermissionProps } from "@saleor/types";
|
||||||
import { PermissionEnum } from "@saleor/types/globalTypes";
|
import { PermissionEnum } from "@saleor/types/globalTypes";
|
||||||
|
@ -54,9 +53,6 @@ export interface HomePageProps extends UserPermissionProps {
|
||||||
sales: Home_salesToday_gross;
|
sales: Home_salesToday_gross;
|
||||||
topProducts: Home_productTopToday_edges_node[];
|
topProducts: Home_productTopToday_edges_node[];
|
||||||
userName: string;
|
userName: string;
|
||||||
channelChoices: Choices;
|
|
||||||
channelValue: string;
|
|
||||||
onChannelChange: (value: string) => void;
|
|
||||||
onOrdersToCaptureClick: () => void;
|
onOrdersToCaptureClick: () => void;
|
||||||
onOrdersToFulfillClick: () => void;
|
onOrdersToFulfillClick: () => void;
|
||||||
onProductClick: (productId: string, variantId: string) => void;
|
onProductClick: (productId: string, variantId: string) => void;
|
||||||
|
@ -65,8 +61,6 @@ export interface HomePageProps extends UserPermissionProps {
|
||||||
|
|
||||||
const HomePage: React.FC<HomePageProps> = props => {
|
const HomePage: React.FC<HomePageProps> = props => {
|
||||||
const {
|
const {
|
||||||
channelChoices,
|
|
||||||
channelValue,
|
|
||||||
userName,
|
userName,
|
||||||
orders,
|
orders,
|
||||||
sales,
|
sales,
|
||||||
|
@ -78,7 +72,6 @@ const HomePage: React.FC<HomePageProps> = props => {
|
||||||
onProductsOutOfStockClick,
|
onProductsOutOfStockClick,
|
||||||
ordersToCapture,
|
ordersToCapture,
|
||||||
ordersToFulfill,
|
ordersToFulfill,
|
||||||
onChannelChange,
|
|
||||||
productsOutOfStock,
|
productsOutOfStock,
|
||||||
userPermissions
|
userPermissions
|
||||||
} = props;
|
} = props;
|
||||||
|
@ -87,12 +80,7 @@ const HomePage: React.FC<HomePageProps> = props => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<HomeHeader
|
<HomeHeader userName={userName} />
|
||||||
userName={userName}
|
|
||||||
channelValue={channelValue}
|
|
||||||
channelChoices={channelChoices}
|
|
||||||
onChannelChange={onChannelChange}
|
|
||||||
/>
|
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
<Grid>
|
<Grid>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { useChannelsList } from "@saleor/channels/queries";
|
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
|
||||||
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
import useUser from "@saleor/hooks/useUser";
|
||||||
import React, { useCallback, useEffect } from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { getUserName, maybe } from "../../misc";
|
import { getUserName, maybe } from "../../misc";
|
||||||
import { orderListUrl } from "../../orders/urls";
|
import { orderListUrl } from "../../orders/urls";
|
||||||
|
@ -14,29 +13,10 @@ import { HomePageQuery } from "../queries";
|
||||||
const HomeSection = () => {
|
const HomeSection = () => {
|
||||||
const navigate = useNavigator();
|
const navigate = useNavigator();
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const { data: channelsData } = useChannelsList({});
|
const { channel } = useAppChannel();
|
||||||
|
|
||||||
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), []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HomePageQuery displayLoader variables={{ channel: channelChoice }}>
|
<HomePageQuery displayLoader variables={{ channel: channel.slug }}>
|
||||||
{({ data }) => (
|
{({ data }) => (
|
||||||
<HomePage
|
<HomePage
|
||||||
activities={maybe(() =>
|
activities={maybe(() =>
|
||||||
|
@ -47,9 +27,6 @@ const HomeSection = () => {
|
||||||
topProducts={maybe(() =>
|
topProducts={maybe(() =>
|
||||||
data.productTopToday.edges.map(edge => edge.node)
|
data.productTopToday.edges.map(edge => edge.node)
|
||||||
)}
|
)}
|
||||||
channelChoices={channelChoices}
|
|
||||||
channelValue={channelChoice}
|
|
||||||
onChannelChange={handleChannelChange}
|
|
||||||
onProductClick={(productId, variantId) =>
|
onProductClick={(productId, variantId) =>
|
||||||
navigate(productVariantEditUrl(productId, variantId))
|
navigate(productVariantEditUrl(productId, variantId))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
|
@ -7,14 +7,26 @@ export default function useLocalStorage<T>(
|
||||||
initialValue: T
|
initialValue: T
|
||||||
): [T, SetLocalStorage<T>] {
|
): [T, SetLocalStorage<T>] {
|
||||||
const [storedValue, setStoredValue] = useState<T>(() => {
|
const [storedValue, setStoredValue] = useState<T>(() => {
|
||||||
const item = window.localStorage.getItem(key);
|
let result: T;
|
||||||
return item ? JSON.parse(item) : initialValue;
|
try {
|
||||||
|
const item = window.localStorage.getItem(key);
|
||||||
|
result = item ? JSON.parse(item) : initialValue;
|
||||||
|
} catch {
|
||||||
|
result = initialValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
const setValue = (value: SetLocalStorageValue<T>) => {
|
const setValue = (value: SetLocalStorageValue<T>) => {
|
||||||
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
||||||
setStoredValue(valueToStore);
|
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];
|
return [storedValue, setValue];
|
||||||
|
|
|
@ -27,6 +27,9 @@ import ChannelsSection from "./channels";
|
||||||
import { channelsSection } from "./channels/urls";
|
import { channelsSection } from "./channels/urls";
|
||||||
import CollectionSection from "./collections";
|
import CollectionSection from "./collections";
|
||||||
import AppLayout from "./components/AppLayout";
|
import AppLayout from "./components/AppLayout";
|
||||||
|
import useAppChannel, {
|
||||||
|
AppChannelProvider
|
||||||
|
} from "./components/AppLayout/AppChannelContext";
|
||||||
import { DateProvider } from "./components/Date";
|
import { DateProvider } from "./components/Date";
|
||||||
import { LocaleProvider } from "./components/Locale";
|
import { LocaleProvider } from "./components/Locale";
|
||||||
import MessageManagerProvider from "./components/messages";
|
import MessageManagerProvider from "./components/messages";
|
||||||
|
@ -60,7 +63,7 @@ import { PermissionEnum } from "./types/globalTypes";
|
||||||
import WarehouseSection from "./warehouses";
|
import WarehouseSection from "./warehouses";
|
||||||
import { warehouseSection } from "./warehouses/urls";
|
import { warehouseSection } from "./warehouses/urls";
|
||||||
|
|
||||||
if (process.env.GTM_ID !== undefined) {
|
if (process.env.GTM_ID) {
|
||||||
TagManager.initialize({ gtmId: GTM_ID });
|
TagManager.initialize({ gtmId: GTM_ID });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +114,9 @@ const App: React.FC = () => {
|
||||||
<AppStateProvider>
|
<AppStateProvider>
|
||||||
<ShopProvider>
|
<ShopProvider>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<Routes />
|
<AppChannelProvider>
|
||||||
|
<Routes />
|
||||||
|
</AppChannelProvider>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</ShopProvider>
|
</ShopProvider>
|
||||||
</AppStateProvider>
|
</AppStateProvider>
|
||||||
|
@ -135,11 +140,15 @@ const Routes: React.FC = () => {
|
||||||
tokenVerifyLoading,
|
tokenVerifyLoading,
|
||||||
user
|
user
|
||||||
} = useAuth();
|
} = useAuth();
|
||||||
|
const { channel } = useAppChannel(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<WindowTitle title={intl.formatMessage(commonMessages.dashboard)} />
|
<WindowTitle title={intl.formatMessage(commonMessages.dashboard)} />
|
||||||
{isAuthenticated && !tokenAuthLoading && !tokenVerifyLoading ? (
|
{channel &&
|
||||||
|
isAuthenticated &&
|
||||||
|
!tokenAuthLoading &&
|
||||||
|
!tokenVerifyLoading ? (
|
||||||
<AppLayout>
|
<AppLayout>
|
||||||
<ErrorBoundary
|
<ErrorBoundary
|
||||||
onError={() =>
|
onError={() =>
|
||||||
|
@ -271,7 +280,7 @@ const Routes: React.FC = () => {
|
||||||
</Switch>
|
</Switch>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
) : hasToken && tokenVerifyLoading ? (
|
) : (isAuthenticated && !channel) || (hasToken && tokenVerifyLoading) ? (
|
||||||
<LoginLoading />
|
<LoginLoading />
|
||||||
) : (
|
) : (
|
||||||
<Auth />
|
<Auth />
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import Button from "@material-ui/core/Button";
|
import Button from "@material-ui/core/Button";
|
||||||
import Card from "@material-ui/core/Card";
|
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 Container from "@saleor/components/Container";
|
||||||
import FilterBar from "@saleor/components/FilterBar";
|
import FilterBar from "@saleor/components/FilterBar";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
|
@ -32,18 +30,8 @@ export interface OrderDraftListPageProps
|
||||||
SortPage<OrderDraftListUrlSortField>,
|
SortPage<OrderDraftListUrlSortField>,
|
||||||
TabPageProps {
|
TabPageProps {
|
||||||
orders: OrderDraftList_draftOrders_edges_node[];
|
orders: OrderDraftList_draftOrders_edges_node[];
|
||||||
onSettingsOpen?: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
|
||||||
theme => ({
|
|
||||||
settings: {
|
|
||||||
marginRight: theme.spacing(2)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
{ name: "OrderDraftListPage" }
|
|
||||||
);
|
|
||||||
|
|
||||||
const OrderDraftListPage: React.FC<OrderDraftListPageProps> = ({
|
const OrderDraftListPage: React.FC<OrderDraftListPageProps> = ({
|
||||||
currentTab,
|
currentTab,
|
||||||
disabled,
|
disabled,
|
||||||
|
@ -53,7 +41,6 @@ const OrderDraftListPage: React.FC<OrderDraftListPageProps> = ({
|
||||||
onAll,
|
onAll,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
onSettingsOpen,
|
|
||||||
onTabChange,
|
onTabChange,
|
||||||
onTabDelete,
|
onTabDelete,
|
||||||
onTabSave,
|
onTabSave,
|
||||||
|
@ -61,26 +48,11 @@ const OrderDraftListPage: React.FC<OrderDraftListPageProps> = ({
|
||||||
...listProps
|
...listProps
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const classes = useStyles({});
|
|
||||||
const structure = createFilterStructure(intl, filterOpts);
|
const structure = createFilterStructure(intl, filterOpts);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<PageHeader title={intl.formatMessage(sectionNames.draftOrders)}>
|
<PageHeader title={intl.formatMessage(sectionNames.draftOrders)}>
|
||||||
{!!onSettingsOpen && (
|
|
||||||
<CardMenu
|
|
||||||
className={classes.settings}
|
|
||||||
menuItems={[
|
|
||||||
{
|
|
||||||
label: intl.formatMessage({
|
|
||||||
defaultMessage: "Settings",
|
|
||||||
description: "button"
|
|
||||||
}),
|
|
||||||
onSelect: onSettingsOpen
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import Button from "@material-ui/core/Button";
|
import Button from "@material-ui/core/Button";
|
||||||
import Card from "@material-ui/core/Card";
|
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 Container from "@saleor/components/Container";
|
||||||
import FilterBar from "@saleor/components/FilterBar";
|
import FilterBar from "@saleor/components/FilterBar";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
|
@ -24,18 +22,8 @@ export interface OrderListPageProps
|
||||||
FilterPageProps<OrderFilterKeys, OrderListFilterOpts>,
|
FilterPageProps<OrderFilterKeys, OrderListFilterOpts>,
|
||||||
SortPage<OrderListUrlSortField> {
|
SortPage<OrderListUrlSortField> {
|
||||||
orders: OrderList_orders_edges_node[];
|
orders: OrderList_orders_edges_node[];
|
||||||
onSettingsOpen?: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
|
||||||
theme => ({
|
|
||||||
settings: {
|
|
||||||
marginRight: theme.spacing(2)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
{ name: "OrderListPage" }
|
|
||||||
);
|
|
||||||
|
|
||||||
const OrderListPage: React.FC<OrderListPageProps> = ({
|
const OrderListPage: React.FC<OrderListPageProps> = ({
|
||||||
currentTab,
|
currentTab,
|
||||||
initialSearch,
|
initialSearch,
|
||||||
|
@ -44,7 +32,6 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
|
||||||
onAdd,
|
onAdd,
|
||||||
onAll,
|
onAll,
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
onSettingsOpen,
|
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
onTabChange,
|
onTabChange,
|
||||||
onTabDelete,
|
onTabDelete,
|
||||||
|
@ -52,26 +39,11 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
|
||||||
...listProps
|
...listProps
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const classes = useStyles({});
|
|
||||||
const filterStructure = createFilterStructure(intl, filterOpts);
|
const filterStructure = createFilterStructure(intl, filterOpts);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<PageHeader title={intl.formatMessage(sectionNames.orders)}>
|
<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}>
|
<Button color="primary" variant="contained" onClick={onAdd}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Create order"
|
defaultMessage="Create order"
|
||||||
|
|
|
@ -24,7 +24,7 @@ import useModalDialogOpen from "@saleor/hooks/useModalDialogOpen";
|
||||||
import useSearchQuery from "@saleor/hooks/useSearchQuery";
|
import useSearchQuery from "@saleor/hooks/useSearchQuery";
|
||||||
import { buttonMessages } from "@saleor/intl";
|
import { buttonMessages } from "@saleor/intl";
|
||||||
import { maybe, renderCollection } from "@saleor/misc";
|
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 getOrderErrorMessage from "@saleor/utils/errors/order";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import InfiniteScroll from "react-infinite-scroller";
|
import InfiniteScroll from "react-infinite-scroller";
|
||||||
|
@ -87,12 +87,13 @@ type SetVariantsAction = (
|
||||||
data: SearchOrderVariant_search_edges_node_variants[]
|
data: SearchOrderVariant_search_edges_node_variants[]
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
export interface OrderProductAddDialogProps extends FetchMoreProps {
|
export interface OrderProductAddDialogProps
|
||||||
|
extends FetchMoreProps,
|
||||||
|
ChannelProps {
|
||||||
confirmButtonState: ConfirmButtonTransitionState;
|
confirmButtonState: ConfirmButtonTransitionState;
|
||||||
errors: OrderErrorFragment[];
|
errors: OrderErrorFragment[];
|
||||||
open: boolean;
|
open: boolean;
|
||||||
products: SearchOrderVariant_search_edges_node[];
|
products: SearchOrderVariant_search_edges_node[];
|
||||||
selectedChannel: string;
|
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onFetch: (query: string) => void;
|
onFetch: (query: string) => void;
|
||||||
onSubmit: (data: SearchOrderVariant_search_edges_node_variants[]) => void;
|
onSubmit: (data: SearchOrderVariant_search_edges_node_variants[]) => void;
|
||||||
|
@ -169,7 +170,7 @@ const OrderProductAddDialog: React.FC<OrderProductAddDialogProps> = props => {
|
||||||
loading,
|
loading,
|
||||||
hasMore,
|
hasMore,
|
||||||
products,
|
products,
|
||||||
selectedChannel,
|
selectedChannelId,
|
||||||
onFetch,
|
onFetch,
|
||||||
onFetchMore,
|
onFetchMore,
|
||||||
onClose,
|
onClose,
|
||||||
|
@ -257,7 +258,7 @@ const OrderProductAddDialog: React.FC<OrderProductAddDialogProps> = props => {
|
||||||
(product, productIndex) =>
|
(product, productIndex) =>
|
||||||
product.variants.some(variant =>
|
product.variants.some(variant =>
|
||||||
variant.channelListings.some(
|
variant.channelListings.some(
|
||||||
listing => listing.channel.id === selectedChannel
|
listing => listing.channel.id === selectedChannelId
|
||||||
)
|
)
|
||||||
) ? (
|
) ? (
|
||||||
<React.Fragment key={product ? product.id : "skeleton"}>
|
<React.Fragment key={product ? product.id : "skeleton"}>
|
||||||
|
@ -293,7 +294,7 @@ const OrderProductAddDialog: React.FC<OrderProductAddDialogProps> = props => {
|
||||||
{maybe(() => product.variants, []).map(
|
{maybe(() => product.variants, []).map(
|
||||||
(variant, variantIndex) =>
|
(variant, variantIndex) =>
|
||||||
variant.channelListings.some(
|
variant.channelListings.some(
|
||||||
listing => listing.channel.id === selectedChannel
|
listing => listing.channel.id === selectedChannelId
|
||||||
) ? (
|
) ? (
|
||||||
<TableRow key={variant.id}>
|
<TableRow key={variant.id}>
|
||||||
<TableCell />
|
<TableCell />
|
||||||
|
|
|
@ -31,7 +31,10 @@ import {
|
||||||
OrderDraftCancel,
|
OrderDraftCancel,
|
||||||
OrderDraftCancelVariables
|
OrderDraftCancelVariables
|
||||||
} from "./types/OrderDraftCancel";
|
} from "./types/OrderDraftCancel";
|
||||||
import { OrderDraftCreate } from "./types/OrderDraftCreate";
|
import {
|
||||||
|
OrderDraftCreate,
|
||||||
|
OrderDraftCreateVariables
|
||||||
|
} from "./types/OrderDraftCreate";
|
||||||
import {
|
import {
|
||||||
OrderDraftFinalize,
|
OrderDraftFinalize,
|
||||||
OrderDraftFinalizeVariables
|
OrderDraftFinalizeVariables
|
||||||
|
@ -371,9 +374,10 @@ const orderDraftCreateMutation = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const useOrderDraftCreateMutation = makeMutation<OrderDraftCreate, {}>(
|
export const useOrderDraftCreateMutation = makeMutation<
|
||||||
orderDraftCreateMutation
|
OrderDraftCreate,
|
||||||
);
|
OrderDraftCreateVariables
|
||||||
|
>(orderDraftCreateMutation);
|
||||||
|
|
||||||
const orderLineDeleteMutation = gql`
|
const orderLineDeleteMutation = gql`
|
||||||
${fragmentOrderDetails}
|
${fragmentOrderDetails}
|
||||||
|
|
|
@ -28,7 +28,7 @@ export enum OrderListUrlFiltersWithMultipleValuesEnum {
|
||||||
}
|
}
|
||||||
export type OrderListUrlFilters = Filters<OrderListUrlFiltersEnum> &
|
export type OrderListUrlFilters = Filters<OrderListUrlFiltersEnum> &
|
||||||
FiltersWithMultipleValues<OrderListUrlFiltersWithMultipleValuesEnum>;
|
FiltersWithMultipleValues<OrderListUrlFiltersWithMultipleValuesEnum>;
|
||||||
export type OrderListUrlDialog = "cancel" | "settings" | TabActionDialog;
|
export type OrderListUrlDialog = "cancel" | TabActionDialog;
|
||||||
export enum OrderListUrlSortField {
|
export enum OrderListUrlSortField {
|
||||||
number = "number",
|
number = "number",
|
||||||
customer = "customer",
|
customer = "customer",
|
||||||
|
@ -61,7 +61,7 @@ export enum OrderDraftListUrlFiltersEnum {
|
||||||
query = "query"
|
query = "query"
|
||||||
}
|
}
|
||||||
export type OrderDraftListUrlFilters = Filters<OrderDraftListUrlFiltersEnum>;
|
export type OrderDraftListUrlFilters = Filters<OrderDraftListUrlFiltersEnum>;
|
||||||
export type OrderDraftListUrlDialog = "remove" | "settings" | TabActionDialog;
|
export type OrderDraftListUrlDialog = "remove" | TabActionDialog;
|
||||||
export enum OrderDraftListUrlSortField {
|
export enum OrderDraftListUrlSortField {
|
||||||
number = "number",
|
number = "number",
|
||||||
customer = "customer",
|
customer = "customer",
|
||||||
|
|
|
@ -561,7 +561,7 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
|
||||||
products={variantSearchOpts.data?.search.edges.map(
|
products={variantSearchOpts.data?.search.edges.map(
|
||||||
edge => edge.node
|
edge => edge.node
|
||||||
)}
|
)}
|
||||||
selectedChannel={order?.channel?.id}
|
selectedChannelId={order?.channel?.id}
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
onFetch={variantSearch}
|
onFetch={variantSearch}
|
||||||
onFetchMore={loadMore}
|
onFetchMore={loadMore}
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import DeleteIcon from "@material-ui/icons/Delete";
|
import DeleteIcon from "@material-ui/icons/Delete";
|
||||||
import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog";
|
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
|
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
|
||||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||||
import SaveFilterTabDialog, {
|
import SaveFilterTabDialog, {
|
||||||
SaveFilterTabDialogFormData
|
SaveFilterTabDialogFormData
|
||||||
} from "@saleor/components/SaveFilterTabDialog";
|
} from "@saleor/components/SaveFilterTabDialog";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
import useChannelsSettings from "@saleor/hooks/useChannelsSettings";
|
|
||||||
import useListSettings from "@saleor/hooks/useListSettings";
|
import useListSettings from "@saleor/hooks/useListSettings";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
|
@ -80,6 +79,8 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = ({ params }) => {
|
||||||
onCompleted: handleCreateOrderCreateSuccess
|
onCompleted: handleCreateOrderCreateSuccess
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { channel } = useAppChannel();
|
||||||
|
|
||||||
const tabs = getFilterTabs();
|
const tabs = getFilterTabs();
|
||||||
|
|
||||||
const currentTab =
|
const currentTab =
|
||||||
|
@ -106,12 +107,6 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = ({ params }) => {
|
||||||
OrderDraftListUrlQueryParams
|
OrderDraftListUrlQueryParams
|
||||||
>(navigate, orderDraftListUrl, params);
|
>(navigate, orderDraftListUrl, params);
|
||||||
|
|
||||||
const {
|
|
||||||
channelChoices,
|
|
||||||
handleChannelSelectConfirm,
|
|
||||||
selectedChannel
|
|
||||||
} = useChannelsSettings("ordersDraftListChannel", { closeModal, openModal });
|
|
||||||
|
|
||||||
const handleTabChange = (tab: number) => {
|
const handleTabChange = (tab: number) => {
|
||||||
reset();
|
reset();
|
||||||
navigate(
|
navigate(
|
||||||
|
@ -171,16 +166,6 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = ({ params }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!!channelChoices?.length && (
|
|
||||||
<ChannelSettingsDialog
|
|
||||||
channelsChoices={channelChoices}
|
|
||||||
defaultChoice={selectedChannel}
|
|
||||||
open={params.action === "settings"}
|
|
||||||
confirmButtonState="default"
|
|
||||||
onClose={closeModal}
|
|
||||||
onConfirm={handleChannelSelectConfirm}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<TypedOrderDraftBulkCancelMutation
|
<TypedOrderDraftBulkCancelMutation
|
||||||
onCompleted={handleOrderDraftBulkCancel}
|
onCompleted={handleOrderDraftBulkCancel}
|
||||||
>
|
>
|
||||||
|
@ -214,7 +199,7 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = ({ params }) => {
|
||||||
onAdd={() =>
|
onAdd={() =>
|
||||||
createOrder({
|
createOrder({
|
||||||
variables: {
|
variables: {
|
||||||
input: { channel: selectedChannel }
|
input: { channel: channel.id }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -240,11 +225,6 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = ({ params }) => {
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
}
|
}
|
||||||
onSettingsOpen={
|
|
||||||
!!channelChoices?.length
|
|
||||||
? () => openModal("settings")
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<ActionDialog
|
<ActionDialog
|
||||||
confirmButtonState={orderDraftBulkDeleteOpts.status}
|
confirmButtonState={orderDraftBulkDeleteOpts.status}
|
||||||
|
|
|
@ -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 DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||||
import SaveFilterTabDialog, {
|
import SaveFilterTabDialog, {
|
||||||
SaveFilterTabDialogFormData
|
SaveFilterTabDialogFormData
|
||||||
} from "@saleor/components/SaveFilterTabDialog";
|
} from "@saleor/components/SaveFilterTabDialog";
|
||||||
import useChannelsSettings from "@saleor/hooks/useChannelsSettings";
|
|
||||||
import useListSettings from "@saleor/hooks/useListSettings";
|
import useListSettings from "@saleor/hooks/useListSettings";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
|
@ -68,6 +67,8 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
onCompleted: handleCreateOrderCreateSuccess
|
onCompleted: handleCreateOrderCreateSuccess
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { channel } = useAppChannel();
|
||||||
|
|
||||||
const tabs = getFilterTabs();
|
const tabs = getFilterTabs();
|
||||||
|
|
||||||
const currentTab =
|
const currentTab =
|
||||||
|
@ -93,12 +94,6 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
OrderListUrlQueryParams
|
OrderListUrlQueryParams
|
||||||
>(navigate, orderListUrl, params);
|
>(navigate, orderListUrl, params);
|
||||||
|
|
||||||
const {
|
|
||||||
channelChoices,
|
|
||||||
handleChannelSelectConfirm,
|
|
||||||
selectedChannel
|
|
||||||
} = useChannelsSettings("ordersListChannel", { closeModal, openModal });
|
|
||||||
|
|
||||||
const handleTabChange = (tab: number) =>
|
const handleTabChange = (tab: number) =>
|
||||||
navigate(
|
navigate(
|
||||||
orderListUrl({
|
orderListUrl({
|
||||||
|
@ -142,16 +137,6 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!!channelChoices?.length && (
|
|
||||||
<ChannelSettingsDialog
|
|
||||||
channelsChoices={channelChoices}
|
|
||||||
defaultChoice={selectedChannel}
|
|
||||||
open={params.action === "settings"}
|
|
||||||
confirmButtonState="default"
|
|
||||||
onClose={closeModal}
|
|
||||||
onConfirm={handleChannelSelectConfirm}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<OrderListPage
|
<OrderListPage
|
||||||
settings={settings}
|
settings={settings}
|
||||||
currentTab={currentTab}
|
currentTab={currentTab}
|
||||||
|
@ -163,7 +148,7 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
onAdd={() =>
|
onAdd={() =>
|
||||||
createOrder({
|
createOrder({
|
||||||
variables: {
|
variables: {
|
||||||
input: { channel: selectedChannel }
|
input: { channel: channel.id }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -180,9 +165,6 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
|
||||||
initialSearch={params.query || ""}
|
initialSearch={params.query || ""}
|
||||||
tabs={getFilterTabs().map(tab => tab.name)}
|
tabs={getFilterTabs().map(tab => tab.name)}
|
||||||
onAll={resetFilters}
|
onAll={resetFilters}
|
||||||
onSettingsOpen={
|
|
||||||
!!channelChoices?.length ? () => openModal("settings") : undefined
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<SaveFilterTabDialog
|
<SaveFilterTabDialog
|
||||||
open={params.action === "save-search"}
|
open={params.action === "save-search"}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import {
|
||||||
import { GridAttributes_grid_edges_node } from "@saleor/products/types/GridAttributes";
|
import { GridAttributes_grid_edges_node } from "@saleor/products/types/GridAttributes";
|
||||||
import { ProductList_products_edges_node } from "@saleor/products/types/ProductList";
|
import { ProductList_products_edges_node } from "@saleor/products/types/ProductList";
|
||||||
import { ProductListUrlSortField } from "@saleor/products/urls";
|
import { ProductListUrlSortField } from "@saleor/products/urls";
|
||||||
import { ListActions, ListProps, SortPage } from "@saleor/types";
|
import { ChannelProps, ListActions, ListProps, SortPage } from "@saleor/types";
|
||||||
import TDisplayColumn, {
|
import TDisplayColumn, {
|
||||||
DisplayColumnProps
|
DisplayColumnProps
|
||||||
} from "@saleor/utils/columns/DisplayColumn";
|
} from "@saleor/utils/columns/DisplayColumn";
|
||||||
|
@ -99,12 +99,12 @@ const DisplayColumn = TDisplayColumn as React.FunctionComponent<
|
||||||
interface ProductListProps
|
interface ProductListProps
|
||||||
extends ListProps<ProductListColumns>,
|
extends ListProps<ProductListColumns>,
|
||||||
ListActions,
|
ListActions,
|
||||||
SortPage<ProductListUrlSortField> {
|
SortPage<ProductListUrlSortField>,
|
||||||
|
ChannelProps {
|
||||||
activeAttributeSortId: string;
|
activeAttributeSortId: string;
|
||||||
gridAttributes: GridAttributes_grid_edges_node[];
|
gridAttributes: GridAttributes_grid_edges_node[];
|
||||||
products: ProductList_products_edges_node[];
|
products: ProductList_products_edges_node[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
selectedChannel: string;
|
|
||||||
channelsCount: number;
|
channelsCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ export const ProductList: React.FC<ProductListProps> = props => {
|
||||||
onUpdateListSettings,
|
onUpdateListSettings,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
onSort,
|
onSort,
|
||||||
selectedChannel
|
selectedChannelId
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
|
@ -286,7 +286,7 @@ export const ProductList: React.FC<ProductListProps> = props => {
|
||||||
product => {
|
product => {
|
||||||
const isSelected = product ? isChecked(product.id) : false;
|
const isSelected = product ? isChecked(product.id) : false;
|
||||||
const channel = product?.channelListings.find(
|
const channel = product?.channelListings.find(
|
||||||
listing => listing.channel.id === selectedChannel
|
listing => listing.channel.id === selectedChannelId
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -362,9 +362,7 @@ export const ProductList: React.FC<ProductListProps> = props => {
|
||||||
) : product?.channelListings !== undefined ? (
|
) : product?.channelListings !== undefined ? (
|
||||||
<ChannelsAvailabilityDropdown
|
<ChannelsAvailabilityDropdown
|
||||||
allChannelsCount={channelsCount}
|
allChannelsCount={channelsCount}
|
||||||
currentChannel={
|
currentChannel={channel}
|
||||||
channel || product?.channelListings[0]
|
|
||||||
}
|
|
||||||
channels={product?.channelListings}
|
channels={product?.channelListings}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
} from "@saleor/products/types/GridAttributes";
|
} from "@saleor/products/types/GridAttributes";
|
||||||
import { ProductList_products_edges_node } from "@saleor/products/types/ProductList";
|
import { ProductList_products_edges_node } from "@saleor/products/types/ProductList";
|
||||||
import {
|
import {
|
||||||
|
ChannelProps,
|
||||||
FetchMoreProps,
|
FetchMoreProps,
|
||||||
FilterPageProps,
|
FilterPageProps,
|
||||||
ListActions,
|
ListActions,
|
||||||
|
@ -38,7 +39,8 @@ export interface ProductListPageProps
|
||||||
ListActions,
|
ListActions,
|
||||||
FilterPageProps<ProductFilterKeys, ProductListFilterOpts>,
|
FilterPageProps<ProductFilterKeys, ProductListFilterOpts>,
|
||||||
FetchMoreProps,
|
FetchMoreProps,
|
||||||
SortPage<ProductListUrlSortField> {
|
SortPage<ProductListUrlSortField>,
|
||||||
|
ChannelProps {
|
||||||
activeAttributeSortId: string;
|
activeAttributeSortId: string;
|
||||||
availableInGridAttributes: GridAttributes_availableInGrid_edges_node[];
|
availableInGridAttributes: GridAttributes_availableInGrid_edges_node[];
|
||||||
channelsCount: number;
|
channelsCount: number;
|
||||||
|
@ -47,8 +49,6 @@ export interface ProductListPageProps
|
||||||
totalGridAttributes: number;
|
totalGridAttributes: number;
|
||||||
products: ProductList_products_edges_node[];
|
products: ProductList_products_edges_node[];
|
||||||
onExport: () => void;
|
onExport: () => void;
|
||||||
selectedChannel: string;
|
|
||||||
onSettingsOpen?: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
|
@ -84,12 +84,11 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
|
||||||
onFetchMore,
|
onFetchMore,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
onSettingsOpen,
|
|
||||||
onTabChange,
|
onTabChange,
|
||||||
onTabDelete,
|
onTabDelete,
|
||||||
onTabSave,
|
onTabSave,
|
||||||
onUpdateListSettings,
|
onUpdateListSettings,
|
||||||
selectedChannel,
|
selectedChannelId,
|
||||||
...listProps
|
...listProps
|
||||||
} = props;
|
} = props;
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
@ -134,13 +133,6 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
|
||||||
}),
|
}),
|
||||||
onSelect: onExport,
|
onSelect: onExport,
|
||||||
testId: "export"
|
testId: "export"
|
||||||
},
|
|
||||||
onSettingsOpen && {
|
|
||||||
label: intl.formatMessage({
|
|
||||||
defaultMessage: "Settings",
|
|
||||||
description: "button"
|
|
||||||
}),
|
|
||||||
onSelect: onSettingsOpen
|
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
data-test="menu"
|
data-test="menu"
|
||||||
|
@ -199,7 +191,7 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
|
||||||
gridAttributes={gridAttributes}
|
gridAttributes={gridAttributes}
|
||||||
settings={settings}
|
settings={settings}
|
||||||
channelsCount={channelsCount}
|
channelsCount={channelsCount}
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={selectedChannelId}
|
||||||
onUpdateListSettings={onUpdateListSettings}
|
onUpdateListSettings={onUpdateListSettings}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
@ -58,6 +58,7 @@ const props: ProductUpdatePageProps = {
|
||||||
placeholderImage,
|
placeholderImage,
|
||||||
product,
|
product,
|
||||||
saveButtonBarState: "default",
|
saveButtonBarState: "default",
|
||||||
|
selectedChannelId: "123",
|
||||||
taxTypes,
|
taxTypes,
|
||||||
variants: product.variants,
|
variants: product.variants,
|
||||||
warehouses: warehouseList
|
warehouses: warehouseList
|
||||||
|
|
|
@ -22,7 +22,12 @@ import { maybe } from "@saleor/misc";
|
||||||
import ProductVariantPrice from "@saleor/products/components/ProductVariantPrice";
|
import ProductVariantPrice from "@saleor/products/components/ProductVariantPrice";
|
||||||
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
|
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
|
||||||
import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections";
|
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 React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
|
@ -42,7 +47,7 @@ import ProductTaxes from "../ProductTaxes";
|
||||||
import ProductVariants from "../ProductVariants";
|
import ProductVariants from "../ProductVariants";
|
||||||
import ProductUpdateForm from "./form";
|
import ProductUpdateForm from "./form";
|
||||||
|
|
||||||
export interface ProductUpdatePageProps extends ListActions {
|
export interface ProductUpdatePageProps extends ListActions, ChannelProps {
|
||||||
defaultWeightUnit: string;
|
defaultWeightUnit: string;
|
||||||
errors: ProductErrorWithAttributesFragment[];
|
errors: ProductErrorWithAttributesFragment[];
|
||||||
channelsErrors: ProductChannelListingErrorFragment[];
|
channelsErrors: ProductChannelListingErrorFragment[];
|
||||||
|
@ -83,7 +88,9 @@ export interface ProductUpdatePageProps extends ListActions {
|
||||||
onWarehouseConfigure();
|
onWarehouseConfigure();
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData {
|
export interface ProductUpdatePageSubmitData
|
||||||
|
extends ProductUpdatePageFormData,
|
||||||
|
ChannelProps {
|
||||||
addStocks: ProductStockInput[];
|
addStocks: ProductStockInput[];
|
||||||
attributes: ProductAttributeInput[];
|
attributes: ProductAttributeInput[];
|
||||||
collections: string[];
|
collections: string[];
|
||||||
|
@ -96,7 +103,6 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
defaultWeightUnit,
|
defaultWeightUnit,
|
||||||
disabled,
|
disabled,
|
||||||
categories: categoryChoiceList,
|
categories: categoryChoiceList,
|
||||||
channelChoices,
|
|
||||||
channelsErrors,
|
channelsErrors,
|
||||||
allChannelsCount,
|
allChannelsCount,
|
||||||
currentChannels = [],
|
currentChannels = [],
|
||||||
|
@ -133,6 +139,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
onWarehouseConfigure,
|
onWarehouseConfigure,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
|
selectedChannelId,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar
|
||||||
|
@ -235,7 +242,6 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
variants={variants}
|
variants={variants}
|
||||||
product={product}
|
product={product}
|
||||||
channelChoices={channelChoices}
|
|
||||||
onRowClick={onVariantShow}
|
onRowClick={onVariantShow}
|
||||||
onVariantAdd={onVariantAdd}
|
onVariantAdd={onVariantAdd}
|
||||||
onVariantsAdd={onVariantsAdd}
|
onVariantsAdd={onVariantsAdd}
|
||||||
|
@ -244,6 +250,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
isChecked={isChecked}
|
isChecked={isChecked}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
|
selectedChannelId={selectedChannelId}
|
||||||
toggle={toggle}
|
toggle={toggle}
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -7,7 +7,6 @@ import { fade } from "@material-ui/core/styles/colorManipulator";
|
||||||
import TableCell from "@material-ui/core/TableCell";
|
import TableCell from "@material-ui/core/TableCell";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
import { ChannelsSelect } from "@saleor/components/ChannelsSelect";
|
|
||||||
import Checkbox from "@saleor/components/Checkbox";
|
import Checkbox from "@saleor/components/Checkbox";
|
||||||
import LinkChoice from "@saleor/components/LinkChoice";
|
import LinkChoice from "@saleor/components/LinkChoice";
|
||||||
import Money from "@saleor/components/Money";
|
import Money from "@saleor/components/Money";
|
||||||
|
@ -19,12 +18,11 @@ import {
|
||||||
SortableTableRow
|
SortableTableRow
|
||||||
} from "@saleor/components/SortableTable";
|
} from "@saleor/components/SortableTable";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, IntlShape, useIntl } from "react-intl";
|
import { FormattedMessage, IntlShape, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { maybe, renderCollection } from "../../../misc";
|
import { maybe, renderCollection } from "../../../misc";
|
||||||
import { ListActions, ReorderAction } from "../../../types";
|
import { ChannelProps, ListActions, ReorderAction } from "../../../types";
|
||||||
import {
|
import {
|
||||||
ProductDetails_product,
|
ProductDetails_product,
|
||||||
ProductDetails_product_variants,
|
ProductDetails_product_variants,
|
||||||
|
@ -84,9 +82,6 @@ const useStyles = makeStyles(
|
||||||
width: 200
|
width: 200
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
channelSelect: {
|
|
||||||
marginRight: theme.spacing(1)
|
|
||||||
},
|
|
||||||
colGrab: {
|
colGrab: {
|
||||||
width: 60
|
width: 60
|
||||||
},
|
},
|
||||||
|
@ -183,12 +178,11 @@ function getAvailabilityLabel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProductVariantsProps extends ListActions {
|
interface ProductVariantsProps extends ListActions, ChannelProps {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
product: ProductDetails_product;
|
product: ProductDetails_product;
|
||||||
variants: ProductDetails_product_variants[];
|
variants: ProductDetails_product_variants[];
|
||||||
onVariantReorder: ReorderAction;
|
onVariantReorder: ReorderAction;
|
||||||
channelChoices: SingleAutocompleteChoiceType[];
|
|
||||||
onRowClick: (id: string) => () => void;
|
onRowClick: (id: string) => () => void;
|
||||||
onSetDefaultVariant(variant: ProductDetails_product_variants);
|
onSetDefaultVariant(variant: ProductDetails_product_variants);
|
||||||
onVariantAdd?();
|
onVariantAdd?();
|
||||||
|
@ -199,7 +193,6 @@ const numberOfColumns = 7;
|
||||||
|
|
||||||
export const ProductVariants: React.FC<ProductVariantsProps> = props => {
|
export const ProductVariants: React.FC<ProductVariantsProps> = props => {
|
||||||
const {
|
const {
|
||||||
channelChoices,
|
|
||||||
disabled,
|
disabled,
|
||||||
variants,
|
variants,
|
||||||
product,
|
product,
|
||||||
|
@ -210,6 +203,7 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
|
||||||
onSetDefaultVariant,
|
onSetDefaultVariant,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
|
selectedChannelId,
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar
|
||||||
|
@ -218,9 +212,6 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [warehouse, setWarehouse] = React.useState<string>(null);
|
const [warehouse, setWarehouse] = React.useState<string>(null);
|
||||||
const [channelChoice, setChannelChoice] = useStateFromProps(
|
|
||||||
channelChoices[0]?.value
|
|
||||||
);
|
|
||||||
const hasVariants = maybe(() => variants.length > 0, true);
|
const hasVariants = maybe(() => variants.length > 0, true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -261,11 +252,6 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
|
||||||
|
|
||||||
{variants.length > 0 ? (
|
{variants.length > 0 ? (
|
||||||
<CardContent className={classes.warehouseSelectContainer}>
|
<CardContent className={classes.warehouseSelectContainer}>
|
||||||
<ChannelsSelect
|
|
||||||
channelChoice={channelChoice}
|
|
||||||
channelChoices={channelChoices}
|
|
||||||
setChannelChoice={setChannelChoice}
|
|
||||||
/>
|
|
||||||
<Typography className={classes.warehouseLabel}>
|
<Typography className={classes.warehouseLabel}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Available inventory at:"
|
defaultMessage="Available inventory at:"
|
||||||
|
@ -345,7 +331,7 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
const channel = variant.channelListings.find(
|
const channel = variant.channelListings.find(
|
||||||
listing => listing.channel.id === channelChoice
|
listing => listing.channel.id === selectedChannelId
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -19,11 +19,7 @@ export const productAddPath = urlJoin(productSection, "add");
|
||||||
export const productAddUrl = productAddPath;
|
export const productAddUrl = productAddPath;
|
||||||
|
|
||||||
export const productListPath = productSection;
|
export const productListPath = productSection;
|
||||||
export type ProductListUrlDialog =
|
export type ProductListUrlDialog = "delete" | "export" | TabActionDialog;
|
||||||
| "settings"
|
|
||||||
| "delete"
|
|
||||||
| "export"
|
|
||||||
| TabActionDialog;
|
|
||||||
export enum ProductListUrlFiltersEnum {
|
export enum ProductListUrlFiltersEnum {
|
||||||
priceFrom = "priceFrom",
|
priceFrom = "priceFrom",
|
||||||
priceTo = "priceTo",
|
priceTo = "priceTo",
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import DeleteIcon from "@material-ui/icons/Delete";
|
import DeleteIcon from "@material-ui/icons/Delete";
|
||||||
import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog";
|
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
|
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
|
||||||
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||||
import SaveFilterTabDialog, {
|
import SaveFilterTabDialog, {
|
||||||
SaveFilterTabDialogFormData
|
SaveFilterTabDialogFormData
|
||||||
|
@ -16,7 +16,6 @@ import {
|
||||||
import { Task } from "@saleor/containers/BackgroundTasks/types";
|
import { Task } from "@saleor/containers/BackgroundTasks/types";
|
||||||
import useBackgroundTask from "@saleor/hooks/useBackgroundTask";
|
import useBackgroundTask from "@saleor/hooks/useBackgroundTask";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
import useChannelsSettings from "@saleor/hooks/useChannelsSettings";
|
|
||||||
import useListSettings from "@saleor/hooks/useListSettings";
|
import useListSettings from "@saleor/hooks/useListSettings";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
|
@ -126,33 +125,25 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
|
||||||
first: 100
|
first: 100
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const { availableChannels, channel } = useAppChannel();
|
||||||
|
|
||||||
const [openModal, closeModal] = createDialogActionHandlers<
|
const [openModal, closeModal] = createDialogActionHandlers<
|
||||||
ProductListUrlDialog,
|
ProductListUrlDialog,
|
||||||
ProductListUrlQueryParams
|
ProductListUrlQueryParams
|
||||||
>(navigate, productListUrl, params);
|
>(navigate, productListUrl, params);
|
||||||
|
|
||||||
const {
|
// Reset pagination
|
||||||
channel,
|
React.useEffect(
|
||||||
channels,
|
() =>
|
||||||
channelChoices,
|
navigate(
|
||||||
handleChannelSelectConfirm,
|
productListUrl({
|
||||||
selectedChannel,
|
...params,
|
||||||
slug
|
...DEFAULT_INITIAL_PAGINATION_DATA
|
||||||
} = useChannelsSettings("productsListChannel", { closeModal, openModal });
|
}),
|
||||||
|
true
|
||||||
React.useEffect(() => {
|
),
|
||||||
const action = selectedChannel
|
[settings.rowNumber]
|
||||||
? {}
|
);
|
||||||
: { action: "settings" as ProductListUrlDialog };
|
|
||||||
navigate(
|
|
||||||
productListUrl({
|
|
||||||
...{ ...params, ...action },
|
|
||||||
...DEFAULT_INITIAL_PAGINATION_DATA
|
|
||||||
}),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}, [settings.rowNumber]);
|
|
||||||
|
|
||||||
const tabs = getFilterTabs();
|
const tabs = getFilterTabs();
|
||||||
|
|
||||||
|
@ -231,13 +222,13 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const paginationState = createPaginationState(settings.rowNumber, params);
|
const paginationState = createPaginationState(settings.rowNumber, params);
|
||||||
const filter = getFilterVariables(params, slug);
|
const filter = getFilterVariables(params, channel.slug);
|
||||||
const sort = getSortQueryVariables(params, slug);
|
const sort = getSortQueryVariables(params, channel.slug);
|
||||||
const queryVariables = React.useMemo<ProductListVariables>(
|
const queryVariables = React.useMemo<ProductListVariables>(
|
||||||
() => ({
|
() => ({
|
||||||
...paginationState,
|
...paginationState,
|
||||||
filter,
|
filter,
|
||||||
...(slug ? { sort } : {})
|
sort
|
||||||
}),
|
}),
|
||||||
[params, settings.rowNumber]
|
[params, settings.rowNumber]
|
||||||
);
|
);
|
||||||
|
@ -388,22 +379,9 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
|
||||||
initialSearch={params.query || ""}
|
initialSearch={params.query || ""}
|
||||||
tabs={getFilterTabs().map(tab => tab.name)}
|
tabs={getFilterTabs().map(tab => tab.name)}
|
||||||
onExport={() => openModal("export")}
|
onExport={() => openModal("export")}
|
||||||
channelsCount={channelChoices?.length}
|
channelsCount={availableChannels?.length}
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={channel.id}
|
||||||
onSettingsOpen={
|
|
||||||
!!channelChoices?.length ? () => openModal("settings") : undefined
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
{!!channelChoices?.length && (
|
|
||||||
<ChannelSettingsDialog
|
|
||||||
channelsChoices={channelChoices}
|
|
||||||
defaultChoice={selectedChannel}
|
|
||||||
open={params.action === "settings"}
|
|
||||||
confirmButtonState="default"
|
|
||||||
onClose={closeModal}
|
|
||||||
onConfirm={handleChannelSelectConfirm}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<ActionDialog
|
<ActionDialog
|
||||||
open={params.action === "delete"}
|
open={params.action === "delete"}
|
||||||
confirmButtonState={productBulkDeleteOpts.status}
|
confirmButtonState={productBulkDeleteOpts.status}
|
||||||
|
@ -449,7 +427,7 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
|
||||||
warehouses={
|
warehouses={
|
||||||
warehouses.data?.warehouses.edges.map(edge => edge.node) || []
|
warehouses.data?.warehouses.edges.map(edge => edge.node) || []
|
||||||
}
|
}
|
||||||
channels={channels}
|
channels={availableChannels}
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
onSubmit={data =>
|
onSubmit={data =>
|
||||||
exportProducts({
|
exportProducts({
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
createSortedChannelsDataFromProduct
|
createSortedChannelsDataFromProduct
|
||||||
} from "@saleor/channels/utils";
|
} from "@saleor/channels/utils";
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
|
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
|
||||||
import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog";
|
import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog";
|
||||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
|
@ -114,6 +115,7 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
|
||||||
displayLoader: true,
|
displayLoader: true,
|
||||||
variables: { id }
|
variables: { id }
|
||||||
});
|
});
|
||||||
|
const { channel } = useAppChannel();
|
||||||
|
|
||||||
const handleUpdate = (data: ProductUpdateMutationResult) => {
|
const handleUpdate = (data: ProductUpdateMutationResult) => {
|
||||||
if (data.productUpdate.errors.length === 0) {
|
if (data.productUpdate.errors.length === 0) {
|
||||||
|
@ -404,6 +406,7 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
|
||||||
loading: searchCollectionsOpts.loading,
|
loading: searchCollectionsOpts.loading,
|
||||||
onFetchMore: loadMoreCollections
|
onFetchMore: loadMoreCollections
|
||||||
}}
|
}}
|
||||||
|
selectedChannelId={channel.id}
|
||||||
openChannelsModal={handleChannelsModalOpen}
|
openChannelsModal={handleChannelsModalOpen}
|
||||||
onChannelsChange={setCurrentChannels}
|
onChannelsChange={setCurrentChannels}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -21,7 +21,7 @@ import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { getStringOrPlaceholder } from "../../../misc";
|
import { getStringOrPlaceholder } from "../../../misc";
|
||||||
import { FetchMoreProps, SearchProps } from "../../../types";
|
import { ChannelProps, FetchMoreProps, SearchProps } from "../../../types";
|
||||||
import { ShippingMethodTypeEnum } from "../../../types/globalTypes";
|
import { ShippingMethodTypeEnum } from "../../../types/globalTypes";
|
||||||
import ShippingZoneInfo from "../ShippingZoneInfo";
|
import ShippingZoneInfo from "../ShippingZoneInfo";
|
||||||
import ShippingZoneRates from "../ShippingZoneRates";
|
import ShippingZoneRates from "../ShippingZoneRates";
|
||||||
|
@ -34,11 +34,11 @@ export interface FormData {
|
||||||
|
|
||||||
export interface ShippingZoneDetailsPageProps
|
export interface ShippingZoneDetailsPageProps
|
||||||
extends FetchMoreProps,
|
extends FetchMoreProps,
|
||||||
SearchProps {
|
SearchProps,
|
||||||
|
ChannelProps {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
errors: ShippingErrorFragment[];
|
errors: ShippingErrorFragment[];
|
||||||
saveButtonBarState: ConfirmButtonTransitionState;
|
saveButtonBarState: ConfirmButtonTransitionState;
|
||||||
selectedChannel: string;
|
|
||||||
shippingZone: ShippingZoneDetailsFragment;
|
shippingZone: ShippingZoneDetailsFragment;
|
||||||
warehouses: ShippingZoneDetailsFragment_warehouses[];
|
warehouses: ShippingZoneDetailsFragment_warehouses[];
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
|
@ -82,7 +82,7 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
|
||||||
onWeightRateAdd,
|
onWeightRateAdd,
|
||||||
onWeightRateEdit,
|
onWeightRateEdit,
|
||||||
saveButtonBarState,
|
saveButtonBarState,
|
||||||
selectedChannel,
|
selectedChannelId,
|
||||||
shippingZone,
|
shippingZone,
|
||||||
warehouses
|
warehouses
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -160,7 +160,7 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
|
||||||
method => method.type === ShippingMethodTypeEnum.PRICE
|
method => method.type === ShippingMethodTypeEnum.PRICE
|
||||||
)}
|
)}
|
||||||
variant="price"
|
variant="price"
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={selectedChannelId}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
<ShippingZoneRates
|
<ShippingZoneRates
|
||||||
|
@ -172,7 +172,7 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
|
||||||
method => method.type === ShippingMethodTypeEnum.WEIGHT
|
method => method.type === ShippingMethodTypeEnum.WEIGHT
|
||||||
)}
|
)}
|
||||||
variant="weight"
|
variant="weight"
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={selectedChannelId}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -15,16 +15,16 @@ import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import WeightRange from "@saleor/components/WeightRange";
|
import WeightRange from "@saleor/components/WeightRange";
|
||||||
import { ShippingZoneDetailsFragment_shippingMethods } from "@saleor/fragments/types/ShippingZoneDetailsFragment";
|
import { ShippingZoneDetailsFragment_shippingMethods } from "@saleor/fragments/types/ShippingZoneDetailsFragment";
|
||||||
|
import { ChannelProps } from "@saleor/types";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { maybe, renderCollection } from "../../../misc";
|
import { maybe, renderCollection } from "../../../misc";
|
||||||
import { ICONBUTTON_SIZE } from "../../../theme";
|
import { ICONBUTTON_SIZE } from "../../../theme";
|
||||||
|
|
||||||
export interface ShippingZoneRatesProps {
|
export interface ShippingZoneRatesProps extends ChannelProps {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
rates: ShippingZoneDetailsFragment_shippingMethods[];
|
rates: ShippingZoneDetailsFragment_shippingMethods[];
|
||||||
selectedChannel: string;
|
|
||||||
variant: "price" | "weight";
|
variant: "price" | "weight";
|
||||||
onRateAdd: () => void;
|
onRateAdd: () => void;
|
||||||
onRateEdit: (id: string) => void;
|
onRateEdit: (id: string) => void;
|
||||||
|
@ -56,7 +56,7 @@ const ShippingZoneRates: React.FC<ShippingZoneRatesProps> = props => {
|
||||||
onRateEdit,
|
onRateEdit,
|
||||||
onRateRemove,
|
onRateRemove,
|
||||||
rates,
|
rates,
|
||||||
selectedChannel,
|
selectedChannelId,
|
||||||
variant
|
variant
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ const ShippingZoneRates: React.FC<ShippingZoneRatesProps> = props => {
|
||||||
rates,
|
rates,
|
||||||
rate => {
|
rate => {
|
||||||
const channel = rate?.channelListings?.find(
|
const channel = rate?.channelListings?.find(
|
||||||
listing => listing.channel.id === selectedChannel
|
listing => listing.channel.id === selectedChannelId
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import AppHeader from "@saleor/components/AppHeader";
|
import AppHeader from "@saleor/components/AppHeader";
|
||||||
import CardMenu from "@saleor/components/CardMenu";
|
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import Grid from "@saleor/components/Grid";
|
import Grid from "@saleor/components/Grid";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
|
@ -23,8 +22,6 @@ export interface ShippingZonesListPageProps
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
onRemove: (id: string) => void;
|
onRemove: (id: string) => void;
|
||||||
onSubmit: (unit: WeightUnitsEnum) => void;
|
onSubmit: (unit: WeightUnitsEnum) => void;
|
||||||
selectedChannel: string;
|
|
||||||
onSettingsOpen: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ShippingZonesListPage: React.FC<ShippingZonesListPageProps> = ({
|
const ShippingZonesListPage: React.FC<ShippingZonesListPageProps> = ({
|
||||||
|
@ -32,7 +29,6 @@ const ShippingZonesListPage: React.FC<ShippingZonesListPageProps> = ({
|
||||||
disabled,
|
disabled,
|
||||||
userPermissions,
|
userPermissions,
|
||||||
onBack,
|
onBack,
|
||||||
onSettingsOpen,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
...listProps
|
...listProps
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -48,19 +44,7 @@ const ShippingZonesListPage: React.FC<ShippingZonesListPageProps> = ({
|
||||||
defaultMessage: "Shipping",
|
defaultMessage: "Shipping",
|
||||||
description: "header"
|
description: "header"
|
||||||
})}
|
})}
|
||||||
>
|
/>
|
||||||
<CardMenu
|
|
||||||
menuItems={[
|
|
||||||
{
|
|
||||||
label: intl.formatMessage({
|
|
||||||
defaultMessage: "Settings",
|
|
||||||
description: "button"
|
|
||||||
}),
|
|
||||||
onSelect: onSettingsOpen
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</PageHeader>
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<div>
|
<div>
|
||||||
<ShippingZonesList disabled={disabled} {...listProps} />
|
<ShippingZonesList disabled={disabled} {...listProps} />
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { ShippingMethodTypeEnum } from "../types/globalTypes";
|
||||||
export const shippingSection = "/shipping/";
|
export const shippingSection = "/shipping/";
|
||||||
|
|
||||||
export const shippingZonesListPath = shippingSection;
|
export const shippingZonesListPath = shippingSection;
|
||||||
export type ShippingZonesListUrlDialog = "remove" | "remove-many" | "settings";
|
export type ShippingZonesListUrlDialog = "remove" | "remove-many";
|
||||||
export type ShippingZonesListUrlQueryParams = BulkAction &
|
export type ShippingZonesListUrlQueryParams = BulkAction &
|
||||||
Dialog<ShippingZonesListUrlDialog> &
|
Dialog<ShippingZonesListUrlDialog> &
|
||||||
Pagination &
|
Pagination &
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
|
import useAppChannel from "@saleor/components/AppLayout/AppChannelContext";
|
||||||
import NotFoundPage from "@saleor/components/NotFoundPage";
|
import NotFoundPage from "@saleor/components/NotFoundPage";
|
||||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
||||||
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
import useShop from "@saleor/hooks/useShop";
|
import useShop from "@saleor/hooks/useShop";
|
||||||
|
@ -63,8 +63,7 @@ const ShippingZoneDetails: React.FC<ShippingZoneDetailsProps> = ({
|
||||||
displayLoader: true,
|
displayLoader: true,
|
||||||
variables: { id }
|
variables: { id }
|
||||||
});
|
});
|
||||||
|
const { channel } = useAppChannel();
|
||||||
const [selectedChannel] = useLocalStorage("shippingListChannel", "");
|
|
||||||
|
|
||||||
const [openModal, closeModal] = createDialogActionHandlers<
|
const [openModal, closeModal] = createDialogActionHandlers<
|
||||||
ShippingZoneUrlDialog,
|
ShippingZoneUrlDialog,
|
||||||
|
@ -183,7 +182,7 @@ const ShippingZoneDetails: React.FC<ShippingZoneDetailsProps> = ({
|
||||||
loading={searchWarehousesOpts.loading}
|
loading={searchWarehousesOpts.loading}
|
||||||
onFetchMore={loadMore}
|
onFetchMore={loadMore}
|
||||||
onSearchChange={search}
|
onSearchChange={search}
|
||||||
selectedChannel={selectedChannel}
|
selectedChannelId={channel.id}
|
||||||
/>
|
/>
|
||||||
<DeleteShippingRateDialog
|
<DeleteShippingRateDialog
|
||||||
confirmButtonState={deleteShippingRateOpts.status}
|
confirmButtonState={deleteShippingRateOpts.status}
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import DeleteIcon from "@material-ui/icons/Delete";
|
import DeleteIcon from "@material-ui/icons/Delete";
|
||||||
import ChannelSettingsDialog from "@saleor/channels/components/ChannelSettingsDialog";
|
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
import { configurationMenuUrl } from "@saleor/configuration";
|
import { configurationMenuUrl } from "@saleor/configuration";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
import useChannelsSettings from "@saleor/hooks/useChannelsSettings";
|
|
||||||
import useListSettings from "@saleor/hooks/useListSettings";
|
import useListSettings from "@saleor/hooks/useListSettings";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
|
@ -63,12 +61,6 @@ export const ShippingZonesList: React.FC<ShippingZonesListProps> = ({
|
||||||
ShippingZonesListUrlQueryParams
|
ShippingZonesListUrlQueryParams
|
||||||
>(navigate, shippingZonesListUrl, params);
|
>(navigate, shippingZonesListUrl, params);
|
||||||
|
|
||||||
const {
|
|
||||||
channelChoices,
|
|
||||||
handleChannelSelectConfirm,
|
|
||||||
selectedChannel
|
|
||||||
} = useChannelsSettings("shippingListChannel", { closeModal, openModal });
|
|
||||||
|
|
||||||
const { data, loading, refetch } = useShippingZoneList({
|
const { data, loading, refetch } = useShippingZoneList({
|
||||||
displayLoader: true,
|
displayLoader: true,
|
||||||
variables: paginationState
|
variables: paginationState
|
||||||
|
@ -125,14 +117,6 @@ export const ShippingZonesList: React.FC<ShippingZonesListProps> = ({
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ChannelSettingsDialog
|
|
||||||
channelsChoices={channelChoices}
|
|
||||||
defaultChoice={selectedChannel}
|
|
||||||
open={params.action === "settings"}
|
|
||||||
confirmButtonState="default"
|
|
||||||
onClose={closeModal}
|
|
||||||
onConfirm={handleChannelSelectConfirm}
|
|
||||||
/>
|
|
||||||
<ShippingZonesListPage
|
<ShippingZonesListPage
|
||||||
defaultWeightUnit={shop?.defaultWeightUnit}
|
defaultWeightUnit={shop?.defaultWeightUnit}
|
||||||
settings={settings}
|
settings={settings}
|
||||||
|
@ -178,8 +162,6 @@ export const ShippingZonesList: React.FC<ShippingZonesListProps> = ({
|
||||||
</IconButton>
|
</IconButton>
|
||||||
}
|
}
|
||||||
userPermissions={user?.userPermissions || []}
|
userPermissions={user?.userPermissions || []}
|
||||||
selectedChannel={selectedChannel}
|
|
||||||
onSettingsOpen={() => openModal("settings")}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ActionDialog
|
<ActionDialog
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -47,6 +47,7 @@ const updateProps: Omit<CategoryUpdatePageProps, "classes"> = {
|
||||||
productListToolbar: null,
|
productListToolbar: null,
|
||||||
products: category.products.edges.map(edge => edge.node),
|
products: category.products.edges.map(edge => edge.node),
|
||||||
saveButtonBarState: "default",
|
saveButtonBarState: "default",
|
||||||
|
selectedChannelId: "123",
|
||||||
subcategories: category.children.edges.map(edge => edge.node),
|
subcategories: category.children.edges.map(edge => edge.node),
|
||||||
subcategoryListToolbar: null,
|
subcategoryListToolbar: null,
|
||||||
...listActionsProps
|
...listActionsProps
|
||||||
|
|
|
@ -38,7 +38,7 @@ const props: Omit<CollectionDetailsPageProps, "classes"> = {
|
||||||
onSubmit: () => undefined,
|
onSubmit: () => undefined,
|
||||||
openChannelsModal: () => undefined,
|
openChannelsModal: () => undefined,
|
||||||
saveButtonBarState: "default",
|
saveButtonBarState: "default",
|
||||||
selectedChannel: "123"
|
selectedChannelId: "123"
|
||||||
};
|
};
|
||||||
|
|
||||||
storiesOf("Views / Collections / Collection details", module)
|
storiesOf("Views / Collections / Collection details", module)
|
||||||
|
|
|
@ -27,7 +27,7 @@ const props: CollectionListPageProps = {
|
||||||
},
|
},
|
||||||
...tabPageProps,
|
...tabPageProps,
|
||||||
collections,
|
collections,
|
||||||
selectedChannel: "123"
|
selectedChannelId: "123"
|
||||||
};
|
};
|
||||||
|
|
||||||
storiesOf("Views / Collections / Collection list", module)
|
storiesOf("Views / Collections / Collection list", module)
|
||||||
|
|
|
@ -47,7 +47,7 @@ const props: SaleDetailsPageProps = {
|
||||||
productListToolbar: null,
|
productListToolbar: null,
|
||||||
sale,
|
sale,
|
||||||
saveButtonBarState: "default",
|
saveButtonBarState: "default",
|
||||||
selectedChannel: "123",
|
selectedChannelId: "123",
|
||||||
...listActionsProps
|
...listActionsProps
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -42,9 +42,8 @@ const props: SaleListPageProps = {
|
||||||
value: [DiscountStatusEnum.ACTIVE]
|
value: [DiscountStatusEnum.ACTIVE]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSettingsOpen: () => undefined,
|
|
||||||
sales: saleList,
|
sales: saleList,
|
||||||
selectedChannel: "123",
|
selectedChannelId: "123",
|
||||||
sort: {
|
sort: {
|
||||||
...sortPageProps.sort,
|
...sortPageProps.sort,
|
||||||
sort: SaleListUrlSortField.name
|
sort: SaleListUrlSortField.name
|
||||||
|
@ -60,7 +59,6 @@ storiesOf("Views / Discounts / Sale list", module)
|
||||||
<SaleListPage
|
<SaleListPage
|
||||||
{...props}
|
{...props}
|
||||||
sales={saleList.map(sale => ({ ...sale, channelListings: [] }))}
|
sales={saleList.map(sale => ({ ...sale, channelListings: [] }))}
|
||||||
selectedChannel=""
|
selectedChannelId=""
|
||||||
onSettingsOpen={undefined}
|
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
|
@ -47,7 +47,7 @@ const props: VoucherDetailsPageProps = {
|
||||||
openChannelsModal: () => undefined,
|
openChannelsModal: () => undefined,
|
||||||
productListToolbar: null,
|
productListToolbar: null,
|
||||||
saveButtonBarState: "default",
|
saveButtonBarState: "default",
|
||||||
selectedChannel: "123",
|
selectedChannelId: "123",
|
||||||
voucher: voucherDetails
|
voucher: voucherDetails
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -51,8 +51,7 @@ const props: VoucherListPageProps = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSettingsOpen: () => undefined,
|
selectedChannelId: "123",
|
||||||
selectedChannel: "123",
|
|
||||||
sort: {
|
sort: {
|
||||||
...sortPageProps.sort,
|
...sortPageProps.sort,
|
||||||
sort: VoucherListUrlSortField.code
|
sort: VoucherListUrlSortField.code
|
||||||
|
@ -68,8 +67,7 @@ storiesOf("Views / Discounts / Voucher list", module)
|
||||||
.add("no channels", () => (
|
.add("no channels", () => (
|
||||||
<VoucherListPage
|
<VoucherListPage
|
||||||
{...props}
|
{...props}
|
||||||
selectedChannel=""
|
selectedChannelId=""
|
||||||
onSettingsOpen={undefined}
|
|
||||||
vouchers={voucherList.map(voucher => ({
|
vouchers={voucherList.map(voucher => ({
|
||||||
...voucher,
|
...voucher,
|
||||||
channelListings: []
|
channelListings: []
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import placeholderImage from "@assets/images/placeholder60x60.png";
|
import placeholderImage from "@assets/images/placeholder60x60.png";
|
||||||
import { Omit } from "@material-ui/core";
|
import { Omit } from "@material-ui/core";
|
||||||
import { channelsList } from "@saleor/channels/fixtures";
|
|
||||||
import { adminUserPermissions } from "@saleor/fixtures";
|
import { adminUserPermissions } from "@saleor/fixtures";
|
||||||
import { PermissionEnum } from "@saleor/types/globalTypes";
|
import { PermissionEnum } from "@saleor/types/globalTypes";
|
||||||
import { storiesOf } from "@storybook/react";
|
import { storiesOf } from "@storybook/react";
|
||||||
|
@ -11,16 +10,9 @@ import { shop as shopFixture } from "../../../home/fixtures";
|
||||||
import Decorator from "../../Decorator";
|
import Decorator from "../../Decorator";
|
||||||
|
|
||||||
const shop = shopFixture(placeholderImage);
|
const shop = shopFixture(placeholderImage);
|
||||||
const channelChoices = channelsList.map(channel => ({
|
|
||||||
label: channel.name,
|
|
||||||
value: channel.slug
|
|
||||||
}));
|
|
||||||
|
|
||||||
const homePageProps: Omit<HomePageProps, "classes"> = {
|
const homePageProps: Omit<HomePageProps, "classes"> = {
|
||||||
activities: shop.activities.edges.map(edge => edge.node),
|
activities: shop.activities.edges.map(edge => edge.node),
|
||||||
channelChoices,
|
|
||||||
channelValue: channelChoices[0].value,
|
|
||||||
onChannelChange: () => undefined,
|
|
||||||
onOrdersToCaptureClick: () => undefined,
|
onOrdersToCaptureClick: () => undefined,
|
||||||
onOrdersToFulfillClick: () => undefined,
|
onOrdersToFulfillClick: () => undefined,
|
||||||
onProductClick: () => undefined,
|
onProductClick: () => undefined,
|
||||||
|
|
|
@ -37,7 +37,6 @@ const props: OrderDraftListPageProps = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onAdd: () => undefined,
|
onAdd: () => undefined,
|
||||||
onSettingsOpen: () => undefined,
|
|
||||||
orders,
|
orders,
|
||||||
sort: {
|
sort: {
|
||||||
...sortPageProps.sort,
|
...sortPageProps.sort,
|
||||||
|
|
|
@ -21,7 +21,7 @@ const props: OrderProductAddDialogProps = {
|
||||||
onSubmit: () => undefined,
|
onSubmit: () => undefined,
|
||||||
open: true,
|
open: true,
|
||||||
products,
|
products,
|
||||||
selectedChannel: products[0].variants[0].channelListings[0].channel.id
|
selectedChannelId: products[0].variants[0].channelListings[0].channel.id
|
||||||
};
|
};
|
||||||
|
|
||||||
storiesOf("Orders / OrderProductAddDialog", module)
|
storiesOf("Orders / OrderProductAddDialog", module)
|
||||||
|
|
|
@ -42,9 +42,8 @@ const props: ProductListPageProps = {
|
||||||
filterOpts: productListFilterOpts,
|
filterOpts: productListFilterOpts,
|
||||||
gridAttributes: attributes,
|
gridAttributes: attributes,
|
||||||
onExport: () => undefined,
|
onExport: () => undefined,
|
||||||
onSettingsOpen: () => undefined,
|
|
||||||
products,
|
products,
|
||||||
selectedChannel: "123",
|
selectedChannelId: "123",
|
||||||
settings: {
|
settings: {
|
||||||
...pageListProps.default.settings,
|
...pageListProps.default.settings,
|
||||||
columns: ["availability", "productType", "price"]
|
columns: ["availability", "productType", "price"]
|
||||||
|
@ -69,8 +68,7 @@ storiesOf("Views / Products / Product list", module)
|
||||||
<ProductListPage
|
<ProductListPage
|
||||||
{...props}
|
{...props}
|
||||||
channelsCount={0}
|
channelsCount={0}
|
||||||
onSettingsOpen={undefined}
|
selectedChannelId={""}
|
||||||
selectedChannel={""}
|
|
||||||
products={products.map(product => ({ ...product, channelListings: [] }))}
|
products={products.map(product => ({ ...product, channelListings: [] }))}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
|
@ -58,6 +58,7 @@ const props: ProductUpdatePageProps = {
|
||||||
placeholderImage,
|
placeholderImage,
|
||||||
product,
|
product,
|
||||||
saveButtonBarState: "default",
|
saveButtonBarState: "default",
|
||||||
|
selectedChannelId: "123",
|
||||||
taxTypes,
|
taxTypes,
|
||||||
variants: product.variants,
|
variants: product.variants,
|
||||||
warehouses: warehouseList
|
warehouses: warehouseList
|
||||||
|
|
|
@ -27,7 +27,7 @@ const props: ShippingZoneDetailsPageProps = {
|
||||||
onWeightRateAdd: () => undefined,
|
onWeightRateAdd: () => undefined,
|
||||||
onWeightRateEdit: () => undefined,
|
onWeightRateEdit: () => undefined,
|
||||||
saveButtonBarState: "default",
|
saveButtonBarState: "default",
|
||||||
selectedChannel: "12345",
|
selectedChannelId: "12345",
|
||||||
shippingZone,
|
shippingZone,
|
||||||
warehouses: warehouseList
|
warehouses: warehouseList
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,9 +20,7 @@ const props: ShippingZonesListPageProps = {
|
||||||
onAdd: () => undefined,
|
onAdd: () => undefined,
|
||||||
onBack: () => undefined,
|
onBack: () => undefined,
|
||||||
onRemove: () => undefined,
|
onRemove: () => undefined,
|
||||||
onSettingsOpen: () => undefined,
|
|
||||||
onSubmit: () => undefined,
|
onSubmit: () => undefined,
|
||||||
selectedChannel: "123",
|
|
||||||
shippingZones,
|
shippingZones,
|
||||||
userPermissions: adminUserPermissions
|
userPermissions: adminUserPermissions
|
||||||
};
|
};
|
||||||
|
|
|
@ -117,6 +117,10 @@ export interface TabPageProps {
|
||||||
onTabSave: () => void;
|
onTabSave: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ChannelProps {
|
||||||
|
selectedChannelId: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface PartialMutationProviderOutput<
|
export interface PartialMutationProviderOutput<
|
||||||
TData extends {} = {},
|
TData extends {} = {},
|
||||||
TVariables extends {} = {}
|
TVariables extends {} = {}
|
||||||
|
|
|
@ -1,21 +1,32 @@
|
||||||
import { User } from "@saleor/fragments/types/User";
|
import { User } from "@saleor/fragments/types/User";
|
||||||
|
|
||||||
export const isSupported =
|
export const isSupported = !!(
|
||||||
navigator.credentials && navigator.credentials.preventSilentAccess;
|
navigator?.credentials?.preventSilentAccess && PasswordCredential
|
||||||
|
);
|
||||||
|
|
||||||
export function login<T>(loginFn: (id: string, password: string) => T): T {
|
export async function login<T>(
|
||||||
if (isSupported) {
|
loginFn: (id: string, password: string) => T
|
||||||
navigator.credentials.get({ password: true }).then(credential => {
|
): Promise<T | null> {
|
||||||
if (credential instanceof PasswordCredential) {
|
let result: T;
|
||||||
return loginFn(credential.id, credential.password);
|
|
||||||
}
|
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) {
|
if (isSupported) {
|
||||||
const cred = new PasswordCredential({
|
const cred = new PasswordCredential({
|
||||||
iconURL: user.avatar ? user.avatar.url : undefined,
|
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,
|
name: user.firstName ? `${user.firstName} ${user.lastName}` : undefined,
|
||||||
password
|
password
|
||||||
});
|
});
|
||||||
navigator.credentials.store(cred);
|
try {
|
||||||
|
result = navigator.credentials.store(cred);
|
||||||
|
} catch {
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue