@@ -17,6 +17,7 @@ import {
1717 Switch ,
1818 TextField ,
1919 showToast ,
20+ Spinner ,
2021} from "@calcom/ui" ;
2122import { Plus , Edit2 , Trash2 } from "@calcom/ui/components/icon" ;
2223
@@ -33,49 +34,49 @@ type FormData = {
3334} ;
3435
3536const PRICE_MODIFIER_ACTIONS = [
36- { value : "add" , label : "Add (Fixed Amount)" } ,
37- { value : "subtract" , label : "Subtract (Fixed Amount)" } ,
38- { value : "multiply" , label : "Multiply (Percentage)" } ,
39- { value : "divide" , label : "Divide (Percentage)" } ,
40- { value : "set" , label : "Set Fixed Price" } ,
37+ { value : "add" , label : t ( "add_fixed_amount" ) } ,
38+ { value : "subtract" , label : t ( "subtract_fixed_amount" ) } ,
39+ { value : "multiply" , label : t ( "multiply_percentage" ) } ,
40+ { value : "divide" , label : t ( "divide_percentage" ) } ,
41+ { value : "set" , label : t ( "set_fixed_price" ) } ,
4142] ;
4243
4344const CONDITION_TYPES = [
44- { value : "duration" , label : "Duration" } ,
45- { value : "timeOfDay" , label : "Time of Day" } ,
46- { value : "dayOfWeek" , label : "Day of Week" } ,
47- { value : "custom" , label : "Custom Field" } ,
45+ { value : "duration" , label : t ( "duration" ) } ,
46+ { value : "timeOfDay" , label : t ( "time_of_day" ) } ,
47+ { value : "dayOfWeek" , label : t ( "day_of_week" ) } ,
48+ { value : "custom" , label : t ( "custom_field" ) } ,
4849] ;
4950
5051const COMPARISON_OPERATORS = [
51- { value : "eq" , label : "Equals" } ,
52- { value : "neq" , label : "Not Equals" } ,
53- { value : "gt" , label : "Greater Than" } ,
54- { value : "gte" , label : "Greater Than or Equal" } ,
55- { value : "lt" , label : "Less Than" } ,
56- { value : "lte" , label : "Less Than or Equal" } ,
57- { value : "contains" , label : "Contains" } ,
58- { value : "custom" , label : "Custom Function" } ,
52+ { value : "eq" , label : t ( "equals" ) } ,
53+ { value : "neq" , label : t ( "not_equals" ) } ,
54+ { value : "gt" , label : t ( "greater_than" ) } ,
55+ { value : "gte" , label : t ( "greater_than_or_equal" ) } ,
56+ { value : "lt" , label : t ( "less_than" ) } ,
57+ { value : "lte" , label : t ( "less_than_or_equal" ) } ,
58+ { value : "contains" , label : t ( "contains" ) } ,
59+ { value : "custom" , label : t ( "custom_function" ) } ,
5960] ;
6061
6162const DAYS_OF_WEEK = [
62- { value : "sunday" , label : "Sunday" } ,
63- { value : "monday" , label : "Monday" } ,
64- { value : "tuesday" , label : "Tuesday" } ,
65- { value : "wednesday" , label : "Wednesday" } ,
66- { value : "thursday" , label : "Thursday" } ,
67- { value : "friday" , label : "Friday" } ,
68- { value : "saturday" , label : "Saturday" } ,
63+ { value : "sunday" , label : t ( "sunday" ) } ,
64+ { value : "monday" , label : t ( "monday" ) } ,
65+ { value : "tuesday" , label : t ( "tuesday" ) } ,
66+ { value : "wednesday" , label : t ( "wednesday" ) } ,
67+ { value : "thursday" , label : t ( "thursday" ) } ,
68+ { value : "friday" , label : t ( "friday" ) } ,
69+ { value : "saturday" , label : t ( "saturday" ) } ,
6970] ;
7071
7172const CURRENCIES = [
72- { value : "USD" , label : "USD ($)" } ,
73- { value : "EUR" , label : "EUR (€)" } ,
74- { value : "GBP" , label : "GBP (£)" } ,
75- { value : "CAD" , label : "CAD ($)" } ,
76- { value : "AUD" , label : "AUD ($)" } ,
77- { value : "JPY" , label : "JPY (¥)" } ,
78- { value : "INR" , label : "INR (₹)" } ,
73+ { value : "USD" , label : t ( "currency_usd" ) } ,
74+ { value : "EUR" , label : t ( "currency_eur" ) } ,
75+ { value : "GBP" , label : t ( "currency_gbp" ) } ,
76+ { value : "CAD" , label : t ( "currency_cad" ) } ,
77+ { value : "AUD" , label : t ( "currency_aud" ) } ,
78+ { value : "JPY" , label : t ( "currency_jpy" ) } ,
79+ { value : "INR" , label : t ( "currency_inr" ) } ,
7980] ;
8081
8182export function VariablePricingModal ( { eventTypeId, onClose } : VariablePricingProps ) {
@@ -105,7 +106,7 @@ export function VariablePricingModal({ eventTypeId, onClose }: VariablePricingPr
105106 const config = pricingData . pricingConfig ;
106107 form . setValue ( "variablePricing.enabled" , config . enabled ) ;
107108 form . setValue ( "variablePricing.basePrice" , config . basePrice / 100 ) ; // Convert cents to dollars
108- form . setValue ( "variablePricing.currency" , config . currency ) ;
109+ form . setValue ( "variablePricing.currency" , config . currency ?. toUpperCase ?. ( ) || "USD" ) ;
109110 form . setValue ( "variablePricing.rules" , config . rules ) ;
110111 }
111112 } , [ pricingData , form ] ) ;
@@ -121,15 +122,30 @@ export function VariablePricingModal({ eventTypeId, onClose }: VariablePricingPr
121122 enabled : data . enabled ,
122123 basePrice,
123124 currency : data . currency ,
124- rules : data . rules . map ( ( rule ) => ( {
125- id : rule . id ,
126- type : rule . type ,
127- enabled : rule . enabled ,
128- description : rule . description ,
129- action : rule . action ,
130- amount : rule . amount ,
131- condition : rule . condition ,
132- } ) ) ,
125+ rules : data . rules . map ( ( rule ) => {
126+ return {
127+ id : rule . id ,
128+ type : rule . type ,
129+ enabled : rule . enabled ,
130+ description : rule . description ,
131+ priority : rule . priority ?? 0 ,
132+ condition : rule . condition ,
133+ // map UI fields to pricing model
134+ ...( rule . price != null
135+ ? { price : Math . round ( rule . price ) } // already cents if editing existing
136+ : rule . action === "set"
137+ ? { price : Math . round ( ( rule . amount || 0 ) * 100 ) }
138+ : rule . action === "add"
139+ ? { priceModifier : { type : "surcharge" , value : Math . round ( ( rule . amount || 0 ) * 100 ) } }
140+ : rule . action === "subtract"
141+ ? { priceModifier : { type : "discount" , value : Math . round ( ( rule . amount || 0 ) * 100 ) } }
142+ : rule . action === "multiply"
143+ ? { priceModifier : { type : "surcharge" , value : 0 , percentage : rule . amount || 0 } }
144+ : rule . action === "divide"
145+ ? { priceModifier : { type : "discount" , value : 0 , percentage : rule . amount || 0 } }
146+ : { } ) ,
147+ } ;
148+ } ) ,
133149 } ,
134150 } ) ;
135151 } ;
@@ -164,7 +180,7 @@ export function VariablePricingModal({ eventTypeId, onClose }: VariablePricingPr
164180
165181 { isLoading ? (
166182 < div className = "flex justify-center py-8" >
167- < div className = "spinner" > Loading... </ div >
183+ < Spinner / >
168184 </ div >
169185 ) : (
170186 < >
@@ -356,8 +372,11 @@ function AddRuleDialog({ onClose, onAddRule, initialRule }: AddRuleDialogProps)
356372 const [ ruleType , setRuleType ] = useState < PricingRule [ "type" ] > ( initialRule ?. type || "duration" ) ;
357373 const [ enabled , setEnabled ] = useState ( initialRule ?. enabled ?? true ) ;
358374 const [ description , setDescription ] = useState ( initialRule ?. description || "" ) ;
359- const [ action , setAction ] = useState ( initialRule ?. action || "add" ) ;
360- const [ amount , setAmount ] = useState ( initialRule ?. amount || 0 ) ;
375+ const [ action , setAction ] = useState < "add" | "subtract" | "multiply" | "divide" | "set" > (
376+ // @ts -expect-error UI-only state
377+ initialRule ?. price ? "set" : "add"
378+ ) ;
379+ const [ amount , setAmount ] = useState < number > ( 0 ) ;
361380
362381 // Duration condition fields
363382 const [ minDuration , setMinDuration ] = useState (
@@ -435,9 +454,18 @@ function AddRuleDialog({ onClose, onAddRule, initialRule }: AddRuleDialogProps)
435454 type : ruleType ,
436455 enabled,
437456 description,
438- action : action as PricingRule [ "action" ] ,
439- amount : parseInt ( amount . toString ( ) ) ,
440457 condition,
458+ ...( action === "set"
459+ ? { price : Math . round ( amount * 100 ) }
460+ : action === "add"
461+ ? { priceModifier : { type : "surcharge" , value : Math . round ( amount * 100 ) } }
462+ : action === "subtract"
463+ ? { priceModifier : { type : "discount" , value : Math . round ( amount * 100 ) } }
464+ : action === "multiply"
465+ ? { priceModifier : { type : "surcharge" , value : 0 , percentage : amount } }
466+ : action === "divide"
467+ ? { priceModifier : { type : "discount" , value : 0 , percentage : amount } }
468+ : { } ) ,
441469 } ;
442470
443471 onAddRule ( rule ) ;
0 commit comments