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 { export interface MessageDescriptor {
description?: string; description?: string;
defaultMessage: string; defaultMessage: string;
id?: string;
} }
type Messages<Names extends keyof any = string> = Record< type Messages<Names extends keyof any = string> = Record<
Names, Names,

View file

@ -14,13 +14,13 @@ import TableFooter from "@material-ui/core/TableFooter";
import TableRow from "@material-ui/core/TableRow"; import TableRow from "@material-ui/core/TableRow";
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 CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import Checkbox from "@saleor/components/Checkbox"; import Checkbox from "@saleor/components/Checkbox";
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 "../../../i18n";
import { maybe, renderCollection } from "../../../misc"; import { maybe, renderCollection } from "../../../misc";
import { ListActions, ListProps } from "../../../types"; import { ListActions, ListProps } from "../../../types";
import { SaleDetails_sale } from "../../types/SaleDetails"; import { SaleDetails_sale } from "../../types/SaleDetails";
@ -71,13 +71,22 @@ const DiscountCategories = withStyles(styles, {
toggleAll, toggleAll,
selected, selected,
isChecked isChecked
}: DiscountCategoriesProps & WithStyles<typeof styles>) => ( }: DiscountCategoriesProps & WithStyles<typeof styles>) => {
const intl = useIntl();
return (
<Card> <Card>
<CardTitle <CardTitle
title={i18n.t("Eligible Categories")} title={intl.formatMessage({
defaultMessage: "Eligible Categories",
description: "section header"
})}
toolbar={ toolbar={
<Button color="primary" onClick={onCategoryAssign}> <Button color="primary" onClick={onCategoryAssign}>
{i18n.t("Assign categories")} <FormattedMessage
defaultMessage="Assign categories"
description="button"
/>
</Button> </Button>
} }
/> />
@ -92,10 +101,13 @@ const DiscountCategories = withStyles(styles, {
> >
<> <>
<TableCell className={classes.wideColumn}> <TableCell className={classes.wideColumn}>
{i18n.t("Category name")} <FormattedMessage defaultMessage="Category name" />
</TableCell> </TableCell>
<TableCell className={classes.textRight}> <TableCell className={classes.textRight}>
{i18n.t("Products")} <FormattedMessage
defaultMessage="Products"
description="number of products"
/>
</TableCell> </TableCell>
<TableCell /> <TableCell />
</> </>
@ -104,7 +116,9 @@ const DiscountCategories = withStyles(styles, {
<TableRow> <TableRow>
<TablePagination <TablePagination
colSpan={numberOfColumns} colSpan={numberOfColumns}
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false} hasNextPage={
pageInfo && !disabled ? pageInfo.hasNextPage : false
}
onNextPage={onNextPage} onNextPage={onNextPage}
hasPreviousPage={ hasPreviousPage={
pageInfo && !disabled ? pageInfo.hasPreviousPage : false pageInfo && !disabled ? pageInfo.hasPreviousPage : false
@ -136,7 +150,10 @@ const DiscountCategories = withStyles(styles, {
/> />
</TableCell> </TableCell>
<TableCell> <TableCell>
{maybe<React.ReactNode>(() => category.name, <Skeleton />)} {maybe<React.ReactNode>(
() => category.name,
<Skeleton />
)}
</TableCell> </TableCell>
<TableCell className={classes.textRight}> <TableCell className={classes.textRight}>
{maybe<React.ReactNode>( {maybe<React.ReactNode>(
@ -161,7 +178,7 @@ const DiscountCategories = withStyles(styles, {
() => ( () => (
<TableRow> <TableRow>
<TableCell colSpan={numberOfColumns}> <TableCell colSpan={numberOfColumns}>
{i18n.t("No categories found")} <FormattedMessage defaultMessage="No categories found" />
</TableCell> </TableCell>
</TableRow> </TableRow>
) )
@ -169,7 +186,8 @@ const DiscountCategories = withStyles(styles, {
</TableBody> </TableBody>
</Table> </Table>
</Card> </Card>
) );
}
); );
DiscountCategories.displayName = "DiscountCategories"; DiscountCategories.displayName = "DiscountCategories";
export default 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 TableRow from "@material-ui/core/TableRow";
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 CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import Checkbox from "@saleor/components/Checkbox"; import Checkbox from "@saleor/components/Checkbox";
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 "../../../i18n";
import { maybe, renderCollection } from "../../../misc"; import { maybe, renderCollection } from "../../../misc";
import { ListActions, ListProps } from "../../../types"; import { ListActions, ListProps } from "../../../types";
import { SaleDetails_sale } from "../../types/SaleDetails"; import { SaleDetails_sale } from "../../types/SaleDetails";
@ -71,13 +71,22 @@ const DiscountCollections = withStyles(styles, {
toggle, toggle,
toggleAll, toggleAll,
toolbar toolbar
}: DiscountCollectionsProps & WithStyles<typeof styles>) => ( }: DiscountCollectionsProps & WithStyles<typeof styles>) => {
const intl = useIntl();
return (
<Card> <Card>
<CardTitle <CardTitle
title={i18n.t("Eligible Collections")} title={intl.formatMessage({
defaultMessage: "Eligible Collections",
description: "section header"
})}
toolbar={ toolbar={
<Button color="primary" onClick={onCollectionAssign}> <Button color="primary" onClick={onCollectionAssign}>
{i18n.t("Assign collections")} <FormattedMessage
defaultMessage="Assign collections"
description="button"
/>
</Button> </Button>
} }
/> />
@ -91,10 +100,13 @@ const DiscountCollections = withStyles(styles, {
toolbar={toolbar} toolbar={toolbar}
> >
<TableCell className={classes.wideColumn}> <TableCell className={classes.wideColumn}>
{i18n.t("Collection name")} <FormattedMessage defaultMessage="Collection name" />
</TableCell> </TableCell>
<TableCell className={classes.textRight}> <TableCell className={classes.textRight}>
{i18n.t("Products")} <FormattedMessage
defaultMessage="Products"
description="number of products"
/>
</TableCell> </TableCell>
<TableCell /> <TableCell />
</TableHead> </TableHead>
@ -102,7 +114,9 @@ const DiscountCollections = withStyles(styles, {
<TableRow> <TableRow>
<TablePagination <TablePagination
colSpan={numberOfColumns} colSpan={numberOfColumns}
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false} hasNextPage={
pageInfo && !disabled ? pageInfo.hasNextPage : false
}
onNextPage={onNextPage} onNextPage={onNextPage}
hasPreviousPage={ hasPreviousPage={
pageInfo && !disabled ? pageInfo.hasPreviousPage : false pageInfo && !disabled ? pageInfo.hasPreviousPage : false
@ -115,7 +129,9 @@ const DiscountCollections = withStyles(styles, {
{renderCollection( {renderCollection(
maybe(() => sale.collections.edges.map(edge => edge.node)), maybe(() => sale.collections.edges.map(edge => edge.node)),
collection => { collection => {
const isSelected = collection ? isChecked(collection.id) : false; const isSelected = collection
? isChecked(collection.id)
: false;
return ( return (
<TableRow <TableRow
selected={isSelected} selected={isSelected}
@ -161,7 +177,7 @@ const DiscountCollections = withStyles(styles, {
() => ( () => (
<TableRow> <TableRow>
<TableCell colSpan={numberOfColumns}> <TableCell colSpan={numberOfColumns}>
{i18n.t("No collections found")} <FormattedMessage defaultMessage="No collections found" />
</TableCell> </TableCell>
</TableRow> </TableRow>
) )
@ -169,7 +185,8 @@ const DiscountCollections = withStyles(styles, {
</TableBody> </TableBody>
</Table> </Table>
</Card> </Card>
) );
}
); );
DiscountCollections.displayName = "DiscountCollections"; DiscountCollections.displayName = "DiscountCollections";
export default 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 Typography from "@material-ui/core/Typography";
import { filter } from "fuzzaldrin"; import { filter } from "fuzzaldrin";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import Checkbox from "@saleor/components/Checkbox"; import Checkbox from "@saleor/components/Checkbox";
import ConfirmButton, { import ConfirmButton, {
@ -27,7 +28,7 @@ import FormSpacer from "@saleor/components/FormSpacer";
import Hr from "@saleor/components/Hr"; import Hr from "@saleor/components/Hr";
// tslint:disable no-submodule-imports // tslint:disable no-submodule-imports
import { ShopInfo_shop_countries } from "@saleor/components/Shop/types/ShopInfo"; import { ShopInfo_shop_countries } from "@saleor/components/Shop/types/ShopInfo";
import i18n from "../../../i18n"; import { buttonMessages } from "@saleor/intl";
interface FormData { interface FormData {
allCountries: boolean; allCountries: boolean;
@ -72,6 +73,8 @@ const DiscountCountrySelectDialog = withStyles(styles, {
initial, initial,
onConfirm onConfirm
}: DiscountCountrySelectDialogProps & WithStyles<typeof styles>) => { }: DiscountCountrySelectDialogProps & WithStyles<typeof styles>) => {
const intl = useIntl();
const initialForm: FormData = { const initialForm: FormData = {
allCountries: true, allCountries: true,
countries: initial, countries: initial,
@ -90,23 +93,28 @@ const DiscountCountrySelectDialog = withStyles(styles, {
return ( return (
<> <>
<DialogTitle>{i18n.t("Assign Countries")}</DialogTitle> <DialogTitle>
<FormattedMessage
defaultMessage="Assign Countries"
description="dialog header"
/>
</DialogTitle>
<DialogContent> <DialogContent>
<Typography> <Typography>
{i18n.t( <FormattedMessage defaultMessage="Choose countries, you want voucher to be limited to, from the list below" />
"Choose countries, you want voucher to be limited to, from the list below"
)}
</Typography> </Typography>
<FormSpacer /> <FormSpacer />
<TextField <TextField
name="query" name="query"
value={data.query} value={data.query}
onChange={event => change(event, () => fetch(data.query))} onChange={event => change(event, () => fetch(data.query))}
label={i18n.t("Search Countries", { label={intl.formatMessage({
context: "country search input label" defaultMessage: "Filter Countries",
description: "search box label"
})} })}
placeholder={i18n.t("Search by country name", { placeholder={intl.formatMessage({
context: "country search input placeholder" defaultMessage: "Search by country name",
description: "search box placeholder"
})} })}
fullWidth fullWidth
/> />
@ -114,9 +122,10 @@ const DiscountCountrySelectDialog = withStyles(styles, {
<Hr /> <Hr />
<DialogContent className={classes.container}> <DialogContent className={classes.container}>
<Typography className={classes.heading} variant="subtitle1"> <Typography className={classes.heading} variant="subtitle1">
{i18n.t("Countries A to Z", { <FormattedMessage
context: "country selection" defaultMessage="Countries A to Z"
})} description="country selection"
/>
</Typography> </Typography>
<Table> <Table>
<TableBody> <TableBody>
@ -167,7 +176,7 @@ const DiscountCountrySelectDialog = 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}
@ -175,7 +184,10 @@ const DiscountCountrySelectDialog = withStyles(styles, {
variant="contained" variant="contained"
type="submit" type="submit"
> >
{i18n.t("Assign countries", { context: "button" })} <FormattedMessage
defaultMessage="Assign countries"
description="button"
/>
</ConfirmButton> </ConfirmButton>
</DialogActions> </DialogActions>
</> </>

View file

@ -14,6 +14,7 @@ import TableFooter from "@material-ui/core/TableFooter";
import TableRow from "@material-ui/core/TableRow"; import TableRow from "@material-ui/core/TableRow";
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 CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import Checkbox from "@saleor/components/Checkbox"; import Checkbox from "@saleor/components/Checkbox";
@ -24,7 +25,6 @@ import TableCellAvatar, {
} from "@saleor/components/TableCellAvatar"; } from "@saleor/components/TableCellAvatar";
import TableHead from "@saleor/components/TableHead"; import TableHead from "@saleor/components/TableHead";
import TablePagination from "@saleor/components/TablePagination"; import TablePagination from "@saleor/components/TablePagination";
import i18n from "../../../i18n";
import { maybe, renderCollection } from "../../../misc"; import { maybe, renderCollection } from "../../../misc";
import { ListActions, ListProps } from "../../../types"; import { ListActions, ListProps } from "../../../types";
import { SaleDetails_sale } from "../../types/SaleDetails"; import { SaleDetails_sale } from "../../types/SaleDetails";
@ -84,13 +84,22 @@ const DiscountProducts = withStyles(styles, {
toggle, toggle,
toggleAll, toggleAll,
toolbar toolbar
}: SaleProductsProps & WithStyles<typeof styles>) => ( }: SaleProductsProps & WithStyles<typeof styles>) => {
const intl = useIntl();
return (
<Card> <Card>
<CardTitle <CardTitle
title={i18n.t("Eligible Products")} title={intl.formatMessage({
defaultMessage: "Eligible Products",
description: "section header"
})}
toolbar={ toolbar={
<Button color="primary" onClick={onProductAssign}> <Button color="primary" onClick={onProductAssign}>
{i18n.t("Assign products")} <FormattedMessage
defaultMessage="Assign products"
description="button"
/>
</Button> </Button>
} }
/> />
@ -105,14 +114,17 @@ const DiscountProducts = withStyles(styles, {
> >
<TableCell className={classes.colName}> <TableCell className={classes.colName}>
<span className={classes.colNameLabel}> <span className={classes.colNameLabel}>
{i18n.t("Product name")} <FormattedMessage defaultMessage="Product Name" />
</span> </span>
</TableCell> </TableCell>
<TableCell className={classes.colType}> <TableCell className={classes.colType}>
{i18n.t("Product Type")} <FormattedMessage defaultMessage="Product Type" />
</TableCell> </TableCell>
<TableCell className={classes.colPublished}> <TableCell className={classes.colPublished}>
{i18n.t("Published")} <FormattedMessage
defaultMessage="Published"
description="product is published"
/>
</TableCell> </TableCell>
<TableCell /> <TableCell />
</TableHead> </TableHead>
@ -120,7 +132,9 @@ const DiscountProducts = withStyles(styles, {
<TableRow> <TableRow>
<TablePagination <TablePagination
colSpan={numberOfColumns} colSpan={numberOfColumns}
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false} hasNextPage={
pageInfo && !disabled ? pageInfo.hasNextPage : false
}
onNextPage={onNextPage} onNextPage={onNextPage}
hasPreviousPage={ hasPreviousPage={
pageInfo && !disabled ? pageInfo.hasPreviousPage : false pageInfo && !disabled ? pageInfo.hasPreviousPage : false
@ -167,9 +181,13 @@ const DiscountProducts = withStyles(styles, {
<StatusLabel <StatusLabel
label={ label={
product.isPublished product.isPublished
? i18n.t("Published", { context: "product status" }) ? intl.formatMessage({
: i18n.t("Not published", { defaultMessage: "Published",
context: "product status" description: "product is published"
})
: intl.formatMessage({
defaultMessage: "Not published",
description: "product is not published"
}) })
} }
status={product.isPublished ? "success" : "error"} status={product.isPublished ? "success" : "error"}
@ -195,7 +213,7 @@ const DiscountProducts = withStyles(styles, {
() => ( () => (
<TableRow> <TableRow>
<TableCell colSpan={numberOfColumns}> <TableCell colSpan={numberOfColumns}>
{i18n.t("No products found")} <FormattedMessage defaultMessage="No products found" />
</TableCell> </TableCell>
</TableRow> </TableRow>
) )
@ -203,7 +221,8 @@ const DiscountProducts = withStyles(styles, {
</TableBody> </TableBody>
</Table> </Table>
</Card> </Card>
) );
}
); );
DiscountProducts.displayName = "DiscountProducts"; DiscountProducts.displayName = "DiscountProducts";
export default DiscountProducts; export default DiscountProducts;

View file

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

View file

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

View file

@ -8,9 +8,10 @@ import {
} from "@material-ui/core/styles"; } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import React from "react"; import React from "react";
import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import i18n from "../../../i18n"; import { commonMessages } from "@saleor/intl";
import { FormData } from "../SaleDetailsPage"; import { FormData } from "../SaleDetailsPage";
export interface SaleInfoProps { export interface SaleInfoProps {
@ -40,9 +41,14 @@ const SaleInfo = withStyles(styles, {
disabled, disabled,
errors, errors,
onChange onChange
}: SaleInfoProps & WithStyles<typeof styles>) => ( }: SaleInfoProps & WithStyles<typeof styles>) => {
const intl = useIntl();
return (
<Card> <Card>
<CardTitle title={i18n.t("General Information")} /> <CardTitle
title={intl.formatMessage(commonMessages.generalInformations)}
/>
<CardContent className={classes.root}> <CardContent className={classes.root}>
<TextField <TextField
disabled={disabled} disabled={disabled}
@ -50,13 +56,17 @@ const SaleInfo = withStyles(styles, {
helperText={errors.name} helperText={errors.name}
name={"name" as keyof FormData} name={"name" as keyof FormData}
onChange={onChange} onChange={onChange}
label={i18n.t("Name")} label={intl.formatMessage({
defaultMessage: "Name",
description: "sale name"
})}
value={data.name} value={data.name}
fullWidth fullWidth
/> />
</CardContent> </CardContent>
</Card> </Card>
) );
}
); );
SaleInfo.displayName = "SaleInfo"; SaleInfo.displayName = "SaleInfo";
export default 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 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 Date from "@saleor/components/Date"; import Date from "@saleor/components/Date";
@ -19,7 +20,6 @@ import Percent from "@saleor/components/Percent";
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 { maybe, renderCollection } from "@saleor/misc"; import { maybe, renderCollection } from "@saleor/misc";
import { ListActions, ListProps } from "@saleor/types"; import { ListActions, ListProps } from "@saleor/types";
import { SaleType } from "@saleor/types/globalTypes"; import { SaleType } from "@saleor/types/globalTypes";
@ -92,24 +92,22 @@ const SaleList = withStyles(styles, {
toolbar={toolbar} toolbar={toolbar}
> >
<TableCell className={classes.colName}> <TableCell className={classes.colName}>
{i18n.t("Name", { <FormattedMessage defaultMessage="Name" description="sale name" />
context: "sale list table header"
})}
</TableCell> </TableCell>
<TableCell className={classes.colStart}> <TableCell className={classes.colStart}>
{i18n.t("Starts", { <FormattedMessage
context: "sale list table header" defaultMessage="Starts"
})} description="sale start date"
/>
</TableCell> </TableCell>
<TableCell className={classes.colEnd}> <TableCell className={classes.colEnd}>
{i18n.t("Ends", { <FormattedMessage
context: "sale list table header" defaultMessage="Ends"
})} description="sale end date"
/>
</TableCell> </TableCell>
<TableCell className={classes.colValue}> <TableCell className={classes.colValue}>
{i18n.t("Value", { <FormattedMessage defaultMessage="Value" description="sale value" />
context: "sale list table header"
})}
</TableCell> </TableCell>
</TableHead> </TableHead>
<TableFooter> <TableFooter>
@ -193,7 +191,7 @@ const SaleList = withStyles(styles, {
() => ( () => (
<TableRow> <TableRow>
<TableCell colSpan={numberOfColumns}> <TableCell colSpan={numberOfColumns}>
{i18n.t("No sales found")} <FormattedMessage defaultMessage="No sales found" />
</TableCell> </TableCell>
</TableRow> </TableRow>
) )

View file

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

View file

@ -9,11 +9,12 @@ import {
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
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 Hr from "@saleor/components/Hr"; import Hr from "@saleor/components/Hr";
import TextFieldWithChoice from "@saleor/components/TextFieldWithChoice"; import TextFieldWithChoice from "@saleor/components/TextFieldWithChoice";
import i18n from "../../../i18n"; import { commonMessages } from "@saleor/intl";
import { FormErrors } from "../../../types"; import { FormErrors } from "../../../types";
import { SaleType } from "../../../types/globalTypes"; import { SaleType } from "../../../types/globalTypes";
import { FormData } from "../SaleDetailsPage"; import { FormData } from "../SaleDetailsPage";
@ -49,9 +50,17 @@ const SalePricing = withStyles(styles, {
disabled, disabled,
errors, errors,
onChange onChange
}: SalePricingProps & WithStyles<typeof styles>) => ( }: SalePricingProps & WithStyles<typeof styles>) => {
const intl = useIntl();
return (
<Card> <Card>
<CardTitle title={i18n.t("Pricing")} /> <CardTitle
title={intl.formatMessage({
defaultMessage: "Pricing",
description: "sale pricing, header"
})}
/>
<CardContent className={classes.root}> <CardContent className={classes.root}>
<TextFieldWithChoice <TextFieldWithChoice
disabled={disabled} disabled={disabled}
@ -73,7 +82,9 @@ const SalePricing = withStyles(styles, {
helperText={errors.value} helperText={errors.value}
name={"value" as keyof FormData} name={"value" as keyof FormData}
onChange={onChange} onChange={onChange}
label={i18n.t("Discount Value")} label={intl.formatMessage({
defaultMessage: "Discount Value"
})}
value={data.value} value={data.value}
type="number" type="number"
fullWidth fullWidth
@ -85,7 +96,10 @@ const SalePricing = withStyles(styles, {
<Hr /> <Hr />
<CardContent className={classes.root}> <CardContent className={classes.root}>
<Typography className={classes.subheading} variant="subtitle1"> <Typography className={classes.subheading} variant="subtitle1">
{i18n.t("Time Frame")} <FormattedMessage
defaultMessage="Time Frame"
description="time during which sale is active"
/>
</Typography> </Typography>
<TextField <TextField
disabled={disabled} disabled={disabled}
@ -93,7 +107,7 @@ const SalePricing = withStyles(styles, {
helperText={errors.startDate} helperText={errors.startDate}
name={"startDate" as keyof FormData} name={"startDate" as keyof FormData}
onChange={onChange} onChange={onChange}
label={i18n.t("Start Date")} label={intl.formatMessage(commonMessages.startDate)}
value={data.startDate} value={data.startDate}
type="date" type="date"
InputLabelProps={{ InputLabelProps={{
@ -107,7 +121,7 @@ const SalePricing = withStyles(styles, {
helperText={errors.endDate} helperText={errors.endDate}
name={"endDate" as keyof FormData} name={"endDate" as keyof FormData}
onChange={onChange} onChange={onChange}
label={i18n.t("End Date")} label={intl.formatMessage(commonMessages.endDate)}
value={data.endDate} value={data.endDate}
type="date" type="date"
InputLabelProps={{ InputLabelProps={{
@ -117,7 +131,8 @@ const SalePricing = withStyles(styles, {
/> />
</CardContent> </CardContent>
</Card> </Card>
) );
}
); );
SalePricing.displayName = "SalePricing"; SalePricing.displayName = "SalePricing";
export default 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 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 CardSpacer from "@saleor/components/CardSpacer"; import CardSpacer from "@saleor/components/CardSpacer";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
@ -11,7 +12,7 @@ import Hr from "@saleor/components/Hr";
import Money from "@saleor/components/Money"; import Money from "@saleor/components/Money";
import Percent from "@saleor/components/Percent"; import Percent from "@saleor/components/Percent";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import i18n from "../../../i18n"; import { commonMessages } from "@saleor/intl";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { SaleType } from "../../../types/globalTypes"; import { SaleType } from "../../../types/globalTypes";
import { SaleDetails_sale } from "../../types/SaleDetails"; import { SaleDetails_sale } from "../../types/SaleDetails";
@ -24,17 +25,24 @@ export interface SaleSummaryProps {
const SaleSummary: React.StatelessComponent<SaleSummaryProps> = ({ const SaleSummary: React.StatelessComponent<SaleSummaryProps> = ({
defaultCurrency, defaultCurrency,
sale sale
}) => ( }) => {
const intl = useIntl();
return (
<Card> <Card>
<CardTitle title={i18n.t("Summary")} /> <CardTitle title={intl.formatMessage(commonMessages.summary)} />
<CardContent> <CardContent>
<Typography variant="caption">{i18n.t("Name")}</Typography> <Typography variant="caption">
<FormattedMessage defaultMessage="Name" description="sale name" />
</Typography>
<Typography> <Typography>
{maybe<React.ReactNode>(() => sale.name, <Skeleton />)} {maybe<React.ReactNode>(() => sale.name, <Skeleton />)}
</Typography> </Typography>
<FormSpacer /> <FormSpacer />
<Typography variant="caption">{i18n.t("Value")}</Typography> <Typography variant="caption">
<FormattedMessage defaultMessage="Value" description="sale value" />
</Typography>
<Typography> <Typography>
{maybe<React.ReactNode>( {maybe<React.ReactNode>(
() => () =>
@ -56,7 +64,9 @@ const SaleSummary: React.StatelessComponent<SaleSummaryProps> = ({
<Hr /> <Hr />
<CardSpacer /> <CardSpacer />
<Typography variant="caption">{i18n.t("Start Date")}</Typography> <Typography variant="caption">
<FormattedMessage {...commonMessages.startDate} />
</Typography>
<Typography> <Typography>
{maybe<React.ReactNode>( {maybe<React.ReactNode>(
() => ( () => (
@ -67,7 +77,9 @@ const SaleSummary: React.StatelessComponent<SaleSummaryProps> = ({
</Typography> </Typography>
<FormSpacer /> <FormSpacer />
<Typography variant="caption">{i18n.t("End Date")}</Typography> <Typography variant="caption">
<FormattedMessage {...commonMessages.endDate} />
</Typography>
<Typography> <Typography>
{maybe<React.ReactNode>( {maybe<React.ReactNode>(
() => () =>
@ -77,6 +89,7 @@ const SaleSummary: React.StatelessComponent<SaleSummaryProps> = ({
</Typography> </Typography>
</CardContent> </CardContent>
</Card> </Card>
); );
};
SaleSummary.displayName = "SaleSummary"; SaleSummary.displayName = "SaleSummary";
export default SaleSummary; export default SaleSummary;

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

@ -1,10 +1,11 @@
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
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 { VoucherList_vouchers_edges_node } from "../../types/VoucherList"; import { VoucherList_vouchers_edges_node } from "../../types/VoucherList";
import VoucherList from "../VoucherList"; import VoucherList from "../VoucherList";
@ -30,11 +31,14 @@ const VoucherListPage: React.StatelessComponent<VoucherListPageProps> = ({
toggle, toggle,
toggleAll, toggleAll,
toolbar toolbar
}) => ( }) => {
const intl = useIntl();
return (
<Container> <Container>
<PageHeader title={i18n.t("Vouchers")}> <PageHeader title={intl.formatMessage(sectionNames.vouchers)}>
<Button onClick={onAdd} variant="contained" color="primary"> <Button onClick={onAdd} variant="contained" color="primary">
{i18n.t("Add voucher")} <FormattedMessage defaultMessage="Add voucher" description="button" />
<AddIcon /> <AddIcon />
</Button> </Button>
</PageHeader> </PageHeader>
@ -55,6 +59,7 @@ const VoucherListPage: React.StatelessComponent<VoucherListPageProps> = ({
toolbar={toolbar} toolbar={toolbar}
/> />
</Container> </Container>
); );
};
VoucherListPage.displayName = "VoucherListPage"; VoucherListPage.displayName = "VoucherListPage";
export default 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 CardContent from "@material-ui/core/CardContent";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import React from "react"; import React from "react";
import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import { FormSpacer } from "@saleor/components/FormSpacer"; import { FormSpacer } from "@saleor/components/FormSpacer";
import RadioGroupField from "@saleor/components/RadioGroupField"; import RadioGroupField from "@saleor/components/RadioGroupField";
import { RequirementsPicker } from "@saleor/discounts/types"; import { RequirementsPicker } from "@saleor/discounts/types";
import i18n from "@saleor/i18n";
import { FormErrors } from "@saleor/types"; import { FormErrors } from "@saleor/types";
import { FormData } from "../VoucherDetailsPage"; import { FormData } from "../VoucherDetailsPage";
@ -25,24 +25,43 @@ const VoucherRequirements = ({
errors, errors,
onChange onChange
}: VoucherRequirementsProps) => { }: 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 = [ const requirementsPickerChoices = [
{ {
label: i18n.t("None"), label: intl.formatMessage({
defaultMessage: "None",
description: "voucher has no requirements"
}),
value: RequirementsPicker.NONE value: RequirementsPicker.NONE
}, },
{ {
label: i18n.t("Minimal order value"), label: minimalOrderValueText,
value: RequirementsPicker.ORDER value: RequirementsPicker.ORDER
}, },
{ {
label: i18n.t("Minimum quantity of items"), label: minimalQuantityText,
value: RequirementsPicker.ITEM value: RequirementsPicker.ITEM
} }
]; ];
return ( return (
<Card> <Card>
<CardTitle title={i18n.t("Minimum Requirements")} /> <CardTitle
title={intl.formatMessage({
defaultMessage: "Minimum Requirements",
description: "voucher requirements, header"
})}
/>
<CardContent> <CardContent>
<RadioGroupField <RadioGroupField
choices={requirementsPickerChoices} choices={requirementsPickerChoices}
@ -57,7 +76,7 @@ const VoucherRequirements = ({
disabled={disabled} disabled={disabled}
error={!!errors.minAmountSpent} error={!!errors.minAmountSpent}
helperText={errors.minAmountSpent} helperText={errors.minAmountSpent}
label={i18n.t("Minimal order value")} label={minimalOrderValueText}
name={"minAmountSpent" as keyof FormData} name={"minAmountSpent" as keyof FormData}
value={data.minAmountSpent} value={data.minAmountSpent}
onChange={onChange} onChange={onChange}
@ -68,7 +87,7 @@ const VoucherRequirements = ({
disabled={disabled} disabled={disabled}
error={!!errors.minCheckoutItemsQuantity} error={!!errors.minCheckoutItemsQuantity}
helperText={errors.minCheckoutItemsQuantity} helperText={errors.minCheckoutItemsQuantity}
label={i18n.t("Minimum quantity of items")} label={minimalQuantityText}
name={"minCheckoutItemsQuantity" as keyof FormData} name={"minCheckoutItemsQuantity" as keyof FormData}
value={data.minCheckoutItemsQuantity} value={data.minCheckoutItemsQuantity}
onChange={onChange} onChange={onChange}

View file

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

View file

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

View file

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

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 { saleDetailsPageTab } from "./components/SaleDetailsPage"; import { saleDetailsPageTab } from "./components/SaleDetailsPage";
import { voucherDetailsPageTab } from "./components/VoucherDetailsPage"; import { voucherDetailsPageTab } from "./components/VoucherDetailsPage";
import { import {
@ -73,9 +74,12 @@ const VoucherDetailsView: React.StatelessComponent<
); );
}; };
export const DiscountSection: React.StatelessComponent<{}> = () => ( export const DiscountSection: React.StatelessComponent<{}> = () => {
const intl = useIntl();
return (
<> <>
<WindowTitle title={i18n.t("Discounts")} /> <WindowTitle title={intl.formatMessage(sectionNames.vouchers)} />
<Switch> <Switch>
<Route exact path={saleListPath} component={SaleListView} /> <Route exact path={saleListPath} component={SaleListView} />
<Route exact path={saleAddPath} component={SaleCreateView} /> <Route exact path={saleAddPath} component={SaleCreateView} />
@ -85,5 +89,6 @@ export const DiscountSection: React.StatelessComponent<{}> = () => (
<Route path={voucherPath(":id")} component={VoucherDetailsView} /> <Route path={voucherPath(":id")} component={VoucherDetailsView} />
</Switch> </Switch>
</> </>
); );
};
export default DiscountSection; export default DiscountSection;

View file

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

View file

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

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

View file

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

View file

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

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

View file

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