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:
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/dashboard": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(dashboard,medusa,types): allow searching for promotion rule options
|
||||||
@@ -197,7 +197,7 @@ const ComboboxImpl = <T extends Value = string>(
|
|||||||
const showTag = hasValue && isArrayValue
|
const showTag = hasValue && isArrayValue
|
||||||
const showSelected = showTag && !searchValue && !open
|
const showSelected = showTag && !searchValue && !open
|
||||||
|
|
||||||
const hideInput = !isArrayValue && !open
|
const hideInput = !isArrayValue && hasValue && !open
|
||||||
const selectedLabel = options.find((o) => o.value === selectedValues)?.label
|
const selectedLabel = options.find((o) => o.value === selectedValues)?.label
|
||||||
|
|
||||||
const hidePlaceholder = showSelected || open
|
const hidePlaceholder = showSelected || open
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export const useComboboxData = <
|
|||||||
defaultValue,
|
defaultValue,
|
||||||
defaultValueKey,
|
defaultValueKey,
|
||||||
pageSize = 10,
|
pageSize = 10,
|
||||||
|
enabled = true,
|
||||||
}: {
|
}: {
|
||||||
queryKey: QueryKey
|
queryKey: QueryKey
|
||||||
queryFn: (params: TParams) => Promise<TResponse>
|
queryFn: (params: TParams) => Promise<TResponse>
|
||||||
@@ -35,6 +36,7 @@ export const useComboboxData = <
|
|||||||
defaultValueKey?: keyof TParams
|
defaultValueKey?: keyof TParams
|
||||||
defaultValue?: string | string[]
|
defaultValue?: string | string[]
|
||||||
pageSize?: number
|
pageSize?: number
|
||||||
|
enabled?: boolean
|
||||||
}) => {
|
}) => {
|
||||||
const { searchValue, onSearchValueChange, query } = useDebouncedSearch()
|
const { searchValue, onSearchValueChange, query } = useDebouncedSearch()
|
||||||
|
|
||||||
@@ -47,7 +49,7 @@ export const useComboboxData = <
|
|||||||
limit: Array.isArray(defaultValue) ? defaultValue.length : 1,
|
limit: Array.isArray(defaultValue) ? defaultValue.length : 1,
|
||||||
} as TParams)
|
} as TParams)
|
||||||
},
|
},
|
||||||
enabled: !!defaultValue,
|
enabled: !!defaultValue && enabled,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data, ...rest } = useInfiniteQuery({
|
const { data, ...rest } = useInfiniteQuery({
|
||||||
@@ -65,6 +67,7 @@ export const useComboboxData = <
|
|||||||
return moreItemsExist ? lastPage.offset + lastPage.limit : undefined
|
return moreItemsExist ? lastPage.offset + lastPage.limit : undefined
|
||||||
},
|
},
|
||||||
placeholderData: keepPreviousData,
|
placeholderData: keepPreviousData,
|
||||||
|
enabled: enabled,
|
||||||
})
|
})
|
||||||
|
|
||||||
const options = data?.pages.flatMap((page) => getOptions(page)) ?? []
|
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,
|
* If there are no options and the query is empty, then the combobox should be disabled,
|
||||||
* as there is no data to search for.
|
* 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
|
// make sure that the default value is included in the options
|
||||||
if (defaultValue && defaultOptions.length && !searchValue) {
|
if (defaultValue && defaultOptions.length && !searchValue) {
|
||||||
|
|||||||
+30
-54
@@ -1,10 +1,11 @@
|
|||||||
import { RuleAttributeOptionsResponse, StoreDTO } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { Input, Select } from "@medusajs/ui"
|
import { Input } from "@medusajs/ui"
|
||||||
import { useWatch } from "react-hook-form"
|
import { useWatch } from "react-hook-form"
|
||||||
import { Form } from "../../../../../../components/common/form"
|
import { Form } from "../../../../../../components/common/form"
|
||||||
import { Combobox } from "../../../../../../components/inputs/combobox"
|
import { Combobox } from "../../../../../../components/inputs/combobox"
|
||||||
import { usePromotionRuleValues } from "../../../../../../hooks/api/promotions"
|
|
||||||
import { useStore } from "../../../../../../hooks/api/store"
|
import { useStore } from "../../../../../../hooks/api/store"
|
||||||
|
import { useComboboxData } from "../../../../../../hooks/use-combobox-data"
|
||||||
|
import { sdk } from "../../../../../../lib/client"
|
||||||
|
|
||||||
type RuleValueFormFieldType = {
|
type RuleValueFormFieldType = {
|
||||||
form: any
|
form: any
|
||||||
@@ -16,11 +17,11 @@ type RuleValueFormFieldType = {
|
|||||||
name: string
|
name: string
|
||||||
operator: string
|
operator: string
|
||||||
fieldRule: any
|
fieldRule: any
|
||||||
attributes: RuleAttributeOptionsResponse[]
|
attributes: HttpTypes.AdminRuleAttributeOption[]
|
||||||
ruleType: "rules" | "target-rules" | "buy-rules"
|
ruleType: "rules" | "target-rules" | "buy-rules"
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildFilters = (attribute?: string, store?: StoreDTO) => {
|
const buildFilters = (attribute?: string, store?: HttpTypes.AdminStore) => {
|
||||||
if (!attribute || !store) {
|
if (!attribute || !store) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
@@ -49,17 +50,25 @@ export const RuleValueFormField = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const { store, isLoading: isStoreLoading } = useStore()
|
const { store, isLoading: isStoreLoading } = useStore()
|
||||||
const { values: options = [] } = usePromotionRuleValues(
|
|
||||||
ruleType,
|
const comboboxData = useComboboxData({
|
||||||
attribute?.id!,
|
queryFn: async (params) => {
|
||||||
buildFilters(attribute?.id, store),
|
return await sdk.admin.promotion.listRuleValues(
|
||||||
{
|
ruleType,
|
||||||
enabled:
|
attribute?.id!,
|
||||||
!!attribute?.id &&
|
{
|
||||||
["select", "multiselect"].includes(attribute.field_type) &&
|
...params,
|
||||||
!isStoreLoading,
|
...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({
|
const watchOperator = useWatch({
|
||||||
control: form.control,
|
control: form.control,
|
||||||
@@ -103,54 +112,21 @@ export const RuleValueFormField = ({
|
|||||||
<Form.ErrorMessage />
|
<Form.ErrorMessage />
|
||||||
</Form.Item>
|
</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 {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Form.Item className="basis-1/2">
|
<Form.Item className="basis-1/2">
|
||||||
<Form.Control>
|
<Form.Control>
|
||||||
<Combobox
|
<Combobox
|
||||||
{...field}
|
{...field}
|
||||||
|
{...comboboxData}
|
||||||
|
multiple={watchOperator !== "eq"}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
placeholder="Select Values"
|
placeholder={
|
||||||
options={options}
|
watchOperator === "eq" ? "Select Value" : "Select Values"
|
||||||
|
}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
className="bg-ui-bg-base"
|
|
||||||
disabled={!fieldRule.attribute}
|
|
||||||
/>
|
/>
|
||||||
</Form.Control>
|
</Form.Control>
|
||||||
|
|
||||||
<Form.ErrorMessage />
|
<Form.ErrorMessage />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -61,12 +61,12 @@ export interface RuleValueOptionsResponse {
|
|||||||
/**
|
/**
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
export type AdminRuleValueOptionsListResponse = {
|
export type AdminRuleValueOptionsListResponse = PaginatedResponse<{
|
||||||
/**
|
/**
|
||||||
* The list of rule value options.
|
* The list of rule value options.
|
||||||
*/
|
*/
|
||||||
values: AdminRuleValueOption[]
|
values: AdminRuleValueOption[]
|
||||||
}
|
}>
|
||||||
|
|
||||||
export type AdminPromotionRuleBatchResponse = BatchResponse<AdminPromotionRule>
|
export type AdminPromotionRuleBatchResponse = BatchResponse<AdminPromotionRule>
|
||||||
|
|
||||||
|
|||||||
+9
-6
@@ -1,18 +1,18 @@
|
|||||||
import {
|
|
||||||
ContainerRegistrationKeys,
|
|
||||||
remoteQueryObjectFromString,
|
|
||||||
} from "@medusajs/framework/utils"
|
|
||||||
import {
|
import {
|
||||||
AuthenticatedMedusaRequest,
|
AuthenticatedMedusaRequest,
|
||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
} from "@medusajs/framework/http"
|
} from "@medusajs/framework/http"
|
||||||
|
import { HttpTypes } from "@medusajs/framework/types"
|
||||||
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
remoteQueryObjectFromString,
|
||||||
|
} from "@medusajs/framework/utils"
|
||||||
import {
|
import {
|
||||||
ruleQueryConfigurations,
|
ruleQueryConfigurations,
|
||||||
validateRuleAttribute,
|
validateRuleAttribute,
|
||||||
validateRuleType,
|
validateRuleType,
|
||||||
} from "../../../utils"
|
} from "../../../utils"
|
||||||
import { AdminGetPromotionRuleParamsType } from "../../../validators"
|
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)
|
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,
|
applicationMethodType,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { rows } = await remoteQuery(
|
const { rows, metadata } = await remoteQuery(
|
||||||
remoteQueryObjectFromString({
|
remoteQueryObjectFromString({
|
||||||
entryPoint: queryConfig.entryPoint,
|
entryPoint: queryConfig.entryPoint,
|
||||||
variables: {
|
variables: {
|
||||||
@@ -67,5 +67,8 @@ export const GET = async (
|
|||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
values,
|
values,
|
||||||
|
count: metadata.count,
|
||||||
|
offset: metadata.skip,
|
||||||
|
limit: metadata.take,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user