Improve limit messages (#1274)
This commit is contained in:
parent
034eea0dcd
commit
6d3e346a19
11 changed files with 1762 additions and 59 deletions
|
@ -56,12 +56,18 @@ export const ChannelsListPage: React.FC<ChannelsListPageProps> = ({
|
||||||
</Backlink>
|
</Backlink>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title={intl.formatMessage(sectionNames.channels)}
|
title={intl.formatMessage(sectionNames.channels)}
|
||||||
limit={
|
limitText={
|
||||||
hasLimits(limits, "channels") && {
|
hasLimits(limits, "channels") &&
|
||||||
data: limits,
|
intl.formatMessage(
|
||||||
key: "channels",
|
{
|
||||||
text: "channels used"
|
defaultMessage: "{count}/{max} channels used",
|
||||||
}
|
description: "created channels counter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: limits.currentUsage.channels,
|
||||||
|
max: limits.allowedUsage.channels
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import { Typography } from "@material-ui/core";
|
import { Typography } from "@material-ui/core";
|
||||||
import { LimitInfoFragment } from "@saleor/fragments/types/LimitInfoFragment";
|
|
||||||
import { makeStyles } from "@saleor/macaw-ui";
|
import { makeStyles } from "@saleor/macaw-ui";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import ExtendedPageHeader from "../ExtendedPageHeader";
|
import ExtendedPageHeader from "../ExtendedPageHeader";
|
||||||
import { RefreshLimits_shop_limits } from "../Shop/types/RefreshLimits";
|
|
||||||
import Skeleton from "../Skeleton";
|
import Skeleton from "../Skeleton";
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
|
@ -40,27 +38,16 @@ const useStyles = makeStyles(
|
||||||
{ name: "PageHeader" }
|
{ name: "PageHeader" }
|
||||||
);
|
);
|
||||||
|
|
||||||
interface LimitInfo {
|
|
||||||
data: RefreshLimits_shop_limits;
|
|
||||||
key: keyof LimitInfoFragment;
|
|
||||||
text: string;
|
|
||||||
}
|
|
||||||
interface PageHeaderProps {
|
interface PageHeaderProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
inline?: boolean;
|
inline?: boolean;
|
||||||
limit?: LimitInfo;
|
limitText?: string;
|
||||||
title?: React.ReactNode;
|
title?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatLimit(limit: LimitInfo): string {
|
|
||||||
return `${limit.data.currentUsage[limit.key]}/${
|
|
||||||
limit.data.allowedUsage[limit.key]
|
|
||||||
} ${limit.text}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PageHeader: React.FC<PageHeaderProps> = props => {
|
const PageHeader: React.FC<PageHeaderProps> = props => {
|
||||||
const { children, className, inline, limit, title } = props;
|
const { children, className, inline, limitText, title } = props;
|
||||||
|
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
|
|
||||||
|
@ -76,9 +63,9 @@ const PageHeader: React.FC<PageHeaderProps> = props => {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
{limit && (
|
{limitText && (
|
||||||
<Typography className={classes.limit} color="textSecondary">
|
<Typography className={classes.limit} color="textSecondary">
|
||||||
{formatLimit(limit)}
|
{limitText}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Button, Card } from "@material-ui/core";
|
||||||
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 { RefreshLimits_shop_limits } from "@saleor/components/Shop/types/RefreshLimits";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import { OrderDraftListUrlSortField } from "@saleor/orders/urls";
|
import { OrderDraftListUrlSortField } from "@saleor/orders/urls";
|
||||||
import {
|
import {
|
||||||
|
@ -11,11 +12,13 @@ import {
|
||||||
SortPage,
|
SortPage,
|
||||||
TabPageProps
|
TabPageProps
|
||||||
} from "@saleor/types";
|
} from "@saleor/types";
|
||||||
|
import { hasLimits, isLimitReached } from "@saleor/utils/limits";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { OrderDraftList_draftOrders_edges_node } from "../../types/OrderDraftList";
|
import { OrderDraftList_draftOrders_edges_node } from "../../types/OrderDraftList";
|
||||||
import OrderDraftList from "../OrderDraftList";
|
import OrderDraftList from "../OrderDraftList";
|
||||||
|
import OrderLimitReached from "../OrderLimitReached";
|
||||||
import {
|
import {
|
||||||
createFilterStructure,
|
createFilterStructure,
|
||||||
OrderDraftFilterKeys,
|
OrderDraftFilterKeys,
|
||||||
|
@ -28,6 +31,7 @@ export interface OrderDraftListPageProps
|
||||||
FilterPageProps<OrderDraftFilterKeys, OrderDraftListFilterOpts>,
|
FilterPageProps<OrderDraftFilterKeys, OrderDraftListFilterOpts>,
|
||||||
SortPage<OrderDraftListUrlSortField>,
|
SortPage<OrderDraftListUrlSortField>,
|
||||||
TabPageProps {
|
TabPageProps {
|
||||||
|
limits: RefreshLimits_shop_limits;
|
||||||
orders: OrderDraftList_draftOrders_edges_node[];
|
orders: OrderDraftList_draftOrders_edges_node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +40,7 @@ const OrderDraftListPage: React.FC<OrderDraftListPageProps> = ({
|
||||||
disabled,
|
disabled,
|
||||||
filterOpts,
|
filterOpts,
|
||||||
initialSearch,
|
initialSearch,
|
||||||
|
limits,
|
||||||
onAdd,
|
onAdd,
|
||||||
onAll,
|
onAll,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
|
@ -48,14 +53,30 @@ const OrderDraftListPage: React.FC<OrderDraftListPageProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const structure = createFilterStructure(intl, filterOpts);
|
const structure = createFilterStructure(intl, filterOpts);
|
||||||
|
const limitsReached = isLimitReached(limits, "orders");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<PageHeader title={intl.formatMessage(sectionNames.draftOrders)}>
|
<PageHeader
|
||||||
|
title={intl.formatMessage(sectionNames.draftOrders)}
|
||||||
|
limitText={
|
||||||
|
hasLimits(limits, "orders") &&
|
||||||
|
intl.formatMessage(
|
||||||
|
{
|
||||||
|
defaultMessage: "{count}/{max} orders",
|
||||||
|
description: "placed orders counter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: limits.currentUsage.orders,
|
||||||
|
max: limits.allowedUsage.orders
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
disabled={disabled}
|
disabled={disabled || limitsReached}
|
||||||
onClick={onAdd}
|
onClick={onAdd}
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
@ -64,6 +85,7 @@ const OrderDraftListPage: React.FC<OrderDraftListPageProps> = ({
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
{limitsReached && <OrderLimitReached />}
|
||||||
<Card>
|
<Card>
|
||||||
<FilterBar
|
<FilterBar
|
||||||
allTabLabel={intl.formatMessage({
|
allTabLabel={intl.formatMessage({
|
||||||
|
|
21
src/orders/components/OrderLimitReached.tsx
Normal file
21
src/orders/components/OrderLimitReached.tsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import LimitReachedAlert from "@saleor/components/LimitReachedAlert";
|
||||||
|
import React from "react";
|
||||||
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
export const OrderLimitReached: React.FC = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LimitReachedAlert
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Order limit reached",
|
||||||
|
description: "alert"
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<FormattedMessage defaultMessage="You have reached your order limit, you will be billed extra for orders above limit. If you would like to up your limit, contact your administration staff about raising your limits." />
|
||||||
|
</LimitReachedAlert>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
OrderLimitReached.displayName = "OrderLimitReached";
|
||||||
|
export default OrderLimitReached;
|
|
@ -2,18 +2,18 @@ import { Button, Card } from "@material-ui/core";
|
||||||
import CardMenu from "@saleor/components/CardMenu";
|
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 LimitReachedAlert from "@saleor/components/LimitReachedAlert";
|
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import { RefreshLimits_shop_limits } from "@saleor/components/Shop/types/RefreshLimits";
|
import { RefreshLimits_shop_limits } from "@saleor/components/Shop/types/RefreshLimits";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import { makeStyles } from "@saleor/macaw-ui";
|
import { makeStyles } from "@saleor/macaw-ui";
|
||||||
import { OrderListUrlSortField } from "@saleor/orders/urls";
|
import { OrderListUrlSortField } from "@saleor/orders/urls";
|
||||||
import { FilterPageProps, PageListProps, SortPage } from "@saleor/types";
|
import { FilterPageProps, PageListProps, SortPage } from "@saleor/types";
|
||||||
import { isLimitReached } from "@saleor/utils/limits";
|
import { hasLimits, isLimitReached } from "@saleor/utils/limits";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { OrderList_orders_edges_node } from "../../types/OrderList";
|
import { OrderList_orders_edges_node } from "../../types/OrderList";
|
||||||
|
import OrderLimitReached from "../OrderLimitReached";
|
||||||
import OrderList from "../OrderList";
|
import OrderList from "../OrderList";
|
||||||
import {
|
import {
|
||||||
createFilterStructure,
|
createFilterStructure,
|
||||||
|
@ -58,10 +58,26 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const classes = useStyles({});
|
const classes = useStyles({});
|
||||||
const filterStructure = createFilterStructure(intl, filterOpts);
|
const filterStructure = createFilterStructure(intl, filterOpts);
|
||||||
|
const limitsReached = isLimitReached(limits, "orders");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<PageHeader title={intl.formatMessage(sectionNames.orders)}>
|
<PageHeader
|
||||||
|
title={intl.formatMessage(sectionNames.orders)}
|
||||||
|
limitText={
|
||||||
|
hasLimits(limits, "orders") &&
|
||||||
|
intl.formatMessage(
|
||||||
|
{
|
||||||
|
defaultMessage: "{count}/{max} orders",
|
||||||
|
description: "placed order counter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: limits.currentUsage.orders,
|
||||||
|
max: limits.allowedUsage.orders
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
{!!onSettingsOpen && (
|
{!!onSettingsOpen && (
|
||||||
<CardMenu
|
<CardMenu
|
||||||
className={classes.settings}
|
className={classes.settings}
|
||||||
|
@ -77,6 +93,7 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
|
disabled={limitsReached}
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={onAdd}
|
onClick={onAdd}
|
||||||
|
@ -88,16 +105,7 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
{isLimitReached(limits, "orders") && (
|
{limitsReached && <OrderLimitReached />}
|
||||||
<LimitReachedAlert
|
|
||||||
title={intl.formatMessage({
|
|
||||||
defaultMessage: "Order limit reached",
|
|
||||||
description: "alert"
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<FormattedMessage defaultMessage="You have reached your order limit, you will be billed extra for orders above limit. If you would like to up your limit, contact your administration staff about raising your limits." />
|
|
||||||
</LimitReachedAlert>
|
|
||||||
)}
|
|
||||||
<Card>
|
<Card>
|
||||||
<FilterBar
|
<FilterBar
|
||||||
currentTab={currentTab}
|
currentTab={currentTab}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||||
import SaveFilterTabDialog, {
|
import SaveFilterTabDialog, {
|
||||||
SaveFilterTabDialogFormData
|
SaveFilterTabDialogFormData
|
||||||
} from "@saleor/components/SaveFilterTabDialog";
|
} from "@saleor/components/SaveFilterTabDialog";
|
||||||
|
import { useShopLimitsQuery } from "@saleor/components/Shop/query";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
import useListSettings from "@saleor/hooks/useListSettings";
|
import useListSettings from "@saleor/hooks/useListSettings";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
|
@ -81,6 +82,11 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = ({ params }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const { channel, availableChannels } = useAppChannel();
|
const { channel, availableChannels } = useAppChannel();
|
||||||
|
const limitOpts = useShopLimitsQuery({
|
||||||
|
variables: {
|
||||||
|
orders: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const tabs = getFilterTabs();
|
const tabs = getFilterTabs();
|
||||||
|
|
||||||
|
@ -183,6 +189,7 @@ export const OrderDraftList: React.FC<OrderDraftListProps> = ({ params }) => {
|
||||||
<OrderDraftListPage
|
<OrderDraftListPage
|
||||||
currentTab={currentTab}
|
currentTab={currentTab}
|
||||||
filterOpts={getFilterOpts(params)}
|
filterOpts={getFilterOpts(params)}
|
||||||
|
limits={limitOpts.data?.shop.limits}
|
||||||
initialSearch={params.query || ""}
|
initialSearch={params.query || ""}
|
||||||
onSearchChange={handleSearchChange}
|
onSearchChange={handleSearchChange}
|
||||||
onFilterChange={changeFilters}
|
onFilterChange={changeFilters}
|
||||||
|
|
|
@ -134,12 +134,18 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
|
||||||
<Container>
|
<Container>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title={intl.formatMessage(sectionNames.products)}
|
title={intl.formatMessage(sectionNames.products)}
|
||||||
limit={
|
limitText={
|
||||||
hasLimits(limits, "productVariants") && {
|
hasLimits(limits, "productVariants") &&
|
||||||
data: limits,
|
intl.formatMessage(
|
||||||
key: "productVariants",
|
{
|
||||||
text: "SKUs used"
|
defaultMessage: "{count}/{max} SKUs used",
|
||||||
}
|
description: "created products counter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: limits.currentUsage.productVariants,
|
||||||
|
max: limits.allowedUsage.productVariants
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<CardMenu
|
<CardMenu
|
||||||
|
|
|
@ -64,12 +64,18 @@ const StaffListPage: React.FC<StaffListPageProps> = ({
|
||||||
</Backlink>
|
</Backlink>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title={intl.formatMessage(sectionNames.staff)}
|
title={intl.formatMessage(sectionNames.staff)}
|
||||||
limit={
|
limitText={
|
||||||
hasLimits(limits, "staffUsers") && {
|
hasLimits(limits, "staffUsers") &&
|
||||||
data: limits,
|
intl.formatMessage(
|
||||||
key: "staffUsers",
|
{
|
||||||
text: "members"
|
defaultMessage: "{count}/{max} members",
|
||||||
}
|
description: "used staff users counter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: limits.currentUsage.staffUsers,
|
||||||
|
max: limits.allowedUsage.staffUsers
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,6 +4,8 @@ import React from "react";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
filterPageProps,
|
filterPageProps,
|
||||||
|
limits,
|
||||||
|
limitsReached,
|
||||||
listActionsProps,
|
listActionsProps,
|
||||||
pageListProps,
|
pageListProps,
|
||||||
searchPageProps,
|
searchPageProps,
|
||||||
|
@ -36,6 +38,7 @@ const props: OrderDraftListPageProps = {
|
||||||
value: undefined
|
value: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
limits,
|
||||||
onAdd: () => undefined,
|
onAdd: () => undefined,
|
||||||
orders,
|
orders,
|
||||||
sort: {
|
sort: {
|
||||||
|
@ -50,4 +53,7 @@ storiesOf("Views / Orders / Draft order list", module)
|
||||||
.add("loading", () => (
|
.add("loading", () => (
|
||||||
<OrderDraftListPage {...props} disabled orders={undefined} />
|
<OrderDraftListPage {...props} disabled orders={undefined} />
|
||||||
))
|
))
|
||||||
.add("when no data", () => <OrderDraftListPage {...props} orders={[]} />);
|
.add("when no data", () => <OrderDraftListPage {...props} orders={[]} />)
|
||||||
|
.add("limits reached", () => (
|
||||||
|
<OrderDraftListPage {...props} limits={limitsReached} />
|
||||||
|
));
|
||||||
|
|
|
@ -65,12 +65,18 @@ export const WarehouseListPage: React.FC<WarehouseListPageProps> = ({
|
||||||
</Backlink>
|
</Backlink>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title={intl.formatMessage(sectionNames.warehouses)}
|
title={intl.formatMessage(sectionNames.warehouses)}
|
||||||
limit={
|
limitText={
|
||||||
hasLimits(limits, "warehouses") && {
|
hasLimits(limits, "warehouses") &&
|
||||||
data: limits,
|
intl.formatMessage(
|
||||||
key: "warehouses",
|
{
|
||||||
text: "warehouses used"
|
defaultMessage: "{count}/{max} warehouses used",
|
||||||
}
|
description: "used warehouses counter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: limits.currentUsage.warehouses,
|
||||||
|
max: limits.allowedUsage.warehouses
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
|
|
Loading…
Reference in a new issue