1- import * as RadioGroup from "@radix-ui/react-radio-group" ;
2- import { useState , useEffect } from "react" ;
3-
41import type { EventTypeAppSettingsComponent } from "@calcom/app-store/types" ;
52import {
6- convertToSmallestCurrencyUnit ,
73 convertFromSmallestToPresentableCurrencyUnit ,
4+ convertToSmallestCurrencyUnit ,
85} from "@calcom/lib/currencyConversions" ;
96import { useLocale } from "@calcom/lib/hooks/useLocale" ;
107import { RefundPolicy } from "@calcom/lib/payment/types" ;
118import classNames from "@calcom/ui/classNames" ;
129import { Alert } from "@calcom/ui/components/alert" ;
13- import { Select } from "@calcom/ui/components/form" ;
14- import { CheckboxField } from "@calcom/ui/components/form" ;
15- import { TextField } from "@calcom/ui/components/form" ;
10+ import { CheckboxField , Select , TextField } from "@calcom/ui/components/form" ;
1611import { RadioField } from "@calcom/ui/components/radio" ;
17-
12+ import * as RadioGroup from "@radix-ui/react-radio-group" ;
13+ import { useEffect , useState } from "react" ;
1814import { paymentOptions } from "../lib/constants" ;
1915import { currencyOptions } from "../lib/currencyOptions" ;
2016import { autoChargeNoShowFeeTimeUnitEnum } from "../zod" ;
@@ -26,8 +22,10 @@ const EventTypeAppSettingsInterface: EventTypeAppSettingsComponent = ({
2622 setAppData,
2723 disabled,
2824 eventType,
25+ eventTypeFormMetadata,
2926} ) => {
3027 const price = getAppData ( "price" ) ;
28+ const pricePerDuration = getAppData ( "pricePerDuration" ) || { } ;
3129 const currency = getAppData ( "currency" ) || currencyOptions [ 0 ] . value ;
3230 const [ selectedCurrency , setSelectedCurrency ] = useState (
3331 currencyOptions . find ( ( c ) => c . value === currency ) || {
@@ -82,6 +80,10 @@ const EventTypeAppSettingsInterface: EventTypeAppSettingsComponent = ({
8280 { value : autoChargeNoShowFeeTimeUnitEnum . enum . hours , label : t ( "hours" ) } ,
8381 { value : autoChargeNoShowFeeTimeUnitEnum . enum . days , label : t ( "days" ) } ,
8482 ] ;
83+
84+ const multipleDuration = eventTypeFormMetadata ?. multipleDuration ;
85+ const hasMultipleDurations = Array . isArray ( multipleDuration ) && multipleDuration . length > 0 ;
86+
8587 return (
8688 < >
8789 { recurringEventDefined && (
@@ -90,26 +92,66 @@ const EventTypeAppSettingsInterface: EventTypeAppSettingsComponent = ({
9092 { ! recurringEventDefined && requirePayment && (
9193 < >
9294 < div className = "mt-4 block items-center justify-start sm:flex sm:space-x-2" >
93- < TextField
94- data-testid = "stripe-price-input"
95- label = { t ( "price" ) }
96- className = "h-[38px]"
97- addOnLeading = {
98- < > { selectedCurrency . value ? getCurrencySymbol ( "en" , selectedCurrency . value ) : "" } </ >
99- }
100- addOnSuffix = { currency . toUpperCase ( ) }
101- addOnClassname = "h-[38px]"
102- step = "0.01"
103- min = "0.5"
104- type = "number"
105- required
106- placeholder = "Price"
107- disabled = { disabled }
108- onChange = { ( e ) => {
109- setAppData ( "price" , convertToSmallestCurrencyUnit ( Number ( e . target . value ) , currency ) ) ;
110- } }
111- value = { price > 0 ? convertFromSmallestToPresentableCurrencyUnit ( price , currency ) : undefined }
112- />
95+ { hasMultipleDurations ? (
96+ < div className = "flex flex-col space-y-4" >
97+ { multipleDuration . map ( ( duration ) => (
98+ < TextField
99+ key = { duration }
100+ name = { `price-${ duration } ` }
101+ data-testid = { `stripe-price-input-${ duration } ` }
102+ label = { `${ duration } ${ t ( "minute_timeUnit" ) } ` }
103+ className = "h-[38px]"
104+ addOnLeading = {
105+ < > { selectedCurrency . value ? getCurrencySymbol ( "en" , selectedCurrency . value ) : "" } </ >
106+ }
107+ addOnSuffix = { currency . toUpperCase ( ) }
108+ addOnClassname = "h-[38px]"
109+ step = "0.01"
110+ min = "0.5"
111+ type = "number"
112+ required
113+ placeholder = "Price"
114+ disabled = { disabled }
115+ onChange = { ( e ) => {
116+ const newPrice = convertToSmallestCurrencyUnit ( Number ( e . target . value ) , currency ) ;
117+ const newPricePerDuration = { ...pricePerDuration , [ duration ] : newPrice } ;
118+ setAppData ( "pricePerDuration" , newPricePerDuration ) ;
119+
120+ // Keep base price synced with the first duration for backward compatibility
121+ if ( duration === multipleDuration [ 0 ] ) {
122+ setAppData ( "price" , newPrice ) ;
123+ }
124+ } }
125+ value = {
126+ pricePerDuration [ duration ] > 0
127+ ? convertFromSmallestToPresentableCurrencyUnit ( pricePerDuration [ duration ] , currency )
128+ : undefined
129+ }
130+ />
131+ ) ) }
132+ </ div >
133+ ) : (
134+ < TextField
135+ data-testid = "stripe-price-input"
136+ label = { t ( "price" ) }
137+ className = "h-[38px]"
138+ addOnLeading = {
139+ < > { selectedCurrency . value ? getCurrencySymbol ( "en" , selectedCurrency . value ) : "" } </ >
140+ }
141+ addOnSuffix = { currency . toUpperCase ( ) }
142+ addOnClassname = "h-[38px]"
143+ step = "0.01"
144+ min = "0.5"
145+ type = "number"
146+ required
147+ placeholder = "Price"
148+ disabled = { disabled }
149+ onChange = { ( e ) => {
150+ setAppData ( "price" , convertToSmallestCurrencyUnit ( Number ( e . target . value ) , currency ) ) ;
151+ } }
152+ value = { price > 0 ? convertFromSmallestToPresentableCurrencyUnit ( price , currency ) : undefined }
153+ />
154+ ) }
113155 </ div >
114156 < div className = "mt-5 w-60" >
115157 < label className = "text-default mb-1 block text-sm font-medium" htmlFor = "currency" >
0 commit comments