fix(dashboard,medusa,types): allow searching for promotion rule options (#12028)

* fix: allow searching for promotion rule options

* fix: allow searching for promotion rule options

* add changeset

* cleanup
This commit is contained in:
Kasper Fabricius Kristensen
2025-03-28 14:45:05 +01:00
committed by GitHub
parent 6d8390a529
commit 3dba58785f
6 changed files with 55 additions and 65 deletions

View File

@@ -0,0 +1,7 @@
---
"@medusajs/dashboard": patch
"@medusajs/types": patch
"@medusajs/medusa": patch
---
fix(dashboard,medusa,types): allow searching for promotion rule options

View File

@@ -197,7 +197,7 @@ const ComboboxImpl = <T extends Value = string>(
const showTag = hasValue && isArrayValue
const showSelected = showTag && !searchValue && !open
const hideInput = !isArrayValue && !open
const hideInput = !isArrayValue && hasValue && !open
const selectedLabel = options.find((o) => o.value === selectedValues)?.label
const hidePlaceholder = showSelected || open

View File

@@ -28,6 +28,7 @@ export const useComboboxData = <
defaultValue,
defaultValueKey,
pageSize = 10,
enabled = true,
}: {
queryKey: QueryKey
queryFn: (params: TParams) => Promise<TResponse>
@@ -35,6 +36,7 @@ export const useComboboxData = <
defaultValueKey?: keyof TParams
defaultValue?: string | string[]
pageSize?: number
enabled?: boolean
}) => {
const { searchValue, onSearchValueChange, query } = useDebouncedSearch()
@@ -47,7 +49,7 @@ export const useComboboxData = <
limit: Array.isArray(defaultValue) ? defaultValue.length : 1,
} as TParams)
},
enabled: !!defaultValue,
enabled: !!defaultValue && enabled,
})
const { data, ...rest } = useInfiniteQuery({
@@ -65,6 +67,7 @@ export const useComboboxData = <
return moreItemsExist ? lastPage.offset + lastPage.limit : undefined
},
placeholderData: keepPreviousData,
enabled: enabled,
})
const options = data?.pages.flatMap((page) => getOptions(page)) ?? []
@@ -74,7 +77,8 @@ export const useComboboxData = <
* If there are no options and the query is empty, then the combobox should be disabled,
* as there is no data to search for.
*/
const disabled = !rest.isPending && !options.length && !searchValue
const disabled =
(!rest.isPending && !options.length && !searchValue) || !enabled
// make sure that the default value is included in the options
if (defaultValue && defaultOptions.length && !searchValue) {

View File

@@ -1,10 +1,11 @@
import { RuleAttributeOptionsResponse, StoreDTO } from "@medusajs/types"
import { Input, Select } from "@medusajs/ui"
import { HttpTypes } from "@medusajs/types"
import { Input } from "@medusajs/ui"
import { useWatch } from "react-hook-form"
import { Form } from "../../../../../../components/common/form"
import { Combobox } from "../../../../../../components/inputs/combobox"
import { usePromotionRuleValues } from "../../../../../../hooks/api/promotions"
import { useStore } from "../../../../../../hooks/api/store"
import { useComboboxData } from "../../../../../../hooks/use-combobox-data"
import { sdk } from "../../../../../../lib/client"
type RuleValueFormFieldType = {
form: any
@@ -16,11 +17,11 @@ type RuleValueFormFieldType = {
name: string
operator: string
fieldRule: any
attributes: RuleAttributeOptionsResponse[]
attributes: HttpTypes.AdminRuleAttributeOption[]
ruleType: "rules" | "target-rules" | "buy-rules"
}
const buildFilters = (attribute?: string, store?: StoreDTO) => {
const buildFilters = (attribute?: string, store?: HttpTypes.AdminStore) => {
if (!attribute || !store) {
return {}
}
@@ -49,17 +50,25 @@ export const RuleValueFormField = ({
)
const { store, isLoading: isStoreLoading } = useStore()
const { values: options = [] } = usePromotionRuleValues(
ruleType,
attribute?.id!,
buildFilters(attribute?.id, store),
{
enabled:
!!attribute?.id &&
["select", "multiselect"].includes(attribute.field_type) &&
!isStoreLoading,
}
)
const comboboxData = useComboboxData({
queryFn: async (params) => {
return await sdk.admin.promotion.listRuleValues(
ruleType,
attribute?.id!,
{
...params,
...buildFilters(attribute?.id, store!),
}
)
},
enabled:
!!attribute?.id &&
["select", "multiselect"].includes(attribute.field_type) &&
!isStoreLoading,
getOptions: (data) => data.values,
queryKey: ["rule-value-options", ruleType, attribute?.id],
})
const watchOperator = useWatch({
control: form.control,
@@ -103,54 +112,21 @@ export const RuleValueFormField = ({
<Form.ErrorMessage />
</Form.Item>
)
} else if (watchOperator === "eq") {
return (
<Form.Item className="basis-1/2">
<Form.Control>
<Select
{...field}
value={
Array.isArray(field.value) ? field.value[0] : field.value
}
onValueChange={onChange}
disabled={!fieldRule.attribute}
>
<Select.Trigger ref={ref} className="bg-ui-bg-base">
<Select.Value placeholder="Select Value" />
</Select.Trigger>
<Select.Content>
{options?.map((option, i) => (
<Select.Item
key={`${identifier}-value-option-${i}`}
value={option.value}
>
<span className="text-ui-fg-subtle">
{option.label}
</span>
</Select.Item>
))}
</Select.Content>
</Select>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
} else {
return (
<Form.Item className="basis-1/2">
<Form.Control>
<Combobox
{...field}
{...comboboxData}
multiple={watchOperator !== "eq"}
ref={ref}
placeholder="Select Values"
options={options}
placeholder={
watchOperator === "eq" ? "Select Value" : "Select Values"
}
onChange={onChange}
className="bg-ui-bg-base"
disabled={!fieldRule.attribute}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)

View File

@@ -61,12 +61,12 @@ export interface RuleValueOptionsResponse {
/**
* @experimental
*/
export type AdminRuleValueOptionsListResponse = {
export type AdminRuleValueOptionsListResponse = PaginatedResponse<{
/**
* The list of rule value options.
*/
values: AdminRuleValueOption[]
}
}>
export type AdminPromotionRuleBatchResponse = BatchResponse<AdminPromotionRule>

View File

@@ -1,18 +1,18 @@
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/framework/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
import { HttpTypes } from "@medusajs/framework/types"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/framework/utils"
import {
ruleQueryConfigurations,
validateRuleAttribute,
validateRuleType,
} from "../../../utils"
import { AdminGetPromotionRuleParamsType } from "../../../validators"
import { HttpTypes } from "@medusajs/framework/types"
/*
This endpoint returns all the potential values for rules (promotion rules, target rules and buy rules)
@@ -49,7 +49,7 @@ export const GET = async (
applicationMethodType,
})
const { rows } = await remoteQuery(
const { rows, metadata } = await remoteQuery(
remoteQueryObjectFromString({
entryPoint: queryConfig.entryPoint,
variables: {
@@ -67,5 +67,8 @@ export const GET = async (
res.json({
values,
count: metadata.count,
offset: metadata.skip,
limit: metadata.take,
})
}