1425 - Support reference type attribute on attribute details page (#918)
* Support reference type attribute on attribute details page * Trigger CI * Prevent changing attribute entity type during attribute update * Refactor attribute details components
This commit is contained in:
parent
ee05b090b8
commit
8b0fe986b2
17 changed files with 650 additions and 480 deletions
|
@ -733,37 +733,49 @@
|
|||
"context": "dialog content",
|
||||
"string": "Are you sure you want to delete {attributeName}?"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_1005562666": {
|
||||
"context": "attribute's editor component",
|
||||
"string": "Catalog Input type for Store Owner"
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_attributeLabel": {
|
||||
"context": "attribute's label",
|
||||
"string": "Default Label"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_1336738461": {
|
||||
"context": "product attribute type",
|
||||
"string": "Dropdown"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_1376373679": {
|
||||
"context": "file attribute type",
|
||||
"string": "File"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_2592224946": {
|
||||
"context": "check to require attribute to have value",
|
||||
"string": "Value Required"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_3334509011": {
|
||||
"context": "product attribute type",
|
||||
"string": "Multiple Select"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_3605174225": {
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_attributeSlug": {
|
||||
"context": "attribute's slug short code label",
|
||||
"string": "Attribute Code"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_4107478955": {
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_attributeSlugHelperText": {
|
||||
"context": "attribute slug input field helper text",
|
||||
"string": "This is used internally. Make sure you don’t use spaces"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_691600601": {
|
||||
"context": "attribute's label",
|
||||
"string": "Default Label"
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_dropdown": {
|
||||
"context": "product attribute type",
|
||||
"string": "Dropdown"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_entityType": {
|
||||
"context": "attribute's editor component entity",
|
||||
"string": "Entity"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_file": {
|
||||
"context": "file attribute type",
|
||||
"string": "File"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_inputType": {
|
||||
"context": "attribute's editor component",
|
||||
"string": "Catalog Input type for Store Owner"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_multiselect": {
|
||||
"context": "product attribute type",
|
||||
"string": "Multiple Select"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_page": {
|
||||
"context": "page attribute entity type",
|
||||
"string": "Pages"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_references": {
|
||||
"context": "references attribute type",
|
||||
"string": "References"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeDetails_dot_valueRequired": {
|
||||
"context": "check to require attribute to have value",
|
||||
"string": "Value Required"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeListPage_dot_2417065806": {
|
||||
"context": "tab name",
|
||||
|
@ -844,43 +856,46 @@
|
|||
"context": "page title",
|
||||
"string": "Create New Attribute"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_1318123158": {
|
||||
"context": "attribute is filterable in storefront",
|
||||
"string": "Use in Faceted Navigation"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_1877630205": {
|
||||
"context": "attribute properties regarding storefront",
|
||||
"string": "Storefront Properties"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_26409543": {
|
||||
"context": "attribute properties regarding dashboard",
|
||||
"string": "Dashboard Properties"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_3135366329": {
|
||||
"context": "attribute visibility in storefront",
|
||||
"string": "Public"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_3590282519": {
|
||||
"context": "attribute position in storefront filters",
|
||||
"string": "Position in faceted navigation"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_3758203740": {
|
||||
"string": "If enabled, attribute will be accessible to customers."
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_4048785456": {
|
||||
"string": "If enabled this attribute can be used as a column in product table."
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_673770329": {
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_availableInGrid": {
|
||||
"context": "add attribute as column in product list table",
|
||||
"string": "Add to Column Options"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_714335445": {
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_availableInGridCaption": {
|
||||
"context": "caption",
|
||||
"string": "If enabled this attribute can be used as a column in product table."
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_dashboardPropertiesTitle": {
|
||||
"context": "attribute properties regarding dashboard",
|
||||
"string": "Dashboard Properties"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_filterableInDashboard": {
|
||||
"context": "use attribute in filtering",
|
||||
"string": "Use in Filtering"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_787251583": {
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_filterableInDashboardCaption": {
|
||||
"context": "caption",
|
||||
"string": "If enabled, you’ll be able to use this attribute to filter products in product list."
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_filterableInStorefront": {
|
||||
"context": "attribute is filterable in storefront",
|
||||
"string": "Use in Faceted Navigation"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_storefrontPropertiesTitle": {
|
||||
"context": "attribute properties regarding storefront",
|
||||
"string": "Storefront Properties"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_storefrontSearchPosition": {
|
||||
"context": "attribute position in storefront filters",
|
||||
"string": "Position in faceted navigation"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_visibleInStorefront": {
|
||||
"context": "attribute visibility in storefront",
|
||||
"string": "Public"
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeProperties_dot_visibleInStorefrontCaption": {
|
||||
"context": "caption",
|
||||
"string": "If enabled, attribute will be accessible to customers."
|
||||
},
|
||||
"src_dot_attributes_dot_components_dot_AttributeValueDeleteDialog_dot_1326420604": {
|
||||
"context": "delete attribute value",
|
||||
"string": "Are you sure you want to delete \"{name}\" value?"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Card from "@material-ui/core/Card";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
||||
|
@ -7,16 +8,86 @@ import FormSpacer from "@saleor/components/FormSpacer";
|
|||
import SingleSelectField from "@saleor/components/SingleSelectField";
|
||||
import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import { AttributeInputTypeEnum } from "@saleor/types/globalTypes";
|
||||
import {
|
||||
AttributeEntityTypeEnum,
|
||||
AttributeInputTypeEnum
|
||||
} from "@saleor/types/globalTypes";
|
||||
import { getFormErrors } from "@saleor/utils/errors";
|
||||
import getAttributeErrorMessage from "@saleor/utils/errors/attribute";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import { defineMessages, useIntl } from "react-intl";
|
||||
import slugify from "slugify";
|
||||
|
||||
import { getAttributeSlugErrorMessage } from "../../errors";
|
||||
import { AttributePageFormData } from "../AttributePage";
|
||||
|
||||
const messages = defineMessages({
|
||||
attributeLabel: {
|
||||
defaultMessage: "Default Label",
|
||||
description: "attribute's label"
|
||||
},
|
||||
attributeSlug: {
|
||||
defaultMessage: "Attribute Code",
|
||||
description: "attribute's slug short code label"
|
||||
},
|
||||
attributeSlugHelperText: {
|
||||
defaultMessage: "This is used internally. Make sure you don’t use spaces",
|
||||
description: "attribute slug input field helper text"
|
||||
},
|
||||
entityType: {
|
||||
defaultMessage: "Entity",
|
||||
description: "attribute's editor component entity"
|
||||
},
|
||||
inputType: {
|
||||
defaultMessage: "Catalog Input type for Store Owner",
|
||||
description: "attribute's editor component"
|
||||
},
|
||||
valueRequired: {
|
||||
defaultMessage: "Value Required",
|
||||
description: "check to require attribute to have value"
|
||||
}
|
||||
});
|
||||
|
||||
const inputTypeMessages = defineMessages({
|
||||
dropdown: {
|
||||
defaultMessage: "Dropdown",
|
||||
description: "product attribute type"
|
||||
},
|
||||
file: {
|
||||
defaultMessage: "File",
|
||||
description: "file attribute type"
|
||||
},
|
||||
multiselect: {
|
||||
defaultMessage: "Multiple Select",
|
||||
description: "product attribute type"
|
||||
},
|
||||
references: {
|
||||
defaultMessage: "References",
|
||||
description: "references attribute type"
|
||||
}
|
||||
});
|
||||
|
||||
const entityTypeMessages = defineMessages({
|
||||
page: {
|
||||
defaultMessage: "Pages",
|
||||
description: "page attribute entity type"
|
||||
}
|
||||
});
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
inputTypeSection: {
|
||||
columnGap: theme.spacing(2) + "px",
|
||||
display: "flex",
|
||||
[theme.breakpoints.down("md")]: {
|
||||
flexFlow: "wrap",
|
||||
rowGap: theme.spacing(3) + "px"
|
||||
}
|
||||
}
|
||||
}),
|
||||
{ name: "AttributeDetails" }
|
||||
);
|
||||
|
||||
export interface AttributeDetailsProps {
|
||||
canChangeType: boolean;
|
||||
data: AttributePageFormData;
|
||||
|
@ -25,39 +96,39 @@ export interface AttributeDetailsProps {
|
|||
onChange: (event: React.ChangeEvent<any>) => void;
|
||||
}
|
||||
|
||||
const AttributeDetails: React.FC<AttributeDetailsProps> = ({
|
||||
canChangeType,
|
||||
data,
|
||||
disabled,
|
||||
errors,
|
||||
onChange
|
||||
}) => {
|
||||
const AttributeDetails: React.FC<AttributeDetailsProps> = props => {
|
||||
const { canChangeType, data, disabled, errors, onChange } = props;
|
||||
const classes = useStyles(props);
|
||||
const intl = useIntl();
|
||||
const inputTypeChoices = [
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
defaultMessage: "Dropdown",
|
||||
description: "product attribute type"
|
||||
}),
|
||||
label: intl.formatMessage(inputTypeMessages.dropdown),
|
||||
value: AttributeInputTypeEnum.DROPDOWN
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
defaultMessage: "Multiple Select",
|
||||
description: "product attribute type"
|
||||
}),
|
||||
label: intl.formatMessage(inputTypeMessages.multiselect),
|
||||
value: AttributeInputTypeEnum.MULTISELECT
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
defaultMessage: "File",
|
||||
description: "file attribute type"
|
||||
}),
|
||||
label: intl.formatMessage(inputTypeMessages.file),
|
||||
value: AttributeInputTypeEnum.FILE
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage(inputTypeMessages.references),
|
||||
value: AttributeInputTypeEnum.REFERENCE
|
||||
}
|
||||
];
|
||||
const entityTypeChoices = [
|
||||
{
|
||||
label: intl.formatMessage(entityTypeMessages.page),
|
||||
value: AttributeEntityTypeEnum.PAGE
|
||||
}
|
||||
];
|
||||
|
||||
const formErrors = getFormErrors(["name", "slug", "inputType"], errors);
|
||||
const formErrors = getFormErrors(
|
||||
["name", "slug", "inputType", "entityType"],
|
||||
errors
|
||||
);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
|
@ -68,10 +139,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
|
|||
<TextField
|
||||
disabled={disabled}
|
||||
error={!!formErrors.name}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Default Label",
|
||||
description: "attribute's label"
|
||||
})}
|
||||
label={intl.formatMessage(messages.attributeLabel)}
|
||||
name={"name" as keyof AttributePageFormData}
|
||||
fullWidth
|
||||
helperText={getAttributeErrorMessage(formErrors.name, intl)}
|
||||
|
@ -82,45 +150,46 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
|
|||
<TextField
|
||||
disabled={disabled}
|
||||
error={!!formErrors.slug}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Attribute Code",
|
||||
description: "attribute's slug short code label"
|
||||
})}
|
||||
label={intl.formatMessage(messages.attributeSlug)}
|
||||
name={"slug" as keyof AttributePageFormData}
|
||||
placeholder={slugify(data.name).toLowerCase()}
|
||||
fullWidth
|
||||
helperText={
|
||||
getAttributeSlugErrorMessage(formErrors.slug, intl) ||
|
||||
intl.formatMessage({
|
||||
defaultMessage:
|
||||
"This is used internally. Make sure you don’t use spaces",
|
||||
description: "attribute slug input field helper text"
|
||||
})
|
||||
intl.formatMessage(messages.attributeSlugHelperText)
|
||||
}
|
||||
value={data.slug}
|
||||
onChange={onChange}
|
||||
/>
|
||||
<FormSpacer />
|
||||
<div className={classes.inputTypeSection}>
|
||||
<SingleSelectField
|
||||
choices={inputTypeChoices}
|
||||
disabled={disabled || !canChangeType}
|
||||
error={!!formErrors.inputType}
|
||||
hint={getAttributeErrorMessage(formErrors.inputType, intl)}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Catalog Input type for Store Owner",
|
||||
description: "attribute's editor component"
|
||||
})}
|
||||
label={intl.formatMessage(messages.inputType)}
|
||||
name="inputType"
|
||||
onChange={onChange}
|
||||
value={data.inputType}
|
||||
/>
|
||||
{data.inputType === AttributeInputTypeEnum.REFERENCE && (
|
||||
<SingleSelectField
|
||||
choices={entityTypeChoices}
|
||||
disabled={disabled || !canChangeType}
|
||||
error={!!formErrors.entityType}
|
||||
hint={getAttributeErrorMessage(formErrors.entityType, intl)}
|
||||
label={intl.formatMessage(messages.entityType)}
|
||||
name="entityType"
|
||||
onChange={onChange}
|
||||
value={data.entityType}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<FormSpacer />
|
||||
<ControlledCheckbox
|
||||
name={"valueRequired" as keyof AttributePageFormData}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Value Required",
|
||||
description: "check to require attribute to have value"
|
||||
})}
|
||||
label={intl.formatMessage(messages.valueRequired)}
|
||||
checked={data.valueRequired}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES } from "@saleor/attributes/utils/data";
|
||||
import AppHeader from "@saleor/components/AppHeader";
|
||||
import CardSpacer from "@saleor/components/CardSpacer";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
||||
|
@ -17,6 +18,7 @@ import { sectionNames } from "@saleor/intl";
|
|||
import { maybe } from "@saleor/misc";
|
||||
import { ReorderAction } from "@saleor/types";
|
||||
import {
|
||||
AttributeEntityTypeEnum,
|
||||
AttributeInputTypeEnum,
|
||||
AttributeTypeEnum
|
||||
} from "@saleor/types/globalTypes";
|
||||
|
@ -51,6 +53,7 @@ export interface AttributePageFormData extends MetadataFormData {
|
|||
availableInGrid: boolean;
|
||||
filterableInDashboard: boolean;
|
||||
inputType: AttributeInputTypeEnum;
|
||||
entityType: AttributeEntityTypeEnum;
|
||||
filterableInStorefront: boolean;
|
||||
name: string;
|
||||
slug: string;
|
||||
|
@ -84,6 +87,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
|
|||
attribute === null
|
||||
? {
|
||||
availableInGrid: true,
|
||||
entityType: null,
|
||||
filterableInDashboard: true,
|
||||
filterableInStorefront: true,
|
||||
inputType: AttributeInputTypeEnum.DROPDOWN,
|
||||
|
@ -98,6 +102,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
|
|||
}
|
||||
: {
|
||||
availableInGrid: maybe(() => attribute.availableInGrid, true),
|
||||
entityType: attribute?.entityType ?? null,
|
||||
filterableInDashboard: maybe(
|
||||
() => attribute.filterableInDashboard,
|
||||
true
|
||||
|
@ -172,7 +177,9 @@ const AttributePage: React.FC<AttributePageProps> = ({
|
|||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
{data.inputType !== AttributeInputTypeEnum.FILE && (
|
||||
{ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes(
|
||||
data.inputType
|
||||
) && (
|
||||
<>
|
||||
<CardSpacer />
|
||||
<AttributeValues
|
||||
|
|
|
@ -2,6 +2,7 @@ import Card from "@material-ui/core/Card";
|
|||
import CardContent from "@material-ui/core/CardContent";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES } from "@saleor/attributes/utils/data";
|
||||
import CardSpacer from "@saleor/components/CardSpacer";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
||||
|
@ -10,17 +11,59 @@ import FormSpacer from "@saleor/components/FormSpacer";
|
|||
import Hr from "@saleor/components/Hr";
|
||||
import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import {
|
||||
AttributeInputTypeEnum,
|
||||
AttributeTypeEnum
|
||||
} from "@saleor/types/globalTypes";
|
||||
import { AttributeTypeEnum } from "@saleor/types/globalTypes";
|
||||
import { getFormErrors } from "@saleor/utils/errors";
|
||||
import getAttributeErrorMessage from "@saleor/utils/errors/attribute";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { defineMessages, FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { AttributePageFormData } from "../AttributePage";
|
||||
|
||||
const messages = defineMessages({
|
||||
availableInGrid: {
|
||||
defaultMessage: "Add to Column Options",
|
||||
description: "add attribute as column in product list table"
|
||||
},
|
||||
availableInGridCaption: {
|
||||
defaultMessage:
|
||||
"If enabled this attribute can be used as a column in product table.",
|
||||
description: "caption"
|
||||
},
|
||||
dashboardPropertiesTitle: {
|
||||
defaultMessage: "Dashboard Properties",
|
||||
description: "attribute properties regarding dashboard"
|
||||
},
|
||||
filterableInDashboard: {
|
||||
defaultMessage: "Use in Filtering",
|
||||
description: "use attribute in filtering"
|
||||
},
|
||||
filterableInDashboardCaption: {
|
||||
defaultMessage:
|
||||
"If enabled, you’ll be able to use this attribute to filter products in product list.",
|
||||
description: "caption"
|
||||
},
|
||||
filterableInStorefront: {
|
||||
defaultMessage: "Use in Faceted Navigation",
|
||||
description: "attribute is filterable in storefront"
|
||||
},
|
||||
storefrontPropertiesTitle: {
|
||||
defaultMessage: "Storefront Properties",
|
||||
description: "attribute properties regarding storefront"
|
||||
},
|
||||
storefrontSearchPosition: {
|
||||
defaultMessage: "Position in faceted navigation",
|
||||
description: "attribute position in storefront filters"
|
||||
},
|
||||
visibleInStorefront: {
|
||||
defaultMessage: "Public",
|
||||
description: "attribute visibility in storefront"
|
||||
},
|
||||
visibleInStorefrontCaption: {
|
||||
defaultMessage: "If enabled, attribute will be accessible to customers.",
|
||||
description: "caption"
|
||||
}
|
||||
});
|
||||
|
||||
export interface AttributePropertiesProps {
|
||||
data: AttributePageFormData;
|
||||
disabled: boolean;
|
||||
|
@ -38,6 +81,14 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
|
|||
|
||||
const formErrors = getFormErrors(["storefrontSearchPosition"], errors);
|
||||
|
||||
const dashboardProperties = ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes(
|
||||
data.inputType
|
||||
);
|
||||
|
||||
const storefrontFacetedNavigationProperties =
|
||||
ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes(data.inputType) &&
|
||||
data.type === AttributeTypeEnum.PRODUCT_TYPE;
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle title={intl.formatMessage(commonMessages.properties)} />
|
||||
|
@ -74,21 +125,14 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
|
|||
/> */}
|
||||
|
||||
<Typography variant="subtitle1">
|
||||
<FormattedMessage
|
||||
defaultMessage="Storefront Properties"
|
||||
description="attribute properties regarding storefront"
|
||||
/>
|
||||
<FormattedMessage {...messages.storefrontPropertiesTitle} />
|
||||
</Typography>
|
||||
<Hr />
|
||||
{data.inputType !== AttributeInputTypeEnum.FILE &&
|
||||
data.type === AttributeTypeEnum.PRODUCT_TYPE && (
|
||||
{storefrontFacetedNavigationProperties && (
|
||||
<>
|
||||
<ControlledCheckbox
|
||||
name={"filterableInStorefront" as keyof FormData}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Use in Faceted Navigation",
|
||||
description: "attribute is filterable in storefront"
|
||||
})}
|
||||
label={intl.formatMessage(messages.filterableInStorefront)}
|
||||
checked={data.filterableInStorefront}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
|
@ -107,10 +151,7 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
|
|||
name={
|
||||
"storefrontSearchPosition" as keyof AttributePageFormData
|
||||
}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Position in faceted navigation",
|
||||
description: "attribute position in storefront filters"
|
||||
})}
|
||||
label={intl.formatMessage(messages.storefrontSearchPosition)}
|
||||
value={data.storefrontSearchPosition}
|
||||
onChange={onChange}
|
||||
/>
|
||||
|
@ -123,12 +164,9 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
|
|||
name={"visibleInStorefront" as keyof FormData}
|
||||
label={
|
||||
<>
|
||||
<FormattedMessage
|
||||
defaultMessage="Public"
|
||||
description="attribute visibility in storefront"
|
||||
/>
|
||||
<FormattedMessage {...messages.visibleInStorefront} />
|
||||
<Typography variant="caption">
|
||||
<FormattedMessage defaultMessage="If enabled, attribute will be accessible to customers." />
|
||||
<FormattedMessage {...messages.visibleInStorefrontCaption} />
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
|
@ -136,14 +174,11 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
|
|||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
{data.inputType !== AttributeInputTypeEnum.FILE && (
|
||||
{dashboardProperties && (
|
||||
<>
|
||||
<CardSpacer />
|
||||
<Typography variant="subtitle1">
|
||||
<FormattedMessage
|
||||
defaultMessage="Dashboard Properties"
|
||||
description="attribute properties regarding dashboard"
|
||||
/>
|
||||
<FormattedMessage {...messages.dashboardPropertiesTitle} />
|
||||
</Typography>
|
||||
<Hr />
|
||||
<CardSpacer />
|
||||
|
@ -151,12 +186,11 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
|
|||
name={"filterableInDashboard" as keyof FormData}
|
||||
label={
|
||||
<>
|
||||
<FormattedMessage
|
||||
defaultMessage="Use in Filtering"
|
||||
description="use attribute in filtering"
|
||||
/>
|
||||
<FormattedMessage {...messages.filterableInDashboard} />
|
||||
<Typography variant="caption">
|
||||
<FormattedMessage defaultMessage="If enabled, you’ll be able to use this attribute to filter products in product list." />
|
||||
<FormattedMessage
|
||||
{...messages.filterableInDashboardCaption}
|
||||
/>
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
|
@ -169,12 +203,9 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
|
|||
name={"availableInGrid" as keyof FormData}
|
||||
label={
|
||||
<>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add to Column Options"
|
||||
description="add attribute as column in product list table"
|
||||
/>
|
||||
<FormattedMessage {...messages.availableInGrid} />
|
||||
<Typography variant="caption">
|
||||
<FormattedMessage defaultMessage="If enabled this attribute can be used as a column in product table." />
|
||||
<FormattedMessage {...messages.availableInGridCaption} />
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import { AttributeList_attributes_edges_node } from "./types/AttributeList";
|
|||
export const attribute: AttributeDetailsFragment = {
|
||||
__typename: "Attribute" as "Attribute",
|
||||
availableInGrid: true,
|
||||
entityType: null,
|
||||
filterableInDashboard: false,
|
||||
filterableInStorefront: true,
|
||||
id: "UHJvZHVjdEF0dHJpYnV0ZTo5",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
import { AttributeCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: AttributeCreate
|
||||
|
@ -47,6 +47,7 @@ export interface AttributeCreate_attributeCreate_attribute {
|
|||
privateMetadata: (AttributeCreate_attributeCreate_attribute_privateMetadata | null)[];
|
||||
availableInGrid: boolean;
|
||||
inputType: AttributeInputTypeEnum | null;
|
||||
entityType: AttributeEntityTypeEnum | null;
|
||||
storefrontSearchPosition: number;
|
||||
valueRequired: boolean;
|
||||
values: (AttributeCreate_attributeCreate_attribute_values | null)[] | null;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeTypeEnum, AttributeInputTypeEnum } from "./../../types/globalTypes";
|
||||
import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: AttributeDetails
|
||||
|
@ -47,6 +47,7 @@ export interface AttributeDetails_attribute {
|
|||
privateMetadata: (AttributeDetails_attribute_privateMetadata | null)[];
|
||||
availableInGrid: boolean;
|
||||
inputType: AttributeInputTypeEnum | null;
|
||||
entityType: AttributeEntityTypeEnum | null;
|
||||
storefrontSearchPosition: number;
|
||||
valueRequired: boolean;
|
||||
values: (AttributeDetails_attribute_values | null)[] | null;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeUpdateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
import { AttributeUpdateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: AttributeUpdate
|
||||
|
@ -47,6 +47,7 @@ export interface AttributeUpdate_attributeUpdate_attribute {
|
|||
privateMetadata: (AttributeUpdate_attributeUpdate_attribute_privateMetadata | null)[];
|
||||
availableInGrid: boolean;
|
||||
inputType: AttributeInputTypeEnum | null;
|
||||
entityType: AttributeEntityTypeEnum | null;
|
||||
storefrontSearchPosition: number;
|
||||
valueRequired: boolean;
|
||||
values: (AttributeUpdate_attributeUpdate_attribute_values | null)[] | null;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: AttributeValueCreate
|
||||
|
@ -47,6 +47,7 @@ export interface AttributeValueCreate_attributeValueCreate_attribute {
|
|||
privateMetadata: (AttributeValueCreate_attributeValueCreate_attribute_privateMetadata | null)[];
|
||||
availableInGrid: boolean;
|
||||
inputType: AttributeInputTypeEnum | null;
|
||||
entityType: AttributeEntityTypeEnum | null;
|
||||
storefrontSearchPosition: number;
|
||||
valueRequired: boolean;
|
||||
values: (AttributeValueCreate_attributeValueCreate_attribute_values | null)[] | null;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: AttributeValueDelete
|
||||
|
@ -47,6 +47,7 @@ export interface AttributeValueDelete_attributeValueDelete_attribute {
|
|||
privateMetadata: (AttributeValueDelete_attributeValueDelete_attribute_privateMetadata | null)[];
|
||||
availableInGrid: boolean;
|
||||
inputType: AttributeInputTypeEnum | null;
|
||||
entityType: AttributeEntityTypeEnum | null;
|
||||
storefrontSearchPosition: number;
|
||||
valueRequired: boolean;
|
||||
values: (AttributeValueDelete_attributeValueDelete_attribute_values | null)[] | null;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
import { AttributeValueCreateInput, AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum, AttributeErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: AttributeValueUpdate
|
||||
|
@ -47,6 +47,7 @@ export interface AttributeValueUpdate_attributeValueUpdate_attribute {
|
|||
privateMetadata: (AttributeValueUpdate_attributeValueUpdate_attribute_privateMetadata | null)[];
|
||||
availableInGrid: boolean;
|
||||
inputType: AttributeInputTypeEnum | null;
|
||||
entityType: AttributeEntityTypeEnum | null;
|
||||
storefrontSearchPosition: number;
|
||||
valueRequired: boolean;
|
||||
values: (AttributeValueUpdate_attributeValueUpdate_attribute_values | null)[] | null;
|
||||
|
|
|
@ -10,8 +10,53 @@ import {
|
|||
} from "@saleor/types/globalTypes";
|
||||
import { MutationFetchResult } from "react-apollo";
|
||||
|
||||
import { AttributePageFormData } from "../components/AttributePage";
|
||||
import { AttributeValueEditDialogFormData } from "../components/AttributeValueEditDialog";
|
||||
import { AttributeValueDelete } from "../types/AttributeValueDelete";
|
||||
|
||||
export const ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES = [
|
||||
AttributeInputTypeEnum.DROPDOWN,
|
||||
AttributeInputTypeEnum.MULTISELECT
|
||||
];
|
||||
|
||||
function getSimpleAttributeData(
|
||||
data: AttributePageFormData,
|
||||
values: AttributeValueEditDialogFormData[]
|
||||
) {
|
||||
return {
|
||||
...data,
|
||||
metadata: undefined,
|
||||
privateMetadata: undefined,
|
||||
storefrontSearchPosition: parseInt(data.storefrontSearchPosition, 10),
|
||||
values: values.map(value => ({
|
||||
name: value.name
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
function getFileOrReferenceAttributeData(
|
||||
data: AttributePageFormData,
|
||||
values: AttributeValueEditDialogFormData[]
|
||||
) {
|
||||
return {
|
||||
...getSimpleAttributeData(data, values),
|
||||
availableInGrid: undefined,
|
||||
filterableInDashboard: undefined,
|
||||
filterableInStorefront: undefined
|
||||
};
|
||||
}
|
||||
|
||||
export function getAttributeData(
|
||||
data: AttributePageFormData,
|
||||
values: AttributeValueEditDialogFormData[]
|
||||
) {
|
||||
if (ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes(data.inputType)) {
|
||||
return getSimpleAttributeData(data, values);
|
||||
} else {
|
||||
return getFileOrReferenceAttributeData(data, values);
|
||||
}
|
||||
}
|
||||
|
||||
export const isFileValueUnused = (
|
||||
attributesWithNewFileValue: FormsetData<null, File>,
|
||||
existingAttribute:
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import { getAttributeData } from "@saleor/attributes/utils/data";
|
||||
import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import { getStringOrPlaceholder } from "@saleor/misc";
|
||||
import { ReorderEvent } from "@saleor/types";
|
||||
import {
|
||||
AttributeErrorCode,
|
||||
AttributeInputTypeEnum
|
||||
} from "@saleor/types/globalTypes";
|
||||
import { AttributeErrorCode } from "@saleor/types/globalTypes";
|
||||
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||
import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler";
|
||||
import {
|
||||
|
@ -57,33 +55,6 @@ function areValuesEqual(
|
|||
return a.name === b.name;
|
||||
}
|
||||
|
||||
function getSimpleAttributeData(
|
||||
data: AttributePageFormData,
|
||||
values: AttributeValueEditDialogFormData[]
|
||||
) {
|
||||
return {
|
||||
...data,
|
||||
metadata: undefined,
|
||||
privateMetadata: undefined,
|
||||
storefrontSearchPosition: parseInt(data.storefrontSearchPosition, 10),
|
||||
values: values.map(value => ({
|
||||
name: value.name
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
function getFileAttributeData(
|
||||
data: AttributePageFormData,
|
||||
values: AttributeValueEditDialogFormData[]
|
||||
) {
|
||||
return {
|
||||
...getSimpleAttributeData(data, values),
|
||||
availableInGrid: undefined,
|
||||
filterableInDashboard: undefined,
|
||||
filterableInStorefront: undefined
|
||||
};
|
||||
}
|
||||
|
||||
const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
|
||||
const navigate = useNavigator();
|
||||
const notify = useNotifier();
|
||||
|
@ -145,10 +116,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
|
|||
setValues(move(values[oldIndex], values, areValuesEqual, newIndex));
|
||||
|
||||
const handleCreate = async (data: AttributePageFormData) => {
|
||||
const input =
|
||||
data.inputType === AttributeInputTypeEnum.FILE
|
||||
? getFileAttributeData(data, values)
|
||||
: getSimpleAttributeData(data, values);
|
||||
const input = getAttributeData(data, values);
|
||||
|
||||
const result = await attributeCreate({
|
||||
variables: {
|
||||
|
|
|
@ -178,6 +178,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
|
|||
const handleUpdate = async (data: AttributePageFormData) => {
|
||||
const input = {
|
||||
...data,
|
||||
entityType: undefined,
|
||||
inputType: undefined,
|
||||
metadata: undefined,
|
||||
privateMetadata: undefined,
|
||||
|
|
|
@ -12,6 +12,7 @@ export const attributeValueFragment = gql`
|
|||
file {
|
||||
...FileFragment
|
||||
}
|
||||
reference
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -36,6 +37,7 @@ export const attributeDetailsFragment = gql`
|
|||
...MetadataFragment
|
||||
availableInGrid
|
||||
inputType
|
||||
entityType
|
||||
storefrontSearchPosition
|
||||
valueRequired
|
||||
values {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeTypeEnum, AttributeInputTypeEnum } from "./../../types/globalTypes";
|
||||
import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: AttributeDetailsFragment
|
||||
|
@ -47,6 +47,7 @@ export interface AttributeDetailsFragment {
|
|||
privateMetadata: (AttributeDetailsFragment_privateMetadata | null)[];
|
||||
availableInGrid: boolean;
|
||||
inputType: AttributeInputTypeEnum | null;
|
||||
entityType: AttributeEntityTypeEnum | null;
|
||||
storefrontSearchPosition: number;
|
||||
valueRequired: boolean;
|
||||
values: (AttributeDetailsFragment_values | null)[] | null;
|
||||
|
|
|
@ -28403,6 +28403,9 @@ exports[`Storyshots Views / Attributes / Attribute details create 1`] = `
|
|||
<div
|
||||
class="FormSpacer-spacer-id"
|
||||
/>
|
||||
<div
|
||||
class="AttributeDetails-inputTypeSection-id"
|
||||
>
|
||||
<div
|
||||
class="MuiFormControl-root-id SingleSelectField-formControl-id"
|
||||
>
|
||||
|
@ -28457,6 +28460,7 @@ exports[`Storyshots Views / Attributes / Attribute details create 1`] = `
|
|||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="FormSpacer-spacer-id"
|
||||
/>
|
||||
|
@ -29434,6 +29438,9 @@ exports[`Storyshots Views / Attributes / Attribute details default 1`] = `
|
|||
<div
|
||||
class="FormSpacer-spacer-id"
|
||||
/>
|
||||
<div
|
||||
class="AttributeDetails-inputTypeSection-id"
|
||||
>
|
||||
<div
|
||||
class="MuiFormControl-root-id SingleSelectField-formControl-id"
|
||||
>
|
||||
|
@ -29487,6 +29494,7 @@ exports[`Storyshots Views / Attributes / Attribute details default 1`] = `
|
|||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="FormSpacer-spacer-id"
|
||||
/>
|
||||
|
@ -30510,6 +30518,9 @@ exports[`Storyshots Views / Attributes / Attribute details form errors 1`] = `
|
|||
<div
|
||||
class="FormSpacer-spacer-id"
|
||||
/>
|
||||
<div
|
||||
class="AttributeDetails-inputTypeSection-id"
|
||||
>
|
||||
<div
|
||||
class="MuiFormControl-root-id SingleSelectField-formControl-id"
|
||||
>
|
||||
|
@ -30563,6 +30574,7 @@ exports[`Storyshots Views / Attributes / Attribute details form errors 1`] = `
|
|||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="FormSpacer-spacer-id"
|
||||
/>
|
||||
|
@ -31593,6 +31605,9 @@ exports[`Storyshots Views / Attributes / Attribute details loading 1`] = `
|
|||
<div
|
||||
class="FormSpacer-spacer-id"
|
||||
/>
|
||||
<div
|
||||
class="AttributeDetails-inputTypeSection-id"
|
||||
>
|
||||
<div
|
||||
class="MuiFormControl-root-id SingleSelectField-formControl-id"
|
||||
>
|
||||
|
@ -31646,6 +31661,7 @@ exports[`Storyshots Views / Attributes / Attribute details loading 1`] = `
|
|||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="FormSpacer-spacer-id"
|
||||
/>
|
||||
|
@ -32408,6 +32424,9 @@ exports[`Storyshots Views / Attributes / Attribute details multiple select input
|
|||
<div
|
||||
class="FormSpacer-spacer-id"
|
||||
/>
|
||||
<div
|
||||
class="AttributeDetails-inputTypeSection-id"
|
||||
>
|
||||
<div
|
||||
class="MuiFormControl-root-id SingleSelectField-formControl-id"
|
||||
>
|
||||
|
@ -32461,6 +32480,7 @@ exports[`Storyshots Views / Attributes / Attribute details multiple select input
|
|||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="FormSpacer-spacer-id"
|
||||
/>
|
||||
|
@ -33479,6 +33499,9 @@ exports[`Storyshots Views / Attributes / Attribute details no values 1`] = `
|
|||
<div
|
||||
class="FormSpacer-spacer-id"
|
||||
/>
|
||||
<div
|
||||
class="AttributeDetails-inputTypeSection-id"
|
||||
>
|
||||
<div
|
||||
class="MuiFormControl-root-id SingleSelectField-formControl-id"
|
||||
>
|
||||
|
@ -33532,6 +33555,7 @@ exports[`Storyshots Views / Attributes / Attribute details no values 1`] = `
|
|||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="FormSpacer-spacer-id"
|
||||
/>
|
||||
|
|
Loading…
Reference in a new issue