Refactor order section translations (#123)

* Refactor translations in order section

* Ensure ID uniqueness

* Update pot file
This commit is contained in:
Dominik Żegleń 2019-08-26 19:44:42 +02:00 committed by dominik-zeglen
parent f78e5aea31
commit 3d2d56e227
36 changed files with 3045 additions and 1001 deletions

File diff suppressed because it is too large Load diff

View file

@ -50,45 +50,41 @@ export const commonMessages = defineMessages({
}); });
export const buttonMessages = defineMessages({ export const buttonMessages = defineMessages({
back: {
defaultMessage: "Back",
description: "button"
},
cancel: { cancel: {
defaultMessage: "Cancel", defaultMessage: "Cancel",
description: "button", description: "button"
id: "cancel"
}, },
confirm: { confirm: {
defaultMessage: "Confirm", defaultMessage: "Confirm",
description: "button", description: "button"
id: "confirm"
}, },
edit: { edit: {
defaultMessage: "Edit", defaultMessage: "Edit",
description: "button", description: "button"
id: "edit"
}, },
manage: { manage: {
defaultMessage: "Manage", defaultMessage: "Manage",
description: "button", description: "button"
id: "manage"
}, },
remove: { remove: {
defaultMessage: "Remove", defaultMessage: "Remove",
description: "button", description: "button"
id: "remove"
}, },
save: { save: {
defaultMessage: "Save", defaultMessage: "Save",
description: "button", description: "button"
id: "save"
}, },
show: { show: {
defaultMessage: "Show", defaultMessage: "Show",
description: "button", description: "button"
id: "show"
}, },
undo: { undo: {
defaultMessage: "Undo", defaultMessage: "Undo",
description: "button", description: "button"
id: "undo"
} }
}); });
@ -113,10 +109,18 @@ export const sectionNames = defineMessages({
defaultMessage: "Customers", defaultMessage: "Customers",
description: "customers section name" description: "customers section name"
}, },
draftOrders: {
defaultMessage: "Draft Orders",
description: "draft orders section name"
},
navigation: { navigation: {
defaultMessage: "Navigation", defaultMessage: "Navigation",
description: "navigation section name" description: "navigation section name"
}, },
orders: {
defaultMessage: "Orders",
description: "orders section name"
},
pages: { pages: {
defaultMessage: "Pages", defaultMessage: "Pages",
description: "pages section name" description: "pages section name"

View file

@ -5,6 +5,7 @@ import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle"; import DialogTitle from "@material-ui/core/DialogTitle";
import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import AddressEdit from "@saleor/components/AddressEdit"; import AddressEdit from "@saleor/components/AddressEdit";
import ConfirmButton, { import ConfirmButton, {
@ -12,10 +13,10 @@ import ConfirmButton, {
} from "@saleor/components/ConfirmButton"; } from "@saleor/components/ConfirmButton";
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
import useStateFromProps from "@saleor/hooks/useStateFromProps"; import useStateFromProps from "@saleor/hooks/useStateFromProps";
import { buttonMessages } from "@saleor/intl";
import { maybe } from "@saleor/misc"; import { maybe } from "@saleor/misc";
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
import { AddressTypeInput } from "../../../customers/types"; import { AddressTypeInput } from "../../../customers/types";
import i18n from "../../../i18n";
import { UserError } from "../../../types"; import { UserError } from "../../../types";
const styles = createStyles({ const styles = createStyles({
@ -52,6 +53,7 @@ const OrderAddressEditDialog = withStyles(styles, {
onClose, onClose,
onConfirm onConfirm
}: OrderAddressEditDialogProps) => { }: OrderAddressEditDialogProps) => {
const intl = useIntl();
const [countryDisplayName, setCountryDisplayName] = useStateFromProps( const [countryDisplayName, setCountryDisplayName] = useStateFromProps(
maybe( maybe(
() => countries.find(country => address.country === country.code).label () => countries.find(country => address.country === country.code).label
@ -80,8 +82,14 @@ const OrderAddressEditDialog = withStyles(styles, {
<> <>
<DialogTitle> <DialogTitle>
{variant === "billing" {variant === "billing"
? i18n.t("Edit billing address", { context: "title" }) ? intl.formatMessage({
: i18n.t("Edit shipping address", { context: "title" })} defaultMessage: "Edit billing address",
description: "dialog header"
})
: intl.formatMessage({
defaultMessage: "Edit shipping address",
description: "dialog header"
})}
</DialogTitle> </DialogTitle>
<DialogContent className={classes.overflow}> <DialogContent className={classes.overflow}>
<AddressEdit <AddressEdit
@ -95,7 +103,7 @@ const OrderAddressEditDialog = withStyles(styles, {
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={onClose}> <Button onClick={onClose}>
{i18n.t("Cancel", { context: "button" })} <FormattedMessage {...buttonMessages.cancel} />
</Button> </Button>
<ConfirmButton <ConfirmButton
transitionState={confirmButtonState} transitionState={confirmButtonState}
@ -104,7 +112,7 @@ const OrderAddressEditDialog = withStyles(styles, {
onClick={submit} onClick={submit}
type="submit" type="submit"
> >
{i18n.t("Confirm", { context: "button" })} <FormattedMessage {...buttonMessages.confirm} />
</ConfirmButton> </ConfirmButton>
</DialogActions> </DialogActions>
</> </>

View file

@ -1,10 +1,10 @@
import DialogContentText from "@material-ui/core/DialogContentText"; import DialogContentText from "@material-ui/core/DialogContentText";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ActionDialog from "@saleor/components/ActionDialog"; import ActionDialog from "@saleor/components/ActionDialog";
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import i18n from "../../../i18n";
export interface OrderBulkCancelDialogProps { export interface OrderBulkCancelDialogProps {
confirmButtonState: ConfirmButtonTransitionState; confirmButtonState: ConfirmButtonTransitionState;
@ -17,6 +17,7 @@ export interface OrderBulkCancelDialogProps {
const OrderBulkCancelDialog: React.StatelessComponent< const OrderBulkCancelDialog: React.StatelessComponent<
OrderBulkCancelDialogProps OrderBulkCancelDialogProps
> = ({ confirmButtonState, numberOfOrders, open, onClose, onConfirm }) => { > = ({ confirmButtonState, numberOfOrders, open, onClose, onConfirm }) => {
const intl = useIntl();
const [restock, setRestock] = React.useState(true); const [restock, setRestock] = React.useState(true);
return ( return (
@ -24,23 +25,31 @@ const OrderBulkCancelDialog: React.StatelessComponent<
confirmButtonState={confirmButtonState} confirmButtonState={confirmButtonState}
open={open} open={open}
variant="delete" variant="delete"
title={i18n.t("Cancel Orders")} title={intl.formatMessage({
defaultMessage: "Cancel Orders",
description: "dialog header"
})}
onClose={onClose} onClose={onClose}
onConfirm={() => onConfirm(restock)} onConfirm={() => onConfirm(restock)}
> >
<DialogContentText <DialogContentText>
dangerouslySetInnerHTML={{ <FormattedMessage
__html: i18n.t( defaultMessage="Are you sure you want to cancel {counter, plural,
"Are you sure you want to cancel <strong>{{ number }}</strong> orders?", one {this order}
{ other {{displayQuantity} orders}
number: numberOfOrders }?"
} values={{
) counter: numberOfOrders,
}} displayQuantity: <strong>{numberOfOrders}</strong>
/> }}
/>
</DialogContentText>
<ControlledCheckbox <ControlledCheckbox
checked={restock} checked={restock}
label={i18n.t("Release all stock allocated to these orders")} label={intl.formatMessage({
defaultMessage: "Release all stock allocated to these orders",
description: "switch button"
})}
name="restock" name="restock"
onChange={event => setRestock(event.target.value)} onChange={event => setRestock(event.target.value)}
/> />

View file

@ -11,13 +11,14 @@ import {
WithStyles WithStyles
} from "@material-ui/core/styles"; } from "@material-ui/core/styles";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ConfirmButton, { import ConfirmButton, {
ConfirmButtonTransitionState ConfirmButtonTransitionState
} from "@saleor/components/ConfirmButton"; } from "@saleor/components/ConfirmButton";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
import i18n from "../../../i18n"; import { buttonMessages } from "@saleor/intl";
export interface FormData { export interface FormData {
restock: boolean; restock: boolean;
@ -50,55 +51,66 @@ const OrderCancelDialog = withStyles(styles, { name: "OrderCancelDialog" })(
open, open,
onSubmit, onSubmit,
onClose onClose
}: OrderCancelDialogProps) => ( }: OrderCancelDialogProps) => {
<Dialog onClose={onClose} open={open}> const intl = useIntl();
<Form
initial={{ return (
restock: true <Dialog onClose={onClose} open={open}>
}} <Form
onSubmit={onSubmit} initial={{
> restock: true
{({ data, change }) => { }}
return ( onSubmit={onSubmit}
<> >
<DialogTitle> {({ data, change }) => {
{i18n.t("Cancel order", { context: "title" })} return (
</DialogTitle> <>
<DialogContent> <DialogTitle>
<DialogContentText <FormattedMessage
dangerouslySetInnerHTML={{ defaultMessage="Cancel order"
__html: i18n.t( description="dialog header"
"Are you sure you want to cancel order <strong>{{ orderNumber }}</strong>?", />
{ orderNumber } </DialogTitle>
) <DialogContent>
}} <DialogContentText>
/> <FormattedMessage
<ControlledCheckbox defaultMessage="Are you sure you want to cancel order #{orderNumber}?"
checked={data.restock} values={{
label={i18n.t("Release all stock allocated to this order")} orderNumber
name="restock" }}
onChange={change} />
/> <ControlledCheckbox
</DialogContent> checked={data.restock}
<DialogActions> label={intl.formatMessage({
<Button onClick={onClose}> defaultMessage:
{i18n.t("Back", { context: "button" })} "Release all stock allocated to this order",
</Button> description: "switch button"
<ConfirmButton })}
transitionState={confirmButtonState} name="restock"
className={classes.deleteButton} onChange={change}
variant="contained" />
type="submit" </DialogContentText>
> </DialogContent>
{i18n.t("Cancel order", { context: "button" })} <DialogActions>
</ConfirmButton> <Button onClick={onClose}>
</DialogActions> <FormattedMessage {...buttonMessages.back} />
</> </Button>
); <ConfirmButton
}} transitionState={confirmButtonState}
</Form> className={classes.deleteButton}
</Dialog> variant="contained"
) type="submit"
>
<FormattedMessage {...buttonMessages.cancel} />
</ConfirmButton>
</DialogActions>
</>
);
}}
</Form>
</Dialog>
);
}
); );
OrderCancelDialog.displayName = "OrderCancelDialog"; OrderCancelDialog.displayName = "OrderCancelDialog";
export default OrderCancelDialog; export default OrderCancelDialog;

View file

@ -9,6 +9,7 @@ import {
} from "@material-ui/core/styles"; } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import ExternalLink from "@saleor/components/ExternalLink"; import ExternalLink from "@saleor/components/ExternalLink";
@ -18,10 +19,10 @@ import Link from "@saleor/components/Link";
import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField"; import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import useStateFromProps from "@saleor/hooks/useStateFromProps"; import useStateFromProps from "@saleor/hooks/useStateFromProps";
import { buttonMessages } from "@saleor/intl";
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
import { SearchCustomers_customers_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers"; import { SearchCustomers_customers_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers";
import { customerUrl } from "../../../customers/urls"; import { customerUrl } from "../../../customers/urls";
import i18n from "../../../i18n";
import { createHref, maybe } from "../../../misc"; import { createHref, maybe } from "../../../misc";
import { OrderDetails_order } from "../../types/OrderDetails"; import { OrderDetails_order } from "../../types/OrderDetails";
@ -74,6 +75,8 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
onProfileView, onProfileView,
onShippingAddressEdit onShippingAddressEdit
}: OrderCustomerProps) => { }: OrderCustomerProps) => {
const intl = useIntl();
const user = maybe(() => order.user); const user = maybe(() => order.user);
const [userDisplayName, setUserDisplayName] = useStateFromProps( const [userDisplayName, setUserDisplayName] = useStateFromProps(
@ -88,7 +91,10 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
return ( return (
<Card> <Card>
<CardTitle <CardTitle
title={i18n.t("Customer")} title={intl.formatMessage({
defaultMessage: "Customer",
description: "section header"
})}
toolbar={ toolbar={
!!canEditCustomer && ( !!canEditCustomer && (
<Button <Button
@ -97,7 +103,7 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
disabled={!onCustomerEdit} disabled={!onCustomerEdit}
onClick={toggleEditMode} onClick={toggleEditMode}
> >
{i18n.t("Edit")} {intl.formatMessage(buttonMessages.edit)}
</Button> </Button>
) )
} }
@ -133,7 +139,9 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
displayValue={userDisplayName} displayValue={userDisplayName}
fetchChoices={fetchUsers} fetchChoices={fetchUsers}
loading={loading} loading={loading}
placeholder={i18n.t("Search Customers")} placeholder={intl.formatMessage({
defaultMessage: "Search Customers"
})}
onChange={handleUserChange} onChange={handleUserChange}
name="query" name="query"
value={data.query} value={data.query}
@ -142,7 +150,9 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
}} }}
</Form> </Form>
) : user === null ? ( ) : user === null ? (
<Typography>{i18n.t("Anonymous user")}</Typography> <Typography>
<FormattedMessage defaultMessage="Anonymous user" />
</Typography>
) : ( ) : (
<> <>
<Typography className={classes.userEmail}> <Typography className={classes.userEmail}>
@ -154,14 +164,21 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
href={createHref(customerUrl(user.id))} href={createHref(customerUrl(user.id))}
onClick={onProfileView} onClick={onProfileView}
> >
{i18n.t("View Profile")} <FormattedMessage
defaultMessage="View Profile"
description="link"
/>
</Link> </Link>
</div> </div>
{/* TODO: Uncomment it after adding ability to filter {/* TODO: Uncomment it after adding ability to filter
orders by customer */} orders by customer */}
{/* <div> {/* <div>
<Link underline={false} href={}>{i18n.t("View Orders")}</Link> <Link underline={false} href={}>
</div> */} <FormattedMessage defaultMessage="View Orders"
description="link"
/>
</Link>
</div> */}
</> </>
)} )}
</CardContent> </CardContent>
@ -169,14 +186,23 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
<CardContent> <CardContent>
<div className={classes.sectionHeader}> <div className={classes.sectionHeader}>
<Typography className={classes.sectionHeaderTitle}> <Typography className={classes.sectionHeaderTitle}>
{i18n.t("Contact information")} <FormattedMessage
defaultMessage="Contact information"
description="subheader"
/>
</Typography> </Typography>
</div> </div>
{maybe(() => order.userEmail) === undefined ? ( {maybe(() => order.userEmail) === undefined ? (
<Skeleton /> <Skeleton />
) : order.userEmail === null ? ( ) : order.userEmail === null ? (
<Typography>{i18n.t("Not set")}</Typography> <Typography>
<FormattedMessage
defaultMessage="Not set"
description="customer is not set in draft order"
id="orderCustomerCustomerNotSet"
/>
</Typography>
) : ( ) : (
<ExternalLink <ExternalLink
href={`mailto:${maybe(() => order.userEmail)}`} href={`mailto:${maybe(() => order.userEmail)}`}
@ -190,7 +216,7 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
<CardContent> <CardContent>
<div className={classes.sectionHeader}> <div className={classes.sectionHeader}>
<Typography className={classes.sectionHeaderTitle}> <Typography className={classes.sectionHeaderTitle}>
{i18n.t("Shipping Address")} <FormattedMessage defaultMessage="Shipping Address" />
</Typography> </Typography>
{canEditAddresses && ( {canEditAddresses && (
<div className={classes.sectionHeaderToolbar}> <div className={classes.sectionHeaderToolbar}>
@ -200,7 +226,7 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
onClick={onShippingAddressEdit} onClick={onShippingAddressEdit}
disabled={!onShippingAddressEdit && user === undefined} disabled={!onShippingAddressEdit && user === undefined}
> >
{i18n.t("Edit")} <FormattedMessage {...buttonMessages.edit} />
</Button> </Button>
</div> </div>
)} )}
@ -208,7 +234,13 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
{shippingAddress === undefined ? ( {shippingAddress === undefined ? (
<Skeleton /> <Skeleton />
) : shippingAddress === null ? ( ) : shippingAddress === null ? (
<Typography>{i18n.t("Not set")}</Typography> <Typography>
<FormattedMessage
defaultMessage="Not set"
description="shipping address is not set in draft order"
id="orderCustomerShippingAddressNotSet"
/>
</Typography>
) : ( ) : (
<> <>
{shippingAddress.companyName && ( {shippingAddress.companyName && (
@ -243,7 +275,7 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
<CardContent> <CardContent>
<div className={classes.sectionHeader}> <div className={classes.sectionHeader}>
<Typography className={classes.sectionHeaderTitle}> <Typography className={classes.sectionHeaderTitle}>
{i18n.t("Billing Address")} <FormattedMessage defaultMessage="Billing Address" />
</Typography> </Typography>
{canEditAddresses && ( {canEditAddresses && (
<div className={classes.sectionHeaderToolbar}> <div className={classes.sectionHeaderToolbar}>
@ -253,7 +285,7 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
onClick={onBillingAddressEdit} onClick={onBillingAddressEdit}
disabled={!onBillingAddressEdit && user === undefined} disabled={!onBillingAddressEdit && user === undefined}
> >
{i18n.t("Edit")} <FormattedMessage {...buttonMessages.edit} />
</Button> </Button>
</div> </div>
)} )}
@ -261,9 +293,20 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
{billingAddress === undefined ? ( {billingAddress === undefined ? (
<Skeleton /> <Skeleton />
) : billingAddress === null ? ( ) : billingAddress === null ? (
<Typography>{i18n.t("Not set")}</Typography> <Typography>
<FormattedMessage
defaultMessage="Not set"
description="no address is set in draft order"
id="orderCustomerBillingAddressNotSet"
/>
</Typography>
) : maybe(() => shippingAddress.id) === billingAddress.id ? ( ) : maybe(() => shippingAddress.id) === billingAddress.id ? (
<Typography>{i18n.t("Same as shipping address")}</Typography> <Typography>
<FormattedMessage
defaultMessage="Same as shipping address"
description="billing address"
/>
</Typography>
) : ( ) : (
<> <>
{billingAddress.companyName && ( {billingAddress.companyName && (

View file

@ -10,12 +10,13 @@ import {
WithStyles WithStyles
} from "@material-ui/core/styles"; } from "@material-ui/core/styles";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl";
import ConfirmButton, { import ConfirmButton, {
ConfirmButtonTransitionState ConfirmButtonTransitionState
} from "@saleor/components/ConfirmButton"; } from "@saleor/components/ConfirmButton";
import { SingleAutocompleteSelectField } from "@saleor/components/SingleAutocompleteSelectField"; import { SingleAutocompleteSelectField } from "@saleor/components/SingleAutocompleteSelectField";
import i18n from "../../../i18n"; import { buttonMessages } from "@saleor/intl";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
@ -76,7 +77,12 @@ const OrderCustomerEditDialog = withStyles(styles, {
: []; : [];
return ( return (
<Dialog onClose={onClose} open={open} classes={{ paper: classes.dialog }}> <Dialog onClose={onClose} open={open} classes={{ paper: classes.dialog }}>
<DialogTitle>{i18n.t("Edit customer details")}</DialogTitle> <DialogTitle>
<FormattedMessage
defaultMessage="Edit customer details"
description="dialog header"
/>
</DialogTitle>
<DialogContent className={classes.root}> <DialogContent className={classes.root}>
<SingleAutocompleteSelectField <SingleAutocompleteSelectField
choices={choices} choices={choices}
@ -91,7 +97,7 @@ const OrderCustomerEditDialog = withStyles(styles, {
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={onClose}> <Button onClick={onClose}>
{i18n.t("Cancel", { context: "button" })} <FormattedMessage {...buttonMessages.cancel} />
</Button> </Button>
<ConfirmButton <ConfirmButton
transitionState={confirmButtonState} transitionState={confirmButtonState}
@ -99,7 +105,7 @@ const OrderCustomerEditDialog = withStyles(styles, {
variant="contained" variant="contained"
onClick={onConfirm} onClick={onConfirm}
> >
{i18n.t("Confirm", { context: "button" })} <FormattedMessage {...buttonMessages.confirm} />
</ConfirmButton> </ConfirmButton>
</DialogActions> </DialogActions>
</Dialog> </Dialog>

View file

@ -2,10 +2,10 @@ import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent"; import CardContent from "@material-ui/core/CardContent";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import i18n from "../../../i18n";
interface OrderCustomerNoteProps { interface OrderCustomerNoteProps {
note: string; note: string;
@ -13,24 +13,29 @@ interface OrderCustomerNoteProps {
export const OrderCustomerNote: React.StatelessComponent< export const OrderCustomerNote: React.StatelessComponent<
OrderCustomerNoteProps OrderCustomerNoteProps
> = ({ note }) => ( > = ({ note }) => {
<Card> const intl = useIntl();
<CardTitle
title={i18n.t("Notes", { return (
context: "customer notes" <Card>
})} <CardTitle
/> title={intl.formatMessage({
<CardContent> defaultMessage: "Notes",
{note === undefined ? ( description: "notes about customer, header"
<Skeleton /> })}
) : note === "" ? ( />
<Typography color="textSecondary"> <CardContent>
{i18n.t("No notes from customer")} {note === undefined ? (
</Typography> <Skeleton />
) : ( ) : note === "" ? (
<Typography>{note}</Typography> <Typography color="textSecondary">
)} <FormattedMessage defaultMessage="No notes from customer" />
</CardContent> </Typography>
</Card> ) : (
); <Typography>{note}</Typography>
)}
</CardContent>
</Card>
);
};
export default OrderCustomerNote; export default OrderCustomerNote;

View file

@ -6,6 +6,7 @@ import {
} from "@material-ui/core/styles"; } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import React from "react"; import React from "react";
import { useIntl } from "react-intl";
import AppHeader from "@saleor/components/AppHeader"; import AppHeader from "@saleor/components/AppHeader";
import CardMenu from "@saleor/components/CardMenu"; import CardMenu from "@saleor/components/CardMenu";
@ -15,7 +16,7 @@ import { DateTime } from "@saleor/components/Date";
import Grid from "@saleor/components/Grid"; import Grid from "@saleor/components/Grid";
import PageHeader from "@saleor/components/PageHeader"; import PageHeader from "@saleor/components/PageHeader";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import i18n from "../../../i18n"; import { sectionNames } from "@saleor/intl";
import { maybe, renderCollection } from "../../../misc"; import { maybe, renderCollection } from "../../../misc";
import { OrderStatus } from "../../../types/globalTypes"; import { OrderStatus } from "../../../types/globalTypes";
import { OrderDetails_order } from "../../types/OrderDetails"; import { OrderDetails_order } from "../../types/OrderDetails";
@ -81,6 +82,8 @@ const OrderDetailsPage = withStyles(styles, { name: "OrderDetailsPage" })(
onShippingAddressEdit, onShippingAddressEdit,
onProfileView onProfileView
}: OrderDetailsPageProps) => { }: OrderDetailsPageProps) => {
const intl = useIntl();
const canCancel = maybe(() => order.status) !== OrderStatus.CANCELED; const canCancel = maybe(() => order.status) !== OrderStatus.CANCELED;
const canEditAddresses = maybe(() => order.status) !== OrderStatus.CANCELED; const canEditAddresses = maybe(() => order.status) !== OrderStatus.CANCELED;
const canFulfill = maybe(() => order.status) !== OrderStatus.CANCELED; const canFulfill = maybe(() => order.status) !== OrderStatus.CANCELED;
@ -90,7 +93,9 @@ const OrderDetailsPage = withStyles(styles, { name: "OrderDetailsPage" })(
return ( return (
<Container> <Container>
<AppHeader onBack={onBack}>{i18n.t("Orders")}</AppHeader> <AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.orders)}
</AppHeader>
<PageHeader <PageHeader
className={classes.header} className={classes.header}
title={maybe(() => order.number) ? "#" + order.number : undefined} title={maybe(() => order.number) ? "#" + order.number : undefined}
@ -99,7 +104,10 @@ const OrderDetailsPage = withStyles(styles, { name: "OrderDetailsPage" })(
<CardMenu <CardMenu
menuItems={[ menuItems={[
{ {
label: i18n.t("Cancel order", { context: "button" }), label: intl.formatMessage({
defaultMessage: "Cancel order",
description: "button"
}),
onSelect: onOrderCancel onSelect: onOrderCancel
} }
]} ]}

View file

@ -1,9 +1,9 @@
import DialogContentText from "@material-ui/core/DialogContentText"; import DialogContentText from "@material-ui/core/DialogContentText";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ActionDialog from "@saleor/components/ActionDialog"; import ActionDialog from "@saleor/components/ActionDialog";
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
import i18n from "../../../i18n";
export interface OrderDraftCancelDialogProps { export interface OrderDraftCancelDialogProps {
confirmButtonState: ConfirmButtonTransitionState; confirmButtonState: ConfirmButtonTransitionState;
@ -15,29 +15,31 @@ export interface OrderDraftCancelDialogProps {
const OrderDraftCancelDialog: React.StatelessComponent< const OrderDraftCancelDialog: React.StatelessComponent<
OrderDraftCancelDialogProps OrderDraftCancelDialogProps
> = ({ confirmButtonState, onClose, onConfirm, open, orderNumber }) => ( > = ({ confirmButtonState, onClose, onConfirm, open, orderNumber }) => {
<ActionDialog const intl = useIntl();
confirmButtonState={confirmButtonState}
onClose={onClose} return (
onConfirm={onConfirm} <ActionDialog
open={open} confirmButtonState={confirmButtonState}
title={i18n.t("Remove draft order", { onClose={onClose}
context: "modal title" onConfirm={onConfirm}
})} open={open}
variant="delete" title={intl.formatMessage({
> defaultMessage: "Remove draft order",
<DialogContentText description: "dialog header"
dangerouslySetInnerHTML={{ })}
__html: i18n.t( variant="delete"
"Are you sure you want to remove draft <strong>#{{ number }}</strong>?", >
{ <DialogContentText>
context: "modal", <FormattedMessage
number: orderNumber defaultMessage="Are you sure you want to remove draft #{number}?"
} values={{
) orderNumber
}} }}
/> />
</ActionDialog> </DialogContentText>
); </ActionDialog>
);
};
OrderDraftCancelDialog.displayName = "OrderDraftCancelDialog"; OrderDraftCancelDialog.displayName = "OrderDraftCancelDialog";
export default OrderDraftCancelDialog; export default OrderDraftCancelDialog;

View file

@ -2,9 +2,9 @@ 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 CardContent from "@material-ui/core/CardContent";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import i18n from "../../../i18n";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { OrderDetails_order } from "../../types/OrderDetails"; import { OrderDetails_order } from "../../types/OrderDetails";
import OrderDraftDetailsProducts, { import OrderDraftDetailsProducts, {
@ -29,34 +29,40 @@ const OrderDraftDetails: React.StatelessComponent<OrderDraftDetailsProps> = ({
onOrderLineChange, onOrderLineChange,
onOrderLineRemove, onOrderLineRemove,
onShippingMethodEdit onShippingMethodEdit
}) => ( }) => {
<Card> const intl = useIntl();
<CardTitle
title={i18n.t("Order details", { return (
context: "card title" <Card>
})} <CardTitle
toolbar={ title={intl.formatMessage({
<Button color="primary" variant="text" onClick={onOrderLineAdd}> defaultMessage: "Order Details",
{i18n.t("Add products", { description: "section header"
context: "button" })}
})} toolbar={
</Button> <Button color="primary" variant="text" onClick={onOrderLineAdd}>
} <FormattedMessage
/> defaultMessage="Add products"
<OrderDraftDetailsProducts description="button"
lines={maybe(() => order.lines)} />
onOrderLineChange={onOrderLineChange} </Button>
onOrderLineRemove={onOrderLineRemove} }
/> />
{maybe(() => order.lines.length) !== 0 && ( <OrderDraftDetailsProducts
<CardContent> lines={maybe(() => order.lines)}
<OrderDraftDetailsSummary onOrderLineChange={onOrderLineChange}
order={order} onOrderLineRemove={onOrderLineRemove}
onShippingMethodEdit={onShippingMethodEdit} />
/> {maybe(() => order.lines.length) !== 0 && (
</CardContent> <CardContent>
)} <OrderDraftDetailsSummary
</Card> order={order}
); onShippingMethodEdit={onShippingMethodEdit}
/>
</CardContent>
)}
</Card>
);
};
OrderDraftDetails.displayName = "OrderDraftDetails"; OrderDraftDetails.displayName = "OrderDraftDetails";
export default OrderDraftDetails; export default OrderDraftDetails;

View file

@ -14,6 +14,7 @@ import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import DeleteIcon from "@material-ui/icons/Delete"; import DeleteIcon from "@material-ui/icons/Delete";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl";
import { DebounceForm } from "@saleor/components/DebounceForm"; import { DebounceForm } from "@saleor/components/DebounceForm";
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
@ -22,7 +23,6 @@ import Skeleton from "@saleor/components/Skeleton";
import TableCellAvatar, { import TableCellAvatar, {
AVATAR_MARGIN AVATAR_MARGIN
} from "@saleor/components/TableCellAvatar"; } from "@saleor/components/TableCellAvatar";
import i18n from "../../../i18n";
import { maybe, renderCollection } from "../../../misc"; import { maybe, renderCollection } from "../../../misc";
import { OrderDetails_order_lines } from "../../types/OrderDetails"; import { OrderDetails_order_lines } from "../../types/OrderDetails";
@ -89,17 +89,26 @@ const OrderDraftDetailsProducts = withStyles(styles, {
<TableRow> <TableRow>
<TableCell className={classes.colName}> <TableCell className={classes.colName}>
<span className={classes.colNameLabel}> <span className={classes.colNameLabel}>
{i18n.t("Product", { context: "table header" })} <FormattedMessage defaultMessage="Product" />
</span> </span>
</TableCell> </TableCell>
<TableCell className={classes.colQuantity}> <TableCell className={classes.colQuantity}>
{i18n.t("Quantity", { context: "table header" })} <FormattedMessage
defaultMessage="Quantity"
description="quantity of ordered products"
/>
</TableCell> </TableCell>
<TableCell className={classes.colPrice}> <TableCell className={classes.colPrice}>
{i18n.t("Price", { context: "table header" })} <FormattedMessage
defaultMessage="Price"
description="price or ordered products"
/>
</TableCell> </TableCell>
<TableCell className={classes.colTotal}> <TableCell className={classes.colTotal}>
{i18n.t("Total", { context: "table header" })} <FormattedMessage
defaultMessage="Total"
description="total price of ordered products"
/>
</TableCell> </TableCell>
<TableCell className={classes.colAction} /> <TableCell className={classes.colAction} />
</TableRow> </TableRow>
@ -109,7 +118,7 @@ const OrderDraftDetailsProducts = withStyles(styles, {
{maybe(() => lines.length) === 0 ? ( {maybe(() => lines.length) === 0 ? (
<TableRow> <TableRow>
<TableCell colSpan={5}> <TableCell colSpan={5}>
{i18n.t("No Products added to Order")} <FormattedMessage defaultMessage="No Products added to Order" />
</TableCell> </TableCell>
</TableRow> </TableRow>
) : ( ) : (

View file

@ -5,11 +5,11 @@ import {
WithStyles WithStyles
} from "@material-ui/core/styles"; } from "@material-ui/core/styles";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl";
import Link from "@saleor/components/Link"; import Link from "@saleor/components/Link";
import Money from "@saleor/components/Money"; import Money from "@saleor/components/Money";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import i18n from "../../../i18n";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { OrderDetails_order } from "../../types/OrderDetails"; import { OrderDetails_order } from "../../types/OrderDetails";
@ -39,7 +39,12 @@ const OrderDraftDetailsSummary = withStyles(styles, {
<tr> <tr>
{maybe(() => order.subtotal) ? ( {maybe(() => order.subtotal) ? (
<> <>
<td>{i18n.t("Subtotal")}</td> <td>
<FormattedMessage
defaultMessage="Subtotal"
description="subtotal price or an order"
/>
</td>
<td className={classes.textRight}> <td className={classes.textRight}>
<Money money={order.subtotal.gross} /> <Money money={order.subtotal.gross} />
</td> </td>
@ -59,11 +64,16 @@ const OrderDraftDetailsSummary = withStyles(styles, {
order.availableShippingMethods.length > 0 ? ( order.availableShippingMethods.length > 0 ? (
<td> <td>
<Link onClick={onShippingMethodEdit}> <Link onClick={onShippingMethodEdit}>
{i18n.t("Add shipping carrier")} <FormattedMessage
defaultMessage="Add shipping carrier"
description="button"
/>
</Link> </Link>
</td> </td>
) : ( ) : (
<td>{i18n.t("No applicable shipping carriers")}</td> <td>
<FormattedMessage defaultMessage="No applicable shipping carriers" />
</td>
) )
) : ( ) : (
<> <>
@ -90,7 +100,9 @@ const OrderDraftDetailsSummary = withStyles(styles, {
<tr> <tr>
{maybe(() => order.total.tax) !== undefined ? ( {maybe(() => order.total.tax) !== undefined ? (
<> <>
<td>{i18n.t("Taxes (VAT included)")}</td> <td>
<FormattedMessage defaultMessage="Taxes (VAT included)" />
</td>
<td className={classes.textRight}> <td className={classes.textRight}>
<Money money={order.total.tax} /> <Money money={order.total.tax} />
</td> </td>
@ -104,7 +116,12 @@ const OrderDraftDetailsSummary = withStyles(styles, {
<tr> <tr>
{maybe(() => order.total.gross) !== undefined ? ( {maybe(() => order.total.gross) !== undefined ? (
<> <>
<td>{i18n.t("Total")}</td> <td>
<FormattedMessage
defaultMessage="Total"
description="total price of an order"
/>
</td>
<td className={classes.textRight}> <td className={classes.textRight}>
<Money money={order.total.gross} /> <Money money={order.total.gross} />
</td> </td>

View file

@ -1,9 +1,9 @@
import DialogContentText from "@material-ui/core/DialogContentText"; import DialogContentText from "@material-ui/core/DialogContentText";
import React from "react"; import React from "react";
import { FormattedMessage, IntlShape, useIntl } from "react-intl";
import ActionDialog from "@saleor/components/ActionDialog"; import ActionDialog from "@saleor/components/ActionDialog";
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
import i18n from "../../../i18n";
export type OrderDraftFinalizeWarning = export type OrderDraftFinalizeWarning =
| "no-shipping" | "no-shipping"
@ -21,18 +21,28 @@ export interface OrderDraftFinalizeDialogProps {
onConfirm: () => void; onConfirm: () => void;
} }
const warningToText = (warning: OrderDraftFinalizeWarning) => { const warningToText = (warning: OrderDraftFinalizeWarning, intl: IntlShape) => {
switch (warning) { switch (warning) {
case "no-shipping": case "no-shipping":
return i18n.t("No shipping address"); return intl.formatMessage({
defaultMessage: "No shipping address"
});
case "no-billing": case "no-billing":
return i18n.t("No billing address"); return intl.formatMessage({
defaultMessage: "No billing address"
});
case "no-user": case "no-user":
return i18n.t("No user information"); return intl.formatMessage({
defaultMessage: "No user information"
});
case "no-shipping-method": case "no-shipping-method":
return i18n.t("Some products require shipping, but no method provided"); return intl.formatMessage({
defaultMessage: "Some products require shipping, but no method provided"
});
case "unnecessary-shipping-method": case "unnecessary-shipping-method":
return i18n.t("Shipping method provided, but no product requires it"); return intl.formatMessage({
defaultMessage: "Shipping method provided, but no product requires it"
});
} }
}; };
@ -45,48 +55,54 @@ const OrderDraftFinalizeDialog: React.StatelessComponent<
onClose, onClose,
onConfirm, onConfirm,
orderNumber orderNumber
}) => ( }) => {
<ActionDialog const intl = useIntl();
onClose={onClose}
onConfirm={onConfirm} return (
open={open} <ActionDialog
title={i18n.t("Finalize draft order", { onClose={onClose}
context: "modal title" onConfirm={onConfirm}
})} open={open}
confirmButtonLabel={ title={intl.formatMessage({
warnings.length > 0 ? i18n.t("Finalize anyway") : i18n.t("Finalize") defaultMessage: "Finalize draft order",
} description: "dialog header"
confirmButtonState={confirmButtonState} })}
variant={warnings.length > 0 ? "delete" : "default"} confirmButtonLabel={
> warnings.length > 0
<DialogContentText component="div"> ? intl.formatMessage({
{warnings.length > 0 && ( defaultMessage: "Finalize anyway",
<> description: "button"
<p> })
{i18n.t( : intl.formatMessage({
"There are missing or incorrect informations about this order:" defaultMessage: "Finalize",
)} description: "button"
</p> })
<ul> }
{warnings.map(warning => ( confirmButtonState={confirmButtonState}
<li key={warning}>{warningToText(warning)}</li> variant={warnings.length > 0 ? "delete" : "default"}
))} >
</ul> <DialogContentText component="div">
</> {warnings.length > 0 && (
)} <>
<span <p>
dangerouslySetInnerHTML={{ <FormattedMessage defaultMessage="There are missing or incorrect informations about this order:" />
__html: i18n.t( </p>
"Are you sure you want to finalize draft <strong>#{{ number }}</strong>?", <ul>
{ {warnings.map(warning => (
context: "modal", <li key={warning}>{warningToText(warning, intl)}</li>
number: orderNumber ))}
} </ul>
) </>
}} )}
/> <FormattedMessage
</DialogContentText> defaultMessage="Are you sure you want to finalize draft #{number}?"
</ActionDialog> values={{
); orderNumber
}}
/>
</DialogContentText>
</ActionDialog>
);
};
OrderDraftFinalizeDialog.displayName = "OrderDraftFinalize"; OrderDraftFinalizeDialog.displayName = "OrderDraftFinalize";
export default OrderDraftFinalizeDialog; export default OrderDraftFinalizeDialog;

View file

@ -10,6 +10,7 @@ import TableCell from "@material-ui/core/TableCell";
import TableFooter from "@material-ui/core/TableFooter"; import TableFooter from "@material-ui/core/TableFooter";
import TableRow from "@material-ui/core/TableRow"; import TableRow from "@material-ui/core/TableRow";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl";
import Checkbox from "@saleor/components/Checkbox"; import Checkbox from "@saleor/components/Checkbox";
import { DateTime } from "@saleor/components/Date"; import { DateTime } from "@saleor/components/Date";
@ -17,7 +18,6 @@ import Money from "@saleor/components/Money";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
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 i18n from "@saleor/i18n";
import { import {
maybe, maybe,
renderCollection, renderCollection,
@ -85,6 +85,7 @@ export const OrderDraftList = withStyles(styles, { name: "OrderDraftList" })(
status: transformOrderStatus(order.status) status: transformOrderStatus(order.status)
})) }))
: undefined; : undefined;
return ( return (
<Table> <Table>
<TableHead <TableHead
@ -96,16 +97,22 @@ export const OrderDraftList = withStyles(styles, { name: "OrderDraftList" })(
toolbar={toolbar} toolbar={toolbar}
> >
<TableCell padding="dense" className={classes.colNumber}> <TableCell padding="dense" className={classes.colNumber}>
{i18n.t("No. of Order", { context: "table header" })} <FormattedMessage defaultMessage="No. of Order" />
</TableCell> </TableCell>
<TableCell padding="dense" className={classes.colDate}> <TableCell padding="dense" className={classes.colDate}>
{i18n.t("Date", { context: "table header" })} <FormattedMessage
defaultMessage="Date"
description="order draft creation date"
/>
</TableCell> </TableCell>
<TableCell padding="dense" className={classes.colCustomer}> <TableCell padding="dense" className={classes.colCustomer}>
{i18n.t("Customer", { context: "table header" })} <FormattedMessage defaultMessage="Customer" />
</TableCell> </TableCell>
<TableCell className={classes.colTotal} padding="dense"> <TableCell className={classes.colTotal} padding="dense">
{i18n.t("Total", { context: "table header" })} <FormattedMessage
defaultMessage="Total"
description="order draft total price"
/>
</TableCell> </TableCell>
</TableHead> </TableHead>
<TableFooter> <TableFooter>
@ -185,7 +192,7 @@ export const OrderDraftList = withStyles(styles, { name: "OrderDraftList" })(
() => ( () => (
<TableRow> <TableRow>
<TableCell colSpan={numberOfColumns}> <TableCell colSpan={numberOfColumns}>
{i18n.t("No orders found")} <FormattedMessage defaultMessage="No draft orders found" />
</TableCell> </TableCell>
</TableRow> </TableRow>
) )

View file

@ -2,10 +2,11 @@ import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card"; import Card from "@material-ui/core/Card";
import AddIcon from "@material-ui/icons/Add"; import AddIcon from "@material-ui/icons/Add";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
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 i18n from "@saleor/i18n"; import { sectionNames } from "@saleor/intl";
import { ListActions, PageListProps } from "@saleor/types"; import { ListActions, PageListProps } from "@saleor/types";
import { OrderDraftList_draftOrders_edges_node } from "../../types/OrderDraftList"; import { OrderDraftList_draftOrders_edges_node } from "../../types/OrderDraftList";
import OrderDraftList from "../OrderDraftList"; import OrderDraftList from "../OrderDraftList";
@ -18,22 +19,30 @@ const OrderDraftListPage: React.StatelessComponent<OrderDraftListPageProps> = ({
disabled, disabled,
onAdd, onAdd,
...listProps ...listProps
}) => ( }) => {
<Container> const intl = useIntl();
<PageHeader title={i18n.t("Draft Orders")}>
<Button return (
color="primary" <Container>
variant="contained" <PageHeader title={intl.formatMessage(sectionNames.draftOrders)}>
disabled={disabled} <Button
onClick={onAdd} color="primary"
> variant="contained"
{i18n.t("Create order", { context: "button" })} <AddIcon /> disabled={disabled}
</Button> onClick={onAdd}
</PageHeader> >
<Card> <FormattedMessage
<OrderDraftList disabled={disabled} {...listProps} /> defaultMessage="Create order"
</Card> description="button"
</Container> />
); <AddIcon />
</Button>
</PageHeader>
<Card>
<OrderDraftList disabled={disabled} {...listProps} />
</Card>
</Container>
);
};
OrderDraftListPage.displayName = "OrderDraftListPage"; OrderDraftListPage.displayName = "OrderDraftListPage";
export default OrderDraftListPage; export default OrderDraftListPage;

View file

@ -6,6 +6,7 @@ import {
} from "@material-ui/core/styles"; } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import React from "react"; import React from "react";
import { useIntl } from "react-intl";
import AppHeader from "@saleor/components/AppHeader"; import AppHeader from "@saleor/components/AppHeader";
import CardMenu from "@saleor/components/CardMenu"; import CardMenu from "@saleor/components/CardMenu";
@ -16,8 +17,8 @@ import Grid from "@saleor/components/Grid";
import PageHeader from "@saleor/components/PageHeader"; import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import { sectionNames } from "@saleor/intl";
import { SearchCustomers_customers_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers"; import { SearchCustomers_customers_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers";
import i18n from "../../../i18n";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { DraftOrderInput } from "../../../types/globalTypes"; import { DraftOrderInput } from "../../../types/globalTypes";
import { OrderDetails_order } from "../../types/OrderDetails"; import { OrderDetails_order } from "../../types/OrderDetails";
@ -87,69 +88,83 @@ const OrderDraftPage = withStyles(styles, { name: "OrderDraftPage" })(
order, order,
users, users,
usersLoading usersLoading
}: OrderDraftPageProps) => ( }: OrderDraftPageProps) => {
<Container> const intl = useIntl();
<AppHeader onBack={onBack}>{i18n.t("Orders")}</AppHeader>
<PageHeader return (
className={classes.header} <Container>
title={maybe(() => order.number) ? "#" + order.number : undefined} <AppHeader onBack={onBack}>
> {intl.formatMessage(sectionNames.draftOrders)}
<CardMenu </AppHeader>
menuItems={[ <PageHeader
{ className={classes.header}
label: i18n.t("Cancel order", { context: "button" }), title={maybe(() => order.number) ? "#" + order.number : undefined}
onSelect: onDraftRemove >
} <CardMenu
]} menuItems={[
{
label: intl.formatMessage({
defaultMessage: "Cancel order",
description: "button"
}),
onSelect: onDraftRemove
}
]}
/>
</PageHeader>
<div className={classes.date}>
{order && order.created ? (
<Typography variant="caption">
<DateTime date={order.created} />
</Typography>
) : (
<Skeleton style={{ width: "10em" }} />
)}
</div>
<Grid>
<div>
<OrderDraftDetails
order={order}
onOrderLineAdd={onOrderLineAdd}
onOrderLineChange={onOrderLineChange}
onOrderLineRemove={onOrderLineRemove}
onShippingMethodEdit={onShippingMethodEdit}
/>
<OrderHistory
history={maybe(() => order.events)}
onNoteAdd={onNoteAdd}
/>
</div>
<div>
<OrderCustomer
canEditAddresses={true}
canEditCustomer={true}
order={order}
users={users}
loading={usersLoading}
fetchUsers={fetchUsers}
onBillingAddressEdit={onBillingAddressEdit}
onCustomerEdit={onCustomerEdit}
onShippingAddressEdit={onShippingAddressEdit}
onProfileView={onProfileView}
/>
</div>
</Grid>
<SaveButtonBar
state={saveButtonBarState}
disabled={disabled || !maybe(() => order.canFinalize)}
onCancel={onBack}
onSave={onDraftFinalize}
labels={{
save: intl.formatMessage({
defaultMessage: "Finalize",
description: "button"
})
}}
/> />
</PageHeader> </Container>
<div className={classes.date}> );
{order && order.created ? ( }
<Typography variant="caption">
<DateTime date={order.created} />
</Typography>
) : (
<Skeleton style={{ width: "10em" }} />
)}
</div>
<Grid>
<div>
<OrderDraftDetails
order={order}
onOrderLineAdd={onOrderLineAdd}
onOrderLineChange={onOrderLineChange}
onOrderLineRemove={onOrderLineRemove}
onShippingMethodEdit={onShippingMethodEdit}
/>
<OrderHistory
history={maybe(() => order.events)}
onNoteAdd={onNoteAdd}
/>
</div>
<div>
<OrderCustomer
canEditAddresses={true}
canEditCustomer={true}
order={order}
users={users}
loading={usersLoading}
fetchUsers={fetchUsers}
onBillingAddressEdit={onBillingAddressEdit}
onCustomerEdit={onCustomerEdit}
onShippingAddressEdit={onShippingAddressEdit}
onProfileView={onProfileView}
/>
</div>
</Grid>
<SaveButtonBar
state={saveButtonBarState}
disabled={disabled || !maybe(() => order.canFinalize)}
onCancel={onBack}
onSave={onDraftFinalize}
labels={{ save: i18n.t("Finalize", { context: "button" }) }}
/>
</Container>
)
); );
OrderDraftPage.displayName = "OrderDraftPage"; OrderDraftPage.displayName = "OrderDraftPage";
export default OrderDraftPage; export default OrderDraftPage;

View file

@ -14,6 +14,7 @@ import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow"; import TableRow from "@material-ui/core/TableRow";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardMenu from "@saleor/components/CardMenu"; import CardMenu from "@saleor/components/CardMenu";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
@ -23,7 +24,6 @@ import StatusLabel from "@saleor/components/StatusLabel";
import TableCellAvatar, { import TableCellAvatar, {
AVATAR_MARGIN AVATAR_MARGIN
} from "@saleor/components/TableCellAvatar"; } from "@saleor/components/TableCellAvatar";
import i18n from "../../../i18n";
import { maybe, renderCollection } from "../../../misc"; import { maybe, renderCollection } from "../../../misc";
import { FulfillmentStatus } from "../../../types/globalTypes"; import { FulfillmentStatus } from "../../../types/globalTypes";
import { OrderDetails_order_fulfillments } from "../../types/OrderDetails"; import { OrderDetails_order_fulfillments } from "../../types/OrderDetails";
@ -81,8 +81,14 @@ const OrderFulfillment = withStyles(styles, { name: "OrderFulfillment" })(
onOrderFulfillmentCancel, onOrderFulfillmentCancel,
onTrackingCodeAdd onTrackingCodeAdd
}: OrderFulfillmentProps) => { }: OrderFulfillmentProps) => {
const intl = useIntl();
const lines = maybe(() => fulfillment.lines); const lines = maybe(() => fulfillment.lines);
const status = maybe(() => fulfillment.status); const status = maybe(() => fulfillment.status);
const quantity = lines
.map(line => line.quantity)
.reduce((prev, curr) => prev + curr, 0);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -92,16 +98,24 @@ const OrderFulfillment = withStyles(styles, { name: "OrderFulfillment" })(
label={ label={
<> <>
{status === FulfillmentStatus.FULFILLED {status === FulfillmentStatus.FULFILLED
? i18n.t("Fulfilled ({{ quantity }})", { ? intl.formatMessage(
quantity: lines {
.map(line => line.quantity) defaultMessage: "Fulfilled ({quantity})",
.reduce((prev, curr) => prev + curr, 0) description: "section header"
}) },
: i18n.t("Cancelled ({{ quantity }})", { {
quantity: lines quantity
.map(line => line.quantity) }
.reduce((prev, curr) => prev + curr, 0) )
})} : intl.formatMessage(
{
defaultMessage: "Fulfilled ({quantity})",
description: "section header"
},
{
quantity
}
)}
<Typography className={classes.orderNumber} variant="body2"> <Typography className={classes.orderNumber} variant="body2">
{maybe( {maybe(
() => `#${orderNumber}-${fulfillment.fulfillmentOrder}` () => `#${orderNumber}-${fulfillment.fulfillmentOrder}`
@ -122,8 +136,9 @@ const OrderFulfillment = withStyles(styles, { name: "OrderFulfillment" })(
<CardMenu <CardMenu
menuItems={[ menuItems={[
{ {
label: i18n.t("Cancel shipment", { label: intl.formatMessage({
context: "button" defaultMessage: "Cancel shipment",
description: "button"
}), }),
onSelect: onOrderFulfillmentCancel onSelect: onOrderFulfillmentCancel
} }
@ -137,17 +152,29 @@ const OrderFulfillment = withStyles(styles, { name: "OrderFulfillment" })(
<TableRow> <TableRow>
<TableCell className={classes.colName}> <TableCell className={classes.colName}>
<span className={classes.colNameLabel}> <span className={classes.colNameLabel}>
{i18n.t("Product")} <FormattedMessage
defaultMessage="Product"
description="product name"
/>
</span> </span>
</TableCell> </TableCell>
<TableCell className={classes.colQuantity}> <TableCell className={classes.colQuantity}>
{i18n.t("Quantity")} <FormattedMessage
defaultMessage="Quantity"
description="ordered product quantity"
/>
</TableCell> </TableCell>
<TableCell className={classes.colPrice}> <TableCell className={classes.colPrice}>
{i18n.t("Price")} <FormattedMessage
defaultMessage="Price"
description="product price"
/>
</TableCell> </TableCell>
<TableCell className={classes.colTotal}> <TableCell className={classes.colTotal}>
{i18n.t("Total")} <FormattedMessage
defaultMessage="Total"
description="order line total price"
/>
</TableCell> </TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
@ -194,9 +221,12 @@ const OrderFulfillment = withStyles(styles, { name: "OrderFulfillment" })(
{maybe(() => fulfillment.trackingNumber) && ( {maybe(() => fulfillment.trackingNumber) && (
<TableRow> <TableRow>
<TableCell colSpan={numberOfColumns}> <TableCell colSpan={numberOfColumns}>
{i18n.t("Tracking Number: {{ trackingNumber }}", { <FormattedMessage
trackingNumber: fulfillment.trackingNumber defaultMessage="Tracking Number: {trackingNumber}"
})} values={{
trackingNumber: fulfillment.trackingNumber
}}
/>
</TableCell> </TableCell>
</TableRow> </TableRow>
)} )}
@ -205,7 +235,10 @@ const OrderFulfillment = withStyles(styles, { name: "OrderFulfillment" })(
{status === FulfillmentStatus.FULFILLED && !fulfillment.trackingNumber && ( {status === FulfillmentStatus.FULFILLED && !fulfillment.trackingNumber && (
<CardActions> <CardActions>
<Button color="primary" onClick={onTrackingCodeAdd}> <Button color="primary" onClick={onTrackingCodeAdd}>
{i18n.t("Add tracking")} <FormattedMessage
defaultMessage="Add tracking"
description="fulfillment group tracking number"
/>
</Button> </Button>
</CardActions> </CardActions>
)} )}

View file

@ -11,13 +11,14 @@ import {
WithStyles WithStyles
} from "@material-ui/core/styles"; } from "@material-ui/core/styles";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ConfirmButton, { import ConfirmButton, {
ConfirmButtonTransitionState ConfirmButtonTransitionState
} from "@saleor/components/ConfirmButton"; } from "@saleor/components/ConfirmButton";
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
import i18n from "../../../i18n"; import { buttonMessages } from "@saleor/intl";
export interface FormData { export interface FormData {
restock: boolean; restock: boolean;
@ -50,43 +51,56 @@ const OrderFulfillmentCancelDialog = withStyles(styles, {
open, open,
onConfirm, onConfirm,
onClose onClose
}: OrderFulfillmentCancelDialogProps) => ( }: OrderFulfillmentCancelDialogProps) => {
<Dialog onClose={onClose} open={open}> const intl = useIntl();
<Form initial={{ restock: true }} onSubmit={onConfirm}>
{({ change, data, submit }) => ( return (
<> <Dialog onClose={onClose} open={open}>
<DialogTitle> <Form initial={{ restock: true }} onSubmit={onConfirm}>
{i18n.t("Cancel fulfillment", { context: "title" })} {({ change, data, submit }) => (
</DialogTitle> <>
<DialogContent> <DialogTitle>
<DialogContentText> <FormattedMessage
{i18n.t("Are you sure you want to cancel this fulfillment?")} defaultMessage="Cancel Fulfillment"
</DialogContentText> description="dialog header"
<ControlledCheckbox />
checked={data.restock} </DialogTitle>
label={i18n.t("Restock items?")} <DialogContent>
name="restock" <DialogContentText>
onChange={change} <FormattedMessage defaultMessage="Are you sure you want to cancel this fulfillment?" />
/> </DialogContentText>
</DialogContent> <ControlledCheckbox
<DialogActions> checked={data.restock}
<Button onClick={onClose}> label={intl.formatMessage({
{i18n.t("Back", { context: "button" })} defaultMessage: "Restock items?",
</Button> description: "switch button"
<ConfirmButton })}
transitionState={confirmButtonState} name="restock"
className={classes.deleteButton} onChange={change}
variant="contained" />
onClick={submit} </DialogContent>
> <DialogActions>
{i18n.t("Cancel fulfillment", { context: "button" })} <Button onClick={onClose}>
</ConfirmButton> <FormattedMessage {...buttonMessages.back} />
</DialogActions> </Button>
</> <ConfirmButton
)} transitionState={confirmButtonState}
</Form> className={classes.deleteButton}
</Dialog> variant="contained"
) onClick={submit}
>
<FormattedMessage
defaultMessage="Cancel fulfillment"
description="button"
/>
</ConfirmButton>
</DialogActions>
</>
)}
</Form>
</Dialog>
);
}
); );
OrderFulfillmentCancelDialog.displayName = "OrderFulfillmentCancelDialog"; OrderFulfillmentCancelDialog.displayName = "OrderFulfillmentCancelDialog";
export default OrderFulfillmentCancelDialog; export default OrderFulfillmentCancelDialog;

View file

@ -16,6 +16,7 @@ import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow"; import TableRow from "@material-ui/core/TableRow";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ConfirmButton, { import ConfirmButton, {
ConfirmButtonTransitionState ConfirmButtonTransitionState
@ -25,7 +26,7 @@ import { FormSpacer } from "@saleor/components/FormSpacer";
import TableCellAvatar, { import TableCellAvatar, {
AVATAR_MARGIN AVATAR_MARGIN
} from "@saleor/components/TableCellAvatar"; } from "@saleor/components/TableCellAvatar";
import i18n from "../../../i18n"; import { buttonMessages } from "@saleor/intl";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { OrderDetails_order_lines } from "../../types/OrderDetails"; import { OrderDetails_order_lines } from "../../types/OrderDetails";
@ -83,125 +84,143 @@ const OrderFulfillmentDialog = withStyles(styles, {
lines, lines,
onClose, onClose,
onSubmit onSubmit
}: OrderFulfillmentDialogProps) => ( }: OrderFulfillmentDialogProps) => {
<Dialog onClose={onClose} open={open}> const intl = useIntl();
<Form
initial={{ return (
lines: maybe( <Dialog onClose={onClose} open={open}>
() => <Form
lines.map( initial={{
product => product.quantity - product.quantityFulfilled lines: maybe(
), () =>
[] lines.map(
), product => product.quantity - product.quantityFulfilled
trackingNumber: "" ),
}} []
onSubmit={onSubmit} ),
> trackingNumber: ""
{({ data, change }) => { }}
const handleQuantityChange = ( onSubmit={onSubmit}
productIndex: number, >
event: React.ChangeEvent<any> {({ data, change }) => {
) => { const handleQuantityChange = (
const newData = data.lines; productIndex: number,
newData[productIndex] = event.target.value; event: React.ChangeEvent<any>
change({ ) => {
target: { const newData = data.lines;
name: "lines", newData[productIndex] = event.target.value;
value: newData change({
} target: {
} as any); name: "lines",
}; value: newData
return ( }
<> } as any);
<DialogTitle>{i18n.t("Fulfill products")}</DialogTitle> };
<Table className={classes.table}> return (
<TableHead> <>
<TableRow> <DialogTitle>
<TableCell className={classes.colName}> <FormattedMessage
<span className={classes.colNameLabel}> defaultMessage="Fulfill products"
{i18n.t("Product name")} description="dialog header"
</span> />
</TableCell> </DialogTitle>
<TableCell className={classes.colSku}> <Table className={classes.table}>
{i18n.t("SKU")} <TableHead>
</TableCell> <TableRow>
<TableCell className={classes.colQuantity}> <TableCell className={classes.colName}>
{i18n.t("Quantity")} <span className={classes.colNameLabel}>
</TableCell> <FormattedMessage defaultMessage="Product name" />
</TableRow> </span>
</TableHead> </TableCell>
<TableBody> <TableCell className={classes.colSku}>
{lines.map((product, productIndex) => { <FormattedMessage
const remainingQuantity = defaultMessage="SKU"
product.quantity - product.quantityFulfilled; description="product's sku"
return ( />
<TableRow key={product.id}> </TableCell>
<TableCellAvatar <TableCell className={classes.colQuantity}>
className={classes.colName} <FormattedMessage
thumbnail={maybe(() => product.thumbnail.url)} defaultMessage="Quantity"
> description="quantity of fulfilled products"
{product.productName} />
</TableCellAvatar> </TableCell>
<TableCell className={classes.colSku}> </TableRow>
{product.productSku} </TableHead>
</TableCell> <TableBody>
<TableCell className={classes.colQuantity}> {lines.map((product, productIndex) => {
<div className={classes.colQuantityContent}> const remainingQuantity =
<TextField product.quantity - product.quantityFulfilled;
type="number" return (
inputProps={{ <TableRow key={product.id}>
max: remainingQuantity, <TableCellAvatar
style: { textAlign: "right" } className={classes.colName}
}} thumbnail={maybe(() => product.thumbnail.url)}
className={classes.quantityInput} >
value={data.lines[productIndex]} {product.productName}
onChange={event => </TableCellAvatar>
handleQuantityChange(productIndex, event) <TableCell className={classes.colSku}>
} {product.productSku}
error={ </TableCell>
remainingQuantity < data.lines[productIndex] <TableCell className={classes.colQuantity}>
} <div className={classes.colQuantityContent}>
/> <TextField
<div className={classes.remainingQuantity}> type="number"
/ {remainingQuantity} inputProps={{
max: remainingQuantity,
style: { textAlign: "right" }
}}
className={classes.quantityInput}
value={data.lines[productIndex]}
onChange={event =>
handleQuantityChange(productIndex, event)
}
error={
remainingQuantity < data.lines[productIndex]
}
/>
<div className={classes.remainingQuantity}>
/ {remainingQuantity}
</div>
</div> </div>
</div> </TableCell>
</TableCell> </TableRow>
</TableRow> );
); })}
})} </TableBody>
</TableBody> </Table>
</Table> <DialogContent>
<DialogContent> <FormSpacer />
<FormSpacer /> <TextField
<TextField fullWidth
fullWidth label={intl.formatMessage({
label={i18n.t("Tracking number")} defaultMessage: "Tracking number",
name="trackingNumber" description: "fulfillment group"
value={data.trackingNumber} })}
onChange={change} name="trackingNumber"
/> value={data.trackingNumber}
</DialogContent> onChange={change}
<DialogActions> />
<Button onClick={onClose}> </DialogContent>
{i18n.t("Cancel", { context: "button" })} <DialogActions>
</Button> <Button onClick={onClose}>
<ConfirmButton <FormattedMessage {...buttonMessages.cancel} />
transitionState={confirmButtonState} </Button>
color="primary" <ConfirmButton
variant="contained" transitionState={confirmButtonState}
type="submit" color="primary"
> variant="contained"
{i18n.t("Confirm", { context: "button" })} type="submit"
</ConfirmButton> >
</DialogActions> <FormattedMessage {...buttonMessages.confirm} />
</> </ConfirmButton>
); </DialogActions>
}} </>
</Form> );
</Dialog> }}
) </Form>
</Dialog>
);
}
); );
OrderFulfillmentDialog.displayName = "OrderFulfillmentDialog"; OrderFulfillmentDialog.displayName = "OrderFulfillmentDialog";
export default OrderFulfillmentDialog; export default OrderFulfillmentDialog;

View file

@ -5,12 +5,13 @@ import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle"; import DialogTitle from "@material-ui/core/DialogTitle";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ConfirmButton, { import ConfirmButton, {
ConfirmButtonTransitionState ConfirmButtonTransitionState
} from "@saleor/components/ConfirmButton"; } from "@saleor/components/ConfirmButton";
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
import i18n from "../../../i18n"; import { buttonMessages } from "@saleor/intl";
export interface FormData { export interface FormData {
trackingNumber: string; trackingNumber: string;
@ -26,40 +27,49 @@ interface OrderFulfillmentTrackingDialogProps {
const OrderFulfillmentTrackingDialog: React.StatelessComponent< const OrderFulfillmentTrackingDialog: React.StatelessComponent<
OrderFulfillmentTrackingDialogProps OrderFulfillmentTrackingDialogProps
> = ({ confirmButtonState, open, trackingNumber, onConfirm, onClose }) => ( > = ({ confirmButtonState, open, trackingNumber, onConfirm, onClose }) => {
<Dialog onClose={onClose} open={open}> const intl = useIntl();
<Form initial={{ trackingNumber }} onSubmit={onConfirm}>
{({ change, data, submit }) => ( return (
<> <Dialog onClose={onClose} open={open}>
<DialogTitle> <Form initial={{ trackingNumber }} onSubmit={onConfirm}>
{i18n.t("Add tracking code", { context: "title" })} {({ change, data, submit }) => (
</DialogTitle> <>
<DialogContent> <DialogTitle>
<TextField <FormattedMessage
label={i18n.t("Tracking number")} defaultMessage="Add tracking code"
name="trackingNumber" description="dialog header"
onChange={change} />
value={data.trackingNumber} </DialogTitle>
fullWidth <DialogContent>
/> <TextField
</DialogContent> label={intl.formatMessage({
<DialogActions> defaultMessage: "Tracking number"
<Button onClick={onClose}> })}
{i18n.t("Cancel", { context: "button" })} name="trackingNumber"
</Button> onChange={change}
<ConfirmButton value={data.trackingNumber}
transitionState={confirmButtonState} fullWidth
color="primary" />
variant="contained" </DialogContent>
onClick={submit} <DialogActions>
> <Button onClick={onClose}>
{i18n.t("Confirm", { context: "button" })} <FormattedMessage {...buttonMessages.cancel} />
</ConfirmButton> </Button>
</DialogActions> <ConfirmButton
</> transitionState={confirmButtonState}
)} color="primary"
</Form> variant="contained"
</Dialog> onClick={submit}
); >
<FormattedMessage {...buttonMessages.confirm} />
</ConfirmButton>
</DialogActions>
</>
)}
</Form>
</Dialog>
);
};
OrderFulfillmentTrackingDialog.displayName = "OrderFulfillmentTrackingDialog"; OrderFulfillmentTrackingDialog.displayName = "OrderFulfillmentTrackingDialog";
export default OrderFulfillmentTrackingDialog; export default OrderFulfillmentTrackingDialog;

View file

@ -6,6 +6,7 @@ import {
} from "@material-ui/core/styles"; } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import React from "react"; import React from "react";
import { FormattedMessage, IntlShape, useIntl } from "react-intl";
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
import Hr from "@saleor/components/Hr"; import Hr from "@saleor/components/Hr";
@ -16,7 +17,6 @@ import {
TimelineEvent, TimelineEvent,
TimelineNote TimelineNote
} from "@saleor/components/Timeline"; } from "@saleor/components/Timeline";
import i18n from "../../../i18n";
import { import {
OrderEventsEmailsEnum, OrderEventsEmailsEnum,
OrderEventsEnum OrderEventsEnum
@ -27,115 +27,152 @@ export interface FormData {
message: string; message: string;
} }
const getEventMessage = (event: OrderDetails_order_events) => { const getEventMessage = (event: OrderDetails_order_events, intl: IntlShape) => {
switch (event.type) { switch (event.type) {
case OrderEventsEnum.CANCELED: case OrderEventsEnum.CANCELED:
return i18n.t("Order was cancelled", { return intl.formatMessage({
context: "order history message" defaultMessage: "Order was cancelled",
description: "order history message"
}); });
case OrderEventsEnum.DRAFT_ADDED_PRODUCTS: case OrderEventsEnum.DRAFT_ADDED_PRODUCTS:
return i18n.t("Products were added to draft order", { return intl.formatMessage({
context: "order history message" defaultMessage: "Products were added to draft order",
description: "order history message"
}); });
case OrderEventsEnum.DRAFT_CREATED: case OrderEventsEnum.DRAFT_CREATED:
return i18n.t("Draft order was created", { return intl.formatMessage({
context: "order history message" defaultMessage: "Draft order was created",
description: "order history message"
}); });
case OrderEventsEnum.DRAFT_REMOVED_PRODUCTS: case OrderEventsEnum.DRAFT_REMOVED_PRODUCTS:
return i18n.t("Products were removed from draft order", { return intl.formatMessage({
context: "order history message" defaultMessage: "Products were removed from draft order",
description: "order history message"
}); });
case OrderEventsEnum.EMAIL_SENT: case OrderEventsEnum.EMAIL_SENT:
switch (event.emailType) { switch (event.emailType) {
case OrderEventsEmailsEnum.DIGITAL_LINKS: case OrderEventsEmailsEnum.DIGITAL_LINKS:
return i18n.t("Links to the order's digital goods were sent", { return intl.formatMessage({
context: "order history message" defaultMessage: "Links to the order's digital goods were sent",
description: "order history message"
}); });
case OrderEventsEmailsEnum.FULFILLMENT_CONFIRMATION: case OrderEventsEmailsEnum.FULFILLMENT_CONFIRMATION:
return i18n.t("Fulfillment confirmation was sent to customer", { return intl.formatMessage({
context: "order history message" defaultMessage: "Fulfillment confirmation was sent to customer",
description: "order history message"
}); });
case OrderEventsEmailsEnum.ORDER_CONFIRMATION: case OrderEventsEmailsEnum.ORDER_CONFIRMATION:
return i18n.t("Order confirmation was sent to customer", { return intl.formatMessage({
context: "order history message" defaultMessage: "Order confirmation was sent to customer",
description: "order history message"
}); });
case OrderEventsEmailsEnum.PAYMENT_CONFIRMATION: case OrderEventsEmailsEnum.PAYMENT_CONFIRMATION:
return i18n.t("Payment confirmation was sent to customer", { return intl.formatMessage({
context: "order history message" defaultMessage: "Payment confirmation was sent to customer",
description: "order history message"
}); });
case OrderEventsEmailsEnum.SHIPPING_CONFIRMATION: case OrderEventsEmailsEnum.SHIPPING_CONFIRMATION:
return i18n.t("Shipping details was sent to customer", { return intl.formatMessage({
context: "order history message" defaultMessage: "Shipping details was sent to customer",
description: "order history message"
}); });
case OrderEventsEmailsEnum.TRACKING_UPDATED: case OrderEventsEmailsEnum.TRACKING_UPDATED:
return i18n.t("Shipping tracking number was sent to customer", { return intl.formatMessage({
context: "order history message" defaultMessage: "Shipping tracking number was sent to customer",
description: "order history message"
}); });
} }
case OrderEventsEnum.FULFILLMENT_CANCELED: case OrderEventsEnum.FULFILLMENT_CANCELED:
return i18n.t("Fulfillment was cancelled", { return intl.formatMessage({
context: "order history message" defaultMessage: "Fulfillment was cancelled",
description: "order history message"
}); });
case OrderEventsEnum.FULFILLMENT_FULFILLED_ITEMS: case OrderEventsEnum.FULFILLMENT_FULFILLED_ITEMS:
return i18n.t("Fulfilled {{ quantity }} items", { return intl.formatMessage(
context: "order history message", {
quantity: event.quantity defaultMessage: "Fulfilled {quantity} items",
}); description: "order history message"
},
{
quantity: event.quantity
}
);
case OrderEventsEnum.FULFILLMENT_RESTOCKED_ITEMS: case OrderEventsEnum.FULFILLMENT_RESTOCKED_ITEMS:
return i18n.t("Restocked {{ quantity }} items", { return intl.formatMessage(
context: "order history message", {
quantity: event.quantity defaultMessage: "Restocked {quantity} items",
}); description: "order history message"
},
{
quantity: event.quantity
}
);
case OrderEventsEnum.NOTE_ADDED: case OrderEventsEnum.NOTE_ADDED:
return i18n.t("Note was added to the order", { return intl.formatMessage({
context: "order history message" defaultMessage: "Note was added to the order",
description: "order history message"
}); });
case OrderEventsEnum.ORDER_FULLY_PAID: case OrderEventsEnum.ORDER_FULLY_PAID:
return i18n.t("Order was fully paid", { return intl.formatMessage({
context: "order history message" defaultMessage: "Order was fully paid",
description: "order history message"
}); });
case OrderEventsEnum.ORDER_MARKED_AS_PAID: case OrderEventsEnum.ORDER_MARKED_AS_PAID:
return i18n.t("Marked order as paid", { return intl.formatMessage({
context: "order history message" defaultMessage: "Marked order as paid",
description: "order history message"
}); });
case OrderEventsEnum.OTHER: case OrderEventsEnum.OTHER:
return event.message; return event.message;
case OrderEventsEnum.OVERSOLD_ITEMS: case OrderEventsEnum.OVERSOLD_ITEMS:
return i18n.t("Oversold {{ quantity }} items", { return intl.formatMessage(
context: "order history message", {
quantity: event.quantity defaultMessage: "Oversold {quantity} items",
}); description: "order history message"
},
{
quantity: event.quantity
}
);
case OrderEventsEnum.PAYMENT_CAPTURED: case OrderEventsEnum.PAYMENT_CAPTURED:
return i18n.t("Payment was captured", { return intl.formatMessage({
context: "order history message" defaultMessage: "Payment was captured",
description: "order history message"
}); });
case OrderEventsEnum.PAYMENT_FAILED: case OrderEventsEnum.PAYMENT_FAILED:
return i18n.t("Payment failed", { return intl.formatMessage({
context: "order history message" defaultMessage: "Payment failed",
description: "order history message"
}); });
case OrderEventsEnum.PAYMENT_REFUNDED: case OrderEventsEnum.PAYMENT_REFUNDED:
return i18n.t("Payment was refunded", { return intl.formatMessage({
context: "order history message" defaultMessage: "Payment was refunded",
description: "order history message"
}); });
case OrderEventsEnum.PAYMENT_VOIDED: case OrderEventsEnum.PAYMENT_VOIDED:
return i18n.t("Payment was voided", { return intl.formatMessage({
context: "order history message" defaultMessage: "Payment was voided",
description: "order history message"
}); });
case OrderEventsEnum.PLACED: case OrderEventsEnum.PLACED:
return i18n.t("Order was placed", { return intl.formatMessage({
context: "order history message" defaultMessage: "Order was placed",
description: "order history message"
}); });
case OrderEventsEnum.PLACED_FROM_DRAFT: case OrderEventsEnum.PLACED_FROM_DRAFT:
return i18n.t("Order was created from draft", { return intl.formatMessage({
context: "order history message" defaultMessage: "Order was created from draft",
description: "order history message"
}); });
case OrderEventsEnum.TRACKING_UPDATED: case OrderEventsEnum.TRACKING_UPDATED:
return i18n.t("Updated fulfillment group's tracking number", { return intl.formatMessage({
context: "order history message" defaultMessage: "Updated fulfillment group's tracking number",
description: "order history message"
}); });
case OrderEventsEnum.UPDATED_ADDRESS: case OrderEventsEnum.UPDATED_ADDRESS:
return i18n.t("Order address was updated", { return intl.formatMessage({
context: "order history message" defaultMessage: "Order address was updated",
description: "order history message"
}); });
} }
}; };
@ -158,51 +195,55 @@ interface OrderHistoryProps extends WithStyles<typeof styles> {
} }
const OrderHistory = withStyles(styles, { name: "OrderHistory" })( const OrderHistory = withStyles(styles, { name: "OrderHistory" })(
({ classes, history, onNoteAdd }: OrderHistoryProps) => ( ({ classes, history, onNoteAdd }: OrderHistoryProps) => {
<div className={classes.root}> const intl = useIntl();
<Typography className={classes.header} color="textSecondary">
{i18n.t("Order History")} return (
</Typography> <div className={classes.root}>
<Hr /> <Typography className={classes.header} color="textSecondary">
{history ? ( <FormattedMessage defaultMessage="Order History" />
<Timeline> </Typography>
<Form initial={{ message: "" }} onSubmit={onNoteAdd} resetOnSubmit> <Hr />
{({ change, data, submit }) => ( {history ? (
<TimelineAddNote <Timeline>
message={data.message} <Form initial={{ message: "" }} onSubmit={onNoteAdd} resetOnSubmit>
onChange={change} {({ change, data, submit }) => (
onSubmit={submit} <TimelineAddNote
/> message={data.message}
)} onChange={change}
</Form> onSubmit={submit}
{history />
.slice() )}
.reverse() </Form>
.map(event => { {history
if (event.type === OrderEventsEnum.NOTE_ADDED) { .slice()
.reverse()
.map(event => {
if (event.type === OrderEventsEnum.NOTE_ADDED) {
return (
<TimelineNote
date={event.date}
user={event.user}
message={event.message}
key={event.id}
/>
);
}
return ( return (
<TimelineNote <TimelineEvent
date={event.date} date={event.date}
user={event.user} title={getEventMessage(event, intl)}
message={event.message}
key={event.id} key={event.id}
/> />
); );
} })}
return ( </Timeline>
<TimelineEvent ) : (
date={event.date} <Skeleton />
title={getEventMessage(event)} )}
key={event.id} </div>
/> );
); }
})}
</Timeline>
) : (
<Skeleton />
)}
</div>
)
); );
OrderHistory.displayName = "OrderHistory"; OrderHistory.displayName = "OrderHistory";
export default OrderHistory; export default OrderHistory;

View file

@ -10,6 +10,7 @@ import TableCell from "@material-ui/core/TableCell";
import TableFooter from "@material-ui/core/TableFooter"; import TableFooter from "@material-ui/core/TableFooter";
import TableRow from "@material-ui/core/TableRow"; import TableRow from "@material-ui/core/TableRow";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl";
import Checkbox from "@saleor/components/Checkbox"; import Checkbox from "@saleor/components/Checkbox";
import { DateTime } from "@saleor/components/Date"; import { DateTime } from "@saleor/components/Date";
@ -18,7 +19,6 @@ import Skeleton from "@saleor/components/Skeleton";
import StatusLabel from "@saleor/components/StatusLabel"; import StatusLabel from "@saleor/components/StatusLabel";
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 i18n from "@saleor/i18n";
import { import {
maybe, maybe,
renderCollection, renderCollection,
@ -103,22 +103,34 @@ export const OrderList = withStyles(styles, { name: "OrderList" })(
toolbar={toolbar} toolbar={toolbar}
> >
<TableCell padding="dense" className={classes.colNumber}> <TableCell padding="dense" className={classes.colNumber}>
{i18n.t("No. of Order", { context: "table header" })} <FormattedMessage defaultMessage="No. of Order" />
</TableCell> </TableCell>
<TableCell padding="dense" className={classes.colDate}> <TableCell padding="dense" className={classes.colDate}>
{i18n.t("Date", { context: "table header" })} <FormattedMessage
defaultMessage="Date"
description="date when order was placed"
/>
</TableCell> </TableCell>
<TableCell padding="dense" className={classes.colCustomer}> <TableCell padding="dense" className={classes.colCustomer}>
{i18n.t("Customer", { context: "table header" })} <FormattedMessage
defaultMessage="Customer"
description="e-mail or full name"
/>
</TableCell> </TableCell>
<TableCell padding="dense" className={classes.colPayment}> <TableCell padding="dense" className={classes.colPayment}>
{i18n.t("Payment", { context: "table header" })} <FormattedMessage
defaultMessage="Payment"
description="payment status"
/>
</TableCell> </TableCell>
<TableCell padding="dense" className={classes.colFulfillment}> <TableCell padding="dense" className={classes.colFulfillment}>
{i18n.t("Fulfillment status", { context: "table header" })} <FormattedMessage defaultMessage="Fulfillment status" />
</TableCell> </TableCell>
<TableCell className={classes.colTotal} padding="dense"> <TableCell className={classes.colTotal} padding="dense">
{i18n.t("Total", { context: "table header" })} <FormattedMessage
defaultMessage="Total"
description="total order price"
/>
</TableCell> </TableCell>
</TableHead> </TableHead>
<TableFooter> <TableFooter>
@ -220,7 +232,7 @@ export const OrderList = withStyles(styles, { name: "OrderList" })(
() => ( () => (
<TableRow> <TableRow>
<TableCell colSpan={numberOfColumns}> <TableCell colSpan={numberOfColumns}>
{i18n.t("No orders found")} <FormattedMessage defaultMessage="No orders found" />
</TableCell> </TableCell>
</TableRow> </TableRow>
) )

View file

@ -1,11 +1,11 @@
import moment from "moment-timezone"; import moment from "moment-timezone";
import React from "react"; import React from "react";
import { useIntl } from "react-intl";
import { DateContext } from "@saleor/components/Date/DateContext"; import { DateContext } from "@saleor/components/Date/DateContext";
import { FieldType, IFilter } from "@saleor/components/Filter"; import { FieldType, IFilter } from "@saleor/components/Filter";
import FilterBar from "@saleor/components/FilterBar"; import FilterBar from "@saleor/components/FilterBar";
import TimezoneContext from "@saleor/components/Timezone"; import TimezoneContext from "@saleor/components/Timezone";
import i18n from "../../../i18n";
import { FilterProps } from "../../../types"; import { FilterProps } from "../../../types";
import { OrderStatusFilter } from "../../../types/globalTypes"; import { OrderStatusFilter } from "../../../types/globalTypes";
import { OrderListUrlFilters } from "../../urls"; import { OrderListUrlFilters } from "../../urls";
@ -26,6 +26,7 @@ export enum OrderFilterKeys {
const OrderListFilter: React.FC<OrderListFilterProps> = props => { const OrderListFilter: React.FC<OrderListFilterProps> = props => {
const date = React.useContext(DateContext); const date = React.useContext(DateContext);
const tz = React.useContext(TimezoneContext); const tz = React.useContext(TimezoneContext);
const intl = useIntl();
const filterMenu: IFilter = [ const filterMenu: IFilter = [
{ {
@ -40,7 +41,9 @@ const OrderListFilter: React.FC<OrderListFilterProps> = props => {
.toISOString() .toISOString()
.split("T")[0] // Remove timezone .split("T")[0] // Remove timezone
}, },
label: i18n.t("Last 7 Days"), label: intl.formatMessage({
defaultMessage: "Last 7 Days"
}),
value: OrderFilterKeys.dateLastWeek.toString() value: OrderFilterKeys.dateLastWeek.toString()
}, },
{ {
@ -53,7 +56,9 @@ const OrderListFilter: React.FC<OrderListFilterProps> = props => {
.toISOString() .toISOString()
.split("T")[0] // Remove timezone .split("T")[0] // Remove timezone
}, },
label: i18n.t("Last 30 Days"), label: intl.formatMessage({
defaultMessage: "Last 30 Days"
}),
value: OrderFilterKeys.dateLastMonth.toString() value: OrderFilterKeys.dateLastMonth.toString()
}, },
{ {
@ -66,58 +71,90 @@ const OrderListFilter: React.FC<OrderListFilterProps> = props => {
.toISOString() .toISOString()
.split("T")[0] // Remove timezone .split("T")[0] // Remove timezone
}, },
label: i18n.t("Last Year"), label: intl.formatMessage({
defaultMessage: "Last Year"
}),
value: OrderFilterKeys.dateLastYear.toString() value: OrderFilterKeys.dateLastYear.toString()
}, },
{ {
children: [], children: [],
data: { data: {
additionalText: i18n.t("equals"), additionalText: intl.formatMessage({
defaultMessage: "equals"
}),
fieldLabel: null, fieldLabel: null,
type: FieldType.date type: FieldType.date
}, },
label: i18n.t("Specific Date"), label: intl.formatMessage({
defaultMessage: "Specific Date"
}),
value: OrderFilterKeys.dateEqual.toString() value: OrderFilterKeys.dateEqual.toString()
}, },
{ {
children: [], children: [],
data: { data: {
fieldLabel: i18n.t("Range"), fieldLabel: intl.formatMessage({
defaultMessage: "Range"
}),
type: FieldType.rangeDate type: FieldType.rangeDate
}, },
label: i18n.t("Range"), label: intl.formatMessage({
defaultMessage: "Range"
}),
value: OrderFilterKeys.dateRange.toString() value: OrderFilterKeys.dateRange.toString()
} }
], ],
data: { data: {
fieldLabel: i18n.t("Date"), fieldLabel: intl.formatMessage({
defaultMessage: "Date"
}),
type: FieldType.select type: FieldType.select
}, },
label: i18n.t("Date"), label: intl.formatMessage({
defaultMessage: "Date"
}),
value: OrderFilterKeys.date.toString() value: OrderFilterKeys.date.toString()
}, },
{ {
children: [], children: [],
data: { data: {
additionalText: i18n.t("is set as"), additionalText: intl.formatMessage({
fieldLabel: i18n.t("Status"), defaultMessage: "is set as",
description: "date is set as"
}),
fieldLabel: intl.formatMessage({
defaultMessage: "Status",
description: "order fulfillment status"
}),
options: [ options: [
{ {
label: i18n.t("Fulfilled"), label: intl.formatMessage({
defaultMessage: "Fulfilled",
description: "order fulfillment status"
}),
value: OrderStatusFilter.FULFILLED.toString() value: OrderStatusFilter.FULFILLED.toString()
}, },
{ {
label: i18n.t("Partially Fulfilled"), label: intl.formatMessage({
defaultMessage: "Partially Fulfilled",
description: "order fulfillment status"
}),
value: OrderStatusFilter.PARTIALLY_FULFILLED.toString() value: OrderStatusFilter.PARTIALLY_FULFILLED.toString()
}, },
{ {
label: i18n.t("Unfulfilled"), label: intl.formatMessage({
defaultMessage: "Unfulfilled",
description: "order fulfillment status"
}),
value: OrderStatusFilter.UNFULFILLED.toString() value: OrderStatusFilter.UNFULFILLED.toString()
} }
], ],
type: FieldType.select type: FieldType.select
}, },
label: i18n.t("Fulfillment Status"), label: intl.formatMessage({
defaultMessage: "Fulfillment Status",
description: "order"
}),
value: OrderFilterKeys.fulfillment.toString() value: OrderFilterKeys.fulfillment.toString()
} }
]; ];

View file

@ -2,10 +2,11 @@ import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card"; import Card from "@material-ui/core/Card";
import AddIcon from "@material-ui/icons/Add"; import AddIcon from "@material-ui/icons/Add";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
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 i18n from "@saleor/i18n"; import { sectionNames } from "@saleor/intl";
import { FilterPageProps, ListActions, PageListProps } from "@saleor/types"; import { FilterPageProps, ListActions, PageListProps } from "@saleor/types";
import { OrderList_orders_edges_node } from "../../types/OrderList"; import { OrderList_orders_edges_node } from "../../types/OrderList";
import { OrderListUrlFilters } from "../../urls"; import { OrderListUrlFilters } from "../../urls";
@ -33,33 +34,48 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
onTabChange, onTabChange,
onFilterDelete, onFilterDelete,
...listProps ...listProps
}) => ( }) => {
<Container> const intl = useIntl();
<PageHeader title={i18n.t("Orders")}>
<Button color="primary" variant="contained" onClick={onAdd}> return (
{i18n.t("Create order", { context: "button" })} <AddIcon /> <Container>
</Button> <PageHeader title={intl.formatMessage(sectionNames.orders)}>
</PageHeader> <Button color="primary" variant="contained" onClick={onAdd}>
<Card> <FormattedMessage
<OrderListFilter defaultMessage="Create order"
allTabLabel={i18n.t("All Orders")} description="button"
currencySymbol={currencySymbol} />
currentTab={currentTab} <AddIcon />
filterLabel={i18n.t("Select all orders where:")} </Button>
filterTabs={filterTabs} </PageHeader>
filtersList={filtersList} <Card>
initialSearch={initialSearch} <OrderListFilter
searchPlaceholder={i18n.t("Search Orders...")} allTabLabel={intl.formatMessage({
onAll={onAll} defaultMessage: "All Orders",
onSearchChange={onSearchChange} description: "tab name"
onFilterAdd={onFilterAdd} })}
onFilterSave={onFilterSave} currencySymbol={currencySymbol}
onTabChange={onTabChange} currentTab={currentTab}
onFilterDelete={onFilterDelete} filterLabel={intl.formatMessage({
/> defaultMessage: "Select all orders where:"
<OrderList {...listProps} /> })}
</Card> filterTabs={filterTabs}
</Container> filtersList={filtersList}
); initialSearch={initialSearch}
searchPlaceholder={intl.formatMessage({
defaultMessage: "Search Orders..."
})}
onAll={onAll}
onSearchChange={onSearchChange}
onFilterAdd={onFilterAdd}
onFilterSave={onFilterSave}
onTabChange={onTabChange}
onFilterDelete={onFilterDelete}
/>
<OrderList {...listProps} />
</Card>
</Container>
);
};
OrderListPage.displayName = "OrderListPage"; OrderListPage.displayName = "OrderListPage";
export default OrderListPage; export default OrderListPage;

View file

@ -1,9 +1,9 @@
import DialogContentText from "@material-ui/core/DialogContentText"; import DialogContentText from "@material-ui/core/DialogContentText";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ActionDialog from "@saleor/components/ActionDialog"; import ActionDialog from "@saleor/components/ActionDialog";
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
import i18n from "../../../i18n";
export interface OrderMarkAsPaidDialogProps { export interface OrderMarkAsPaidDialogProps {
confirmButtonState: ConfirmButtonTransitionState; confirmButtonState: ConfirmButtonTransitionState;
@ -14,20 +14,25 @@ export interface OrderMarkAsPaidDialogProps {
const OrderMarkAsPaidDialog: React.StatelessComponent< const OrderMarkAsPaidDialog: React.StatelessComponent<
OrderMarkAsPaidDialogProps OrderMarkAsPaidDialogProps
> = ({ confirmButtonState, onClose, onConfirm, open }) => ( > = ({ confirmButtonState, onClose, onConfirm, open }) => {
<ActionDialog const intl = useIntl();
confirmButtonState={confirmButtonState}
open={open} return (
title={i18n.t("Mark order as paid")} <ActionDialog
onClose={onClose} confirmButtonState={confirmButtonState}
onConfirm={onConfirm} open={open}
> title={intl.formatMessage({
<DialogContentText> defaultMessage: "Mark Order as Paid",
{i18n.t("Are you sure you want to mark this order as paid?", { description: "dialog header"
context: "modal content"
})} })}
</DialogContentText> onClose={onClose}
</ActionDialog> onConfirm={onConfirm}
); >
<DialogContentText>
<FormattedMessage defaultMessage="Are you sure you want to mark this order as paid?" />
</DialogContentText>
</ActionDialog>
);
};
OrderMarkAsPaidDialog.displayName = "OrderMarkAsPaidDialog"; OrderMarkAsPaidDialog.displayName = "OrderMarkAsPaidDialog";
export default OrderMarkAsPaidDialog; export default OrderMarkAsPaidDialog;

View file

@ -9,13 +9,13 @@ import {
WithStyles WithStyles
} from "@material-ui/core/styles"; } from "@material-ui/core/styles";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import { Hr } from "@saleor/components/Hr"; import { Hr } from "@saleor/components/Hr";
import Money, { subtractMoney } from "@saleor/components/Money"; import Money, { subtractMoney } from "@saleor/components/Money";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import StatusLabel from "@saleor/components/StatusLabel"; import StatusLabel from "@saleor/components/StatusLabel";
import i18n from "../../../i18n";
import { maybe, transformPaymentStatus } from "../../../misc"; import { maybe, transformPaymentStatus } from "../../../misc";
import { OrderAction, OrderStatus } from "../../../types/globalTypes"; import { OrderAction, OrderStatus } from "../../../types/globalTypes";
import { OrderDetails_order } from "../../types/OrderDetails"; import { OrderDetails_order } from "../../types/OrderDetails";
@ -52,6 +52,8 @@ const OrderPayment = withStyles(styles, { name: "OrderPayment" })(
onRefund, onRefund,
onVoid onVoid
}: OrderPaymentProps) => { }: OrderPaymentProps) => {
const intl = useIntl();
const canCapture = maybe(() => order.actions, []).includes( const canCapture = maybe(() => order.actions, []).includes(
OrderAction.CAPTURE OrderAction.CAPTURE
); );
@ -78,16 +80,25 @@ const OrderPayment = withStyles(styles, { name: "OrderPayment" })(
<table className={classes.root}> <table className={classes.root}>
<tbody> <tbody>
<tr> <tr>
<td>{i18n.t("Subtotal")}</td> <td>
<FormattedMessage
defaultMessage="Subtotal"
description="order subtotal price"
/>
</td>
<td> <td>
{maybe(() => order.lines) === undefined ? ( {maybe(() => order.lines) === undefined ? (
<Skeleton /> <Skeleton />
) : ( ) : (
i18n.t("{{ quantity }} items", { <FormattedMessage
quantity: order.lines defaultMessage="{quantity} items"
.map(line => line.quantity) description="ordered products"
.reduce((curr, prev) => prev + curr, 0) values={{
}) quantity: order.lines
.map(line => line.quantity)
.reduce((curr, prev) => prev + curr, 0)
}}
/>
)} )}
</td> </td>
<td className={classes.textRight}> <td className={classes.textRight}>
@ -99,14 +110,23 @@ const OrderPayment = withStyles(styles, { name: "OrderPayment" })(
</td> </td>
</tr> </tr>
<tr> <tr>
<td>{i18n.t("Taxes")}</td> <td>
<FormattedMessage defaultMessage="Taxes" />
</td>
<td> <td>
{maybe(() => order.total.tax) === undefined ? ( {maybe(() => order.total.tax) === undefined ? (
<Skeleton /> <Skeleton />
) : order.total.tax.amount > 0 ? ( ) : order.total.tax.amount > 0 ? (
i18n.t("VAT included") intl.formatMessage({
defaultMessage: "VAT included",
description: "vat included in order price"
})
) : ( ) : (
i18n.t("does not apply") intl.formatMessage({
defaultMessage: "does not apply",
description: "vat not included in order price",
id: "orderPaymentVATDoesNotApply"
})
)} )}
</td> </td>
<td className={classes.textRight}> <td className={classes.textRight}>
@ -118,13 +138,22 @@ const OrderPayment = withStyles(styles, { name: "OrderPayment" })(
</td> </td>
</tr> </tr>
<tr> <tr>
<td>{i18n.t("Shipping")}</td> <td>
<FormattedMessage
defaultMessage="Shipping"
description="order shipping method name"
/>
</td>
<td> <td>
{maybe(() => order.shippingMethodName) === undefined && {maybe(() => order.shippingMethodName) === undefined &&
maybe(() => order.shippingPrice) === undefined ? ( maybe(() => order.shippingPrice) === undefined ? (
<Skeleton /> <Skeleton />
) : order.shippingMethodName === null ? ( ) : order.shippingMethodName === null ? (
i18n.t("does not apply") intl.formatMessage({
defaultMessage: "does not apply",
description: "order does not require shipping",
id: "orderPaymentShippingDoesNotApply"
})
) : ( ) : (
order.shippingMethodName order.shippingMethodName
)} )}
@ -138,7 +167,12 @@ const OrderPayment = withStyles(styles, { name: "OrderPayment" })(
</td> </td>
</tr> </tr>
<tr className={classes.totalRow}> <tr className={classes.totalRow}>
<td>{i18n.t("Total")}</td> <td>
<FormattedMessage
defaultMessage="Total"
description="order total price"
/>
</td>
<td /> <td />
<td className={classes.textRight}> <td className={classes.textRight}>
{maybe(() => order.total.gross) === undefined ? ( {maybe(() => order.total.gross) === undefined ? (
@ -156,7 +190,12 @@ const OrderPayment = withStyles(styles, { name: "OrderPayment" })(
<table className={classes.root}> <table className={classes.root}>
<tbody> <tbody>
<tr> <tr>
<td>{i18n.t("Preauthorized amount")}</td> <td>
<FormattedMessage
defaultMessage="Preauthorized amount"
description="order payment"
/>
</td>
<td className={classes.textRight}> <td className={classes.textRight}>
{maybe(() => order.totalAuthorized.amount) === undefined ? ( {maybe(() => order.totalAuthorized.amount) === undefined ? (
<Skeleton /> <Skeleton />
@ -166,7 +205,12 @@ const OrderPayment = withStyles(styles, { name: "OrderPayment" })(
</td> </td>
</tr> </tr>
<tr> <tr>
<td>{i18n.t("Captured amount")}</td> <td>
<FormattedMessage
defaultMessage="Captured amount"
description="order payment"
/>
</td>
<td className={classes.textRight}> <td className={classes.textRight}>
{maybe(() => order.totalCaptured.amount) === undefined ? ( {maybe(() => order.totalCaptured.amount) === undefined ? (
<Skeleton /> <Skeleton />
@ -176,7 +220,12 @@ const OrderPayment = withStyles(styles, { name: "OrderPayment" })(
</td> </td>
</tr> </tr>
<tr className={classes.totalRow}> <tr className={classes.totalRow}>
<td>{i18n.t("Outstanding Balance")}</td> <td>
<FormattedMessage
defaultMessage="Outstanding Balance"
description="order payment"
/>
</td>
<td className={classes.textRight}> <td className={classes.textRight}>
{maybe( {maybe(
() => order.total.gross.amount && order.totalCaptured.amount () => order.total.gross.amount && order.totalCaptured.amount
@ -202,22 +251,34 @@ const OrderPayment = withStyles(styles, { name: "OrderPayment" })(
<CardActions> <CardActions>
{canCapture && ( {canCapture && (
<Button color="primary" variant="text" onClick={onCapture}> <Button color="primary" variant="text" onClick={onCapture}>
{i18n.t("Capture", { context: "button" })} <FormattedMessage
defaultMessage="Capture"
description="capture payment, button"
/>
</Button> </Button>
)} )}
{canRefund && ( {canRefund && (
<Button color="primary" variant="text" onClick={onRefund}> <Button color="primary" variant="text" onClick={onRefund}>
{i18n.t("Refund", { context: "button" })} <FormattedMessage
defaultMessage="Refund"
description="button"
/>
</Button> </Button>
)} )}
{canVoid && ( {canVoid && (
<Button color="primary" variant="text" onClick={onVoid}> <Button color="primary" variant="text" onClick={onVoid}>
{i18n.t("Void", { context: "button" })} <FormattedMessage
defaultMessage="Void"
description="void payment, button"
/>
</Button> </Button>
)} )}
{canMarkAsPaid && ( {canMarkAsPaid && (
<Button color="primary" variant="text" onClick={onMarkAsPaid}> <Button color="primary" variant="text" onClick={onMarkAsPaid}>
{i18n.t("Mark as paid", { context: "button" })} <FormattedMessage
defaultMessage="Mark as paid"
description="order, button"
/>
</Button> </Button>
)} )}
</CardActions> </CardActions>

View file

@ -5,12 +5,13 @@ import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle"; import DialogTitle from "@material-ui/core/DialogTitle";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ConfirmButton, { import ConfirmButton, {
ConfirmButtonTransitionState ConfirmButtonTransitionState
} from "@saleor/components/ConfirmButton"; } from "@saleor/components/ConfirmButton";
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
import i18n from "../../../i18n"; import { buttonMessages } from "@saleor/intl";
export interface FormData { export interface FormData {
amount: number; amount: number;
@ -32,58 +33,71 @@ const OrderPaymentDialog: React.StatelessComponent<OrderPaymentDialogProps> = ({
variant, variant,
onClose, onClose,
onSubmit onSubmit
}) => ( }) => {
<Dialog onClose={onClose} open={open}> const intl = useIntl();
<Form
initial={{
amount: initial
}}
onSubmit={data => {
onSubmit(data);
onClose();
}}
>
{({ data, change, submit }) => (
<>
<DialogTitle>
{variant === "capture"
? i18n.t("Capture payment", { context: "title" })
: i18n.t("Refund payment", { context: "title" })}
</DialogTitle>
<DialogContent> return (
<TextField <Dialog onClose={onClose} open={open}>
fullWidth <Form
label={i18n.t("Amount")} initial={{
name="amount" amount: initial
onChange={change} }}
inputProps={{ onSubmit={data => {
step: "0.01" onSubmit(data);
}} onClose();
type="number" }}
value={data.amount} >
/> {({ data, change, submit }) => (
</DialogContent> <>
<DialogActions> <DialogTitle>
<Button onClick={onClose}> {variant === "capture"
{i18n.t("Cancel", { context: "button" })} ? intl.formatMessage({
</Button> defaultMessage: "Capture Payment",
<ConfirmButton description: "dialog header"
transitionState={confirmButtonState} })
color="primary" : intl.formatMessage({
variant="contained" defaultMessage: "Refund Payment",
onClick={() => { description: "dialog header"
onClose(); })}
submit(); </DialogTitle>
}}
> <DialogContent>
{i18n.t("Confirm", { context: "button" })} <TextField
</ConfirmButton> fullWidth
</DialogActions> label={intl.formatMessage({
</> defaultMessage: "Amount",
)} description: "amount of refunded money"
</Form> })}
</Dialog> name="amount"
); onChange={change}
inputProps={{
step: "0.01"
}}
type="number"
value={data.amount}
/>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>
<FormattedMessage {...buttonMessages.cancel} />
</Button>
<ConfirmButton
transitionState={confirmButtonState}
color="primary"
variant="contained"
onClick={() => {
onClose();
submit();
}}
>
<FormattedMessage {...buttonMessages.confirm} />
</ConfirmButton>
</DialogActions>
</>
)}
</Form>
</Dialog>
);
};
OrderPaymentDialog.displayName = "OrderPaymentDialog"; OrderPaymentDialog.displayName = "OrderPaymentDialog";
export default OrderPaymentDialog; export default OrderPaymentDialog;

View file

@ -5,11 +5,12 @@ import DialogContent from "@material-ui/core/DialogContent";
import DialogContentText from "@material-ui/core/DialogContentText"; import DialogContentText from "@material-ui/core/DialogContentText";
import DialogTitle from "@material-ui/core/DialogTitle"; import DialogTitle from "@material-ui/core/DialogTitle";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl";
import ConfirmButton, { import ConfirmButton, {
ConfirmButtonTransitionState ConfirmButtonTransitionState
} from "@saleor/components/ConfirmButton"; } from "@saleor/components/ConfirmButton";
import i18n from "../../../i18n"; import { buttonMessages } from "@saleor/intl";
interface OrderPaymentVoidDialogProps { interface OrderPaymentVoidDialogProps {
confirmButtonState: ConfirmButtonTransitionState; confirmButtonState: ConfirmButtonTransitionState;
@ -22,21 +23,28 @@ const OrderPaymentVoidDialog: React.StatelessComponent<
OrderPaymentVoidDialogProps OrderPaymentVoidDialogProps
> = ({ confirmButtonState, open, onConfirm, onClose }) => ( > = ({ confirmButtonState, open, onConfirm, onClose }) => (
<Dialog onClose={onClose} open={open}> <Dialog onClose={onClose} open={open}>
<DialogTitle>{i18n.t("Void payment", { context: "title" })}</DialogTitle> <DialogTitle>
<FormattedMessage
defaultMessage="Void Payment"
description="dialog header"
/>
</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText> <DialogContentText>
{i18n.t("Are you sure you want to void this payment?")} <FormattedMessage defaultMessage="Are you sure you want to void this payment?" />
</DialogContentText> </DialogContentText>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={onClose}>{i18n.t("Back", { context: "button" })}</Button> <Button onClick={onClose}>
<FormattedMessage {...buttonMessages.back} />
</Button>
<ConfirmButton <ConfirmButton
transitionState={confirmButtonState} transitionState={confirmButtonState}
color="primary" color="primary"
variant="contained" variant="contained"
onClick={onConfirm} onClick={onConfirm}
> >
{i18n.t("Confirm", { context: "button" })} <FormattedMessage {...buttonMessages.confirm} />
</ConfirmButton> </ConfirmButton>
</DialogActions> </DialogActions>
</Dialog> </Dialog>

View file

@ -17,6 +17,7 @@ import TableRow from "@material-ui/core/TableRow";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import React from "react"; import React from "react";
import InfiniteScroll from "react-infinite-scroller"; import InfiniteScroll from "react-infinite-scroller";
import { FormattedMessage, useIntl } from "react-intl";
import Checkbox from "@saleor/components/Checkbox"; import Checkbox from "@saleor/components/Checkbox";
import ConfirmButton, { import ConfirmButton, {
@ -25,7 +26,7 @@ import ConfirmButton, {
import Money from "@saleor/components/Money"; import Money from "@saleor/components/Money";
import TableCellAvatar from "@saleor/components/TableCellAvatar"; import TableCellAvatar from "@saleor/components/TableCellAvatar";
import useSearchQuery from "@saleor/hooks/useSearchQuery"; import useSearchQuery from "@saleor/hooks/useSearchQuery";
import i18n from "@saleor/i18n"; import { buttonMessages } from "@saleor/intl";
import { maybe, renderCollection } from "@saleor/misc"; import { maybe, renderCollection } from "@saleor/misc";
import { FetchMoreProps } from "@saleor/types"; import { FetchMoreProps } from "@saleor/types";
import { import {
@ -161,6 +162,7 @@ const OrderProductAddDialog = withStyles(styles, {
onClose, onClose,
onSubmit onSubmit
}: OrderProductAddDialogProps & WithStyles<typeof styles>) => { }: OrderProductAddDialogProps & WithStyles<typeof styles>) => {
const intl = useIntl();
const [query, onQueryChange] = useSearchQuery(onFetch); const [query, onQueryChange] = useSearchQuery(onFetch);
const [variants, setVariants] = React.useState< const [variants, setVariants] = React.useState<
SearchOrderVariant_products_edges_node_variants[] SearchOrderVariant_products_edges_node_variants[]
@ -187,21 +189,24 @@ const OrderProductAddDialog = withStyles(styles, {
fullWidth fullWidth
maxWidth="sm" maxWidth="sm"
> >
<DialogTitle>{i18n.t("Add product")}</DialogTitle> <DialogTitle>
<FormattedMessage
defaultMessage="Add Product"
description="dialog header"
/>
</DialogTitle>
<DialogContent className={classes.overflow}> <DialogContent className={classes.overflow}>
<TextField <TextField
name="query" name="query"
value={query} value={query}
onChange={onQueryChange} onChange={onQueryChange}
label={i18n.t("Search Products", { label={intl.formatMessage({
context: "product search input label" defaultMessage: "Search Products"
})}
placeholder={intl.formatMessage({
defaultMessage:
"Search by product name, attribute, product type etc..."
})} })}
placeholder={i18n.t(
"Search by product name, attribute, product type etc...",
{
context: "product search input placeholder"
}
)}
fullWidth fullWidth
InputProps={{ InputProps={{
autoComplete: "off", autoComplete: "off",
@ -285,9 +290,13 @@ const OrderProductAddDialog = withStyles(styles, {
<TableCell> <TableCell>
<div>{variant.name}</div> <div>{variant.name}</div>
<div className={classes.grayText}> <div className={classes.grayText}>
{i18n.t("SKU {{ sku }}", { <FormattedMessage
sku: variant.sku defaultMessage="SKU {sku}"
})} description="variant sku"
values={{
sku: variant.sku
}}
/>
</div> </div>
</TableCell> </TableCell>
<TableCell className={classes.textRight}> <TableCell className={classes.textRight}>
@ -301,7 +310,7 @@ const OrderProductAddDialog = withStyles(styles, {
() => ( () => (
<TableRow> <TableRow>
<TableCell colSpan={4}> <TableCell colSpan={4}>
{i18n.t("No products matching given query")} <FormattedMessage defaultMessage="No products matching given query" />
</TableCell> </TableCell>
</TableRow> </TableRow>
) )
@ -312,7 +321,7 @@ const OrderProductAddDialog = withStyles(styles, {
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={onClose}> <Button onClick={onClose}>
{i18n.t("Cancel", { context: "button" })} <FormattedMessage {...buttonMessages.cancel} />
</Button> </Button>
<ConfirmButton <ConfirmButton
transitionState={confirmButtonState} transitionState={confirmButtonState}
@ -321,7 +330,7 @@ const OrderProductAddDialog = withStyles(styles, {
type="submit" type="submit"
onClick={handleSubmit} onClick={handleSubmit}
> >
{i18n.t("Confirm", { context: "button" })} <FormattedMessage {...buttonMessages.confirm} />
</ConfirmButton> </ConfirmButton>
</DialogActions> </DialogActions>
</Dialog> </Dialog>

View file

@ -10,6 +10,7 @@ import {
WithStyles WithStyles
} from "@material-ui/core/styles"; } from "@material-ui/core/styles";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl";
import ConfirmButton, { import ConfirmButton, {
ConfirmButtonTransitionState ConfirmButtonTransitionState
@ -17,7 +18,7 @@ import ConfirmButton, {
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
import Money from "@saleor/components/Money"; import Money from "@saleor/components/Money";
import { SingleSelectField } from "@saleor/components/SingleSelectField"; import { SingleSelectField } from "@saleor/components/SingleSelectField";
import i18n from "../../../i18n"; import { buttonMessages } from "@saleor/intl";
import { OrderDetails_order_availableShippingMethods } from "../../types/OrderDetails"; import { OrderDetails_order_availableShippingMethods } from "../../types/OrderDetails";
export interface FormData { export interface FormData {
@ -85,7 +86,10 @@ const OrderShippingMethodEditDialog = withStyles(styles, {
return ( return (
<Dialog onClose={onClose} open={open} classes={{ paper: classes.dialog }}> <Dialog onClose={onClose} open={open} classes={{ paper: classes.dialog }}>
<DialogTitle> <DialogTitle>
{i18n.t("Edit shipping method", { context: "title" })} <FormattedMessage
defaultMessage="Edit Shipping Method"
description="dialog header"
/>
</DialogTitle> </DialogTitle>
<Form initial={initialForm} onSubmit={onSubmit}> <Form initial={initialForm} onSubmit={onSubmit}>
{({ change, data }) => ( {({ change, data }) => (
@ -100,7 +104,7 @@ const OrderShippingMethodEditDialog = withStyles(styles, {
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={onClose}> <Button onClick={onClose}>
{i18n.t("Cancel", { context: "button" })} <FormattedMessage {...buttonMessages.cancel} />
</Button> </Button>
<ConfirmButton <ConfirmButton
transitionState={confirmButtonState} transitionState={confirmButtonState}
@ -108,7 +112,7 @@ const OrderShippingMethodEditDialog = withStyles(styles, {
variant="contained" variant="contained"
type="submit" type="submit"
> >
{i18n.t("Confirm", { context: "button" })} <FormattedMessage {...buttonMessages.confirm} />
</ConfirmButton> </ConfirmButton>
</DialogActions> </DialogActions>
</> </>

View file

@ -8,6 +8,7 @@ import TableCell from "@material-ui/core/TableCell";
import TableHead from "@material-ui/core/TableHead"; import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow"; import TableRow from "@material-ui/core/TableRow";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import Money from "@saleor/components/Money"; import Money from "@saleor/components/Money";
@ -16,7 +17,6 @@ import StatusLabel from "@saleor/components/StatusLabel";
import TableCellAvatar, { import TableCellAvatar, {
AVATAR_MARGIN AVATAR_MARGIN
} from "@saleor/components/TableCellAvatar"; } from "@saleor/components/TableCellAvatar";
import i18n from "../../../i18n";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { OrderDetails_order_lines } from "../../types/OrderDetails"; import { OrderDetails_order_lines } from "../../types/OrderDetails";
@ -58,90 +58,116 @@ interface OrderUnfulfilledItemsProps extends WithStyles<typeof styles> {
const OrderUnfulfilledItems = withStyles(styles, { const OrderUnfulfilledItems = withStyles(styles, {
name: "OrderUnfulfilledItems" name: "OrderUnfulfilledItems"
})(({ canFulfill, classes, lines, onFulfill }: OrderUnfulfilledItemsProps) => ( })(({ canFulfill, classes, lines, onFulfill }: OrderUnfulfilledItemsProps) => {
<Card> const intl = useIntl();
<CardTitle
title={ return (
<StatusLabel <Card>
label={i18n.t("Unfulfilled ({{ quantity }})", { <CardTitle
quantity: lines title={
.map(line => line.quantity - line.quantityFulfilled) <StatusLabel
.reduce((prev, curr) => prev + curr, 0) label={intl.formatMessage(
})} {
status="error" defaultMessage: "Unfulfilled ({quantity})",
/> description: "section header"
} },
/> {
<Table className={classes.table}> quantity: lines
<TableHead> .map(line => line.quantity - line.quantityFulfilled)
<TableRow> .reduce((prev, curr) => prev + curr, 0)
<TableCell className={classes.colName}> }
<span className={classes.colNameLabel}>{i18n.t("Product")}</span> )}
</TableCell> status="error"
<TableCell className={classes.colQuantity}> />
{i18n.t("Quantity")} }
</TableCell> />
<TableCell className={classes.colPrice}>{i18n.t("Price")}</TableCell> <Table className={classes.table}>
<TableCell className={classes.colTotal}>{i18n.t("Total")}</TableCell> <TableHead>
</TableRow> <TableRow>
</TableHead> <TableCell className={classes.colName}>
<TableBody> <span className={classes.colNameLabel}>
{lines.map(line => ( <FormattedMessage
<TableRow defaultMessage="Product"
className={!!line ? classes.clickableRow : undefined} description="product name"
hover={!!line} />
key={maybe(() => line.id)} </span>
> </TableCell>
<TableCellAvatar
className={classes.colName}
thumbnail={maybe(() => line.thumbnail.url)}
>
{maybe(() => line.productName) || <Skeleton />}
</TableCellAvatar>
<TableCell className={classes.colQuantity}> <TableCell className={classes.colQuantity}>
{maybe(() => line.quantity - line.quantityFulfilled) || ( <FormattedMessage
<Skeleton /> defaultMessage="Quantity"
)} description="ordered products"
/>
</TableCell> </TableCell>
<TableCell className={classes.colPrice}> <TableCell className={classes.colPrice}>
{maybe(() => line.unitPrice.gross) ? ( <FormattedMessage
<Money money={line.unitPrice.gross} /> defaultMessage="Price"
) : ( description="product unit price"
<Skeleton /> />
)}
</TableCell> </TableCell>
<TableCell className={classes.colTotal}> <TableCell className={classes.colTotal}>
{maybe( <FormattedMessage
() => defaultMessage="Total"
(line.quantity - line.quantityFulfilled) * description="order line total price"
line.unitPrice.gross.amount />
) ? (
<Money
money={{
amount:
(line.quantity - line.quantityFulfilled) *
line.unitPrice.gross.amount,
currency: line.unitPrice.gross.currency
}}
/>
) : (
<Skeleton />
)}
</TableCell> </TableCell>
</TableRow> </TableRow>
))} </TableHead>
</TableBody> <TableBody>
</Table> {lines.map(line => (
{canFulfill && ( <TableRow
<CardActions> className={!!line ? classes.clickableRow : undefined}
<Button variant="text" color="primary" onClick={onFulfill}> hover={!!line}
{i18n.t("Fulfill", { key={maybe(() => line.id)}
context: "button" >
})} <TableCellAvatar
</Button> className={classes.colName}
</CardActions> thumbnail={maybe(() => line.thumbnail.url)}
)} >
</Card> {maybe(() => line.productName) || <Skeleton />}
)); </TableCellAvatar>
<TableCell className={classes.colQuantity}>
{maybe(() => line.quantity - line.quantityFulfilled) || (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colPrice}>
{maybe(() => line.unitPrice.gross) ? (
<Money money={line.unitPrice.gross} />
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colTotal}>
{maybe(
() =>
(line.quantity - line.quantityFulfilled) *
line.unitPrice.gross.amount
) ? (
<Money
money={{
amount:
(line.quantity - line.quantityFulfilled) *
line.unitPrice.gross.amount,
currency: line.unitPrice.gross.currency
}}
/>
) : (
<Skeleton />
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{canFulfill && (
<CardActions>
<Button variant="text" color="primary" onClick={onFulfill}>
<FormattedMessage defaultMessage="Fulfill" description="button" />
</Button>
</CardActions>
)}
</Card>
);
});
OrderUnfulfilledItems.displayName = "OrderUnfulfilledItems"; OrderUnfulfilledItems.displayName = "OrderUnfulfilledItems";
export default OrderUnfulfilledItems; export default OrderUnfulfilledItems;

View file

@ -1,9 +1,10 @@
import { parse as parseQs } from "qs"; import { parse as parseQs } from "qs";
import React from "react"; import React from "react";
import { useIntl } from "react-intl";
import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { Route, RouteComponentProps, Switch } from "react-router-dom";
import { sectionNames } from "@saleor/intl";
import { WindowTitle } from "../components/WindowTitle"; import { WindowTitle } from "../components/WindowTitle";
import i18n from "../i18n";
import { import {
orderDraftListPath, orderDraftListPath,
OrderDraftListUrlQueryParams, OrderDraftListUrlQueryParams,
@ -46,15 +47,19 @@ const OrderDetails: React.StatelessComponent<RouteComponentProps<any>> = ({
); );
}; };
const Component = () => ( const Component = () => {
<> const intl = useIntl();
<WindowTitle title={i18n.t("Orders")} />
<Switch> return (
<Route exact path={orderDraftListPath} component={OrderDraftList} /> <>
<Route exact path={orderListPath} component={OrderList} /> <WindowTitle title={intl.formatMessage(sectionNames.orders)} />
<Route path={orderPath(":id")} component={OrderDetails} /> <Switch>
</Switch> <Route exact path={orderDraftListPath} component={OrderDraftList} />
</> <Route exact path={orderListPath} component={OrderList} />
); <Route path={orderPath(":id")} component={OrderDetails} />
</Switch>
</>
);
};
export default Component; export default Component;

View file

@ -1,8 +1,8 @@
import React from "react"; import React from "react";
import { useIntl } from "react-intl";
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 i18n from "../../../i18n";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { OrderAddNote } from "../../types/OrderAddNote"; import { OrderAddNote } from "../../types/OrderAddNote";
import { OrderCancel } from "../../types/OrderCancel"; import { OrderCancel } from "../../types/OrderCancel";
@ -52,55 +52,65 @@ export const OrderDetailsMessages: React.StatelessComponent<
> = ({ children }) => { > = ({ children }) => {
const navigate = useNavigator(); const navigate = useNavigator();
const pushMessage = useNotifier(); const pushMessage = useNotifier();
const intl = useIntl();
const handlePaymentCapture = (data: OrderCapture) => { const handlePaymentCapture = (data: OrderCapture) => {
if (!maybe(() => data.orderCapture.errors.length)) { if (!maybe(() => data.orderCapture.errors.length)) {
pushMessage({ pushMessage({
text: i18n.t("Payment successfully captured", { text: intl.formatMessage({
context: "notification" defaultMessage: "Payment successfully captured"
}) })
}); });
} else { } else {
pushMessage({ pushMessage({
text: i18n.t("Payment not captured: {{ errorMessage }}", { text: intl.formatMessage(
context: "notification", {
errorMessage: data.orderCapture.errors.filter( defaultMessage: "Payment not captured: {errorMessage}"
error => error.field === "payment" },
)[0].message {
}) errorMessage: data.orderCapture.errors.find(
error => error.field === "payment"
).message
}
)
}); });
} }
}; };
const handlePaymentRefund = (data: OrderRefund) => { const handlePaymentRefund = (data: OrderRefund) => {
if (!maybe(() => data.orderRefund.errors.length)) { if (!maybe(() => data.orderRefund.errors.length)) {
pushMessage({ pushMessage({
text: i18n.t("Payment successfully refunded", { text: intl.formatMessage({
context: "notification" defaultMessage: "Payment successfully refunded"
}) })
}); });
} else { } else {
pushMessage({ pushMessage({
text: i18n.t("Payment not refunded: {{ errorMessage }}", { text: intl.formatMessage(
context: "notification", {
errorMessage: data.orderRefund.errors.filter( defaultMessage: "Payment not refunded: {errorMessage}",
error => error.field === "payment" description: "notification"
)[0].message },
}) {
errorMessage: data.orderRefund.errors.find(
error => error.field === "payment"
).message
}
)
}); });
} }
}; };
const handleOrderFulfillmentCreate = (data: OrderCreateFulfillment) => { const handleOrderFulfillmentCreate = (data: OrderCreateFulfillment) => {
if (!maybe(() => data.orderFulfillmentCreate.errors.length)) { if (!maybe(() => data.orderFulfillmentCreate.errors.length)) {
pushMessage({ pushMessage({
text: i18n.t("Items successfully fulfilled", { text: intl.formatMessage({
context: "notification" defaultMessage: "Items successfully fulfilled"
}) })
}); });
navigate(orderUrl(data.orderFulfillmentCreate.order.id), true); navigate(orderUrl(data.orderFulfillmentCreate.order.id), true);
} else { } else {
pushMessage({ pushMessage({
text: i18n.t("Could not fulfill items", { text: intl.formatMessage({
context: "notification" defaultMessage: "Could not fulfill items"
}) })
}); });
} }
@ -108,53 +118,53 @@ export const OrderDetailsMessages: React.StatelessComponent<
const handleOrderMarkAsPaid = (data: OrderMarkAsPaid) => { const handleOrderMarkAsPaid = (data: OrderMarkAsPaid) => {
if (!maybe(() => data.orderMarkAsPaid.errors.length)) { if (!maybe(() => data.orderMarkAsPaid.errors.length)) {
pushMessage({ pushMessage({
text: i18n.t("Order marked as paid", { text: intl.formatMessage({
context: "notification" defaultMessage: "Order marked as paid"
}) })
}); });
navigate(orderUrl(data.orderMarkAsPaid.order.id), true); navigate(orderUrl(data.orderMarkAsPaid.order.id), true);
} else { } else {
pushMessage({ pushMessage({
text: i18n.t("Could not mark order as paid", { text: intl.formatMessage({
context: "notification" defaultMessage: "Could not mark order as paid"
}) })
}); });
} }
}; };
const handleOrderCancel = (data: OrderCancel) => { const handleOrderCancel = (data: OrderCancel) => {
pushMessage({ pushMessage({
text: i18n.t("Order successfully cancelled", { text: intl.formatMessage({
context: "notification" defaultMessage: "Order successfully cancelled"
}) })
}); });
navigate(orderUrl(data.orderCancel.order.id), true); navigate(orderUrl(data.orderCancel.order.id), true);
}; };
const handleDraftCancel = () => { const handleDraftCancel = () => {
pushMessage({ pushMessage({
text: i18n.t("Order successfully cancelled", { text: intl.formatMessage({
context: "notification" defaultMessage: "Order successfully cancelled"
}) })
}); });
navigate(orderListUrl(), true); navigate(orderListUrl(), true);
}; };
const handleOrderVoid = () => { const handleOrderVoid = () => {
pushMessage({ pushMessage({
text: i18n.t("Order payment successfully voided", { text: intl.formatMessage({
context: "notification" defaultMessage: "Order payment successfully voided"
}) })
}); });
}; };
const handleNoteAdd = (data: OrderAddNote) => { const handleNoteAdd = (data: OrderAddNote) => {
if (!maybe(() => data.orderAddNote.errors.length)) { if (!maybe(() => data.orderAddNote.errors.length)) {
pushMessage({ pushMessage({
text: i18n.t("Note successfully added", { text: intl.formatMessage({
context: "notification" defaultMessage: "Note successfully added"
}) })
}); });
} else { } else {
pushMessage({ pushMessage({
text: i18n.t("Could not add note", { text: intl.formatMessage({
context: "notification" defaultMessage: "Could not add note"
}) })
}); });
} }
@ -162,8 +172,8 @@ export const OrderDetailsMessages: React.StatelessComponent<
const handleUpdate = (data: OrderUpdate) => { const handleUpdate = (data: OrderUpdate) => {
if (!maybe(() => data.orderUpdate.errors.length)) { if (!maybe(() => data.orderUpdate.errors.length)) {
pushMessage({ pushMessage({
text: i18n.t("Order successfully updated", { text: intl.formatMessage({
context: "notification" defaultMessage: "Order successfully updated"
}) })
}); });
navigate(orderUrl(data.orderUpdate.order.id), true); navigate(orderUrl(data.orderUpdate.order.id), true);
@ -172,8 +182,8 @@ export const OrderDetailsMessages: React.StatelessComponent<
const handleDraftUpdate = (data: OrderDraftUpdate) => { const handleDraftUpdate = (data: OrderDraftUpdate) => {
if (!maybe(() => data.draftOrderUpdate.errors.length)) { if (!maybe(() => data.draftOrderUpdate.errors.length)) {
pushMessage({ pushMessage({
text: i18n.t("Order successfully updated", { text: intl.formatMessage({
context: "notification" defaultMessage: "Order successfully updated"
}) })
}); });
navigate(orderUrl(data.draftOrderUpdate.order.id), true); navigate(orderUrl(data.draftOrderUpdate.order.id), true);
@ -182,14 +192,14 @@ export const OrderDetailsMessages: React.StatelessComponent<
const handleShippingMethodUpdate = (data: OrderShippingMethodUpdate) => { const handleShippingMethodUpdate = (data: OrderShippingMethodUpdate) => {
if (!maybe(() => data.orderUpdateShipping.errors.length)) { if (!maybe(() => data.orderUpdateShipping.errors.length)) {
pushMessage({ pushMessage({
text: i18n.t("Shipping method successfully updated", { text: intl.formatMessage({
context: "notification" defaultMessage: "Shipping method successfully updated"
}) })
}); });
} else { } else {
pushMessage({ pushMessage({
text: i18n.t("Could not update shipping method", { text: intl.formatMessage({
context: "notification" defaultMessage: "Could not update shipping method"
}) })
}); });
} }
@ -198,14 +208,14 @@ export const OrderDetailsMessages: React.StatelessComponent<
const handleOrderLineDelete = (data: OrderLineDelete) => { const handleOrderLineDelete = (data: OrderLineDelete) => {
if (!maybe(() => data.draftOrderLineDelete.errors.length)) { if (!maybe(() => data.draftOrderLineDelete.errors.length)) {
pushMessage({ pushMessage({
text: i18n.t("Order line deleted", { text: intl.formatMessage({
context: "notification" defaultMessage: "Order line deleted"
}) })
}); });
} else { } else {
pushMessage({ pushMessage({
text: i18n.t("Could not delete order line", { text: intl.formatMessage({
context: "notification" defaultMessage: "Could not delete order line"
}) })
}); });
} }
@ -213,15 +223,15 @@ export const OrderDetailsMessages: React.StatelessComponent<
const handleOrderLinesAdd = (data: OrderLinesAdd) => { const handleOrderLinesAdd = (data: OrderLinesAdd) => {
if (!maybe(() => data.draftOrderLinesCreate.errors.length)) { if (!maybe(() => data.draftOrderLinesCreate.errors.length)) {
pushMessage({ pushMessage({
text: i18n.t("Order line added", { text: intl.formatMessage({
context: "notification" defaultMessage: "Order line added"
}) })
}); });
navigate(orderUrl(data.draftOrderLinesCreate.order.id), true); navigate(orderUrl(data.draftOrderLinesCreate.order.id), true);
} else { } else {
pushMessage({ pushMessage({
text: i18n.t("Could not create order line", { text: intl.formatMessage({
context: "notification" defaultMessage: "Could not create order line"
}) })
}); });
} }
@ -229,14 +239,14 @@ export const OrderDetailsMessages: React.StatelessComponent<
const handleOrderLineUpdate = (data: OrderLineUpdate) => { const handleOrderLineUpdate = (data: OrderLineUpdate) => {
if (!maybe(() => data.draftOrderLineUpdate.errors.length)) { if (!maybe(() => data.draftOrderLineUpdate.errors.length)) {
pushMessage({ pushMessage({
text: i18n.t("Order line updated", { text: intl.formatMessage({
context: "notification" defaultMessage: "Order line updated"
}) })
}); });
} else { } else {
pushMessage({ pushMessage({
text: i18n.t("Could not update order line", { text: intl.formatMessage({
context: "notification" defaultMessage: "Could not update order line"
}) })
}); });
} }
@ -244,15 +254,15 @@ export const OrderDetailsMessages: React.StatelessComponent<
const handleOrderFulfillmentCancel = (data: OrderFulfillmentCancel) => { const handleOrderFulfillmentCancel = (data: OrderFulfillmentCancel) => {
if (!maybe(() => data.orderFulfillmentCancel.errors.length)) { if (!maybe(() => data.orderFulfillmentCancel.errors.length)) {
pushMessage({ pushMessage({
text: i18n.t("Fulfillment successfully cancelled", { text: intl.formatMessage({
context: "notification" defaultMessage: "Fulfillment successfully cancelled"
}) })
}); });
navigate(orderUrl(data.orderFulfillmentCancel.order.id), true); navigate(orderUrl(data.orderFulfillmentCancel.order.id), true);
} else { } else {
pushMessage({ pushMessage({
text: i18n.t("Could not cancel fulfillment", { text: intl.formatMessage({
context: "notification" defaultMessage: "Could not cancel fulfillment"
}) })
}); });
} }
@ -262,15 +272,15 @@ export const OrderDetailsMessages: React.StatelessComponent<
) => { ) => {
if (!maybe(() => data.orderFulfillmentUpdateTracking.errors.length)) { if (!maybe(() => data.orderFulfillmentUpdateTracking.errors.length)) {
pushMessage({ pushMessage({
text: i18n.t("Fulfillment successfully updated", { text: intl.formatMessage({
context: "notification" defaultMessage: "Fulfillment successfully updated"
}) })
}); });
navigate(orderUrl(data.orderFulfillmentUpdateTracking.order.id), true); navigate(orderUrl(data.orderFulfillmentUpdateTracking.order.id), true);
} else { } else {
pushMessage({ pushMessage({
text: i18n.t("Could not update fulfillment", { text: intl.formatMessage({
context: "notification" defaultMessage: "Could not update fulfillment"
}) })
}); });
} }
@ -278,15 +288,15 @@ export const OrderDetailsMessages: React.StatelessComponent<
const handleDraftFinalize = (data: OrderDraftFinalize) => { const handleDraftFinalize = (data: OrderDraftFinalize) => {
if (!maybe(() => data.draftOrderComplete.errors.length)) { if (!maybe(() => data.draftOrderComplete.errors.length)) {
pushMessage({ pushMessage({
text: i18n.t("Draft order successfully finalized", { text: intl.formatMessage({
context: "notification" defaultMessage: "Draft order successfully finalized"
}) })
}); });
navigate(orderUrl(data.draftOrderComplete.order.id), true); navigate(orderUrl(data.draftOrderComplete.order.id), true);
} else { } else {
pushMessage({ pushMessage({
text: i18n.t("Could not finalize draft", { text: intl.formatMessage({
context: "notification" defaultMessage: "Could not finalize draft"
}) })
}); });
} }

View file

@ -2,6 +2,7 @@ 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 React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ActionDialog from "@saleor/components/ActionDialog"; import ActionDialog from "@saleor/components/ActionDialog";
import useBulkActions from "@saleor/hooks/useBulkActions"; import useBulkActions from "@saleor/hooks/useBulkActions";
@ -11,7 +12,6 @@ import useNotifier from "@saleor/hooks/useNotifier";
import usePaginator, { import usePaginator, {
createPaginationState createPaginationState
} from "@saleor/hooks/usePaginator"; } from "@saleor/hooks/usePaginator";
import i18n from "@saleor/i18n";
import { getMutationState, maybe } from "@saleor/misc"; import { getMutationState, maybe } from "@saleor/misc";
import { ListViews } from "@saleor/types"; import { ListViews } from "@saleor/types";
import OrderDraftListPage from "../components/OrderDraftListPage"; import OrderDraftListPage from "../components/OrderDraftListPage";
@ -44,6 +44,7 @@ export const OrderDraftList: React.StatelessComponent<OrderDraftListProps> = ({
const { updateListSettings, settings } = useListSettings( const { updateListSettings, settings } = useListSettings(
ListViews.DRAFT_LIST ListViews.DRAFT_LIST
); );
const intl = useIntl();
const closeModal = () => const closeModal = () =>
navigate( navigate(
@ -56,7 +57,9 @@ export const OrderDraftList: React.StatelessComponent<OrderDraftListProps> = ({
const handleCreateOrderCreateSuccess = (data: OrderDraftCreate) => { const handleCreateOrderCreateSuccess = (data: OrderDraftCreate) => {
notify({ notify({
text: i18n.t("Order draft succesfully created") text: intl.formatMessage({
defaultMessage: "Order draft succesfully created"
})
}); });
navigate(orderUrl(data.draftOrderCreate.order.id)); navigate(orderUrl(data.draftOrderCreate.order.id));
}; };
@ -77,7 +80,9 @@ export const OrderDraftList: React.StatelessComponent<OrderDraftListProps> = ({
const handleOrderDraftBulkCancel = (data: OrderDraftBulkCancel) => { const handleOrderDraftBulkCancel = (data: OrderDraftBulkCancel) => {
if (data.draftOrderBulkDelete.errors.length === 0) { if (data.draftOrderBulkDelete.errors.length === 0) {
notify({ notify({
text: i18n.t("Removed draft orders") text: intl.formatMessage({
defaultMessage: "Removed draft orders"
})
}); });
refetch(); refetch();
reset(); reset();
@ -145,22 +150,29 @@ export const OrderDraftList: React.StatelessComponent<OrderDraftListProps> = ({
onClose={closeModal} onClose={closeModal}
onConfirm={onOrderDraftBulkDelete} onConfirm={onOrderDraftBulkDelete}
open={params.action === "remove"} open={params.action === "remove"}
title={i18n.t("Remove Order Drafts")} title={intl.formatMessage({
defaultMessage: "Delete Order Drafts",
description: "dialog header"
})}
variant="delete" variant="delete"
> >
<DialogContentText <DialogContentText>
dangerouslySetInnerHTML={{ <FormattedMessage
__html: i18n.t( defaultMessage="Are you sure you want to delete {counter, plural,
"Are you sure you want to remove <strong>{{ number }}</strong> order drafts?", one {this order draft}
{ other {{displayQuantity} orderDrafts}
number: maybe( }?"
() => params.ids.length.toString(), description="dialog content"
"..." values={{
) counter: maybe(() => params.ids.length),
} displayQuantity: (
) <strong>
}} {maybe(() => params.ids.length)}
/> </strong>
)
}}
/>
</DialogContentText>
</ActionDialog> </ActionDialog>
</> </>
); );

View file

@ -1,5 +1,6 @@
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog"; import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
import SaveFilterTabDialog, { import SaveFilterTabDialog, {
@ -14,7 +15,6 @@ import usePaginator, {
createPaginationState createPaginationState
} from "@saleor/hooks/usePaginator"; } from "@saleor/hooks/usePaginator";
import useShop from "@saleor/hooks/useShop"; import useShop from "@saleor/hooks/useShop";
import i18n from "@saleor/i18n";
import { getMutationState, maybe } from "@saleor/misc"; import { getMutationState, maybe } from "@saleor/misc";
import { ListViews } from "@saleor/types"; import { ListViews } from "@saleor/types";
import OrderBulkCancelDialog from "../../components/OrderBulkCancelDialog"; import OrderBulkCancelDialog from "../../components/OrderBulkCancelDialog";
@ -62,6 +62,7 @@ export const OrderList: React.StatelessComponent<OrderListProps> = ({
const { updateListSettings, settings } = useListSettings( const { updateListSettings, settings } = useListSettings(
ListViews.ORDER_LIST ListViews.ORDER_LIST
); );
const intl = useIntl();
const tabs = getFilterTabs(); const tabs = getFilterTabs();
@ -133,7 +134,9 @@ export const OrderList: React.StatelessComponent<OrderListProps> = ({
const handleCreateOrderCreateSuccess = (data: OrderDraftCreate) => { const handleCreateOrderCreateSuccess = (data: OrderDraftCreate) => {
notify({ notify({
text: i18n.t("Order draft succesfully created") text: intl.formatMessage({
defaultMessage: "Order draft succesfully created"
})
}); });
navigate(orderUrl(data.draftOrderCreate.order.id)); navigate(orderUrl(data.draftOrderCreate.order.id));
}; };
@ -160,8 +163,8 @@ export const OrderList: React.StatelessComponent<OrderListProps> = ({
const handleOrderBulkCancel = (data: OrderBulkCancel) => { const handleOrderBulkCancel = (data: OrderBulkCancel) => {
if (data.orderBulkCancel.errors.length === 0) { if (data.orderBulkCancel.errors.length === 0) {
notify({ notify({
text: i18n.t("Orders cancelled", { text: intl.formatMessage({
context: "notification" defaultMessage: "Orders cancelled"
}) })
}); });
reset(); reset();
@ -218,9 +221,10 @@ export const OrderList: React.StatelessComponent<OrderListProps> = ({
color="primary" color="primary"
onClick={() => openModal("cancel", listElements)} onClick={() => openModal("cancel", listElements)}
> >
{i18n.t("Cancel", { <FormattedMessage
context: "cancel orders" defaultMessage="Cancel"
})} description="cancel orders, button"
/>
</Button> </Button>
} }
onSearchChange={email => changeFilterField({ email })} onSearchChange={email => changeFilterField({ email })}