Skip to content

Commit 6f61b4f

Browse files
authored
has_variants=false: disable Generate Variants button and fix the selection attributes in variant's edit/crete (#6298)
* Disable generator for Product types with has_attributes=false `has_attributes` will be depracated shortly but for now we the API enforces the has_attributes flag to be true in order to set variant attribute values * Hide selection attributes in variant edit for products with has_attributes=false Provide additinal information that has_variants needs to be on to change create variant with selection attributes. * Improve tooltip around required attribute types * Extract messages
1 parent 1245dab commit 6f61b4f

15 files changed

Lines changed: 258 additions & 87 deletions

File tree

locale/defaultMessages.json

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,10 +1100,6 @@
11001100
"4YJHut": {
11011101
"string": "Clear search"
11021102
},
1103-
"4YtpqB": {
1104-
"context": "tooltip when generate variants is disabled due to unsupported required attributes",
1105-
"string": "Required attributes ({attributes}) are not currently supported by Generator.{newline}{newline}To use the Generator:{newline}1. Make them optional in product type{newline}2. Generate variants{newline}3. Set values manually{newline}4. Restore required setting"
1106-
},
11071103
"4YyeCx": {
11081104
"context": "label for order total amount",
11091105
"string": "Order total"
@@ -1731,6 +1727,10 @@
17311727
"context": "order refund amount, input button",
17321728
"string": "Refund {currency} {amount}"
17331729
},
1730+
"8FDx9i": {
1731+
"context": "tooltip when generate variants is disabled due to product type configuration (hasVariants=false or no selection attributes)",
1732+
"string": "To use the Generator, this product type needs:{newline}{newline}• 'Product type uses Variant Attributes' enabled{newline}• Selection variant attributes defined"
1733+
},
17341734
"8GC/ah": {
17351735
"context": "variants section name",
17361736
"string": "Variants"
@@ -1848,6 +1848,10 @@
18481848
"context": "dialog content",
18491849
"string": "{counter,plural,one{Are you sure you want to publish this model?} other{Are you sure you want to publish {displayQuantity} models?}}"
18501850
},
1851+
"9+iLpf": {
1852+
"context": "link text to product type settings",
1853+
"string": "Configure in product type settings"
1854+
},
18511855
"91vQMc": {
18521856
"string": "order management"
18531857
},
@@ -7725,6 +7729,10 @@
77257729
"context": "tooltip shown when shipping method is selected but no other options exist",
77267730
"string": "No alternative shipping methods available"
77277731
},
7732+
"gDvd3v": {
7733+
"context": "tooltip when generate variants is disabled due to unsupported required attributes",
7734+
"string": "Required attributes with unsupported types:{newline}{attributes}{newline}{newline}To use the Generator:{newline}1. Make them optional in product type{newline}2. Generate variants{newline}3. Set values manually{newline}4. Restore required setting"
7735+
},
77287736
"gE6aiQ": {
77297737
"context": "PageTypeDeleteWarningDialog single no assigned items description",
77307738
"string": "Are you sure you want to delete <b>{typeName}</b>? If you remove it you won’t be able to assign it to created models."
@@ -8600,10 +8608,6 @@
86008608
"context": "Webhook subscription query card title",
86018609
"string": "Subscription Query"
86028610
},
8603-
"lEx2zw": {
8604-
"context": "tooltip when generate variants is disabled due to no selection attributes",
8605-
"string": "No selection attributes defined for this product type."
8606-
},
86078611
"lF+VJQ": {
86088612
"context": "Shipment information card header",
86098613
"string": "Shipment information"
@@ -11058,6 +11062,10 @@
1105811062
"context": "gift card removed success alert message",
1105911063
"string": "{selectedItemsCount,plural,one{Successfully deleted gift card} other{Successfully deleted gift cards}}"
1106011064
},
11065+
"zN0Eub": {
11066+
"context": "info message when hasVariants is false but variant attributes exist",
11067+
"string": "This product type has {count, plural, one {# variant attribute} other {# variant attributes}} defined, but 'Product type uses Variant Attributes' is disabled. Edit {productTypeLink} product type to enable variant attributes."
11068+
},
1106111069
"zQvVDJ": {
1106211070
"string": "All"
1106311071
},

src/components/Datagrid/components/Header.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,21 @@ const ButtonFullScreen: FC<PropsWithChildren<ButtonFullScreenProps>> = ({
3131

3232
interface ButtonAddRowProps {
3333
onAddRow: React.MouseEventHandler<HTMLButtonElement>;
34+
disabled?: boolean;
3435
}
3536

36-
const ButtonAddRow: FC<PropsWithChildren<ButtonAddRowProps>> = ({ onAddRow, children }) => {
37+
const ButtonAddRow: FC<PropsWithChildren<ButtonAddRowProps>> = ({
38+
onAddRow,
39+
disabled,
40+
children,
41+
}) => {
3742
return (
38-
<Button data-test-id="button-add-variant" variant="secondary" onClick={onAddRow}>
43+
<Button
44+
data-test-id="button-add-variant"
45+
variant="secondary"
46+
onClick={onAddRow}
47+
disabled={disabled}
48+
>
3949
<PlusIcon />
4050
{children}
4151
</Button>

src/fragments/products.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ export const fragmentVariant = gql`
309309
productType {
310310
id
311311
name
312+
hasVariants
312313
}
313314
channelListings {
314315
id

src/graphql/hooks.generated.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3010,6 +3010,7 @@ export const ProductVariantFragmentDoc = gql`
30103010
productType {
30113011
id
30123012
name
3013+
hasVariants
30133014
}
30143015
channelListings {
30153016
id
@@ -15811,6 +15812,8 @@ export const ProductVariantCreateDataDocument = gql`
1581115812
name
1581215813
productType {
1581315814
id
15815+
name
15816+
hasVariants
1581415817
selectionVariantAttributes: variantAttributes(
1581515818
variantSelection: VARIANT_SELECTION
1581615819
) {

src/graphql/types.generated.ts

Lines changed: 5 additions & 5 deletions
Large diffs are not rendered by default.

src/products/components/ProductUpdatePage/ProductUpdatePage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ const ProductUpdatePage = ({
490490
)}
491491
<ProductVariants
492492
productId={productId}
493+
productTypeId={product?.productType.id ?? ""}
493494
productName={product?.name}
494495
errors={variantListErrors}
495496
channels={listings}
@@ -498,6 +499,7 @@ const ProductUpdatePage = ({
498499
variantAttributes={product?.productType.variantAttributes}
499500
selectionVariantAttributes={product?.productType.selectionVariantAttributes}
500501
nonSelectionVariantAttributes={product?.productType.nonSelectionVariantAttributes}
502+
hasVariants={hasVariants ?? false}
501503
onAttributeValuesSearch={onAttributeValuesSearch}
502504
onChange={handlers.changeVariants}
503505
onRowClick={onVariantShow}

src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx

Lines changed: 82 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ import {
1111
Attributes,
1212
VariantAttributeScope,
1313
} from "@dashboard/components/Attributes";
14+
import { DashboardCard } from "@dashboard/components/Card";
1415
import CardSpacer from "@dashboard/components/CardSpacer";
1516
import { ConfirmButtonTransitionState } from "@dashboard/components/ConfirmButton";
1617
import Grid from "@dashboard/components/Grid";
1718
import { DetailPageLayout } from "@dashboard/components/Layouts";
19+
import Link from "@dashboard/components/Link";
1820
import { Metadata } from "@dashboard/components/Metadata";
1921
import { Savebar } from "@dashboard/components/Savebar";
2022
import {
@@ -31,9 +33,11 @@ import { SubmitPromise } from "@dashboard/hooks/useForm";
3133
import useNavigator from "@dashboard/hooks/useNavigator";
3234
import { ProductDetailsChannelsAvailabilityCard } from "@dashboard/products/components/ProductVariantChannels/ChannelsAvailabilityCard";
3335
import { productUrl } from "@dashboard/products/urls";
36+
import { productTypeUrl } from "@dashboard/productTypes/urls";
3437
import { Container, FetchMoreProps, RelayToFlat, ReorderAction } from "@dashboard/types";
3538
import { mapEdgesToItems } from "@dashboard/utils/maps";
36-
import { defineMessages, useIntl } from "react-intl";
39+
import { Box, Text } from "@saleor/macaw-ui-next";
40+
import { defineMessages, FormattedMessage, useIntl } from "react-intl";
3741

3842
import { ProductShipping } from "../ProductShipping";
3943
import { ProductStocks } from "../ProductStocks";
@@ -228,51 +232,84 @@ export const ProductVariantCreatePage = ({
228232
product={product}
229233
onManageClick={toggleManageChannels}
230234
/>
231-
<Attributes
232-
title={intl.formatMessage(messages.attributesHeader)}
233-
attributes={data.attributes.filter(
234-
attribute =>
235-
attribute.data.variantAttributeScope ===
236-
VariantAttributeScope.NOT_VARIANT_SELECTION,
237-
)}
238-
attributeValues={attributeValues}
239-
loading={disabled}
240-
disabled={disabled}
241-
errors={errors}
242-
onChange={handlers.selectAttribute}
243-
onMultiChange={handlers.selectAttributeMultiple}
244-
onFileChange={handlers.selectAttributeFile}
245-
onReferencesRemove={handlers.selectAttributeReference}
246-
onReferencesAddClick={onAssignReferencesClick}
247-
onReferencesReorder={handlers.reorderAttributeValue}
248-
fetchAttributeValues={fetchAttributeValues}
249-
fetchMoreAttributeValues={fetchMoreAttributeValues}
250-
onAttributeSelectBlur={onAttributeSelectBlur}
251-
richTextGetters={attributeRichTextGetters}
252-
/>
253235
<CardSpacer />
254-
<Attributes
255-
title={intl.formatMessage(messages.attributesSelectionHeader)}
256-
attributes={data.attributes.filter(
257-
attribute =>
258-
attribute.data.variantAttributeScope ===
259-
VariantAttributeScope.VARIANT_SELECTION,
260-
)}
261-
attributeValues={attributeValues}
262-
loading={disabled}
263-
disabled={disabled}
264-
errors={errors}
265-
onChange={handlers.selectAttribute}
266-
onMultiChange={handlers.selectAttributeMultiple}
267-
onFileChange={handlers.selectAttributeFile}
268-
onReferencesRemove={handlers.selectAttributeReference}
269-
onReferencesAddClick={onAssignReferencesClick}
270-
onReferencesReorder={handlers.reorderAttributeValue}
271-
fetchAttributeValues={fetchAttributeValues}
272-
fetchMoreAttributeValues={fetchMoreAttributeValues}
273-
onAttributeSelectBlur={onAttributeSelectBlur}
274-
richTextGetters={attributeRichTextGetters}
275-
/>
236+
{product?.productType?.hasVariants && (
237+
<Attributes
238+
title={intl.formatMessage(messages.attributesHeader)}
239+
attributes={data.attributes.filter(
240+
attribute =>
241+
attribute.data.variantAttributeScope ===
242+
VariantAttributeScope.NOT_VARIANT_SELECTION,
243+
)}
244+
attributeValues={attributeValues}
245+
loading={disabled}
246+
disabled={disabled}
247+
errors={errors}
248+
onChange={handlers.selectAttribute}
249+
onMultiChange={handlers.selectAttributeMultiple}
250+
onFileChange={handlers.selectAttributeFile}
251+
onReferencesRemove={handlers.selectAttributeReference}
252+
onReferencesAddClick={onAssignReferencesClick}
253+
onReferencesReorder={handlers.reorderAttributeValue}
254+
fetchAttributeValues={fetchAttributeValues}
255+
fetchMoreAttributeValues={fetchMoreAttributeValues}
256+
onAttributeSelectBlur={onAttributeSelectBlur}
257+
richTextGetters={attributeRichTextGetters}
258+
/>
259+
)}
260+
{product?.productType?.hasVariants && (
261+
<>
262+
<CardSpacer />
263+
<Attributes
264+
title={intl.formatMessage(messages.attributesSelectionHeader)}
265+
attributes={data.attributes.filter(
266+
attribute =>
267+
attribute.data.variantAttributeScope ===
268+
VariantAttributeScope.VARIANT_SELECTION,
269+
)}
270+
attributeValues={attributeValues}
271+
loading={disabled}
272+
disabled={disabled}
273+
errors={errors}
274+
onChange={handlers.selectAttribute}
275+
onMultiChange={handlers.selectAttributeMultiple}
276+
onFileChange={handlers.selectAttributeFile}
277+
onReferencesRemove={handlers.selectAttributeReference}
278+
onReferencesAddClick={onAssignReferencesClick}
279+
onReferencesReorder={handlers.reorderAttributeValue}
280+
fetchAttributeValues={fetchAttributeValues}
281+
fetchMoreAttributeValues={fetchMoreAttributeValues}
282+
onAttributeSelectBlur={onAttributeSelectBlur}
283+
richTextGetters={attributeRichTextGetters}
284+
/>
285+
</>
286+
)}
287+
{!product?.productType?.hasVariants && data.attributes.length > 0 && (
288+
<DashboardCard paddingTop={6}>
289+
<DashboardCard.Content>
290+
<Box display="flex" flexDirection="column" gap={4} paddingBottom={4}>
291+
<Text size={5} fontWeight="bold">
292+
{intl.formatMessage(messages.attributesHeader)}
293+
</Text>
294+
<Text size={2} color="default2">
295+
<FormattedMessage
296+
id="zN0Eub"
297+
defaultMessage="This product type has {count, plural, one {# variant attribute} other {# variant attributes}} defined, but 'Product type uses Variant Attributes' is disabled. Edit {productTypeLink} product type to enable variant attributes."
298+
description="info message when hasVariants is false but variant attributes exist"
299+
values={{
300+
count: data.attributes.length,
301+
productTypeLink: product?.productType ? (
302+
<Link href={productTypeUrl(product.productType.id)} underline>
303+
{product.productType.name}
304+
</Link>
305+
) : null,
306+
}}
307+
/>
308+
</Text>
309+
</Box>
310+
</DashboardCard.Content>
311+
</DashboardCard>
312+
)}
276313
<CardSpacer />
277314
<ProductVariantCheckoutSettings
278315
data={data}

src/products/components/ProductVariantPage/ProductVariantPage.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ describe("ProductVariantPage - Reference Attribute Caching", () => {
202202
__typename: "ProductType" as const,
203203
id: "product-type-1",
204204
name: "Default Product Type",
205+
hasVariants: true,
205206
},
206207
channelListings: [
207208
{
@@ -237,6 +238,7 @@ describe("ProductVariantPage - Reference Attribute Caching", () => {
237238
defaultVariantId: "default-variant-id",
238239
defaultWeightUnit: "kg",
239240
errors: [],
241+
hasVariants: true,
240242
channelErrors: [],
241243
header: "Edit Variant",
242244
variant: mockVariant,

src/products/components/ProductVariantPage/ProductVariantPage.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ interface ProductVariantPageProps {
9595
header: string;
9696
channels: ChannelPriceData[];
9797
channelErrors: ProductChannelListingErrorFragment[];
98+
/** Whether the product type supports variant attributes */
99+
hasVariants: boolean;
98100
loading?: boolean;
99101
placeholderImage?: string;
100102
saveButtonBarState: ConfirmButtonTransitionState;
@@ -136,6 +138,7 @@ export const ProductVariantPage = ({
136138
defaultVariantId,
137139
defaultWeightUnit,
138140
errors: apiErrors,
141+
hasVariants,
139142
header,
140143
loading,
141144
placeholderImage,
@@ -288,7 +291,9 @@ export const ProductVariantPage = ({
288291
<VariantAttributesSection
289292
title={intl.formatMessage(messages.nonSelectionAttributes)}
290293
attributes={nonSelectionAttributes}
294+
totalAttributesCount={data.attributes.length}
291295
selectionAttributesExist={selectionAttributes.length > 0}
296+
hasVariants={hasVariants}
292297
attributeValues={attributeValues}
293298
productTypeName={variant.product.productType.name}
294299
productTypeUrl={productTypeUrl(variant.product.productType.id)}
@@ -306,7 +311,7 @@ export const ProductVariantPage = ({
306311
richTextGetters={attributeRichTextGetters}
307312
/>
308313
)}
309-
{selectionAttributes.length > 0 && (
314+
{hasVariants && selectionAttributes.length > 0 && (
310315
<>
311316
<CardSpacer />
312317
<Attributes

src/products/components/ProductVariantPage/VariantAttributesSection.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ import { FormattedMessage } from "react-intl";
1717
interface VariantAttributesSectionProps extends AttributeRowHandlers {
1818
title: ReactNode;
1919
attributes: AttributeInput[];
20+
/** Total count of all variant attributes (selection + non-selection) */
21+
totalAttributesCount: number;
2022
selectionAttributesExist: boolean;
23+
/** Whether the product type supports variant attributes */
24+
hasVariants: boolean;
2125
attributeValues: AttributeValueFragment[];
2226
productTypeName: string;
2327
productTypeUrl: string;
@@ -30,7 +34,9 @@ interface VariantAttributesSectionProps extends AttributeRowHandlers {
3034
export const VariantAttributesSection = ({
3135
title,
3236
attributes,
37+
totalAttributesCount,
3338
selectionAttributesExist,
39+
hasVariants,
3440
attributeValues,
3541
productTypeName,
3642
productTypeUrl,
@@ -89,6 +95,37 @@ export const VariantAttributesSection = ({
8995
</Box>
9096
);
9197

98+
// When hasVariants is false but attributes exist, show info message instead of attribute fields
99+
if (!hasVariants && totalAttributesCount > 0) {
100+
return (
101+
<>
102+
<CardSpacer />
103+
<DashboardCard paddingTop={6}>
104+
<DashboardCard.Content>
105+
<Box display="flex" flexDirection="column" gap={4} paddingBottom={4}>
106+
{titleWithTooltip}
107+
<Text size={2} color="default2">
108+
<FormattedMessage
109+
id="zN0Eub"
110+
defaultMessage="This product type has {count, plural, one {# variant attribute} other {# variant attributes}} defined, but 'Product type uses Variant Attributes' is disabled. Edit {productTypeLink} product type to enable variant attributes."
111+
description="info message when hasVariants is false but variant attributes exist"
112+
values={{
113+
count: totalAttributesCount,
114+
productTypeLink: (
115+
<Link href={productTypeUrl} underline>
116+
{productTypeName}
117+
</Link>
118+
),
119+
}}
120+
/>
121+
</Text>
122+
</Box>
123+
</DashboardCard.Content>
124+
</DashboardCard>
125+
</>
126+
);
127+
}
128+
92129
// Only show empty state when both non-selection and selection attributes are empty
93130
if (attributes.length === 0) {
94131
if (selectionAttributesExist) {

0 commit comments

Comments
 (0)