Use react-intl

This commit is contained in:
Dominik Żegleń 2019-08-16 21:32:42 +02:00 committed by dominik-zeglen
parent fe57528820
commit 77da4694ac
13 changed files with 655 additions and 486 deletions

View file

@ -1,10 +1,6 @@
import { import { Theme } from "@material-ui/core/styles";
createStyles,
Theme,
withStyles,
WithStyles
} from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import makeStyles from "@material-ui/styles/makeStyles";
import React from "react"; import React from "react";
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
@ -15,12 +11,12 @@ import Hr from "@saleor/components/Hr";
import ImageTile from "@saleor/components/ImageTile"; import ImageTile from "@saleor/components/ImageTile";
import ImageUpload from "@saleor/components/ImageUpload"; import ImageUpload from "@saleor/components/ImageUpload";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import i18n from "../../../i18n"; import { commonMessages } from "@saleor/intl";
import { FormattedMessage, useIntl } from "react-intl";
import { CategoryDetails_category_backgroundImage } from "../../types/CategoryDetails"; import { CategoryDetails_category_backgroundImage } from "../../types/CategoryDetails";
import { FormData } from "../CategoryUpdatePage"; import { FormData } from "../CategoryUpdatePage";
const styles = (theme: Theme) => const useStyles = makeStyles((theme: Theme) => ({
createStyles({
fileField: { fileField: {
display: "none" display: "none"
}, },
@ -41,9 +37,9 @@ const styles = (theme: Theme) =>
position: "relative", position: "relative",
width: 148 width: 148
} }
}); }));
export interface CategoryBackgroundProps extends WithStyles<typeof styles> { export interface CategoryBackgroundProps {
data: FormData; data: FormData;
image: CategoryDetails_category_backgroundImage; image: CategoryDetails_category_backgroundImage;
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
@ -51,43 +47,38 @@ export interface CategoryBackgroundProps extends WithStyles<typeof styles> {
onImageUpload: (file: File) => void; onImageUpload: (file: File) => void;
} }
export const CategoryBackground = withStyles(styles)( const CategoryBackground: React.FC<CategoryBackgroundProps> = props => {
class CategoryBackgroundComponent extends React.Component< const classes = useStyles(props);
CategoryBackgroundProps, const intl = useIntl();
{} const anchor = React.useRef<HTMLInputElement>();
> {
imgInputAnchor = React.createRef<HTMLInputElement>();
clickImgInput = () => this.imgInputAnchor.current.click(); const { data, onImageUpload, image, onChange, onImageDelete } = props;
const handleImageUploadButtonClick = () => anchor.current.click();
render() {
const {
classes,
data,
onImageUpload,
image,
onChange,
onImageDelete
} = this.props;
return ( return (
<Card> <Card>
<CardTitle <CardTitle
title={i18n.t("Background image (optional)")} title={intl.formatMessage({
defaultMessage: "Background image (optional)",
description: "section header",
id: "categoryBackgroundHeader"
})}
toolbar={ toolbar={
<> <>
<Button <Button
variant="text" variant="text"
color="primary" color="primary"
onClick={this.clickImgInput} onClick={handleImageUploadButtonClick}
> >
{i18n.t("Upload image")} <FormattedMessage {...commonMessages.uploadImage} />
</Button> </Button>
<input <input
className={classes.fileField} className={classes.fileField}
id="fileUpload" id="fileUpload"
onChange={event => onImageUpload(event.target.files[0])} onChange={event => onImageUpload(event.target.files[0])}
type="file" type="file"
ref={this.imgInputAnchor} ref={anchor}
/> />
</> </>
} }
@ -114,8 +105,8 @@ export const CategoryBackground = withStyles(styles)(
<CardContent> <CardContent>
<TextField <TextField
name="backgroundImageAlt" name="backgroundImageAlt"
label={i18n.t("Description")} label={intl.formatMessage(commonMessages.description)}
helperText={i18n.t("Optional")} helperText={intl.formatMessage(commonMessages.optionalField)}
value={data.backgroundImageAlt} value={data.backgroundImageAlt}
onChange={onChange} onChange={onChange}
fullWidth fullWidth
@ -126,8 +117,6 @@ export const CategoryBackground = withStyles(styles)(
)} )}
</Card> </Card>
); );
} };
}
);
CategoryBackground.displayName = "CategoryBackground"; CategoryBackground.displayName = "CategoryBackground";
export default CategoryBackground; export default CategoryBackground;

View file

@ -1,5 +1,6 @@
import { ContentState, convertToRaw, RawDraftContentState } from "draft-js"; import { ContentState, convertToRaw, RawDraftContentState } from "draft-js";
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 Form from "@saleor/components/Form";
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 SeoForm from "@saleor/components/SeoForm"; import SeoForm from "@saleor/components/SeoForm";
import i18n from "../../../i18n"; import { commonMessages, sectionNames } from "@saleor/intl";
import { UserError } from "../../../types"; import { UserError } from "../../../types";
import CategoryDetailsForm from "../../components/CategoryDetailsForm"; import CategoryDetailsForm from "../../components/CategoryDetailsForm";
@ -43,7 +44,9 @@ export const CategoryCreatePage: React.StatelessComponent<
onBack, onBack,
errors: userErrors, errors: userErrors,
saveButtonBarState saveButtonBarState
}) => ( }) => {
const intl = useIntl();
return (
<Form <Form
onSubmit={onSubmit} onSubmit={onSubmit}
initial={initialData} initial={initialData}
@ -52,8 +55,16 @@ export const CategoryCreatePage: React.StatelessComponent<
> >
{({ data, change, errors, submit, hasChanged }) => ( {({ data, change, errors, submit, hasChanged }) => (
<Container> <Container>
<AppHeader onBack={onBack}>{i18n.t("Categories")}</AppHeader> <AppHeader onBack={onBack}>
<PageHeader title={i18n.t("Add Category")} /> {intl.formatMessage(sectionNames.categories)}
</AppHeader>
<PageHeader
title={intl.formatMessage({
defaultMessage: "Create New Category",
description: "page header",
id: "categoryCreatePageHeader"
})}
/>
<div> <div>
<CategoryDetailsForm <CategoryDetailsForm
disabled={disabled} disabled={disabled}
@ -63,9 +74,11 @@ export const CategoryCreatePage: React.StatelessComponent<
/> />
<CardSpacer /> <CardSpacer />
<SeoForm <SeoForm
helperText={i18n.t( helperText={intl.formatMessage({
"Add search engine title and description to make this product easier to find" defaultMessage:
)} "Add search engine title and description to make this category easier to find",
id: "categoryCreatePageSeo"
})}
title={data.seoTitle} title={data.seoTitle}
titlePlaceholder={data.name} titlePlaceholder={data.name}
description={data.seoDescription} description={data.seoDescription}
@ -77,9 +90,6 @@ export const CategoryCreatePage: React.StatelessComponent<
<SaveButtonBar <SaveButtonBar
onCancel={onBack} onCancel={onBack}
onSave={submit} onSave={submit}
labels={{
save: i18n.t("Save category")
}}
state={saveButtonBarState} state={saveButtonBarState}
disabled={disabled || !hasChanged} disabled={disabled || !hasChanged}
/> />
@ -88,5 +98,6 @@ export const CategoryCreatePage: React.StatelessComponent<
)} )}
</Form> </Form>
); );
};
CategoryCreatePage.displayName = "CategoryCreatePage"; CategoryCreatePage.displayName = "CategoryCreatePage";
export default CategoryCreatePage; export default CategoryCreatePage;

View file

@ -11,8 +11,9 @@ import {
WithStyles WithStyles
} from "@material-ui/core/styles"; } from "@material-ui/core/styles";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl";
import i18n from "../../../i18n"; import { commonMessages } from "@saleor/intl";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
@ -36,27 +37,35 @@ const CategoryDeleteDialog = withStyles(styles, {
name: "CategoryDeleteDialog" name: "CategoryDeleteDialog"
})(({ classes, name, open, onConfirm, onClose }: CategoryDeleteDialogProps) => ( })(({ classes, name, open, onConfirm, onClose }: CategoryDeleteDialogProps) => (
<Dialog onClose={onClose} open={open}> <Dialog onClose={onClose} open={open}>
<DialogTitle>{i18n.t("Delete category", { context: "title" })}</DialogTitle> <DialogTitle>
<FormattedMessage
defaultMessage="Delete category"
description="dialog title"
id="categoryDeleteDialogTitle"
/>
</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText <DialogContentText>
dangerouslySetInnerHTML={{ <FormattedMessage
__html: i18n.t( defaultMessage="Are you sure you want to remove {name}?"
"Are you sure you want to remove <strong>{{name}}</strong>?", description="delete category"
{ name } id="<categoryDeleteDialogContent<"
) values={{
name: <strong>{name}</strong>
}} }}
/> />
</DialogContentText>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={onClose}> <Button onClick={onClose}>
{i18n.t("Cancel", { context: "button" })} <FormattedMessage {...commonMessages.cancel} />
</Button> </Button>
<Button <Button
className={classes.deleteButton} className={classes.deleteButton}
variant="contained" variant="contained"
onClick={onConfirm} onClick={onConfirm}
> >
{i18n.t("Delete category", { context: "button" })} <FormattedMessage {...commonMessages.save} />
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>

View file

@ -4,11 +4,12 @@ import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import { RawDraftContentState } from "draft-js"; import { RawDraftContentState } from "draft-js";
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 RichTextEditor from "@saleor/components/RichTextEditor"; import RichTextEditor from "@saleor/components/RichTextEditor";
import i18n from "../../../i18n"; import { commonMessages } from "@saleor/intl";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { CategoryDetails_category } from "../../types/CategoryDetails"; import { CategoryDetails_category } from "../../types/CategoryDetails";
@ -40,15 +41,23 @@ export const CategoryDetailsForm = withStyles(styles, {
onChange, onChange,
errors errors
}: CategoryDetailsFormProps) => { }: CategoryDetailsFormProps) => {
const intl = useIntl();
return ( return (
<Card> <Card>
<CardTitle title={i18n.t("General information")} /> <CardTitle
title={intl.formatMessage(commonMessages.generalInformations)}
/>
<CardContent> <CardContent>
<> <>
<div> <div>
<TextField <TextField
classes={{ root: classes.root }} classes={{ root: classes.root }}
label={i18n.t("Name")} label={intl.formatMessage({
defaultMessage: "Name",
description: "category name",
id: "categoryDetailsFormNameInputLabel"
})}
name="name" name="name"
disabled={disabled} disabled={disabled}
value={data && data.name} value={data && data.name}
@ -62,7 +71,7 @@ export const CategoryDetailsForm = withStyles(styles, {
disabled={disabled} disabled={disabled}
error={!!errors.descriptionJson} error={!!errors.descriptionJson}
helperText={errors.descriptionJson} helperText={errors.descriptionJson}
label={i18n.t("Description")} label={intl.formatMessage(commonMessages.description)}
initial={maybe(() => JSON.parse(category.descriptionJson))} initial={maybe(() => JSON.parse(category.descriptionJson))}
name="description" name="description"
onChange={onChange} onChange={onChange}

View file

@ -12,13 +12,13 @@ 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, 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 "@saleor/i18n";
import { renderCollection } from "@saleor/misc"; import { renderCollection } from "@saleor/misc";
import { ListActions, ListProps } from "@saleor/types"; import { ListActions, ListProps } from "@saleor/types";
@ -87,14 +87,25 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })(
onPreviousPage, onPreviousPage,
onUpdateListSettings, onUpdateListSettings,
onRowClick onRowClick
}: CategoryListProps) => ( }: CategoryListProps) => {
const intl = useIntl();
return (
<Card> <Card>
{!isRoot && ( {!isRoot && (
<CardTitle <CardTitle
title={i18n.t("All Subcategories")} title={intl.formatMessage({
defaultMessage: "All Subcategories",
description: "section header",
id: "categoryListSubcategoriesSectionHeader"
})}
toolbar={ toolbar={
<Button color="primary" variant="text" onClick={onAdd}> <Button color="primary" variant="text" onClick={onAdd}>
{i18n.t("Add subcategory")} <FormattedMessage
defaultMessage="Add subcategory"
description="button"
id="categoryListAddSubcategoryButton"
/>
</Button> </Button>
} }
/> />
@ -109,13 +120,25 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })(
toolbar={toolbar} toolbar={toolbar}
> >
<TableCell className={classes.colName}> <TableCell className={classes.colName}>
{i18n.t("Category Name", { context: "object" })} <FormattedMessage
defaultMessage="Category Name"
description="category list: name column header"
id="categoryListNameColumnHeader"
/>
</TableCell> </TableCell>
<TableCell className={classes.colSubcategories}> <TableCell className={classes.colSubcategories}>
{i18n.t("Subcategories", { context: "object" })} <FormattedMessage
defaultMessage="Subcategories"
description="category list: subcategories column header"
id="categoryListSubcategoriesColumnHeader"
/>
</TableCell> </TableCell>
<TableCell className={classes.colProducts}> <TableCell className={classes.colProducts}>
{i18n.t("No. Products", { context: "object" }).replace(" ", "\xa0")} <FormattedMessage
defaultMessage="No. Products"
description="category list: number of products column header"
id="categoryListNumberOfProductsColumnHeader"
/>
</TableCell> </TableCell>
</TableHead> </TableHead>
<TableFooter> <TableFooter>
@ -123,7 +146,9 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })(
<TablePagination <TablePagination
colSpan={numberOfColumns} colSpan={numberOfColumns}
settings={settings} settings={settings}
hasNextPage={pageInfo && !disabled ? pageInfo.hasNextPage : false} hasNextPage={
pageInfo && !disabled ? pageInfo.hasNextPage : false
}
onNextPage={onNextPage} onNextPage={onNextPage}
onUpdateListSettings={onUpdateListSettings} onUpdateListSettings={onUpdateListSettings}
hasPreviousPage={ hasPreviousPage={
@ -182,9 +207,17 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })(
() => ( () => (
<TableRow> <TableRow>
<TableCell colSpan={numberOfColumns}> <TableCell colSpan={numberOfColumns}>
{isRoot {isRoot ? (
? i18n.t("No categories found") <FormattedMessage
: i18n.t("No subcategories found")} defaultMessage="No categories found"
id="categoryListNoCategories"
/>
) : (
<FormattedMessage
defaultMessage="No subcategories found"
id="categoryListNoSubcategories"
/>
)}
</TableCell> </TableCell>
</TableRow> </TableRow>
) )
@ -192,7 +225,8 @@ const CategoryList = withStyles(styles, { name: "CategoryList" })(
</TableBody> </TableBody>
</Table> </Table>
</Card> </Card>
) );
}
); );
CategoryList.displayName = "CategoryList"; CategoryList.displayName = "CategoryList";
export default CategoryList; export default CategoryList;

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 CategoryList from "../CategoryList"; import CategoryList from "../CategoryList";
@ -36,11 +37,18 @@ export const CategoryListPage: React.StatelessComponent<CategoryTableProps> = ({
toggle, toggle,
toggleAll, toggleAll,
toolbar toolbar
}) => ( }) => {
const intl = useIntl();
return (
<Container> <Container>
<PageHeader title={i18n.t("Categories")}> <PageHeader title={intl.formatMessage(sectionNames.categories)}>
<Button color="primary" variant="contained" onClick={onAdd}> <Button color="primary" variant="contained" onClick={onAdd}>
{i18n.t("Add category")} <AddIcon /> <FormattedMessage
defaultMessage="Add category"
description="button"
id="categoryListPageAddCategoryButton"
/>
<AddIcon />
</Button> </Button>
</PageHeader> </PageHeader>
<CategoryList <CategoryList
@ -62,5 +70,6 @@ export const CategoryListPage: React.StatelessComponent<CategoryTableProps> = ({
/> />
</Container> </Container>
); );
};
CategoryListPage.displayName = "CategoryListPage"; CategoryListPage.displayName = "CategoryListPage";
export default CategoryListPage; export default CategoryListPage;

View file

@ -13,13 +13,13 @@ import TableFooter from "@material-ui/core/TableFooter";
import TableHead from "@material-ui/core/TableHead"; import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow"; import TableRow from "@material-ui/core/TableRow";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import TableCellAvatar from "@saleor/components/TableCellAvatar"; import TableCellAvatar from "@saleor/components/TableCellAvatar";
import TablePagination from "@saleor/components/TablePagination"; import TablePagination from "@saleor/components/TablePagination";
import i18n from "../../../i18n"; import { maybe, renderCollection } from "@saleor/misc";
import { maybe, renderCollection } from "../../../misc";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
@ -61,13 +61,24 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
onNextPage, onNextPage,
onPreviousPage, onPreviousPage,
onRowClick onRowClick
}: ProductListProps) => ( }: ProductListProps) => {
const intl = useIntl();
return (
<Card> <Card>
<CardTitle <CardTitle
title={i18n.t("Products")} title={intl.formatMessage({
defaultMessage: "Products",
description: "section header",
id: "categoryProductsHeader"
})}
toolbar={ toolbar={
<Button variant="text" color="primary" onClick={onAddProduct}> <Button variant="text" color="primary" onClick={onAddProduct}>
{i18n.t("Add product")} <FormattedMessage
defaultMessage="Add product"
description="button"
id="categoryProductsAddProductButton"
/>
</Button> </Button>
} }
/> />
@ -76,9 +87,19 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
<TableRow> <TableRow>
{(products === undefined || products.length > 0) && <TableCell />} {(products === undefined || products.length > 0) && <TableCell />}
<TableCell className={classes.textLeft}> <TableCell className={classes.textLeft}>
{i18n.t("Name", { context: "object" })} <FormattedMessage
defaultMessage="Name"
description="product list: product name column header"
id="categoryProductsNameHeader"
/>
</TableCell>
<TableCell>
<FormattedMessage
defaultMessage="Type"
description="product list: product type column header"
id="categoryProductsTypeHeader"
/>
</TableCell> </TableCell>
<TableCell>{i18n.t("Type", { context: "object" })}</TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableFooter> <TableFooter>
@ -123,14 +144,20 @@ export const ProductList = withStyles(styles, { name: "ProductList" })(
), ),
() => ( () => (
<TableRow> <TableRow>
<TableCell colSpan={3}>{i18n.t("No products found")}</TableCell> <TableCell colSpan={3}>
<FormattedMessage
defaultMessage="No products found"
id="categoryProductsNoProducts"
/>
</TableCell>
</TableRow> </TableRow>
) )
)} )}
</TableBody> </TableBody>
</Table> </Table>
</Card> </Card>
) );
}
); );
ProductList.displayName = "CategoryProductList"; ProductList.displayName = "CategoryProductList";
export default ProductList; export default ProductList;

View file

@ -1,10 +1,10 @@
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card"; import Card from "@material-ui/core/Card";
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 ProductList from "@saleor/components/ProductList"; import ProductList from "@saleor/components/ProductList";
import i18n from "../../../i18n";
import { ListActions, PageListProps } from "../../../types"; import { ListActions, PageListProps } from "../../../types";
import { CategoryDetails_category_products_edges_node } from "../../types/CategoryDetails"; import { CategoryDetails_category_products_edges_node } from "../../types/CategoryDetails";
@ -29,13 +29,28 @@ export const CategoryProductsCard: React.StatelessComponent<
toggle, toggle,
toggleAll, toggleAll,
toolbar toolbar
}) => ( }) => {
const intl = useIntl();
return (
<Card> <Card>
<CardTitle <CardTitle
title={i18n.t("Products in {{ categoryName }}", { categoryName })} title={intl.formatMessage(
{
defaultMessage: "Products in {name}",
description: "products in category section header",
id: "categoryProductsCardHeader"
},
{
name: categoryName
}
)}
toolbar={ toolbar={
<Button color="primary" variant="text" onClick={onAdd}> <Button color="primary" variant="text" onClick={onAdd}>
{i18n.t("Add product")} <FormattedMessage
defaultMessage="Add product"
description="button"
id="categoryProductsCardAddProductButton"
/>
</Button> </Button>
} }
/> />
@ -58,6 +73,7 @@ export const CategoryProductsCard: React.StatelessComponent<
/> />
</Card> </Card>
); );
};
CategoryProductsCard.displayName = "CategoryProductsCard"; CategoryProductsCard.displayName = "CategoryProductsCard";
export default CategoryProductsCard; export default CategoryProductsCard;

View file

@ -1,5 +1,6 @@
import { RawDraftContentState } from "draft-js"; import { RawDraftContentState } from "draft-js";
import React from "react"; import React from "react";
import { useIntl, FormattedMessage } 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";
@ -10,7 +11,6 @@ import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import SeoForm from "@saleor/components/SeoForm"; import SeoForm from "@saleor/components/SeoForm";
import { Tab, TabContainer } from "@saleor/components/Tab"; import { Tab, TabContainer } from "@saleor/components/Tab";
import i18n from "../../../i18n";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { TabListActions, UserError } from "../../../types"; import { TabListActions, UserError } from "../../../types";
import CategoryDetailsForm from "../../components/CategoryDetailsForm"; import CategoryDetailsForm from "../../components/CategoryDetailsForm";
@ -96,6 +96,7 @@ export const CategoryUpdatePage: React.StatelessComponent<
toggle, toggle,
toggleAll toggleAll
}: CategoryUpdatePageProps) => { }: CategoryUpdatePageProps) => {
const intl = useIntl();
const initialData: FormData = category const initialData: FormData = category
? { ? {
backgroundImageAlt: maybe(() => category.backgroundImage.alt, ""), backgroundImageAlt: maybe(() => category.backgroundImage.alt, ""),
@ -139,9 +140,11 @@ export const CategoryUpdatePage: React.StatelessComponent<
/> />
<CardSpacer /> <CardSpacer />
<SeoForm <SeoForm
helperText={i18n.t( helperText={intl.formatMessage({
"Add search engine title and description to make this category easier to find" defaultMessage:
)} "Add search engine title and description to make this category easier to find",
id: "categoryUpdatePageSeo"
})}
title={data.seoTitle} title={data.seoTitle}
titlePlaceholder={data.name} titlePlaceholder={data.name}
description={data.seoDescription} description={data.seoDescription}
@ -156,13 +159,21 @@ export const CategoryUpdatePage: React.StatelessComponent<
isActive={currentTab === CategoryPageTab.categories} isActive={currentTab === CategoryPageTab.categories}
changeTab={changeTab} changeTab={changeTab}
> >
{i18n.t("Subcategories")} <FormattedMessage
defaultMessage="Subcategories"
description="category list: number of subcategories column header"
id="categoryUpdatePageSubcategoriesColumnHeader"
/>
</CategoriesTab> </CategoriesTab>
<ProductsTab <ProductsTab
isActive={currentTab === CategoryPageTab.products} isActive={currentTab === CategoryPageTab.products}
changeTab={changeTab} changeTab={changeTab}
> >
{i18n.t("Products")} <FormattedMessage
defaultMessage="Products"
description="category list: number of products column header"
id="categoryUpdatePageProductsColumnHeader"
/>
</ProductsTab> </ProductsTab>
</TabContainer> </TabContainer>
<CardSpacer /> <CardSpacer />
@ -204,9 +215,6 @@ export const CategoryUpdatePage: React.StatelessComponent<
onCancel={onBack} onCancel={onBack}
onDelete={onDelete} onDelete={onDelete}
onSave={submit} onSave={submit}
labels={{
delete: i18n.t("Delete category")
}}
state={saveButtonBarState} state={saveButtonBarState}
disabled={disabled || !hasChanged} disabled={disabled || !hasChanged}
/> />

View file

@ -1,9 +1,9 @@
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 i18n from "../../i18n";
import { getMutationState, maybe } from "../../misc"; import { getMutationState, maybe } from "../../misc";
import CategoryCreatePage from "../components/CategoryCreatePage"; import CategoryCreatePage from "../components/CategoryCreatePage";
import { TypedCategoryCreateMutation } from "../mutations"; import { TypedCategoryCreateMutation } from "../mutations";
@ -19,10 +19,16 @@ export const CategoryCreateView: React.StatelessComponent<
> = ({ parentId }) => { > = ({ parentId }) => {
const navigate = useNavigator(); const navigate = useNavigator();
const notify = useNotifier(); const notify = useNotifier();
const intl = useIntl();
const handleSuccess = (data: CategoryCreate) => { const handleSuccess = (data: CategoryCreate) => {
if (data.categoryCreate.errors.length === 0) { if (data.categoryCreate.errors.length === 0) {
notify({ text: i18n.t("Category created") }); notify({
text: intl.formatMessage({
defaultMessage: "Category created",
id: "categoryCreateCategoryCreated"
})
});
navigate(categoryUrl(data.categoryCreate.category.id)); navigate(categoryUrl(data.categoryCreate.category.id));
} }
}; };
@ -42,7 +48,13 @@ export const CategoryCreateView: React.StatelessComponent<
return ( return (
<> <>
<WindowTitle title={i18n.t("Create category")} /> <WindowTitle
title={intl.formatMessage({
defaultMessage: "Create category",
description: "window title",
id: "categoryCreateWindowTitle"
})}
/>
<CategoryCreatePage <CategoryCreatePage
saveButtonBarState={formTransitionState} saveButtonBarState={formTransitionState}
errors={errors} errors={errors}

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";
@ -12,7 +13,6 @@ import usePaginator, {
createPaginationState createPaginationState
} from "@saleor/hooks/usePaginator"; } from "@saleor/hooks/usePaginator";
import { PAGINATE_BY } from "../../config"; import { PAGINATE_BY } from "../../config";
import i18n from "../../i18n";
import { getMutationState, maybe } from "../../misc"; import { getMutationState, maybe } from "../../misc";
import { TypedProductBulkDeleteMutation } from "../../products/mutations"; import { TypedProductBulkDeleteMutation } from "../../products/mutations";
import { productBulkDelete } from "../../products/types/productBulkDelete"; import { productBulkDelete } from "../../products/types/productBulkDelete";
@ -59,12 +59,14 @@ export const CategoryDetails: React.StatelessComponent<
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
params.ids params.ids
); );
const intl = useIntl();
const handleCategoryDelete = (data: CategoryDelete) => { const handleCategoryDelete = (data: CategoryDelete) => {
if (data.categoryDelete.errors.length === 0) { if (data.categoryDelete.errors.length === 0) {
notify({ notify({
text: i18n.t("Category deleted", { text: intl.formatMessage({
context: "notification" defaultMessage: "Category deleted",
id: "categoryDetailsCategoryDeleted"
}) })
}); });
navigate(categoryListUrl()); navigate(categoryListUrl());
@ -140,7 +142,10 @@ export const CategoryDetails: React.StatelessComponent<
if (data.categoryBulkDelete.errors.length === 0) { if (data.categoryBulkDelete.errors.length === 0) {
closeModal(); closeModal();
notify({ notify({
text: i18n.t("Categories removed") text: intl.formatMessage({
defaultMessage: "Categories removed",
id: "categoryDetailsCategoriesRemoved"
})
}); });
refetch(); refetch();
reset(); reset();
@ -151,7 +156,10 @@ export const CategoryDetails: React.StatelessComponent<
if (data.productBulkDelete.errors.length === 0) { if (data.productBulkDelete.errors.length === 0) {
closeModal(); closeModal();
notify({ notify({
text: i18n.t("Products removed") text: intl.formatMessage({
defaultMessage: "Products removed",
id: "categoryDetailsProductsRemoved"
})
}); });
refetch(); refetch();
reset(); reset();
@ -319,32 +327,43 @@ export const CategoryDetails: React.StatelessComponent<
deleteCategory({ variables: { id } }) deleteCategory({ variables: { id } })
} }
open={params.action === "delete"} open={params.action === "delete"}
title={i18n.t("Delete category", { title={intl.formatMessage({
context: "modal title" defaultMessage: "Delete category",
description: "dialog title",
id:
"categoryDetailsDeleteCategoryDialogTitle"
})} })}
variant="delete" variant="delete"
> >
<DialogContentText <DialogContentText>
dangerouslySetInnerHTML={{ <FormattedMessage
__html: i18n.t( defaultMessage="Are you sure you want to remove {name}?"
"Are you sure you want to remove <strong>{{ categoryName }}</strong>? <br /> ", description="remove category"
{ id="categoryDetailsDeleteCategoryDialogContent"
categoryName: maybe( values={{
() => data.category.name name: (
), <strong>
context: "modal message" {maybe(
} () => data.category.name,
"..."
)}
</strong>
) )
}} }}
/> />
</DialogContentText>
<DialogContentText> <DialogContentText>
{i18n.t( <FormattedMessage
"Remember that this will also remove all products assigned to this category." defaultMessage="Remember that this will also remove all products assigned to this category."
)} id="categoryDetailsDeleteCategoryDialogContentAdditionalText"
/>
</DialogContentText> </DialogContentText>
</ActionDialog> </ActionDialog>
<ActionDialog <ActionDialog
open={params.action === "delete-categories"} open={
params.action === "delete-categories" &&
maybe(() => params.ids.length > 0)
}
confirmButtonState={ confirmButtonState={
categoryBulkDeleteMutationState categoryBulkDeleteMutationState
} }
@ -354,27 +373,30 @@ export const CategoryDetails: React.StatelessComponent<
variables: { ids: params.ids } variables: { ids: params.ids }
}) })
} }
title={i18n.t("Remove categories")} title={intl.formatMessage({
defaultMessage: "Remove categories",
description: "dialog title",
id:
"categoryDetailsDeleteSubcategoriesDialogTitle"
})}
variant="delete" variant="delete"
> >
<DialogContentText <DialogContentText>
dangerouslySetInnerHTML={{ <FormattedMessage
__html: i18n.t( defaultMessage="Are you sure you want to remove {number} categories?"
"Are you sure you want to remove <strong>{{ number }}</strong> categories?", id="categoryDetailsDeleteCategoriesDialogContent"
{ values={{
number: maybe( number: (
() => <strong>{params.ids.length}</strong>
params.ids.length.toString(),
"..."
)
}
) )
}} }}
/> />
</DialogContentText>
<DialogContentText> <DialogContentText>
{i18n.t( <FormattedMessage
"Remember that this will also remove all products assigned to this category." defaultMessage="Remember that this will also remove all products assigned to this category."
)} id="categoryDetailsDeleteCategoriesDialogContentAdditionalText"
/>
</DialogContentText> </DialogContentText>
</ActionDialog> </ActionDialog>
<ActionDialog <ActionDialog
@ -388,23 +410,26 @@ export const CategoryDetails: React.StatelessComponent<
variables: { ids: params.ids } variables: { ids: params.ids }
}) })
} }
title={i18n.t("Remove products")} title={intl.formatMessage({
defaultMessage: "Remove products",
description: "dialog title",
id:
"categoryDetailsDeleteProductsDialogTitle"
})}
variant="delete" variant="delete"
> >
<DialogContentText {" "}
dangerouslySetInnerHTML={{ <DialogContentText>
__html: i18n.t( <FormattedMessage
"Are you sure you want to remove <strong>{{ number }}</strong> products?", defaultMessage="Are you sure you want to remove {number} products?"
{ id="categoryDetailsDeleteProductsDialogContent"
number: maybe( values={{
() => number: (
params.ids.length.toString(), <strong>{params.ids.length}</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 useBulkActions from "@saleor/hooks/useBulkActions"; import useBulkActions from "@saleor/hooks/useBulkActions";
@ -10,7 +11,6 @@ import useNavigator from "@saleor/hooks/useNavigator";
import usePaginator, { import usePaginator, {
createPaginationState createPaginationState
} from "@saleor/hooks/usePaginator"; } from "@saleor/hooks/usePaginator";
import i18n from "@saleor/i18n";
import { getMutationState, maybe } from "@saleor/misc"; import { getMutationState, maybe } from "@saleor/misc";
import { ListViews } from "@saleor/types"; import { ListViews } from "@saleor/types";
import { CategoryListPage } from "../components/CategoryListPage/CategoryListPage"; import { CategoryListPage } from "../components/CategoryListPage/CategoryListPage";
@ -39,6 +39,8 @@ export const CategoryList: React.StatelessComponent<CategoryListProps> = ({
const { updateListSettings, settings } = useListSettings( const { updateListSettings, settings } = useListSettings(
ListViews.CATEGORY_LIST ListViews.CATEGORY_LIST
); );
const intl = useIntl();
const paginationState = createPaginationState(settings.rowNumber, params); const paginationState = createPaginationState(settings.rowNumber, params);
return ( return (
<TypedRootCategoriesQuery displayLoader variables={paginationState}> <TypedRootCategoriesQuery displayLoader variables={paginationState}>
@ -124,26 +126,25 @@ export const CategoryList: React.StatelessComponent<CategoryListProps> = ({
}) })
} }
open={params.action === "delete"} open={params.action === "delete"}
title={i18n.t("Remove categories")} title={intl.formatMessage({
defaultMessage: "Remove categories",
description: "dialog title",
id: "categoryListDeleteSubcategoriesDialogTitle"
})}
variant="delete" variant="delete"
> >
<DialogContentText <FormattedMessage
dangerouslySetInnerHTML={{ defaultMessage="Are you sure you want to remove {number} categories?"
__html: i18n.t( id="categoryListDeleteCategoriesDialogContent"
"Are you sure you want to remove <strong>{{ number }}</strong> categories?", values={{
{ number: <strong>{params.ids.length}</strong>
number: maybe(
() => params.ids.length.toString(),
"..."
)
}
)
}} }}
/> />
<DialogContentText> <DialogContentText>
{i18n.t( <FormattedMessage
"Remember that this will also remove all products assigned to this category." defaultMessage="Remember that this will also remove all products assigned to this category."
)} id="categoryListDeleteCategoriesDialogContentAdditionalText"
/>
</DialogContentText> </DialogContentText>
</ActionDialog> </ActionDialog>
</> </>

View file

@ -9,10 +9,19 @@ export const commonMessages = defineMessages({
defaultMessage: "Confirm", defaultMessage: "Confirm",
id: "confirm" id: "confirm"
}, },
description: {
defaultMessage: "Description",
id: "description"
},
generalInformations: { generalInformations: {
defaultMessage: "General Informations", defaultMessage: "General Informations",
id: "generalInformations" id: "generalInformations"
}, },
optionalField: {
defaultMessage: "Optional",
description: "field is optional",
id: "optionalField"
},
properties: { properties: {
defaultMessage: "Properties", defaultMessage: "Properties",
id: "properties" id: "properties"
@ -24,6 +33,11 @@ export const commonMessages = defineMessages({
savedChanges: { savedChanges: {
defaultMessage: "Saved changes", defaultMessage: "Saved changes",
id: "savedChanges" id: "savedChanges"
},
uploadImage: {
defaultMessage: "Upload image",
description: "button",
id: "uploadImage"
} }
}); });
@ -32,5 +46,10 @@ export const sectionNames = defineMessages({
defaultMessage: "Attributes", defaultMessage: "Attributes",
description: "attributes section name", description: "attributes section name",
id: "attributes" id: "attributes"
},
categories: {
defaultMessage: "Categories",
description: "categories section name",
id: "categories"
} }
}); });