Refactor discount section translations (#114)

* Refactor discount sectionts translations

Fix bulk action dialogs

Fix id collision

Update pot file

wip

* Fix id collision
This commit is contained in:
Dominik Żegleń 2019-08-26 15:59:32 +02:00 committed by dominik-zeglen
parent a2efcde035
commit 2d0a33fc0d
32 changed files with 2142 additions and 823 deletions

File diff suppressed because it is too large Load diff

1
react-intl.d.ts vendored
View file

@ -5,6 +5,7 @@ declare module "react-intl" {
export interface MessageDescriptor {
description?: string;
defaultMessage: string;
id?: string;
}
type Messages<Names extends keyof any = string> = Record<
Names,

View file

@ -14,13 +14,13 @@ import TableFooter from "@material-ui/core/TableFooter";
import TableRow from "@material-ui/core/TableRow";
import DeleteIcon from "@material-ui/icons/Delete";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import Checkbox from "@saleor/components/Checkbox";
import Skeleton from "@saleor/components/Skeleton";
import TableHead from "@saleor/components/TableHead";
import TablePagination from "@saleor/components/TablePagination";
import i18n from "../../../i18n";
import { maybe, renderCollection } from "../../../misc";
import { ListActions, ListProps } from "../../../types";
import { SaleDetails_sale } from "../../types/SaleDetails";
@ -71,105 +71,123 @@ const DiscountCategories = withStyles(styles, {
toggleAll,
selected,
isChecked
}: DiscountCategoriesProps & WithStyles<typeof styles>) => (
<Card>
<CardTitle
title={i18n.t("Eligible Categories")}
toolbar={
<Button color="primary" onClick={onCategoryAssign}>
{i18n.t("Assign categories")}
</Button>
}
/>
<Table>
<TableHead
colSpan={numberOfColumns}
selected={selected}
disabled={disabled}
items={maybe(() => sale.categories.edges.map(edge => edge.node))}
toggleAll={toggleAll}
toolbar={toolbar}
>
<>
<TableCell className={classes.wideColumn}>
{i18n.t("Category name")}
</TableCell>
<TableCell className={classes.textRight}>
{i18n.t("Products")}
</TableCell>
<TableCell />
</>
</TableHead>
<TableFooter>
<TableRow>
<TablePagination
colSpan={numberOfColumns}
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
onNextPage={onNextPage}
hasPreviousPage={
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
}
onPreviousPage={onPreviousPage}
/>
</TableRow>
</TableFooter>
<TableBody>
{renderCollection(
maybe(() => sale.categories.edges.map(edge => edge.node)),
category => {
const isSelected = category ? isChecked(category.id) : false;
}: DiscountCategoriesProps & WithStyles<typeof styles>) => {
const intl = useIntl();
return (
<TableRow
hover={!!category}
key={category ? category.id : "skeleton"}
onClick={category && onRowClick(category.id)}
className={classes.tableRow}
selected={isSelected}
>
<TableCell padding="checkbox">
<Checkbox
checked={isSelected}
disabled={disabled}
disableClickPropagation
onChange={() => toggle(category.id)}
/>
</TableCell>
<TableCell>
{maybe<React.ReactNode>(() => category.name, <Skeleton />)}
</TableCell>
<TableCell className={classes.textRight}>
{maybe<React.ReactNode>(
() => category.products.totalCount,
<Skeleton />
)}
</TableCell>
<TableCell className={classes.iconCell}>
<IconButton
disabled={!category || disabled}
onClick={event => {
event.stopPropagation();
onCategoryUnassign(category.id);
}}
>
<DeleteIcon color="primary" />
</IconButton>
return (
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Eligible Categories",
description: "section header"
})}
toolbar={
<Button color="primary" onClick={onCategoryAssign}>
<FormattedMessage
defaultMessage="Assign categories"
description="button"
/>
</Button>
}
/>
<Table>
<TableHead
colSpan={numberOfColumns}
selected={selected}
disabled={disabled}
items={maybe(() => sale.categories.edges.map(edge => edge.node))}
toggleAll={toggleAll}
toolbar={toolbar}
>
<>
<TableCell className={classes.wideColumn}>
<FormattedMessage defaultMessage="Category name" />
</TableCell>
<TableCell className={classes.textRight}>
<FormattedMessage
defaultMessage="Products"
description="number of products"
/>
</TableCell>
<TableCell />
</>
</TableHead>
<TableFooter>
<TableRow>
<TablePagination
colSpan={numberOfColumns}
hasNextPage={
pageInfo && !disabled ? pageInfo.hasNextPage : false
}
onNextPage={onNextPage}
hasPreviousPage={
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
}
onPreviousPage={onPreviousPage}
/>
</TableRow>
</TableFooter>
<TableBody>
{renderCollection(
maybe(() => sale.categories.edges.map(edge => edge.node)),
category => {
const isSelected = category ? isChecked(category.id) : false;
return (
<TableRow
hover={!!category}
key={category ? category.id : "skeleton"}
onClick={category && onRowClick(category.id)}
className={classes.tableRow}
selected={isSelected}
>
<TableCell padding="checkbox">
<Checkbox
checked={isSelected}
disabled={disabled}
disableClickPropagation
onChange={() => toggle(category.id)}
/>
</TableCell>
<TableCell>
{maybe<React.ReactNode>(
() => category.name,
<Skeleton />
)}
</TableCell>
<TableCell className={classes.textRight}>
{maybe<React.ReactNode>(
() => category.products.totalCount,
<Skeleton />
)}
</TableCell>
<TableCell className={classes.iconCell}>
<IconButton
disabled={!category || disabled}
onClick={event => {
event.stopPropagation();
onCategoryUnassign(category.id);
}}
>
<DeleteIcon color="primary" />
</IconButton>
</TableCell>
</TableRow>
);
},
() => (
<TableRow>
<TableCell colSpan={numberOfColumns}>
<FormattedMessage defaultMessage="No categories found" />
</TableCell>
</TableRow>
);
},
() => (
<TableRow>
<TableCell colSpan={numberOfColumns}>
{i18n.t("No categories found")}
</TableCell>
</TableRow>
)
)}
</TableBody>
</Table>
</Card>
)
)
)}
</TableBody>
</Table>
</Card>
);
}
);
DiscountCategories.displayName = "DiscountCategories";
export default DiscountCategories;

View file

@ -14,13 +14,13 @@ import TableFooter from "@material-ui/core/TableFooter";
import TableRow from "@material-ui/core/TableRow";
import DeleteIcon from "@material-ui/icons/Delete";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import Checkbox from "@saleor/components/Checkbox";
import Skeleton from "@saleor/components/Skeleton";
import TableHead from "@saleor/components/TableHead";
import TablePagination from "@saleor/components/TablePagination";
import i18n from "../../../i18n";
import { maybe, renderCollection } from "../../../misc";
import { ListActions, ListProps } from "../../../types";
import { SaleDetails_sale } from "../../types/SaleDetails";
@ -71,105 +71,122 @@ const DiscountCollections = withStyles(styles, {
toggle,
toggleAll,
toolbar
}: DiscountCollectionsProps & WithStyles<typeof styles>) => (
<Card>
<CardTitle
title={i18n.t("Eligible Collections")}
toolbar={
<Button color="primary" onClick={onCollectionAssign}>
{i18n.t("Assign collections")}
</Button>
}
/>
<Table>
<TableHead
colSpan={numberOfColumns}
selected={selected}
disabled={disabled}
items={maybe(() => sale.collections.edges.map(edge => edge.node))}
toggleAll={toggleAll}
toolbar={toolbar}
>
<TableCell className={classes.wideColumn}>
{i18n.t("Collection name")}
</TableCell>
<TableCell className={classes.textRight}>
{i18n.t("Products")}
</TableCell>
<TableCell />
</TableHead>
<TableFooter>
<TableRow>
<TablePagination
colSpan={numberOfColumns}
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
onNextPage={onNextPage}
hasPreviousPage={
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
}
onPreviousPage={onPreviousPage}
/>
</TableRow>
</TableFooter>
<TableBody>
{renderCollection(
maybe(() => sale.collections.edges.map(edge => edge.node)),
collection => {
const isSelected = collection ? isChecked(collection.id) : false;
return (
<TableRow
selected={isSelected}
hover={!!collection}
key={collection ? collection.id : "skeleton"}
onClick={collection && onRowClick(collection.id)}
className={classes.tableRow}
>
<TableCell padding="checkbox">
<Checkbox
checked={isSelected}
disabled={disabled}
disableClickPropagation
onChange={() => toggle(collection.id)}
/>
</TableCell>
<TableCell>
{maybe<React.ReactNode>(
() => collection.name,
<Skeleton />
)}
</TableCell>
<TableCell className={classes.textRight}>
{maybe<React.ReactNode>(
() => collection.products.totalCount,
<Skeleton />
)}
</TableCell>
<TableCell className={classes.iconCell}>
<IconButton
disabled={!collection || disabled}
onClick={event => {
event.stopPropagation();
onCollectionUnassign(collection.id);
}}
>
<DeleteIcon color="primary" />
</IconButton>
}: DiscountCollectionsProps & WithStyles<typeof styles>) => {
const intl = useIntl();
return (
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Eligible Collections",
description: "section header"
})}
toolbar={
<Button color="primary" onClick={onCollectionAssign}>
<FormattedMessage
defaultMessage="Assign collections"
description="button"
/>
</Button>
}
/>
<Table>
<TableHead
colSpan={numberOfColumns}
selected={selected}
disabled={disabled}
items={maybe(() => sale.collections.edges.map(edge => edge.node))}
toggleAll={toggleAll}
toolbar={toolbar}
>
<TableCell className={classes.wideColumn}>
<FormattedMessage defaultMessage="Collection name" />
</TableCell>
<TableCell className={classes.textRight}>
<FormattedMessage
defaultMessage="Products"
description="number of products"
/>
</TableCell>
<TableCell />
</TableHead>
<TableFooter>
<TableRow>
<TablePagination
colSpan={numberOfColumns}
hasNextPage={
pageInfo && !disabled ? pageInfo.hasNextPage : false
}
onNextPage={onNextPage}
hasPreviousPage={
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
}
onPreviousPage={onPreviousPage}
/>
</TableRow>
</TableFooter>
<TableBody>
{renderCollection(
maybe(() => sale.collections.edges.map(edge => edge.node)),
collection => {
const isSelected = collection
? isChecked(collection.id)
: false;
return (
<TableRow
selected={isSelected}
hover={!!collection}
key={collection ? collection.id : "skeleton"}
onClick={collection && onRowClick(collection.id)}
className={classes.tableRow}
>
<TableCell padding="checkbox">
<Checkbox
checked={isSelected}
disabled={disabled}
disableClickPropagation
onChange={() => toggle(collection.id)}
/>
</TableCell>
<TableCell>
{maybe<React.ReactNode>(
() => collection.name,
<Skeleton />
)}
</TableCell>
<TableCell className={classes.textRight}>
{maybe<React.ReactNode>(
() => collection.products.totalCount,
<Skeleton />
)}
</TableCell>
<TableCell className={classes.iconCell}>
<IconButton
disabled={!collection || disabled}
onClick={event => {
event.stopPropagation();
onCollectionUnassign(collection.id);
}}
>
<DeleteIcon color="primary" />
</IconButton>
</TableCell>
</TableRow>
);
},
() => (
<TableRow>
<TableCell colSpan={numberOfColumns}>
<FormattedMessage defaultMessage="No collections found" />
</TableCell>
</TableRow>
);
},
() => (
<TableRow>
<TableCell colSpan={numberOfColumns}>
{i18n.t("No collections found")}
</TableCell>
</TableRow>
)
)}
</TableBody>
</Table>
</Card>
)
)
)}
</TableBody>
</Table>
</Card>
);
}
);
DiscountCollections.displayName = "DiscountCollections";
export default DiscountCollections;

View file

@ -17,6 +17,7 @@ import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography";
import { filter } from "fuzzaldrin";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import Checkbox from "@saleor/components/Checkbox";
import ConfirmButton, {
@ -27,7 +28,7 @@ import FormSpacer from "@saleor/components/FormSpacer";
import Hr from "@saleor/components/Hr";
// tslint:disable no-submodule-imports
import { ShopInfo_shop_countries } from "@saleor/components/Shop/types/ShopInfo";
import i18n from "../../../i18n";
import { buttonMessages } from "@saleor/intl";
interface FormData {
allCountries: boolean;
@ -72,6 +73,8 @@ const DiscountCountrySelectDialog = withStyles(styles, {
initial,
onConfirm
}: DiscountCountrySelectDialogProps & WithStyles<typeof styles>) => {
const intl = useIntl();
const initialForm: FormData = {
allCountries: true,
countries: initial,
@ -90,23 +93,28 @@ const DiscountCountrySelectDialog = withStyles(styles, {
return (
<>
<DialogTitle>{i18n.t("Assign Countries")}</DialogTitle>
<DialogTitle>
<FormattedMessage
defaultMessage="Assign Countries"
description="dialog header"
/>
</DialogTitle>
<DialogContent>
<Typography>
{i18n.t(
"Choose countries, you want voucher to be limited to, from the list below"
)}
<FormattedMessage defaultMessage="Choose countries, you want voucher to be limited to, from the list below" />
</Typography>
<FormSpacer />
<TextField
name="query"
value={data.query}
onChange={event => change(event, () => fetch(data.query))}
label={i18n.t("Search Countries", {
context: "country search input label"
label={intl.formatMessage({
defaultMessage: "Filter Countries",
description: "search box label"
})}
placeholder={i18n.t("Search by country name", {
context: "country search input placeholder"
placeholder={intl.formatMessage({
defaultMessage: "Search by country name",
description: "search box placeholder"
})}
fullWidth
/>
@ -114,9 +122,10 @@ const DiscountCountrySelectDialog = withStyles(styles, {
<Hr />
<DialogContent className={classes.container}>
<Typography className={classes.heading} variant="subtitle1">
{i18n.t("Countries A to Z", {
context: "country selection"
})}
<FormattedMessage
defaultMessage="Countries A to Z"
description="country selection"
/>
</Typography>
<Table>
<TableBody>
@ -167,7 +176,7 @@ const DiscountCountrySelectDialog = withStyles(styles, {
</DialogContent>
<DialogActions>
<Button onClick={onClose}>
{i18n.t("Cancel", { context: "button" })}
<FormattedMessage {...buttonMessages.cancel} />
</Button>
<ConfirmButton
transitionState={confirmButtonState}
@ -175,7 +184,10 @@ const DiscountCountrySelectDialog = withStyles(styles, {
variant="contained"
type="submit"
>
{i18n.t("Assign countries", { context: "button" })}
<FormattedMessage
defaultMessage="Assign countries"
description="button"
/>
</ConfirmButton>
</DialogActions>
</>

View file

@ -14,6 +14,7 @@ import TableFooter from "@material-ui/core/TableFooter";
import TableRow from "@material-ui/core/TableRow";
import DeleteIcon from "@material-ui/icons/Delete";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import Checkbox from "@saleor/components/Checkbox";
@ -24,7 +25,6 @@ import TableCellAvatar, {
} from "@saleor/components/TableCellAvatar";
import TableHead from "@saleor/components/TableHead";
import TablePagination from "@saleor/components/TablePagination";
import i18n from "../../../i18n";
import { maybe, renderCollection } from "../../../misc";
import { ListActions, ListProps } from "../../../types";
import { SaleDetails_sale } from "../../types/SaleDetails";
@ -84,126 +84,145 @@ const DiscountProducts = withStyles(styles, {
toggle,
toggleAll,
toolbar
}: SaleProductsProps & WithStyles<typeof styles>) => (
<Card>
<CardTitle
title={i18n.t("Eligible Products")}
toolbar={
<Button color="primary" onClick={onProductAssign}>
{i18n.t("Assign products")}
</Button>
}
/>
<Table>
<TableHead
colSpan={numberOfColumns}
selected={selected}
disabled={disabled}
items={maybe(() => sale.products.edges.map(edge => edge.node))}
toggleAll={toggleAll}
toolbar={toolbar}
>
<TableCell className={classes.colName}>
<span className={classes.colNameLabel}>
{i18n.t("Product name")}
</span>
</TableCell>
<TableCell className={classes.colType}>
{i18n.t("Product Type")}
</TableCell>
<TableCell className={classes.colPublished}>
{i18n.t("Published")}
</TableCell>
<TableCell />
</TableHead>
<TableFooter>
<TableRow>
<TablePagination
colSpan={numberOfColumns}
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false}
onNextPage={onNextPage}
hasPreviousPage={
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
}
onPreviousPage={onPreviousPage}
/>
</TableRow>
</TableFooter>
<TableBody>
{renderCollection(
maybe(() => sale.products.edges.map(edge => edge.node)),
product => {
const isSelected = product ? isChecked(product.id) : false;
return (
<TableRow
hover={!!product}
key={product ? product.id : "skeleton"}
onClick={product && onRowClick(product.id)}
className={classes.tableRow}
selected={isSelected}
>
<TableCell padding="checkbox">
<Checkbox
checked={isSelected}
disabled={disabled}
disableClickPropagation
onChange={() => toggle(product.id)}
/>
</TableCell>
<TableCellAvatar
className={classes.colName}
thumbnail={maybe(() => product.thumbnail.url)}
}: SaleProductsProps & WithStyles<typeof styles>) => {
const intl = useIntl();
return (
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Eligible Products",
description: "section header"
})}
toolbar={
<Button color="primary" onClick={onProductAssign}>
<FormattedMessage
defaultMessage="Assign products"
description="button"
/>
</Button>
}
/>
<Table>
<TableHead
colSpan={numberOfColumns}
selected={selected}
disabled={disabled}
items={maybe(() => sale.products.edges.map(edge => edge.node))}
toggleAll={toggleAll}
toolbar={toolbar}
>
<TableCell className={classes.colName}>
<span className={classes.colNameLabel}>
<FormattedMessage defaultMessage="Product Name" />
</span>
</TableCell>
<TableCell className={classes.colType}>
<FormattedMessage defaultMessage="Product Type" />
</TableCell>
<TableCell className={classes.colPublished}>
<FormattedMessage
defaultMessage="Published"
description="product is published"
/>
</TableCell>
<TableCell />
</TableHead>
<TableFooter>
<TableRow>
<TablePagination
colSpan={numberOfColumns}
hasNextPage={
pageInfo && !disabled ? pageInfo.hasNextPage : false
}
onNextPage={onNextPage}
hasPreviousPage={
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
}
onPreviousPage={onPreviousPage}
/>
</TableRow>
</TableFooter>
<TableBody>
{renderCollection(
maybe(() => sale.products.edges.map(edge => edge.node)),
product => {
const isSelected = product ? isChecked(product.id) : false;
return (
<TableRow
hover={!!product}
key={product ? product.id : "skeleton"}
onClick={product && onRowClick(product.id)}
className={classes.tableRow}
selected={isSelected}
>
{maybe<React.ReactNode>(() => product.name, <Skeleton />)}
</TableCellAvatar>
<TableCell className={classes.colType}>
{maybe<React.ReactNode>(
() => product.productType.name,
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colPublished}>
{product && product.isPublished !== undefined ? (
<StatusLabel
label={
product.isPublished
? i18n.t("Published", { context: "product status" })
: i18n.t("Not published", {
context: "product status"
})
}
status={product.isPublished ? "success" : "error"}
<TableCell padding="checkbox">
<Checkbox
checked={isSelected}
disabled={disabled}
disableClickPropagation
onChange={() => toggle(product.id)}
/>
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colActions}>
<IconButton
disabled={!product || disabled}
onClick={event => {
event.stopPropagation();
onProductUnassign(product.id);
}}
</TableCell>
<TableCellAvatar
className={classes.colName}
thumbnail={maybe(() => product.thumbnail.url)}
>
<DeleteIcon color="primary" />
</IconButton>
{maybe<React.ReactNode>(() => product.name, <Skeleton />)}
</TableCellAvatar>
<TableCell className={classes.colType}>
{maybe<React.ReactNode>(
() => product.productType.name,
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colPublished}>
{product && product.isPublished !== undefined ? (
<StatusLabel
label={
product.isPublished
? intl.formatMessage({
defaultMessage: "Published",
description: "product is published"
})
: intl.formatMessage({
defaultMessage: "Not published",
description: "product is not published"
})
}
status={product.isPublished ? "success" : "error"}
/>
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colActions}>
<IconButton
disabled={!product || disabled}
onClick={event => {
event.stopPropagation();
onProductUnassign(product.id);
}}
>
<DeleteIcon color="primary" />
</IconButton>
</TableCell>
</TableRow>
);
},
() => (
<TableRow>
<TableCell colSpan={numberOfColumns}>
<FormattedMessage defaultMessage="No products found" />
</TableCell>
</TableRow>
);
},
() => (
<TableRow>
<TableCell colSpan={numberOfColumns}>
{i18n.t("No products found")}
</TableCell>
</TableRow>
)
)}
</TableBody>
</Table>
</Card>
)
)
)}
</TableBody>
</Table>
</Card>
);
}
);
DiscountProducts.displayName = "DiscountProducts";
export default DiscountProducts;

View file

@ -1,4 +1,5 @@
import React from "react";
import { useIntl } from "react-intl";
import AppHeader from "@saleor/components/AppHeader";
import CardSpacer from "@saleor/components/CardSpacer";
@ -8,7 +9,7 @@ import Form from "@saleor/components/Form";
import Grid from "@saleor/components/Grid";
import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar";
import i18n from "../../../i18n";
import { sectionNames } from "@saleor/intl";
import { UserError } from "../../../types";
import { SaleType } from "../../../types/globalTypes";
import SaleInfo from "../SaleInfo";
@ -39,6 +40,8 @@ const SaleCreatePage: React.StatelessComponent<SaleCreatePageProps> = ({
saveButtonBarState,
onBack
}) => {
const intl = useIntl();
const initialForm: FormData = {
endDate: "",
name: "",
@ -50,8 +53,15 @@ const SaleCreatePage: React.StatelessComponent<SaleCreatePageProps> = ({
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>{i18n.t("Sales")}</AppHeader>
<PageHeader title={i18n.t("Create Sale")} />
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.sales)}
</AppHeader>
<PageHeader
title={intl.formatMessage({
defaultMessage: "Create Sale",
description: "page header"
})}
/>
<Grid>
<div>
<SaleInfo

View file

@ -1,4 +1,5 @@
import React from "react";
import { useIntl } from "react-intl";
import AppHeader from "@saleor/components/AppHeader";
import CardSpacer from "@saleor/components/CardSpacer";
@ -9,7 +10,7 @@ import Grid from "@saleor/components/Grid";
import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { Tab, TabContainer } from "@saleor/components/Tab";
import i18n from "../../../i18n";
import { sectionNames } from "@saleor/intl";
import { maybe } from "../../../misc";
import { ListProps, TabListActions, UserError } from "../../../types";
import { SaleType } from "../../../types/globalTypes";
@ -102,6 +103,8 @@ const SaleDetailsPage: React.StatelessComponent<SaleDetailsPageProps> = ({
toggle,
toggleAll
}) => {
const intl = useIntl();
const initialForm: FormData = {
endDate: maybe(() => (sale.endDate ? sale.endDate : ""), ""),
name: maybe(() => sale.name, ""),
@ -113,7 +116,9 @@ const SaleDetailsPage: React.StatelessComponent<SaleDetailsPageProps> = ({
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>{i18n.t("Sales")}</AppHeader>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.sales)}
</AppHeader>
<PageHeader title={maybe(() => sale.name)} />
<Grid>
<div>
@ -137,34 +142,55 @@ const SaleDetailsPage: React.StatelessComponent<SaleDetailsPageProps> = ({
isActive={activeTab === SaleDetailsPageTab.categories}
changeTab={onTabClick}
>
{i18n.t("Categories ({{ number }})", {
number: maybe(
() => sale.categories.totalCount.toString(),
"…"
)
})}
{intl.formatMessage(
{
defaultMessage: "Categories ({quantity})",
description: "number of categories",
id: "saleDetailsPageCategoriesQuantity"
},
{
quantity: maybe(
() => sale.categories.totalCount.toString(),
"…"
)
}
)}
</CategoriesTab>
<CollectionsTab
isActive={activeTab === SaleDetailsPageTab.collections}
changeTab={onTabClick}
>
{i18n.t("Collections ({{ number }})", {
number: maybe(
() => sale.collections.totalCount.toString(),
"…"
)
})}
{intl.formatMessage(
{
defaultMessage: "Collections ({quantity})",
description: "number of collections",
id: "saleDetailsPageCollectionsQuantity"
},
{
quantity: maybe(
() => sale.collections.totalCount.toString(),
"…"
)
}
)}
</CollectionsTab>
<ProductsTab
isActive={activeTab === SaleDetailsPageTab.products}
changeTab={onTabClick}
>
{i18n.t("Products ({{ number }})", {
number: maybe(
() => sale.products.totalCount.toString(),
"…"
)
})}
{intl.formatMessage(
{
defaultMessage: "Products ({quantity})",
description: "number of products",
id: "saleDetailsPageProductsQuantity"
},
{
quantity: maybe(
() => sale.products.totalCount.toString(),
"…"
)
}
)}
</ProductsTab>
</TabContainer>
<CardSpacer />

View file

@ -8,9 +8,10 @@ import {
} from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField";
import React from "react";
import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import i18n from "../../../i18n";
import { commonMessages } from "@saleor/intl";
import { FormData } from "../SaleDetailsPage";
export interface SaleInfoProps {
@ -40,23 +41,32 @@ const SaleInfo = withStyles(styles, {
disabled,
errors,
onChange
}: SaleInfoProps & WithStyles<typeof styles>) => (
<Card>
<CardTitle title={i18n.t("General Information")} />
<CardContent className={classes.root}>
<TextField
disabled={disabled}
error={!!errors.name}
helperText={errors.name}
name={"name" as keyof FormData}
onChange={onChange}
label={i18n.t("Name")}
value={data.name}
fullWidth
}: SaleInfoProps & WithStyles<typeof styles>) => {
const intl = useIntl();
return (
<Card>
<CardTitle
title={intl.formatMessage(commonMessages.generalInformations)}
/>
</CardContent>
</Card>
)
<CardContent className={classes.root}>
<TextField
disabled={disabled}
error={!!errors.name}
helperText={errors.name}
name={"name" as keyof FormData}
onChange={onChange}
label={intl.formatMessage({
defaultMessage: "Name",
description: "sale name"
})}
value={data.name}
fullWidth
/>
</CardContent>
</Card>
);
}
);
SaleInfo.displayName = "SaleInfo";
export default SaleInfo;

View file

@ -11,6 +11,7 @@ import TableCell from "@material-ui/core/TableCell";
import TableFooter from "@material-ui/core/TableFooter";
import TableRow from "@material-ui/core/TableRow";
import React from "react";
import { FormattedMessage } from "react-intl";
import Checkbox from "@saleor/components/Checkbox";
import Date from "@saleor/components/Date";
@ -19,7 +20,6 @@ import Percent from "@saleor/components/Percent";
import Skeleton from "@saleor/components/Skeleton";
import TableHead from "@saleor/components/TableHead";
import TablePagination from "@saleor/components/TablePagination";
import i18n from "@saleor/i18n";
import { maybe, renderCollection } from "@saleor/misc";
import { ListActions, ListProps } from "@saleor/types";
import { SaleType } from "@saleor/types/globalTypes";
@ -92,24 +92,22 @@ const SaleList = withStyles(styles, {
toolbar={toolbar}
>
<TableCell className={classes.colName}>
{i18n.t("Name", {
context: "sale list table header"
})}
<FormattedMessage defaultMessage="Name" description="sale name" />
</TableCell>
<TableCell className={classes.colStart}>
{i18n.t("Starts", {
context: "sale list table header"
})}
<FormattedMessage
defaultMessage="Starts"
description="sale start date"
/>
</TableCell>
<TableCell className={classes.colEnd}>
{i18n.t("Ends", {
context: "sale list table header"
})}
<FormattedMessage
defaultMessage="Ends"
description="sale end date"
/>
</TableCell>
<TableCell className={classes.colValue}>
{i18n.t("Value", {
context: "sale list table header"
})}
<FormattedMessage defaultMessage="Value" description="sale value" />
</TableCell>
</TableHead>
<TableFooter>
@ -193,7 +191,7 @@ const SaleList = withStyles(styles, {
() => (
<TableRow>
<TableCell colSpan={numberOfColumns}>
{i18n.t("No sales found")}
<FormattedMessage defaultMessage="No sales found" />
</TableCell>
</TableRow>
)

View file

@ -1,10 +1,11 @@
import Button from "@material-ui/core/Button";
import AddIcon from "@material-ui/icons/Add";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import Container from "@saleor/components/Container";
import PageHeader from "@saleor/components/PageHeader";
import i18n from "@saleor/i18n";
import { sectionNames } from "@saleor/intl";
import { ListActions, PageListProps } from "@saleor/types";
import { SaleList_sales_edges_node } from "../../types/SaleList";
import SaleList from "../SaleList";
@ -17,16 +18,22 @@ export interface SaleListPageProps extends PageListProps, ListActions {
const SaleListPage: React.StatelessComponent<SaleListPageProps> = ({
onAdd,
...listProps
}) => (
<Container>
<PageHeader title={i18n.t("Sales")}>
<Button onClick={onAdd} variant="contained" color="primary">
{i18n.t("Add sale")}
<AddIcon />
</Button>
</PageHeader>
<SaleList {...listProps} />
</Container>
);
}) => {
const intl = useIntl();
return (
<Container>
<PageHeader title={intl.formatMessage(sectionNames.sales)}>
<Button onClick={onAdd} variant="contained" color="primary">
<FormattedMessage defaultMessage="Add Sale"
description="button"
/>
<AddIcon />
</Button>
</PageHeader>
<SaleList {...listProps} />
</Container>
);
};
SaleListPage.displayName = "SaleListPage";
export default SaleListPage;

View file

@ -9,11 +9,12 @@ import {
import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import Hr from "@saleor/components/Hr";
import TextFieldWithChoice from "@saleor/components/TextFieldWithChoice";
import i18n from "../../../i18n";
import { commonMessages } from "@saleor/intl";
import { FormErrors } from "../../../types";
import { SaleType } from "../../../types/globalTypes";
import { FormData } from "../SaleDetailsPage";
@ -49,75 +50,89 @@ const SalePricing = withStyles(styles, {
disabled,
errors,
onChange
}: SalePricingProps & WithStyles<typeof styles>) => (
<Card>
<CardTitle title={i18n.t("Pricing")} />
<CardContent className={classes.root}>
<TextFieldWithChoice
disabled={disabled}
ChoiceProps={{
label: data.type === SaleType.FIXED ? defaultCurrency : "%",
name: "type",
values: [
{
label: defaultCurrency,
value: SaleType.FIXED
},
{
label: "%",
value: SaleType.PERCENTAGE
}
]
}}
error={!!errors.value}
helperText={errors.value}
name={"value" as keyof FormData}
onChange={onChange}
label={i18n.t("Discount Value")}
value={data.value}
type="number"
fullWidth
inputProps={{
min: 0
}}
}: SalePricingProps & WithStyles<typeof styles>) => {
const intl = useIntl();
return (
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Pricing",
description: "sale pricing, header"
})}
/>
</CardContent>
<Hr />
<CardContent className={classes.root}>
<Typography className={classes.subheading} variant="subtitle1">
{i18n.t("Time Frame")}
</Typography>
<TextField
disabled={disabled}
error={!!errors.startDate}
helperText={errors.startDate}
name={"startDate" as keyof FormData}
onChange={onChange}
label={i18n.t("Start Date")}
value={data.startDate}
type="date"
InputLabelProps={{
shrink: true
}}
fullWidth
/>
<TextField
disabled={disabled}
error={!!errors.endDate}
helperText={errors.endDate}
name={"endDate" as keyof FormData}
onChange={onChange}
label={i18n.t("End Date")}
value={data.endDate}
type="date"
InputLabelProps={{
shrink: true
}}
fullWidth
/>
</CardContent>
</Card>
)
<CardContent className={classes.root}>
<TextFieldWithChoice
disabled={disabled}
ChoiceProps={{
label: data.type === SaleType.FIXED ? defaultCurrency : "%",
name: "type",
values: [
{
label: defaultCurrency,
value: SaleType.FIXED
},
{
label: "%",
value: SaleType.PERCENTAGE
}
]
}}
error={!!errors.value}
helperText={errors.value}
name={"value" as keyof FormData}
onChange={onChange}
label={intl.formatMessage({
defaultMessage: "Discount Value"
})}
value={data.value}
type="number"
fullWidth
inputProps={{
min: 0
}}
/>
</CardContent>
<Hr />
<CardContent className={classes.root}>
<Typography className={classes.subheading} variant="subtitle1">
<FormattedMessage
defaultMessage="Time Frame"
description="time during which sale is active"
/>
</Typography>
<TextField
disabled={disabled}
error={!!errors.startDate}
helperText={errors.startDate}
name={"startDate" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.startDate)}
value={data.startDate}
type="date"
InputLabelProps={{
shrink: true
}}
fullWidth
/>
<TextField
disabled={disabled}
error={!!errors.endDate}
helperText={errors.endDate}
name={"endDate" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.endDate)}
value={data.endDate}
type="date"
InputLabelProps={{
shrink: true
}}
fullWidth
/>
</CardContent>
</Card>
);
}
);
SalePricing.displayName = "SalePricing";
export default SalePricing;

View file

@ -2,6 +2,7 @@ import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import Typography from "@material-ui/core/Typography";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardSpacer from "@saleor/components/CardSpacer";
import CardTitle from "@saleor/components/CardTitle";
@ -11,7 +12,7 @@ import Hr from "@saleor/components/Hr";
import Money from "@saleor/components/Money";
import Percent from "@saleor/components/Percent";
import Skeleton from "@saleor/components/Skeleton";
import i18n from "../../../i18n";
import { commonMessages } from "@saleor/intl";
import { maybe } from "../../../misc";
import { SaleType } from "../../../types/globalTypes";
import { SaleDetails_sale } from "../../types/SaleDetails";
@ -24,59 +25,71 @@ export interface SaleSummaryProps {
const SaleSummary: React.StatelessComponent<SaleSummaryProps> = ({
defaultCurrency,
sale
}) => (
<Card>
<CardTitle title={i18n.t("Summary")} />
<CardContent>
<Typography variant="caption">{i18n.t("Name")}</Typography>
<Typography>
{maybe<React.ReactNode>(() => sale.name, <Skeleton />)}
</Typography>
<FormSpacer />
}) => {
const intl = useIntl();
<Typography variant="caption">{i18n.t("Value")}</Typography>
<Typography>
{maybe<React.ReactNode>(
() =>
sale.type === SaleType.FIXED ? (
<Money
money={{
amount: sale.value,
currency: defaultCurrency
}}
/>
) : (
<Percent amount={sale.value} />
return (
<Card>
<CardTitle title={intl.formatMessage(commonMessages.summary)} />
<CardContent>
<Typography variant="caption">
<FormattedMessage defaultMessage="Name" description="sale name" />
</Typography>
<Typography>
{maybe<React.ReactNode>(() => sale.name, <Skeleton />)}
</Typography>
<FormSpacer />
<Typography variant="caption">
<FormattedMessage defaultMessage="Value" description="sale value" />
</Typography>
<Typography>
{maybe<React.ReactNode>(
() =>
sale.type === SaleType.FIXED ? (
<Money
money={{
amount: sale.value,
currency: defaultCurrency
}}
/>
) : (
<Percent amount={sale.value} />
),
<Skeleton />
)}
</Typography>
<CardSpacer />
<Hr />
<CardSpacer />
<Typography variant="caption">
<FormattedMessage {...commonMessages.startDate} />
</Typography>
<Typography>
{maybe<React.ReactNode>(
() => (
<Date date={sale.startDate} plain />
),
<Skeleton />
)}
</Typography>
<Skeleton />
)}
</Typography>
<FormSpacer />
<CardSpacer />
<Hr />
<CardSpacer />
<Typography variant="caption">{i18n.t("Start Date")}</Typography>
<Typography>
{maybe<React.ReactNode>(
() => (
<Date date={sale.startDate} plain />
),
<Skeleton />
)}
</Typography>
<FormSpacer />
<Typography variant="caption">{i18n.t("End Date")}</Typography>
<Typography>
{maybe<React.ReactNode>(
() =>
sale.endDate === null ? "-" : <Date date={sale.endDate} plain />,
<Skeleton />
)}
</Typography>
</CardContent>
</Card>
);
<Typography variant="caption">
<FormattedMessage {...commonMessages.endDate} />
</Typography>
<Typography>
{maybe<React.ReactNode>(
() =>
sale.endDate === null ? "-" : <Date date={sale.endDate} plain />,
<Skeleton />
)}
</Typography>
</CardContent>
</Card>
);
};
SaleSummary.displayName = "SaleSummary";
export default SaleSummary;

View file

@ -1,4 +1,5 @@
import React from "react";
import { useIntl } from "react-intl";
import AppHeader from "@saleor/components/AppHeader";
import CardSpacer from "@saleor/components/CardSpacer";
@ -8,7 +9,6 @@ import Form from "@saleor/components/Form";
import Grid from "@saleor/components/Grid";
import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar";
import i18n from "../../../i18n";
import { UserError } from "../../../types";
import {
DiscountValueTypeEnum,
@ -21,6 +21,7 @@ import VoucherLimits from "../VoucherLimits";
import VoucherRequirements from "../VoucherRequirements";
import VoucherTypes from "../VoucherTypes";
import { sectionNames } from "@saleor/intl";
import VoucherValue from "../VoucherValue";
export interface FormData {
applyOncePerCustomer: boolean;
@ -58,6 +59,8 @@ const VoucherCreatePage: React.StatelessComponent<VoucherCreatePageProps> = ({
onBack,
onSubmit
}) => {
const intl = useIntl();
const initialForm: FormData = {
applyOncePerCustomer: false,
applyOncePerOrder: false,
@ -81,8 +84,15 @@ const VoucherCreatePage: React.StatelessComponent<VoucherCreatePageProps> = ({
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>{i18n.t("Vouchers")}</AppHeader>
<PageHeader title={i18n.t("Create Voucher")} />
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.vouchers)}
</AppHeader>
<PageHeader
title={intl.formatMessage({
defaultMessage: "Create Voucher",
description: "page header"
})}
/>
<Grid>
<div>
<VoucherInfo

View file

@ -2,11 +2,12 @@ import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import TextField from "@material-ui/core/TextField";
import React from "react";
import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import Grid from "@saleor/components/Grid";
import i18n from "../../../i18n";
import { commonMessages } from "@saleor/intl";
import { FormErrors } from "../../../types";
import { FormData } from "../VoucherDetailsPage";
@ -24,9 +25,16 @@ const VoucherDates = ({
errors,
onChange
}: VoucherDatesProps) => {
const intl = useIntl();
return (
<Card>
<CardTitle title={i18n.t("Active Dates")} />
<CardTitle
title={intl.formatMessage({
defaultMessage: "Active Dates",
description: "time during voucher is active, header"
})}
/>
<CardContent>
<Grid variant="uniform">
<TextField
@ -35,7 +43,7 @@ const VoucherDates = ({
helperText={errors.startDate}
name={"startDate" as keyof FormData}
onChange={onChange}
label={i18n.t("Start Date")}
label={intl.formatMessage(commonMessages.startDate)}
value={data.startDate}
type="date"
InputLabelProps={{
@ -49,7 +57,7 @@ const VoucherDates = ({
helperText={errors.startDate}
name={"startTime" as keyof FormData}
onChange={onChange}
label={i18n.t("Start Hour")}
label={intl.formatMessage(commonMessages.startHour)}
value={data.startTime}
type="time"
InputLabelProps={{
@ -60,7 +68,10 @@ const VoucherDates = ({
</Grid>
<ControlledCheckbox
checked={data.hasEndDate}
label={i18n.t("Set end date")}
label={intl.formatMessage({
defaultMessage: "Set end date",
description: "voucher end date, switch button"
})}
name={"hasEndDate" as keyof FormData}
onChange={onChange}
/>
@ -72,7 +83,7 @@ const VoucherDates = ({
helperText={errors.endDate}
name={"endDate" as keyof FormData}
onChange={onChange}
label={i18n.t("End Date")}
label={intl.formatMessage(commonMessages.endDate)}
value={data.endDate}
type="date"
InputLabelProps={{
@ -86,7 +97,7 @@ const VoucherDates = ({
helperText={errors.endDate}
name={"endTime" as keyof FormData}
onChange={onChange}
label={i18n.t("End Hour")}
label={intl.formatMessage(commonMessages.endHour)}
value={data.endTime}
type="time"
InputLabelProps={{

View file

@ -1,5 +1,6 @@
import Typography from "@material-ui/core/Typography";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import AppHeader from "@saleor/components/AppHeader";
import CardSpacer from "@saleor/components/CardSpacer";
@ -12,7 +13,7 @@ import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { Tab, TabContainer } from "@saleor/components/Tab";
import { RequirementsPicker } from "@saleor/discounts/types";
import i18n from "../../../i18n";
import { sectionNames } from "@saleor/intl";
import { maybe, splitDateTime } from "../../../misc";
import { ListProps, TabListActions, UserError } from "../../../types";
import {
@ -128,6 +129,8 @@ const VoucherDetailsPage: React.StatelessComponent<VoucherDetailsPageProps> = ({
collectionListToolbar,
productListToolbar
}) => {
const intl = useIntl();
let requirementsPickerInitValue;
if (maybe(() => voucher.minAmountSpent.amount) > 0) {
requirementsPickerInitValue = RequirementsPicker.ORDER;
@ -166,7 +169,9 @@ const VoucherDetailsPage: React.StatelessComponent<VoucherDetailsPageProps> = ({
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>{i18n.t("Vouchers")}</AppHeader>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.vouchers)}
</AppHeader>
<PageHeader title={maybe(() => voucher.code)} />
<Grid>
<div>
@ -204,34 +209,52 @@ const VoucherDetailsPage: React.StatelessComponent<VoucherDetailsPageProps> = ({
isActive={activeTab === VoucherDetailsPageTab.categories}
changeTab={onTabClick}
>
{i18n.t("Categories ({{ number }})", {
number: maybe(
() => voucher.categories.totalCount.toString(),
"…"
)
})}
{intl.formatMessage(
{
defaultMessage: "Categories ({quantity})",
description: "number of categories"
},
{
quantity: maybe(
() => voucher.categories.totalCount.toString(),
"…"
)
}
)}
</CategoriesTab>
<CollectionsTab
isActive={activeTab === VoucherDetailsPageTab.collections}
changeTab={onTabClick}
>
{i18n.t("Collections ({{ number }})", {
number: maybe(
() => voucher.collections.totalCount.toString(),
"…"
)
})}
{intl.formatMessage(
{
defaultMessage: "Collections ({quantity})",
description: "number of collections"
},
{
quantity: maybe(
() => voucher.collections.totalCount.toString(),
"…"
)
}
)}
</CollectionsTab>
<ProductsTab
isActive={activeTab === VoucherDetailsPageTab.products}
changeTab={onTabClick}
>
{i18n.t("Products ({{ number }})", {
number: maybe(
() => voucher.products.totalCount.toString(),
"…"
)
})}
{intl.formatMessage(
{
defaultMessage: "Products ({quantity})",
description: "number of products"
},
{
quantity: maybe(
() => voucher.products.totalCount.toString(),
"…"
)
}
)}
</ProductsTab>
</TabContainer>
<CardSpacer />
@ -291,12 +314,17 @@ const VoucherDetailsPage: React.StatelessComponent<VoucherDetailsPageProps> = ({
<CountryList
countries={maybe(() => voucher.countries)}
disabled={disabled}
emptyText={i18n.t("Voucher applies to all countries")}
emptyText={intl.formatMessage({
defaultMessage: "Voucher applies to all countries"
})}
title={
<>
{i18n.t("Countries")}
{intl.formatMessage({
defaultMessage: "Countries",
description: "voucher country range"
})}
<Typography variant="caption">
{i18n.t("Vouchers limited to these countries")}
<FormattedMessage defaultMessage="Voucher is limited to these countries" />
</Typography>
</>
}

View file

@ -2,10 +2,11 @@ import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import TextField from "@material-ui/core/TextField";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import Button from "@material-ui/core/Button";
import CardTitle from "@saleor/components/CardTitle";
import i18n from "../../../i18n";
import { commonMessages } from "@saleor/intl";
import { generateCode } from "../../../misc";
import { FormErrors } from "../../../types";
import { FormData } from "../VoucherDetailsPage";
@ -25,6 +26,8 @@ const VoucherInfo = ({
variant,
onChange
}: VoucherInfoProps) => {
const intl = useIntl();
const onGenerateCode = () =>
onChange({
target: {
@ -36,11 +39,14 @@ const VoucherInfo = ({
return (
<Card>
<CardTitle
title={i18n.t("General Information")}
title={intl.formatMessage(commonMessages.generalInformations)}
toolbar={
variant === "create" && (
<Button color="primary" onClick={onGenerateCode}>
{i18n.t("Generate Code")}
<FormattedMessage
defaultMessage="Generate Code"
description="voucher code, button"
/>
</Button>
)
}
@ -52,7 +58,9 @@ const VoucherInfo = ({
fullWidth
helperText={errors.code}
name={"code" as keyof FormData}
label={i18n.t("Discount Code")}
label={intl.formatMessage({
defaultMessage: "Discount Code"
})}
value={data.code}
onChange={onChange}
/>

View file

@ -2,10 +2,10 @@ import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import TextField from "@material-ui/core/TextField";
import React from "react";
import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import i18n from "../../../i18n";
import { FormErrors } from "../../../types";
import { FormData } from "../VoucherDetailsPage";
@ -23,15 +23,23 @@ const VoucherLimits = ({
errors,
onChange
}: VoucherLimitsProps) => {
const intl = useIntl();
return (
<Card>
<CardTitle title={i18n.t("Usage Limit ")} />
<CardTitle
title={intl.formatMessage({
defaultMessage: "Usage Limit",
description: "voucher usage limit, header"
})}
/>
<CardContent>
<ControlledCheckbox
checked={data.hasUsageLimit}
label={i18n.t(
"Limit number of times this discount can be used in total"
)}
label={intl.formatMessage({
defaultMessage:
"Limit number of times this discount can be used in total"
})}
name={"hasUsageLimit" as keyof FormData}
onChange={onChange}
/>
@ -40,7 +48,10 @@ const VoucherLimits = ({
disabled={disabled}
error={!!errors.usageLimit}
helperText={errors.usageLimit}
label={i18n.t("Limit of Uses")}
label={intl.formatMessage({
defaultMessage: "Limit of Uses",
description: "voucher"
})}
name={"usageLimit" as keyof FormData}
value={data.usageLimit}
onChange={onChange}
@ -53,7 +64,10 @@ const VoucherLimits = ({
)}
<ControlledCheckbox
checked={data.applyOncePerCustomer}
label={i18n.t("Limit to one use per customer")}
label={intl.formatMessage({
defaultMessage: "Limit to one use per customer",
description: "limit voucher"
})}
name={"applyOncePerCustomer" as keyof FormData}
onChange={onChange}
/>

View file

@ -11,6 +11,7 @@ import TableCell from "@material-ui/core/TableCell";
import TableFooter from "@material-ui/core/TableFooter";
import TableRow from "@material-ui/core/TableRow";
import React from "react";
import { FormattedMessage } from "react-intl";
import Checkbox from "@saleor/components/Checkbox";
import Date from "@saleor/components/Date";
@ -19,7 +20,6 @@ import Percent from "@saleor/components/Percent";
import Skeleton from "@saleor/components/Skeleton";
import TableHead from "@saleor/components/TableHead";
import TablePagination from "@saleor/components/TablePagination";
import i18n from "@saleor/i18n";
import { maybe, renderCollection } from "@saleor/misc";
import { ListActions, ListProps } from "@saleor/types";
import { DiscountValueTypeEnum } from "@saleor/types/globalTypes";
@ -107,34 +107,40 @@ const VoucherList = withStyles(styles, {
toolbar={toolbar}
>
<TableCell className={classes.colName}>
{i18n.t("Code", {
context: "voucher list table header"
})}
<FormattedMessage
defaultMessage="Code"
description="voucher code"
/>
</TableCell>
<TableCell className={classes.colMinSpent}>
{i18n.t("Min. Spent", {
context: "voucher list table header"
})}
<FormattedMessage
defaultMessage="Min. Spent"
description="minimum amount of spent money to activate voucher"
/>
</TableCell>
<TableCell className={classes.colStart}>
{i18n.t("Starts", {
context: "voucher list table header"
})}
<FormattedMessage
defaultMessage="Starts"
description="voucher is active from date"
/>
</TableCell>
<TableCell className={classes.colEnd}>
{i18n.t("Ends", {
context: "voucher list table header"
})}
<FormattedMessage
defaultMessage="Ends"
description="voucher is active until date"
/>
</TableCell>
<TableCell className={classes.colValue}>
{i18n.t("Value", {
context: "voucher list table header"
})}
<FormattedMessage
defaultMessage="Value"
description="voucher value"
/>
</TableCell>
<TableCell className={classes.colUses}>
{i18n.t("Uses", {
context: "voucher list table header"
})}
<FormattedMessage
defaultMessage="Uses"
description="voucher uses"
/>
</TableCell>
</TableHead>
<TableFooter>
@ -239,7 +245,7 @@ const VoucherList = withStyles(styles, {
() => (
<TableRow>
<TableCell colSpan={numberOfColumns}>
{i18n.t("No vouchers found")}
<FormattedMessage defaultMessage="No vouchers found" />
</TableCell>
</TableRow>
)

View file

@ -1,10 +1,11 @@
import Button from "@material-ui/core/Button";
import AddIcon from "@material-ui/icons/Add";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import Container from "@saleor/components/Container";
import PageHeader from "@saleor/components/PageHeader";
import i18n from "@saleor/i18n";
import { sectionNames } from "@saleor/intl";
import { ListActions, PageListProps } from "@saleor/types";
import { VoucherList_vouchers_edges_node } from "../../types/VoucherList";
import VoucherList from "../VoucherList";
@ -30,31 +31,35 @@ const VoucherListPage: React.StatelessComponent<VoucherListPageProps> = ({
toggle,
toggleAll,
toolbar
}) => (
<Container>
<PageHeader title={i18n.t("Vouchers")}>
<Button onClick={onAdd} variant="contained" color="primary">
{i18n.t("Add voucher")}
<AddIcon />
</Button>
</PageHeader>
<VoucherList
defaultCurrency={defaultCurrency}
settings={settings}
disabled={disabled}
onNextPage={onNextPage}
onPreviousPage={onPreviousPage}
onUpdateListSettings={onUpdateListSettings}
onRowClick={onRowClick}
pageInfo={pageInfo}
vouchers={vouchers}
isChecked={isChecked}
selected={selected}
toggle={toggle}
toggleAll={toggleAll}
toolbar={toolbar}
/>
</Container>
);
}) => {
const intl = useIntl();
return (
<Container>
<PageHeader title={intl.formatMessage(sectionNames.vouchers)}>
<Button onClick={onAdd} variant="contained" color="primary">
<FormattedMessage defaultMessage="Add voucher" description="button" />
<AddIcon />
</Button>
</PageHeader>
<VoucherList
defaultCurrency={defaultCurrency}
settings={settings}
disabled={disabled}
onNextPage={onNextPage}
onPreviousPage={onPreviousPage}
onUpdateListSettings={onUpdateListSettings}
onRowClick={onRowClick}
pageInfo={pageInfo}
vouchers={vouchers}
isChecked={isChecked}
selected={selected}
toggle={toggle}
toggleAll={toggleAll}
toolbar={toolbar}
/>
</Container>
);
};
VoucherListPage.displayName = "VoucherListPage";
export default VoucherListPage;

View file

@ -2,12 +2,12 @@ import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import TextField from "@material-ui/core/TextField";
import React from "react";
import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import { FormSpacer } from "@saleor/components/FormSpacer";
import RadioGroupField from "@saleor/components/RadioGroupField";
import { RequirementsPicker } from "@saleor/discounts/types";
import i18n from "@saleor/i18n";
import { FormErrors } from "@saleor/types";
import { FormData } from "../VoucherDetailsPage";
@ -25,24 +25,43 @@ const VoucherRequirements = ({
errors,
onChange
}: VoucherRequirementsProps) => {
const intl = useIntl();
const minimalOrderValueText = intl.formatMessage({
defaultMessage: "Minimal order value",
description: "voucher requirement"
});
const minimalQuantityText = intl.formatMessage({
defaultMessage: "Minimum quantity of items",
description: "voucher requirement"
});
const requirementsPickerChoices = [
{
label: i18n.t("None"),
label: intl.formatMessage({
defaultMessage: "None",
description: "voucher has no requirements"
}),
value: RequirementsPicker.NONE
},
{
label: i18n.t("Minimal order value"),
label: minimalOrderValueText,
value: RequirementsPicker.ORDER
},
{
label: i18n.t("Minimum quantity of items"),
label: minimalQuantityText,
value: RequirementsPicker.ITEM
}
];
return (
<Card>
<CardTitle title={i18n.t("Minimum Requirements")} />
<CardTitle
title={intl.formatMessage({
defaultMessage: "Minimum Requirements",
description: "voucher requirements, header"
})}
/>
<CardContent>
<RadioGroupField
choices={requirementsPickerChoices}
@ -57,7 +76,7 @@ const VoucherRequirements = ({
disabled={disabled}
error={!!errors.minAmountSpent}
helperText={errors.minAmountSpent}
label={i18n.t("Minimal order value")}
label={minimalOrderValueText}
name={"minAmountSpent" as keyof FormData}
value={data.minAmountSpent}
onChange={onChange}
@ -68,7 +87,7 @@ const VoucherRequirements = ({
disabled={disabled}
error={!!errors.minCheckoutItemsQuantity}
helperText={errors.minCheckoutItemsQuantity}
label={i18n.t("Minimum quantity of items")}
label={minimalQuantityText}
name={"minCheckoutItemsQuantity" as keyof FormData}
value={data.minCheckoutItemsQuantity}
onChange={onChange}

View file

@ -2,6 +2,7 @@ import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import Typography from "@material-ui/core/Typography";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardSpacer from "@saleor/components/CardSpacer";
import CardTitle from "@saleor/components/CardTitle";
@ -11,7 +12,7 @@ import Hr from "@saleor/components/Hr";
import Money from "@saleor/components/Money";
import Percent from "@saleor/components/Percent";
import Skeleton from "@saleor/components/Skeleton";
import i18n from "../../../i18n";
import { commonMessages } from "@saleor/intl";
import { maybe } from "../../../misc";
import { DiscountValueTypeEnum } from "../../../types/globalTypes";
import { translateVoucherTypes } from "../../translations";
@ -26,19 +27,25 @@ const VoucherSummary: React.StatelessComponent<VoucherSummaryProps> = ({
defaultCurrency,
voucher
}) => {
const intl = useIntl();
const translatedVoucherTypes = translateVoucherTypes();
return (
<Card>
<CardTitle title={i18n.t("Summary")} />
<CardTitle title={intl.formatMessage(commonMessages.summary)} />
<CardContent>
<Typography variant="caption">{i18n.t("Code")}</Typography>
<Typography variant="caption">
<FormattedMessage defaultMessage="Code" description="voucher code" />
</Typography>
<Typography>
{maybe<React.ReactNode>(() => voucher.code, <Skeleton />)}
</Typography>
<FormSpacer />
<Typography variant="caption">{i18n.t("Applies to")}</Typography>
<Typography variant="caption">
<FormattedMessage defaultMessage="Applies to" description="voucher" />
</Typography>
<Typography>
{maybe<React.ReactNode>(
() => translatedVoucherTypes[voucher.type],
@ -47,7 +54,12 @@ const VoucherSummary: React.StatelessComponent<VoucherSummaryProps> = ({
</Typography>
<FormSpacer />
<Typography variant="caption">{i18n.t("Value")}</Typography>
<Typography variant="caption">
<FormattedMessage
defaultMessage="Value"
description="voucher value"
/>
</Typography>
<Typography>
{maybe<React.ReactNode>(
() =>
@ -69,7 +81,9 @@ const VoucherSummary: React.StatelessComponent<VoucherSummaryProps> = ({
<Hr />
<CardSpacer />
<Typography variant="caption">{i18n.t("Start Date")}</Typography>
<Typography variant="caption">
{intl.formatMessage(commonMessages.startDate)}
</Typography>
<Typography>
{maybe<React.ReactNode>(
() => (
@ -80,7 +94,9 @@ const VoucherSummary: React.StatelessComponent<VoucherSummaryProps> = ({
</Typography>
<FormSpacer />
<Typography variant="caption">{i18n.t("End Date")}</Typography>
<Typography variant="caption">
{intl.formatMessage(commonMessages.endDate)}
</Typography>
<Typography>
{maybe<React.ReactNode>(
() =>
@ -97,7 +113,12 @@ const VoucherSummary: React.StatelessComponent<VoucherSummaryProps> = ({
<Hr />
<CardSpacer />
<Typography variant="caption">{i18n.t("Min. Order Value")}</Typography>
<Typography variant="caption">
<FormattedMessage
defaultMessage="Min. Order Value"
description="voucher value requirement"
/>
</Typography>
<Typography>
{maybe<React.ReactNode>(
() =>
@ -111,7 +132,12 @@ const VoucherSummary: React.StatelessComponent<VoucherSummaryProps> = ({
</Typography>
<FormSpacer />
<Typography variant="caption">{i18n.t("Usage Limit")}</Typography>
<Typography variant="caption">
<FormattedMessage
defaultMessage="Usage Limit"
description="voucher value requirement"
/>
</Typography>
<Typography>
{maybe<React.ReactNode>(
() => (voucher.usageLimit === null ? "-" : voucher.usageLimit),

View file

@ -1,11 +1,11 @@
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import React from "react";
import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import Grid from "@saleor/components/Grid";
import RadioGroupField from "@saleor/components/RadioGroupField";
import i18n from "../../../i18n";
import { FormErrors } from "../../../types";
import { DiscountValueTypeEnum } from "../../../types/globalTypes";
import { FormData } from "../VoucherDetailsPage";
@ -23,24 +23,40 @@ const VoucherTypes = ({
errors,
onChange
}: VoucherTypesProps) => {
const intl = useIntl();
const voucherTypeChoices = [
{
label: i18n.t("Fixed Amount"),
label: intl.formatMessage({
defaultMessage: "Fixed Amount",
description: "voucher discount type"
}),
value: DiscountValueTypeEnum.FIXED
},
{
label: i18n.t("Percentage"),
label: intl.formatMessage({
defaultMessage: "Percentage",
description: "voucher discount type"
}),
value: DiscountValueTypeEnum.PERCENTAGE
},
{
label: i18n.t("Free Shipping"),
label: intl.formatMessage({
defaultMessage: "Free Shipping",
description: "voucher discount type"
}),
value: "SHIPPING"
}
];
return (
<Card>
<CardTitle title={i18n.t("Discount Type")} />
<CardTitle
title={intl.formatMessage({
defaultMessage: "Discount Type",
description: "header"
})}
/>
<CardContent>
<Grid variant="uniform">
<RadioGroupField

View file

@ -2,6 +2,7 @@ import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import Typography from "@material-ui/core/Typography";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import ControlledSwitch from "@saleor/components/ControlledSwitch";
@ -9,7 +10,6 @@ import { FormSpacer } from "@saleor/components/FormSpacer";
import Hr from "@saleor/components/Hr";
import RadioGroupField from "@saleor/components/RadioGroupField";
import TextFieldWithChoice from "@saleor/components/TextFieldWithChoice";
import i18n from "../../../i18n";
import { FormErrors } from "../../../types";
import { DiscountValueTypeEnum } from "../../../types/globalTypes";
import { translateVoucherTypes } from "../../translations";
@ -37,6 +37,8 @@ const VoucherValue = ({
variant,
onChange
}: VoucherValueProps) => {
const intl = useIntl();
const translatedVoucherTypes = translateVoucherTypes();
const voucherTypeChoices = Object.values(VoucherType).map(type => ({
label: translatedVoucherTypes[type],
@ -45,7 +47,12 @@ const VoucherValue = ({
return (
<Card>
<CardTitle title={i18n.t("Value")} />
<CardTitle
title={intl.formatMessage({
defaultMessage: "Value",
description: "section header"
})}
/>
<CardContent>
<TextFieldWithChoice
disabled={disabled}
@ -61,7 +68,9 @@ const VoucherValue = ({
helperText={errors.discountValue}
name={"value" as keyof FormData}
onChange={onChange}
label={i18n.t("Discount Value")}
label={intl.formatMessage({
defaultMessage: "Discount Value"
})}
value={data.value}
type="number"
fullWidth
@ -77,7 +86,9 @@ const VoucherValue = ({
disabled={disabled}
error={!!errors.type}
hint={errors.type}
label={i18n.t("Discount Specific Information")}
label={intl.formatMessage({
defaultMessage: "Discount Specific Information"
})}
name={"type" as keyof FormData}
value={data.type}
onChange={onChange}
@ -91,13 +102,12 @@ const VoucherValue = ({
checked={data.applyOncePerOrder}
label={
<>
{i18n.t("Only once per order", {
context: "voucher application"
})}
<FormattedMessage
defaultMessage="Only once per order"
description="voucher application, switch button"
/>
<Typography variant="caption">
{i18n.t(
"If this option is disabled, discount will be counted for every eligible product"
)}
<FormattedMessage defaultMessage="If this option is disabled, discount will be counted for every eligible product" />
</Typography>
</>
}

View file

@ -1,9 +1,10 @@
import { parse as parseQs } from "qs";
import React from "react";
import { useIntl } from "react-intl";
import { Route, RouteComponentProps, Switch } from "react-router-dom";
import { sectionNames } from "@saleor/intl";
import { WindowTitle } from "../components/WindowTitle";
import i18n from "../i18n";
import { saleDetailsPageTab } from "./components/SaleDetailsPage";
import { voucherDetailsPageTab } from "./components/VoucherDetailsPage";
import {
@ -73,17 +74,21 @@ const VoucherDetailsView: React.StatelessComponent<
);
};
export const DiscountSection: React.StatelessComponent<{}> = () => (
<>
<WindowTitle title={i18n.t("Discounts")} />
<Switch>
<Route exact path={saleListPath} component={SaleListView} />
<Route exact path={saleAddPath} component={SaleCreateView} />
<Route exact path={voucherAddPath} component={VoucherCreateView} />
<Route path={salePath(":id")} component={SaleDetailsView} />
<Route exact path={voucherListPath} component={VoucherListView} />
<Route path={voucherPath(":id")} component={VoucherDetailsView} />
</Switch>
</>
);
export const DiscountSection: React.StatelessComponent<{}> = () => {
const intl = useIntl();
return (
<>
<WindowTitle title={intl.formatMessage(sectionNames.vouchers)} />
<Switch>
<Route exact path={saleListPath} component={SaleListView} />
<Route exact path={saleAddPath} component={SaleCreateView} />
<Route exact path={voucherAddPath} component={VoucherCreateView} />
<Route path={salePath(":id")} component={SaleDetailsView} />
<Route exact path={voucherListPath} component={VoucherListView} />
<Route path={voucherPath(":id")} component={VoucherDetailsView} />
</Switch>
</>
);
};
export default DiscountSection;

View file

@ -1,10 +1,11 @@
import React from "react";
import { useIntl } from "react-intl";
import { WindowTitle } from "@saleor/components/WindowTitle";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import useShop from "@saleor/hooks/useShop";
import i18n from "../../i18n";
import { sectionNames } from "@saleor/intl";
import { decimal, getMutationState, maybe } from "../../misc";
import { DiscountValueTypeEnum, SaleType } from "../../types/globalTypes";
import SaleCreatePage from "../components/SaleCreatePage";
@ -22,12 +23,13 @@ export const SaleDetails: React.StatelessComponent = () => {
const navigate = useNavigator();
const pushMessage = useNotifier();
const shop = useShop();
const intl = useIntl();
const handleSaleCreate = (data: SaleCreate) => {
if (data.saleCreate.errors.length === 0) {
pushMessage({
text: i18n.t("Successfully created sale", {
context: "notification"
text: intl.formatMessage({
defaultMessage: "Successfully created sale"
})
});
navigate(saleUrl(data.saleCreate.sale.id), true);
@ -45,7 +47,7 @@ export const SaleDetails: React.StatelessComponent = () => {
return (
<>
<WindowTitle title={i18n.t("Sales")} />
<WindowTitle title={intl.formatMessage(sectionNames.sales)} />
<SaleCreatePage
defaultCurrency={maybe(() => shop.defaultCurrency)}
disabled={saleCreateOpts.loading}

View file

@ -1,6 +1,7 @@
import Button from "@material-ui/core/Button";
import DialogContentText from "@material-ui/core/DialogContentText";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ActionDialog from "@saleor/components/ActionDialog";
import AssignCategoriesDialog from "@saleor/components/AssignCategoryDialog";
@ -14,13 +15,13 @@ import usePaginator, {
createPaginationState
} from "@saleor/hooks/usePaginator";
import useShop from "@saleor/hooks/useShop";
import { commonMessages, sectionNames } from "@saleor/intl";
import { categoryUrl } from "../../categories/urls";
import { collectionUrl } from "../../collections/urls";
import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "../../config";
import SearchCategories from "../../containers/SearchCategories";
import SearchCollections from "../../containers/SearchCollections";
import SearchProducts from "../../containers/SearchProducts";
import i18n from "../../i18n";
import { decimal, getMutationState, maybe } from "../../misc";
import { productUrl } from "../../products/urls";
import { DiscountValueTypeEnum, SaleType } from "../../types/globalTypes";
@ -67,6 +68,7 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
params.ids
);
const intl = useIntl();
const paginationState = createPaginationState(PAGINATE_BY, params);
const changeTab = (tab: SaleDetailsPageTab) => {
@ -81,8 +83,8 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
const handleSaleDelete = (data: SaleDelete) => {
if (data.saleDelete.errors.length === 0) {
notify({
text: i18n.t("Removed sale", {
context: "notification"
text: intl.formatMessage({
defaultMessage: "Removed sale"
})
});
navigate(saleListUrl(), true);
@ -92,9 +94,7 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
const handleSaleUpdate = (data: SaleUpdate) => {
if (data.saleUpdate.errors.length === 0) {
notify({
text: i18n.t("Updated sale", {
context: "notification"
})
text: intl.formatMessage(commonMessages.savedChanges)
});
}
};
@ -131,6 +131,8 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
}
};
const canOpenBulkActionDialog = maybe(() => params.ids.length > 0);
return (
<TypedSaleCataloguesRemove onCompleted={handleCatalogueRemove}>
{(saleCataloguesRemove, saleCataloguesRemoveOpts) => (
@ -222,7 +224,9 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
return (
<>
<WindowTitle title={i18n.t("Sales")} />
<WindowTitle
title={intl.formatMessage(sectionNames.sales)}
/>
<SaleDetailsPage
defaultCurrency={maybe(
() => shop.defaultCurrency
@ -295,7 +299,11 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
openModal("unassign-category", listElements)
}
>
{i18n.t("Unassign")}
<FormattedMessage
defaultMessage="Unassign"
description="unassign category from sale, button"
id="saleDetailsUnassignCategory"
/>
</Button>
}
collectionListToolbar={
@ -308,7 +316,11 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
)
}
>
{i18n.t("Unassign")}
<FormattedMessage
defaultMessage="Unassign"
description="unassign collection from sale, button"
id="saleDetailsUnassignCollection"
/>
</Button>
}
productListToolbar={
@ -318,7 +330,11 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
openModal("unassign-product", listElements)
}
>
{i18n.t("Unassign")}
<FormattedMessage
defaultMessage="Unassign"
description="unassign product from sale, button"
id="saleDetailsUnassignProduct"
/>
</Button>
}
isChecked={isSelected}
@ -437,77 +453,111 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
)}
</SearchCollections>
<ActionDialog
open={params.action === "unassign-category"}
title={i18n.t("Unassign Categories From Sale")}
open={
params.action === "unassign-category" &&
canOpenBulkActionDialog
}
title={intl.formatMessage({
defaultMessage: "Unassign Categories From Sale",
description: "dialog header"
})}
confirmButtonState={unassignTransitionState}
onClose={closeModal}
onConfirm={() =>
handleCategoriesUnassign(params.ids)
}
>
<DialogContentText
dangerouslySetInnerHTML={{
__html: i18n.t(
"Are you sure you want to unassign <strong>{{ saleName }}</strong> categories?",
{
saleName: maybe(
() => params.ids.length.toString(),
"..."
{canOpenBulkActionDialog && (
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to unassign {counter, plural,
one {this category}
other {{displayQuantity} categories}
}?"
description="dialog content"
values={{
counter: params.ids.length,
displayQuantity: (
<strong>{params.ids.length}</strong>
)
}
)
}}
/>
}}
/>
</DialogContentText>
)}
</ActionDialog>
<ActionDialog
open={params.action === "unassign-collection"}
title={i18n.t("Unassign Collections From Sale")}
open={
params.action === "unassign-collection" &&
canOpenBulkActionDialog
}
title={intl.formatMessage({
defaultMessage:
"Unassign Collections From Sale",
description: "dialog header"
})}
confirmButtonState={unassignTransitionState}
onClose={closeModal}
onConfirm={() =>
handleCollectionsUnassign(params.ids)
}
>
<DialogContentText
dangerouslySetInnerHTML={{
__html: i18n.t(
"Are you sure you want to unassign <strong>{{ saleName }}</strong> collections?",
{
saleName: maybe(
() => params.ids.length.toString(),
"..."
{canOpenBulkActionDialog && (
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to unassign {counter, plural,
one {this collection}
other {{displayQuantity} collections}
}?"
description="dialog content"
values={{
counter: params.ids.length,
displayQuantity: (
<strong>{params.ids.length}</strong>
)
}
)
}}
/>
}}
/>
</DialogContentText>
)}
</ActionDialog>
<ActionDialog
open={params.action === "unassign-product"}
title={i18n.t("Unassign Products From Sale")}
open={
params.action === "unassign-product" &&
canOpenBulkActionDialog
}
title={intl.formatMessage({
defaultMessage: "Unassign Products From Sale",
description: "dialog header"
})}
confirmButtonState={unassignTransitionState}
onClose={closeModal}
onConfirm={() =>
handleProductsUnassign(params.ids)
}
>
<DialogContentText
dangerouslySetInnerHTML={{
__html: i18n.t(
"Are you sure you want to unassign <strong>{{ saleName }}</strong> products?",
{
saleName: maybe(
() => params.ids.length.toString(),
"..."
{canOpenBulkActionDialog && (
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to unassign {counter, plural,
one {this product}
other {{displayQuantity} products}
}?"
description="dialog content"
values={{
counter: params.ids.length,
displayQuantity: (
<strong>{params.ids.length}</strong>
)
}
)
}}
/>
}}
/>
</DialogContentText>
)}
</ActionDialog>
<ActionDialog
open={params.action === "remove"}
title={i18n.t("Remove Sale")}
title={intl.formatMessage({
defaultMessage: "Delete Sale",
description: "dialog header"
})}
confirmButtonState={removeTransitionState}
onClose={closeModal}
variant="delete"
@ -517,19 +567,19 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
})
}
>
<DialogContentText
dangerouslySetInnerHTML={{
__html: i18n.t(
"Are you sure you want to remove <strong>{{ saleName }}</strong>?",
{
saleName: maybe(
() => data.sale.name,
"..."
)
}
)
}}
/>
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to delete {saleName}?"
description="dialog content"
values={{
saleName: (
<strong>
{maybe(() => data.sale.name, "...")}
</strong>
)
}}
/>
</DialogContentText>
</ActionDialog>
</>
);

View file

@ -2,6 +2,7 @@ import DialogContentText from "@material-ui/core/DialogContentText";
import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ActionDialog from "@saleor/components/ActionDialog";
import { WindowTitle } from "@saleor/components/WindowTitle";
@ -13,7 +14,7 @@ import usePaginator, {
createPaginationState
} from "@saleor/hooks/usePaginator";
import useShop from "@saleor/hooks/useShop";
import i18n from "@saleor/i18n";
import { commonMessages, sectionNames } from "@saleor/intl";
import { getMutationState, maybe } from "@saleor/misc";
import { ListViews } from "@saleor/types";
import SaleListPage from "../components/SaleListPage";
@ -44,10 +45,12 @@ export const SaleList: React.StatelessComponent<SaleListProps> = ({
const { updateListSettings, settings } = useListSettings(
ListViews.SALES_LIST
);
const intl = useIntl();
const closeModal = () => navigate(saleListUrl(), true);
const paginationState = createPaginationState(settings.rowNumber, params);
const canOpenBulkActionDialog = maybe(() => params.ids.length > 0);
return (
<TypedSaleList displayLoader variables={paginationState}>
@ -61,7 +64,7 @@ export const SaleList: React.StatelessComponent<SaleListProps> = ({
const handleSaleBulkDelete = (data: SaleBulkDelete) => {
if (data.saleBulkDelete.errors.length === 0) {
notify({
text: i18n.t("Removed sales")
text: intl.formatMessage(commonMessages.savedChanges)
});
reset();
closeModal();
@ -86,7 +89,7 @@ export const SaleList: React.StatelessComponent<SaleListProps> = ({
return (
<>
<WindowTitle title={i18n.t("Sales")} />
<WindowTitle title={intl.formatMessage(sectionNames.sales)} />
<SaleListPage
defaultCurrency={maybe(() => shop.defaultCurrency)}
sales={maybe(() => data.sales.edges.map(edge => edge.node))}
@ -122,23 +125,30 @@ export const SaleList: React.StatelessComponent<SaleListProps> = ({
confirmButtonState={bulkRemoveTransitionState}
onClose={closeModal}
onConfirm={onSaleBulkDelete}
open={params.action === "remove"}
title={i18n.t("Remove Sales")}
open={params.action === "remove" && canOpenBulkActionDialog}
title={intl.formatMessage({
defaultMessage: "Delete Sales",
description: "dialog header"
})}
variant="delete"
>
<DialogContentText
dangerouslySetInnerHTML={{
__html: i18n.t(
"Are you sure you want to remove <strong>{{ number }}</strong> sales?",
{
number: maybe(
() => params.ids.length.toString(),
"..."
{canOpenBulkActionDialog && (
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to delete {counter, plural,
one {this sale}
other {{displayQuantity} sales}
}?"
description="dialog content"
values={{
counter: params.ids.length,
displayQuantity: (
<strong>{params.ids.length}</strong>
)
}
)
}}
/>
}}
/>
</DialogContentText>
)}
</ActionDialog>
</>
);

View file

@ -1,10 +1,11 @@
import React from "react";
import { useIntl } from "react-intl";
import { WindowTitle } from "@saleor/components/WindowTitle";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import useShop from "@saleor/hooks/useShop";
import i18n from "../../i18n";
import { sectionNames } from "@saleor/intl";
import { decimal, getMutationState, joinDateTime, maybe } from "../../misc";
import {
DiscountValueTypeEnum,
@ -20,12 +21,13 @@ export const VoucherDetails: React.StatelessComponent = () => {
const navigate = useNavigator();
const notify = useNotifier();
const shop = useShop();
const intl = useIntl();
const handleVoucherCreate = (data: VoucherCreate) => {
if (data.voucherCreate.errors.length === 0) {
notify({
text: i18n.t("Successfully created voucher", {
context: "notification"
text: intl.formatMessage({
defaultMessage: "Successfully created voucher"
})
});
navigate(voucherUrl(data.voucherCreate.voucher.id), true);
@ -43,7 +45,7 @@ export const VoucherDetails: React.StatelessComponent = () => {
return (
<>
<WindowTitle title={i18n.t("Vouchers")} />
<WindowTitle title={intl.formatMessage(sectionNames.vouchers)} />
<VoucherCreatePage
defaultCurrency={maybe(() => shop.defaultCurrency)}
disabled={voucherCreateOpts.loading}

View file

@ -1,6 +1,7 @@
import Button from "@material-ui/core/Button";
import DialogContentText from "@material-ui/core/DialogContentText";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ActionDialog from "@saleor/components/ActionDialog";
import AssignCategoriesDialog from "@saleor/components/AssignCategoryDialog";
@ -14,13 +15,13 @@ import usePaginator, {
createPaginationState
} from "@saleor/hooks/usePaginator";
import useShop from "@saleor/hooks/useShop";
import { commonMessages, sectionNames } from "@saleor/intl";
import { categoryUrl } from "../../categories/urls";
import { collectionUrl } from "../../collections/urls";
import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "../../config";
import SearchCategories from "../../containers/SearchCategories";
import SearchCollections from "../../containers/SearchCollections";
import SearchProducts from "../../containers/SearchProducts";
import i18n from "../../i18n";
import { decimal, getMutationState, joinDateTime, maybe } from "../../misc";
import { productUrl } from "../../products/urls";
import {
@ -66,6 +67,7 @@ export const VoucherDetails: React.StatelessComponent<VoucherDetailsProps> = ({
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
params.ids
);
const intl = useIntl();
const paginationState = createPaginationState(PAGINATE_BY, params);
const changeTab = (tab: VoucherDetailsPageTab) => {
@ -80,8 +82,8 @@ export const VoucherDetails: React.StatelessComponent<VoucherDetailsProps> = ({
const handleVoucherDelete = (data: VoucherDelete) => {
if (data.voucherDelete.errors.length === 0) {
notify({
text: i18n.t("Removed voucher", {
context: "notification"
text: intl.formatMessage({
defaultMessage: "Deleted voucher"
})
});
navigate(voucherListUrl(), true);
@ -92,9 +94,7 @@ export const VoucherDetails: React.StatelessComponent<VoucherDetailsProps> = ({
if (data.voucherUpdate.errors.length === 0) {
closeModal();
notify({
text: i18n.t("Updated voucher", {
context: "notification"
})
text: intl.formatMessage(commonMessages.savedChanges)
});
}
};
@ -130,6 +130,8 @@ export const VoucherDetails: React.StatelessComponent<VoucherDetailsProps> = ({
}
};
const canOpenBulkActionDialog = maybe(() => params.ids.length > 0);
return (
<TypedVoucherCataloguesRemove onCompleted={handleCatalogueRemove}>
{(voucherCataloguesRemove, voucherCataloguesRemoveOpts) => (
@ -225,7 +227,9 @@ export const VoucherDetails: React.StatelessComponent<VoucherDetailsProps> = ({
return (
<>
<WindowTitle title={i18n.t("Vouchers")} />
<WindowTitle
title={intl.formatMessage(sectionNames.vouchers)}
/>
<VoucherDetailsPage
defaultCurrency={maybe(
() => shop.defaultCurrency
@ -374,7 +378,11 @@ export const VoucherDetails: React.StatelessComponent<VoucherDetailsProps> = ({
openModal("unassign-category", listElements)
}
>
{i18n.t("Unassign")}
<FormattedMessage
defaultMessage="Unassign"
description="unassign category from voucher, button"
id="voucherDetailsUnassignCategory"
/>
</Button>
}
collectionListToolbar={
@ -387,7 +395,11 @@ export const VoucherDetails: React.StatelessComponent<VoucherDetailsProps> = ({
)
}
>
{i18n.t("Unassign")}
<FormattedMessage
defaultMessage="Unassign"
description="unassign collection from voucher, button"
id="voucherDetailsUnassignCollection"
/>
</Button>
}
productListToolbar={
@ -397,7 +409,11 @@ export const VoucherDetails: React.StatelessComponent<VoucherDetailsProps> = ({
openModal("unassign-product", listElements)
}
>
{i18n.t("Unassign")}
<FormattedMessage
defaultMessage="Unassign"
description="unassign product from voucher, button"
id="voucherDetailsUnassignProduct"
/>
</Button>
}
isChecked={isSelected}
@ -539,77 +555,113 @@ export const VoucherDetails: React.StatelessComponent<VoucherDetailsProps> = ({
)}
</SearchProducts>
<ActionDialog
open={params.action === "unassign-category"}
title={i18n.t("Unassign Categories From Sale")}
open={
params.action === "unassign-category" &&
canOpenBulkActionDialog
}
title={intl.formatMessage({
defaultMessage:
"Unassign Categories From Voucher",
description: "dialog header"
})}
confirmButtonState={unassignTransitionState}
onClose={closeModal}
onConfirm={() =>
handleCategoriesUnassign(params.ids)
}
>
<DialogContentText
dangerouslySetInnerHTML={{
__html: i18n.t(
"Are you sure you want to unassign <strong>{{ saleName }}</strong> categories?",
{
saleName: maybe(
() => params.ids.length.toString(),
"..."
{canOpenBulkActionDialog && (
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to unassign {counter, plural,
one {this category}
other {{displayQuantity} categories}
}?"
description="dialog content"
values={{
counter: params.ids.length,
displayQuantity: (
<strong>{params.ids.length}</strong>
)
}
)
}}
/>
}}
/>
</DialogContentText>
)}
</ActionDialog>
<ActionDialog
open={params.action === "unassign-collection"}
title={i18n.t("Unassign Collections From Sale")}
open={
params.action === "unassign-collection" &&
canOpenBulkActionDialog
}
title={intl.formatMessage({
defaultMessage:
"Unassign Collections From Voucher",
description: "dialog header"
})}
confirmButtonState={unassignTransitionState}
onClose={closeModal}
onConfirm={() =>
handleCollectionsUnassign(params.ids)
}
>
<DialogContentText
dangerouslySetInnerHTML={{
__html: i18n.t(
"Are you sure you want to unassign <strong>{{ saleName }}</strong> collections?",
{
saleName: maybe(
() => params.ids.length.toString(),
"..."
{canOpenBulkActionDialog && (
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to unassign {counter, plural,
one {this collection}
other {{displayQuantity} collections}
}?"
description="dialog content"
values={{
counter: params.ids.length,
displayQuantity: (
<strong>{params.ids.length}</strong>
)
}
)
}}
/>
}}
/>
</DialogContentText>
)}
</ActionDialog>
<ActionDialog
open={params.action === "unassign-product"}
title={i18n.t("Unassign Products From Sale")}
open={
params.action === "unassign-product" &&
canOpenBulkActionDialog
}
title={intl.formatMessage({
defaultMessage:
"Unassign Products From Voucher",
description: "dialog header"
})}
confirmButtonState={unassignTransitionState}
onClose={closeModal}
onConfirm={() =>
handleProductsUnassign(params.ids)
}
>
<DialogContentText
dangerouslySetInnerHTML={{
__html: i18n.t(
"Are you sure you want to unassign <strong>{{ saleName }}</strong> products?",
{
saleName: maybe(
() => params.ids.length.toString(),
"..."
{canOpenBulkActionDialog && (
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to unassign {counter, plural,
one {this product}
other {{displayQuantity} products}
}?"
description="dialog content"
values={{
counter: params.ids.length,
displayQuantity: (
<strong>{params.ids.length}</strong>
)
}
)
}}
/>
}}
/>
</DialogContentText>
)}
</ActionDialog>
<ActionDialog
open={params.action === "remove"}
title={i18n.t("Remove Voucher")}
title={intl.formatMessage({
defaultMessage: "Delete Voucher",
description: "dialog header"
})}
confirmButtonState={removeTransitionState}
onClose={closeModal}
variant="delete"
@ -619,19 +671,19 @@ export const VoucherDetails: React.StatelessComponent<VoucherDetailsProps> = ({
})
}
>
<DialogContentText
dangerouslySetInnerHTML={{
__html: i18n.t(
"Are you sure you want to remove <strong>{{ voucherCode }}</strong>?",
{
voucherCode: maybe(
() => data.voucher.code,
"..."
)
}
)
}}
/>
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to delete {voucherCode}?"
description="dialog content"
values={{
voucherCode: (
<strong>
{maybe(() => data.voucher.code, "...")}
</strong>
)
}}
/>
</DialogContentText>
</ActionDialog>
</>
);

View file

@ -2,6 +2,7 @@ import DialogContentText from "@material-ui/core/DialogContentText";
import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ActionDialog from "@saleor/components/ActionDialog";
import { WindowTitle } from "@saleor/components/WindowTitle";
@ -13,7 +14,7 @@ import usePaginator, {
createPaginationState
} from "@saleor/hooks/usePaginator";
import useShop from "@saleor/hooks/useShop";
import i18n from "@saleor/i18n";
import { commonMessages, sectionNames } from "@saleor/intl";
import { getMutationState, maybe } from "@saleor/misc";
import { ListViews } from "@saleor/types";
import VoucherListPage from "../components/VoucherListPage";
@ -44,10 +45,12 @@ export const VoucherList: React.StatelessComponent<VoucherListProps> = ({
const { updateListSettings, settings } = useListSettings(
ListViews.VOUCHER_LIST
);
const intl = useIntl();
const closeModal = () => navigate(voucherListUrl(), true);
const paginationState = createPaginationState(settings.rowNumber, params);
const canOpenBulkActionDialog = maybe(() => params.ids.length > 0);
return (
<TypedVoucherList displayLoader variables={paginationState}>
@ -61,7 +64,7 @@ export const VoucherList: React.StatelessComponent<VoucherListProps> = ({
const handleVoucherBulkDelete = (data: VoucherBulkDelete) => {
if (data.voucherBulkDelete.errors.length === 0) {
notify({
text: i18n.t("Removed vouchers")
text: intl.formatMessage(commonMessages.savedChanges)
});
reset();
closeModal();
@ -85,7 +88,9 @@ export const VoucherList: React.StatelessComponent<VoucherListProps> = ({
});
return (
<>
<WindowTitle title={i18n.t("Vouchers")} />
<WindowTitle
title={intl.formatMessage(sectionNames.vouchers)}
/>
<VoucherListPage
defaultCurrency={maybe(() => shop.defaultCurrency)}
settings={settings}
@ -123,23 +128,30 @@ export const VoucherList: React.StatelessComponent<VoucherListProps> = ({
confirmButtonState={bulkRemoveTransitionState}
onClose={closeModal}
onConfirm={onVoucherBulkDelete}
open={params.action === "remove"}
title={i18n.t("Remove Vouchers")}
open={params.action === "remove" && canOpenBulkActionDialog}
title={intl.formatMessage({
defaultMessage: "Delete Vouchers",
description: "dialog header"
})}
variant="delete"
>
<DialogContentText
dangerouslySetInnerHTML={{
__html: i18n.t(
"Are you sure you want to remove <strong>{{ number }}</strong> vouchers?",
{
number: maybe(
() => params.ids.length.toString(),
"..."
{canOpenBulkActionDialog && (
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to delete {counter, plural,
one {this voucher}
other {{displayQuantity} vouchers}
}?"
description="dialog content"
values={{
counter: params.ids.length,
displayQuantity: (
<strong>{params.ids.length}</strong>
)
}
)
}}
/>
}}
/>
</DialogContentText>
)}
</ActionDialog>
</>
);

View file

@ -13,6 +13,12 @@ export const commonMessages = defineMessages({
email: {
defaultMessage: "E-mail Address"
},
endDate: {
defaultMessage: "End Date"
},
endHour: {
defaultMessage: "End Hour"
},
firstName: {
defaultMessage: "First Name"
},
@ -28,6 +34,15 @@ export const commonMessages = defineMessages({
savedChanges: {
defaultMessage: "Saved changes"
},
startDate: {
defaultMessage: "Start Date"
},
startHour: {
defaultMessage: "Start Hour"
},
summary: {
defaultMessage: "Summary"
},
uploadImage: {
defaultMessage: "Upload image",
description: "button"
@ -94,6 +109,10 @@ export const sectionNames = defineMessages({
defaultMessage: "Product Types",
description: "product types section name"
},
sales: {
defaultMessage: "Sales",
description: "sales section name"
},
shipping: {
defaultMessage: "Shipping Methods",
description: "shipping section name"
@ -109,5 +128,9 @@ export const sectionNames = defineMessages({
taxes: {
defaultMessage: "Taxes",
description: "taxes section name"
},
vouchers: {
defaultMessage: "Vouchers",
description: "vouchers section name"
}
});