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:
Dawid Tarasiuk 2021-01-12 12:11:15 +01:00 committed by Jakub Majorek
parent ee05b090b8
commit 8b0fe986b2
17 changed files with 650 additions and 480 deletions

View file

@ -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 dont 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, youll 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?"

View file

@ -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 dont 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 dont 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}

View file

@ -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

View file

@ -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, youll 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, youll 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>
</>
}

View file

@ -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",

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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:

View file

@ -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: {

View file

@ -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,

View file

@ -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 {

View file

@ -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;

View file

@ -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"
/>