Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 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
6 changes: 6 additions & 0 deletions .changeset/large-vans-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"saleor-dashboard": patch
---

Added filtering UI to "Assign Model" dialogs.
This allows filtering by "Model type" when assigning values to Model reference attributes in products and product variants.
42 changes: 22 additions & 20 deletions locale/defaultMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2262,10 +2262,6 @@
"BNTZLv": {
"string": "Order #{orderId} was placed from draft"
},
"BOYzu+": {
"context": "search results",
"string": "No models found"
},
"BR8au7": {
"context": "delete channel",
"string": "Select channel that you wish to move existing orders to."
Expand Down Expand Up @@ -4020,6 +4016,10 @@
"context": "product rating",
"string": "Product Rating"
},
"L7j412": {
"context": "no models placeholder",
"string": "No models found"
},
"L8J/jr": {
"context": "status pill when order is fully paid",
"string": "Fully Captured"
Expand Down Expand Up @@ -4673,6 +4673,10 @@
"context": "stock input label",
"string": "Initial stock (all warehouses)"
},
"OpgSjI": {
"context": "dialog header",
"string": "Assign Model"
},
"OqYCQ4": {
"context": "allow legacy gift card use description",
"string": "When enabled, gift card can be applied to a checkout using addPromoCode mutation."
Expand Down Expand Up @@ -5107,6 +5111,9 @@
"context": "tracking number of the shipment",
"string": "Tracking number"
},
"R4gIJc": {
"string": "Search by model name..."
},
"R98JLZ": {
"context": "OrderCustomer Fulfillment from All Warehouses",
"string": "Fulfill from All Warehouses"
Expand Down Expand Up @@ -6107,6 +6114,10 @@
"context": "shipping method not available label",
"string": "(not available)"
},
"X1WqwZ": {
"context": "button, assign models and save",
"string": "Assign and save"
},
"X6PF8z": {
"context": "entity (product, collection, shipping method) name",
"string": "Name"
Expand Down Expand Up @@ -6493,10 +6504,6 @@
"YzLUXA": {
"string": "Ensure this value is greater than 0."
},
"Z+m5hG": {
"context": "dialog header",
"string": "Assign model"
},
"Z/7hyu": {
"context": "card balance label",
"string": "Card Balance"
Expand All @@ -6511,10 +6518,6 @@
"Z6QAbw": {
"string": "This variant does not have any digital content"
},
"Z768vg": {
"context": "placeholder",
"string": "Search by model name, etc..."
},
"Z7Tf8e": {
"context": "stock exceeded dialog description",
"string": "Stock for items shown below are not enough to prepare fulfillment:"
Expand Down Expand Up @@ -7217,10 +7220,6 @@
"context": "page header",
"string": "Create New Category"
},
"ch96Wv": {
"context": "assign reference to a model, button",
"string": "Assign and save"
},
"chvryR": {
"context": "order history message",
"string": "Invoice was requested by {requestedBy}"
Expand Down Expand Up @@ -7559,6 +7558,9 @@
"context": "order transaction refund summary label",
"string": "Selected products"
},
"fEeZGG": {
"string": "Search Models"
},
"fEfCtO": {
"context": "voucher discount type",
"string": "Percentage"
Expand Down Expand Up @@ -8471,10 +8473,6 @@
"kTr2o8": {
"string": "Attribute name"
},
"kTt3D2": {
"context": "label",
"string": "Search models"
},
"kVKTwC": {
"context": "order expiration card title",
"string": "Order expiration"
Expand Down Expand Up @@ -10604,6 +10602,10 @@
"context": "Badge shown when channel is marked for removal",
"string": "to remove"
},
"wBRaTi": {
"context": "no models placeholder",
"string": "No models available"
},
"wDUBLR": {
"context": "order refund amount",
"string": "Proposed refund amount"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import {
AttributeEntityTypeEnum,
AttributeInputTypeEnum,
PageWhereInput,
ProductWhereInput,
SearchCategoriesQuery,
SearchCollectionsQuery,
SearchPagesQuery,
} from "@dashboard/graphql";
import { RelayToFlat } from "@dashboard/types";
import { defineMessages, useIntl } from "react-intl";

import AssignCategoryDialog from "../AssignCategoryDialog";
import AssignCollectionDialog from "../AssignCollectionDialog";
import AssignContainerDialog from "../AssignContainerDialog";
import AssignModelDialog from "../AssignModelDialog";
import AssignProductDialog, { AssignProductDialogProps } from "../AssignProductDialog";
import AssignVariantDialog from "../AssignVariantDialog";
import { AttributeInput } from "../Attributes";
import { InitialPageConstraints } from "../ModalFilters/entityConfigs/ModalPageFilterProvider";
import { InitialConstraints } from "../ModalFilters/entityConfigs/ModalProductFilterProvider";
import {
filterCategoriesByAttributeValues,
Expand All @@ -22,43 +24,31 @@ import {
filterProductsByAttributeValues,
} from "./utils";

const pagesMessages = defineMessages({
confirmBtn: {
id: "ch96Wv",
defaultMessage: "Assign and save",
description: "assign reference to a model, button",
},
header: {
id: "Z+m5hG",
defaultMessage: "Assign model",
description: "dialog header",
},
searchLabel: {
id: "kTt3D2",
defaultMessage: "Search models",
description: "label",
},
searchPlaceholder: {
id: "Z768vg",
defaultMessage: "Search by model name, etc...",
description: "placeholder",
},
noPagesFound: {
id: "BOYzu+",
defaultMessage: "No models found",
description: "search results",
},
});
export type ProductFilterChangeHandler = (
filterVariables: ProductWhereInput,
channel: string | undefined,
query: string,
) => void;

type AssignAttributeValueDialogProps = AssignProductDialogProps & {
export type PageFilterChangeHandler = (filterVariables: PageWhereInput, query: string) => void;

export type AssignAttributeValueDialogFilterChangeMap = {
[AttributeEntityTypeEnum.PRODUCT]?: ProductFilterChangeHandler;
[AttributeEntityTypeEnum.PRODUCT_VARIANT]?: ProductFilterChangeHandler;
[AttributeEntityTypeEnum.PAGE]?: PageFilterChangeHandler;
};

type AssignAttributeValueDialogProps = Omit<AssignProductDialogProps, "onFilterChange"> & {
entityType: AttributeEntityTypeEnum;
attribute: AttributeInput;
pages: RelayToFlat<SearchPagesQuery["search"]>;
collections: RelayToFlat<SearchCollectionsQuery["search"]>;
categories: RelayToFlat<SearchCategoriesQuery["search"]>;
initialConstraints?: InitialConstraints;
initialConstraints?: InitialConstraints & InitialPageConstraints;
// onFetch is required for non-product dialogs (containers, variants, collections, categories)
onFetch: (value: string) => void;
// Generic filter callback map by entity type.
onFilterChange?: AssignAttributeValueDialogFilterChangeMap;
};

const getSingleOrMultipleDialogProps = (attribute: AttributeInput) => {
Expand All @@ -73,41 +63,35 @@ const getSingleOrMultipleDialogProps = (attribute: AttributeInput) => {
return { selectedId, selectionMode: "single" as const };
};

const AssignAttributeValueDialog = ({
entityType,
pages,
products,
collections,
categories,
attribute,
labels,
initialConstraints,
...rest
}: AssignAttributeValueDialogProps) => {
const intl = useIntl();
const AssignAttributeValueDialog = (props: AssignAttributeValueDialogProps) => {
const {
entityType,
pages,
products,
collections,
categories,
attribute,
initialConstraints,
onFilterChange,
...rest
} = props;
Comment on lines +66 to +77
Copy link
Member

Choose a reason for hiding this comment

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

I guess not needed change, maybe we can add some static check or claude rule to pick one of the syntaxes so agents dont try to change that with no reason

const filteredProducts = filterProductsByAttributeValues(products, attribute);
const filteredPages = filterPagesByAttributeValues(pages, attribute);
const filteredCollections = filterCollectionsByAttributeValues(collections, attribute);
const filteredCategories = filterCategoriesByAttributeValues(categories, attribute);
const productFilterChange = onFilterChange?.[AttributeEntityTypeEnum.PRODUCT];
const variantFilterChange =
onFilterChange?.[AttributeEntityTypeEnum.PRODUCT_VARIANT] ??
onFilterChange?.[AttributeEntityTypeEnum.PRODUCT];
const pageFilterChange = onFilterChange?.[AttributeEntityTypeEnum.PAGE];

switch (entityType) {
case AttributeEntityTypeEnum.PAGE:
return (
<AssignContainerDialog
containers={
filteredPages?.map(page => ({
id: page.id,
name: page.title,
})) ?? []
}
emptyMessage={intl.formatMessage(pagesMessages.noPagesFound)}
labels={{
confirmBtn: intl.formatMessage(pagesMessages.confirmBtn),
label: intl.formatMessage(pagesMessages.searchLabel),
placeholder: intl.formatMessage(pagesMessages.searchPlaceholder),
title: intl.formatMessage(pagesMessages.header),
...labels,
}}
<AssignModelDialog
pages={filteredPages ?? []}
initialConstraints={initialConstraints}
onFilterChange={pageFilterChange}
{...getSingleOrMultipleDialogProps(attribute)}
{...rest}
/>
Expand All @@ -117,6 +101,7 @@ const AssignAttributeValueDialog = ({
<AssignProductDialog
products={filteredProducts ?? []}
initialConstraints={initialConstraints}
onFilterChange={productFilterChange}
{...getSingleOrMultipleDialogProps(attribute)}
{...rest}
/>
Expand All @@ -126,6 +111,7 @@ const AssignAttributeValueDialog = ({
<AssignVariantDialog
products={filteredProducts}
initialConstraints={initialConstraints}
onFilterChange={variantFilterChange}
{...getSingleOrMultipleDialogProps(attribute)}
{...rest}
/>
Expand Down
Loading
Loading