diff --git a/CHANGELOG.md b/CHANGELOG.md index 022fd9728d4..2ad7aadd5d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ All notable, unreleased changes to this project will be documented in this file. - Add multiline field plugins - #974 by @dominik-zeglen - Handle limit reached error - #990 by @dominik-zeglen - Display Cloud limits - #1004 by @dominik-zeglen +- Introducing numeric attributes - #1065 by @piotrgrundas - Add shipping method description - #1058 by @jwm0 - Fix voucher and sales sorting errors - #1063 by @orzechdev - Fix custom currency formatting - #1067 by @orzechdev diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 37820f7d94d..805fb21fe75 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -869,6 +869,18 @@ "context": "dialog content", "string": "Are you sure you want to delete {attributeName}?" }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_acreFt": { + "context": "acre-ft unit", + "string": "acre-ft" + }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_acreInch": { + "context": "acre-inch unit", + "string": "acre-inch" + }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_area": { + "context": "area units type", + "string": "Area" + }, "src_dot_attributes_dot_components_dot_AttributeDetails_dot_attributeLabel": { "context": "attribute's label", "string": "Default Label" @@ -881,6 +893,10 @@ "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_distance": { + "context": "distance units type", + "string": "Distance" + }, "src_dot_attributes_dot_components_dot_AttributeDetails_dot_dropdown": { "context": "product attribute type", "string": "Dropdown" @@ -893,18 +909,34 @@ "context": "file attribute type", "string": "File" }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_imperial": { + "context": "imperial unit system", + "string": "Imperial" + }, "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_metric": { + "context": "metric unit system", + "string": "Metric" + }, "src_dot_attributes_dot_components_dot_AttributeDetails_dot_multiselect": { "context": "product attribute type", "string": "Multiple Select" }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_numeric": { + "context": "numeric attribute type", + "string": "Numeric" + }, "src_dot_attributes_dot_components_dot_AttributeDetails_dot_page": { "context": "page attribute entity type", "string": "Pages" }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_pint": { + "context": "pint unit", + "string": "pint" + }, "src_dot_attributes_dot_components_dot_AttributeDetails_dot_product": { "context": "product attribute entity type", "string": "Products" @@ -913,14 +945,38 @@ "context": "references attribute type", "string": "References" }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_selectUnit": { + "context": "check to require numeric attribute unit", + "string": "Select unit" + }, "src_dot_attributes_dot_components_dot_AttributeDetails_dot_text": { "context": "text attribute type", "string": "Text" }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_unit": { + "context": "numeric attribute unit", + "string": "Unit" + }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_unitOf": { + "context": "numeric attribute units of", + "string": "Units of" + }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_unitSystem": { + "context": "numeric attribute unit system", + "string": "System" + }, "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_AttributeDetails_dot_volume": { + "context": "volume units types", + "string": "Volume" + }, + "src_dot_attributes_dot_components_dot_AttributeDetails_dot_weight": { + "context": "weight units type", + "string": "Weight" + }, "src_dot_attributes_dot_components_dot_AttributeListPage_dot_2417065806": { "context": "tab name", "string": "All Attributes" diff --git a/package-lock.json b/package-lock.json index edec382ff4c..8f273c0c540 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2802,7 +2802,6 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, - "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -2848,7 +2847,6 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, - "optional": true, "requires": { "kind-of": "^3.0.2" } @@ -2858,7 +2856,6 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, - "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -27839,7 +27836,6 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, - "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -27885,7 +27881,6 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, - "optional": true, "requires": { "kind-of": "^3.0.2" } @@ -27895,7 +27890,6 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, - "optional": true, "requires": { "is-buffer": "^1.1.5" } diff --git a/schema.graphql b/schema.graphql index 86387212888..8cf7476336c 100644 --- a/schema.graphql +++ b/schema.graphql @@ -382,6 +382,15 @@ type AppUpdate { app: App } +enum AreaUnitsEnum { + SQ_CM + SQ_M + SQ_KM + SQ_FT + SQ_YD + SQ_INCH +} + type AssignNavigation { menu: Menu menuErrors: [MenuError!]! @deprecated(reason: "Use errors field instead. This field will be removed in Saleor 4.0.") @@ -399,6 +408,7 @@ type Attribute implements Node & ObjectWithMetadata { name: String slug: String type: AttributeTypeEnum + unit: MeasurementUnitsEnum values: [AttributeValue] valueRequired: Boolean! visibleInStorefront: Boolean! @@ -438,6 +448,7 @@ input AttributeCreateInput { name: String! slug: String type: AttributeTypeEnum! + unit: MeasurementUnitsEnum values: [AttributeValueCreateInput] valueRequired: Boolean isVariantOnly: Boolean @@ -493,6 +504,7 @@ input AttributeFilterInput { input AttributeInput { slug: String! values: [String] + valuesRange: IntRangeInput } enum AttributeInputTypeEnum { @@ -500,6 +512,7 @@ enum AttributeInputTypeEnum { MULTISELECT FILE REFERENCE + NUMERIC RICH_TEXT } @@ -559,6 +572,7 @@ type AttributeUpdate { input AttributeUpdateInput { name: String slug: String + unit: MeasurementUnitsEnum removeValues: [ID] addValues: [AttributeValueCreateInput] valueRequired: Boolean @@ -1781,6 +1795,15 @@ enum DiscountValueTypeEnum { PERCENTAGE } +enum DistanceUnitsEnum { + CM + M + KM + FT + YD + INCH +} + type Domain { host: String! sslEnabled: Boolean! @@ -2367,6 +2390,39 @@ type Margin { stop: Int } +enum MeasurementUnitsEnum { + CM + M + KM + FT + YD + INCH + SQ_CM + SQ_M + SQ_KM + SQ_FT + SQ_YD + SQ_INCH + CUBIC_MILLIMETER + CUBIC_CENTIMETER + CUBIC_DECIMETER + CUBIC_METER + LITER + CUBIC_FOOT + CUBIC_INCH + CUBIC_YARD + QT + PINT + FL_OZ + ACRE_IN + ACRE_FT + G + LB + OZ + KG + TONNE +} + type Menu implements Node & ObjectWithMetadata { id: ID! name: String! @@ -5637,6 +5693,22 @@ type VerifyToken { errors: [AccountError!]! } +enum VolumeUnitsEnum { + CUBIC_MILLIMETER + CUBIC_CENTIMETER + CUBIC_DECIMETER + CUBIC_METER + LITER + CUBIC_FOOT + CUBIC_INCH + CUBIC_YARD + QT + PINT + FL_OZ + ACRE_IN + ACRE_FT +} + type Voucher implements Node { id: ID! name: String @@ -6043,10 +6115,11 @@ type Weight { scalar WeightScalar enum WeightUnitsEnum { - KG + G LB OZ - G + KG + TONNE } scalar _Any diff --git a/src/attributes/components/AttributeDetails/AttributeDetails.tsx b/src/attributes/components/AttributeDetails/AttributeDetails.tsx index d11da34686b..9ff1a46c491 100644 --- a/src/attributes/components/AttributeDetails/AttributeDetails.tsx +++ b/src/attributes/components/AttributeDetails/AttributeDetails.tsx @@ -1,11 +1,13 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import TextField from "@material-ui/core/TextField"; +import { NumericUnits } from "@saleor/attributes/components/AttributeDetails/NumericUnits"; import CardTitle from "@saleor/components/CardTitle"; import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import FormSpacer from "@saleor/components/FormSpacer"; import SingleSelectField from "@saleor/components/SingleSelectField"; import { AttributeErrorFragment } from "@saleor/fragments/types/AttributeErrorFragment"; +import { UseFormResult } from "@saleor/hooks/useForm"; import { commonMessages } from "@saleor/intl"; import { makeStyles } from "@saleor/theme"; import { @@ -20,56 +22,7 @@ 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" - }, - text: { - defaultMessage: "Text", - description: "text attribute type" - } -}); +import { inputTypeMessages, messages } from "./messages"; const entityTypeMessages = defineMessages({ page: { @@ -96,16 +49,29 @@ const useStyles = makeStyles( { name: "AttributeDetails" } ); -export interface AttributeDetailsProps { +export interface AttributeDetailsProps + extends Pick< + UseFormResult, + "set" | "setError" | "data" | "clearErrors" | "errors" + > { canChangeType: boolean; - data: AttributePageFormData; disabled: boolean; - errors: AttributeErrorFragment[]; + apiErrors: AttributeErrorFragment[]; onChange: (event: React.ChangeEvent) => void; } const AttributeDetails: React.FC = props => { - const { canChangeType, data, disabled, errors, onChange } = props; + const { + canChangeType, + errors, + clearErrors, + setError, + data, + disabled, + apiErrors, + onChange, + set + } = props; const classes = useStyles(props); const intl = useIntl(); const inputTypeChoices = [ @@ -128,6 +94,10 @@ const AttributeDetails: React.FC = props => { { label: intl.formatMessage(inputTypeMessages.text), value: AttributeInputTypeEnum.RICH_TEXT + }, + { + label: intl.formatMessage(inputTypeMessages.numeric), + value: AttributeInputTypeEnum.NUMERIC } ]; const entityTypeChoices = [ @@ -141,9 +111,9 @@ const AttributeDetails: React.FC = props => { } ]; - const formErrors = getFormErrors( - ["name", "slug", "inputType", "entityType"], - errors + const formApiErrors = getFormErrors( + ["name", "slug", "inputType", "entityType", "unit"], + apiErrors ); return ( @@ -154,24 +124,24 @@ const AttributeDetails: React.FC = props => { = props => { = props => { = props => { onChange={onChange} disabled={disabled} /> + {data.inputType === AttributeInputTypeEnum.NUMERIC && ( + + )} ); diff --git a/src/attributes/components/AttributeDetails/NumericUnits.tsx b/src/attributes/components/AttributeDetails/NumericUnits.tsx new file mode 100644 index 00000000000..302b633dad0 --- /dev/null +++ b/src/attributes/components/AttributeDetails/NumericUnits.tsx @@ -0,0 +1,189 @@ +import { AttributePageFormData } from "@saleor/attributes/components/AttributePage"; +import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; +import SingleSelectField from "@saleor/components/SingleSelectField"; +import { UseFormResult } from "@saleor/hooks/useForm"; +import { commonMessages } from "@saleor/intl"; +import { makeStyles } from "@saleor/theme"; +import { MeasurementUnitsEnum } from "@saleor/types/globalTypes"; +import React, { useEffect, useMemo, useState } from "react"; +import { useIntl } from "react-intl"; + +import * as M from "./messages"; +import { + getUnitChoices, + unitMapping, + UnitSystem, + unitSystemChoices, + UnitType, + unitTypeChoices +} from "./utils"; + +const useStyles = makeStyles( + theme => ({ + unitsRow: { + columnGap: theme.spacing(2), + display: "flex", + [theme.breakpoints.down("sm")]: { + flexFlow: "wrap", + rowGap: theme.spacing(3) + } + }, + hr: { + border: "none", + borderTop: `1px solid ${theme.palette.divider}`, + height: 0, + margin: "0.5rem 0", + width: "100%" + } + }), + { name: "NumericUnits" } +); + +interface UnitData { + unit?: MeasurementUnitsEnum; + system?: UnitSystem; + type?: UnitType; +} + +interface NumericUnitsProps + extends Pick< + UseFormResult, + "set" | "setError" | "data" | "errors" | "clearErrors" + > { + disabled: boolean; +} + +export const NumericUnits: React.FC = ({ + data, + disabled, + errors, + set, + setError, + clearErrors +}) => { + const { formatMessage } = useIntl(); + const classes = useStyles(); + const [unitData, setUnitData] = useState({ + unit: data.unit ?? null + }); + + const { unit, system, type } = unitData; + const errorProps = { + error: !!errors.unit, + hint: formatMessage(commonMessages.requiredField) + }; + const [typeChoices, systemChoices, unitChoices] = useMemo( + () => [ + unitTypeChoices.map(choice => ({ + ...choice, + label: formatMessage(choice.label) + })), + unitSystemChoices.map(choice => ({ + ...choice, + label: formatMessage(choice.label) + })), + getUnitChoices(formatMessage) + ], + [] + ); + + useEffect(() => set({ unit }), [unit]); + + useEffect(() => { + if (data.unit) { + const selectInitialUnitData = () => { + const initialData: UnitData = { unit: data.unit }; + + Object.entries(unitChoices).some(([system, types]) => { + const systemMatch = Object.entries(types).some(([type, units]) => { + const unitMatch = units.some(({ value }) => value === data.unit); + if (unitMatch) { + initialData.type = type as UnitType; + } + return unitMatch; + }); + if (systemMatch) { + initialData.system = system as UnitSystem; + } + return systemMatch; + }); + + return initialData; + }; + + setUnitData(selectInitialUnitData()); + } + }, []); + + useEffect(() => { + if (unit === undefined && !errors.unit) { + setError("unit", formatMessage(commonMessages.requiredField)); + } + if (errors.unit && (unit || unit === null)) { + clearErrors("unit"); + } + }, [unitData, errors]); + + return ( +
+
+ + setUnitData({ unit: target.value ? undefined : null }) + } + disabled={disabled} + /> + {data.unit !== null && ( +
+ ) => + setUnitData({ system: target.value as UnitSystem }) + } + value={system} + disabled={disabled} + /> + ) => + setUnitData(({ system }) => ({ + system, + type: target.value as UnitType + })) + } + disabled={!system || disabled} + value={type} + /> + ) => + setUnitData(data => ({ + ...data, + unit: target.value as MeasurementUnitsEnum + })) + } + disabled={!type || disabled} + value={ + type && unitMapping[system][type].includes(unit) + ? unit + : undefined + } + /> +
+ )} +
+ ); +}; diff --git a/src/attributes/components/AttributeDetails/messages.tsx b/src/attributes/components/AttributeDetails/messages.tsx new file mode 100644 index 00000000000..ae7275913f9 --- /dev/null +++ b/src/attributes/components/AttributeDetails/messages.tsx @@ -0,0 +1,142 @@ +import React from "react"; +import { defineMessages } from "react-intl"; + +export 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" + }, + selectUnit: { + defaultMessage: "Select unit", + description: "check to require numeric attribute unit" + }, + unitSystem: { + defaultMessage: "System", + description: "numeric attribute unit system" + }, + + unitOf: { + defaultMessage: "Units of", + description: "numeric attribute units of" + }, + unit: { + defaultMessage: "Unit", + description: "numeric attribute unit" + } +}); + +export 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" + }, + text: { + defaultMessage: "Text", + description: "text attribute type" + }, + numeric: { + defaultMessage: "Numeric", + description: "numeric attribute type" + } +}); + +export const unitSystemMessages = defineMessages({ + metric: { + defaultMessage: "Metric", + description: "metric unit system" + }, + imperial: { + defaultMessage: "Imperial", + description: "imperial unit system" + } +}); + +export const unitTypeMessages = defineMessages({ + volume: { + defaultMessage: "Volume", + description: "volume units types" + }, + + distance: { + defaultMessage: "Distance", + description: "distance units type" + }, + weight: { + defaultMessage: "Weight", + description: "weight units type" + }, + area: { + defaultMessage: "Area", + description: "area units type" + } +}); + +export const unitMessages = defineMessages({ + pint: { defaultMessage: "pint", description: "pint unit" }, + acreInch: { defaultMessage: "acre-inch", description: "acre-inch unit" }, + acreFt: { defaultMessage: "acre-ft", description: "acre-ft unit" } +}); + +export const units = { + cubicCentimeter: <>cm³, + cubicDecimeter: <>dm³, + cubicMeter: <>m³, + liter: "l", + centimeter: "cm", + meter: "m", + kilometer: "km", + gram: "g", + kilogram: "kg", + tonne: "t", + squareCentimeter: <>cm², + squareMeter: <>m², + squareKilometer: <>km², + cubicFoot: <>ft³, + cubicInch: <>in³, + cubicYard: <>yd³, + qt: "qt", + flOz: "fl. oz", + pint: unitMessages.pint, + acreInch: unitMessages.acreInch, + acreFt: unitMessages.acreFt, + ft: "ft", + yd: "yd", + inch: "in", + oz: "oz", + lbs: "lbs", + squareFt: <>ft², + squareYd: <>yd², + squareInch: <>in² +}; diff --git a/src/attributes/components/AttributeDetails/utils.ts b/src/attributes/components/AttributeDetails/utils.ts new file mode 100644 index 00000000000..9936a2c4a8d --- /dev/null +++ b/src/attributes/components/AttributeDetails/utils.ts @@ -0,0 +1,166 @@ +import { Choice } from "@saleor/components/SingleSelectField"; +import { MeasurementUnitsEnum } from "@saleor/types/globalTypes"; +import React from "react"; +import { IntlShape, MessageDescriptor } from "react-intl"; + +import * as M from "./messages"; + +export type UnitSystem = "imperial" | "metric"; +export type UnitType = "volume" | "weight" | "area" | "distance"; + +const UNIT_MESSAGES_MAPPING = { + [MeasurementUnitsEnum.CUBIC_FOOT]: M.units.cubicFoot, + [MeasurementUnitsEnum.CUBIC_INCH]: M.units.cubicInch, + [MeasurementUnitsEnum.CUBIC_YARD]: M.units.cubicYard, + [MeasurementUnitsEnum.QT]: M.units.qt, + [MeasurementUnitsEnum.FL_OZ]: M.units.flOz, + [MeasurementUnitsEnum.PINT]: M.units.pint, + [MeasurementUnitsEnum.ACRE_IN]: M.units.acreInch, + [MeasurementUnitsEnum.ACRE_FT]: M.units.acreFt, + [MeasurementUnitsEnum.FT]: M.units.ft, + [MeasurementUnitsEnum.YD]: M.units.yd, + [MeasurementUnitsEnum.INCH]: M.units.inch, + [MeasurementUnitsEnum.LB]: M.units.lbs, + [MeasurementUnitsEnum.OZ]: M.units.oz, + [MeasurementUnitsEnum.SQ_FT]: M.units.squareFt, + [MeasurementUnitsEnum.SQ_YD]: M.units.squareYd, + [MeasurementUnitsEnum.SQ_INCH]: M.units.squareInch, + [MeasurementUnitsEnum.CUBIC_CENTIMETER]: M.units.cubicCentimeter, + [MeasurementUnitsEnum.CUBIC_DECIMETER]: M.units.cubicDecimeter, + [MeasurementUnitsEnum.CUBIC_METER]: M.units.cubicMeter, + [MeasurementUnitsEnum.LITER]: M.units.liter, + [MeasurementUnitsEnum.CM]: M.units.centimeter, + [MeasurementUnitsEnum.M]: M.units.meter, + [MeasurementUnitsEnum.KM]: M.units.kilometer, + [MeasurementUnitsEnum.G]: M.units.gram, + [MeasurementUnitsEnum.KG]: M.units.kilogram, + [MeasurementUnitsEnum.TONNE]: M.units.tonne, + [MeasurementUnitsEnum.SQ_CM]: M.units.squareCentimeter, + [MeasurementUnitsEnum.SQ_M]: M.units.squareMeter, + [MeasurementUnitsEnum.SQ_KM]: M.units.squareKilometer +}; + +export const getMeasurementUnitMessage = ( + unit: MeasurementUnitsEnum, + formatMessage: IntlShape["formatMessage"] +): MessageDescriptor | React.ReactNode => { + const message = UNIT_MESSAGES_MAPPING[unit]; + return typeof message === "string" || React.isValidElement(message) + ? message + : formatMessage(message); +}; + +export const unitSystemChoices: Array> = [ + { + label: M.unitSystemMessages.metric, + value: "metric" + }, + { + label: M.unitSystemMessages.imperial, + value: "imperial" + } +]; + +export const unitTypeChoices: Array> = [ + { + label: M.unitTypeMessages.volume, + value: "volume" + }, + { + label: M.unitTypeMessages.distance, + value: "distance" + }, + { + label: M.unitTypeMessages.weight, + value: "weight" + }, + { + label: M.unitTypeMessages.area, + value: "area" + } +]; + +export const unitMapping = { + imperial: { + volume: [ + MeasurementUnitsEnum.CUBIC_FOOT, + MeasurementUnitsEnum.CUBIC_INCH, + MeasurementUnitsEnum.CUBIC_YARD, + MeasurementUnitsEnum.QT, + MeasurementUnitsEnum.FL_OZ, + MeasurementUnitsEnum.PINT, + MeasurementUnitsEnum.ACRE_IN, + MeasurementUnitsEnum.ACRE_FT + ], + distance: [ + MeasurementUnitsEnum.FT, + MeasurementUnitsEnum.YD, + MeasurementUnitsEnum.INCH + ], + weight: [MeasurementUnitsEnum.LB, MeasurementUnitsEnum.OZ], + area: [ + MeasurementUnitsEnum.SQ_FT, + MeasurementUnitsEnum.SQ_YD, + MeasurementUnitsEnum.SQ_INCH + ] + }, + metric: { + volume: [ + MeasurementUnitsEnum.CUBIC_CENTIMETER, + MeasurementUnitsEnum.CUBIC_DECIMETER, + MeasurementUnitsEnum.CUBIC_METER, + MeasurementUnitsEnum.LITER + ], + distance: [ + MeasurementUnitsEnum.CM, + MeasurementUnitsEnum.M, + MeasurementUnitsEnum.KM + ], + weight: [ + MeasurementUnitsEnum.G, + MeasurementUnitsEnum.KG, + MeasurementUnitsEnum.TONNE + ], + area: [ + MeasurementUnitsEnum.SQ_CM, + MeasurementUnitsEnum.SQ_M, + MeasurementUnitsEnum.SQ_KM + ] + } +}; + +const extractTypeChoices = ( + typeEnums: { + [key in UnitType]: MeasurementUnitsEnum[]; + }, + formatMessage: IntlShape["formatMessage"] +) => + Object.entries(typeEnums).reduce( + (acc, [type, units]) => ({ + ...acc, + [type]: units.map(unit => ({ + value: unit, + label: getMeasurementUnitMessage(unit, formatMessage) + })) + }), + {} + ); + +export const getUnitChoices = ( + formatMessage: IntlShape["formatMessage"] +): { + [key in UnitSystem]: { + [key in UnitType]: Array>; + }; +} => + Object.entries(unitMapping).reduce( + (acc, [system, typeEnums]) => ({ + ...acc, + [system]: extractTypeChoices(typeEnums, formatMessage) + }), + {} + ) as { + [key in UnitSystem]: { + [key in UnitType]: Array>; + }; + }; diff --git a/src/attributes/components/AttributePage/AttributePage.tsx b/src/attributes/components/AttributePage/AttributePage.tsx index e8c56cdd4bc..02dac4c8d29 100644 --- a/src/attributes/components/AttributePage/AttributePage.tsx +++ b/src/attributes/components/AttributePage/AttributePage.tsx @@ -20,7 +20,8 @@ import { ReorderAction } from "@saleor/types"; import { AttributeEntityTypeEnum, AttributeInputTypeEnum, - AttributeTypeEnum + AttributeTypeEnum, + MeasurementUnitsEnum } from "@saleor/types/globalTypes"; import { mapMetadataItemToInput } from "@saleor/utils/maps"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; @@ -59,13 +60,14 @@ export interface AttributePageFormData extends MetadataFormData { slug: string; storefrontSearchPosition: string; valueRequired: boolean; + unit: MeasurementUnitsEnum | null | undefined; visibleInStorefront: boolean; } const AttributePage: React.FC = ({ attribute, disabled, - errors, + errors: apiErrors, saveButtonBarState, values, onBack, @@ -98,36 +100,27 @@ const AttributePage: React.FC = ({ storefrontSearchPosition: "", type: AttributeTypeEnum.PRODUCT_TYPE, valueRequired: true, - visibleInStorefront: true + visibleInStorefront: true, + unit: undefined } : { - availableInGrid: maybe(() => attribute.availableInGrid, true), + availableInGrid: attribute?.availableInGrid ?? true, entityType: attribute?.entityType ?? null, - filterableInDashboard: maybe( - () => attribute.filterableInDashboard, - true - ), - filterableInStorefront: maybe( - () => attribute.filterableInStorefront, - true - ), - inputType: maybe( - () => attribute.inputType, - AttributeInputTypeEnum.DROPDOWN - ), + filterableInDashboard: attribute?.filterableInDashboard ?? true, + filterableInStorefront: attribute?.filterableInStorefront ?? true, + inputType: attribute?.inputType ?? AttributeInputTypeEnum.DROPDOWN, metadata: attribute?.metadata?.map(mapMetadataItemToInput), - name: maybe(() => attribute.name, ""), + name: attribute?.name ?? "", privateMetadata: attribute?.privateMetadata?.map( mapMetadataItemToInput ), - slug: maybe(() => attribute.slug, ""), - storefrontSearchPosition: maybe( - () => attribute.storefrontSearchPosition.toString(), - "" - ), + slug: attribute?.slug ?? "", + storefrontSearchPosition: + attribute?.storefrontSearchPosition.toString() ?? "", type: attribute?.type || AttributeTypeEnum.PRODUCT_TYPE, - valueRequired: maybe(() => attribute.valueRequired, true), - visibleInStorefront: maybe(() => attribute.visibleInStorefront, true) + valueRequired: !!attribute?.valueRequired ?? true, + visibleInStorefront: attribute?.visibleInStorefront ?? true, + unit: attribute?.unit || null }; const handleSubmit = (data: AttributePageFormData) => { @@ -150,7 +143,16 @@ const AttributePage: React.FC = ({ return (
- {({ change, data, hasChanged, submit }) => { + {({ + change, + set, + data, + hasChanged, + submit, + errors, + setError, + clearErrors + }) => { const changeMetadata = makeMetadataChangeHandler(change); return ( @@ -174,8 +176,12 @@ const AttributePage: React.FC = ({ canChangeType={attribute === null} data={data} disabled={disabled} - errors={errors} + apiErrors={apiErrors} onChange={change} + set={set} + errors={errors} + setError={setError} + clearErrors={clearErrors} /> {ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes( data.inputType @@ -205,7 +211,7 @@ const AttributePage: React.FC = ({ diff --git a/src/attributes/fixtures.ts b/src/attributes/fixtures.ts index 84a02b28398..b0a4fbf8d27 100644 --- a/src/attributes/fixtures.ts +++ b/src/attributes/fixtures.ts @@ -28,6 +28,7 @@ export const attribute: AttributeDetailsFragment = { storefrontSearchPosition: 2, type: AttributeTypeEnum.PRODUCT_TYPE, valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -62,6 +63,7 @@ export const attributes: Array value.reference); - } - if (attribute.attribute.inputType === AttributeInputTypeEnum.RICH_TEXT) { - return [attribute.values[0]?.richText]; + switch (attribute.attribute.inputType) { + case AttributeInputTypeEnum.REFERENCE: + return attribute.values.map(value => value.reference); + + case AttributeInputTypeEnum.RICH_TEXT: + return [attribute.values[0]?.richText]; + + case AttributeInputTypeEnum.NUMERIC: + return [attribute.values[0]?.name]; + + default: + return attribute.values.map(value => value.slug); } - return attribute.values.map(value => value.slug); } export const isFileValueUnused = ( diff --git a/src/components/Attributes/AttributeRow.tsx b/src/components/Attributes/AttributeRow.tsx index a5da29d7866..0e575b7cf6d 100644 --- a/src/components/Attributes/AttributeRow.tsx +++ b/src/components/Attributes/AttributeRow.tsx @@ -1,4 +1,6 @@ +import { InputAdornment, TextField } from "@material-ui/core"; import makeStyles from "@material-ui/core/styles/makeStyles"; +import { getMeasurementUnitMessage } from "@saleor/attributes/components/AttributeDetails/utils"; import { AttributeInput } from "@saleor/components/Attributes/Attributes"; import BasicAttributeRow from "@saleor/components/Attributes/BasicAttributeRow"; import ExtendedAttributeRow from "@saleor/components/Attributes/ExtendedAttributeRow"; @@ -158,6 +160,34 @@ const AttributeRow: React.FC = ({ /> ); + case AttributeInputTypeEnum.NUMERIC: + return ( + + onChange(attribute.id, event.target.value)} + type="number" + value={attribute.value[0]} + InputProps={ + attribute.data.unit && { + endAdornment: ( + + {getMeasurementUnitMessage( + attribute.data.unit, + intl.formatMessage + )} + + ) + } + } + /> + + ); default: return ( diff --git a/src/components/Attributes/Attributes.tsx b/src/components/Attributes/Attributes.tsx index cc70a5f5821..f2bab1ede51 100644 --- a/src/components/Attributes/Attributes.tsx +++ b/src/components/Attributes/Attributes.tsx @@ -13,7 +13,8 @@ import { FormsetAtomicData } from "@saleor/hooks/useFormset"; import { makeStyles } from "@saleor/theme"; import { AttributeEntityTypeEnum, - AttributeInputTypeEnum + AttributeInputTypeEnum, + MeasurementUnitsEnum } from "@saleor/types/globalTypes"; import classNames from "classnames"; import React from "react"; @@ -25,6 +26,7 @@ import { VariantAttributeScope } from "./types"; export interface AttributeInputData { inputType: AttributeInputTypeEnum; entityType?: AttributeEntityTypeEnum; + unit?: MeasurementUnitsEnum | null; variantAttributeScope?: VariantAttributeScope; isRequired: boolean; values: AttributeValueFragment[]; diff --git a/src/components/Attributes/BasicAttributeRow.tsx b/src/components/Attributes/BasicAttributeRow.tsx index 6f2665ffec3..9a5c4efb8f2 100644 --- a/src/components/Attributes/BasicAttributeRow.tsx +++ b/src/components/Attributes/BasicAttributeRow.tsx @@ -20,7 +20,7 @@ const useStyles = makeStyles( ); interface BasicAttributeRowProps { - label: string; + label: string | React.ReactNode; } const BasicAttributeRow: React.FC = props => { diff --git a/src/components/Attributes/fixtures.ts b/src/components/Attributes/fixtures.ts index a0126c9d5eb..21a1758338b 100644 --- a/src/components/Attributes/fixtures.ts +++ b/src/components/Attributes/fixtures.ts @@ -193,12 +193,34 @@ const RICH_TEXT_ATTRIBUTE: AttributeInput = { value: [] }; +const NUMERIC_ATTRIBUTE: AttributeInput = { + data: { + inputType: AttributeInputTypeEnum.NUMERIC, + isRequired: true, + values: [ + { + __typename: "AttributeValue", + file: null, + id: "QXR0cmlidXRlVmFsdWU6MTAx", + name: "12cm", + reference: null, + richText: null, + slug: "319_35" + } + ] + }, + id: "QXR0cmlidXRlOjM1", + label: "Numeric Attribute", + value: [] +}; + export const ATTRIBUTES: AttributeInput[] = [ DROPDOWN_ATTRIBUTE, MULTISELECT_ATTRIBUTE, FILE_ATTRIBUTE, REFERENCE_ATTRIBUTE, - RICH_TEXT_ATTRIBUTE + RICH_TEXT_ATTRIBUTE, + NUMERIC_ATTRIBUTE ]; export const ATTRIBUTES_SELECTED: AttributeInput[] = [ @@ -228,5 +250,9 @@ export const ATTRIBUTES_SELECTED: AttributeInput[] = [ { ...RICH_TEXT_ATTRIBUTE, value: [RICH_TEXT_ATTRIBUTE.data.values[0].richText] + }, + { + ...NUMERIC_ATTRIBUTE, + value: [NUMERIC_ATTRIBUTE.data.values[0].name] } ]; diff --git a/src/components/SingleSelectField/SingleSelectField.tsx b/src/components/SingleSelectField/SingleSelectField.tsx index 0e35b054ac9..25de8186c08 100644 --- a/src/components/SingleSelectField/SingleSelectField.tsx +++ b/src/components/SingleSelectField/SingleSelectField.tsx @@ -28,9 +28,9 @@ const useStyles = makeStyles( { name: "SingleSelectField" } ); -export interface Choice { - value: string; - label: string | React.ReactNode; +export interface Choice { + value: T; + label: L; } export type Choices = Choice[]; @@ -40,8 +40,8 @@ interface SingleSelectFieldProps { className?: string; disabled?: boolean; error?: boolean; - hint?: string; - label?: string; + hint?: string | React.ReactNode; + label?: string | React.ReactNode; name?: string; selectProps?: SelectProps; placeholder?: string; diff --git a/src/fragments/attributes.ts b/src/fragments/attributes.ts index b9db3468e2e..6415a3b4d1d 100644 --- a/src/fragments/attributes.ts +++ b/src/fragments/attributes.ts @@ -26,6 +26,7 @@ export const attributeFragment = gql` visibleInStorefront filterableInDashboard filterableInStorefront + unit } `; @@ -39,6 +40,7 @@ export const attributeDetailsFragment = gql` availableInGrid inputType entityType + unit storefrontSearchPosition valueRequired values { diff --git a/src/fragments/pages.ts b/src/fragments/pages.ts index b9f49c980b1..fc887643563 100644 --- a/src/fragments/pages.ts +++ b/src/fragments/pages.ts @@ -23,6 +23,7 @@ export const pageAttributesFragment = gql` inputType entityType valueRequired + unit values { ...AttributeValueFragment } diff --git a/src/fragments/products.ts b/src/fragments/products.ts index 5fdff13da2f..97424418b7f 100644 --- a/src/fragments/products.ts +++ b/src/fragments/products.ts @@ -127,6 +127,7 @@ export const productVariantAttributesFragment = gql` inputType entityType valueRequired + unit values { ...AttributeValueFragment } @@ -239,6 +240,7 @@ export const variantAttributeFragment = gql` inputType entityType valueRequired + unit values { ...AttributeValueFragment } diff --git a/src/fragments/types/AttributeDetailsFragment.ts b/src/fragments/types/AttributeDetailsFragment.ts index fddd5a6c8e8..7c9ddb30bac 100644 --- a/src/fragments/types/AttributeDetailsFragment.ts +++ b/src/fragments/types/AttributeDetailsFragment.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeTypeEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; +import { AttributeTypeEnum, MeasurementUnitsEnum, AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: AttributeDetailsFragment @@ -46,6 +46,7 @@ export interface AttributeDetailsFragment { visibleInStorefront: boolean; filterableInDashboard: boolean; filterableInStorefront: boolean; + unit: MeasurementUnitsEnum | null; metadata: (AttributeDetailsFragment_metadata | null)[]; privateMetadata: (AttributeDetailsFragment_privateMetadata | null)[]; availableInGrid: boolean; diff --git a/src/fragments/types/AttributeFragment.ts b/src/fragments/types/AttributeFragment.ts index f17e015054d..4f580eeb703 100644 --- a/src/fragments/types/AttributeFragment.ts +++ b/src/fragments/types/AttributeFragment.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeTypeEnum } from "./../../types/globalTypes"; +import { AttributeTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: AttributeFragment @@ -18,4 +18,5 @@ export interface AttributeFragment { visibleInStorefront: boolean; filterableInDashboard: boolean; filterableInStorefront: boolean; + unit: MeasurementUnitsEnum | null; } diff --git a/src/fragments/types/PageAttributesFragment.ts b/src/fragments/types/PageAttributesFragment.ts index aabe979d47a..625a5b7627e 100644 --- a/src/fragments/types/PageAttributesFragment.ts +++ b/src/fragments/types/PageAttributesFragment.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: PageAttributesFragment @@ -33,6 +33,7 @@ export interface PageAttributesFragment_attributes_attribute { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (PageAttributesFragment_attributes_attribute_values | null)[] | null; } diff --git a/src/fragments/types/PageDetailsFragment.ts b/src/fragments/types/PageDetailsFragment.ts index 20bce742b19..45719c1c2a8 100644 --- a/src/fragments/types/PageDetailsFragment.ts +++ b/src/fragments/types/PageDetailsFragment.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: PageDetailsFragment @@ -33,6 +33,7 @@ export interface PageDetailsFragment_attributes_attribute { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (PageDetailsFragment_attributes_attribute_values | null)[] | null; } diff --git a/src/fragments/types/PageTypeDetailsFragment.ts b/src/fragments/types/PageTypeDetailsFragment.ts index a1412360cf8..5fe34537eb3 100644 --- a/src/fragments/types/PageTypeDetailsFragment.ts +++ b/src/fragments/types/PageTypeDetailsFragment.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeTypeEnum } from "./../../types/globalTypes"; +import { AttributeTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: PageTypeDetailsFragment @@ -30,6 +30,7 @@ export interface PageTypeDetailsFragment_attributes { visibleInStorefront: boolean; filterableInDashboard: boolean; filterableInStorefront: boolean; + unit: MeasurementUnitsEnum | null; } export interface PageTypeDetailsFragment { diff --git a/src/fragments/types/Product.ts b/src/fragments/types/Product.ts index ac389a1be17..b1819cd3499 100644 --- a/src/fragments/types/Product.ts +++ b/src/fragments/types/Product.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: Product @@ -33,6 +33,7 @@ export interface Product_attributes_attribute { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (Product_attributes_attribute_values | null)[] | null; } diff --git a/src/fragments/types/ProductTypeDetailsFragment.ts b/src/fragments/types/ProductTypeDetailsFragment.ts index 44a169ef84e..c4a28ea4489 100644 --- a/src/fragments/types/ProductTypeDetailsFragment.ts +++ b/src/fragments/types/ProductTypeDetailsFragment.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; +import { AttributeTypeEnum, MeasurementUnitsEnum, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: ProductTypeDetailsFragment @@ -36,6 +36,7 @@ export interface ProductTypeDetailsFragment_productAttributes { visibleInStorefront: boolean; filterableInDashboard: boolean; filterableInStorefront: boolean; + unit: MeasurementUnitsEnum | null; } export interface ProductTypeDetailsFragment_variantAttributes { @@ -47,6 +48,7 @@ export interface ProductTypeDetailsFragment_variantAttributes { visibleInStorefront: boolean; filterableInDashboard: boolean; filterableInStorefront: boolean; + unit: MeasurementUnitsEnum | null; } export interface ProductTypeDetailsFragment_weight { diff --git a/src/fragments/types/ProductVariant.ts b/src/fragments/types/ProductVariant.ts index f04319242fb..fd3397d82d2 100644 --- a/src/fragments/types/ProductVariant.ts +++ b/src/fragments/types/ProductVariant.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: ProductVariant @@ -45,6 +45,7 @@ export interface ProductVariant_selectionAttributes_attribute { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -94,6 +95,7 @@ export interface ProductVariant_nonSelectionAttributes_attribute { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductVariant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/fragments/types/ProductVariantAttributesFragment.ts b/src/fragments/types/ProductVariantAttributesFragment.ts index 94101bc52ed..2cc0c665706 100644 --- a/src/fragments/types/ProductVariantAttributesFragment.ts +++ b/src/fragments/types/ProductVariantAttributesFragment.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: ProductVariantAttributesFragment @@ -33,6 +33,7 @@ export interface ProductVariantAttributesFragment_attributes_attribute { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductVariantAttributesFragment_attributes_attribute_values | null)[] | null; } diff --git a/src/fragments/types/SelectedVariantAttributeFragment.ts b/src/fragments/types/SelectedVariantAttributeFragment.ts index 3bca456b461..383ad35a212 100644 --- a/src/fragments/types/SelectedVariantAttributeFragment.ts +++ b/src/fragments/types/SelectedVariantAttributeFragment.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: SelectedVariantAttributeFragment @@ -33,6 +33,7 @@ export interface SelectedVariantAttributeFragment_attribute { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (SelectedVariantAttributeFragment_attribute_values | null)[] | null; } diff --git a/src/fragments/types/VariantAttributeFragment.ts b/src/fragments/types/VariantAttributeFragment.ts index ec7caa47679..04313d81fce 100644 --- a/src/fragments/types/VariantAttributeFragment.ts +++ b/src/fragments/types/VariantAttributeFragment.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL fragment: VariantAttributeFragment @@ -33,5 +33,6 @@ export interface VariantAttributeFragment { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (VariantAttributeFragment_values | null)[] | null; } diff --git a/src/hooks/useForm.ts b/src/hooks/useForm.ts index 5b4ed8a2dbc..5fb19376dd4 100644 --- a/src/hooks/useForm.ts +++ b/src/hooks/useForm.ts @@ -1,5 +1,7 @@ import { toggle } from "@saleor/utils/lists"; import isEqual from "lodash-es/isEqual"; +import omit from "lodash/omit"; +import React from "react"; import { useState } from "react"; import useStateFromProps from "./useStateFromProps"; @@ -14,6 +16,10 @@ export type SubmitPromise = Promise; export type FormChange = (event: ChangeEvent, cb?: () => void) => void; +export type FormErrors = { + [field in keyof T]?: string | React.ReactNode; +}; + export interface UseFormResult { change: FormChange; data: T; @@ -23,6 +29,9 @@ export interface UseFormResult { submit: () => void; triggerChange: () => void; toggleValue: FormChange; + errors: FormErrors; + setError: (name: keyof T, error: string | React.ReactNode) => void; + clearErrors: (name?: keyof T | Array) => void; } type FormData = Record; @@ -55,6 +64,7 @@ function useForm( onSubmit?: (data: T) => SubmitPromise | void ): UseFormResult { const [hasChanged, setChanged] = useState(false); + const [errors, setErrors] = useState>({}); const [data, setData] = useStateFromProps(initial, { mergeFunc: merge, onRefresh: newData => handleRefresh(data, newData, setChanged) @@ -109,7 +119,7 @@ function useForm( } async function submit() { - if (typeof onSubmit === "function") { + if (typeof onSubmit === "function" && !Object.keys(errors).length) { const result = onSubmit(data); if (result) { const errors = await result; @@ -124,8 +134,24 @@ function useForm( setChanged(true); } + const setError = (field: keyof T, error: string | React.ReactNode) => + setErrors(e => ({ ...e, [field]: error })); + + const clearErrors = (field?: keyof T | Array) => { + if (!field) { + setErrors({}); + } else { + setErrors(errors => + omit>(errors, Array.isArray(field) ? field : [field]) + ); + } + }; + return { + setError, + errors, change, + clearErrors, data, hasChanged, reset, diff --git a/src/pageTypes/fixtures.ts b/src/pageTypes/fixtures.ts index d6ea96ef724..562f31375b0 100644 --- a/src/pageTypes/fixtures.ts +++ b/src/pageTypes/fixtures.ts @@ -46,7 +46,8 @@ export const pageType: PageTypeDetails_pageType = { visibleInStorefront: true, filterableInDashboard: true, filterableInStorefront: true, - type: AttributeTypeEnum.PAGE_TYPE + type: AttributeTypeEnum.PAGE_TYPE, + unit: null }, { __typename: "Attribute" as "Attribute", @@ -56,7 +57,8 @@ export const pageType: PageTypeDetails_pageType = { visibleInStorefront: true, filterableInDashboard: true, filterableInStorefront: true, - type: AttributeTypeEnum.PAGE_TYPE + type: AttributeTypeEnum.PAGE_TYPE, + unit: null }, { __typename: "Attribute" as "Attribute", @@ -66,7 +68,8 @@ export const pageType: PageTypeDetails_pageType = { visibleInStorefront: true, filterableInDashboard: true, filterableInStorefront: true, - type: AttributeTypeEnum.PAGE_TYPE + type: AttributeTypeEnum.PAGE_TYPE, + unit: null } ], privateMetadata: [] diff --git a/src/pageTypes/types/AssignPageAttribute.ts b/src/pageTypes/types/AssignPageAttribute.ts index 866bf5f20ff..6aa92dbf65e 100644 --- a/src/pageTypes/types/AssignPageAttribute.ts +++ b/src/pageTypes/types/AssignPageAttribute.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { PageErrorCode, AttributeTypeEnum } from "./../../types/globalTypes"; +import { PageErrorCode, AttributeTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: AssignPageAttribute @@ -36,6 +36,7 @@ export interface AssignPageAttribute_pageAttributeAssign_pageType_attributes { visibleInStorefront: boolean; filterableInDashboard: boolean; filterableInStorefront: boolean; + unit: MeasurementUnitsEnum | null; } export interface AssignPageAttribute_pageAttributeAssign_pageType { diff --git a/src/pageTypes/types/PageTypeAttributeReorder.ts b/src/pageTypes/types/PageTypeAttributeReorder.ts index 2141129b917..5b7ecea624a 100644 --- a/src/pageTypes/types/PageTypeAttributeReorder.ts +++ b/src/pageTypes/types/PageTypeAttributeReorder.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { ReorderInput, PageErrorCode, AttributeTypeEnum } from "./../../types/globalTypes"; +import { ReorderInput, PageErrorCode, AttributeTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: PageTypeAttributeReorder @@ -36,6 +36,7 @@ export interface PageTypeAttributeReorder_pageTypeReorderAttributes_pageType_att visibleInStorefront: boolean; filterableInDashboard: boolean; filterableInStorefront: boolean; + unit: MeasurementUnitsEnum | null; } export interface PageTypeAttributeReorder_pageTypeReorderAttributes_pageType { diff --git a/src/pageTypes/types/PageTypeCreate.ts b/src/pageTypes/types/PageTypeCreate.ts index 2d8c0282397..ecd9622ac78 100644 --- a/src/pageTypes/types/PageTypeCreate.ts +++ b/src/pageTypes/types/PageTypeCreate.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { PageTypeCreateInput, PageErrorCode, AttributeTypeEnum } from "./../../types/globalTypes"; +import { PageTypeCreateInput, PageErrorCode, AttributeTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: PageTypeCreate @@ -36,6 +36,7 @@ export interface PageTypeCreate_pageTypeCreate_pageType_attributes { visibleInStorefront: boolean; filterableInDashboard: boolean; filterableInStorefront: boolean; + unit: MeasurementUnitsEnum | null; } export interface PageTypeCreate_pageTypeCreate_pageType { diff --git a/src/pageTypes/types/PageTypeDetails.ts b/src/pageTypes/types/PageTypeDetails.ts index 134c622f4a7..095e3654963 100644 --- a/src/pageTypes/types/PageTypeDetails.ts +++ b/src/pageTypes/types/PageTypeDetails.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeTypeEnum } from "./../../types/globalTypes"; +import { AttributeTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: PageTypeDetails @@ -30,6 +30,7 @@ export interface PageTypeDetails_pageType_attributes { visibleInStorefront: boolean; filterableInDashboard: boolean; filterableInStorefront: boolean; + unit: MeasurementUnitsEnum | null; } export interface PageTypeDetails_pageType { diff --git a/src/pageTypes/types/PageTypeUpdate.ts b/src/pageTypes/types/PageTypeUpdate.ts index ef53e47b58a..07692e7d220 100644 --- a/src/pageTypes/types/PageTypeUpdate.ts +++ b/src/pageTypes/types/PageTypeUpdate.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { PageTypeUpdateInput, PageErrorCode, AttributeTypeEnum } from "./../../types/globalTypes"; +import { PageTypeUpdateInput, PageErrorCode, AttributeTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: PageTypeUpdate @@ -36,6 +36,7 @@ export interface PageTypeUpdate_pageTypeUpdate_pageType_attributes { visibleInStorefront: boolean; filterableInDashboard: boolean; filterableInStorefront: boolean; + unit: MeasurementUnitsEnum | null; } export interface PageTypeUpdate_pageTypeUpdate_pageType { diff --git a/src/pageTypes/types/UnassignPageAttribute.ts b/src/pageTypes/types/UnassignPageAttribute.ts index cc279509568..e1353d0bfc4 100644 --- a/src/pageTypes/types/UnassignPageAttribute.ts +++ b/src/pageTypes/types/UnassignPageAttribute.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { PageErrorCode, AttributeTypeEnum } from "./../../types/globalTypes"; +import { PageErrorCode, AttributeTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: UnassignPageAttribute @@ -36,6 +36,7 @@ export interface UnassignPageAttribute_pageAttributeUnassign_pageType_attributes visibleInStorefront: boolean; filterableInDashboard: boolean; filterableInStorefront: boolean; + unit: MeasurementUnitsEnum | null; } export interface UnassignPageAttribute_pageAttributeUnassign_pageType { diff --git a/src/pages/fixtures.ts b/src/pages/fixtures.ts index 39d6dd409ab..dd89b685044 100644 --- a/src/pages/fixtures.ts +++ b/src/pages/fixtures.ts @@ -48,6 +48,7 @@ export const page: PageDetails_page = { entityType: null, inputType: AttributeInputTypeEnum.DROPDOWN, valueRequired: false, + unit: null, values: [ { id: "QXR0cmlidXRlVmFsdWU6ODc=", @@ -100,6 +101,7 @@ export const page: PageDetails_page = { entityType: null, inputType: AttributeInputTypeEnum.MULTISELECT, valueRequired: false, + unit: null, values: [ { id: "QXR0cmlidXRlVmFsdWU6OTA=", diff --git a/src/pages/types/PageCreate.ts b/src/pages/types/PageCreate.ts index 437d089ec42..326d6c78212 100644 --- a/src/pages/types/PageCreate.ts +++ b/src/pages/types/PageCreate.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { PageCreateInput, PageErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; +import { PageCreateInput, PageErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: PageCreate @@ -41,6 +41,7 @@ export interface PageCreate_pageCreate_page_attributes_attribute { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (PageCreate_pageCreate_page_attributes_attribute_values | null)[] | null; } diff --git a/src/pages/types/PageDetails.ts b/src/pages/types/PageDetails.ts index 1cb8238deb3..a2c33a4e1a0 100644 --- a/src/pages/types/PageDetails.ts +++ b/src/pages/types/PageDetails.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: PageDetails @@ -33,6 +33,7 @@ export interface PageDetails_page_attributes_attribute { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (PageDetails_page_attributes_attribute_values | null)[] | null; } diff --git a/src/pages/types/PageUpdate.ts b/src/pages/types/PageUpdate.ts index 74e6cfc5d21..38849430411 100644 --- a/src/pages/types/PageUpdate.ts +++ b/src/pages/types/PageUpdate.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { PageInput, PageErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; +import { PageInput, PageErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: PageUpdate @@ -40,6 +40,7 @@ export interface PageUpdate_pageUpdate_page_attributes_attribute { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (PageUpdate_pageUpdate_page_attributes_attribute_values | null)[] | null; } diff --git a/src/pages/utils/data.ts b/src/pages/utils/data.ts index 083383dfe28..63b850332d4 100644 --- a/src/pages/utils/data.ts +++ b/src/pages/utils/data.ts @@ -15,7 +15,8 @@ export function getAttributeInputFromPage( inputType: attribute.attribute.inputType, isRequired: attribute.attribute.valueRequired, selectedValues: attribute.values, - values: attribute.attribute.values + values: attribute.attribute.values, + unit: attribute.attribute.unit }, id: attribute.attribute.id, label: attribute.attribute.name, diff --git a/src/productTypes/fixtures.ts b/src/productTypes/fixtures.ts index 540c17d51eb..7fccb75a23c 100644 --- a/src/productTypes/fixtures.ts +++ b/src/productTypes/fixtures.ts @@ -22,6 +22,7 @@ export const attributes: ProductType_productType_productAttributes[] = [ name: "Author", slug: "author", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -59,6 +60,7 @@ export const attributes: ProductType_productType_productAttributes[] = [ name: "Box Size", slug: "box-size", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -120,6 +122,7 @@ export const attributes: ProductType_productType_productAttributes[] = [ name: "Brand", slug: "brand", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -145,6 +148,7 @@ export const attributes: ProductType_productType_productAttributes[] = [ name: "Candy Box Size", slug: "candy-box-size", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -194,6 +198,7 @@ export const attributes: ProductType_productType_productAttributes[] = [ name: "Coffee Genre", slug: "coffee-genre", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -231,6 +236,7 @@ export const attributes: ProductType_productType_productAttributes[] = [ name: "Collar", slug: "collar", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -280,6 +286,7 @@ export const attributes: ProductType_productType_productAttributes[] = [ name: "Color", slug: "color", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -317,6 +324,7 @@ export const attributes: ProductType_productType_productAttributes[] = [ name: "Cover", slug: "cover", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -402,6 +410,7 @@ export const attributes: ProductType_productType_productAttributes[] = [ name: "Flavor", slug: "flavor", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -439,6 +448,7 @@ export const attributes: ProductType_productType_productAttributes[] = [ name: "Language", slug: "language", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -476,6 +486,7 @@ export const attributes: ProductType_productType_productAttributes[] = [ name: "Publisher", slug: "publisher", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -513,6 +524,7 @@ export const attributes: ProductType_productType_productAttributes[] = [ name: "Size", slug: "size", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue" as "AttributeValue", @@ -680,6 +692,7 @@ export const productTypes: Array ({ name: "Attachment", slug: "attachment", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue", @@ -3046,6 +3051,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ name: "Borders", slug: "Borders", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue", @@ -3089,6 +3095,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({ name: "Legacy", slug: "Legacy", valueRequired: true, + unit: null, values: [ { __typename: "AttributeValue", diff --git a/src/products/queries.ts b/src/products/queries.ts index d5cd3c7641e..30b53aa0f79 100644 --- a/src/products/queries.ts +++ b/src/products/queries.ts @@ -219,6 +219,7 @@ const productTypeQuery = gql` slug name valueRequired + unit values { ...AttributeValueFragment } diff --git a/src/products/types/CreateMultipleVariantsData.ts b/src/products/types/CreateMultipleVariantsData.ts index 7f9618fcbe6..aa904b78438 100644 --- a/src/products/types/CreateMultipleVariantsData.ts +++ b/src/products/types/CreateMultipleVariantsData.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: CreateMultipleVariantsData @@ -33,6 +33,7 @@ export interface CreateMultipleVariantsData_product_attributes_attribute { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (CreateMultipleVariantsData_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductChannelListingUpdate.ts b/src/products/types/ProductChannelListingUpdate.ts index 00c3d915c87..dfc5f991f4d 100644 --- a/src/products/types/ProductChannelListingUpdate.ts +++ b/src/products/types/ProductChannelListingUpdate.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { ProductChannelListingUpdateInput, AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum, ProductErrorCode } from "./../../types/globalTypes"; +import { ProductChannelListingUpdateInput, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductChannelListingUpdate @@ -33,6 +33,7 @@ export interface ProductChannelListingUpdate_productChannelListingUpdate_product inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductChannelListingUpdate_productChannelListingUpdate_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductCreate.ts b/src/products/types/ProductCreate.ts index a52ad3f0dba..ffe492d8c9d 100644 --- a/src/products/types/ProductCreate.ts +++ b/src/products/types/ProductCreate.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { ProductCreateInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductCreateInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductCreate @@ -40,6 +40,7 @@ export interface ProductCreate_productCreate_product_attributes_attribute { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductCreate_productCreate_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductDetails.ts b/src/products/types/ProductDetails.ts index 661fed2dd3a..57d2cdd5bce 100644 --- a/src/products/types/ProductDetails.ts +++ b/src/products/types/ProductDetails.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: ProductDetails @@ -33,6 +33,7 @@ export interface ProductDetails_product_attributes_attribute { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductDetails_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductMediaCreate.ts b/src/products/types/ProductMediaCreate.ts index 6c1176e9566..7469eeaa66f 100644 --- a/src/products/types/ProductMediaCreate.ts +++ b/src/products/types/ProductMediaCreate.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductMediaCreate @@ -39,6 +39,7 @@ export interface ProductMediaCreate_productMediaCreate_product_attributes_attrib inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductMediaCreate_productMediaCreate_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductMediaUpdate.ts b/src/products/types/ProductMediaUpdate.ts index 2fed1599e05..5ab54218422 100644 --- a/src/products/types/ProductMediaUpdate.ts +++ b/src/products/types/ProductMediaUpdate.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductMediaUpdate @@ -39,6 +39,7 @@ export interface ProductMediaUpdate_productMediaUpdate_product_attributes_attrib inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductMediaUpdate_productMediaUpdate_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductType.ts b/src/products/types/ProductType.ts index 9f829698ce9..4f65a8c14c5 100644 --- a/src/products/types/ProductType.ts +++ b/src/products/types/ProductType.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, AttributeEntityTypeEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: ProductType @@ -33,6 +33,7 @@ export interface ProductType_productType_productAttributes { slug: string | null; name: string | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductType_productType_productAttributes_values | null)[] | null; } diff --git a/src/products/types/ProductUpdate.ts b/src/products/types/ProductUpdate.ts index 93ce99645db..a356f469131 100644 --- a/src/products/types/ProductUpdate.ts +++ b/src/products/types/ProductUpdate.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { ProductInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductUpdate @@ -40,6 +40,7 @@ export interface ProductUpdate_productUpdate_product_attributes_attribute { inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductVariantChannelListingUpdate.ts b/src/products/types/ProductVariantChannelListingUpdate.ts index 1e52ad7ea6a..2f1f926288a 100644 --- a/src/products/types/ProductVariantChannelListingUpdate.ts +++ b/src/products/types/ProductVariantChannelListingUpdate.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { ProductVariantChannelListingAddInput, AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum, ProductErrorCode } from "./../../types/globalTypes"; +import { ProductVariantChannelListingAddInput, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductVariantChannelListingUpdate @@ -45,6 +45,7 @@ export interface ProductVariantChannelListingUpdate_productVariantChannelListing inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_selectionAttributes_attribute_values | null)[] | null; } @@ -94,6 +95,7 @@ export interface ProductVariantChannelListingUpdate_productVariantChannelListing inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductVariantCreateData.ts b/src/products/types/ProductVariantCreateData.ts index 37396976c54..9abc72fc8f2 100644 --- a/src/products/types/ProductVariantCreateData.ts +++ b/src/products/types/ProductVariantCreateData.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: ProductVariantCreateData @@ -52,6 +52,7 @@ export interface ProductVariantCreateData_product_productType_selectionVariantAt inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductVariantCreateData_product_productType_selectionVariantAttributes_values | null)[] | null; } @@ -79,6 +80,7 @@ export interface ProductVariantCreateData_product_productType_nonSelectionVarian inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductVariantCreateData_product_productType_nonSelectionVariantAttributes_values | null)[] | null; } diff --git a/src/products/types/ProductVariantDetails.ts b/src/products/types/ProductVariantDetails.ts index d2bd7b02f4b..e763ce06d38 100644 --- a/src/products/types/ProductVariantDetails.ts +++ b/src/products/types/ProductVariantDetails.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: ProductVariantDetails @@ -45,6 +45,7 @@ export interface ProductVariantDetails_productVariant_selectionAttributes_attrib inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductVariantDetails_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -94,6 +95,7 @@ export interface ProductVariantDetails_productVariant_nonSelectionAttributes_att inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductVariantDetails_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductVariantReorder.ts b/src/products/types/ProductVariantReorder.ts index bb291f5cefd..a2e8aae3389 100644 --- a/src/products/types/ProductVariantReorder.ts +++ b/src/products/types/ProductVariantReorder.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { ReorderInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ReorderInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductVariantReorder @@ -39,6 +39,7 @@ export interface ProductVariantReorder_productVariantReorder_product_attributes_ inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductVariantReorder_productVariantReorder_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/ProductVariantSetDefault.ts b/src/products/types/ProductVariantSetDefault.ts index 9edfddb02ac..e8f0ace2b4e 100644 --- a/src/products/types/ProductVariantSetDefault.ts +++ b/src/products/types/ProductVariantSetDefault.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: ProductVariantSetDefault @@ -39,6 +39,7 @@ export interface ProductVariantSetDefault_productVariantSetDefault_product_attri inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (ProductVariantSetDefault_productVariantSetDefault_product_attributes_attribute_values | null)[] | null; } diff --git a/src/products/types/SimpleProductUpdate.ts b/src/products/types/SimpleProductUpdate.ts index 8fbfb2c4ef2..3ce0b1daf07 100644 --- a/src/products/types/SimpleProductUpdate.ts +++ b/src/products/types/SimpleProductUpdate.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { ProductInput, ProductVariantInput, StockInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum, StockErrorCode } from "./../../types/globalTypes"; +import { ProductInput, ProductVariantInput, StockInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum, StockErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: SimpleProductUpdate @@ -40,6 +40,7 @@ export interface SimpleProductUpdate_productUpdate_product_attributes_attribute inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (SimpleProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null; } @@ -336,6 +337,7 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_selecti inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (SimpleProductUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -385,6 +387,7 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_nonSele inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (SimpleProductUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } @@ -625,6 +628,7 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_s inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (SimpleProductUpdate_productVariantStocksCreate_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -674,6 +678,7 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_n inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (SimpleProductUpdate_productVariantStocksCreate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } @@ -913,6 +918,7 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_s inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (SimpleProductUpdate_productVariantStocksDelete_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -962,6 +968,7 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_n inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (SimpleProductUpdate_productVariantStocksDelete_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } @@ -1202,6 +1209,7 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_s inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -1251,6 +1259,7 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_n inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/products/types/VariantCreate.ts b/src/products/types/VariantCreate.ts index 78ab5c0f3de..1bccfde5bdf 100644 --- a/src/products/types/VariantCreate.ts +++ b/src/products/types/VariantCreate.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { ProductVariantCreateInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductVariantCreateInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: VariantCreate @@ -52,6 +52,7 @@ export interface VariantCreate_productVariantCreate_productVariant_selectionAttr inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (VariantCreate_productVariantCreate_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -101,6 +102,7 @@ export interface VariantCreate_productVariantCreate_productVariant_nonSelectionA inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (VariantCreate_productVariantCreate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/products/types/VariantMediaAssign.ts b/src/products/types/VariantMediaAssign.ts index 8b56687fb7d..69fd95156a5 100644 --- a/src/products/types/VariantMediaAssign.ts +++ b/src/products/types/VariantMediaAssign.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: VariantMediaAssign @@ -51,6 +51,7 @@ export interface VariantMediaAssign_variantMediaAssign_productVariant_selectionA inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (VariantMediaAssign_variantMediaAssign_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -100,6 +101,7 @@ export interface VariantMediaAssign_variantMediaAssign_productVariant_nonSelecti inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (VariantMediaAssign_variantMediaAssign_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/products/types/VariantMediaUnassign.ts b/src/products/types/VariantMediaUnassign.ts index bff7807e060..acd06b520ed 100644 --- a/src/products/types/VariantMediaUnassign.ts +++ b/src/products/types/VariantMediaUnassign.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; +import { ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: VariantMediaUnassign @@ -51,6 +51,7 @@ export interface VariantMediaUnassign_variantMediaUnassign_productVariant_select inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (VariantMediaUnassign_variantMediaUnassign_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -100,6 +101,7 @@ export interface VariantMediaUnassign_variantMediaUnassign_productVariant_nonSel inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (VariantMediaUnassign_variantMediaUnassign_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/products/types/VariantUpdate.ts b/src/products/types/VariantUpdate.ts index 42bc61a5165..fc9be253093 100644 --- a/src/products/types/VariantUpdate.ts +++ b/src/products/types/VariantUpdate.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { StockInput, AttributeValueInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, ProductMediaType, WeightUnitsEnum, StockErrorCode } from "./../../types/globalTypes"; +import { StockInput, AttributeValueInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum, StockErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: VariantUpdate @@ -52,6 +52,7 @@ export interface VariantUpdate_productVariantUpdate_productVariant_selectionAttr inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (VariantUpdate_productVariantUpdate_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -101,6 +102,7 @@ export interface VariantUpdate_productVariantUpdate_productVariant_nonSelectionA inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (VariantUpdate_productVariantUpdate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } @@ -341,6 +343,7 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_selecti inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (VariantUpdate_productVariantStocksUpdate_productVariant_selectionAttributes_attribute_values | null)[] | null; } @@ -390,6 +393,7 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_nonSele inputType: AttributeInputTypeEnum | null; entityType: AttributeEntityTypeEnum | null; valueRequired: boolean; + unit: MeasurementUnitsEnum | null; values: (VariantUpdate_productVariantStocksUpdate_productVariant_nonSelectionAttributes_attribute_values | null)[] | null; } diff --git a/src/products/utils/data.ts b/src/products/utils/data.ts index 710e73c8dff..a97307ec62b 100644 --- a/src/products/utils/data.ts +++ b/src/products/utils/data.ts @@ -52,7 +52,8 @@ export function getAttributeInputFromProduct( inputType: attribute.attribute.inputType, isRequired: attribute.attribute.valueRequired, selectedValues: attribute.values, - values: attribute.attribute.values + values: attribute.attribute.values, + unit: attribute.attribute.unit }, id: attribute.attribute.id, label: attribute.attribute.name, @@ -70,7 +71,8 @@ export function getAttributeInputFromProductType( entityType: attribute.entityType, inputType: attribute.inputType, isRequired: attribute.valueRequired, - values: attribute.values + values: attribute.values, + unit: attribute.unit }, id: attribute.id, label: attribute.name, @@ -88,6 +90,7 @@ export function getAttributeInputFromAttributes( inputType: attribute.inputType, isRequired: attribute.valueRequired, values: attribute.values, + unit: attribute.unit, variantAttributeScope }, id: attribute.id, @@ -107,6 +110,7 @@ export function getAttributeInputFromSelectedAttributes( isRequired: attribute.attribute.valueRequired, selectedValues: attribute.values, values: attribute.attribute.values, + unit: attribute.attribute.unit, variantAttributeScope }, id: attribute.attribute.id, diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 948580f3029..fcc86ed3c82 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -61,7 +61,7 @@ exports[`Storyshots Attributes / Attributes default 1`] = `
- 5 Attributes + 6 Attributes