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:
Riqwan Thamir authored and GitHub committed 2024-04-06 13:20:31 +02:00
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,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,