feat(dashboard): added details page for promotions + edit sliders (#6882)
* chore: added details page for promotions * chore: add edit rules, edit details and edit campaign pages * chore: change to type button * chore: connection for rules * chore: listing rule labels of multiple modules * chore: add badge summary list * chore: fix campaigns
This commit is contained in:
1 parent
07fb058d96
commit
5e30b8cce6
60 files changed
+2803
-153
No files matched your search
@@ -0,0 +1,2 @@
|
||||
export * from "./types"
|
||||
export * from "./validators"
|
||||
@@ -0,0 +1,9 @@
|
||||
import { CampaignDTO, PaginatedResponse } from "@medusajs/types"
|
||||
|
||||
export type AdminCampaignsListRes = PaginatedResponse<{
|
||||
campaigns: CampaignDTO[]
|
||||
}>
|
||||
|
||||
export type AdminCampaignRes = {
|
||||
campaign: CampaignDTO
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export * from "./campaigns"
|
||||
export * from "./promotions"
|
||||
@@ -0,0 +1,115 @@
|
||||
import { AdminGetPromotionRulesRes } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
RuleOperator,
|
||||
RuleType,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../../types/routing"
|
||||
import {
|
||||
operatorsMap,
|
||||
ruleAttributesMap,
|
||||
ruleQueryConfigurations,
|
||||
validateRuleType,
|
||||
} from "../../utils"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const { id, rule_type: ruleType } = req.params
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
validateRuleType(ruleType)
|
||||
|
||||
const dasherizedRuleType = ruleType.split("-").join("_")
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint: "promotion",
|
||||
variables: { id },
|
||||
fields: req.remoteQueryConfig.fields,
|
||||
})
|
||||
|
||||
const [promotion] = await remoteQuery(queryObject)
|
||||
const ruleAttributes = ruleAttributesMap[ruleType]
|
||||
const promotionRules: any[] = []
|
||||
|
||||
if (dasherizedRuleType === RuleType.RULES) {
|
||||
promotionRules.push(...(promotion?.rules || []))
|
||||
} else if (dasherizedRuleType === RuleType.TARGET_RULES) {
|
||||
promotionRules.push(...(promotion.application_method?.target_rules || []))
|
||||
} else if (dasherizedRuleType === RuleType.BUY_RULES) {
|
||||
promotionRules.push(...(promotion.application_method?.buy_rules || []))
|
||||
}
|
||||
|
||||
const transformedRules: AdminGetPromotionRulesRes = []
|
||||
const disguisedRules = ruleAttributes.filter((attr) => !!attr.disguised)
|
||||
|
||||
for (const disguisedRule of disguisedRules) {
|
||||
const value = promotion.application_method?.[disguisedRule.id]
|
||||
const values = [{ label: value, value }]
|
||||
|
||||
transformedRules.push({
|
||||
id: undefined,
|
||||
attribute: disguisedRule.id,
|
||||
attribute_label: disguisedRule.label,
|
||||
operator: RuleOperator.EQ,
|
||||
operator_label: operatorsMap[RuleOperator.EQ].label,
|
||||
values,
|
||||
disguised: true,
|
||||
required: true,
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
for (const promotionRule of promotionRules) {
|
||||
const currentRuleAttribute = ruleAttributes.find(
|
||||
(attr) => attr.value === promotionRule.attribute
|
||||
)
|
||||
|
||||
if (!currentRuleAttribute) {
|
||||
continue
|
||||
}
|
||||
|
||||
const queryConfig = ruleQueryConfigurations[currentRuleAttribute.id]
|
||||
const rows = await remoteQuery(
|
||||
remoteQueryObjectFromString({
|
||||
entryPoint: queryConfig.entryPoint,
|
||||
variables: {
|
||||
filters: {
|
||||
[queryConfig.valueAttr]: promotionRule.values.map((v) => v.value),
|
||||
},
|
||||
},
|
||||
fields: [queryConfig.labelAttr, queryConfig.valueAttr],
|
||||
})
|
||||
)
|
||||
|
||||
const valueLabelMap = new Map<string, string>(
|
||||
rows.map((row) => [
|
||||
row[queryConfig.valueAttr],
|
||||
row[queryConfig.labelAttr],
|
||||
])
|
||||
)
|
||||
|
||||
promotionRule.values = promotionRule.values.map((value) => ({
|
||||
value: value.value,
|
||||
label: valueLabelMap.get(value.value) || value.value,
|
||||
}))
|
||||
|
||||
transformedRules.push({
|
||||
...promotionRule,
|
||||
attribute_label: currentRuleAttribute.label,
|
||||
operator_label:
|
||||
operatorsMap[promotionRule.operator]?.label || promotionRule.operator,
|
||||
disguised: false,
|
||||
required: currentRuleAttribute.required || false,
|
||||
})
|
||||
}
|
||||
|
||||
res.json({
|
||||
rules: transformedRules,
|
||||
})
|
||||
}
|
||||
@@ -45,6 +45,16 @@ export const adminPromotionRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
method: ["GET"],
|
||||
matcher: "/admin/promotions/:id/:rule_type",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetPromotionsPromotionParams,
|
||||
QueryConfig.retrieveNewTransformQueryConfig
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/promotions/:id",
|
||||
|
||||
@@ -21,25 +21,35 @@ export const defaultAdminPromotionFields = [
|
||||
"deleted_at",
|
||||
"campaign.id",
|
||||
"campaign.name",
|
||||
"campaign.campaign_identifier",
|
||||
"campaign.starts_at",
|
||||
"campaign.ends_at",
|
||||
"application_method.value",
|
||||
"application_method.type",
|
||||
"application_method.max_quantity",
|
||||
"application_method.apply_to_quantity",
|
||||
"application_method.buy_rules_min_quantity",
|
||||
"application_method.target_type",
|
||||
"application_method.allocation",
|
||||
"application_method.created_at",
|
||||
"application_method.updated_at",
|
||||
"application_method.deleted_at",
|
||||
"application_method.buy_rules.id",
|
||||
"application_method.buy_rules.attribute",
|
||||
"application_method.buy_rules.operator",
|
||||
"application_method.buy_rules.values.value",
|
||||
"application_method.target_rules.id",
|
||||
"application_method.target_rules.attribute",
|
||||
"application_method.target_rules.operator",
|
||||
"application_method.target_rules.values.value",
|
||||
"rules.id",
|
||||
"rules.attribute",
|
||||
"rules.operator",
|
||||
"rules.values.value",
|
||||
]
|
||||
|
||||
const defaults = [...defaultAdminPromotionFields]
|
||||
|
||||
export const retrieveTransformQueryConfig = {
|
||||
defaultFields: defaultAdminPromotionFields,
|
||||
defaultRelations: defaultAdminPromotionRelations,
|
||||
@@ -57,3 +67,9 @@ export const listRuleValueTransformQueryConfig = {
|
||||
allowed: [],
|
||||
isList: true,
|
||||
}
|
||||
|
||||
// TODO: replace this with the old one
|
||||
export const retrieveNewTransformQueryConfig = {
|
||||
defaults,
|
||||
isList: false,
|
||||
}
|
||||
@@ -1,32 +1,14 @@
|
||||
import { RuleOperator } from "@medusajs/utils"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../types/routing"
|
||||
|
||||
const operators = [
|
||||
{
|
||||
id: RuleOperator.IN,
|
||||
value: RuleOperator.IN,
|
||||
label: "In",
|
||||
},
|
||||
{
|
||||
id: RuleOperator.EQ,
|
||||
value: RuleOperator.EQ,
|
||||
label: "Equals",
|
||||
},
|
||||
{
|
||||
id: RuleOperator.NE,
|
||||
value: RuleOperator.NE,
|
||||
label: "Not In",
|
||||
},
|
||||
]
|
||||
import { operatorsMap } from "../utils"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.json({
|
||||
operators,
|
||||
operators: Object.values(operatorsMap),
|
||||
})
|
||||
}
|
||||
+6
-55
@@ -6,67 +6,18 @@ import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../../../types/routing"
|
||||
import { validateRuleAttribute, validateRuleType } from "../../../utils"
|
||||
|
||||
const queryConfigurations = {
|
||||
region: {
|
||||
entryPoint: "region",
|
||||
labelAttr: "name",
|
||||
valueAttr: "id",
|
||||
},
|
||||
currency: {
|
||||
entryPoint: "currency",
|
||||
labelAttr: "name",
|
||||
valueAttr: "code",
|
||||
},
|
||||
customer_group: {
|
||||
entryPoint: "customer_group",
|
||||
labelAttr: "name",
|
||||
valueAttr: "id",
|
||||
},
|
||||
sales_channel: {
|
||||
entryPoint: "sales_channel",
|
||||
labelAttr: "name",
|
||||
valueAttr: "id",
|
||||
},
|
||||
country: {
|
||||
entryPoint: "country",
|
||||
labelAttr: "display_name",
|
||||
valueAttr: "iso_2",
|
||||
},
|
||||
product: {
|
||||
entryPoint: "product",
|
||||
labelAttr: "title",
|
||||
valueAttr: "id",
|
||||
},
|
||||
product_category: {
|
||||
entryPoint: "product_category",
|
||||
labelAttr: "name",
|
||||
valueAttr: "id",
|
||||
},
|
||||
product_collection: {
|
||||
entryPoint: "product_collection",
|
||||
labelAttr: "title",
|
||||
valueAttr: "id",
|
||||
},
|
||||
product_type: {
|
||||
entryPoint: "product_type",
|
||||
labelAttr: "value",
|
||||
valueAttr: "id",
|
||||
},
|
||||
product_tag: {
|
||||
entryPoint: "product_tag",
|
||||
labelAttr: "value",
|
||||
valueAttr: "id",
|
||||
},
|
||||
}
|
||||
import {
|
||||
ruleQueryConfigurations,
|
||||
validateRuleAttribute,
|
||||
validateRuleType,
|
||||
} from "../../../utils"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const { rule_type: ruleType, rule_attribute_id: ruleAttributeId } = req.params
|
||||
const queryConfig = queryConfigurations[ruleAttributeId]
|
||||
const queryConfig = ruleQueryConfigurations[ruleAttributeId]
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
validateRuleType(ruleType)
|
||||
|
||||
@@ -3,3 +3,7 @@ import { PaginatedResponse, PromotionDTO } from "@medusajs/types"
|
||||
export type AdminPromotionsListRes = PaginatedResponse<{
|
||||
promotions: PromotionDTO[]
|
||||
}>
|
||||
|
||||
export type AdminPromotionRes = {
|
||||
promotion: PromotionDTO
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
export * from "./operators-map"
|
||||
export * from "./rule-attributes-map"
|
||||
export * from "./rule-query-configuration"
|
||||
export * from "./validate-rule-attribute"
|
||||
export * from "./validate-rule-type"
|
||||
@@ -0,0 +1,19 @@
|
||||
import { RuleOperator } from "@medusajs/utils"
|
||||
|
||||
export const operatorsMap = {
|
||||
[RuleOperator.IN]: {
|
||||
id: RuleOperator.IN,
|
||||
value: RuleOperator.IN,
|
||||
label: "In",
|
||||
},
|
||||
[RuleOperator.EQ]: {
|
||||
id: RuleOperator.EQ,
|
||||
value: RuleOperator.EQ,
|
||||
label: "Equals",
|
||||
},
|
||||
[RuleOperator.NE]: {
|
||||
id: RuleOperator.NE,
|
||||
value: RuleOperator.NE,
|
||||
label: "Not In",
|
||||
},
|
||||
}
|
||||
@@ -1,3 +1,17 @@
|
||||
export enum DisguisedRule {
|
||||
APPLY_TO_QUANTITY = "apply_to_quantity",
|
||||
BUY_RULES_MIN_QUANTITY = "buy_rules_min_quantity",
|
||||
}
|
||||
|
||||
export const disguisedRulesMap = {
|
||||
[DisguisedRule.APPLY_TO_QUANTITY]: {
|
||||
relation: "application_method",
|
||||
},
|
||||
[DisguisedRule.BUY_RULES_MIN_QUANTITY]: {
|
||||
relation: "application_method",
|
||||
},
|
||||
}
|
||||
|
||||
const ruleAttributes = [
|
||||
{
|
||||
id: "currency",
|
||||
@@ -66,20 +80,24 @@ const commonAttributes = [
|
||||
|
||||
const buyRuleAttributes = [
|
||||
{
|
||||
id: "buy_rules_min_quantity",
|
||||
value: "buy_rules_min_quantity",
|
||||
id: DisguisedRule.BUY_RULES_MIN_QUANTITY,
|
||||
value: DisguisedRule.BUY_RULES_MIN_QUANTITY,
|
||||
label: "Minimum quantity of items",
|
||||
field_type: "number",
|
||||
required: true,
|
||||
disguised: true,
|
||||
},
|
||||
...commonAttributes,
|
||||
]
|
||||
|
||||
const targetRuleAttributes = [
|
||||
{
|
||||
id: "apply_to_quantity",
|
||||
value: "apply_to_quantity",
|
||||
id: DisguisedRule.APPLY_TO_QUANTITY,
|
||||
value: DisguisedRule.APPLY_TO_QUANTITY,
|
||||
label: "Quantity of items promotion will apply to",
|
||||
field_type: "number",
|
||||
required: true,
|
||||
disguised: true,
|
||||
},
|
||||
...commonAttributes,
|
||||
]
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
export const ruleQueryConfigurations = {
|
||||
region: {
|
||||
entryPoint: "region",
|
||||
labelAttr: "name",
|
||||
valueAttr: "id",
|
||||
},
|
||||
currency: {
|
||||
entryPoint: "currency",
|
||||
labelAttr: "name",
|
||||
valueAttr: "code",
|
||||
},
|
||||
customer_group: {
|
||||
entryPoint: "customer_group",
|
||||
labelAttr: "name",
|
||||
valueAttr: "id",
|
||||
},
|
||||
sales_channel: {
|
||||
entryPoint: "sales_channel",
|
||||
labelAttr: "name",
|
||||
valueAttr: "id",
|
||||
},
|
||||
country: {
|
||||
entryPoint: "country",
|
||||
labelAttr: "display_name",
|
||||
valueAttr: "iso_2",
|
||||
},
|
||||
product: {
|
||||
entryPoint: "product",
|
||||
labelAttr: "title",
|
||||
valueAttr: "id",
|
||||
},
|
||||
product_category: {
|
||||
entryPoint: "product_category",
|
||||
labelAttr: "name",
|
||||
valueAttr: "id",
|
||||
},
|
||||
product_collection: {
|
||||
entryPoint: "product_collection",
|
||||
labelAttr: "title",
|
||||
valueAttr: "id",
|
||||
},
|
||||
product_type: {
|
||||
entryPoint: "product_type",
|
||||
labelAttr: "value",
|
||||
valueAttr: "id",
|
||||
},
|
||||
product_tag: {
|
||||
entryPoint: "product_tag",
|
||||
labelAttr: "value",
|
||||
valueAttr: "id",
|
||||
},
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import { XorConstraint } from "../../../types/validators/xor"
|
||||
import { AdminPostCampaignsReq } from "../campaigns/validators"
|
||||
|
||||
export class AdminGetPromotionsPromotionParams extends FindParams {}
|
||||
export class AdminGetPromotionRules extends FindParams {}
|
||||
|
||||
export class AdminGetPromotionsRuleValueParams extends extendedFindParamsMixin({
|
||||
limit: 100,
|
||||
|
||||
Reference in new issue
Block a user