Refactor product section translations (#125)
This commit is contained in:
parent
c214cc298d
commit
63e4f9bd34
31 changed files with 2120 additions and 896 deletions
File diff suppressed because it is too large
Load diff
|
@ -133,6 +133,10 @@ export const sectionNames = defineMessages({
|
||||||
defaultMessage: "Product Types",
|
defaultMessage: "Product Types",
|
||||||
description: "product types section name"
|
description: "product types section name"
|
||||||
},
|
},
|
||||||
|
products: {
|
||||||
|
defaultMessage: "Products",
|
||||||
|
description: "products section name"
|
||||||
|
},
|
||||||
sales: {
|
sales: {
|
||||||
defaultMessage: "Sales",
|
defaultMessage: "Sales",
|
||||||
description: "sales section name"
|
description: "sales section name"
|
||||||
|
|
|
@ -7,6 +7,7 @@ import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown";
|
||||||
import makeStyles from "@material-ui/styles/makeStyles";
|
import makeStyles from "@material-ui/styles/makeStyles";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
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 Grid from "@saleor/components/Grid";
|
import Grid from "@saleor/components/Grid";
|
||||||
|
@ -18,7 +19,6 @@ import SingleAutocompleteSelectField, {
|
||||||
SingleAutocompleteChoiceType
|
SingleAutocompleteChoiceType
|
||||||
} from "@saleor/components/SingleAutocompleteSelectField";
|
} from "@saleor/components/SingleAutocompleteSelectField";
|
||||||
import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset";
|
import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset";
|
||||||
import i18n from "@saleor/i18n";
|
|
||||||
import { maybe } from "@saleor/misc";
|
import { maybe } from "@saleor/misc";
|
||||||
import { ProductDetails_product_attributes_attribute_values } from "@saleor/products/types/ProductDetails";
|
import { ProductDetails_product_attributes_attribute_values } from "@saleor/products/types/ProductDetails";
|
||||||
import { AttributeInputTypeEnum } from "@saleor/types/globalTypes";
|
import { AttributeInputTypeEnum } from "@saleor/types/globalTypes";
|
||||||
|
@ -127,21 +127,30 @@ const ProductAttributes: React.FC<ProductAttributesProps> = ({
|
||||||
onChange,
|
onChange,
|
||||||
onMultiChange
|
onMultiChange
|
||||||
}) => {
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
const classes = useStyles({});
|
const classes = useStyles({});
|
||||||
const [expanded, setExpansionStatus] = React.useState(true);
|
const [expanded, setExpansionStatus] = React.useState(true);
|
||||||
const toggleExpansion = () => setExpansionStatus(!expanded);
|
const toggleExpansion = () => setExpansionStatus(!expanded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className={classes.card}>
|
<Card className={classes.card}>
|
||||||
<CardTitle title={i18n.t("Attributes")} />
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Attributes",
|
||||||
|
description: "product attributes, section header"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<CardContent className={classes.cardContent}>
|
<CardContent className={classes.cardContent}>
|
||||||
<div className={classes.expansionBar}>
|
<div className={classes.expansionBar}>
|
||||||
<div className={classes.expansionBarLabelContainer}>
|
<div className={classes.expansionBarLabelContainer}>
|
||||||
<Typography className={classes.expansionBarLabel} variant="caption">
|
<Typography className={classes.expansionBarLabel} variant="caption">
|
||||||
{i18n.t("{{ number }} Attributes", {
|
<FormattedMessage
|
||||||
context: "number of attribute",
|
defaultMessage="{number} Attributes"
|
||||||
|
description="number of product attributes"
|
||||||
|
values={{
|
||||||
number: attributes.length
|
number: attributes.length
|
||||||
})}
|
}}
|
||||||
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
@ -184,7 +193,10 @@ const ProductAttributes: React.FC<ProductAttributesProps> = ({
|
||||||
)}
|
)}
|
||||||
emptyOption
|
emptyOption
|
||||||
name={`attribute:${attribute.label}`}
|
name={`attribute:${attribute.label}`}
|
||||||
label={i18n.t("Value")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Value",
|
||||||
|
description: "attribute value"
|
||||||
|
})}
|
||||||
value={attribute.value[0]}
|
value={attribute.value[0]}
|
||||||
onChange={event =>
|
onChange={event =>
|
||||||
onChange(attribute.id, event.target.value)
|
onChange(attribute.id, event.target.value)
|
||||||
|
@ -195,7 +207,10 @@ const ProductAttributes: React.FC<ProductAttributesProps> = ({
|
||||||
<MultiAutocompleteSelectField
|
<MultiAutocompleteSelectField
|
||||||
choices={getMultiChoices(attribute.data.values)}
|
choices={getMultiChoices(attribute.data.values)}
|
||||||
displayValues={getMultiDisplayValue(attribute)}
|
displayValues={getMultiDisplayValue(attribute)}
|
||||||
label={i18n.t("Values")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Values",
|
||||||
|
description: "attribute values"
|
||||||
|
})}
|
||||||
name={`attribute:${attribute.label}`}
|
name={`attribute:${attribute.label}`}
|
||||||
value={attribute.value}
|
value={attribute.value}
|
||||||
onChange={event =>
|
onChange={event =>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
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 FormSpacer from "@saleor/components/FormSpacer";
|
import FormSpacer from "@saleor/components/FormSpacer";
|
||||||
import MultiSelectField from "@saleor/components/MultiSelectField";
|
import MultiSelectField from "@saleor/components/MultiSelectField";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import SingleSelectField from "@saleor/components/SingleSelectField";
|
import SingleSelectField from "@saleor/components/SingleSelectField";
|
||||||
import i18n from "../../../i18n";
|
|
||||||
|
|
||||||
interface ProductCategoryAndCollectionsFormProps {
|
interface ProductCategoryAndCollectionsFormProps {
|
||||||
categories?: Array<{ value: string; label: string }>;
|
categories?: Array<{ value: string; label: string }>;
|
||||||
|
@ -26,15 +26,25 @@ const ProductCategoryAndCollectionsForm = ({
|
||||||
category,
|
category,
|
||||||
loading,
|
loading,
|
||||||
onChange
|
onChange
|
||||||
}: ProductCategoryAndCollectionsFormProps) => (
|
}: ProductCategoryAndCollectionsFormProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<PageHeader title={i18n.t("Organisation")} />
|
<PageHeader
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Organization",
|
||||||
|
description: "product organization, header"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<SingleSelectField
|
<SingleSelectField
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
error={!!errors.category}
|
error={!!errors.category}
|
||||||
hint={errors.category}
|
hint={errors.category}
|
||||||
label={i18n.t("Category")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Category"
|
||||||
|
})}
|
||||||
choices={loading ? [] : categories}
|
choices={loading ? [] : categories}
|
||||||
name="category"
|
name="category"
|
||||||
value={category}
|
value={category}
|
||||||
|
@ -45,7 +55,9 @@ const ProductCategoryAndCollectionsForm = ({
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
error={!!errors.collections}
|
error={!!errors.collections}
|
||||||
hint={errors.collections}
|
hint={errors.collections}
|
||||||
label={i18n.t("Collections")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Collections"
|
||||||
|
})}
|
||||||
choices={loading ? [] : collections}
|
choices={loading ? [] : collections}
|
||||||
name="collections"
|
name="collections"
|
||||||
value={productCollections}
|
value={productCollections}
|
||||||
|
@ -54,6 +66,7 @@ const ProductCategoryAndCollectionsForm = ({
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
ProductCategoryAndCollectionsForm.displayName =
|
ProductCategoryAndCollectionsForm.displayName =
|
||||||
"ProductCategoryAndCollectionsForm";
|
"ProductCategoryAndCollectionsForm";
|
||||||
export default ProductCategoryAndCollectionsForm;
|
export default ProductCategoryAndCollectionsForm;
|
||||||
|
|
|
@ -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";
|
||||||
|
@ -16,6 +17,7 @@ import { SearchCategories_categories_edges_node } from "@saleor/containers/Searc
|
||||||
import { SearchCollections_collections_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
|
import { SearchCollections_collections_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
|
||||||
import useFormset from "@saleor/hooks/useFormset";
|
import useFormset from "@saleor/hooks/useFormset";
|
||||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||||
|
import { sectionNames } from "@saleor/intl";
|
||||||
import {
|
import {
|
||||||
getChoices,
|
getChoices,
|
||||||
ProductAttributeValueChoices,
|
ProductAttributeValueChoices,
|
||||||
|
@ -23,7 +25,6 @@ import {
|
||||||
} from "@saleor/products/utils/data";
|
} from "@saleor/products/utils/data";
|
||||||
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
|
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
|
||||||
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
||||||
import i18n from "../../../i18n";
|
|
||||||
import { UserError } from "../../../types";
|
import { UserError } from "../../../types";
|
||||||
import { ProductCreateData_productTypes_edges_node_productAttributes } from "../../types/ProductCreateData";
|
import { ProductCreateData_productTypes_edges_node_productAttributes } from "../../types/ProductCreateData";
|
||||||
import {
|
import {
|
||||||
|
@ -96,6 +97,8 @@ export const ProductCreatePage: React.StatelessComponent<
|
||||||
onBack,
|
onBack,
|
||||||
onSubmit
|
onSubmit
|
||||||
}: ProductCreatePageProps) => {
|
}: ProductCreatePageProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
// Form values
|
// Form values
|
||||||
const {
|
const {
|
||||||
change: changeAttributeData,
|
change: changeAttributeData,
|
||||||
|
@ -200,7 +203,9 @@ export const ProductCreatePage: React.StatelessComponent<
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<AppHeader onBack={onBack}>{i18n.t("Products")}</AppHeader>
|
<AppHeader onBack={onBack}>
|
||||||
|
{intl.formatMessage(sectionNames.products)}
|
||||||
|
</AppHeader>
|
||||||
<PageHeader title={header} />
|
<PageHeader title={header} />
|
||||||
<Grid>
|
<Grid>
|
||||||
<div>
|
<div>
|
||||||
|
@ -239,9 +244,10 @@ export const ProductCreatePage: React.StatelessComponent<
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<SeoForm
|
<SeoForm
|
||||||
helperText={i18n.t(
|
helperText={intl.formatMessage({
|
||||||
|
defaultMessage:
|
||||||
"Add search engine title and description to make this product easier to find"
|
"Add search engine title and description to make this product easier to find"
|
||||||
)}
|
})}
|
||||||
title={data.seoTitle}
|
title={data.seoTitle}
|
||||||
titlePlaceholder={data.name}
|
titlePlaceholder={data.name}
|
||||||
description={data.seoDescription}
|
description={data.seoDescription}
|
||||||
|
|
|
@ -9,11 +9,12 @@ import {
|
||||||
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";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
@ -48,9 +49,14 @@ export const ProductDetailsForm = withStyles(styles, {
|
||||||
errors,
|
errors,
|
||||||
initialDescription,
|
initialDescription,
|
||||||
onChange
|
onChange
|
||||||
}: ProductDetailsFormProps) => (
|
}: ProductDetailsFormProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle title={i18n.t("General information")} />
|
<CardTitle
|
||||||
|
title={intl.formatMessage(commonMessages.generalInformations)}
|
||||||
|
/>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -58,7 +64,10 @@ export const ProductDetailsForm = withStyles(styles, {
|
||||||
helperText={errors.name}
|
helperText={errors.name}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
fullWidth
|
fullWidth
|
||||||
label={i18n.t("Name")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Name",
|
||||||
|
description: "product name"
|
||||||
|
})}
|
||||||
name="name"
|
name="name"
|
||||||
rows={5}
|
rows={5}
|
||||||
value={data.name}
|
value={data.name}
|
||||||
|
@ -71,13 +80,14 @@ export const ProductDetailsForm = withStyles(styles, {
|
||||||
error={!!errors.descriptionJson}
|
error={!!errors.descriptionJson}
|
||||||
helperText={errors.descriptionJson}
|
helperText={errors.descriptionJson}
|
||||||
initial={initialDescription}
|
initial={initialDescription}
|
||||||
label={i18n.t("Description")}
|
label={intl.formatMessage(commonMessages.description)}
|
||||||
name="description"
|
name="description"
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
ProductDetailsForm.displayName = "ProductDetailsForm";
|
ProductDetailsForm.displayName = "ProductDetailsForm";
|
||||||
export default ProductDetailsForm;
|
export default ProductDetailsForm;
|
||||||
|
|
|
@ -8,10 +8,10 @@ import {
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
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 Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import i18n from "../../../i18n";
|
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
@ -64,9 +64,17 @@ const ProductImageNavigation = withStyles(styles, {
|
||||||
highlighted,
|
highlighted,
|
||||||
images,
|
images,
|
||||||
onRowClick
|
onRowClick
|
||||||
}: ProductImageNavigationProps) => (
|
}: ProductImageNavigationProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
<Card className={classes.card}>
|
<Card className={classes.card}>
|
||||||
<CardTitle title={i18n.t("All photos")} />
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "All Photos",
|
||||||
|
description: "section header"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{images === undefined ? (
|
{images === undefined ? (
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
|
@ -76,7 +84,8 @@ const ProductImageNavigation = withStyles(styles, {
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames({
|
||||||
[classes.imageContainer]: true,
|
[classes.imageContainer]: true,
|
||||||
[classes.highlightedImageContainer]: image.id === highlighted
|
[classes.highlightedImageContainer]:
|
||||||
|
image.id === highlighted
|
||||||
})}
|
})}
|
||||||
onClick={onRowClick(image.id)}
|
onClick={onRowClick(image.id)}
|
||||||
key={image.id}
|
key={image.id}
|
||||||
|
@ -88,7 +97,8 @@ const ProductImageNavigation = withStyles(styles, {
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
ProductImageNavigation.displayName = "ProductImageNavigation";
|
ProductImageNavigation.displayName = "ProductImageNavigation";
|
||||||
export default ProductImageNavigation;
|
export default ProductImageNavigation;
|
||||||
|
|
|
@ -8,6 +8,7 @@ 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 AppHeader from "@saleor/components/AppHeader";
|
import AppHeader from "@saleor/components/AppHeader";
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
|
@ -18,7 +19,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 Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import i18n from "../../../i18n";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import ProductImageNavigation from "../ProductImageNavigation";
|
import ProductImageNavigation from "../ProductImageNavigation";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
|
@ -69,7 +70,10 @@ const ProductImagePage = withStyles(styles, { name: "ProductImagePage" })(
|
||||||
onDelete,
|
onDelete,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
onSubmit
|
onSubmit
|
||||||
}: ProductImagePageProps) => (
|
}: ProductImagePageProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
<Form
|
<Form
|
||||||
initial={{ description: image ? image.alt : "" }}
|
initial={{ description: image ? image.alt : "" }}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
|
@ -79,7 +83,12 @@ const ProductImagePage = withStyles(styles, { name: "ProductImagePage" })(
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<AppHeader onBack={onBack}>{product}</AppHeader>
|
<AppHeader onBack={onBack}>{product}</AppHeader>
|
||||||
<PageHeader title={i18n.t("Edit Photo")} />
|
<PageHeader
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Edit Photo",
|
||||||
|
description: "header"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<Grid variant="inverted">
|
<Grid variant="inverted">
|
||||||
<div>
|
<div>
|
||||||
<ProductImageNavigation
|
<ProductImageNavigation
|
||||||
|
@ -89,12 +98,20 @@ const ProductImagePage = withStyles(styles, { name: "ProductImagePage" })(
|
||||||
onRowClick={onRowClick}
|
onRowClick={onRowClick}
|
||||||
/>
|
/>
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle title={i18n.t("Photo Information")} />
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Photo Information",
|
||||||
|
description: "section header"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<TextField
|
<TextField
|
||||||
name="description"
|
name="description"
|
||||||
label={i18n.t("Description")}
|
label={intl.formatMessage(commonMessages.description)}
|
||||||
helperText={i18n.t("Optional")}
|
helperText={intl.formatMessage({
|
||||||
|
defaultMessage: "Optional",
|
||||||
|
description: "field is optional"
|
||||||
|
})}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={change}
|
onChange={change}
|
||||||
value={data.description}
|
value={data.description}
|
||||||
|
@ -106,7 +123,12 @@ const ProductImagePage = withStyles(styles, { name: "ProductImagePage" })(
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle title={i18n.t("Photo View")} />
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Photo View",
|
||||||
|
description: "section header"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{!!image ? (
|
{!!image ? (
|
||||||
<div className={classes.imageContainer}>
|
<div className={classes.imageContainer}>
|
||||||
|
@ -130,7 +152,8 @@ const ProductImagePage = withStyles(styles, { name: "ProductImagePage" })(
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</Form>
|
</Form>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
ProductImagePage.displayName = "ProductImagePage";
|
ProductImagePage.displayName = "ProductImagePage";
|
||||||
export default ProductImagePage;
|
export default ProductImagePage;
|
||||||
|
|
|
@ -2,6 +2,7 @@ import Button from "@material-ui/core/Button";
|
||||||
import Card from "@material-ui/core/Card";
|
import Card from "@material-ui/core/Card";
|
||||||
import CardContent from "@material-ui/core/CardContent";
|
import CardContent from "@material-ui/core/CardContent";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createStyles,
|
createStyles,
|
||||||
|
@ -12,10 +13,10 @@ import {
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
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 { commonMessages } from "@saleor/intl";
|
||||||
import { ReorderAction } from "@saleor/types";
|
import { ReorderAction } from "@saleor/types";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { SortableContainer, SortableElement } from "react-sortable-hoc";
|
import { SortableContainer, SortableElement } from "react-sortable-hoc";
|
||||||
import i18n from "../../../i18n";
|
|
||||||
import { ProductDetails_product_images } from "../../types/ProductDetails";
|
import { ProductDetails_product_images } from "../../types/ProductDetails";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
|
@ -181,27 +182,34 @@ const ProductImages = withStyles(styles, { name: "ProductImages" })(
|
||||||
onImageDelete,
|
onImageDelete,
|
||||||
onImageReorder,
|
onImageReorder,
|
||||||
onImageUpload
|
onImageUpload
|
||||||
}: ProductImagesProps) => (
|
}: ProductImagesProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const upload = React.useRef(null);
|
||||||
|
|
||||||
|
return (
|
||||||
<Card className={classes.card}>
|
<Card className={classes.card}>
|
||||||
<CardTitle
|
<CardTitle
|
||||||
title={i18n.t("Images")}
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Images",
|
||||||
|
description: "section header"
|
||||||
|
})}
|
||||||
toolbar={
|
toolbar={
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => this.upload.click()}
|
onClick={() => upload.current.click()}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
variant="text"
|
variant="text"
|
||||||
color="primary"
|
color="primary"
|
||||||
data-tc="button-upload-image"
|
data-tc="button-upload-image"
|
||||||
>
|
>
|
||||||
{i18n.t("Upload image")}
|
{intl.formatMessage(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={ref => (this.upload = ref)}
|
ref={upload}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
@ -249,7 +257,8 @@ const ProductImages = withStyles(styles, { name: "ProductImages" })(
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
ProductImages.displayName = "ProductImages";
|
ProductImages.displayName = "ProductImages";
|
||||||
export default ProductImages;
|
export default ProductImages;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import { FieldType, IFilter } from "@saleor/components/Filter";
|
import { FieldType, IFilter } from "@saleor/components/Filter";
|
||||||
import FilterBar from "@saleor/components/FilterBar";
|
import FilterBar from "@saleor/components/FilterBar";
|
||||||
import i18n from "@saleor/i18n";
|
|
||||||
import { FilterProps } from "@saleor/types";
|
import { FilterProps } from "@saleor/types";
|
||||||
import { StockAvailability } from "@saleor/types/globalTypes";
|
import { StockAvailability } from "@saleor/types/globalTypes";
|
||||||
import { ProductListUrlFilters } from "../../urls";
|
import { ProductListUrlFilters } from "../../urls";
|
||||||
|
@ -17,44 +17,74 @@ export enum ProductFilterKeys {
|
||||||
stock,
|
stock,
|
||||||
query
|
query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ProductListFilter: React.FC<ProductListFilterProps> = props => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
const filterMenu: IFilter = [
|
const filterMenu: IFilter = [
|
||||||
{
|
{
|
||||||
children: [],
|
children: [],
|
||||||
data: {
|
data: {
|
||||||
additionalText: i18n.t("is set as"),
|
additionalText: intl.formatMessage({
|
||||||
fieldLabel: i18n.t("Status"),
|
defaultMessage: "is set as",
|
||||||
|
description: "product status is set as"
|
||||||
|
}),
|
||||||
|
fieldLabel: intl.formatMessage({
|
||||||
|
defaultMessage: "Status",
|
||||||
|
description: "product status"
|
||||||
|
}),
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
label: i18n.t("Visible"),
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Visible",
|
||||||
|
description: "product is visible"
|
||||||
|
}),
|
||||||
value: true
|
value: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18n.t("Hidden"),
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Hidden",
|
||||||
|
description: "product is hidden"
|
||||||
|
}),
|
||||||
value: false
|
value: false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
type: FieldType.select
|
type: FieldType.select
|
||||||
},
|
},
|
||||||
label: i18n.t("Visibility"),
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Visibility",
|
||||||
|
description: "product visibility"
|
||||||
|
}),
|
||||||
value: ProductFilterKeys.published.toString()
|
value: ProductFilterKeys.published.toString()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
children: [],
|
children: [],
|
||||||
data: {
|
data: {
|
||||||
fieldLabel: i18n.t("Stock quantity"),
|
fieldLabel: intl.formatMessage({
|
||||||
|
defaultMessage: "Stock quantity"
|
||||||
|
}),
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
label: i18n.t("Available"),
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Available",
|
||||||
|
description: "product status"
|
||||||
|
}),
|
||||||
value: StockAvailability.IN_STOCK
|
value: StockAvailability.IN_STOCK
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18n.t("Out Of Stock"),
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Out Of Stock",
|
||||||
|
description: "product status"
|
||||||
|
}),
|
||||||
value: StockAvailability.OUT_OF_STOCK
|
value: StockAvailability.OUT_OF_STOCK
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
type: FieldType.select
|
type: FieldType.select
|
||||||
},
|
},
|
||||||
label: i18n.t("Stock"),
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Stock",
|
||||||
|
description: "product stock"
|
||||||
|
}),
|
||||||
value: ProductFilterKeys.stock.toString()
|
value: ProductFilterKeys.stock.toString()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -62,34 +92,46 @@ const filterMenu: IFilter = [
|
||||||
{
|
{
|
||||||
children: [],
|
children: [],
|
||||||
data: {
|
data: {
|
||||||
additionalText: i18n.t("equals"),
|
additionalText: intl.formatMessage({
|
||||||
|
defaultMessage: "equals",
|
||||||
|
description: "product price"
|
||||||
|
}),
|
||||||
fieldLabel: null,
|
fieldLabel: null,
|
||||||
type: FieldType.price
|
type: FieldType.price
|
||||||
},
|
},
|
||||||
label: i18n.t("Specific Price"),
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Specific Price"
|
||||||
|
}),
|
||||||
value: ProductFilterKeys.priceEqual.toString()
|
value: ProductFilterKeys.priceEqual.toString()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
children: [],
|
children: [],
|
||||||
data: {
|
data: {
|
||||||
fieldLabel: i18n.t("Range"),
|
fieldLabel: intl.formatMessage({
|
||||||
|
defaultMessage: "Range"
|
||||||
|
}),
|
||||||
type: FieldType.rangePrice
|
type: FieldType.rangePrice
|
||||||
},
|
},
|
||||||
label: i18n.t("Range"),
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Range"
|
||||||
|
}),
|
||||||
value: ProductFilterKeys.priceRange.toString()
|
value: ProductFilterKeys.priceRange.toString()
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
data: {
|
data: {
|
||||||
fieldLabel: i18n.t("Price"),
|
fieldLabel: intl.formatMessage({
|
||||||
|
defaultMessage: "Price"
|
||||||
|
}),
|
||||||
type: FieldType.range
|
type: FieldType.range
|
||||||
},
|
},
|
||||||
label: i18n.t("Price"),
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Price"
|
||||||
|
}),
|
||||||
value: ProductFilterKeys.price.toString()
|
value: ProductFilterKeys.price.toString()
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const ProductListFilter: React.FC<ProductListFilterProps> = props => (
|
return <FilterBar {...props} filterMenu={filterMenu} />;
|
||||||
<FilterBar {...props} filterMenu={filterMenu} />
|
};
|
||||||
);
|
|
||||||
ProductListFilter.displayName = "ProductListFilter";
|
ProductListFilter.displayName = "ProductListFilter";
|
||||||
export default ProductListFilter;
|
export default ProductListFilter;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { Theme } from "@material-ui/core/styles";
|
||||||
import AddIcon from "@material-ui/icons/Add";
|
import AddIcon from "@material-ui/icons/Add";
|
||||||
import makeStyles from "@material-ui/styles/makeStyles";
|
import makeStyles from "@material-ui/styles/makeStyles";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { CategoryDetails_category_products_edges_node } from "@saleor/categories/types/CategoryDetails";
|
import { CategoryDetails_category_products_edges_node } from "@saleor/categories/types/CategoryDetails";
|
||||||
import ColumnPicker, {
|
import ColumnPicker, {
|
||||||
|
@ -14,7 +15,7 @@ import PageHeader from "@saleor/components/PageHeader";
|
||||||
import ProductList from "@saleor/components/ProductList";
|
import ProductList from "@saleor/components/ProductList";
|
||||||
import { ProductListColumns } from "@saleor/config";
|
import { ProductListColumns } from "@saleor/config";
|
||||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||||
import i18n from "@saleor/i18n";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import { FilterPageProps, ListActions, PageListProps } from "@saleor/types";
|
import { FilterPageProps, ListActions, PageListProps } from "@saleor/types";
|
||||||
import { toggle } from "@saleor/utils/lists";
|
import { toggle } from "@saleor/utils/lists";
|
||||||
import { ProductListUrlFilters } from "../../urls";
|
import { ProductListUrlFilters } from "../../urls";
|
||||||
|
@ -53,6 +54,7 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
|
||||||
onUpdateListSettings,
|
onUpdateListSettings,
|
||||||
...listProps
|
...listProps
|
||||||
} = props;
|
} = props;
|
||||||
|
const intl = useIntl();
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
const [selectedColumns, setSelectedColumns] = useStateFromProps(
|
const [selectedColumns, setSelectedColumns] = useStateFromProps(
|
||||||
settings.columns
|
settings.columns
|
||||||
|
@ -74,22 +76,31 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
|
||||||
|
|
||||||
const columns: ColumnPickerChoice[] = [
|
const columns: ColumnPickerChoice[] = [
|
||||||
{
|
{
|
||||||
label: i18n.t("Published"),
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Published",
|
||||||
|
description: "product status"
|
||||||
|
}),
|
||||||
value: "isPublished" as ProductListColumns
|
value: "isPublished" as ProductListColumns
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18n.t("Price"),
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Price",
|
||||||
|
description: "product price"
|
||||||
|
}),
|
||||||
value: "price" as ProductListColumns
|
value: "price" as ProductListColumns
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18n.t("Type"),
|
label: intl.formatMessage({
|
||||||
|
defaultMessage: "Type",
|
||||||
|
description: "product type"
|
||||||
|
}),
|
||||||
value: "productType" as ProductListColumns
|
value: "productType" as ProductListColumns
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<PageHeader title={i18n.t("Products")}>
|
<PageHeader title={intl.formatMessage(sectionNames.products)}>
|
||||||
<ColumnPicker
|
<ColumnPicker
|
||||||
className={classes.columnPicker}
|
className={classes.columnPicker}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
@ -105,19 +116,27 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
|
||||||
variant="contained"
|
variant="contained"
|
||||||
data-tc="add-product"
|
data-tc="add-product"
|
||||||
>
|
>
|
||||||
{i18n.t("Add product")} <AddIcon />
|
<FormattedMessage defaultMessage="Add Product" description="button" />
|
||||||
|
<AddIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<Card>
|
<Card>
|
||||||
<ProductListFilter
|
<ProductListFilter
|
||||||
allTabLabel={i18n.t("All Products")}
|
allTabLabel={intl.formatMessage({
|
||||||
|
defaultMessage: "All Products",
|
||||||
|
description: "tab name"
|
||||||
|
})}
|
||||||
currencySymbol={currencySymbol}
|
currencySymbol={currencySymbol}
|
||||||
currentTab={currentTab}
|
currentTab={currentTab}
|
||||||
filterLabel={i18n.t("Select all products where:")}
|
filterLabel={intl.formatMessage({
|
||||||
|
defaultMessage: "Select all products where:"
|
||||||
|
})}
|
||||||
filterTabs={filterTabs}
|
filterTabs={filterTabs}
|
||||||
filtersList={filtersList}
|
filtersList={filtersList}
|
||||||
initialSearch={initialSearch}
|
initialSearch={initialSearch}
|
||||||
searchPlaceholder={i18n.t("Search Products...")}
|
searchPlaceholder={intl.formatMessage({
|
||||||
|
defaultMessage: "Search Products..."
|
||||||
|
})}
|
||||||
onAll={onAll}
|
onAll={onAll}
|
||||||
onSearchChange={onSearchChange}
|
onSearchChange={onSearchChange}
|
||||||
onFilterAdd={onFilterAdd}
|
onFilterAdd={onFilterAdd}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import CardSpacer from "@saleor/components/CardSpacer";
|
import CardSpacer from "@saleor/components/CardSpacer";
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
|
@ -20,7 +21,6 @@ import SingleAutocompleteSelectField, {
|
||||||
SingleAutocompleteChoiceType
|
SingleAutocompleteChoiceType
|
||||||
} from "@saleor/components/SingleAutocompleteSelectField";
|
} from "@saleor/components/SingleAutocompleteSelectField";
|
||||||
import { ChangeEvent } from "@saleor/hooks/useForm";
|
import { ChangeEvent } from "@saleor/hooks/useForm";
|
||||||
import i18n from "@saleor/i18n";
|
|
||||||
import { maybe } from "@saleor/misc";
|
import { maybe } from "@saleor/misc";
|
||||||
import { FormErrors } from "@saleor/types";
|
import { FormErrors } from "@saleor/types";
|
||||||
|
|
||||||
|
@ -86,9 +86,17 @@ const ProductOrganization = withStyles(styles, { name: "ProductOrganization" })(
|
||||||
onCategoryChange,
|
onCategoryChange,
|
||||||
onCollectionChange,
|
onCollectionChange,
|
||||||
onProductTypeChange
|
onProductTypeChange
|
||||||
}: ProductOrganizationProps) => (
|
}: ProductOrganizationProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
<Card className={classes.card}>
|
<Card className={classes.card}>
|
||||||
<CardTitle title={i18n.t("Organize Product")} />
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Organize Product",
|
||||||
|
description: "section header"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{canChangeType ? (
|
{canChangeType ? (
|
||||||
<SingleAutocompleteSelectField
|
<SingleAutocompleteSelectField
|
||||||
|
@ -97,7 +105,9 @@ const ProductOrganization = withStyles(styles, { name: "ProductOrganization" })(
|
||||||
helperText={errors.productType}
|
helperText={errors.productType}
|
||||||
name="productType"
|
name="productType"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label={i18n.t("Product Type")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Product Type"
|
||||||
|
})}
|
||||||
choices={productTypes}
|
choices={productTypes}
|
||||||
value={data.productType}
|
value={data.productType}
|
||||||
onChange={onProductTypeChange}
|
onChange={onProductTypeChange}
|
||||||
|
@ -106,19 +116,25 @@ const ProductOrganization = withStyles(styles, { name: "ProductOrganization" })(
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Typography className={classes.label} variant="caption">
|
<Typography className={classes.label} variant="caption">
|
||||||
{i18n.t("Product Type")}
|
<FormattedMessage defaultMessage="Product Type" />
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography>{maybe(() => productType.name, "...")}</Typography>
|
<Typography>{maybe(() => productType.name, "...")}</Typography>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
<Typography className={classes.label} variant="caption">
|
<Typography className={classes.label} variant="caption">
|
||||||
{i18n.t("Product Type")}
|
<FormattedMessage defaultMessage="Product Type" />
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography>
|
<Typography>
|
||||||
{maybe(
|
{maybe(
|
||||||
() =>
|
() =>
|
||||||
productType.hasVariants
|
productType.hasVariants
|
||||||
? i18n.t("Configurable")
|
? intl.formatMessage({
|
||||||
: i18n.t("Simple"),
|
defaultMessage: "Configurable",
|
||||||
|
description: "product is configurable"
|
||||||
|
})
|
||||||
|
: intl.formatMessage({
|
||||||
|
defaultMessage: "Simple",
|
||||||
|
description: "product is not configurable"
|
||||||
|
}),
|
||||||
"..."
|
"..."
|
||||||
)}
|
)}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -132,7 +148,9 @@ const ProductOrganization = withStyles(styles, { name: "ProductOrganization" })(
|
||||||
error={!!errors.category}
|
error={!!errors.category}
|
||||||
helperText={errors.category}
|
helperText={errors.category}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label={i18n.t("Category")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Category"
|
||||||
|
})}
|
||||||
choices={disabled ? [] : categories}
|
choices={disabled ? [] : categories}
|
||||||
name="category"
|
name="category"
|
||||||
value={data.category}
|
value={data.category}
|
||||||
|
@ -145,20 +163,25 @@ const ProductOrganization = withStyles(styles, { name: "ProductOrganization" })(
|
||||||
<FormSpacer />
|
<FormSpacer />
|
||||||
<MultiAutocompleteSelectField
|
<MultiAutocompleteSelectField
|
||||||
displayValues={collectionsInputDisplayValue}
|
displayValues={collectionsInputDisplayValue}
|
||||||
label={i18n.t("Collections")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Collections"
|
||||||
|
})}
|
||||||
choices={disabled ? [] : collections}
|
choices={disabled ? [] : collections}
|
||||||
name="collections"
|
name="collections"
|
||||||
value={data.collections}
|
value={data.collections}
|
||||||
helperText={i18n.t(
|
helperText={intl.formatMessage({
|
||||||
"*Optional. Adding product to collection helps users find it."
|
defaultMessage:
|
||||||
)}
|
"*Optional. Adding product to collection helps users find it.",
|
||||||
|
description: "field is optional"
|
||||||
|
})}
|
||||||
onChange={onCollectionChange}
|
onChange={onCollectionChange}
|
||||||
fetchChoices={fetchCollections}
|
fetchChoices={fetchCollections}
|
||||||
data-tc="collections"
|
data-tc="collections"
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
ProductOrganization.displayName = "ProductOrganization";
|
ProductOrganization.displayName = "ProductOrganization";
|
||||||
export default ProductOrganization;
|
export default ProductOrganization;
|
||||||
|
|
|
@ -7,11 +7,11 @@ import {
|
||||||
WithStyles
|
WithStyles
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
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 PriceField from "@saleor/components/PriceField";
|
import PriceField from "@saleor/components/PriceField";
|
||||||
import i18n from "../../../i18n";
|
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
@ -33,12 +33,22 @@ interface ProductPricingProps extends WithStyles<typeof styles> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProductPricing = withStyles(styles, { name: "ProductPricing" })(
|
const ProductPricing = withStyles(styles, { name: "ProductPricing" })(
|
||||||
({ classes, currency, data, disabled, onChange }: ProductPricingProps) => (
|
({ classes, currency, data, disabled, onChange }: ProductPricingProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle title={i18n.t("Pricing")}>
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Pricing",
|
||||||
|
description: "product pricing"
|
||||||
|
})}
|
||||||
|
>
|
||||||
<ControlledCheckbox
|
<ControlledCheckbox
|
||||||
name="chargeTaxes"
|
name="chargeTaxes"
|
||||||
label={i18n.t("Charge taxes for this item")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Charge taxes for this item"
|
||||||
|
})}
|
||||||
checked={data.chargeTaxes}
|
checked={data.chargeTaxes}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -48,7 +58,10 @@ const ProductPricing = withStyles(styles, { name: "ProductPricing" })(
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<PriceField
|
<PriceField
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label={i18n.t("Price")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Price",
|
||||||
|
description: "product price"
|
||||||
|
})}
|
||||||
name="basePrice"
|
name="basePrice"
|
||||||
value={data.basePrice}
|
value={data.basePrice}
|
||||||
currencySymbol={currency}
|
currencySymbol={currency}
|
||||||
|
@ -57,7 +70,8 @@ const ProductPricing = withStyles(styles, { name: "ProductPricing" })(
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
ProductPricing.displayName = "ProductPricing";
|
ProductPricing.displayName = "ProductPricing";
|
||||||
export default ProductPricing;
|
export default ProductPricing;
|
||||||
|
|
|
@ -8,9 +8,9 @@ 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 { maybe } from "../../../misc";
|
import { maybe } from "../../../misc";
|
||||||
import { ProductDetails_product } from "../../types/ProductDetails";
|
import { ProductDetails_product } from "../../types/ProductDetails";
|
||||||
|
|
||||||
|
@ -35,15 +35,33 @@ interface ProductStockProps extends WithStyles<typeof styles> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProductStock = withStyles(styles, { name: "ProductStock" })(
|
const ProductStock = withStyles(styles, { name: "ProductStock" })(
|
||||||
({ classes, data, disabled, product, onChange, errors }: ProductStockProps) => (
|
({
|
||||||
|
classes,
|
||||||
|
data,
|
||||||
|
disabled,
|
||||||
|
product,
|
||||||
|
onChange,
|
||||||
|
errors
|
||||||
|
}: ProductStockProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle title={i18n.t("Inventory")} />
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Inventory",
|
||||||
|
description: "product stock, section header",
|
||||||
|
id: "productStockHeader"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<TextField
|
<TextField
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
name="sku"
|
name="sku"
|
||||||
label={i18n.t("SKU (Stock Keeping Unit)")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "SKU (Stock Keeping Unit)"
|
||||||
|
})}
|
||||||
value={data.sku}
|
value={data.sku}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
error={!!errors.sku}
|
error={!!errors.sku}
|
||||||
|
@ -52,22 +70,35 @@ const ProductStock = withStyles(styles, { name: "ProductStock" })(
|
||||||
<TextField
|
<TextField
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
name="stockQuantity"
|
name="stockQuantity"
|
||||||
label={i18n.t("Inventory")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Inventory",
|
||||||
|
description: "product stock",
|
||||||
|
id: "prodictStockInventoryLabel"
|
||||||
|
})}
|
||||||
value={data.stockQuantity}
|
value={data.stockQuantity}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
helperText={
|
helperText={
|
||||||
product
|
product
|
||||||
? i18n.t("Allocated: {{ quantity }}", {
|
? intl.formatMessage(
|
||||||
quantity: maybe(() => product.variants[0].quantityAllocated)
|
{
|
||||||
})
|
defaultMessage: "Allocated: {quantity}",
|
||||||
|
description: "allocated product stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
quantity: maybe(
|
||||||
|
() => product.variants[0].quantityAllocated
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
ProductStock.displayName = "ProductStock";
|
ProductStock.displayName = "ProductStock";
|
||||||
export default ProductStock;
|
export default ProductStock;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { convertFromRaw, RawDraftContentState } from "draft-js";
|
import { convertFromRaw, 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";
|
||||||
|
@ -15,7 +16,7 @@ import { SearchCategories_categories_edges_node } from "@saleor/containers/Searc
|
||||||
import { SearchCollections_collections_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
|
import { SearchCollections_collections_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
|
||||||
import useFormset from "@saleor/hooks/useFormset";
|
import useFormset from "@saleor/hooks/useFormset";
|
||||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||||
import i18n from "@saleor/i18n";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import { maybe } from "@saleor/misc";
|
import { maybe } from "@saleor/misc";
|
||||||
import { ListActions, UserError } from "@saleor/types";
|
import { ListActions, UserError } from "@saleor/types";
|
||||||
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
|
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
|
||||||
|
@ -107,6 +108,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar
|
||||||
}) => {
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
const attributeInput = React.useMemo(
|
const attributeInput = React.useMemo(
|
||||||
() => getAttributeInputFromProduct(product),
|
() => getAttributeInputFromProduct(product),
|
||||||
[product]
|
[product]
|
||||||
|
@ -188,7 +190,9 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Container>
|
<Container>
|
||||||
<AppHeader onBack={onBack}>{i18n.t("Products")}</AppHeader>
|
<AppHeader onBack={onBack}>
|
||||||
|
{intl.formatMessage(sectionNames.products)}
|
||||||
|
</AppHeader>
|
||||||
<PageHeader title={header} />
|
<PageHeader title={header} />
|
||||||
<Grid>
|
<Grid>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
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";
|
||||||
|
@ -9,7 +10,7 @@ import SingleAutocompleteSelectField, {
|
||||||
} from "@saleor/components/SingleAutocompleteSelectField";
|
} from "@saleor/components/SingleAutocompleteSelectField";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset";
|
import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset";
|
||||||
import i18n from "../../../i18n";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import { ProductVariant_attributes_attribute_values } from "../../types/ProductVariant";
|
import { ProductVariant_attributes_attribute_values } from "../../types/ProductVariant";
|
||||||
|
|
||||||
export interface VariantAttributeInputData {
|
export interface VariantAttributeInputData {
|
||||||
|
@ -67,9 +68,14 @@ const ProductVariantAttributes: React.FC<ProductVariantAttributesProps> = ({
|
||||||
disabled,
|
disabled,
|
||||||
errors,
|
errors,
|
||||||
onChange
|
onChange
|
||||||
}) => (
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle title={i18n.t("General Information")} />
|
<CardTitle
|
||||||
|
title={intl.formatMessage(commonMessages.generalInformations)}
|
||||||
|
/>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Grid variant="uniform">
|
<Grid variant="uniform">
|
||||||
{attributes === undefined ? (
|
{attributes === undefined ? (
|
||||||
|
@ -102,5 +108,6 @@ const ProductVariantAttributes: React.FC<ProductVariantAttributesProps> = ({
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
ProductVariantAttributes.displayName = "ProductVariantAttributes";
|
ProductVariantAttributes.displayName = "ProductVariantAttributes";
|
||||||
export default ProductVariantAttributes;
|
export default ProductVariantAttributes;
|
||||||
|
|
|
@ -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";
|
||||||
|
@ -16,7 +17,6 @@ import {
|
||||||
getVariantAttributeErrors,
|
getVariantAttributeErrors,
|
||||||
getVariantAttributeInputFromProduct
|
getVariantAttributeInputFromProduct
|
||||||
} from "@saleor/products/utils/data";
|
} from "@saleor/products/utils/data";
|
||||||
import i18n from "../../../i18n";
|
|
||||||
import { maybe } from "../../../misc";
|
import { maybe } from "../../../misc";
|
||||||
import { UserError } from "../../../types";
|
import { UserError } from "../../../types";
|
||||||
import { ProductVariantCreateData_product } from "../../types/ProductVariantCreateData";
|
import { ProductVariantCreateData_product } from "../../types/ProductVariantCreateData";
|
||||||
|
@ -63,6 +63,7 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onVariantClick
|
onVariantClick
|
||||||
}) => {
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
const attributeInput = React.useMemo(
|
const attributeInput = React.useMemo(
|
||||||
() => getVariantAttributeInputFromProduct(product),
|
() => getVariantAttributeInputFromProduct(product),
|
||||||
[product]
|
[product]
|
||||||
|
@ -150,8 +151,14 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
||||||
<SaveButtonBar
|
<SaveButtonBar
|
||||||
disabled={loading || !onSubmit || !hasChanged}
|
disabled={loading || !onSubmit || !hasChanged}
|
||||||
labels={{
|
labels={{
|
||||||
delete: i18n.t("Remove variant"),
|
delete: intl.formatMessage({
|
||||||
save: i18n.t("Save variant")
|
defaultMessage: "Delete Variant",
|
||||||
|
description: "button"
|
||||||
|
}),
|
||||||
|
save: intl.formatMessage({
|
||||||
|
defaultMessage: "Save variant",
|
||||||
|
description: "button"
|
||||||
|
})
|
||||||
}}
|
}}
|
||||||
state={saveButtonBarState}
|
state={saveButtonBarState}
|
||||||
onCancel={onBack}
|
onCancel={onBack}
|
||||||
|
|
|
@ -11,11 +11,12 @@ import {
|
||||||
WithStyles
|
WithStyles
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import ConfirmButton, {
|
import ConfirmButton, {
|
||||||
ConfirmButtonTransitionState
|
ConfirmButtonTransitionState
|
||||||
} from "@saleor/components/ConfirmButton";
|
} from "@saleor/components/ConfirmButton";
|
||||||
import i18n from "../../../i18n";
|
import { buttonMessages } from "@saleor/intl";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
@ -50,21 +51,25 @@ const ProductVariantDeleteDialog = withStyles(styles, {
|
||||||
}: ProductVariantDeleteDialogProps) => (
|
}: ProductVariantDeleteDialogProps) => (
|
||||||
<Dialog onClose={onClose} open={open}>
|
<Dialog onClose={onClose} open={open}>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
{i18n.t("Delete variant", { context: "title" })}
|
<FormattedMessage
|
||||||
|
defaultMessage="Delete Variant"
|
||||||
|
description="dialog header"
|
||||||
|
/>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText
|
<DialogContentText>
|
||||||
dangerouslySetInnerHTML={{
|
<FormattedMessage
|
||||||
__html: i18n.t(
|
defaultMessage="Are you sure you want to delete {name}?"
|
||||||
"Are you sure you want to remove <strong>{{name}}</strong>?",
|
description="delete product variant"
|
||||||
{ name }
|
values={{
|
||||||
)
|
name
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</DialogContentText>
|
||||||
</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}
|
||||||
|
@ -72,7 +77,10 @@ const ProductVariantDeleteDialog = withStyles(styles, {
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={onConfirm}
|
onClick={onConfirm}
|
||||||
>
|
>
|
||||||
{i18n.t("Delete variant", { context: "button" })}
|
<FormattedMessage
|
||||||
|
defaultMessage="Delete variant"
|
||||||
|
description="button"
|
||||||
|
/>
|
||||||
</ConfirmButton>
|
</ConfirmButton>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
|
@ -11,8 +11,9 @@ import {
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import i18n from "../../../i18n";
|
import { buttonMessages } from "@saleor/intl";
|
||||||
import { ProductImage } from "../../types/ProductImage";
|
import { ProductImage } from "../../types/ProductImage";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
|
@ -72,7 +73,10 @@ const ProductVariantImageSelectDialog = withStyles(styles, {
|
||||||
}: ProductVariantImageSelectDialogProps) => (
|
}: ProductVariantImageSelectDialogProps) => (
|
||||||
<Dialog onClose={onClose} open={open}>
|
<Dialog onClose={onClose} open={open}>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
{i18n.t("Image selection", { context: "title" })}
|
<FormattedMessage
|
||||||
|
defaultMessage="Image Selection"
|
||||||
|
description="dialog header"
|
||||||
|
/>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
|
@ -97,7 +101,7 @@ const ProductVariantImageSelectDialog = withStyles(styles, {
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={onClose}>
|
<Button onClick={onClose}>
|
||||||
{i18n.t("Close", { context: "button" })}
|
<FormattedMessage {...buttonMessages.back} />
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
|
@ -9,10 +9,10 @@ import {
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import i18n from "../../../i18n";
|
|
||||||
import { ProductImage } from "../../types/ProductImage";
|
import { ProductImage } from "../../types/ProductImage";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
|
@ -54,10 +54,16 @@ interface ProductVariantImagesProps extends WithStyles<typeof styles> {
|
||||||
|
|
||||||
export const ProductVariantImages = withStyles(styles, {
|
export const ProductVariantImages = withStyles(styles, {
|
||||||
name: "ProductVariantImages"
|
name: "ProductVariantImages"
|
||||||
})(({ classes, disabled, images, onImageAdd }: ProductVariantImagesProps) => (
|
})(({ classes, disabled, images, onImageAdd }: ProductVariantImagesProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle
|
<CardTitle
|
||||||
title={i18n.t("Images")}
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Images",
|
||||||
|
description: "section header"
|
||||||
|
})}
|
||||||
toolbar={
|
toolbar={
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
|
@ -65,7 +71,10 @@ export const ProductVariantImages = withStyles(styles, {
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={onImageAdd}
|
onClick={onImageAdd}
|
||||||
>
|
>
|
||||||
{i18n.t("Choose photos")}
|
<FormattedMessage
|
||||||
|
defaultMessage="Choose photos"
|
||||||
|
description="button"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -83,12 +92,13 @@ export const ProductVariantImages = withStyles(styles, {
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<Typography className={classes.helpText}>
|
<Typography className={classes.helpText}>
|
||||||
{i18n.t("Select a specific variant image from product images")}
|
<FormattedMessage defaultMessage="Select a specific variant image from product images" />
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
));
|
);
|
||||||
|
});
|
||||||
ProductVariantImages.displayName = "ProductVariantImages";
|
ProductVariantImages.displayName = "ProductVariantImages";
|
||||||
export default ProductVariantImages;
|
export default ProductVariantImages;
|
||||||
|
|
|
@ -12,11 +12,11 @@ import TableCell from "@material-ui/core/TableCell";
|
||||||
import TableRow from "@material-ui/core/TableRow";
|
import TableRow from "@material-ui/core/TableRow";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
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 i18n from "../../../i18n";
|
|
||||||
import { maybe, renderCollection } from "../../../misc";
|
import { maybe, renderCollection } from "../../../misc";
|
||||||
import { ProductVariantCreateData_product_variants } from "../../types/ProductVariantCreateData";
|
import { ProductVariantCreateData_product_variants } from "../../types/ProductVariantCreateData";
|
||||||
import { ProductVariantDetails_productVariant } from "../../types/ProductVariantDetails";
|
import { ProductVariantDetails_productVariant } from "../../types/ProductVariantDetails";
|
||||||
|
@ -63,9 +63,17 @@ const ProductVariantNavigation = withStyles(styles, {
|
||||||
variants,
|
variants,
|
||||||
onAdd,
|
onAdd,
|
||||||
onRowClick
|
onRowClick
|
||||||
}: ProductVariantNavigationProps) => (
|
}: ProductVariantNavigationProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle title={i18n.t("Variants")} />
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Variants",
|
||||||
|
description: "section header"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<Table>
|
<Table>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{renderCollection(variants, variant => (
|
{renderCollection(variants, variant => (
|
||||||
|
@ -93,22 +101,32 @@ const ProductVariantNavigation = withStyles(styles, {
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={2}>
|
<TableCell colSpan={2}>
|
||||||
<Button color="primary" onClick={onAdd}>
|
<Button color="primary" onClick={onAdd}>
|
||||||
{i18n.t("Add variant")}
|
<FormattedMessage
|
||||||
|
defaultMessage="Add variant"
|
||||||
|
description="button"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
) : (
|
) : (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCellAvatar className={classes.tabActive} thumbnail={null} />
|
<TableCellAvatar
|
||||||
|
className={classes.tabActive}
|
||||||
|
thumbnail={null}
|
||||||
|
/>
|
||||||
<TableCell className={classes.textLeft}>
|
<TableCell className={classes.textLeft}>
|
||||||
{i18n.t("New Variant")}
|
<FormattedMessage
|
||||||
|
defaultMessage="New Variant"
|
||||||
|
description="variant name"
|
||||||
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
ProductVariantNavigation.displayName = "ProductVariantNavigation";
|
ProductVariantNavigation.displayName = "ProductVariantNavigation";
|
||||||
export default ProductVariantNavigation;
|
export default ProductVariantNavigation;
|
||||||
|
|
|
@ -7,10 +7,10 @@ import {
|
||||||
WithStyles
|
WithStyles
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
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 PriceField from "@saleor/components/PriceField";
|
import PriceField from "@saleor/components/PriceField";
|
||||||
import i18n from "../../../i18n";
|
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
@ -39,20 +39,34 @@ const ProductVariantPrice = withStyles(styles, { name: "ProductVariantPrice" })(
|
||||||
priceOverride,
|
priceOverride,
|
||||||
loading,
|
loading,
|
||||||
onChange
|
onChange
|
||||||
}: ProductVariantPriceProps) => (
|
}: ProductVariantPriceProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle title={i18n.t("Pricing")} />
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Pricing",
|
||||||
|
description: "product pricing, section header"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className={classes.grid}>
|
<div className={classes.grid}>
|
||||||
<div>
|
<div>
|
||||||
<PriceField
|
<PriceField
|
||||||
error={!!errors.price_override}
|
error={!!errors.price_override}
|
||||||
name="priceOverride"
|
name="priceOverride"
|
||||||
label={i18n.t("Selling price override")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Selling price override"
|
||||||
|
})}
|
||||||
hint={
|
hint={
|
||||||
errors.price_override
|
errors.price_override
|
||||||
? errors.price_override
|
? errors.price_override
|
||||||
: i18n.t("Optional")
|
: intl.formatMessage({
|
||||||
|
defaultMessage: "Optional",
|
||||||
|
description: "optional field",
|
||||||
|
id: "productVariantPriceOptionalPriceOverrideField"
|
||||||
|
})
|
||||||
}
|
}
|
||||||
value={priceOverride}
|
value={priceOverride}
|
||||||
currencySymbol={currencySymbol}
|
currencySymbol={currencySymbol}
|
||||||
|
@ -64,8 +78,18 @@ const ProductVariantPrice = withStyles(styles, { name: "ProductVariantPrice" })(
|
||||||
<PriceField
|
<PriceField
|
||||||
error={!!errors.cost_price}
|
error={!!errors.cost_price}
|
||||||
name="costPrice"
|
name="costPrice"
|
||||||
label={i18n.t("Cost price override")}
|
label={intl.formatMessage({
|
||||||
hint={errors.cost_price ? errors.cost_price : i18n.t("Optional")}
|
defaultMessage: "Cost price override"
|
||||||
|
})}
|
||||||
|
hint={
|
||||||
|
errors.cost_price
|
||||||
|
? errors.cost_price
|
||||||
|
: intl.formatMessage({
|
||||||
|
defaultMessage: "Optional",
|
||||||
|
description: "optional field",
|
||||||
|
id: "productVariantPriceOptionalCostPriceField"
|
||||||
|
})
|
||||||
|
}
|
||||||
value={costPrice}
|
value={costPrice}
|
||||||
currencySymbol={currencySymbol}
|
currencySymbol={currencySymbol}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
@ -75,7 +99,8 @@ const ProductVariantPrice = withStyles(styles, { name: "ProductVariantPrice" })(
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
ProductVariantPrice.displayName = "ProductVariantPrice";
|
ProductVariantPrice.displayName = "ProductVariantPrice";
|
||||||
export default ProductVariantPrice;
|
export default ProductVariantPrice;
|
||||||
|
|
|
@ -8,9 +8,9 @@ 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";
|
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
@ -42,9 +42,17 @@ const ProductVariantStock = withStyles(styles, { name: "ProductVariantStock" })(
|
||||||
stockAllocated,
|
stockAllocated,
|
||||||
loading,
|
loading,
|
||||||
onChange
|
onChange
|
||||||
}: ProductVariantStockProps) => (
|
}: ProductVariantStockProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle title={i18n.t("Stock")} />
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Stock",
|
||||||
|
description: "product variant stock, section header"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className={classes.grid}>
|
<div className={classes.grid}>
|
||||||
<div>
|
<div>
|
||||||
|
@ -52,15 +60,23 @@ const ProductVariantStock = withStyles(styles, { name: "ProductVariantStock" })(
|
||||||
error={!!errors.quantity}
|
error={!!errors.quantity}
|
||||||
name="quantity"
|
name="quantity"
|
||||||
value={quantity}
|
value={quantity}
|
||||||
label={i18n.t("Inventory")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Inventory",
|
||||||
|
description: "product variant stock"
|
||||||
|
})}
|
||||||
helperText={
|
helperText={
|
||||||
errors.quantity
|
errors.quantity
|
||||||
? errors.quantity
|
? errors.quantity
|
||||||
: !!stockAllocated
|
: !!stockAllocated
|
||||||
? i18n.t("Allocated: {{ quantity }}", {
|
? intl.formatMessage(
|
||||||
context: "variant allocated stock",
|
{
|
||||||
|
defaultMessage: "Allocated: {quantity}",
|
||||||
|
description: "variant allocated stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
quantity: stockAllocated
|
quantity: stockAllocated
|
||||||
})
|
}
|
||||||
|
)
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
@ -74,7 +90,9 @@ const ProductVariantStock = withStyles(styles, { name: "ProductVariantStock" })(
|
||||||
helperText={errors.sku}
|
helperText={errors.sku}
|
||||||
name="sku"
|
name="sku"
|
||||||
value={sku}
|
value={sku}
|
||||||
label={i18n.t("SKU (Stock Keeping Unit)")}
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "SKU (Stock Keeping Unit)"
|
||||||
|
})}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
@ -83,7 +101,8 @@ const ProductVariantStock = withStyles(styles, { name: "ProductVariantStock" })(
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
ProductVariantStock.displayName = "ProductVariantStock";
|
ProductVariantStock.displayName = "ProductVariantStock";
|
||||||
export default ProductVariantStock;
|
export default ProductVariantStock;
|
||||||
|
|
|
@ -14,6 +14,7 @@ import TableCell from "@material-ui/core/TableCell";
|
||||||
import TableRow from "@material-ui/core/TableRow";
|
import TableRow from "@material-ui/core/TableRow";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
import Checkbox from "@saleor/components/Checkbox";
|
import Checkbox from "@saleor/components/Checkbox";
|
||||||
|
@ -21,7 +22,6 @@ import Money from "@saleor/components/Money";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import StatusLabel from "@saleor/components/StatusLabel";
|
import StatusLabel from "@saleor/components/StatusLabel";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import i18n from "../../../i18n";
|
|
||||||
import { renderCollection } from "../../../misc";
|
import { renderCollection } from "../../../misc";
|
||||||
import { ListActions } from "../../../types";
|
import { ListActions } from "../../../types";
|
||||||
import { ProductDetails_product_variants } from "../../types/ProductDetails";
|
import { ProductDetails_product_variants } from "../../types/ProductDetails";
|
||||||
|
@ -88,10 +88,16 @@ export const ProductVariants = withStyles(styles, { name: "ProductVariants" })(
|
||||||
toggle,
|
toggle,
|
||||||
toggleAll,
|
toggleAll,
|
||||||
toolbar
|
toolbar
|
||||||
}: ProductVariantsProps) => (
|
}: ProductVariantsProps) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle
|
<CardTitle
|
||||||
title={i18n.t("Variants")}
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Variants",
|
||||||
|
description: "section header"
|
||||||
|
})}
|
||||||
toolbar={
|
toolbar={
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
|
@ -100,7 +106,10 @@ export const ProductVariants = withStyles(styles, { name: "ProductVariants" })(
|
||||||
color="primary"
|
color="primary"
|
||||||
data-tc="button-edit-attributes"
|
data-tc="button-edit-attributes"
|
||||||
>
|
>
|
||||||
{i18n.t("Edit attributes")}
|
<FormattedMessage
|
||||||
|
defaultMessage="Edit attributes"
|
||||||
|
description="product variant attributes, button"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={onVariantAdd}
|
onClick={onVariantAdd}
|
||||||
|
@ -108,16 +117,17 @@ export const ProductVariants = withStyles(styles, { name: "ProductVariants" })(
|
||||||
color="primary"
|
color="primary"
|
||||||
data-tc="button-add-variant"
|
data-tc="button-add-variant"
|
||||||
>
|
>
|
||||||
{i18n.t("Add variant")}
|
<FormattedMessage
|
||||||
|
defaultMessage="Add variant"
|
||||||
|
description="button"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography>
|
<Typography>
|
||||||
{i18n.t(
|
<FormattedMessage defaultMessage="Use variants for products that come in a variety of versions for example different sizes or colors" />
|
||||||
"Use variants for products that come in a variety of versions for example different sizes or colors"
|
|
||||||
)}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<Table className={classes.denseTable}>
|
<Table className={classes.denseTable}>
|
||||||
|
@ -129,14 +139,27 @@ export const ProductVariants = withStyles(styles, { name: "ProductVariants" })(
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.colName}>{i18n.t("Name")}</TableCell>
|
<TableCell className={classes.colName}>
|
||||||
<TableCell className={classes.colStatus}>
|
<FormattedMessage
|
||||||
{i18n.t("Status")}
|
defaultMessage="Name"
|
||||||
|
description="product variant name"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className={classes.colStatus}>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Status"
|
||||||
|
description="product variant status"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className={classes.colSku}>
|
||||||
|
<FormattedMessage defaultMessage="SKU" />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={classes.colSku}>{i18n.t("SKU")}</TableCell>
|
|
||||||
<Hidden smDown>
|
<Hidden smDown>
|
||||||
<TableCell className={classes.colPrice}>
|
<TableCell className={classes.colPrice}>
|
||||||
{i18n.t("Price")}
|
<FormattedMessage
|
||||||
|
defaultMessage="Price"
|
||||||
|
description="product variant price"
|
||||||
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</Hidden>
|
</Hidden>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
|
@ -168,11 +191,19 @@ export const ProductVariants = withStyles(styles, { name: "ProductVariants" })(
|
||||||
<TableCell className={classes.colStatus}>
|
<TableCell className={classes.colStatus}>
|
||||||
{variant ? (
|
{variant ? (
|
||||||
<StatusLabel
|
<StatusLabel
|
||||||
status={variant.stockQuantity > 0 ? "success" : "error"}
|
status={
|
||||||
|
variant.stockQuantity > 0 ? "success" : "error"
|
||||||
|
}
|
||||||
label={
|
label={
|
||||||
variant.stockQuantity > 0
|
variant.stockQuantity > 0
|
||||||
? i18n.t("Available")
|
? intl.formatMessage({
|
||||||
: i18n.t("Unavailable")
|
defaultMessage: "Available",
|
||||||
|
description: "product variant status"
|
||||||
|
})
|
||||||
|
: intl.formatMessage({
|
||||||
|
defaultMessage: "Unavailable",
|
||||||
|
description: "product variant status"
|
||||||
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -203,7 +234,7 @@ export const ProductVariants = withStyles(styles, { name: "ProductVariants" })(
|
||||||
() => (
|
() => (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={numberOfColumns}>
|
<TableCell colSpan={numberOfColumns}>
|
||||||
{i18n.t("This product has no variants")}
|
<FormattedMessage defaultMessage="This product has no variants" />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)
|
)
|
||||||
|
@ -211,7 +242,8 @@ export const ProductVariants = withStyles(styles, { name: "ProductVariants" })(
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
ProductVariants.displayName = "ProductVariants";
|
ProductVariants.displayName = "ProductVariants";
|
||||||
export default ProductVariants;
|
export default ProductVariants;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { parse as parseQs } from "qs";
|
import { parse as parseQs } from "qs";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||||
|
|
||||||
|
import { sectionNames } from "@saleor/intl";
|
||||||
import { WindowTitle } from "../components/WindowTitle";
|
import { WindowTitle } from "../components/WindowTitle";
|
||||||
import i18n from "../i18n";
|
|
||||||
import {
|
import {
|
||||||
productAddPath,
|
productAddPath,
|
||||||
productImagePath,
|
productImagePath,
|
||||||
|
@ -86,9 +87,12 @@ const ProductVariantCreate: React.StatelessComponent<
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Component = () => (
|
const Component = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
<>
|
<>
|
||||||
<WindowTitle title={i18n.t("Products")} />
|
<WindowTitle title={intl.formatMessage(sectionNames.products)} />
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path={productListPath} component={ProductList} />
|
<Route exact path={productListPath} component={ProductList} />
|
||||||
<Route exact path={productAddPath} component={ProductCreate} />
|
<Route exact path={productAddPath} component={ProductCreate} />
|
||||||
|
@ -109,5 +113,6 @@ const Component = () => (
|
||||||
</Switch>
|
</Switch>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Component;
|
export default Component;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
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";
|
||||||
|
@ -7,7 +8,6 @@ import useShop from "@saleor/hooks/useShop";
|
||||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../config";
|
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../config";
|
||||||
import SearchCategories from "../../containers/SearchCategories";
|
import SearchCategories from "../../containers/SearchCategories";
|
||||||
import SearchCollections from "../../containers/SearchCollections";
|
import SearchCollections from "../../containers/SearchCollections";
|
||||||
import i18n from "../../i18n";
|
|
||||||
import { decimal, getMutationState, maybe } from "../../misc";
|
import { decimal, getMutationState, maybe } from "../../misc";
|
||||||
import ProductCreatePage, {
|
import ProductCreatePage, {
|
||||||
ProductCreatePageSubmitData
|
ProductCreatePageSubmitData
|
||||||
|
@ -27,6 +27,7 @@ export const ProductUpdate: React.StatelessComponent<
|
||||||
const navigate = useNavigator();
|
const navigate = useNavigator();
|
||||||
const notify = useNotifier();
|
const notify = useNotifier();
|
||||||
const shop = useShop();
|
const shop = useShop();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
const handleAttributesEdit = undefined;
|
const handleAttributesEdit = undefined;
|
||||||
const handleBack = () => navigate(productListUrl());
|
const handleBack = () => navigate(productListUrl());
|
||||||
|
@ -41,7 +42,9 @@ export const ProductUpdate: React.StatelessComponent<
|
||||||
const handleSuccess = (data: ProductCreate) => {
|
const handleSuccess = (data: ProductCreate) => {
|
||||||
if (data.productCreate.errors.length === 0) {
|
if (data.productCreate.errors.length === 0) {
|
||||||
notify({
|
notify({
|
||||||
text: i18n.t("Product created")
|
text: intl.formatMessage({
|
||||||
|
defaultMessage: "Product created"
|
||||||
|
})
|
||||||
});
|
});
|
||||||
navigate(productUrl(data.productCreate.product.id));
|
navigate(productUrl(data.productCreate.product.id));
|
||||||
} else {
|
} else {
|
||||||
|
@ -109,7 +112,12 @@ export const ProductUpdate: React.StatelessComponent<
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<WindowTitle title={i18n.t("Create product")} />
|
<WindowTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Create Product",
|
||||||
|
description: "window title"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<ProductCreatePage
|
<ProductCreatePage
|
||||||
currency={maybe(() => shop.defaultCurrency)}
|
currency={maybe(() => shop.defaultCurrency)}
|
||||||
categories={maybe(
|
categories={maybe(
|
||||||
|
@ -127,7 +135,10 @@ export const ProductUpdate: React.StatelessComponent<
|
||||||
)}
|
)}
|
||||||
fetchCategories={searchCategory}
|
fetchCategories={searchCategory}
|
||||||
fetchCollections={searchCollection}
|
fetchCollections={searchCollection}
|
||||||
header={i18n.t("New Product")}
|
header={intl.formatMessage({
|
||||||
|
defaultMessage: "New Product",
|
||||||
|
description: "page header"
|
||||||
|
})}
|
||||||
productTypes={maybe(() =>
|
productTypes={maybe(() =>
|
||||||
data.productTypes.edges.map(edge => edge.node)
|
data.productTypes.edges.map(edge => edge.node)
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
import 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 ProductImagePage from "../components/ProductImagePage";
|
import ProductImagePage from "../components/ProductImagePage";
|
||||||
import {
|
import {
|
||||||
|
@ -32,6 +32,7 @@ export const ProductImage: React.StatelessComponent<ProductImageProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const navigate = useNavigator();
|
const navigate = useNavigator();
|
||||||
const notify = useNotifier();
|
const notify = useNotifier();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
const handleBack = () => navigate(productUrl(productId));
|
const handleBack = () => navigate(productUrl(productId));
|
||||||
const handleUpdateSuccess = (data: ProductImageUpdate) => {
|
const handleUpdateSuccess = (data: ProductImageUpdate) => {
|
||||||
|
@ -103,19 +104,15 @@ export const ProductImage: React.StatelessComponent<ProductImageProps> = ({
|
||||||
}
|
}
|
||||||
onConfirm={handleDelete}
|
onConfirm={handleDelete}
|
||||||
open={params.action === "remove"}
|
open={params.action === "remove"}
|
||||||
title={i18n.t("Remove image", {
|
title={intl.formatMessage({
|
||||||
context: "modal title"
|
defaultMessage: "Remove Image",
|
||||||
|
description: "dialog header"
|
||||||
})}
|
})}
|
||||||
variant="delete"
|
variant="delete"
|
||||||
confirmButtonState={deleteTransitionState}
|
confirmButtonState={deleteTransitionState}
|
||||||
>
|
>
|
||||||
<DialogContentText>
|
<DialogContentText>
|
||||||
{i18n.t(
|
<FormattedMessage defaultMessage="Are you sure you want to remove this image?" />
|
||||||
"Are you sure you want to remove this image?",
|
|
||||||
{
|
|
||||||
context: "modal content"
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
</ActionDialog>
|
</ActionDialog>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -3,6 +3,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 DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
|
||||||
|
@ -19,7 +20,6 @@ import usePaginator, {
|
||||||
createPaginationState
|
createPaginationState
|
||||||
} from "@saleor/hooks/usePaginator";
|
} from "@saleor/hooks/usePaginator";
|
||||||
import useShop from "@saleor/hooks/useShop";
|
import useShop from "@saleor/hooks/useShop";
|
||||||
import i18n from "@saleor/i18n";
|
|
||||||
import { getMutationState, maybe } from "@saleor/misc";
|
import { getMutationState, maybe } from "@saleor/misc";
|
||||||
import { ListViews } from "@saleor/types";
|
import { ListViews } from "@saleor/types";
|
||||||
import ProductListPage from "../../components/ProductListPage";
|
import ProductListPage from "../../components/ProductListPage";
|
||||||
|
@ -67,6 +67,8 @@ export const ProductList: React.StatelessComponent<ProductListProps> = ({
|
||||||
const { updateListSettings, settings } = useListSettings<ProductListColumns>(
|
const { updateListSettings, settings } = useListSettings<ProductListColumns>(
|
||||||
ListViews.PRODUCT_LIST
|
ListViews.PRODUCT_LIST
|
||||||
);
|
);
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
const tabs = getFilterTabs();
|
const tabs = getFilterTabs();
|
||||||
|
|
||||||
const currentTab =
|
const currentTab =
|
||||||
|
@ -155,7 +157,9 @@ export const ProductList: React.StatelessComponent<ProductListProps> = ({
|
||||||
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"
|
||||||
|
})
|
||||||
});
|
});
|
||||||
reset();
|
reset();
|
||||||
refetch();
|
refetch();
|
||||||
|
@ -166,7 +170,10 @@ export const ProductList: React.StatelessComponent<ProductListProps> = ({
|
||||||
if (data.productBulkPublish.errors.length === 0) {
|
if (data.productBulkPublish.errors.length === 0) {
|
||||||
closeModal();
|
closeModal();
|
||||||
notify({
|
notify({
|
||||||
text: i18n.t("Changed publication status")
|
text: intl.formatMessage({
|
||||||
|
defaultMessage: "Changed publication status",
|
||||||
|
description: "product status update notification"
|
||||||
|
})
|
||||||
});
|
});
|
||||||
reset();
|
reset();
|
||||||
refetch();
|
refetch();
|
||||||
|
@ -235,13 +242,19 @@ export const ProductList: React.StatelessComponent<ProductListProps> = ({
|
||||||
openModal("unpublish", listElements)
|
openModal("unpublish", listElements)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{i18n.t("Unpublish")}
|
<FormattedMessage
|
||||||
|
defaultMessage="Unpublish"
|
||||||
|
description="unpublish product, button"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => openModal("publish", listElements)}
|
onClick={() => openModal("publish", listElements)}
|
||||||
>
|
>
|
||||||
{i18n.t("Publish")}
|
<FormattedMessage
|
||||||
|
defaultMessage="Publish"
|
||||||
|
description="publish product, button"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
<IconButton
|
<IconButton
|
||||||
color="primary"
|
color="primary"
|
||||||
|
@ -274,22 +287,29 @@ export const ProductList: React.StatelessComponent<ProductListProps> = ({
|
||||||
variables: { ids: params.ids }
|
variables: { ids: params.ids }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
title={i18n.t("Remove products")}
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Delete Products",
|
||||||
|
description: "dialog header"
|
||||||
|
})}
|
||||||
variant="delete"
|
variant="delete"
|
||||||
>
|
>
|
||||||
<DialogContentText
|
<DialogContentText>
|
||||||
dangerouslySetInnerHTML={{
|
<FormattedMessage
|
||||||
__html: i18n.t(
|
defaultMessage="Are you sure you want to delete {counter, plural,
|
||||||
"Are you sure you want to remove <strong>{{ number }}</strong> products?",
|
one {this product}
|
||||||
{
|
other {{displayQuantity} products}
|
||||||
number: maybe(
|
}?"
|
||||||
() => params.ids.length.toString(),
|
description="dialog content"
|
||||||
"..."
|
values={{
|
||||||
)
|
counter: maybe(() => params.ids.length),
|
||||||
}
|
displayQuantity: (
|
||||||
|
<strong>
|
||||||
|
{maybe(() => params.ids.length)}
|
||||||
|
</strong>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</DialogContentText>
|
||||||
</ActionDialog>
|
</ActionDialog>
|
||||||
<ActionDialog
|
<ActionDialog
|
||||||
open={params.action === "publish"}
|
open={params.action === "publish"}
|
||||||
|
@ -303,21 +323,28 @@ export const ProductList: React.StatelessComponent<ProductListProps> = ({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
title={i18n.t("Publish products")}
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Publish Products",
|
||||||
|
description: "dialog header"
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<DialogContentText
|
<DialogContentText>
|
||||||
dangerouslySetInnerHTML={{
|
<FormattedMessage
|
||||||
__html: i18n.t(
|
defaultMessage="Are you sure you want to publish {counter, plural,
|
||||||
"Are you sure you want to publish <strong>{{ number }}</strong> products?",
|
one {this product}
|
||||||
{
|
other {{displayQuantity} products}
|
||||||
number: maybe(
|
}?"
|
||||||
() => params.ids.length.toString(),
|
description="dialog content"
|
||||||
"..."
|
values={{
|
||||||
)
|
counter: maybe(() => params.ids.length),
|
||||||
}
|
displayQuantity: (
|
||||||
|
<strong>
|
||||||
|
{maybe(() => params.ids.length)}
|
||||||
|
</strong>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</DialogContentText>
|
||||||
</ActionDialog>
|
</ActionDialog>
|
||||||
<ActionDialog
|
<ActionDialog
|
||||||
open={params.action === "unpublish"}
|
open={params.action === "unpublish"}
|
||||||
|
@ -331,21 +358,28 @@ export const ProductList: React.StatelessComponent<ProductListProps> = ({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
title={i18n.t("Unpublish products")}
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Unpublish Products",
|
||||||
|
description: "dialog header"
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<DialogContentText
|
<DialogContentText>
|
||||||
dangerouslySetInnerHTML={{
|
<FormattedMessage
|
||||||
__html: i18n.t(
|
defaultMessage="Are you sure you want to unpublish {counter, plural,
|
||||||
"Are you sure you want to unpublish <strong>{{ number }}</strong> products?",
|
one {this product}
|
||||||
{
|
other {{displayQuantity} products}
|
||||||
number: maybe(
|
}?"
|
||||||
() => params.ids.length.toString(),
|
description="dialog content"
|
||||||
"..."
|
values={{
|
||||||
)
|
counter: maybe(() => params.ids.length),
|
||||||
}
|
displayQuantity: (
|
||||||
|
<strong>
|
||||||
|
{maybe(() => params.ids.length)}
|
||||||
|
</strong>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</DialogContentText>
|
||||||
</ActionDialog>
|
</ActionDialog>
|
||||||
<SaveFilterTabDialog
|
<SaveFilterTabDialog
|
||||||
open={params.action === "save-search"}
|
open={params.action === "save-search"}
|
||||||
|
|
|
@ -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 placeholderImg from "@assets/images/placeholder255x255.png";
|
import placeholderImg from "@assets/images/placeholder255x255.png";
|
||||||
import ActionDialog from "@saleor/components/ActionDialog";
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
|
@ -9,10 +10,10 @@ import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
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 { commonMessages } from "@saleor/intl";
|
||||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config";
|
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config";
|
||||||
import SearchCategories from "../../../containers/SearchCategories";
|
import SearchCategories from "../../../containers/SearchCategories";
|
||||||
import SearchCollections from "../../../containers/SearchCollections";
|
import SearchCollections from "../../../containers/SearchCollections";
|
||||||
import i18n from "../../../i18n";
|
|
||||||
import { getMutationState, maybe } from "../../../misc";
|
import { getMutationState, maybe } from "../../../misc";
|
||||||
import { productTypeUrl } from "../../../productTypes/urls";
|
import { productTypeUrl } from "../../../productTypes/urls";
|
||||||
import ProductUpdatePage from "../../components/ProductUpdatePage";
|
import ProductUpdatePage from "../../components/ProductUpdatePage";
|
||||||
|
@ -53,6 +54,7 @@ export const ProductUpdate: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
|
||||||
params.ids
|
params.ids
|
||||||
);
|
);
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
const openModal = (action: ProductUrlDialog) =>
|
const openModal = (action: ProductUrlDialog) =>
|
||||||
navigate(
|
navigate(
|
||||||
|
@ -73,12 +75,18 @@ export const ProductUpdate: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
>
|
>
|
||||||
{({ data, loading, refetch }) => {
|
{({ data, loading, refetch }) => {
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
notify({ text: i18n.t("Product removed") });
|
notify({
|
||||||
|
text: intl.formatMessage({
|
||||||
|
defaultMessage: "Product removed"
|
||||||
|
})
|
||||||
|
});
|
||||||
navigate(productListUrl());
|
navigate(productListUrl());
|
||||||
};
|
};
|
||||||
const handleUpdate = (data: ProductUpdateMutationResult) => {
|
const handleUpdate = (data: ProductUpdateMutationResult) => {
|
||||||
if (data.productUpdate.errors.length === 0) {
|
if (data.productUpdate.errors.length === 0) {
|
||||||
notify({ text: i18n.t("Saved changes") });
|
notify({
|
||||||
|
text: intl.formatMessage(commonMessages.savedChanges)
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const attributeError = data.productUpdate.errors.find(
|
const attributeError = data.productUpdate.errors.find(
|
||||||
err => err.field === "attributes"
|
err => err.field === "attributes"
|
||||||
|
@ -103,7 +111,7 @@ export const ProductUpdate: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
};
|
};
|
||||||
const handleImageDeleteSuccess = () =>
|
const handleImageDeleteSuccess = () =>
|
||||||
notify({
|
notify({
|
||||||
text: i18n.t("Image successfully deleted")
|
text: intl.formatMessage(commonMessages.savedChanges)
|
||||||
});
|
});
|
||||||
const handleVariantAdd = () =>
|
const handleVariantAdd = () =>
|
||||||
navigate(productVariantAddUrl(id));
|
navigate(productVariantAddUrl(id));
|
||||||
|
@ -276,18 +284,20 @@ export const ProductUpdate: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
confirmButtonState={deleteTransitionState}
|
confirmButtonState={deleteTransitionState}
|
||||||
onConfirm={() => deleteProduct.mutate({ id })}
|
onConfirm={() => deleteProduct.mutate({ id })}
|
||||||
variant="delete"
|
variant="delete"
|
||||||
title={i18n.t("Remove product")}
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Delete Product",
|
||||||
|
description: "dialog header"
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<DialogContentText
|
<DialogContentText>
|
||||||
dangerouslySetInnerHTML={{
|
<FormattedMessage
|
||||||
__html: i18n.t(
|
defaultMessage="Are you sure you want to delete {name}?"
|
||||||
"Are you sure you want to remove <strong>{{ name }}</strong>?",
|
description="delete product"
|
||||||
{
|
values={{
|
||||||
name: product ? product.name : undefined
|
name: product ? product.name : undefined
|
||||||
}
|
|
||||||
)
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</DialogContentText>
|
||||||
</ActionDialog>
|
</ActionDialog>
|
||||||
<ActionDialog
|
<ActionDialog
|
||||||
open={params.action === "remove-variants"}
|
open={params.action === "remove-variants"}
|
||||||
|
@ -301,21 +311,28 @@ export const ProductUpdate: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
variant="delete"
|
variant="delete"
|
||||||
title={i18n.t("Remove product variants")}
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Delete Product Variants",
|
||||||
|
description: "dialog header"
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<DialogContentText
|
<DialogContentText>
|
||||||
dangerouslySetInnerHTML={{
|
<FormattedMessage
|
||||||
__html: i18n.t(
|
defaultMessage="Are you sure you want to delete {counter, plural,
|
||||||
"Are you sure you want to remove <strong>{{ number }}</strong> variants?",
|
one {this variant}
|
||||||
{
|
other {{displayQuantity} variants}
|
||||||
number: maybe(
|
}?"
|
||||||
() => params.ids.length.toString(),
|
description="dialog content"
|
||||||
"..."
|
values={{
|
||||||
)
|
counter: maybe(() => params.ids.length),
|
||||||
}
|
displayQuantity: (
|
||||||
|
<strong>
|
||||||
|
{maybe(() => params.ids.length)}
|
||||||
|
</strong>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</DialogContentText>
|
||||||
</ActionDialog>
|
</ActionDialog>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import placeholderImg from "@assets/images/placeholder255x255.png";
|
import placeholderImg from "@assets/images/placeholder255x255.png";
|
||||||
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 { commonMessages } from "@saleor/intl";
|
||||||
import { decimal, getMutationState, maybe } from "../../misc";
|
import { decimal, getMutationState, maybe } from "../../misc";
|
||||||
import ProductVariantDeleteDialog from "../components/ProductVariantDeleteDialog";
|
import ProductVariantDeleteDialog from "../components/ProductVariantDeleteDialog";
|
||||||
import ProductVariantPage, {
|
import ProductVariantPage, {
|
||||||
|
@ -33,6 +34,7 @@ export const ProductVariant: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const navigate = useNavigator();
|
const navigate = useNavigator();
|
||||||
const notify = useNotifier();
|
const notify = useNotifier();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TypedProductVariantQuery
|
<TypedProductVariantQuery
|
||||||
|
@ -44,12 +46,16 @@ export const ProductVariant: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
const variant = data ? data.productVariant : undefined;
|
const variant = data ? data.productVariant : undefined;
|
||||||
const handleBack = () => navigate(productUrl(productId));
|
const handleBack = () => navigate(productUrl(productId));
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
notify({ text: i18n.t("Variant removed") });
|
notify({
|
||||||
|
text: intl.formatMessage({
|
||||||
|
defaultMessage: "Variant removed"
|
||||||
|
})
|
||||||
|
});
|
||||||
navigate(productUrl(productId));
|
navigate(productUrl(productId));
|
||||||
};
|
};
|
||||||
const handleUpdate = (data: VariantUpdate) => {
|
const handleUpdate = (data: VariantUpdate) => {
|
||||||
if (!maybe(() => data.productVariantUpdate.errors.length)) {
|
if (!maybe(() => data.productVariantUpdate.errors.length)) {
|
||||||
notify({ text: i18n.t("Changes saved") });
|
notify({ text: intl.formatMessage(commonMessages.savedChanges) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
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 { decimal, getMutationState, maybe } from "../../misc";
|
import { decimal, getMutationState, maybe } from "../../misc";
|
||||||
import ProductVariantCreatePage, {
|
import ProductVariantCreatePage, {
|
||||||
ProductVariantCreatePageSubmitData
|
ProductVariantCreatePageSubmitData
|
||||||
|
@ -24,6 +24,7 @@ export const ProductVariant: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
const navigate = useNavigator();
|
const navigate = useNavigator();
|
||||||
const notify = useNotifier();
|
const notify = useNotifier();
|
||||||
const shop = useShop();
|
const shop = useShop();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TypedProductVariantCreateQuery
|
<TypedProductVariantCreateQuery
|
||||||
|
@ -34,7 +35,11 @@ export const ProductVariant: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
{({ data, loading: productLoading }) => {
|
{({ data, loading: productLoading }) => {
|
||||||
const handleCreateSuccess = (data: VariantCreate) => {
|
const handleCreateSuccess = (data: VariantCreate) => {
|
||||||
if (data.productVariantCreate.errors.length === 0) {
|
if (data.productVariantCreate.errors.length === 0) {
|
||||||
notify({ text: i18n.t("Product created") });
|
notify({
|
||||||
|
text: intl.formatMessage({
|
||||||
|
defaultMessage: "Product created"
|
||||||
|
})
|
||||||
|
});
|
||||||
navigate(
|
navigate(
|
||||||
productVariantEditUrl(
|
productVariantEditUrl(
|
||||||
productId,
|
productId,
|
||||||
|
@ -81,7 +86,12 @@ export const ProductVariant: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<WindowTitle title={i18n.t("Create variant")} />
|
<WindowTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Create Variant",
|
||||||
|
description: "window title"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<ProductVariantCreatePage
|
<ProductVariantCreatePage
|
||||||
currencySymbol={maybe(() => shop.defaultCurrency)}
|
currencySymbol={maybe(() => shop.defaultCurrency)}
|
||||||
errors={maybe(
|
errors={maybe(
|
||||||
|
@ -89,7 +99,10 @@ export const ProductVariant: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
variantCreateResult.data.productVariantCreate.errors,
|
variantCreateResult.data.productVariantCreate.errors,
|
||||||
[]
|
[]
|
||||||
)}
|
)}
|
||||||
header={i18n.t("Add Variant")}
|
header={intl.formatMessage({
|
||||||
|
defaultMessage: "Add Variant",
|
||||||
|
description: "header"
|
||||||
|
})}
|
||||||
loading={disableForm}
|
loading={disableForm}
|
||||||
product={maybe(() => data.product)}
|
product={maybe(() => data.product)}
|
||||||
onBack={handleBack}
|
onBack={handleBack}
|
||||||
|
|
Loading…
Reference in a new issue