Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions locale/defaultMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1100,10 +1100,6 @@
"4YJHut": {
"string": "Clear search"
},
"4YtpqB": {
"context": "tooltip when generate variants is disabled due to unsupported required attributes",
"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"
},
"4YyeCx": {
"context": "label for order total amount",
"string": "Order total"
Expand Down Expand Up @@ -1731,6 +1727,10 @@
"context": "order refund amount, input button",
"string": "Refund {currency} {amount}"
},
"8FDx9i": {
"context": "tooltip when generate variants is disabled due to product type configuration (hasVariants=false or no selection attributes)",
"string": "To use the Generator, this product type needs:{newline}{newline}• 'Product type uses Variant Attributes' enabled{newline}• Selection variant attributes defined"
},
"8GC/ah": {
"context": "variants section name",
"string": "Variants"
Expand Down Expand Up @@ -1848,6 +1848,10 @@
"context": "dialog content",
"string": "{counter,plural,one{Are you sure you want to publish this model?} other{Are you sure you want to publish {displayQuantity} models?}}"
},
"9+iLpf": {
"context": "link text to product type settings",
"string": "Configure in product type settings"
},
"91vQMc": {
"string": "order management"
},
Expand Down Expand Up @@ -7725,6 +7729,10 @@
"context": "tooltip shown when shipping method is selected but no other options exist",
"string": "No alternative shipping methods available"
},
"gDvd3v": {
"context": "tooltip when generate variants is disabled due to unsupported required attributes",
"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"
},
"gE6aiQ": {
"context": "PageTypeDeleteWarningDialog single no assigned items description",
"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."
Expand Down Expand Up @@ -8600,10 +8608,6 @@
"context": "Webhook subscription query card title",
"string": "Subscription Query"
},
"lEx2zw": {
"context": "tooltip when generate variants is disabled due to no selection attributes",
"string": "No selection attributes defined for this product type."
},
"lF+VJQ": {
"context": "Shipment information card header",
"string": "Shipment information"
Expand Down Expand Up @@ -11058,6 +11062,10 @@
"context": "gift card removed success alert message",
"string": "{selectedItemsCount,plural,one{Successfully deleted gift card} other{Successfully deleted gift cards}}"
},
"zN0Eub": {
"context": "info message when hasVariants is false but variant attributes exist",
"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."
},
"zQvVDJ": {
"string": "All"
},
Expand Down
14 changes: 12 additions & 2 deletions src/components/Datagrid/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,21 @@ const ButtonFullScreen: FC<PropsWithChildren<ButtonFullScreenProps>> = ({

interface ButtonAddRowProps {
onAddRow: React.MouseEventHandler<HTMLButtonElement>;
disabled?: boolean;
}

const ButtonAddRow: FC<PropsWithChildren<ButtonAddRowProps>> = ({ onAddRow, children }) => {
const ButtonAddRow: FC<PropsWithChildren<ButtonAddRowProps>> = ({
onAddRow,
disabled,
children,
}) => {
return (
<Button data-test-id="button-add-variant" variant="secondary" onClick={onAddRow}>
<Button
data-test-id="button-add-variant"
variant="secondary"
onClick={onAddRow}
disabled={disabled}
>
<PlusIcon />
{children}
</Button>
Expand Down
1 change: 1 addition & 0 deletions src/fragments/products.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ export const fragmentVariant = gql`
productType {
id
name
hasVariants
}
channelListings {
id
Expand Down
3 changes: 3 additions & 0 deletions src/graphql/hooks.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3010,6 +3010,7 @@ export const ProductVariantFragmentDoc = gql`
productType {
id
name
hasVariants
}
channelListings {
id
Expand Down Expand Up @@ -15811,6 +15812,8 @@ export const ProductVariantCreateDataDocument = gql`
name
productType {
id
name
hasVariants
selectionVariantAttributes: variantAttributes(
variantSelection: VARIANT_SELECTION
) {
Expand Down
10 changes: 5 additions & 5 deletions src/graphql/types.generated.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ const ProductUpdatePage = ({
)}
<ProductVariants
productId={productId}
productTypeId={product?.productType.id ?? ""}
productName={product?.name}
Comment on lines 491 to 494
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

productTypeId is being passed as product?.productType.id ?? "". An empty string will make productTypeUrl(productTypeId) resolve to /product-types/ (list) instead of a specific product type, which is especially confusing now that the disabled Generator tooltip links to product type settings. Prefer not passing a sentinel "" (e.g. render ProductVariants only when the id is available, or make the prop optional and hide the link when missing).

Copilot uses AI. Check for mistakes.
errors={variantListErrors}
channels={listings}
Expand All @@ -498,6 +499,7 @@ const ProductUpdatePage = ({
variantAttributes={product?.productType.variantAttributes}
selectionVariantAttributes={product?.productType.selectionVariantAttributes}
nonSelectionVariantAttributes={product?.productType.nonSelectionVariantAttributes}
hasVariants={hasVariants ?? false}
onAttributeValuesSearch={onAttributeValuesSearch}
onChange={handlers.changeVariants}
onRowClick={onVariantShow}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import {
Attributes,
VariantAttributeScope,
} from "@dashboard/components/Attributes";
import { DashboardCard } from "@dashboard/components/Card";
import CardSpacer from "@dashboard/components/CardSpacer";
import { ConfirmButtonTransitionState } from "@dashboard/components/ConfirmButton";
import Grid from "@dashboard/components/Grid";
import { DetailPageLayout } from "@dashboard/components/Layouts";
import Link from "@dashboard/components/Link";
import { Metadata } from "@dashboard/components/Metadata";
import { Savebar } from "@dashboard/components/Savebar";
import {
Expand All @@ -31,9 +33,11 @@ import { SubmitPromise } from "@dashboard/hooks/useForm";
import useNavigator from "@dashboard/hooks/useNavigator";
import { ProductDetailsChannelsAvailabilityCard } from "@dashboard/products/components/ProductVariantChannels/ChannelsAvailabilityCard";
import { productUrl } from "@dashboard/products/urls";
import { productTypeUrl } from "@dashboard/productTypes/urls";
import { Container, FetchMoreProps, RelayToFlat, ReorderAction } from "@dashboard/types";
import { mapEdgesToItems } from "@dashboard/utils/maps";
import { defineMessages, useIntl } from "react-intl";
import { Box, Text } from "@saleor/macaw-ui-next";
import { defineMessages, FormattedMessage, useIntl } from "react-intl";

import { ProductShipping } from "../ProductShipping";
import { ProductStocks } from "../ProductStocks";
Expand Down Expand Up @@ -228,51 +232,84 @@ export const ProductVariantCreatePage = ({
product={product}
onManageClick={toggleManageChannels}
/>
<Attributes
title={intl.formatMessage(messages.attributesHeader)}
attributes={data.attributes.filter(
attribute =>
attribute.data.variantAttributeScope ===
VariantAttributeScope.NOT_VARIANT_SELECTION,
)}
attributeValues={attributeValues}
loading={disabled}
disabled={disabled}
errors={errors}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMultiple}
onFileChange={handlers.selectAttributeFile}
onReferencesRemove={handlers.selectAttributeReference}
onReferencesAddClick={onAssignReferencesClick}
onReferencesReorder={handlers.reorderAttributeValue}
fetchAttributeValues={fetchAttributeValues}
fetchMoreAttributeValues={fetchMoreAttributeValues}
onAttributeSelectBlur={onAttributeSelectBlur}
richTextGetters={attributeRichTextGetters}
/>
<CardSpacer />
<Attributes
title={intl.formatMessage(messages.attributesSelectionHeader)}
attributes={data.attributes.filter(
attribute =>
attribute.data.variantAttributeScope ===
VariantAttributeScope.VARIANT_SELECTION,
)}
attributeValues={attributeValues}
loading={disabled}
disabled={disabled}
errors={errors}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMultiple}
onFileChange={handlers.selectAttributeFile}
onReferencesRemove={handlers.selectAttributeReference}
onReferencesAddClick={onAssignReferencesClick}
onReferencesReorder={handlers.reorderAttributeValue}
fetchAttributeValues={fetchAttributeValues}
fetchMoreAttributeValues={fetchMoreAttributeValues}
onAttributeSelectBlur={onAttributeSelectBlur}
richTextGetters={attributeRichTextGetters}
/>
{product?.productType?.hasVariants && (
<Attributes
title={intl.formatMessage(messages.attributesHeader)}
attributes={data.attributes.filter(
attribute =>
attribute.data.variantAttributeScope ===
VariantAttributeScope.NOT_VARIANT_SELECTION,
)}
attributeValues={attributeValues}
loading={disabled}
disabled={disabled}
errors={errors}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMultiple}
onFileChange={handlers.selectAttributeFile}
onReferencesRemove={handlers.selectAttributeReference}
onReferencesAddClick={onAssignReferencesClick}
onReferencesReorder={handlers.reorderAttributeValue}
fetchAttributeValues={fetchAttributeValues}
fetchMoreAttributeValues={fetchMoreAttributeValues}
onAttributeSelectBlur={onAttributeSelectBlur}
richTextGetters={attributeRichTextGetters}
/>
)}
{product?.productType?.hasVariants && (
<>
<CardSpacer />
<Attributes
title={intl.formatMessage(messages.attributesSelectionHeader)}
attributes={data.attributes.filter(
attribute =>
attribute.data.variantAttributeScope ===
VariantAttributeScope.VARIANT_SELECTION,
)}
attributeValues={attributeValues}
loading={disabled}
disabled={disabled}
errors={errors}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMultiple}
onFileChange={handlers.selectAttributeFile}
onReferencesRemove={handlers.selectAttributeReference}
onReferencesAddClick={onAssignReferencesClick}
onReferencesReorder={handlers.reorderAttributeValue}
fetchAttributeValues={fetchAttributeValues}
fetchMoreAttributeValues={fetchMoreAttributeValues}
onAttributeSelectBlur={onAttributeSelectBlur}
richTextGetters={attributeRichTextGetters}
/>
</>
)}
{!product?.productType?.hasVariants && data.attributes.length > 0 && (
<DashboardCard paddingTop={6}>
<DashboardCard.Content>
<Box display="flex" flexDirection="column" gap={4} paddingBottom={4}>
<Text size={5} fontWeight="bold">
{intl.formatMessage(messages.attributesHeader)}
</Text>
<Text size={2} color="default2">
<FormattedMessage
id="zN0Eub"
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."
description="info message when hasVariants is false but variant attributes exist"
values={{
count: data.attributes.length,
productTypeLink: product?.productType ? (
<Link href={productTypeUrl(product.productType.id)} underline>
{product.productType.name}
</Link>
) : null,
}}
/>
</Text>
</Box>
</DashboardCard.Content>
</DashboardCard>
)}
<CardSpacer />
<ProductVariantCheckoutSettings
data={data}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ describe("ProductVariantPage - Reference Attribute Caching", () => {
__typename: "ProductType" as const,
id: "product-type-1",
name: "Default Product Type",
hasVariants: true,
},
channelListings: [
{
Expand Down Expand Up @@ -237,6 +238,7 @@ describe("ProductVariantPage - Reference Attribute Caching", () => {
defaultVariantId: "default-variant-id",
defaultWeightUnit: "kg",
errors: [],
hasVariants: true,
channelErrors: [],
header: "Edit Variant",
variant: mockVariant,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ interface ProductVariantPageProps {
header: string;
channels: ChannelPriceData[];
channelErrors: ProductChannelListingErrorFragment[];
/** Whether the product type supports variant attributes */
hasVariants: boolean;
loading?: boolean;
placeholderImage?: string;
saveButtonBarState: ConfirmButtonTransitionState;
Expand Down Expand Up @@ -136,6 +138,7 @@ export const ProductVariantPage = ({
defaultVariantId,
defaultWeightUnit,
errors: apiErrors,
hasVariants,
header,
loading,
placeholderImage,
Expand Down Expand Up @@ -288,7 +291,9 @@ export const ProductVariantPage = ({
<VariantAttributesSection
title={intl.formatMessage(messages.nonSelectionAttributes)}
attributes={nonSelectionAttributes}
totalAttributesCount={data.attributes.length}
selectionAttributesExist={selectionAttributes.length > 0}
hasVariants={hasVariants}
attributeValues={attributeValues}
productTypeName={variant.product.productType.name}
productTypeUrl={productTypeUrl(variant.product.productType.id)}
Expand All @@ -306,7 +311,7 @@ export const ProductVariantPage = ({
richTextGetters={attributeRichTextGetters}
/>
)}
{selectionAttributes.length > 0 && (
{hasVariants && selectionAttributes.length > 0 && (
<>
<CardSpacer />
Comment on lines +314 to 316
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New UI behavior is introduced here for hasVariants (hiding selection attributes and showing an informational card when variant attributes exist but the product type has variants disabled), but the existing ProductVariantPage.test.tsx only exercises hasVariants: true. Add coverage for the hasVariants: false cases to prevent regressions.

Copilot uses AI. Check for mistakes.
<Attributes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ import { FormattedMessage } from "react-intl";
interface VariantAttributesSectionProps extends AttributeRowHandlers {
title: ReactNode;
attributes: AttributeInput[];
/** Total count of all variant attributes (selection + non-selection) */
totalAttributesCount: number;
selectionAttributesExist: boolean;
/** Whether the product type supports variant attributes */
hasVariants: boolean;
attributeValues: AttributeValueFragment[];
productTypeName: string;
productTypeUrl: string;
Expand All @@ -30,7 +34,9 @@ interface VariantAttributesSectionProps extends AttributeRowHandlers {
export const VariantAttributesSection = ({
title,
attributes,
totalAttributesCount,
selectionAttributesExist,
hasVariants,
attributeValues,
productTypeName,
productTypeUrl,
Expand Down Expand Up @@ -89,6 +95,37 @@ export const VariantAttributesSection = ({
</Box>
);

// When hasVariants is false but attributes exist, show info message instead of attribute fields
if (!hasVariants && totalAttributesCount > 0) {
return (
<>
<CardSpacer />
<DashboardCard paddingTop={6}>
<DashboardCard.Content>
<Box display="flex" flexDirection="column" gap={4} paddingBottom={4}>
{titleWithTooltip}
<Text size={2} color="default2">
<FormattedMessage
id="zN0Eub"
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."
description="info message when hasVariants is false but variant attributes exist"
values={{
count: totalAttributesCount,
productTypeLink: (
<Link href={productTypeUrl} underline>
{productTypeName}
</Link>
),
}}
/>
</Text>
</Box>
</DashboardCard.Content>
</DashboardCard>
</>
);
}

// Only show empty state when both non-selection and selection attributes are empty
if (attributes.length === 0) {
if (selectionAttributesExist) {
Expand Down
Loading
Loading