Change StatusLabels to Pills (#1867)

* Change StatusLabels to Pills (#1819)

* Refactor StatusLabel component to use Pills

* Remove StatusLabel component

* Remove StatusChip component

* Refactor ChannelsAvailibilityDropdown for usage with Pills

* Remove AvailabilityStatusLabel

* Fix FilterErrorsList dot

* Refactor plugin availability

* Update messages

* Update snapshots & solve rebase conflicts

* Fix rebase conflict - duplicate intl

* Handle products & collections without channels

* Change plugins messages from inactive to deactivated

* Remove redundant ChannelConfigPopup

* Change AvailabilityMenu from onClick to onHover

* Update snapshots & fix rebase conflicts

* Add order title

* Fix status colors

* Remove unused import

* Fix extra spacing in unfulfilled pill

* Add payment card title & move messages outside file

* Fix impromper skeleton rendering in product list

* Update snapshots & fix rebase conflicts

* Change plugins pills to interactive

* Update snapshots

* Trigger deployment

* Apply minor CR fixes

* Change callb
acks props to mapped objects in ChannelsAvailabilityMenu

* Rebase / update snapshots

* Fix fulfilled from label position

* Update snapshots

* Change messages names in oRderPayment

* Fix Pill overflowing

* Update snapshots
This commit is contained in:
Michał Droń 2022-02-17 12:08:17 +01:00 committed by GitHub
parent 675a3a959a
commit f5f4858cf1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 2680 additions and 4972 deletions

View file

@ -2040,25 +2040,33 @@
"context": "select title",
"string": "Select channels you want for {contentType} to be available on"
},
"src_dot_components_dot_ChannelsAvailabilityDropdown_dot_1043589445": {
"context": "product channel publication status",
"string": "hidden"
"src_dot_components_dot_ChannelsAvailabilityDropdown_dot_channel": {
"context": "Channel label",
"string": "Channel"
},
"src_dot_components_dot_ChannelsAvailabilityDropdown_dot_1702481199": {
"context": "product channel publication date",
"string": "published since {date}"
},
"src_dot_components_dot_ChannelsAvailabilityDropdown_dot_1944644572": {
"src_dot_components_dot_ChannelsAvailabilityDropdown_dot_dropdownLabel": {
"context": "product status title",
"string": "{count}/{allCount} channels"
"string": "{channelCount} {channelCount,plural, =1 {Channel} other {Channels}}"
},
"src_dot_components_dot_ChannelsAvailabilityDropdown_dot_3285520461": {
"context": "product channel publication date",
"string": "Will become available on {date}"
"src_dot_components_dot_ChannelsAvailabilityDropdown_dot_noChannels": {
"context": "dropdown label when there are no channels assigned",
"string": "No channels"
},
"src_dot_components_dot_ChannelsAvailabilityDropdown_dot_802058289": {
"context": "product status",
"string": "Available in {count} out of {allCount, plural, one {# channel} other {# channels}}"
"src_dot_components_dot_ChannelsAvailabilityDropdown_dot_published": {
"context": "Status label when object is published in a channel",
"string": "Published"
},
"src_dot_components_dot_ChannelsAvailabilityDropdown_dot_scheduled": {
"context": "Status label when object is scheduled to publish in a channel",
"string": "Scheduled to publish"
},
"src_dot_components_dot_ChannelsAvailabilityDropdown_dot_status": {
"context": "Status label",
"string": "Status"
},
"src_dot_components_dot_ChannelsAvailabilityDropdown_dot_unpublished": {
"context": "Status label when object is unpublished in a channel",
"string": "Unpublished"
},
"src_dot_components_dot_ColumnPicker_dot_1483881697": {
"context": "button",
@ -2467,18 +2475,6 @@
"src_dot_components_dot_SingleSelectField_dot_4205644805": {
"string": "No results found"
},
"src_dot_components_dot_StatusLabel_dot_active": {
"context": "status label active",
"string": "Active"
},
"src_dot_components_dot_StatusLabel_dot_deactivated": {
"context": "status label deactivated",
"string": "Deactivated"
},
"src_dot_components_dot_StatusLabel_dot_inactive": {
"context": "status label inactive",
"string": "Inactive"
},
"src_dot_components_dot_TableHead_dot_868570480": {
"string": "Selected {number} items"
},
@ -4377,6 +4373,9 @@
"context": "OrderCustomer Fulfillment from Local Warehouse",
"string": "Fulfill from Local Stock"
},
"src_dot_orders_dot_components_dot_OrderDetailsPage_dot_580490159": {
"string": "Order #{orderNumber}"
},
"src_dot_orders_dot_components_dot_OrderDetailsPage_dot_cancelOrder": {
"context": "cancel button",
"string": "Cancel order"
@ -4950,85 +4949,93 @@
"context": "dialog header",
"string": "Void Payment"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_1322321687": {
"context": "order payment",
"string": "Refunded amount"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_1325966144": {
"context": "order shipping method name",
"string": "Shipping"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_1817306106": {
"context": "vat included in order price",
"string": "VAT included"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_1934027242": {
"context": "voucher type order discount",
"string": "Voucher"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_2183023165": {
"context": "ordered products",
"string": "{quantity} items"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_2320183694": {
"context": "order payment",
"string": "Captured amount"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_2392156856": {
"context": "staff added type order discount",
"string": "Staff added"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_2444197639": {
"context": "void payment, button",
"string": "Void"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_2845258362": {
"context": "button",
"string": "Refund"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_3500506678": {
"context": "order, button",
"string": "Mark as paid"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_353147224": {
"context": "order payment",
"string": "Outstanding Balance"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_3720114122": {
"context": "order discount",
"string": "Discount"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_372187363": {
"context": "order payment",
"string": "Paid with Gift Card"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_3768782744": {
"context": "order payment",
"string": "Preauthorized amount"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_3955023266": {
"string": "Taxes"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_4211710217": {
"src_dot_orders_dot_components_dot_OrderPayment_dot_capture": {
"context": "capture payment, button",
"string": "Capture"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_781550514": {
"context": "order subtotal price",
"string": "Subtotal"
"src_dot_orders_dot_components_dot_OrderPayment_dot_captured": {
"context": "order payment",
"string": "Captured amount"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_878013594": {
"context": "order total price",
"string": "Total"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_orderPaymentClickAndCollectShippingMethod": {
"src_dot_orders_dot_components_dot_OrderPayment_dot_clickAndCollectShippingMethod": {
"context": "OrderPayment click&collect shipping method",
"string": "click&collect"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_orderPaymentShippingDoesNotApply": {
"src_dot_orders_dot_components_dot_OrderPayment_dot_discount": {
"context": "order discount",
"string": "Discount"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_itemCount": {
"context": "ordered products",
"string": "{quantity} items"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_markAsPaid": {
"context": "order, button",
"string": "Mark as paid"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_outstanding": {
"context": "order payment",
"string": "Outstanding Balance"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_paidWithGiftCard": {
"context": "order payment",
"string": "Paid with Gift Card"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_paymentTitle": {
"context": "Payment card title",
"string": "Payment status"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_preauthorized": {
"context": "order payment",
"string": "Preauthorized amount"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_refund": {
"context": "button",
"string": "Refund"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_refunded": {
"context": "order payment",
"string": "Refunded amount"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_shipping": {
"context": "order shipping method name",
"string": "Shipping"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_shippingDoesNotApply": {
"context": "OrderPayment does not require shipping",
"string": "does not apply"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_shippingNotApplicable": {
"context": "order does not require shipping",
"string": "does not apply"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_staffAdded": {
"context": "staff added type order discount",
"string": "Staff added"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_subtotal": {
"context": "order subtotal price",
"string": "Subtotal"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_taxes": {
"string": "Taxes"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_total": {
"context": "order total price",
"string": "Total"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_vatIncluded": {
"context": "vat included in order price",
"string": "VAT included"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_void": {
"context": "button",
"string": "Refund"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_voucher": {
"context": "voucher type order discount",
"string": "Voucher"
},
"src_dot_orders_dot_components_dot_OrderProductAddDialog_dot_2272209368": {
"context": "variant sku",
"string": "SKU {sku}"
@ -5945,18 +5952,26 @@
"src_dot_plugins_dot_components_dot_PluginsList_dot_666641390": {
"string": "No plugins found"
},
"src_dot_plugins_dot_components_dot_PluginsList_dot_active": {
"context": "status label active",
"string": "Active"
},
"src_dot_plugins_dot_components_dot_PluginsList_dot_channelLabel": {
"context": "table header channel col label",
"string": "Channel"
},
"src_dot_plugins_dot_components_dot_PluginsList_dot_channelTitle": {
"context": "plugin channel availability status title",
"string": "Active in {activeChannelsCount}"
"string": "{activeChannelsCount,plural, =0 {Deactivated} other {Active in {activeChannelsCount}}}"
},
"src_dot_plugins_dot_components_dot_PluginsList_dot_confLabel": {
"context": "table header configuration col label",
"string": "Configuration"
},
"src_dot_plugins_dot_components_dot_PluginsList_dot_deactivated": {
"context": "status label deactivated",
"string": "Deactivated"
},
"src_dot_plugins_dot_components_dot_PluginsList_dot_description": {
"context": "global config plugin status popup description",
"string": "Global plugins are set across all channels in your ecommerce. Only status is shown for those types of plugins"

View file

@ -101,7 +101,6 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
<CardSpacer />
<CollectionProducts
disabled={disabled}
channelsCount={channelsCount}
collection={collection}
{...collectionProductsProps}
/>

View file

@ -1,8 +1,11 @@
import { TableBody, TableCell, TableFooter, TableRow } from "@material-ui/core";
import { CollectionListUrlSortField } from "@saleor/collections/urls";
import { canBeSorted } from "@saleor/collections/views/CollectionList/sort";
import AvailabilityStatusLabel from "@saleor/components/AvailabilityStatusLabel";
import { ChannelsAvailabilityDropdown } from "@saleor/components/ChannelsAvailabilityDropdown";
import {
getChannelAvailabilityColor,
getChannelAvailabilityLabel
} from "@saleor/components/ChannelsAvailabilityDropdown/utils";
import Checkbox from "@saleor/components/Checkbox";
import ResponsiveTable from "@saleor/components/ResponsiveTable";
import Skeleton from "@saleor/components/Skeleton";
@ -11,7 +14,7 @@ import TableHead from "@saleor/components/TableHead";
import TablePagination from "@saleor/components/TablePagination";
import TooltipTableCellHeader from "@saleor/components/TooltipTableCellHeader";
import { commonTooltipMessages } from "@saleor/components/TooltipTableCellHeader/messages";
import { makeStyles } from "@saleor/macaw-ui";
import { makeStyles, Pill } from "@saleor/macaw-ui";
import { maybe, renderCollection } from "@saleor/misc";
import { ChannelProps, ListActions, ListProps, SortPage } from "@saleor/types";
import { getArrowDirection } from "@saleor/utils/sort";
@ -19,7 +22,6 @@ import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { CollectionList_collections_edges_node } from "../../types/CollectionList";
import { messages } from "./messages";
const useStyles = makeStyles(
theme => ({
@ -52,14 +54,12 @@ interface CollectionListProps
SortPage<CollectionListUrlSortField>,
ChannelProps {
collections: CollectionList_collections_edges_node[];
channelsCount: number;
}
const numberOfColumns = 4;
const CollectionList: React.FC<CollectionListProps> = props => {
const {
channelsCount,
collections,
disabled,
settings,
@ -194,17 +194,16 @@ const CollectionList: React.FC<CollectionListProps> = props => {
data-test-availability={!!collection?.channelListings?.length}
>
{(!collection && <Skeleton />) ||
(!collection?.channelListings?.length && "-") ||
(collection?.channelListings !== undefined && channel ? (
<AvailabilityStatusLabel
channel={channel}
messages={messages}
(channel ? (
<Pill
label={intl.formatMessage(
getChannelAvailabilityLabel(channel)
)}
color={getChannelAvailabilityColor(channel)}
/>
) : (
<ChannelsAvailabilityDropdown
allChannelsCount={channelsCount}
channels={collection?.channelListings}
showStatus
/>
))}
</TableCell>

View file

@ -98,7 +98,6 @@ const CollectionListPage: React.FC<CollectionListPageProps> = ({
/>
<CollectionList
disabled={disabled}
channelsCount={channelsCount}
selectedChannelId={selectedChannelId}
filterDependency={filterDependency}
{...listProps}

View file

@ -54,13 +54,11 @@ const useStyles = makeStyles(
export interface CollectionProductsProps extends PageListProps, ListActions {
collection: CollectionDetails_collection;
channelsCount: number;
onProductUnassign: (id: string, event: React.MouseEvent<any>) => void;
}
const CollectionProducts: React.FC<CollectionProductsProps> = props => {
const {
channelsCount,
collection,
disabled,
onAdd,
@ -195,7 +193,6 @@ const CollectionProducts: React.FC<CollectionProductsProps> = props => {
"-"
) : product?.channelListings !== undefined ? (
<ChannelsAvailabilityDropdown
allChannelsCount={channelsCount}
channels={product?.channelListings}
/>
) : (

View file

@ -1,33 +0,0 @@
import StatusLabel from "@saleor/components/StatusLabel";
import useDateLocalize from "@saleor/hooks/useDateLocalize";
import React from "react";
import { useIntl } from "react-intl";
export const AvailabilityStatusLabel = ({ channel, messages }) => {
const intl = useIntl();
const localizeDate = useDateLocalize();
return (
<StatusLabel
label={intl.formatMessage(
channel.publicationDate
? channel.isPublished
? messages.published
: messages.willBePublished
: messages.unpublished,
{
date: localizeDate(channel.publicationDate, "L")
}
)}
status={
channel.publicationDate
? channel.isPublished
? "success"
: "alert"
: "error"
}
/>
);
};
export default AvailabilityStatusLabel;

View file

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

View file

@ -8,9 +8,7 @@ import ChannelsAvailabilityDropdown, {
} from "./ChannelsAvailabilityDropdown";
const props: ChannelsAvailabilityDropdownProps = {
allChannelsCount: 6,
channels: productChannels,
showStatus: true
channels: productChannels
};
storiesOf("Generics / ChannelsAvailabilityDropdown", module)

View file

@ -1,140 +1,63 @@
import { Menu, MenuItem, Typography } from "@material-ui/core";
import { CollectionList_collections_edges_node_channelListings } from "@saleor/collections/types/CollectionList";
import Hr from "@saleor/components/Hr";
import StatusLabel from "@saleor/components/StatusLabel";
import useDateLocalize from "@saleor/hooks/useDateLocalize";
import { Card, Popper } from "@material-ui/core";
import { Pill } from "@saleor/macaw-ui";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { useIntl } from "react-intl";
import { useStyles } from "./styles";
type Channels = Pick<
CollectionList_collections_edges_node_channelListings,
"isPublished" | "publicationDate" | "channel"
>;
import ChannelsAvailabilityMenuContent from "../ChannelsAvailabilityMenuContent";
import { messages } from "./messages";
import {
CollectionChannels,
getDropdownColor,
mapChannelsToPills
} from "./utils";
export interface ChannelsAvailabilityDropdownProps {
allChannelsCount: number;
channels: Channels[];
showStatus?: boolean;
channels: CollectionChannels[] | null;
}
const isActive = (channelData: Channels) => channelData?.isPublished;
export const ChannelsAvailabilityDropdown: React.FC<ChannelsAvailabilityDropdownProps> = ({
allChannelsCount,
channels,
showStatus = false
channels
}) => {
const intl = useIntl();
const classes = useStyles({});
const localizeDate = useDateLocalize();
const [anchorEl, setAnchorEl] = React.useState(null);
const [isPopupOpen, setPopupOpen] = React.useState(false);
const anchor = React.useRef<HTMLDivElement>(null);
const handleClick = event => setAnchorEl(event.currentTarget);
const handleClose = () => setAnchorEl(null);
const activeInAllChannels = React.useMemo(
() => showStatus && channels.every(isActive),
[channels, showStatus]
);
const dropdownColor = React.useMemo(() => getDropdownColor(channels), [
channels
]);
if (!channels?.length) {
return (
<Pill label={intl.formatMessage(messages.noChannels)} color="error" />
);
}
return (
<div onClick={e => e.stopPropagation()}>
<div
aria-controls="availability-menu"
aria-haspopup="true"
role="button"
onClick={handleClick}
>
<StatusLabel
label={intl.formatMessage(
{
defaultMessage: "{count}/{allCount} channels",
description: "product status title"
},
{
allCount: allChannelsCount,
count: channels.length
}
)}
status={
showStatus ? (activeInAllChannels ? "success" : "error") : undefined
}
<div
onClick={e => {
e.preventDefault();
e.stopPropagation();
}}
ref={anchor}
onMouseOver={() => setPopupOpen(true)}
onMouseLeave={() => setPopupOpen(false)}
>
<div aria-controls="availability-menu" aria-haspopup="true" role="button">
<Pill
label={intl.formatMessage(messages.dropdownLabel, {
channelCount: channels.length
})}
color={dropdownColor}
onClick={() => null} // required for dashed border
/>
</div>
<Menu
id="availability-menu"
anchorEl={anchorEl}
keepMounted
elevation={8}
open={Boolean(anchorEl)}
onClose={handleClose}
getContentAnchorEl={null}
anchorOrigin={{
horizontal: "center",
vertical: "bottom"
}}
transformOrigin={{
horizontal: "center",
vertical: "top"
}}
>
<Typography className={classes.title}>
<FormattedMessage
defaultMessage="Available in {count} out of {allCount, plural, one {# channel} other {# channels}}"
description="product status"
values={{
allCount: allChannelsCount,
count: channels.length
}}
<Popper anchorEl={anchor.current} open={isPopupOpen} placement={"left"}>
<Card elevation={8}>
<ChannelsAvailabilityMenuContent
pills={mapChannelsToPills(channels)}
/>
</Typography>
<Hr className={classes.hr} />
{channels.map(channelData => {
const notPublishedText = intl.formatMessage(
{
defaultMessage: "Will become available on {date}",
description: "product channel publication date"
},
{
date: localizeDate(channelData.publicationDate, "L")
}
);
const publishedText = intl.formatMessage(
{
defaultMessage: "published since {date}",
description: "product channel publication date"
},
{
date: localizeDate(channelData.publicationDate, "L")
}
);
return (
<MenuItem key={channelData.channel.id} className={classes.menuItem}>
<StatusLabel
label={channelData.channel.name}
status={isActive(channelData) ? "success" : "error"}
/>
<div>
<Typography variant="caption" className={classes.caption}>
{channelData.isPublished && channelData.publicationDate
? publishedText
: channelData.publicationDate && !channelData.isPublished
? notPublishedText
: channelData.isPublished
? ""
: intl.formatMessage({
defaultMessage: "hidden",
description: "product channel publication status"
})}
</Typography>
</div>
</MenuItem>
);
})}
</Menu>
</Card>
</Popper>
</div>
);
};

View file

@ -0,0 +1,36 @@
import { defineMessages } from "react-intl";
export const messages = defineMessages({
status: {
defaultMessage: "Status",
description: "Status label"
},
channel: {
defaultMessage: "Channel",
description: "Channel label"
},
dropdownLabel: {
defaultMessage:
"{channelCount} {channelCount,plural, =1 {Channel} other {Channels}}",
description: "product status title"
},
noChannels: {
defaultMessage: "No channels",
description: "dropdown label when there are no channels assigned"
}
});
export const channelStatusMessages = defineMessages({
unpublished: {
defaultMessage: "Unpublished",
description: "Status label when object is unpublished in a channel"
},
scheduled: {
defaultMessage: "Scheduled to publish",
description: "Status label when object is scheduled to publish in a channel"
},
published: {
defaultMessage: "Published",
description: "Status label when object is published in a channel"
}
});

View file

@ -1,21 +0,0 @@
import { makeStyles } from "@saleor/macaw-ui";
export const useStyles = makeStyles(
theme => ({
caption: {
paddingLeft: theme.spacing(2)
},
hr: {
left: theme.spacing(-1),
position: "relative",
width: `calc(100% + ${theme.spacing(2)}px)`
},
menuItem: {
display: "block"
},
title: {
padding: theme.spacing(1, 2, 1, 2)
}
}),
{ name: "ChannelsAvailabilityDropdown" }
);

View file

@ -0,0 +1,61 @@
import { CollectionList_collections_edges_node_channelListings } from "@saleor/collections/types/CollectionList";
import { PillColor } from "@saleor/macaw-ui";
import { MessageDescriptor } from "react-intl";
import { Pill } from "../ChannelsAvailabilityMenuContent";
import { channelStatusMessages } from "./messages";
export type CollectionChannels = Pick<
CollectionList_collections_edges_node_channelListings,
"isPublished" | "publicationDate" | "channel"
>;
export type Channels = Pick<
CollectionList_collections_edges_node_channelListings,
"channel"
>;
export const isActive = (channelData: CollectionChannels) =>
channelData?.isPublished;
export const isScheduled = (channelData: CollectionChannels) =>
channelData?.publicationDate && !channelData?.isPublished;
export const getDropdownColor = (channels: CollectionChannels[]) => {
if (channels.some(isActive)) {
return "success";
}
if (channels.some(isScheduled)) {
return "warning";
}
return "error";
};
export const getChannelAvailabilityColor = (
channelData: CollectionChannels
): PillColor => {
if (isActive(channelData)) {
return "success";
}
if (isScheduled(channelData)) {
return "warning";
}
return "error";
};
export const getChannelAvailabilityLabel = (
channelData: CollectionChannels
): MessageDescriptor => {
if (isActive(channelData)) {
return channelStatusMessages.published;
}
if (isScheduled(channelData)) {
return channelStatusMessages.scheduled;
}
return channelStatusMessages.unpublished;
};
export const mapChannelsToPills = (channelData: CollectionChannels[]): Pill[] =>
channelData.map(channel => ({
channel: channel.channel,
color: getChannelAvailabilityColor(channel),
label: getChannelAvailabilityLabel(channel)
}));

View file

@ -0,0 +1,51 @@
import { Typography } from "@material-ui/core";
import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer";
import { CollectionList_collections_edges_node_channelListings_channel } from "@saleor/collections/types/CollectionList";
import { Pill, PillColor } from "@saleor/macaw-ui";
import ScrollableContent from "@saleor/plugins/components/PluginsList/PluginAvailabilityStatusPopup/ScrollableContent";
import React from "react";
import { MessageDescriptor } from "react-intl";
import { useIntl } from "react-intl";
import { messages } from "../ChannelsAvailabilityDropdown/messages";
import { useStyles } from "./styles";
export interface ChannelsAvailabilityMenuContentProps {
pills: Pill[];
}
export interface Pill {
channel: CollectionList_collections_edges_node_channelListings_channel;
color: PillColor;
label: MessageDescriptor;
}
export const ChannelsAvailabilityMenuContent: React.FC<ChannelsAvailabilityMenuContentProps> = ({
pills
}) => {
const intl = useIntl();
const classes = useStyles({});
return (
<div className={classes.menuContainer}>
<div className={classes.row}>
<Typography variant="caption" className={classes.caption}>
{intl.formatMessage(messages.channel)}
</Typography>
<Typography variant="caption" className={classes.caption}>
{intl.formatMessage(messages.status)}
</Typography>
</div>
<ScrollableContent>
{pills.map(pill => (
<div key={pill.channel.id} className={classes.row}>
<Typography>{pill.channel.name}</Typography>
<HorizontalSpacer spacing={4} />
<Pill label={intl.formatMessage(pill.label)} color={pill.color} />
</div>
))}
</ScrollableContent>
</div>
);
};
ChannelsAvailabilityMenuContent.displayName = "ChannelsAvailabilityMenuContent";
export default ChannelsAvailabilityMenuContent;

View file

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

View file

@ -0,0 +1,36 @@
import { makeStyles } from "@saleor/macaw-ui";
export const useStyles = makeStyles(
theme => ({
menuContainer: {
padding: theme.spacing(2)
},
row: {
display: "flex",
justifyContent: "space-between",
"&:not(:last-child)": {
marginBottom: theme.spacing(2)
}
},
caption: {
textTransform: "uppercase",
color: theme.palette.saleor.main[3],
fontWeight: 500,
letterSpacing: "0.1em"
},
hr: {
left: theme.spacing(-1),
position: "relative",
width: `calc(100% + ${theme.spacing(2)}px)`
},
menuItem: {
display: "block"
},
title: {
padding: theme.spacing(1, 2, 1, 2)
}
}),
{ name: "ChannelsAvailabilityMenuContent" }
);

View file

@ -1,9 +1,7 @@
import { Typography } from "@material-ui/core";
import { fade, makeStyles } from "@material-ui/core/styles";
import InlineAlert from "@saleor/components/Alert/InlineAlert";
import { useStyles as useDotStyles } from "@saleor/components/StatusLabel";
import errorTracker from "@saleor/services/errorTracking";
import classNames from "classnames";
import React from "react";
import { useIntl } from "react-intl";
@ -21,7 +19,12 @@ const useStyles = makeStyles(
},
dot: {
backgroundColor: theme.palette.primary.contrastText,
marginRight: theme.spacing(1)
marginRight: theme.spacing(1),
borderRadius: "100%",
height: 8,
minHeight: 8,
width: 8,
minWidth: 8
},
itemContainer: {
display: "flex",
@ -43,7 +46,6 @@ const FilterErrorsList: React.FC<FilterErrorsListProps> = ({
errorMessages
}) => {
const classes = useStyles({});
const dotClasses = useDotStyles({});
const intl = useIntl();
const getErrorMessage = (code: string) => {
@ -69,7 +71,7 @@ const FilterErrorsList: React.FC<FilterErrorsListProps> = ({
<InlineAlert>
{errors.map(code => (
<div className={classes.itemContainer} key={code}>
<div className={classNames(classes.dot, dotClasses.dot)} />
<div className={classes.dot} />
<Typography className={classes.listItemTitle}>
{getErrorMessage(code)}
</Typography>

View file

@ -1,17 +0,0 @@
import Decorator from "@saleor/storybook/Decorator";
import { storiesOf } from "@storybook/react";
import React from "react";
import StatusChip from "./StatusChip";
import { StatusType } from "./types";
storiesOf("Generics / Status Chip", module)
.addDecorator(Decorator)
.add("neutral", () => (
<StatusChip label="label" status={StatusType.NEUTRAL} />
))
.add("error", () => <StatusChip label="label" status={StatusType.ERROR} />)
.add("success", () => (
<StatusChip label="label" status={StatusType.SUCCESS} />
))
.add("alert", () => <StatusChip label="label" status={StatusType.ALERT} />);

View file

@ -1,92 +0,0 @@
import { Typography } from "@material-ui/core";
import { makeStyles } from "@saleor/macaw-ui";
import classNames from "classnames";
import React from "react";
import { Size } from "../ActionDialog/types";
import { StatusType } from "./types";
export interface StatusChipProps {
status?: StatusType;
size?: Size;
label?: string;
}
export const statusChipStyles = {
alert: {
background: "#FFF4E4"
},
alertLabel: {
color: "#FFB84E"
},
error: {
backgroundColor: "rgba(254, 110, 118, 0.15)"
},
errorLabel: {
color: "#FE6E76"
},
neutral: {
background: "rgba(40, 35, 74, 0.1)"
},
neutralLabel: {
color: "#28234A"
},
success: {
background: "rgba(93, 194, 146, 0.3)"
},
successLabel: {
color: "#5DC292"
},
lg: {
padding: "4px 16px"
},
lgLabel: {
fontSize: "1.5rem"
},
md: {
padding: "4px 16px"
},
mdLabel: {
fontSize: 16
}
};
const useStyles = makeStyles(
theme => ({
label: {
fontWeight: theme.typography.fontWeightBold,
textTransform: "uppercase"
},
root: {
borderRadius: 22,
display: "inline-block"
},
...statusChipStyles
}),
{ name: "StatusChip" }
);
const StatusChip: React.FC<StatusChipProps> = props => {
const { status = StatusType.NEUTRAL, size = "lg", label } = props;
const classes = useStyles(props);
if (!label) {
return null;
}
return (
<div className={classNames(classes.root, classes[status], classes[size])}>
<Typography
className={classNames(
classes.label,
classes[`${status}Label`],
classes[`${size}Label`]
)}
>
{label}
</Typography>
</div>
);
};
export default StatusChip;

View file

@ -1 +0,0 @@
export { default } from "./StatusChip";

View file

@ -1,6 +0,0 @@
export enum StatusType {
NEUTRAL = "neutral",
ERROR = "error",
ALERT = "alert",
SUCCESS = "success"
}

View file

@ -1,102 +0,0 @@
import { Typography } from "@material-ui/core";
import grey from "@material-ui/core/colors/grey";
import yellow from "@material-ui/core/colors/yellow";
import { makeStyles } from "@saleor/macaw-ui";
import Label from "@saleor/orders/components/OrderHistory/Label";
import classNames from "classnames";
import React from "react";
export const useStyles = makeStyles(
theme => {
const dot = {
borderRadius: "100%",
height: 8,
minHeight: 8,
width: 8,
minWidth: 8
};
return {
dot,
container: {
display: "flex",
flexDirection: "row",
alignItems: "center"
},
containerVertical: {
alignItems: "flex-start"
},
textContainer: {
marginLeft: theme.spacing(1),
display: "flex",
flexDirection: "column",
width: "100%"
},
dotVertical: {
marginTop: theme.spacing(1)
},
alertDot: {
backgroundColor: yellow[500],
...dot
},
errorDot: {
backgroundColor: theme.palette.error.main,
...dot
},
neutralDot: {
backgroundColor: grey[300],
...dot
},
successDot: {
backgroundColor: theme.palette.primary.main,
...dot
},
span: {
display: "inline"
}
};
},
{ name: "StatusLabel" }
);
export interface StatusLabelProps {
label: string | React.ReactNode;
status: "success" | "alert" | "neutral" | "error" | undefined;
subtitle?: string;
className?: string;
}
const StatusLabel: React.FC<StatusLabelProps> = ({
className,
label,
status,
subtitle
}) => {
const classes = useStyles({});
return (
<div
className={classNames({
[classes.container]: true,
[classes.containerVertical]: !!subtitle
})}
>
<div
className={classNames({
[className]: true,
[classes.dotVertical]: !!subtitle,
[classes.successDot]: status === "success",
[classes.alertDot]: status === "alert",
[classes.neutralDot]: status === "neutral",
[classes.errorDot]: status === "error"
})}
></div>
<div className={classes.textContainer}>
<Typography>{label}</Typography>
{subtitle && <Label text={subtitle} />}
</div>
</div>
);
};
export default StatusLabel;

View file

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

View file

@ -1,16 +0,0 @@
import { defineMessages } from "react-intl";
export const statusLabelMessages = defineMessages({
active: {
defaultMessage: "Active",
description: "status label active"
},
inactive: {
defaultMessage: "Inactive",
description: "status label inactive"
},
deactivated: {
defaultMessage: "Deactivated",
description: "status label deactivated"
}
});

View file

@ -10,8 +10,7 @@ import { DateTime } from "@saleor/components/Date";
import Money from "@saleor/components/Money";
import ResponsiveTable from "@saleor/components/ResponsiveTable";
import Skeleton from "@saleor/components/Skeleton";
import StatusLabel from "@saleor/components/StatusLabel";
import { Button, makeStyles } from "@saleor/macaw-ui";
import { Button, makeStyles, Pill } from "@saleor/macaw-ui";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
@ -120,8 +119,8 @@ const CustomerOrders: React.FC<CustomerOrdersProps> = props => {
<TableCell>
{maybe(() => order.paymentStatus.status) !== undefined ? (
order.paymentStatus.status === null ? null : (
<StatusLabel
status={order.paymentStatus.status}
<Pill
color={order.paymentStatus.status}
label={order.paymentStatus.localized}
/>
)

View file

@ -27,7 +27,6 @@ export interface SaleProductsProps extends ListProps, ListActions {
products:
| SaleDetails_sale_products_edges_node[]
| VoucherDetails_voucher_products_edges_node[];
channelsCount: number;
onProductAssign: () => void;
onProductUnassign: (id: string) => void;
}
@ -36,7 +35,6 @@ const numberOfColumns = 5;
const DiscountProducts: React.FC<SaleProductsProps> = props => {
const {
channelsCount,
products,
disabled,
pageInfo,
@ -150,7 +148,6 @@ const DiscountProducts: React.FC<SaleProductsProps> = props => {
"-"
) : product?.channelListings !== undefined ? (
<ChannelsAvailabilityDropdown
allChannelsCount={channelsCount}
channels={product?.channelListings}
/>
) : (

View file

@ -320,7 +320,6 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
onRowClick={onProductClick}
pageInfo={pageInfo}
products={mapEdgesToItems(sale?.products)}
channelsCount={allChannelsCount}
isChecked={isChecked}
selected={selected}
toggle={toggle}

View file

@ -350,7 +350,6 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
onRowClick={onProductClick}
pageInfo={pageInfo}
products={mapEdgesToItems(voucher.products)}
channelsCount={allChannelsCount}
isChecked={isChecked}
selected={selected}
toggle={toggle}

View file

@ -9,8 +9,6 @@ import {
import { IntlShape } from "react-intl";
import { MultiAutocompleteChoiceType } from "./components/MultiAutocompleteSelectField";
import { StatusType } from "./components/StatusChip/types";
import { StatusLabelProps } from "./components/StatusLabel";
import { AddressType, AddressTypeInput } from "./customers/types";
import {
commonStatusMessages,
@ -21,6 +19,7 @@ import { OrderDetails_order_shippingAddress } from "./orders/types/OrderDetails"
import {
MutationResultAdditionalProps,
PartialMutationProviderOutput,
StatusType,
UserError
} from "./types";
import {
@ -81,7 +80,7 @@ export const removeDoubleSlashes = (url: string) =>
export const transformPaymentStatus = (
status: string,
intl: IntlShape
): { localized: string; status: StatusLabelProps["status"] } => {
): { localized: string; status: StatusType } => {
switch (status) {
case PaymentChargeStatusEnum.PARTIALLY_CHARGED:
return {
@ -96,17 +95,17 @@ export const transformPaymentStatus = (
case PaymentChargeStatusEnum.PARTIALLY_REFUNDED:
return {
localized: intl.formatMessage(paymentStatusMessages.partiallyRefunded),
status: StatusType.ERROR
status: StatusType.INFO
};
case PaymentChargeStatusEnum.FULLY_REFUNDED:
return {
localized: intl.formatMessage(paymentStatusMessages.refunded),
status: StatusType.SUCCESS
status: StatusType.INFO
};
case PaymentChargeStatusEnum.PENDING:
return {
localized: intl.formatMessage(paymentStatusMessages.pending),
status: StatusType.NEUTRAL
status: StatusType.WARNING
};
case PaymentChargeStatusEnum.REFUSED:
return {
@ -143,7 +142,7 @@ export const transformOrderStatus = (
case OrderStatus.PARTIALLY_FULFILLED:
return {
localized: intl.formatMessage(orderStatusMessages.partiallyFulfilled),
status: StatusType.NEUTRAL
status: StatusType.WARNING
};
case OrderStatus.UNFULFILLED:
return {
@ -158,22 +157,22 @@ export const transformOrderStatus = (
case OrderStatus.DRAFT:
return {
localized: intl.formatMessage(orderStatusMessages.draft),
status: StatusType.ERROR
status: StatusType.INFO
};
case OrderStatus.UNCONFIRMED:
return {
localized: intl.formatMessage(orderStatusMessages.unconfirmed),
status: StatusType.NEUTRAL
status: StatusType.INFO
};
case OrderStatus.PARTIALLY_RETURNED:
return {
localized: intl.formatMessage(orderStatusMessages.partiallyReturned),
status: StatusType.NEUTRAL
status: StatusType.INFO
};
case OrderStatus.RETURNED:
return {
localized: intl.formatMessage(orderStatusMessages.returned),
status: StatusType.NEUTRAL
status: StatusType.INFO
};
}
return {

View file

@ -7,7 +7,6 @@ import Form from "@saleor/components/Form";
import Grid from "@saleor/components/Grid";
import Metadata, { MetadataFormData } from "@saleor/components/Metadata";
import PageHeader from "@saleor/components/PageHeader";
import PageTitleWithStatusChip from "@saleor/components/PageTitleWithStatusChip";
import Savebar from "@saleor/components/Savebar";
import Skeleton from "@saleor/components/Skeleton";
import { SubmitPromise } from "@saleor/hooks/useForm";
@ -36,8 +35,8 @@ import OrderFulfilledProductsCard from "../OrderFulfilledProductsCard";
import OrderHistory, { FormData as HistoryFormData } from "../OrderHistory";
import OrderInvoiceList from "../OrderInvoiceList";
import OrderPayment from "../OrderPayment/OrderPayment";
import OrderStatusChip from "../OrderStatusChip/OrderStatusChip";
import OrderUnfulfilledProductsCard from "../OrderUnfulfilledProductsCard";
import Title from "./Title";
import { filteredConditionalItems, hasAnyItemsReplaceable } from "./utils";
const useStyles = makeStyles(
@ -224,11 +223,7 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
<PageHeader
className={classes.header}
inline
title={
<PageTitleWithStatusChip title={order?.number}>
<OrderStatusChip order={order} />
</PageTitleWithStatusChip>
}
title={<Title order={order} />}
cardMenu={<CardMenu outlined menuItems={selectCardMenuItems} />}
/>
<div className={classes.date}>

View file

@ -0,0 +1,48 @@
import { makeStyles, Pill } from "@saleor/macaw-ui";
import { transformOrderStatus } from "@saleor/misc";
import { OrderDetails_order } from "@saleor/orders/types/OrderDetails";
import React from "react";
import { useIntl } from "react-intl";
export interface TitleProps {
order?: OrderDetails_order;
}
const useStyles = makeStyles(
theme => ({
container: {
alignItems: "center",
display: "flex"
},
statusContainer: {
marginLeft: theme.spacing(2)
}
}),
{ name: "OrderDetailsTitle" }
);
const Title: React.FC<TitleProps> = props => {
const intl = useIntl();
const classes = useStyles(props);
const { order } = props;
if (!order) {
return null;
}
const { localized, status } = transformOrderStatus(order.status, intl);
return (
<div className={classes.container}>
{intl.formatMessage(
{ defaultMessage: "Order #{orderNumber}" },
{ orderNumber: order?.number }
)}
<div className={classes.statusContainer}>
<Pill label={localized} color={status} />
</div>
</div>
);
};
export default Title;

View file

@ -10,10 +10,9 @@ import { DateTime } from "@saleor/components/Date";
import Money from "@saleor/components/Money";
import ResponsiveTable from "@saleor/components/ResponsiveTable";
import Skeleton from "@saleor/components/Skeleton";
import StatusLabel from "@saleor/components/StatusLabel";
import TableCellHeader from "@saleor/components/TableCellHeader";
import TablePagination from "@saleor/components/TablePagination";
import { makeStyles } from "@saleor/macaw-ui";
import { makeStyles, Pill } from "@saleor/macaw-ui";
import {
maybe,
renderCollection,
@ -52,11 +51,15 @@ const useStyles = makeStyles(
},
colTotal: {}
},
pill: {
maxWidth: "100%",
...overflowing
},
colCustomer: overflowing,
colDate: {},
colFulfillment: overflowing,
colFulfillment: {},
colNumber: {},
colPayment: overflowing,
colPayment: {},
colTotal: {
textAlign: "right"
},
@ -227,8 +230,9 @@ export const OrderList: React.FC<OrderListProps> = props => {
<TableCell className={classes.colPayment}>
{maybe(() => order.paymentStatus.status) !== undefined ? (
order.paymentStatus.status === null ? null : (
<StatusLabel
status={order.paymentStatus.status}
<Pill
className={classes.pill}
color={order.paymentStatus.status}
label={order.paymentStatus.localized}
/>
)
@ -238,8 +242,9 @@ export const OrderList: React.FC<OrderListProps> = props => {
</TableCell>
<TableCell className={classes.colFulfillment}>
{maybe(() => order.status) ? (
<StatusLabel
status={order.status.status}
<Pill
className={classes.pill}
color={order.status.status}
label={order.status.localized}
/>
) : (

View file

@ -1,10 +1,10 @@
import { Card, CardContent } from "@material-ui/core";
import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer";
import CardTitle from "@saleor/components/CardTitle";
import { Hr } from "@saleor/components/Hr";
import Money from "@saleor/components/Money";
import Skeleton from "@saleor/components/Skeleton";
import StatusLabel from "@saleor/components/StatusLabel";
import { Button, makeStyles } from "@saleor/macaw-ui";
import { Button, makeStyles, Pill } from "@saleor/macaw-ui";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
@ -15,7 +15,7 @@ import {
OrderStatus
} from "../../../types/globalTypes";
import { OrderDetails_order } from "../../types/OrderDetails";
import messages from "./messages";
import { orderPaymentMessages, paymentButtonMessages } from "./messages";
import {
extractOrderGiftCardUsedAmount,
extractOutstandingBalance,
@ -38,6 +38,9 @@ const useStyles = makeStyles(
},
totalRow: {
fontWeight: 600
},
titleContainer: {
display: "flex"
}
}),
{ name: "OrderPayment" }
@ -65,10 +68,7 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
const canMarkAsPaid = maybe(() => order.actions, []).includes(
OrderAction.MARK_AS_PAID
);
const payment = transformPaymentStatus(
maybe(() => order.paymentStatus),
intl
);
const payment = transformPaymentStatus(order?.paymentStatus, intl);
const refundedAmount = extractRefundedAmount(order);
const outstandingBalance = extractOutstandingBalance(order);
const usedGiftCardAmount = extractOrderGiftCardUsedAmount(order);
@ -84,10 +84,10 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
if (order.shippingMethodName === null) {
return order.collectionPointName == null ? (
<FormattedMessage {...messages.orderPaymentShippingDoesNotApply} />
<FormattedMessage {...orderPaymentMessages.shippingDoesNotApply} />
) : (
<FormattedMessage
{...messages.orderPaymentClickAndCollectShippingMethod}
{...orderPaymentMessages.clickAndCollectShippingMethod}
/>
);
}
@ -98,20 +98,21 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
<Card>
<CardTitle
title={
maybe(() => order.paymentStatus) === undefined ? (
!order?.paymentStatus ? (
<Skeleton />
) : (
<div className={classes.header}>
<StatusLabel label={payment.localized} status={payment.status} />
<div className={classes.titleContainer}>
<FormattedMessage {...orderPaymentMessages.paymentTitle} />
<HorizontalSpacer spacing={2} />
<Pill label={payment.localized} color={payment.status} />
</div>
{maybe(() => order.status) !== OrderStatus.CANCELED &&
(canCapture || canRefund || canVoid || canMarkAsPaid) && (
<div>
{canCapture && (
<Button variant="tertiary" onClick={onCapture}>
<FormattedMessage
defaultMessage="Capture"
description="capture payment, button"
/>
<FormattedMessage {...paymentButtonMessages.capture} />
</Button>
)}
{canRefund && (
@ -120,25 +121,18 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
onClick={onRefund}
data-test-id="refund-button"
>
<FormattedMessage
defaultMessage="Refund"
description="button"
/>
<FormattedMessage {...paymentButtonMessages.refund} />
</Button>
)}
{canVoid && (
<Button variant="tertiary" onClick={onVoid}>
<FormattedMessage
defaultMessage="Void"
description="void payment, button"
/>
<FormattedMessage {...paymentButtonMessages.void} />
</Button>
)}
{canMarkAsPaid && (
<Button variant="tertiary" onClick={onMarkAsPaid}>
<FormattedMessage
defaultMessage="Mark as paid"
description="order, button"
{...paymentButtonMessages.markAsPaid}
/>
</Button>
)}
@ -153,18 +147,14 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
<tbody>
<tr>
<td>
<FormattedMessage
defaultMessage="Subtotal"
description="order subtotal price"
/>
<FormattedMessage {...orderPaymentMessages.subtotal} />
</td>
<td>
{maybe(() => order.lines) === undefined ? (
<Skeleton />
) : (
<FormattedMessage
defaultMessage="{quantity} items"
description="ordered products"
{...orderPaymentMessages.itemCount}
values={{
quantity: order.lines
.map(line => line.quantity)
@ -183,22 +173,15 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
</tr>
<tr>
<td>
<FormattedMessage defaultMessage="Taxes" />
<FormattedMessage {...orderPaymentMessages.taxes} />
</td>
<td>
{maybe(() => order.total.tax) === undefined ? (
<Skeleton />
) : order.total.tax.amount > 0 ? (
intl.formatMessage({
defaultMessage: "VAT included",
description: "vat included in order price"
})
intl.formatMessage(orderPaymentMessages.vatIncluded)
) : (
intl.formatMessage({
defaultMessage: "does not apply",
description: "vat not included in order price",
id: "orderPaymentVATDoesNotApply"
})
intl.formatMessage(orderPaymentMessages.vatNotIncluded)
)}
</td>
<td className={classes.textRight}>
@ -211,10 +194,7 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
</tr>
<tr>
<td>
<FormattedMessage
defaultMessage="Shipping"
description="order shipping method name"
/>
<FormattedMessage {...orderPaymentMessages.shipping} />
</td>
<td>{getDeliveryMethodName(order)}</td>
<td className={classes.textRight}>
@ -228,22 +208,13 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
{order?.discounts?.map(discount => (
<tr>
<td>
<FormattedMessage
defaultMessage="Discount"
description="order discount"
/>
<FormattedMessage {...orderPaymentMessages.discount} />
</td>
<td>
{discount.type === OrderDiscountType.MANUAL ? (
<FormattedMessage
defaultMessage="Staff added"
description="staff added type order discount"
/>
<FormattedMessage {...orderPaymentMessages.staffAdded} />
) : (
<FormattedMessage
defaultMessage="Voucher"
description="voucher type order discount"
/>
<FormattedMessage {...orderPaymentMessages.voucher} />
)}
</td>
<td className={classes.textRight}>
@ -253,10 +224,7 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
))}
<tr className={classes.totalRow}>
<td>
<FormattedMessage
defaultMessage="Total"
description="order total price"
/>
<FormattedMessage {...orderPaymentMessages.total} />
</td>
<td />
<td className={classes.textRight}>
@ -278,8 +246,7 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
<tr>
<td>
<FormattedMessage
defaultMessage="Paid with Gift Card"
description="order payment"
{...orderPaymentMessages.paidWithGiftCard}
/>
</td>
<td className={classes.textRight}>
@ -294,10 +261,7 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
)}
<tr>
<td>
<FormattedMessage
defaultMessage="Preauthorized amount"
description="order payment"
/>
<FormattedMessage {...orderPaymentMessages.preauthorized} />
</td>
<td className={classes.textRight}>
{maybe(() => order.totalAuthorized.amount) === undefined ? (
@ -309,10 +273,7 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
</tr>
<tr>
<td>
<FormattedMessage
defaultMessage="Captured amount"
description="order payment"
/>
<FormattedMessage {...orderPaymentMessages.captured} />
</td>
<td className={classes.textRight}>
{maybe(() => order.totalCaptured.amount) === undefined ? (
@ -324,10 +285,7 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
</tr>
<tr>
<td>
<FormattedMessage
defaultMessage="Refunded amount"
description="order payment"
/>
<FormattedMessage {...orderPaymentMessages.refunded} />
</td>
<td className={classes.textRight}>
{refundedAmount?.amount === undefined ? (
@ -339,10 +297,7 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
</tr>
<tr className={classes.totalRow}>
<td>
<FormattedMessage
defaultMessage="Outstanding Balance"
description="order payment"
/>
<FormattedMessage {...orderPaymentMessages.outstanding} />
</td>
<td className={classes.textRight}>
{outstandingBalance?.amount === undefined ? (

View file

@ -1,14 +1,99 @@
import { defineMessages } from "react-intl";
const messages = defineMessages({
orderPaymentClickAndCollectShippingMethod: {
export const orderPaymentMessages = defineMessages({
clickAndCollectShippingMethod: {
defaultMessage: "click&collect",
description: "OrderPayment click&collect shipping method"
},
orderPaymentShippingDoesNotApply: {
shippingDoesNotApply: {
defaultMessage: "does not apply",
description: "OrderPayment does not require shipping"
},
paymentTitle: {
defaultMessage: "Payment status",
description: "Payment card title"
},
subtotal: {
defaultMessage: "Subtotal",
description: "order subtotal price"
},
itemCount: {
defaultMessage: "{quantity} items",
description: "ordered products"
},
taxes: {
defaultMessage: "Taxes"
},
vatIncluded: {
defaultMessage: "VAT included",
description: "vat included in order price"
},
vatNotIncluded: {
defaultMessage: "does not apply",
description: "vat not included in order price",
id: "orderPaymentVATDoesNotApply"
},
shipping: {
defaultMessage: "Shipping",
description: "order shipping method name"
},
shippingNotApplicable: {
defaultMessage: "does not apply",
description: "order does not require shipping"
},
discount: {
defaultMessage: "Discount",
description: "order discount"
},
staffAdded: {
defaultMessage: "Staff added",
description: "staff added type order discount"
},
voucher: {
defaultMessage: "Voucher",
description: "voucher type order discount"
},
total: {
defaultMessage: "Total",
description: "order total price"
},
preauthorized: {
defaultMessage: "Preauthorized amount",
description: "order payment"
},
captured: {
defaultMessage: "Captured amount",
description: "order payment"
},
refunded: {
defaultMessage: "Refunded amount",
description: "order payment"
},
outstanding: {
defaultMessage: "Outstanding Balance",
description: "order payment"
},
paidWithGiftCard: {
defaultMessage: "Paid with Gift Card",
description: "order payment"
}
});
export default messages;
export const paymentButtonMessages = defineMessages({
capture: {
defaultMessage: "Capture",
description: "capture payment, button"
},
refund: {
defaultMessage: "Refund",
description: "button"
},
void: {
defaultMessage: "Refund",
description: "button"
},
markAsPaid: {
defaultMessage: "Mark as paid",
description: "order, button"
}
});

View file

@ -1,8 +1,7 @@
import { Typography } from "@material-ui/core";
import DefaultCardTitle from "@saleor/components/CardTitle";
import { StatusType } from "@saleor/components/StatusChip/types";
import StatusLabel from "@saleor/components/StatusLabel";
import { makeStyles } from "@saleor/macaw-ui";
import { makeStyles, Pill } from "@saleor/macaw-ui";
import { StatusType } from "@saleor/types";
import { FulfillmentStatus } from "@saleor/types/globalTypes";
import camelCase from "lodash/camelCase";
import React from "react";
@ -14,7 +13,8 @@ const useStyles = makeStyles(
theme => ({
title: {
width: "100%",
display: "flex"
display: "flex",
justifyContent: "space-between"
},
orderNumber: {
display: "inline",
@ -22,6 +22,7 @@ const useStyles = makeStyles(
},
warehouseName: {
float: "right",
alignSelf: "center",
color: theme.palette.text.secondary,
margin: `auto ${theme.spacing(1)} auto auto`
}
@ -89,19 +90,19 @@ const selectStatus = (status: CardTitleStatus) => {
case FulfillmentStatus.FULFILLED:
return StatusType.SUCCESS;
case FulfillmentStatus.REFUNDED:
return StatusType.NEUTRAL;
return StatusType.INFO;
case FulfillmentStatus.RETURNED:
return StatusType.NEUTRAL;
return StatusType.INFO;
case FulfillmentStatus.REPLACED:
return StatusType.NEUTRAL;
return StatusType.INFO;
case FulfillmentStatus.REFUNDED_AND_RETURNED:
return StatusType.NEUTRAL;
return StatusType.INFO;
case FulfillmentStatus.WAITING_FOR_APPROVAL:
return StatusType.ALERT;
return StatusType.WARNING;
case FulfillmentStatus.CANCELED:
return StatusType.ERROR;
default:
return StatusType.ALERT;
return StatusType.WARNING;
}
};
@ -130,36 +131,40 @@ const CardTitle: React.FC<CardTitleProps> = ({
);
const title = (
<div className={classes.title}>
<>
{intl.formatMessage(messageForStatus, {
fulfillmentName,
quantity: totalQuantity
})}
<Typography className={classes.orderNumber} variant="body1">
{fulfillmentName}
</Typography>
{!!warehouseName && (
<Typography className={classes.warehouseName} variant="caption">
<FormattedMessage
{...messages.fulfilledFrom}
values={{
warehouseName
}}
/>
{fulfillmentName && (
<Typography className={classes.orderNumber} variant="body1">
{fulfillmentName}
</Typography>
)}
</div>
</>
);
return (
<DefaultCardTitle
toolbar={toolbar}
title={
withStatus ? (
<StatusLabel label={title} status={selectStatus(status)} />
) : (
title
)
<div className={classes.title}>
{withStatus ? (
<Pill label={title} color={selectStatus(status)} />
) : (
title
)}
{!!warehouseName && (
<Typography className={classes.warehouseName} variant="caption">
<FormattedMessage
{...messages.fulfilledFrom}
values={{
warehouseName
}}
/>
</Typography>
)}
</div>
}
/>
);

View file

@ -1,23 +0,0 @@
import StatusChip from "@saleor/components/StatusChip";
import { transformOrderStatus } from "@saleor/misc";
import { OrderDetails_order } from "@saleor/orders/types/OrderDetails";
import React from "react";
import { useIntl } from "react-intl";
interface OrderStatusChipProps {
order?: OrderDetails_order;
}
const OrderStatusChip: React.FC<OrderStatusChipProps> = ({ order }) => {
const intl = useIntl();
if (!order) {
return null;
}
const { localized, status } = transformOrderStatus(order.status, intl);
return <StatusChip size="md" status={status} label={localized} />;
};
export default OrderStatusChip;

View file

@ -8,11 +8,10 @@ import {
import Checkbox from "@saleor/components/Checkbox";
import ResponsiveTable from "@saleor/components/ResponsiveTable";
import Skeleton from "@saleor/components/Skeleton";
import StatusLabel from "@saleor/components/StatusLabel";
import TableCellHeader from "@saleor/components/TableCellHeader";
import TableHead from "@saleor/components/TableHead";
import TablePagination from "@saleor/components/TablePagination";
import { makeStyles } from "@saleor/macaw-ui";
import { makeStyles, Pill } from "@saleor/macaw-ui";
import { maybe, renderCollection } from "@saleor/misc";
import { PageListUrlSortField } from "@saleor/pages/urls";
import { ListActions, ListProps, SortPage } from "@saleor/types";
@ -179,7 +178,7 @@ const PageList: React.FC<PageListProps> = props => {
<TableCell className={classes.colVisibility}>
{maybe<React.ReactNode>(
() => (
<StatusLabel
<Pill
label={
page.isPublished
? intl.formatMessage({
@ -191,7 +190,7 @@ const PageList: React.FC<PageListProps> = props => {
description: "page status"
})
}
status={page.isPublished ? "success" : "error"}
color={page.isPublished ? "success" : "error"}
/>
),
<Skeleton />

View file

@ -1,27 +1,12 @@
import { Typography } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";
import StatusLabel from "@saleor/components/StatusLabel";
import { statusLabelMessages } from "@saleor/components/StatusLabel/messages";
import { Pill } from "@saleor/macaw-ui";
import { Plugins_plugins_edges_node } from "@saleor/plugins/types/Plugins";
import { isPluginGlobal } from "@saleor/plugins/views/utils";
import React from "react";
import { useIntl } from "react-intl";
import { pluginStatusMessages } from "./messages";
import { pluginAvailabilityStatusMessages as messages } from "./messages";
import {
getActiveChannelConfigsCount,
getAllChannelConfigsCount
} from "./utils";
const useStyles = makeStyles(
() => ({
horizontalContainer: {
display: "flex",
flexDirection: "row"
}
}),
{ name: "ChannelStatusLabel" }
);
import { getActiveChannelConfigsCount } from "./utils";
interface PluginAvailabilityStatusProps {
plugin: Plugins_plugins_edges_node;
@ -30,7 +15,6 @@ interface PluginAvailabilityStatusProps {
const PluginAvailabilityStatus: React.FC<PluginAvailabilityStatusProps> = ({
plugin: { globalConfiguration, channelConfigurations }
}) => {
const classes = useStyles({});
const intl = useIntl();
const isGlobalPlugin = isPluginGlobal(globalConfiguration);
@ -45,26 +29,21 @@ const PluginAvailabilityStatus: React.FC<PluginAvailabilityStatusProps> = ({
const globalPluginLabel = intl.formatMessage(
isStatusActive
? statusLabelMessages.active
: statusLabelMessages.deactivated
? pluginStatusMessages.active
: pluginStatusMessages.deactivated
);
return (
<StatusLabel
<Pill
label={
isGlobalPlugin ? (
globalPluginLabel
) : (
<div className={classes.horizontalContainer}>
<Typography>
{`${intl.formatMessage(messages.channelTitle, {
activeChannelsCount
})}/${getAllChannelConfigsCount(channelConfigurations)}`}
</Typography>
</div>
)
isGlobalPlugin
? globalPluginLabel
: intl.formatMessage(messages.channelTitle, {
activeChannelsCount
})
}
status={isStatusActive ? "success" : "error"}
color={isStatusActive ? "success" : "error"}
onClick={() => null} // required for dashed border
/>
);
};

View file

@ -1,76 +0,0 @@
import { CardContent, Divider, Typography } from "@material-ui/core";
import CardSpacer from "@saleor/components/CardSpacer";
import CollectionWithDividers from "@saleor/components/CollectionWithDividers";
import StatusLabel from "@saleor/components/StatusLabel";
import { statusLabelMessages } from "@saleor/components/StatusLabel/messages";
import { PluginBaseFragment } from "@saleor/fragments/types/PluginBaseFragment";
import { makeStyles } from "@saleor/macaw-ui";
import React from "react";
import { useIntl } from "react-intl";
import { channelConfigPluginMessages as messages } from "../messages";
import {
getActiveChannelConfigsCount,
getAllChannelConfigsCount
} from "../utils";
import ScrollableContent from "./ScrollableContent";
const useStyles = makeStyles(
theme => ({
itemContainer: {
padding: theme.spacing(0, 4)
}
}),
{ name: "ChannelConfigPluginPopupBody" }
);
interface ChannelConfigPluginPopupBodyProps {
plugin: PluginBaseFragment;
}
const ChannelConfigPluginPopupBody: React.FC<ChannelConfigPluginPopupBodyProps> = ({
plugin: { channelConfigurations }
}) => {
const intl = useIntl();
const classes = useStyles({});
return (
<>
<CardContent>
<Typography>
{intl.formatMessage(messages.title, {
allChannelsCount: getAllChannelConfigsCount(channelConfigurations),
activeChannelsCount: getActiveChannelConfigsCount(
channelConfigurations
)
})}
</Typography>
</CardContent>
<Divider />
<ScrollableContent>
<CardSpacer />
<CollectionWithDividers
collection={channelConfigurations}
DividerComponent={CardSpacer}
renderItem={({ channel, active }) => (
<div className={classes.itemContainer}>
<StatusLabel
key={channel.id}
label={channel.name}
status={active ? "success" : "error"}
subtitle={intl.formatMessage(
active
? statusLabelMessages.active
: statusLabelMessages.inactive
)}
/>
</div>
)}
/>
<CardSpacer />
</ScrollableContent>
</>
);
};
export default ChannelConfigPluginPopupBody;

View file

@ -1,12 +1,12 @@
import { CardContent, Typography } from "@material-ui/core";
import CardSpacer from "@saleor/components/CardSpacer";
import StatusLabel from "@saleor/components/StatusLabel";
import { statusLabelMessages } from "@saleor/components/StatusLabel/messages";
import { PluginBaseFragment } from "@saleor/fragments/types/PluginBaseFragment";
import { Pill } from "@saleor/macaw-ui";
import React from "react";
import { useIntl } from "react-intl";
import { globalConfigPluginMessages as messages } from "../messages";
import { pluginStatusMessages } from "../messages";
interface GlobalConfigPluginPopupBodyProps {
plugin: PluginBaseFragment;
@ -28,10 +28,12 @@ const GlobalConfigPluginPopupBody: React.FC<GlobalConfigPluginPopupBodyProps> =
{intl.formatMessage(messages.description)}
</Typography>
<CardSpacer />
<StatusLabel
status={active ? "success" : "error"}
<Pill
color={active ? "success" : "error"}
label={intl.formatMessage(
active ? statusLabelMessages.active : statusLabelMessages.inactive
active
? pluginStatusMessages.active
: pluginStatusMessages.deactivated
)}
/>
</CardContent>

View file

@ -1,16 +1,17 @@
import { Card, Popper } from "@material-ui/core";
import ChannelsAvailabilityMenuContent from "@saleor/components/ChannelsAvailabilityMenuContent";
import { PluginBaseFragment } from "@saleor/fragments/types/PluginBaseFragment";
import { makeStyles } from "@saleor/macaw-ui";
import { isPluginGlobal } from "@saleor/plugins/views/utils";
import React from "react";
import ChannelConfigPluginPopupBody from "./ChannelConfigPluginPopupBody";
import { mapPluginsToPills } from "../utils";
import GlobalConfigPluginPopupBody from "./GlobalConfigPluginPopupBody";
const useStyles = makeStyles(
() => ({
container: {
maxWidth: 300,
maxWidth: 500,
zIndex: 1000
}
}),
@ -43,7 +44,9 @@ const PluginAvailabilityStatusPopup: React.FC<PluginAvailabilityStatusPopupProps
{isGlobalPlugin ? (
<GlobalConfigPluginPopupBody plugin={plugin} />
) : (
<ChannelConfigPluginPopupBody plugin={plugin} />
<ChannelsAvailabilityMenuContent
pills={mapPluginsToPills(plugin.channelConfigurations)}
/>
)}
</Card>
</Popper>

View file

@ -2,7 +2,8 @@ import { defineMessages } from "react-intl";
export const pluginAvailabilityStatusMessages = defineMessages({
channelTitle: {
defaultMessage: "Active in {activeChannelsCount}",
defaultMessage:
"{activeChannelsCount,plural, =0 {Deactivated} other {Active in {activeChannelsCount}}}",
description: "plugin channel availability status title"
}
});
@ -54,3 +55,14 @@ export const pluginChannelConfigurationCellMessages = defineMessages({
id: "pluginChannelConfigurationCellMessages per channel"
}
});
export const pluginStatusMessages = defineMessages({
active: {
defaultMessage: "Active",
description: "status label active"
},
deactivated: {
defaultMessage: "Deactivated",
description: "status label deactivated"
}
});

View file

@ -1,4 +1,9 @@
import { Pill } from "@saleor/components/ChannelsAvailabilityMenuContent";
import { PluginConfigurationBaseFragment } from "@saleor/fragments/types/PluginConfigurationBaseFragment";
import { PillColor } from "@saleor/macaw-ui";
import { MessageDescriptor } from "react-intl";
import { pluginStatusMessages } from "./messages";
export const getAllChannelConfigsCount = (
channelConfigurations: PluginConfigurationBaseFragment[]
@ -7,3 +12,22 @@ export const getAllChannelConfigsCount = (
export const getActiveChannelConfigsCount = (
channelConfigurations: PluginConfigurationBaseFragment[]
) => channelConfigurations?.filter(({ active }) => !!active).length;
export const getPluginStatusLabel = (
channelData: PluginConfigurationBaseFragment
): MessageDescriptor =>
channelData.active
? pluginStatusMessages.active
: pluginStatusMessages.deactivated;
export const getPluginStatusColor = (
channelData: PluginConfigurationBaseFragment
): PillColor => (channelData.active ? "success" : "error");
export const mapPluginsToPills = (
channelConfigurations: PluginConfigurationBaseFragment[]
): Pill[] =>
channelConfigurations.map(channel => ({
channel: channel.channel,
color: getPluginStatusColor(channel),
label: getPluginStatusLabel(channel)
}));

View file

@ -1,5 +1,4 @@
import { IFilter, IFilterElement } from "@saleor/components/Filter";
import { statusLabelMessages } from "@saleor/components/StatusLabel/messages";
import { sectionNames } from "@saleor/intl";
import { AutocompleteFilterOpts, FilterOpts } from "@saleor/types";
import { PluginConfigurationType } from "@saleor/types/globalTypes";
@ -10,7 +9,10 @@ import {
} from "@saleor/utils/filters/fields";
import { defineMessages, IntlShape } from "react-intl";
import { pluginChannelConfigurationCellMessages } from "../PluginsList/messages";
import {
pluginChannelConfigurationCellMessages,
pluginStatusMessages
} from "../PluginsList/messages";
export enum PluginFilterKeys {
active = "active",
@ -58,8 +60,8 @@ export function createFilterStructure(
intl.formatMessage(messages.channelStatusSectionSubtitle),
opts.isActive.value,
{
negative: intl.formatMessage(statusLabelMessages.inactive),
positive: intl.formatMessage(statusLabelMessages.active)
negative: intl.formatMessage(pluginStatusMessages.deactivated),
positive: intl.formatMessage(pluginStatusMessages.active)
}
)
},

View file

@ -5,8 +5,11 @@ import {
TableRow,
Typography
} from "@material-ui/core";
import AvailabilityStatusLabel from "@saleor/components/AvailabilityStatusLabel";
import { ChannelsAvailabilityDropdown } from "@saleor/components/ChannelsAvailabilityDropdown";
import {
getChannelAvailabilityColor,
getChannelAvailabilityLabel
} from "@saleor/components/ChannelsAvailabilityDropdown/utils";
import Checkbox from "@saleor/components/Checkbox";
import Date from "@saleor/components/Date";
import MoneyRange from "@saleor/components/MoneyRange";
@ -20,7 +23,7 @@ import TablePagination from "@saleor/components/TablePagination";
import TooltipTableCellHeader from "@saleor/components/TooltipTableCellHeader";
import { commonTooltipMessages } from "@saleor/components/TooltipTableCellHeader/messages";
import { ProductListColumns } from "@saleor/config";
import { makeStyles } from "@saleor/macaw-ui";
import { makeStyles, Pill } from "@saleor/macaw-ui";
import { maybe, renderCollection } from "@saleor/misc";
import {
getAttributeIdFromColumnValue,
@ -39,7 +42,7 @@ import classNames from "classnames";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { columnsMessages, messages } from "./messages";
import { columnsMessages } from "./messages";
const useStyles = makeStyles(
theme => ({
@ -116,13 +119,11 @@ interface ProductListProps
gridAttributes: GridAttributes_grid_edges_node[];
products: ProductList_products_edges_node[];
loading: boolean;
channelsCount: number;
}
export const ProductList: React.FC<ProductListProps> = props => {
const {
activeAttributeSortId,
channelsCount,
settings,
disabled,
isChecked,
@ -397,20 +398,19 @@ export const ProductList: React.FC<ProductListProps> = props => {
!!product?.channelListings?.length
}
>
{(!product && <Skeleton />) ||
(!product?.channelListings?.length && "-") ||
(product?.channelListings !== undefined && channel ? (
<AvailabilityStatusLabel
channel={channel}
messages={messages}
{(product &&
(channel ? (
<Pill
label={intl.formatMessage(
getChannelAvailabilityLabel(channel)
)}
color={getChannelAvailabilityColor(channel)}
/>
) : (
<ChannelsAvailabilityDropdown
allChannelsCount={channelsCount}
channels={product?.channelListings}
showStatus
/>
))}
))) ?? <Skeleton />}
</TableCell>
</DisplayColumn>
{gridAttributesFromSettings.map(gridAttribute => (

View file

@ -245,7 +245,6 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
loading={loading}
gridAttributes={gridAttributes}
settings={settings}
channelsCount={channelsCount}
selectedChannelId={selectedChannelId}
onUpdateListSettings={onUpdateListSettings}
filterDependency={filterDependency}

File diff suppressed because it is too large Load diff

View file

@ -36,7 +36,6 @@ function loadStories() {
require("./stories/components/SaveFilterTabDialog");
require("./stories/components/SingleSelectField");
require("./stories/components/Skeleton");
require("./stories/components/StatusLabel");
require("./stories/components/TablePagination");
require("./stories/components/Timeline");
require("./stories/components/Weight");

View file

@ -1,19 +0,0 @@
import StatusLabel from "@saleor/components/StatusLabel";
import { storiesOf } from "@storybook/react";
import React from "react";
import CardDecorator from "../../CardDecorator";
import Decorator from "../../Decorator";
storiesOf("Generics / StatusLabel", module)
.addDecorator(CardDecorator)
.addDecorator(Decorator)
.add("when success", () => (
<StatusLabel label="Example label" status="success" />
))
.add("when neutral", () => (
<StatusLabel label="Example label" status="neutral" />
))
.add("when error", () => (
<StatusLabel label="Example label" status="error" />
));

View file

@ -217,3 +217,10 @@ export interface AutocompleteFilterOpts
}
export type Ids = string[];
export enum StatusType {
INFO = "info",
ERROR = "error",
WARNING = "warning",
SUCCESS = "success"
}