diff --git a/.env.example b/.env.example index 0ff47a8580ef6c..ff2e2c1875f4a0 100644 --- a/.env.example +++ b/.env.example @@ -343,12 +343,14 @@ APP_ROUTER_APPS_SLUG_SETUP_ENABLED=0 APP_ROUTER_APPS_CATEGORIES_ENABLED=0 # whether we redirect to the future/apps/categories/[category] from /apps/categories/[category] or not APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED=0 +APP_ROUTER_BOOKING_ENABLED=0 APP_ROUTER_BOOKINGS_STATUS_ENABLED=0 APP_ROUTER_WORKFLOWS_ENABLED=0 APP_ROUTER_SETTINGS_TEAMS_ENABLED=0 APP_ROUTER_GETTING_STARTED_STEP_ENABLED=0 APP_ROUTER_APPS_ENABLED=0 APP_ROUTER_VIDEO_ENABLED=0 +APP_ROUTER_TEAM_ENABLED=0 APP_ROUTER_TEAMS_ENABLED=0 APP_ROUTER_AVAILABILITY_ENABLED=0 APP_ROUTER_AUTH_FORGOT_PASSWORD_ENABLED=0 @@ -392,3 +394,7 @@ VAPID_PRIVATE_KEY= # Custom privacy policy / terms URLs (for self-hosters: change to your privacy policy / terms URLs) NEXT_PUBLIC_WEBSITE_PRIVACY_POLICY_URL= NEXT_PUBLIC_WEBSITE_TERMS_URL= + +# NEXT_PUBLIC_LOGGER_LEVEL=3 sets to log info, warn, error and fatal logs. +# [0: silly & upwards, 1: trace & upwards, 2: debug & upwards, 3: info & upwards, 4: warn & upwards, 5: error & fatal, 6: fatal] +NEXT_PUBLIC_LOGGER_LEVEL= diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index a3c94d605590ca..66600c40a00818 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -7,14 +7,14 @@ jobs: test: name: Unit timeout-minutes: 20 - runs-on: buildjet-2vcpu-ubuntu-2204 + runs-on: buildjet-4vcpu-ubuntu-2204 steps: - uses: actions/checkout@v4 - uses: ./.github/actions/dangerous-git-checkout - uses: ./.github/actions/yarn-install - - run: yarn test + - run: yarn test -- --no-isolate # We could add different timezones here that we need to run our tests in - - run: TZ=America/Los_Angeles yarn test -- --timeZoneDependentTestsOnly + - run: TZ=America/Los_Angeles yarn test -- --timeZoneDependentTestsOnly --no-isolate - name: Run API v2 tests working-directory: apps/api/v2 run: | diff --git a/.yarn/patches/@prisma-client-npm-5.4.2-fca489b2dc.patch b/.yarn/patches/@prisma-client-npm-5.4.2-fca489b2dc.patch new file mode 100644 index 00000000000000..93d2ea935d6591 --- /dev/null +++ b/.yarn/patches/@prisma-client-npm-5.4.2-fca489b2dc.patch @@ -0,0 +1,26 @@ +diff --git a/runtime/binary.js b/runtime/binary.js +index c81267d752644043e97b35d26369a5ce266abfd0..034c11262143d1acbf9902745a441d3b0cdfc0fd 100644 +--- a/runtime/binary.js ++++ b/runtime/binary.js +@@ -185,7 +185,7 @@ It should have this form: { url: "CONNECTION_STRING" }`);if(r&&typeof r=="object + It should have this form: { url: "CONNECTION_STRING" }`);if(typeof i!="string")throw new Me(`Invalid value ${JSON.stringify(i)} for datasource "${t}" provided to PrismaClient constructor. + It should have this form: { url: "CONNECTION_STRING" }`)}}}},adapter:(e,A)=>{if(e===null)return;if(e===void 0)throw new Me('"adapter" property must not be undefined, use null to conditionally disable driver adapters.');if(!cg(A).includes("driverAdapters"))throw new Me('"adapter" property can only be provided to PrismaClient constructor when "driverAdapters" preview feature is enabled.')},datasourceUrl:e=>{if(typeof e<"u"&&typeof e!="string")throw new Me(`Invalid value ${JSON.stringify(e)} for "datasourceUrl" provided to PrismaClient constructor. + Expected string or undefined.`)},errorFormat:e=>{if(e){if(typeof e!="string")throw new Me(`Invalid value ${JSON.stringify(e)} for "errorFormat" provided to PrismaClient constructor.`);if(!PR.includes(e)){let A=Ni(e,PR);throw new Me(`Invalid errorFormat ${e} provided to PrismaClient constructor.${A}`)}}},log:e=>{if(!e)return;if(!Array.isArray(e))throw new Me(`Invalid value ${JSON.stringify(e)} for "log" provided to PrismaClient constructor.`);function A(t){if(typeof t=="string"&&!GR.includes(t)){let r=Ni(t,GR);throw new Me(`Invalid log level "${t}" provided to PrismaClient constructor.${r}`)}}for(let t of e){A(t);let r={level:A,emit:n=>{let i=["stdout","event"];if(!i.includes(n)){let s=Ni(n,i);throw new Me(`Invalid value ${JSON.stringify(n)} for "emit" in logLevel provided to PrismaClient constructor.${s}`)}}};if(t&&typeof t=="object")for(let[n,i]of Object.entries(t))if(r[n])r[n](i);else throw new Me(`Invalid property ${n} for "log" provided to PrismaClient constructor`)}},__internal:e=>{if(!e)return;let A=["debug","hooks","engine","measurePerformance"];if(typeof e!="object")throw new Me(`Invalid value ${JSON.stringify(e)} for "__internal" to PrismaClient constructor`);for(let[t]of Object.entries(e))if(!A.includes(t)){let r=Ni(t,A);throw new Me(`Invalid property ${JSON.stringify(t)} for "__internal" provided to PrismaClient constructor.${r}`)}}};function YR(e,A){for(let[t,r]of Object.entries(e)){if(!vR.includes(t)){let n=Ni(t,vR);throw new Me(`Unknown property ${t} provided to PrismaClient constructor.${n}`)}VJ[t](r,A)}if(e.datasourceUrl&&e.datasources)throw new Me('Can not use "datasourceUrl" and "datasources" options at the same time. Pick one of them')}function Ni(e,A){if(A.length===0||typeof e!="string")return"";let t=qJ(e,A);return t?` Did you mean "${t}"?`:""}function qJ(e,A){if(A.length===0)return null;let t=A.map(n=>({value:n,distance:(0,JR.default)(e,n)}));t.sort((n,i)=>n.distance{let r=new Array(e.length),n=null,i=!1,s=0,o=()=>{i||(s++,s===e.length&&(i=!0,n?t(n):A(r)))},a=c=>{i||(i=!0,t(c))};for(let c=0;c{r[c]=g,o()},g=>{if(!Cg(g)){a(g);return}g.batchRequestIdx===c?a(g):(n||(n=g),o())})})}var pr=ce("prisma:client");typeof globalThis=="object"&&(globalThis.NODE_CLIENT=!0);var OJ={requestArgsToMiddlewareArgs:e=>e,middlewareArgsToRequestArgs:e=>e},HJ=Symbol.for("prisma.client.transaction.id"),WJ={id:0,nextId(){return++this.id}};function _R(e){class A{constructor(r){this._middlewares=new dg;this._createPrismaPromise=ed();this.$extends=Xf;EI(e),r&&YR(r,e);let n=r?.adapter?cf(r.adapter):void 0,i=new HR.EventEmitter().on("error",()=>{});this._extensions=wa.empty(),this._previewFeatures=cg(e),this._clientVersion=e.clientVersion??TR,this._activeProvider=e.activeProvider,this._tracingHelper=DR(this._previewFeatures);let s={rootEnvPath:e.relativeEnvPaths.rootEnvPath&&Qo.default.resolve(e.dirname,e.relativeEnvPaths.rootEnvPath),schemaEnvPath:e.relativeEnvPaths.schemaEnvPath&&Qo.default.resolve(e.dirname,e.relativeEnvPaths.schemaEnvPath)},o=!n&&qi(s,{conflictCheck:"none"})||e.injectableEdgeEnv?.();try{let a=r??{},c=a.__internal??{},g=c.debug===!0;g&&ce.enable("prisma:client");let l=Qo.default.resolve(e.dirname,e.relativePath);WR.default.existsSync(l)||(l=e.dirname),pr("dirname",e.dirname),pr("relativePath",e.relativePath),pr("cwd",l);let u=c.engine||{};if(a.errorFormat?this._errorFormat=a.errorFormat:process.env.NODE_ENV==="production"?this._errorFormat="minimal":process.env.NO_COLOR?this._errorFormat="colorless":this._errorFormat="colorless",this._runtimeDataModel=e.runtimeDataModel,this._engineConfig={cwd:l,dirname:e.dirname,enableDebugLogs:g,allowTriggerPanic:u.allowTriggerPanic,datamodelPath:Qo.default.join(e.dirname,e.filename??"schema.prisma"),prismaPath:u.binaryPath??void 0,engineEndpoint:u.endpoint,generator:e.generator,showColors:this._errorFormat==="pretty",logLevel:a.log&&kR(a.log),logQueries:a.log&&!!(typeof a.log=="string"?a.log==="query":a.log.find(E=>typeof E=="string"?E==="query":E.level==="query")),env:o?.parsed??{},flags:[],clientVersion:e.clientVersion,engineVersion:e.engineVersion,previewFeatures:this._previewFeatures,activeProvider:e.activeProvider,inlineSchema:e.inlineSchema,overrideDatasources:hI(a,e.datasourceNames),inlineDatasources:e.inlineDatasources,inlineSchemaHash:e.inlineSchemaHash,tracingHelper:this._tracingHelper,logEmitter:i,isBundled:e.isBundled,adapter:n},pr("clientVersion",e.clientVersion),this._engine=tR(e,this._engineConfig),this._requestHandler=new Ig(this,i),a.log)for(let E of a.log){let h=typeof E=="string"?E:E.emit==="stdout"?E.level:null;h&&this.$on(h,d=>{Wi.log(`${Wi.tags[h]??""}`,d.message||d.query)})}this._metrics=new yn(this._engine)}catch(a){throw a.clientVersion=this._clientVersion,a}return this._appliedParent=is(this)}get[Symbol.toStringTag](){return"PrismaClient"}$use(r){this._middlewares.use(r)}$on(r,n){r==="beforeExit"?this._engine.on("beforeExit",n):this._engine.on(r,i=>{let s=i.fields;return n(r==="query"?{timestamp:i.timestamp,query:s?.query??i.query,params:s?.params??i.params,duration:s?.duration_ms??i.duration,target:i.target}:{timestamp:i.timestamp,message:s?.message??i.message,target:i.target})})}$connect(){try{return this._engine.start()}catch(r){throw r.clientVersion=this._clientVersion,r}}async $disconnect(){try{await this._engine.stop()}catch(r){throw r.clientVersion=this._clientVersion,r}finally{Rd()}}$executeRawInternal(r,n,i,s){let o=this._activeProvider,a=this._engineConfig.adapter?.flavour;return this._request({action:"executeRaw",args:i,transaction:r,clientMethod:n,argsMapper:$h({clientMethod:n,activeProvider:o,activeProviderFlavour:a}),callsite:nr(this._errorFormat),dataPath:[],middlewareArgsMapper:s})}$executeRaw(r,...n){return this._createPrismaPromise(i=>{if(r.raw!==void 0||r.sql!==void 0){let[s,o]=qR(r,n);return zh(this._activeProvider,s.text,s.values,Array.isArray(r)?"prisma.$executeRaw``":"prisma.$executeRaw(sql``)"),this.$executeRawInternal(i,"$executeRaw",s,o)}throw new AA("`$executeRaw` is a tag function, please use it like the following:\n```\nconst result = await prisma.$executeRaw`UPDATE User SET cool = ${true} WHERE email = ${'user@email.com'};`\n```\n\nOr read our docs at https://www.prisma.io/docs/concepts/components/prisma-client/raw-database-access#executeraw\n",{clientVersion:this._clientVersion})})}$executeRawUnsafe(r,...n){return this._createPrismaPromise(i=>(zh(this._activeProvider,r,n,"prisma.$executeRawUnsafe(, [...values])"),this.$executeRawInternal(i,"$executeRawUnsafe",[r,...n])))}$runCommandRaw(r){if(e.activeProvider!=="mongodb")throw new AA(`The ${e.activeProvider} provider does not support $runCommandRaw. Use the mongodb provider.`,{clientVersion:this._clientVersion});return this._createPrismaPromise(n=>this._request({args:r,clientMethod:"$runCommandRaw",dataPath:[],action:"runCommandRaw",argsMapper:CR,callsite:nr(this._errorFormat),transaction:n}))}async $queryRawInternal(r,n,i,s){let o=this._activeProvider,a=this._engineConfig.adapter?.flavour;return this._request({action:"queryRaw",args:i,transaction:r,clientMethod:n,argsMapper:$h({clientMethod:n,activeProvider:o,activeProviderFlavour:a}),callsite:nr(this._errorFormat),dataPath:[],middlewareArgsMapper:s}).then(LR)}$queryRaw(r,...n){return this._createPrismaPromise(i=>{if(r.raw!==void 0||r.sql!==void 0)return this.$queryRawInternal(i,"$queryRaw",...qR(r,n));throw new AA("`$queryRaw` is a tag function, please use it like the following:\n```\nconst result = await prisma.$queryRaw`SELECT * FROM User WHERE id = ${1} OR email = ${'user@email.com'};`\n```\n\nOr read our docs at https://www.prisma.io/docs/concepts/components/prisma-client/raw-database-access#queryraw\n",{clientVersion:this._clientVersion})})}$queryRawUnsafe(r,...n){return this._createPrismaPromise(i=>this.$queryRawInternal(i,"$queryRawUnsafe",[r,...n]))}_transactionWithArray({promises:r,options:n}){let i=WJ.nextId(),s=bR(r.length),o=r.map((a,c)=>{if(a?.[Symbol.toStringTag]!=="PrismaPromise")throw new Error("All elements of the array need to be Prisma Client promises. Hint: Please make sure you are not awaiting the Prisma client calls you intended to pass in the $transaction function.");let g=n?.isolationLevel,l={kind:"batch",id:i,index:c,isolationLevel:g,lock:s};return a.requestTransaction?.(l)??a});return VR(o)}async _transactionWithCallback({callback:r,options:n}){let i={traceparent:this._tracingHelper.getTraceParent()},s=await this._engine.transaction("start",i,n),o;try{let a={kind:"itx",...s};o=await r(this._createItxClient(a)),await this._engine.transaction("commit",i,s)}catch(a){throw await this._engine.transaction("rollback",i,s).catch(()=>{}),a}return o}_createItxClient(r){return is(Et(pa(this),[aA("_appliedParent",()=>this._appliedParent._createItxClient(r)),aA("_createPrismaPromise",()=>ed(r)),aA(HJ,()=>r.id),As(td)]))}$transaction(r,n){let i;typeof r=="function"?i=()=>this._transactionWithCallback({callback:r,options:n}):i=()=>this._transactionWithArray({promises:r,options:n});let s={name:"transaction",attributes:{method:"$transaction"}};return this._tracingHelper.runInChildSpan(s,i)}_request(r){r.otelParentCtx=this._tracingHelper.getActiveContext();let n=r.middlewareArgsMapper??OJ,i={args:n.requestArgsToMiddlewareArgs(r.args),dataPath:r.dataPath,runInTransaction:!!r.transaction,action:r.action,model:r.model},s={middleware:{name:"middleware",middleware:!0,attributes:{method:"$use"},active:!1},operation:{name:"operation",attributes:{method:i.action,model:i.model,name:i.model?`${i.model}.${i.action}`:i.action}}},o=-1,a=async c=>{let g=this._middlewares.get(++o);if(g)return this._tracingHelper.runInChildSpan(s.middleware,Q=>g(c,I=>(Q?.end(),a(I))));let{runInTransaction:l,args:u,...E}=c,h={...r,...E};u&&(h.args=n.middlewareArgsToRequestArgs(u)),r.transaction!==void 0&&l===!1&&delete h.transaction;let d=await nI(this,h);return h.model?eI({result:d,modelName:h.model,args:h.args,extensions:this._extensions,runtimeDataModel:this._runtimeDataModel}):d};return this._tracingHelper.runInChildSpan(s.operation,()=>new OR.AsyncResource("prisma-client-request").runInAsyncScope(()=>a(i)))}async _executeRequest({args:r,clientMethod:n,dataPath:i,callsite:s,action:o,model:a,argsMapper:c,transaction:g,unpacker:l,otelParentCtx:u,customDataProxyFetch:E}){try{r=c?c(r):r;let h={name:"serialize"},d=this._tracingHelper.runInChildSpan(h,()=>ER({modelName:a,runtimeDataModel:this._runtimeDataModel,action:o,args:r,clientMethod:n,callsite:s,extensions:this._extensions,errorFormat:this._errorFormat,clientVersion:this._clientVersion}));return ce.enabled("prisma:client")&&(pr("Prisma Client call:"),pr(`prisma.${n}(${Mf(r)})`),pr("Generated request:"),pr(JSON.stringify(d,null,2)+` +-`)),g?.kind==="batch"&&await g.lock,this._requestHandler.request({protocolQuery:d,modelName:a,action:o,clientMethod:n,dataPath:i,callsite:s,args:r,extensions:this._extensions,transaction:g,unpacker:l,otelParentCtx:u,otelChildCtx:this._tracingHelper.getActiveContext(),customDataProxyFetch:E})}catch(h){throw h.clientVersion=this._clientVersion,h}}get $metrics(){if(!this._hasPreviewFlag("metrics"))throw new AA("`metrics` preview feature must be enabled in order to access metrics API",{clientVersion:this._clientVersion});return this._metrics}_hasPreviewFlag(r){return!!this._engineConfig.previewFeatures?.includes(r)}}return A}function qR(e,A){return _J(e)?[new QA(e,A),mR]:[e,yR]}function _J(e){return Array.isArray(e)&&Array.isArray(e.raw)}var KJ=new Set(["toJSON","$$typeof","asymmetricMatch",Symbol.iterator,Symbol.toStringTag,Symbol.isConcatSpreadable,Symbol.toPrimitive]);function KR(e){return new Proxy(e,{get(A,t){if(t in A)return A[t];if(!KJ.has(t))throw new TypeError(`Invalid enum value: ${String(t)}`)}})}function jR(e){qi(e,{conflictCheck:"warn"})}0&&(module.exports={DMMF,DMMFClass,Debug,Decimal,Extensions,MetricsClient,NotFoundError,ObjectEnumValue,PrismaClientInitializationError,PrismaClientKnownRequestError,PrismaClientRustPanicError,PrismaClientUnknownRequestError,PrismaClientValidationError,Public,Sql,Types,defineDmmfProperty,empty,getPrismaClient,itxClientDenyList,join,makeStrictEnum,objectEnumNames,objectEnumValues,raw,sqltag,warnEnvConflicts,warnOnce}); ++`)),g?.kind==="batch"&&await g.lock,this._requestHandler.request({protocolQuery:d,modelName:a,action:o,clientMethod:n,dataPath:i,callsite:s,args:r,extensions:this._extensions,transaction:g,unpacker:l,otelParentCtx:u,otelChildCtx:this._tracingHelper.getActiveContext(),customDataProxyFetch:E})}catch(h){throw h.clientVersion=this._clientVersion,h}}get $metrics(){if(!this._hasPreviewFlag("metrics"))throw new AA("`metrics` preview feature must be enabled in order to access metrics API",{clientVersion:this._clientVersion});return this._metrics}_hasPreviewFlag(r){return!!this._engineConfig.previewFeatures?.includes(r)}}return A}function qR(e,A){return _J(e)?[new QA(e,A),mR]:[e,yR]}function _J(e){return Array.isArray(e)&&Array.isArray(e.raw)}var KJ=new Set(["toJSON","$$typeof","asymmetricMatch",Symbol.iterator,Symbol.toStringTag,Symbol.isConcatSpreadable,Symbol.toPrimitive]);function KR(e){return new Proxy(e,{get(A,t){if(t in A)return A[t];if(!KJ.has(t))throw new TypeError(`Invalid enum value: ${String(t)}`)}})}function jR(e){qi(e,{conflictCheck:"none"})}0&&(module.exports={DMMF,DMMFClass,Debug,Decimal,Extensions,MetricsClient,NotFoundError,ObjectEnumValue,PrismaClientInitializationError,PrismaClientKnownRequestError,PrismaClientRustPanicError,PrismaClientUnknownRequestError,PrismaClientValidationError,Public,Sql,Types,defineDmmfProperty,empty,getPrismaClient,itxClientDenyList,join,makeStrictEnum,objectEnumNames,objectEnumValues,raw,sqltag,warnEnvConflicts,warnOnce}); + /*! Bundled license information: + + undici/lib/fetch/body.js: +diff --git a/runtime/library.js b/runtime/library.js +index 65b30894c697f97a54924dcf7acc4b7e45002f4d..b2d26124f34759bb222a08e76bcc0f6d52b3c573 100644 +--- a/runtime/library.js ++++ b/runtime/library.js +@@ -126,7 +126,7 @@ It should have this form: { url: "CONNECTION_STRING" }`);if(n&&typeof n=="object + It should have this form: { url: "CONNECTION_STRING" }`);if(typeof o!="string")throw new q(`Invalid value ${JSON.stringify(o)} for datasource "${r}" provided to PrismaClient constructor. + It should have this form: { url: "CONNECTION_STRING" }`)}}}},adapter:(e,t)=>{if(e===null)return;if(e===void 0)throw new q('"adapter" property must not be undefined, use null to conditionally disable driver adapters.');if(!gn(t).includes("driverAdapters"))throw new q('"adapter" property can only be provided to PrismaClient constructor when "driverAdapters" preview feature is enabled.')},datasourceUrl:e=>{if(typeof e<"u"&&typeof e!="string")throw new q(`Invalid value ${JSON.stringify(e)} for "datasourceUrl" provided to PrismaClient constructor. + Expected string or undefined.`)},errorFormat:e=>{if(e){if(typeof e!="string")throw new q(`Invalid value ${JSON.stringify(e)} for "errorFormat" provided to PrismaClient constructor.`);if(!Cl.includes(e)){let t=Rt(e,Cl);throw new q(`Invalid errorFormat ${e} provided to PrismaClient constructor.${t}`)}}},log:e=>{if(!e)return;if(!Array.isArray(e))throw new q(`Invalid value ${JSON.stringify(e)} for "log" provided to PrismaClient constructor.`);function t(r){if(typeof r=="string"&&!Al.includes(r)){let n=Rt(r,Al);throw new q(`Invalid log level "${r}" provided to PrismaClient constructor.${n}`)}}for(let r of e){t(r);let n={level:t,emit:i=>{let o=["stdout","event"];if(!o.includes(i)){let s=Rt(i,o);throw new q(`Invalid value ${JSON.stringify(i)} for "emit" in logLevel provided to PrismaClient constructor.${s}`)}}};if(r&&typeof r=="object")for(let[i,o]of Object.entries(r))if(n[i])n[i](o);else throw new q(`Invalid property ${i} for "log" provided to PrismaClient constructor`)}},__internal:e=>{if(!e)return;let t=["debug","hooks","engine","measurePerformance"];if(typeof e!="object")throw new q(`Invalid value ${JSON.stringify(e)} for "__internal" to PrismaClient constructor`);for(let[r]of Object.entries(e))if(!t.includes(r)){let n=Rt(r,t);throw new q(`Invalid property ${JSON.stringify(r)} for "__internal" provided to PrismaClient constructor.${n}`)}}};function Ml(e,t){for(let[r,n]of Object.entries(e)){if(!Tl.includes(r)){let i=Rt(r,Tl);throw new q(`Unknown property ${r} provided to PrismaClient constructor.${i}`)}Xd[r](n,t)}if(e.datasourceUrl&&e.datasources)throw new q('Can not use "datasourceUrl" and "datasources" options at the same time. Pick one of them')}function Rt(e,t){if(t.length===0||typeof e!="string")return"";let r=em(e,t);return r?` Did you mean "${r}"?`:""}function em(e,t){if(t.length===0)return null;let r=t.map(i=>({value:i,distance:(0,Rl.default)(e,i)}));r.sort((i,o)=>i.distance{let n=new Array(e.length),i=null,o=!1,s=0,a=()=>{o||(s++,s===e.length&&(o=!0,i?r(i):t(n)))},l=u=>{o||(o=!0,r(u))};for(let u=0;u{n[u]=c,a()},c=>{if(!Pn(c)){l(c);return}c.batchRequestIdx===u?l(c):(i||(i=c),a())})})}var Ue=D("prisma:client");typeof globalThis=="object"&&(globalThis.NODE_CLIENT=!0);var tm={requestArgsToMiddlewareArgs:e=>e,middlewareArgsToRequestArgs:e=>e},rm=Symbol.for("prisma.client.transaction.id"),nm={id:0,nextId(){return++this.id}};function Dl(e){class t{constructor(n){this._middlewares=new wn;this._createPrismaPromise=Hi();this.$extends=ra;xa(e),n&&Ml(n,e);let i=n?.adapter?fs(n.adapter):void 0,o=new Fl.EventEmitter().on("error",()=>{});this._extensions=ln.empty(),this._previewFeatures=gn(e),this._clientVersion=e.clientVersion??wl,this._activeProvider=e.activeProvider,this._tracingHelper=ml(this._previewFeatures);let s={rootEnvPath:e.relativeEnvPaths.rootEnvPath&&yr.default.resolve(e.dirname,e.relativeEnvPaths.rootEnvPath),schemaEnvPath:e.relativeEnvPaths.schemaEnvPath&&yr.default.resolve(e.dirname,e.relativeEnvPaths.schemaEnvPath)},a=!i&&_t(s,{conflictCheck:"none"})||e.injectableEdgeEnv?.();try{let l=n??{},u=l.__internal??{},c=u.debug===!0;c&&D.enable("prisma:client");let p=yr.default.resolve(e.dirname,e.relativePath);Ol.default.existsSync(p)||(p=e.dirname),Ue("dirname",e.dirname),Ue("relativePath",e.relativePath),Ue("cwd",p);let d=u.engine||{};if(l.errorFormat?this._errorFormat=l.errorFormat:process.env.NODE_ENV==="production"?this._errorFormat="minimal":process.env.NO_COLOR?this._errorFormat="colorless":this._errorFormat="colorless",this._runtimeDataModel=e.runtimeDataModel,this._engineConfig={cwd:p,dirname:e.dirname,enableDebugLogs:c,allowTriggerPanic:d.allowTriggerPanic,datamodelPath:yr.default.join(e.dirname,e.filename??"schema.prisma"),prismaPath:d.binaryPath??void 0,engineEndpoint:d.endpoint,generator:e.generator,showColors:this._errorFormat==="pretty",logLevel:l.log&&gl(l.log),logQueries:l.log&&!!(typeof l.log=="string"?l.log==="query":l.log.find(f=>typeof f=="string"?f==="query":f.level==="query")),env:a?.parsed??{},flags:[],clientVersion:e.clientVersion,engineVersion:e.engineVersion,previewFeatures:this._previewFeatures,activeProvider:e.activeProvider,inlineSchema:e.inlineSchema,overrideDatasources:ba(l,e.datasourceNames),inlineDatasources:e.inlineDatasources,inlineSchemaHash:e.inlineSchemaHash,tracingHelper:this._tracingHelper,logEmitter:o,isBundled:e.isBundled,adapter:i},Ue("clientVersion",e.clientVersion),this._engine=Ua(e,this._engineConfig),this._requestHandler=new Cn(this,o),l.log)for(let f of l.log){let y=typeof f=="string"?f:f.emit==="stdout"?f.level:null;y&&this.$on(y,g=>{$t.log(`${$t.tags[y]??""}`,g.message||g.query)})}this._metrics=new dt(this._engine)}catch(l){throw l.clientVersion=this._clientVersion,l}return this._appliedParent=zt(this)}get[Symbol.toStringTag](){return"PrismaClient"}$use(n){this._middlewares.use(n)}$on(n,i){n==="beforeExit"?this._engine.on("beforeExit",i):this._engine.on(n,o=>{let s=o.fields;return i(n==="query"?{timestamp:o.timestamp,query:s?.query??o.query,params:s?.params??o.params,duration:s?.duration_ms??o.duration,target:o.target}:{timestamp:o.timestamp,message:s?.message??o.message,target:o.target})})}$connect(){try{return this._engine.start()}catch(n){throw n.clientVersion=this._clientVersion,n}}async $disconnect(){try{await this._engine.stop()}catch(n){throw n.clientVersion=this._clientVersion,n}finally{Eo()}}$executeRawInternal(n,i,o,s){let a=this._activeProvider,l=this._engineConfig.adapter?.flavour;return this._request({action:"executeRaw",args:o,transaction:n,clientMethod:i,argsMapper:Ji({clientMethod:i,activeProvider:a,activeProviderFlavour:l}),callsite:Ve(this._errorFormat),dataPath:[],middlewareArgsMapper:s})}$executeRaw(n,...i){return this._createPrismaPromise(o=>{if(n.raw!==void 0||n.sql!==void 0){let[s,a]=Il(n,i);return Gi(this._activeProvider,s.text,s.values,Array.isArray(n)?"prisma.$executeRaw``":"prisma.$executeRaw(sql``)"),this.$executeRawInternal(o,"$executeRaw",s,a)}throw new X("`$executeRaw` is a tag function, please use it like the following:\n```\nconst result = await prisma.$executeRaw`UPDATE User SET cool = ${true} WHERE email = ${'user@email.com'};`\n```\n\nOr read our docs at https://www.prisma.io/docs/concepts/components/prisma-client/raw-database-access#executeraw\n",{clientVersion:this._clientVersion})})}$executeRawUnsafe(n,...i){return this._createPrismaPromise(o=>(Gi(this._activeProvider,n,i,"prisma.$executeRawUnsafe(, [...values])"),this.$executeRawInternal(o,"$executeRawUnsafe",[n,...i])))}$runCommandRaw(n){if(e.activeProvider!=="mongodb")throw new X(`The ${e.activeProvider} provider does not support $runCommandRaw. Use the mongodb provider.`,{clientVersion:this._clientVersion});return this._createPrismaPromise(i=>this._request({args:n,clientMethod:"$runCommandRaw",dataPath:[],action:"runCommandRaw",argsMapper:nl,callsite:Ve(this._errorFormat),transaction:i}))}async $queryRawInternal(n,i,o,s){let a=this._activeProvider,l=this._engineConfig.adapter?.flavour;return this._request({action:"queryRaw",args:o,transaction:n,clientMethod:i,argsMapper:Ji({clientMethod:i,activeProvider:a,activeProviderFlavour:l}),callsite:Ve(this._errorFormat),dataPath:[],middlewareArgsMapper:s}).then(Pl)}$queryRaw(n,...i){return this._createPrismaPromise(o=>{if(n.raw!==void 0||n.sql!==void 0)return this.$queryRawInternal(o,"$queryRaw",...Il(n,i));throw new X("`$queryRaw` is a tag function, please use it like the following:\n```\nconst result = await prisma.$queryRaw`SELECT * FROM User WHERE id = ${1} OR email = ${'user@email.com'};`\n```\n\nOr read our docs at https://www.prisma.io/docs/concepts/components/prisma-client/raw-database-access#queryraw\n",{clientVersion:this._clientVersion})})}$queryRawUnsafe(n,...i){return this._createPrismaPromise(o=>this.$queryRawInternal(o,"$queryRawUnsafe",[n,...i]))}_transactionWithArray({promises:n,options:i}){let o=nm.nextId(),s=fl(n.length),a=n.map((l,u)=>{if(l?.[Symbol.toStringTag]!=="PrismaPromise")throw new Error("All elements of the array need to be Prisma Client promises. Hint: Please make sure you are not awaiting the Prisma client calls you intended to pass in the $transaction function.");let c=i?.isolationLevel,p={kind:"batch",id:o,index:u,isolationLevel:c,lock:s};return l.requestTransaction?.(p)??l});return Sl(a)}async _transactionWithCallback({callback:n,options:i}){let o={traceparent:this._tracingHelper.getTraceParent()},s=await this._engine.transaction("start",o,i),a;try{let l={kind:"itx",...s};a=await n(this._createItxClient(l)),await this._engine.transaction("commit",o,s)}catch(l){throw await this._engine.transaction("rollback",o,s).catch(()=>{}),l}return a}_createItxClient(n){return zt(Ee(on(this),[re("_appliedParent",()=>this._appliedParent._createItxClient(n)),re("_createPrismaPromise",()=>Hi(n)),re(rm,()=>n.id),Gt(zi)]))}$transaction(n,i){let o;typeof n=="function"?o=()=>this._transactionWithCallback({callback:n,options:i}):o=()=>this._transactionWithArray({promises:n,options:i});let s={name:"transaction",attributes:{method:"$transaction"}};return this._tracingHelper.runInChildSpan(s,o)}_request(n){n.otelParentCtx=this._tracingHelper.getActiveContext();let i=n.middlewareArgsMapper??tm,o={args:i.requestArgsToMiddlewareArgs(n.args),dataPath:n.dataPath,runInTransaction:!!n.transaction,action:n.action,model:n.model},s={middleware:{name:"middleware",middleware:!0,attributes:{method:"$use"},active:!1},operation:{name:"operation",attributes:{method:o.action,model:o.model,name:o.model?`${o.model}.${o.action}`:o.action}}},a=-1,l=async u=>{let c=this._middlewares.get(++a);if(c)return this._tracingHelper.runInChildSpan(s.middleware,P=>c(u,T=>(P?.end(),l(T))));let{runInTransaction:p,args:d,...f}=u,y={...n,...f};d&&(y.args=i.middlewareArgsToRequestArgs(d)),n.transaction!==void 0&&p===!1&&delete y.transaction;let g=await ua(this,y);return y.model?oa({result:g,modelName:y.model,args:y.args,extensions:this._extensions,runtimeDataModel:this._runtimeDataModel}):g};return this._tracingHelper.runInChildSpan(s.operation,()=>new kl.AsyncResource("prisma-client-request").runInAsyncScope(()=>l(o)))}async _executeRequest({args:n,clientMethod:i,dataPath:o,callsite:s,action:a,model:l,argsMapper:u,transaction:c,unpacker:p,otelParentCtx:d,customDataProxyFetch:f}){try{n=u?u(n):n;let y={name:"serialize"},g=this._tracingHelper.runInChildSpan(y,()=>el({modelName:l,runtimeDataModel:this._runtimeDataModel,action:a,args:n,clientMethod:i,callsite:s,extensions:this._extensions,errorFormat:this._errorFormat,clientVersion:this._clientVersion}));return D.enabled("prisma:client")&&(Ue("Prisma Client call:"),Ue(`prisma.${i}(${$s(n)})`),Ue("Generated request:"),Ue(JSON.stringify(g,null,2)+` +-`)),c?.kind==="batch"&&await c.lock,this._requestHandler.request({protocolQuery:g,modelName:l,action:a,clientMethod:i,dataPath:o,callsite:s,args:n,extensions:this._extensions,transaction:c,unpacker:p,otelParentCtx:d,otelChildCtx:this._tracingHelper.getActiveContext(),customDataProxyFetch:f})}catch(y){throw y.clientVersion=this._clientVersion,y}}get $metrics(){if(!this._hasPreviewFlag("metrics"))throw new X("`metrics` preview feature must be enabled in order to access metrics API",{clientVersion:this._clientVersion});return this._metrics}_hasPreviewFlag(n){return!!this._engineConfig.previewFeatures?.includes(n)}}return t}function Il(e,t){return im(e)?[new oe(e,t),ul]:[e,cl]}function im(e){return Array.isArray(e)&&Array.isArray(e.raw)}var om=new Set(["toJSON","$$typeof","asymmetricMatch",Symbol.iterator,Symbol.toStringTag,Symbol.isConcatSpreadable,Symbol.toPrimitive]);function _l(e){return new Proxy(e,{get(t,r){if(r in t)return t[r];if(!om.has(r))throw new TypeError(`Invalid enum value: ${String(r)}`)}})}function Nl(e){_t(e,{conflictCheck:"warn"})}0&&(module.exports={DMMF,DMMFClass,Debug,Decimal,Extensions,MetricsClient,NotFoundError,ObjectEnumValue,PrismaClientInitializationError,PrismaClientKnownRequestError,PrismaClientRustPanicError,PrismaClientUnknownRequestError,PrismaClientValidationError,Public,Sql,Types,defineDmmfProperty,empty,getPrismaClient,itxClientDenyList,join,makeStrictEnum,objectEnumNames,objectEnumValues,raw,sqltag,warnEnvConflicts,warnOnce}); ++`)),c?.kind==="batch"&&await c.lock,this._requestHandler.request({protocolQuery:g,modelName:l,action:a,clientMethod:i,dataPath:o,callsite:s,args:n,extensions:this._extensions,transaction:c,unpacker:p,otelParentCtx:d,otelChildCtx:this._tracingHelper.getActiveContext(),customDataProxyFetch:f})}catch(y){throw y.clientVersion=this._clientVersion,y}}get $metrics(){if(!this._hasPreviewFlag("metrics"))throw new X("`metrics` preview feature must be enabled in order to access metrics API",{clientVersion:this._clientVersion});return this._metrics}_hasPreviewFlag(n){return!!this._engineConfig.previewFeatures?.includes(n)}}return t}function Il(e,t){return im(e)?[new oe(e,t),ul]:[e,cl]}function im(e){return Array.isArray(e)&&Array.isArray(e.raw)}var om=new Set(["toJSON","$$typeof","asymmetricMatch",Symbol.iterator,Symbol.toStringTag,Symbol.isConcatSpreadable,Symbol.toPrimitive]);function _l(e){return new Proxy(e,{get(t,r){if(r in t)return t[r];if(!om.has(r))throw new TypeError(`Invalid enum value: ${String(r)}`)}})}function Nl(e){_t(e,{conflictCheck:"none"})}0&&(module.exports={DMMF,DMMFClass,Debug,Decimal,Extensions,MetricsClient,NotFoundError,ObjectEnumValue,PrismaClientInitializationError,PrismaClientKnownRequestError,PrismaClientRustPanicError,PrismaClientUnknownRequestError,PrismaClientValidationError,Public,Sql,Types,defineDmmfProperty,empty,getPrismaClient,itxClientDenyList,join,makeStrictEnum,objectEnumNames,objectEnumValues,raw,sqltag,warnEnvConflicts,warnOnce}); + /*! Bundled license information: + + decimal.js/decimal.mjs: diff --git a/README.md b/README.md index dee240506a4e42..bea598d0a6d090 100644 --- a/README.md +++ b/README.md @@ -173,11 +173,29 @@ yarn dx #### Development tip -> Add `NEXT_PUBLIC_DEBUG=1` anywhere in your `.env` to get logging information for all the queries and mutations driven by **tRPC**. +Add `NEXT_PUBLIC_LOGGER_LEVEL={level}` to your .env file to control the logging verbosity for all tRPC queries and mutations.\ +Where {level} can be one of the following: + +`0` for silly \ +`1` for trace \ +`2` for debug \ +`3` for info \ +`4` for warn \ +`5` for error \ +`6` for fatal + +When you set `NEXT_PUBLIC_LOGGER_LEVEL={level}` in your .env file, it enables logging at that level and higher. Here's how it works: + +The logger will include all logs that are at the specified level or higher. For example: \ +- If you set `NEXT_PUBLIC_LOGGER_LEVEL=2`, it will log from level 2 (debug) upwards, meaning levels 2 (debug), 3 (info), 4 (warn), 5 (error), and (fatal) will be logged. \ +- If you set `NEXT_PUBLIC_LOGGER_LEVEL=3`, it will log from level 3 (info) upwards, meaning levels 3 (info), 4 (warn), 5 (error), and 6 (fatal) will be logged, but level 2 (debug) and level 1 (trace) will be ignored. \ + + ```sh -echo 'NEXT_PUBLIC_DEBUG=1' >> .env +echo 'NEXT_PUBLIC_LOGGER_LEVEL=3' >> .env ``` +for Logger level to be set at info, for example. #### Gitpod Setup diff --git a/apps/api/v1/lib/validations/booking.ts b/apps/api/v1/lib/validations/booking.ts index f09e80542313d0..33b61dacb34bb8 100644 --- a/apps/api/v1/lib/validations/booking.ts +++ b/apps/api/v1/lib/validations/booking.ts @@ -1,6 +1,13 @@ import { z } from "zod"; -import { _AttendeeModel, _BookingModel as Booking, _PaymentModel, _UserModel } from "@calcom/prisma/zod"; +import { + _AttendeeModel, + _BookingModel as Booking, + _EventTypeModel, + _PaymentModel, + _TeamModel, + _UserModel, +} from "@calcom/prisma/zod"; import { extendedBookingCreateBody, iso8601 } from "@calcom/prisma/zod-utils"; import { schemaQueryUserId } from "./shared/queryUserId"; @@ -46,7 +53,23 @@ export const schemaBookingEditBodyParams = schemaBookingBaseBodyParams .merge(schemaBookingEditParams) .omit({ uid: true }); +const teamSchema = _TeamModel.pick({ + name: true, + slug: true, +}); + export const schemaBookingReadPublic = Booking.extend({ + eventType: _EventTypeModel + .pick({ + title: true, + slug: true, + }) + .merge( + z.object({ + team: teamSchema.nullish(), + }) + ) + .nullish(), attendees: z .array( _AttendeeModel.pick({ @@ -87,6 +110,7 @@ export const schemaBookingReadPublic = Booking.extend({ timeZone: true, attendees: true, user: true, + eventType: true, payment: true, metadata: true, status: true, diff --git a/apps/api/v1/lib/validations/shared/queryExpandRelations.ts b/apps/api/v1/lib/validations/shared/queryExpandRelations.ts new file mode 100644 index 00000000000000..f6a5115deb9fcf --- /dev/null +++ b/apps/api/v1/lib/validations/shared/queryExpandRelations.ts @@ -0,0 +1,12 @@ +import { z } from "zod"; + +const expandEnum = z.enum(["team"]); + +export const schemaQuerySingleOrMultipleExpand = z + .union([ + expandEnum, // Allow a single value from the enum + z.array(expandEnum).refine((arr) => new Set(arr).size === arr.length, { + message: "Array values must be unique", + }), // Allow an array of enum values, with uniqueness constraint + ]) + .optional(); diff --git a/apps/api/v1/pages/api/bookings/[id]/_get.ts b/apps/api/v1/pages/api/bookings/[id]/_get.ts index bc8511b5867bb1..01eafee532e0cf 100644 --- a/apps/api/v1/pages/api/bookings/[id]/_get.ts +++ b/apps/api/v1/pages/api/bookings/[id]/_get.ts @@ -4,6 +4,7 @@ import { defaultResponder } from "@calcom/lib/server"; import prisma from "@calcom/prisma"; import { schemaBookingReadPublic } from "~/lib/validations/booking"; +import { schemaQuerySingleOrMultipleExpand } from "~/lib/validations/shared/queryExpandRelations"; import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransformParseInt"; /** @@ -89,9 +90,21 @@ import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransform export async function getHandler(req: NextApiRequest) { const { query } = req; const { id } = schemaQueryIdParseInt.parse(query); + + const queryFilterForExpand = schemaQuerySingleOrMultipleExpand.parse(req.query.expand); + const expand = Array.isArray(queryFilterForExpand) + ? queryFilterForExpand + : queryFilterForExpand + ? [queryFilterForExpand] + : []; const booking = await prisma.booking.findUnique({ where: { id }, - include: { attendees: true, user: true, payment: true }, + include: { + attendees: true, + user: true, + payment: true, + eventType: expand.includes("team") ? { include: { team: true } } : false, + }, }); return { booking: schemaBookingReadPublic.parse(booking) }; } diff --git a/apps/api/v1/pages/api/bookings/_get.ts b/apps/api/v1/pages/api/bookings/_get.ts index 2174841e97d721..e9ec08b4b72a4c 100644 --- a/apps/api/v1/pages/api/bookings/_get.ts +++ b/apps/api/v1/pages/api/bookings/_get.ts @@ -12,6 +12,7 @@ import { } from "~/lib/utils/retrieveScopedAccessibleUsers"; import { schemaBookingGetParams, schemaBookingReadPublic } from "~/lib/validations/booking"; import { schemaQuerySingleOrMultipleAttendeeEmails } from "~/lib/validations/shared/queryAttendeeEmail"; +import { schemaQuerySingleOrMultipleExpand } from "~/lib/validations/shared/queryExpandRelations"; import { schemaQuerySingleOrMultipleUserIds } from "~/lib/validations/shared/queryUserId"; /** @@ -216,10 +217,18 @@ export async function handler(req: NextApiRequest) { args.take = take; args.skip = skip; } + const queryFilterForExpand = schemaQuerySingleOrMultipleExpand.parse(req.query.expand); + const expand = Array.isArray(queryFilterForExpand) + ? queryFilterForExpand + : queryFilterForExpand + ? [queryFilterForExpand] + : []; + args.include = { attendees: true, user: true, payment: true, + eventType: expand.includes("team") ? { include: { team: true } } : false, }; const queryFilterForAttendeeEmails = schemaQuerySingleOrMultipleAttendeeEmails.parse(req.query); diff --git a/apps/api/v1/test/lib/bookings/_get.integration-test.ts b/apps/api/v1/test/lib/bookings/_get.integration-test.ts index 74206dd83b3679..4d915a634a7ef3 100644 --- a/apps/api/v1/test/lib/bookings/_get.integration-test.ts +++ b/apps/api/v1/test/lib/bookings/_get.integration-test.ts @@ -164,7 +164,6 @@ describe("GET /api/bookings", async () => { const responseData = await handler(req); responseData.bookings.forEach((booking) => { - console.log(booking); expect(new Date(booking.startTime).getTime()).toBeGreaterThanOrEqual(new Date().getTime()); }); }); @@ -188,4 +187,27 @@ describe("GET /api/bookings", async () => { }); }); }); + + describe("Expand feature to add relational data in return payload", () => { + it("Returns only team data when expand=team is set", async () => { + const adminUser = await prisma.user.findFirstOrThrow({ where: { email: "owner1-acme@example.com" } }); + const { req } = createMocks({ + method: "GET", + query: { + expand: "team", + }, + pagination: DefaultPagination, + }); + + req.userId = adminUser.id; + req.isOrganizationOwnerOrAdmin = true; + + const responseData = await handler(req); + console.log("bookings=>", responseData.bookings); + responseData.bookings.forEach((booking) => { + if (booking.id === 31) expect(booking.eventType?.team?.slug).toBe("team1"); + if (booking.id === 19) expect(booking.eventType?.team).toBe(null); + }); + }); + }); }); diff --git a/apps/api/v2/package.json b/apps/api/v2/package.json index 5eda351e7920cc..1e3b4f1c8af040 100644 --- a/apps/api/v2/package.json +++ b/apps/api/v2/package.json @@ -28,7 +28,7 @@ "dependencies": { "@calcom/platform-constants": "*", "@calcom/platform-enums": "*", - "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.34", + "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.36", "@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2", "@calcom/platform-types": "*", "@calcom/platform-utils": "*", diff --git a/apps/api/v2/src/app.module.ts b/apps/api/v2/src/app.module.ts index 76531dd09c01a7..16f55de93add34 100644 --- a/apps/api/v2/src/app.module.ts +++ b/apps/api/v2/src/app.module.ts @@ -1,4 +1,5 @@ import appConfig from "@/config/app"; +import { CustomThrottlerGuard } from "@/lib/throttler-guard"; import { AppLoggerMiddleware } from "@/middleware/app.logger.middleware"; import { RewriterMiddleware } from "@/middleware/app.rewrites.middleware"; import { JsonBodyMiddleware } from "@/middleware/body/json.body.middleware"; @@ -15,7 +16,7 @@ import { BullModule } from "@nestjs/bull"; import { MiddlewareConsumer, Module, NestModule, RequestMethod } from "@nestjs/common"; import { ConfigModule } from "@nestjs/config"; import { APP_GUARD, APP_INTERCEPTOR } from "@nestjs/core"; -import { seconds, ThrottlerGuard, ThrottlerModule } from "@nestjs/throttler"; +import { seconds, ThrottlerModule } from "@nestjs/throttler"; import { ThrottlerStorageRedisService } from "nestjs-throttler-storage-redis"; import { AppController } from "./app.controller"; @@ -32,6 +33,7 @@ import { AppController } from "./app.controller"; BullModule.forRoot({ redis: `${process.env.REDIS_URL}${process.env.NODE_ENV === "production" ? "?tls=true" : ""}`, }), + // Rate limiting here is handled by the CustomThrottlerGuard ThrottlerModule.forRootAsync({ imports: [RedisModule], inject: [RedisService], @@ -59,7 +61,7 @@ import { AppController } from "./app.controller"; }, { provide: APP_GUARD, - useClass: ThrottlerGuard, + useClass: CustomThrottlerGuard, }, ], }) diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/controllers/event-types.controller.ts b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/controllers/event-types.controller.ts index 9b5ba327c0ea7c..e08562a65f9c96 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/controllers/event-types.controller.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/controllers/event-types.controller.ts @@ -36,7 +36,7 @@ import { InternalServerErrorException, ParseIntPipe, } from "@nestjs/common"; -import { ApiTags as DocsTags } from "@nestjs/swagger"; +import { ApiExcludeController as DocsExcludeController } from "@nestjs/swagger"; import { EVENT_TYPE_READ, EVENT_TYPE_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants"; import { getPublicEvent, getEventTypesByViewer } from "@calcom/platform-libraries-0.0.2"; @@ -47,7 +47,7 @@ import { PrismaClient } from "@calcom/prisma"; version: [VERSION_2024_04_15, VERSION_2024_06_11], }) @UseGuards(PermissionsGuard) -@DocsTags("Event types") +@DocsExcludeController(true) export class EventTypesController_2024_04_15 { constructor( private readonly eventTypesService: EventTypesService_2024_04_15, diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/services/event-types.service.ts b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/services/event-types.service.ts index 549851f6998334..2c6ac159fc4267 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/services/event-types.service.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/services/event-types.service.ts @@ -15,6 +15,7 @@ import { updateEventType, EventTypesPublic, getEventTypesPublic, + systemBeforeFieldEmail, } from "@calcom/platform-libraries"; import { EventType } from "@calcom/prisma/client"; @@ -127,8 +128,17 @@ export class EventTypesService_2024_04_15 { async updateEventType(eventTypeId: number, body: UpdateEventTypeInput_2024_04_15, user: UserWithProfile) { this.checkCanUpdateEventType(user.id, eventTypeId); const eventTypeUser = await this.getUserToUpdateEvent(user); + const bookingFields = [...(body.bookingFields || [])]; + + if ( + !bookingFields.find((field) => field.type === "email") && + !bookingFields.find((field) => field.type === "phone") + ) { + bookingFields.push(systemBeforeFieldEmail); + } + await updateEventType({ - input: { id: eventTypeId, ...body }, + input: { id: eventTypeId, ...body, bookingFields }, ctx: { user: eventTypeUser, // eslint-disable-next-line @typescript-eslint/ban-ts-comment diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/controllers/event-types.controller.e2e-spec.ts b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/controllers/event-types.controller.e2e-spec.ts index 79bce6fc488ce1..3b8990c8b15de3 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/controllers/event-types.controller.e2e-spec.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/controllers/event-types.controller.e2e-spec.ts @@ -250,7 +250,6 @@ describe("Event types Endpoints", () => { expect(createdEventType.description).toEqual(body.description); expect(createdEventType.lengthInMinutes).toEqual(body.lengthInMinutes); expect(createdEventType.locations).toEqual(body.locations); - expect(createdEventType.bookingFields).toEqual(body.bookingFields); expect(createdEventType.ownerId).toEqual(user.id); expect(createdEventType.scheduleId).toEqual(firstSchedule.id); expect(createdEventType.bookingLimitsCount).toEqual(body.bookingLimitsCount); @@ -259,6 +258,16 @@ describe("Event types Endpoints", () => { expect(createdEventType.offsetStart).toEqual(body.offsetStart); expect(createdEventType.bookingWindow).toEqual(body.bookingWindow); expect(createdEventType.recurrence).toEqual(body.recurrence); + + const responseBookingFields = body.bookingFields || []; + const expectedBookingFields = [ + { isDefault: true, required: true, slug: "name", type: "name" }, + { isDefault: true, required: true, slug: "email", type: "email" }, + { isDefault: true, required: false, slug: "rescheduleReason", type: "textarea" }, + ...responseBookingFields.map((field) => ({ isDefault: false, ...field })), + ]; + + expect(createdEventType.bookingFields).toEqual(expectedBookingFields); eventType = responseBody.data; }); }); @@ -476,6 +485,16 @@ describe("Event types Endpoints", () => { let legacyEventTypeId1: number; let legacyEventTypeId2: number; + const expectedReturnSystemFields = [ + { isDefault: true, required: true, slug: "name", type: "name" }, + { isDefault: true, required: true, slug: "email", type: "email" }, + { isDefault: true, type: "radioInput", slug: "location", required: false }, + { isDefault: true, required: true, slug: "title", type: "text" }, + { isDefault: true, required: false, slug: "notes", type: "textarea" }, + { isDefault: true, required: false, slug: "guests", type: "multiemail" }, + { isDefault: true, required: false, slug: "rescheduleReason", type: "textarea" }, + ]; + beforeAll(async () => { const moduleRef = await withApiAuth( userEmail, @@ -545,7 +564,7 @@ describe("Event types Endpoints", () => { .expect(400); }); - it("should return empty bookingFields if system fields are the only one in database", async () => { + it("should return system bookingFields stored in database", async () => { const legacyEventTypeInput = { title: "legacy event type", description: "legacy event type description", @@ -638,11 +657,11 @@ describe("Event types Endpoints", () => { .then(async (response) => { const responseBody: ApiSuccessResponse = response.body; const fetchedEventType = responseBody.data; - expect(fetchedEventType.bookingFields).toEqual([]); + expect(fetchedEventType.bookingFields).toEqual(expectedReturnSystemFields); }); }); - it("should return user created bookingFields among system fields in the database", async () => { + it("should return user created bookingFields with system fields", async () => { const userDefinedBookingField = { name: "team", type: "textarea", @@ -755,7 +774,9 @@ describe("Event types Endpoints", () => { const fetchedEventType = responseBody.data; expect(fetchedEventType.bookingFields).toEqual([ + ...expectedReturnSystemFields, { + isDefault: false, type: userDefinedBookingField.type, slug: userDefinedBookingField.name, label: userDefinedBookingField.label, diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/controllers/event-types.controller.ts b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/controllers/event-types.controller.ts index 01c048c6f03df7..f6f69ff52a1dcc 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/controllers/event-types.controller.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/controllers/event-types.controller.ts @@ -24,7 +24,7 @@ import { Delete, Query, } from "@nestjs/common"; -import { ApiTags as DocsTags } from "@nestjs/swagger"; +import { ApiHeader, ApiTags as DocsTags } from "@nestjs/swagger"; import { EVENT_TYPE_READ, EVENT_TYPE_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants"; import { @@ -39,12 +39,23 @@ import { }) @UseGuards(PermissionsGuard) @DocsTags("Event types") +@ApiHeader({ + name: "cal-api-version", + description: `Must be set to \`2024-06-14\``, + required: true, +}) export class EventTypesController_2024_06_14 { constructor(private readonly eventTypesService: EventTypesService_2024_06_14) {} @Post("/") @Permissions([EVENT_TYPE_WRITE]) @UseGuards(ApiAuthGuard) + @ApiHeader({ + name: "Authorization", + description: + "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + required: true, + }) async createEventType( @Body() body: CreateEventTypeInput_2024_06_14, @GetUser() user: UserWithProfile @@ -60,6 +71,12 @@ export class EventTypesController_2024_06_14 { @Get("/:eventTypeId") @Permissions([EVENT_TYPE_READ]) @UseGuards(ApiAuthGuard) + @ApiHeader({ + name: "Authorization", + description: + "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + required: true, + }) async getEventTypeById( @Param("eventTypeId") eventTypeId: string, @GetUser() user: UserWithProfile @@ -91,6 +108,12 @@ export class EventTypesController_2024_06_14 { @Patch("/:eventTypeId") @Permissions([EVENT_TYPE_WRITE]) @UseGuards(ApiAuthGuard) + @ApiHeader({ + name: "Authorization", + description: + "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + required: true, + }) @HttpCode(HttpStatus.OK) async updateEventType( @Param("eventTypeId") eventTypeId: number, @@ -108,6 +131,12 @@ export class EventTypesController_2024_06_14 { @Delete("/:eventTypeId") @Permissions([EVENT_TYPE_WRITE]) @UseGuards(ApiAuthGuard) + @ApiHeader({ + name: "Authorization", + description: + "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + required: true, + }) async deleteEventType( @Param("eventTypeId") eventTypeId: number, @GetUser("id") userId: number diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts index de55f2561088f4..c7bf2bd7bae1e0 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts @@ -6,8 +6,8 @@ import { Injectable } from "@nestjs/common"; import { getEventTypeById, - transformApiEventTypeBookingFields, - transformApiEventTypeLocations, + transformBookingFieldsApiToInternal, + transformLocationsApiToInternal, } from "@calcom/platform-libraries"; import { CreateEventTypeInput_2024_06_14 } from "@calcom/platform-types"; import type { PrismaClient } from "@calcom/prisma"; @@ -30,8 +30,8 @@ type InputEventTransformed = Omit< > & { length: number; slug: string; - locations?: ReturnType; - bookingFields?: ReturnType; + locations?: ReturnType; + bookingFields?: ReturnType; }; @Injectable() diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/input-event-types.service.ts b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/input-event-types.service.ts index 1901acd77a657e..b604b3632ab943 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/input-event-types.service.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/input-event-types.service.ts @@ -1,12 +1,16 @@ import { Injectable } from "@nestjs/common"; import { - transformApiEventTypeBookingFields, - transformApiEventTypeLocations, - transformApiEventTypeIntervalLimits, - transformApiEventTypeFutureBookingLimits, - transformApiEventTypeRecurrence, + transformBookingFieldsApiToInternal, + transformLocationsApiToInternal, + transformIntervalLimitsApiToInternal, + transformFutureBookingLimitsApiToInternal, + transformRecurrenceApiToInternal, + systemBeforeFieldName, + systemBeforeFieldEmail, + systemAfterFieldRescheduleReason, } from "@calcom/platform-libraries"; +import { systemBeforeFieldLocation } from "@calcom/platform-libraries"; import { CreateEventTypeInput_2024_06_14, UpdateEventTypeInput_2024_06_14 } from "@calcom/platform-types"; @Injectable() @@ -30,11 +34,12 @@ export class InputEventTypesService_2024_06_14 { ...rest } = inputEventType; + const hasMultipleLocations = (locations || defaultLocations).length > 1; const eventType = { ...rest, length: lengthInMinutes, locations: this.transformInputLocations(locations || defaultLocations), - bookingFields: this.transformInputBookingFields(bookingFields), + bookingFields: this.transformInputBookingFields(bookingFields, hasMultipleLocations), bookingLimits: bookingLimitsCount ? this.transformInputIntervalLimits(bookingLimitsCount) : undefined, durationLimits: bookingLimitsDuration ? this.transformInputIntervalLimits(bookingLimitsDuration) @@ -59,11 +64,15 @@ export class InputEventTypesService_2024_06_14 { ...rest } = inputEventType; + const hasMultipleLocations = !!(locations && locations?.length > 1); + const eventType = { ...rest, length: lengthInMinutes, locations: locations ? this.transformInputLocations(locations) : undefined, - bookingFields: bookingFields ? this.transformInputBookingFields(bookingFields) : undefined, + bookingFields: bookingFields + ? this.transformInputBookingFields(bookingFields, hasMultipleLocations) + : undefined, schedule: scheduleId, bookingLimits: bookingLimitsCount ? this.transformInputIntervalLimits(bookingLimitsCount) : undefined, durationLimits: bookingLimitsDuration @@ -77,23 +86,35 @@ export class InputEventTypesService_2024_06_14 { } transformInputLocations(inputLocations: CreateEventTypeInput_2024_06_14["locations"]) { - return transformApiEventTypeLocations(inputLocations); + return transformLocationsApiToInternal(inputLocations); } - transformInputBookingFields(inputBookingFields: CreateEventTypeInput_2024_06_14["bookingFields"]) { - return transformApiEventTypeBookingFields(inputBookingFields); + transformInputBookingFields( + inputBookingFields: CreateEventTypeInput_2024_06_14["bookingFields"], + hasMultipleLocations: boolean + ) { + const defaultFieldsBefore = [systemBeforeFieldName, systemBeforeFieldEmail]; + // note(Lauris): if event type has multiple locations then a radio button booking field has to be displayed to allow booker to pick location + if (hasMultipleLocations) { + defaultFieldsBefore.push(systemBeforeFieldLocation); + } + + const customFields = transformBookingFieldsApiToInternal(inputBookingFields); + const defaultFieldsAfter = [systemAfterFieldRescheduleReason]; + + return [...defaultFieldsBefore, ...customFields, ...defaultFieldsAfter]; } transformInputIntervalLimits(inputBookingFields: CreateEventTypeInput_2024_06_14["bookingLimitsCount"]) { - return transformApiEventTypeIntervalLimits(inputBookingFields); + return transformIntervalLimitsApiToInternal(inputBookingFields); } transformInputBookingWindow(inputBookingWindow: CreateEventTypeInput_2024_06_14["bookingWindow"]) { - const res = transformApiEventTypeFutureBookingLimits(inputBookingWindow); + const res = transformFutureBookingLimitsApiToInternal(inputBookingWindow); return !!res ? res : {}; } transformInputRecurrignEvent(recurrence: CreateEventTypeInput_2024_06_14["recurrence"]) { - return transformApiEventTypeRecurrence(recurrence); + return transformRecurrenceApiToInternal(recurrence); } } diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts index 205793df44fe8e..82c7490a065b8e 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts @@ -4,17 +4,17 @@ import type { EventType, User, Schedule } from "@prisma/client"; import { EventTypeMetaDataSchema, userMetadata, - getResponseEventTypeLocations, - getResponseEventTypeBookingFields, + transformLocationsInternalToApi, + transformBookingFieldsInternalToApi, parseRecurringEvent, TransformedLocationsSchema, BookingFieldsSchema, SystemField, - UserField, + CustomField, parseBookingLimit, - getResponseEventTypeIntervalLimits, - getResponseEventTypeFutureBookingLimits, - getResponseEventTypeRecurrence, + transformIntervalLimitsInternalToApi, + transformFutureBookingLimitsInternalToApi, + transformRecurrenceInternalToApi, } from "@calcom/platform-libraries"; import { TransformFutureBookingsLimitSchema_2024_06_14 } from "@calcom/platform-types"; @@ -144,19 +144,20 @@ export class OutputEventTypesService_2024_06_14 { transformLocations(locations: any) { if (!locations) return []; - return getResponseEventTypeLocations(TransformedLocationsSchema.parse(locations)); + return transformLocationsInternalToApi(TransformedLocationsSchema.parse(locations)); } - transformBookingFields(inputBookingFields: (SystemField | UserField)[] | null) { - if (!inputBookingFields) return []; - const userFields = inputBookingFields.filter((field) => field.editable === "user") as UserField[]; - return getResponseEventTypeBookingFields(userFields); + transformBookingFields(bookingFields: (SystemField | CustomField)[] | null) { + if (!bookingFields) return []; + + return transformBookingFieldsInternalToApi(bookingFields); } transformRecurringEvent(recurringEvent: any) { if (!recurringEvent) return null; const recurringEventParsed = parseRecurringEvent(recurringEvent); - return getResponseEventTypeRecurrence(recurringEventParsed); + if (!recurringEventParsed) return null; + return transformRecurrenceInternalToApi(recurringEventParsed); } transformMetadata(metadata: any) { @@ -182,10 +183,10 @@ export class OutputEventTypesService_2024_06_14 { transformIntervalLimits(bookingLimits: any) { const bookingLimitsParsed = parseBookingLimit(bookingLimits); - return getResponseEventTypeIntervalLimits(bookingLimitsParsed); + return transformIntervalLimitsInternalToApi(bookingLimitsParsed); } transformBookingWindow(bookingLimits: TransformFutureBookingsLimitSchema_2024_06_14) { - return getResponseEventTypeFutureBookingLimits(bookingLimits); + return transformFutureBookingLimitsInternalToApi(bookingLimits); } } diff --git a/apps/api/v2/src/ee/schedules/schedules_2024_04_15/controllers/schedules.controller.ts b/apps/api/v2/src/ee/schedules/schedules_2024_04_15/controllers/schedules.controller.ts index ad6d31c06a0af6..54a6d7034b275b 100644 --- a/apps/api/v2/src/ee/schedules/schedules_2024_04_15/controllers/schedules.controller.ts +++ b/apps/api/v2/src/ee/schedules/schedules_2024_04_15/controllers/schedules.controller.ts @@ -23,7 +23,7 @@ import { Patch, UseGuards, } from "@nestjs/common"; -import { ApiResponse, ApiTags as DocsTags } from "@nestjs/swagger"; +import { ApiResponse, ApiExcludeController as DocsExcludeController } from "@nestjs/swagger"; import { Throttle } from "@nestjs/throttler"; import { SCHEDULE_READ, SCHEDULE_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants"; @@ -36,7 +36,7 @@ import { CreateScheduleInput_2024_04_15 } from "../inputs/create-schedule.input" version: VERSION_2024_04_15_VALUE, }) @UseGuards(ApiAuthGuard, PermissionsGuard) -@DocsTags("Schedules") +@DocsExcludeController(true) export class SchedulesController_2024_04_15 { constructor(private readonly schedulesService: SchedulesService_2024_04_15) {} diff --git a/apps/api/v2/src/ee/schedules/schedules_2024_06_11/controllers/schedules.controller.ts b/apps/api/v2/src/ee/schedules/schedules_2024_06_11/controllers/schedules.controller.ts index bb60283f078c8f..1c11d0fe825a9d 100644 --- a/apps/api/v2/src/ee/schedules/schedules_2024_06_11/controllers/schedules.controller.ts +++ b/apps/api/v2/src/ee/schedules/schedules_2024_06_11/controllers/schedules.controller.ts @@ -17,7 +17,7 @@ import { Patch, UseGuards, } from "@nestjs/common"; -import { ApiResponse, ApiTags as DocsTags } from "@nestjs/swagger"; +import { ApiHeader, ApiOperation, ApiResponse, ApiTags as DocsTags } from "@nestjs/swagger"; import { Throttle } from "@nestjs/throttler"; import { SCHEDULE_READ, SCHEDULE_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants"; @@ -38,11 +38,38 @@ import { }) @UseGuards(ApiAuthGuard, PermissionsGuard) @DocsTags("Schedules") +@ApiHeader({ + name: "cal-api-version", + description: `Must be set to \`2024-06-11\``, + required: true, +}) +@ApiHeader({ + name: "Authorization", + description: + "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + required: true, +}) export class SchedulesController_2024_06_11 { constructor(private readonly schedulesService: SchedulesService_2024_06_11) {} @Post("/") @Permissions([SCHEDULE_WRITE]) + @ApiOperation({ + summary: "Create a schedule", + description: ` + The point of creating schedules is for event types to be available at specific times. + + First goal of schedules is to have a default schedule. If you are platform customer and created managed users, then it is important to note that each managed user should have a default schedule. + 1. If you passed \`timeZone\` when creating managed user, then the default schedule from Monday to Friday from 9AM to 5PM will be created with that timezone. Managed user can then change the default schedule via \`AvailabilitySettings\` atom. + 2. If you did not, then we assume you want that user has specific schedule right away. You should create default schedule by specifying + \`"isDefault": true\` in the request body. Until the user has a default schedule that user can't be booked or manage his / her schedule via the AvailabilitySettings atom. + + Second goal is to create other schedules that event types can point to, so that when that event is booked availability is not checked against the default schedule but against that specific schedule. + After creating a non default schedule you can update event type to point to that schedule via the PATCH \`event-types/{eventTypeId}\` endpoint. + + When specifying start time and end time for each day use 24 hour format e.g. 08:00, 15:00 etc. + `, + }) async createSchedule( @GetUser() user: UserWithProfile, @Body() bodySchedule: CreateScheduleInput_2024_06_11 @@ -59,7 +86,7 @@ export class SchedulesController_2024_06_11 { @Permissions([SCHEDULE_READ]) @ApiResponse({ status: 200, - description: "Returns the default schedule", + description: "Returns the default schedule of the authenticated user", type: GetDefaultScheduleOutput_2024_06_11, }) async getDefaultSchedule(@GetUser() user: UserWithProfile): Promise { @@ -88,6 +115,9 @@ export class SchedulesController_2024_06_11 { @Get("/") @Permissions([SCHEDULE_READ]) + @ApiOperation({ + description: "Returns all schedules of the authenticated user", + }) async getSchedules(@GetUser() user: UserWithProfile): Promise { const schedules = await this.schedulesService.getUserSchedules(user.id); diff --git a/apps/api/v2/src/lib/throttler-guard.ts b/apps/api/v2/src/lib/throttler-guard.ts new file mode 100644 index 00000000000000..a54d3a0c850560 --- /dev/null +++ b/apps/api/v2/src/lib/throttler-guard.ts @@ -0,0 +1,45 @@ +import { isApiKey } from "@/lib/api-key"; +import { Injectable, Logger } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { Reflector } from "@nestjs/core"; +import { ThrottlerGuard, ThrottlerModuleOptions, ThrottlerStorage } from "@nestjs/throttler"; +import { Request } from "express"; + +import { X_CAL_CLIENT_ID } from "@calcom/platform-constants"; + +@Injectable() +export class CustomThrottlerGuard extends ThrottlerGuard { + private logger = new Logger("CustomThrottlerGuard"); + + constructor( + options: ThrottlerModuleOptions, + storageService: ThrottlerStorage, + reflector: Reflector, + private readonly config: ConfigService + ) { + super(options, storageService, reflector); + } + + protected async getTracker(request: Request): Promise { + const authorizationHeader = request.get("Authorization")?.replace("Bearer ", ""); + + if (authorizationHeader) { + return isApiKey(authorizationHeader, this.config.get("api.apiKeyPrefix") ?? "cal_") + ? `api_key_${authorizationHeader}` + : `access_token_${authorizationHeader}`; + } + + const oauthClientId = request.get(X_CAL_CLIENT_ID); + + if (oauthClientId) { + return oauthClientId; + } + + if (request.ip) { + return request.ip; + } + + this.logger.log(`no tracker found: ${request.url}`); + return "unknown"; + } +} diff --git a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts index b81124117b8a9b..b92e22df12f96b 100644 --- a/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts +++ b/apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts @@ -79,7 +79,6 @@ export class OAuthClientUsersController { `Creating user with data: ${JSON.stringify(body, null, 2)} for OAuth Client with ID ${oAuthClientId}` ); const client = await this.oauthRepository.getOAuthClient(oAuthClientId); - console.log("asap createUser client", JSON.stringify(client, null, 2)); const isPlatformManaged = true; const { user, tokens } = await this.oAuthClientUsersService.createOauthClientUser( diff --git a/apps/api/v2/src/modules/users/inputs/create-managed-user.input.ts b/apps/api/v2/src/modules/users/inputs/create-managed-user.input.ts index fdfa6d25f3ca14..2be466e15adfa4 100644 --- a/apps/api/v2/src/modules/users/inputs/create-managed-user.input.ts +++ b/apps/api/v2/src/modules/users/inputs/create-managed-user.input.ts @@ -29,7 +29,11 @@ export class CreateManagedUserInput { @IsTimeZone() @IsOptional() @CapitalizeTimeZone() - @ApiProperty({ example: "America/New_York" }) + @ApiProperty({ + example: "America/New_York", + description: `Timezone is used to create user's default schedule from Monday to Friday from 9AM to 5PM. If it is not passed then user does not have + a default schedule and it must be created manually via the /schedules endpoint. Until the schedule is created, the user can't access availability atom to set his / her availability nor booked.`, + }) timeZone?: string; @IsEnum(Locales) diff --git a/apps/api/v2/swagger/documentation.json b/apps/api/v2/swagger/documentation.json index f9f0083a6096a7..6fd37c188b3c3e 100644 --- a/apps/api/v2/swagger/documentation.json +++ b/apps/api/v2/swagger/documentation.json @@ -597,14 +597,33 @@ }, "/v2/event-types": { "post": { - "operationId": "EventTypesController_2024_04_15_createEventType", - "parameters": [], + "operationId": "EventTypesController_2024_06_14_createEventType", + "parameters": [ + { + "name": "cal-api-version", + "in": "header", + "description": "Must be set to `2024-06-14`", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + "required": true, + "schema": { + "type": "string" + } + } + ], "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateEventTypeInput_2024_04_15" + "$ref": "#/components/schemas/CreateEventTypeInput_2024_06_14" } } } @@ -615,7 +634,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateEventTypeOutput" + "$ref": "#/components/schemas/CreateEventTypeOutput_2024_06_14" } } } @@ -626,15 +645,52 @@ ] }, "get": { - "operationId": "EventTypesController_2024_04_15_getEventTypes", - "parameters": [], + "operationId": "EventTypesController_2024_06_14_getEventTypes", + "parameters": [ + { + "name": "cal-api-version", + "in": "header", + "description": "Must be set to `2024-06-14`", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "username", + "required": true, + "in": "query", + "description": "The username of the user to get event types for. If only username provided will get all event types.", + "schema": { + "type": "string" + } + }, + { + "name": "eventSlug", + "required": true, + "in": "query", + "description": "Slug of event type to return. Notably, if eventSlug is provided then username must be provided too, because multiple users can have event with same slug.", + "schema": { + "type": "string" + } + }, + { + "name": "usernames", + "required": true, + "in": "query", + "description": "Get dynamic event type for multiple usernames separated by comma. e.g `usernames=alice,bob`", + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetEventTypesOutput" + "$ref": "#/components/schemas/GetEventTypesOutput_2024_06_14" } } } @@ -647,14 +703,32 @@ }, "/v2/event-types/{eventTypeId}": { "get": { - "operationId": "EventTypesController_2024_04_15_getEventType", + "operationId": "EventTypesController_2024_06_14_getEventTypeById", "parameters": [ + { + "name": "cal-api-version", + "in": "header", + "description": "Must be set to `2024-06-14`", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "eventTypeId", "required": true, "in": "path", "schema": { - "type": "number" + "type": "string" + } + }, + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + "required": true, + "schema": { + "type": "string" } } ], @@ -664,7 +738,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetEventTypeOutput" + "$ref": "#/components/schemas/GetEventTypeOutput_2024_06_14" } } } @@ -675,8 +749,17 @@ ] }, "patch": { - "operationId": "EventTypesController_2024_04_15_updateEventType", + "operationId": "EventTypesController_2024_06_14_updateEventType", "parameters": [ + { + "name": "cal-api-version", + "in": "header", + "description": "Must be set to `2024-06-14`", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "eventTypeId", "required": true, @@ -684,6 +767,15 @@ "schema": { "type": "number" } + }, + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + "required": true, + "schema": { + "type": "string" + } } ], "requestBody": { @@ -691,7 +783,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateEventTypeInput_2024_04_15" + "$ref": "#/components/schemas/UpdateEventTypeInput_2024_06_14" } } } @@ -702,7 +794,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateEventTypeOutput" + "$ref": "#/components/schemas/UpdateEventTypeOutput_2024_06_14" } } } @@ -713,8 +805,17 @@ ] }, "delete": { - "operationId": "EventTypesController_2024_04_15_deleteEventType", + "operationId": "EventTypesController_2024_06_14_deleteEventType", "parameters": [ + { + "name": "cal-api-version", + "in": "header", + "description": "Must be set to `2024-06-14`", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "eventTypeId", "required": true, @@ -722,6 +823,15 @@ "schema": { "type": "number" } + }, + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + "required": true, + "schema": { + "type": "string" + } } ], "responses": { @@ -730,7 +840,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeleteEventTypeOutput" + "$ref": "#/components/schemas/DeleteEventTypeOutput_2024_06_14" } } } @@ -816,91 +926,6 @@ ] } }, - "/v2/event-types/{username}/{eventSlug}/public": { - "get": { - "operationId": "EventTypesController_2024_04_15_getPublicEventType", - "parameters": [ - { - "name": "username", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - }, - { - "name": "eventSlug", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - }, - { - "name": "isTeamEvent", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "org", - "required": false, - "in": "query", - "schema": { - "nullable": true, - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GetEventTypePublicOutput" - } - } - } - } - }, - "tags": [ - "Event types" - ] - } - }, - "/v2/event-types/{username}/public": { - "get": { - "operationId": "EventTypesController_2024_04_15_getPublicEventTypes", - "parameters": [ - { - "name": "username", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GetEventTypesPublicOutput" - } - } - } - } - }, - "tags": [ - "Event types" - ] - } - }, "/v2/organizations/{orgId}/teams": { "get": { "operationId": "OrganizationsTeamsController_getAllTeams", @@ -1843,6 +1868,15 @@ "schema": { "type": "number" } + }, + { + "name": "eventSlug", + "required": true, + "in": "query", + "description": "Slug of team event type to return.", + "schema": { + "type": "string" + } } ], "responses": { @@ -3005,14 +3039,35 @@ }, "/v2/schedules": { "post": { - "operationId": "SchedulesController_2024_04_15_createSchedule", - "parameters": [], + "operationId": "SchedulesController_2024_06_11_createSchedule", + "summary": "Create a schedule", + "description": "\n The point of creating schedules is for event types to be available at specific times.\n\n First goal of schedules is to have a default schedule. If you are platform customer and created managed users, then it is important to note that each managed user should have a default schedule.\n 1. If you passed `timeZone` when creating managed user, then the default schedule from Monday to Friday from 9AM to 5PM will be created with that timezone. Managed user can then change the default schedule via `AvailabilitySettings` atom.\n 2. If you did not, then we assume you want that user has specific schedule right away. You should create default schedule by specifying\n `\"isDefault\": true` in the request body. Until the user has a default schedule that user can't be booked or manage his / her schedule via the AvailabilitySettings atom.\n\n Second goal is to create other schedules that event types can point to, so that when that event is booked availability is not checked against the default schedule but against that specific schedule.\n After creating a non default schedule you can update event type to point to that schedule via the PATCH `event-types/{eventTypeId}` endpoint.\n\n When specifying start time and end time for each day use 24 hour format e.g. 08:00, 15:00 etc.\n ", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "cal-api-version", + "in": "header", + "description": "Must be set to `2024-06-11`", + "required": true, + "schema": { + "type": "string" + } + } + ], "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateScheduleInput_2024_04_15" + "$ref": "#/components/schemas/CreateScheduleInput_2024_06_11" } } } @@ -3023,7 +3078,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateScheduleOutput_2024_04_15" + "$ref": "#/components/schemas/CreateScheduleOutput_2024_06_11" } } } @@ -3034,15 +3089,36 @@ ] }, "get": { - "operationId": "SchedulesController_2024_04_15_getSchedules", - "parameters": [], + "operationId": "SchedulesController_2024_06_11_getSchedules", + "summary": "", + "description": "Returns all schedules of the authenticated user", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "cal-api-version", + "in": "header", + "description": "Must be set to `2024-06-11`", + "required": true, + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetSchedulesOutput_2024_04_15" + "$ref": "#/components/schemas/GetSchedulesOutput_2024_06_11" } } } @@ -3055,15 +3131,34 @@ }, "/v2/schedules/default": { "get": { - "operationId": "SchedulesController_2024_04_15_getDefaultSchedule", - "parameters": [], + "operationId": "SchedulesController_2024_06_11_getDefaultSchedule", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "cal-api-version", + "in": "header", + "description": "Must be set to `2024-06-11`", + "required": true, + "schema": { + "type": "string" + } + } + ], "responses": { "200": { - "description": "Returns the default schedule", + "description": "Returns the default schedule of the authenticated user", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetDefaultScheduleOutput_2024_04_15" + "$ref": "#/components/schemas/GetDefaultScheduleOutput_2024_06_11" } } } @@ -3076,8 +3171,26 @@ }, "/v2/schedules/{scheduleId}": { "get": { - "operationId": "SchedulesController_2024_04_15_getSchedule", + "operationId": "SchedulesController_2024_06_11_getSchedule", "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "cal-api-version", + "in": "header", + "description": "Must be set to `2024-06-11`", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "scheduleId", "required": true, @@ -3093,7 +3206,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetScheduleOutput_2024_04_15" + "$ref": "#/components/schemas/GetScheduleOutput_2024_06_11" } } } @@ -3104,10 +3217,28 @@ ] }, "patch": { - "operationId": "SchedulesController_2024_04_15_updateSchedule", + "operationId": "SchedulesController_2024_06_11_updateSchedule", "parameters": [ { - "name": "scheduleId", + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "cal-api-version", + "in": "header", + "description": "Must be set to `2024-06-11`", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "scheduleId", "required": true, "in": "path", "schema": { @@ -3120,7 +3251,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateScheduleInput_2024_04_15" + "$ref": "#/components/schemas/UpdateScheduleInput_2024_06_11" } } } @@ -3131,7 +3262,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateScheduleOutput_2024_04_15" + "$ref": "#/components/schemas/UpdateScheduleOutput_2024_06_11" } } } @@ -3142,8 +3273,26 @@ ] }, "delete": { - "operationId": "SchedulesController_2024_04_15_deleteSchedule", + "operationId": "SchedulesController_2024_06_11_deleteSchedule", "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` either managed user access token or api key prefixed with cal_", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "cal-api-version", + "in": "header", + "description": "Must be set to `2024-06-11`", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "scheduleId", "required": true, @@ -3159,7 +3308,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeleteScheduleOutput_2024_04_15" + "$ref": "#/components/schemas/DeleteScheduleOutput_2024_06_11" } } } @@ -4779,7 +4928,8 @@ }, "timeZone": { "type": "string", - "example": "America/New_York" + "example": "America/New_York", + "description": "Timezone is used to create user's default schedule from Monday to Friday from 9AM to 5PM. If it is not passed then user does not have\n a default schedule and it must be created manually via the /schedules endpoint. Until the schedule is created, the user can't access availability atom to set his / her availability nor booked." }, "locale": { "enum": [ @@ -5236,1631 +5386,1911 @@ "refreshToken" ] }, - "CreateEventTypeInput_2024_06_14": { + "AddressLocation_2024_06_14": { "type": "object", "properties": { - "lengthInMinutes": { - "type": "number", - "example": 60 - }, - "title": { + "type": { "type": "string", - "example": "Learn the secrets of masterchief!" + "example": "address", + "description": "only allowed value for type is `address`" }, - "description": { + "address": { "type": "string", - "example": "Discover the culinary wonders of the Argentina by making the best flan ever!" - } - }, - "required": [ - "lengthInMinutes", - "title", - "description" - ] - }, - "EventTypeOutput_2024_06_14": { - "type": "object", - "properties": { - "id": { - "type": "number", - "example": 1 + "example": "123 Example St, City, Country" + }, + "public": { + "type": "boolean" } }, "required": [ - "id" + "type", + "address", + "public" ] }, - "CreateEventTypeOutput_2024_06_14": { + "LinkLocation_2024_06_14": { "type": "object", "properties": { - "status": { + "type": { "type": "string", - "enum": [ - "success", - "error" - ], - "example": "success" + "example": "link", + "description": "only allowed value for type is `link`" }, - "data": { - "$ref": "#/components/schemas/EventTypeOutput_2024_06_14" + "link": { + "type": "string", + "example": "https://customvideo.com/join/123456" + }, + "public": { + "type": "boolean" } }, "required": [ - "status", - "data" + "type", + "link", + "public" ] }, - "GetEventTypeOutput_2024_06_14": { + "IntegrationLocation_2024_06_14": { "type": "object", "properties": { - "status": { + "type": { "type": "string", - "enum": [ - "success", - "error" - ], - "example": "success" + "example": "integration", + "description": "only allowed value for type is `integration`" }, - "data": { - "nullable": true, - "allOf": [ - { - "$ref": "#/components/schemas/EventTypeOutput_2024_06_14" - } + "integration": { + "type": "string", + "example": "cal-video", + "enum": [ + "cal-video" ] } }, "required": [ - "status", - "data" + "type", + "integration" ] }, - "GetEventTypesOutput_2024_06_14": { + "PhoneLocation_2024_06_14": { "type": "object", "properties": { - "status": { + "type": { "type": "string", - "enum": [ - "success", - "error" - ], - "example": "success" + "example": "phone", + "description": "only allowed value for type is `phone`" }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventTypeOutput_2024_06_14" - } + "phone": { + "type": "string", + "example": "+37120993151" + }, + "public": { + "type": "boolean" } }, "required": [ - "status", - "data" + "type", + "phone", + "public" ] }, - "UpdateEventTypeInput_2024_06_14": { - "type": "object", - "properties": {} - }, - "UpdateEventTypeOutput_2024_06_14": { + "PhoneFieldInput_2024_06_14": { "type": "object", "properties": { - "status": { + "type": { "type": "string", - "enum": [ - "success", - "error" - ], - "example": "success" + "example": "phone", + "description": "only allowed value for type is `phone`" }, - "data": { - "$ref": "#/components/schemas/EventTypeOutput_2024_06_14" + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" + }, + "label": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "placeholder": { + "type": "string" } }, "required": [ - "status", - "data" + "type", + "slug", + "label", + "required", + "placeholder" ] }, - "DeleteData_2024_06_14": { + "AddressFieldInput_2024_06_14": { "type": "object", "properties": { - "id": { - "type": "number", - "example": 1 + "type": { + "type": "string", + "example": "address", + "description": "only allowed value for type is `address`" }, - "lengthInMinutes": { - "type": "number", - "example": 60 + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" }, - "title": { + "label": { "type": "string", - "example": "Learn the secrets of masterchief!" + "example": "Please enter your address" }, - "slug": { - "type": "string" + "required": { + "type": "boolean" + }, + "placeholder": { + "type": "string", + "example": "e.g., 1234 Main St" } }, "required": [ - "id", - "lengthInMinutes", - "title", - "slug" + "type", + "slug", + "label", + "required", + "placeholder" ] }, - "DeleteEventTypeOutput_2024_06_14": { + "TextFieldInput_2024_06_14": { "type": "object", "properties": { - "status": { + "type": { "type": "string", - "enum": [ - "success", - "error" - ], - "example": "success" + "example": "text", + "description": "only allowed value for type is `text`" }, - "data": { - "$ref": "#/components/schemas/DeleteData_2024_06_14" + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" + }, + "label": { + "type": "string", + "example": "Please enter your text" + }, + "required": { + "type": "boolean" + }, + "placeholder": { + "type": "string", + "example": "e.g., Enter text here" } }, "required": [ - "status", - "data" + "type", + "slug", + "label", + "required", + "placeholder" ] }, - "SelectedCalendarsInputDto": { + "NumberFieldInput_2024_06_14": { "type": "object", "properties": { - "integration": { - "type": "string" + "type": { + "type": "string", + "example": "number", + "description": "only allowed value for type is `number`" }, - "externalId": { - "type": "string" + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" }, - "credentialId": { - "type": "number" + "label": { + "type": "string", + "example": "Please enter a number" + }, + "required": { + "type": "boolean" + }, + "placeholder": { + "type": "string", + "example": "e.g., 100" } }, "required": [ - "integration", - "externalId", - "credentialId" + "type", + "slug", + "label", + "required", + "placeholder" ] }, - "SelectedCalendarOutputDto": { + "TextAreaFieldInput_2024_06_14": { "type": "object", "properties": { - "userId": { - "type": "number" + "type": { + "type": "string", + "example": "textarea", + "description": "only allowed value for type is `textarea`" }, - "integration": { - "type": "string" + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" }, - "externalId": { - "type": "string" + "label": { + "type": "string", + "example": "Please enter detailed information" }, - "credentialId": { - "type": "number", - "nullable": true + "required": { + "type": "boolean" + }, + "placeholder": { + "type": "string", + "example": "e.g., Detailed description here..." } }, "required": [ - "userId", - "integration", - "externalId", - "credentialId" + "type", + "slug", + "label", + "required", + "placeholder" ] }, - "SelectedCalendarOutputResponseDto": { + "SelectFieldInput_2024_06_14": { "type": "object", "properties": { - "status": { + "type": { "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] + "example": "select", + "description": "only allowed value for type is `select`" }, - "data": { - "$ref": "#/components/schemas/SelectedCalendarOutputDto" + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" + }, + "label": { + "type": "string", + "example": "Please select an option" + }, + "required": { + "type": "boolean" + }, + "placeholder": { + "type": "string", + "example": "Select..." + }, + "options": { + "example": [ + "Option 1", + "Option 2" + ], + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "status", - "data" + "type", + "slug", + "label", + "required", + "placeholder", + "options" ] }, - "EventTypeLocation_2024_04_15": { + "MultiSelectFieldInput_2024_06_14": { "type": "object", "properties": { "type": { "type": "string", - "example": "link" + "example": "multiselect", + "description": "only allowed value for type is `multiselect`" }, - "link": { + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" + }, + "label": { "type": "string", - "example": "https://masterchief.com/argentina/flan/video/9129412" + "example": "Please select multiple options" + }, + "required": { + "type": "boolean" + }, + "options": { + "example": [ + "Option 1", + "Option 2" + ], + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "type" + "type", + "slug", + "label", + "required", + "options" ] }, - "CreateEventTypeInput_2024_04_15": { + "MultiEmailFieldInput_2024_06_14": { "type": "object", "properties": { - "length": { - "type": "number", - "minimum": 1, - "example": 60 - }, - "slug": { + "type": { "type": "string", - "example": "cooking-class" + "example": "multiemail", + "description": "only allowed value for type is `multiemail`" }, - "title": { + "slug": { "type": "string", - "example": "Learn the secrets of masterchief!" + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" }, - "description": { + "label": { "type": "string", - "example": "Discover the culinary wonders of the Argentina by making the best flan ever!" - }, - "locations": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventTypeLocation_2024_04_15" - } + "example": "Please enter multiple emails" }, - "disableGuests": { + "required": { "type": "boolean" }, - "slotInterval": { - "type": "number", - "minimum": 0 - }, - "minimumBookingNotice": { - "type": "number", - "minimum": 0 - }, - "beforeEventBuffer": { - "type": "number", - "minimum": 0 - }, - "afterEventBuffer": { - "type": "number", - "minimum": 0 + "placeholder": { + "type": "string", + "example": "e.g., example@example.com" } }, "required": [ - "length", + "type", "slug", - "title" + "label", + "required", + "placeholder" ] }, - "EventTypeOutput": { + "CheckboxGroupFieldInput_2024_06_14": { "type": "object", "properties": { - "id": { - "type": "number", - "example": 1 - }, - "length": { - "type": "number", - "example": 60 + "type": { + "type": "string", + "example": "checkbox", + "description": "only allowed value for type is `checkbox`" }, "slug": { "type": "string", - "example": "cooking-class" + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" }, - "title": { + "label": { "type": "string", - "example": "Learn the secrets of masterchief!" + "example": "Select all that apply" }, - "description": { - "type": "string", - "nullable": true, - "example": "Discover the culinary wonders of the Argentina by making the best flan ever!" + "required": { + "type": "boolean" }, - "locations": { - "nullable": true, + "options": { + "example": [ + "Checkbox 1", + "Checkbox 2" + ], "type": "array", "items": { - "$ref": "#/components/schemas/EventTypeLocation_2024_04_15" + "type": "string" } } }, "required": [ - "id", - "length", + "type", "slug", - "title", - "description", - "locations" + "label", + "required", + "options" ] }, - "CreateEventTypeOutput": { + "RadioGroupFieldInput_2024_06_14": { "type": "object", "properties": { - "status": { + "type": { "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] + "example": "radio", + "description": "only allowed value for type is `radio`" }, - "data": { - "$ref": "#/components/schemas/EventTypeOutput" + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" + }, + "label": { + "type": "string", + "example": "Select one option" + }, + "required": { + "type": "boolean" + }, + "options": { + "example": [ + "Radio 1", + "Radio 2" + ], + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "status", - "data" + "type", + "slug", + "label", + "required", + "options" ] }, - "Data": { + "BooleanFieldInput_2024_06_14": { "type": "object", "properties": { - "eventType": { - "$ref": "#/components/schemas/EventTypeOutput" + "type": { + "type": "string", + "example": "boolean", + "description": "only allowed value for type is `boolean`" + }, + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" + }, + "label": { + "type": "string", + "example": "Agree to terms?" + }, + "required": { + "type": "boolean" } }, "required": [ - "eventType" + "type", + "slug", + "label", + "required" ] }, - "GetEventTypeOutput": { + "BusinessDaysWindow_2024_06_14": { "type": "object", "properties": { - "status": { + "type": { "type": "string", - "example": "success", "enum": [ - "success", - "error" - ] + "businessDays", + "calendarDays", + "range" + ], + "description": "Whether the window should be business days, calendar days or a range of dates" }, - "data": { - "$ref": "#/components/schemas/Data" + "value": { + "type": "number", + "example": 5, + "description": "How many business day into the future can this event be booked" + }, + "rolling": { + "type": "boolean", + "example": true, + "description": "If true, the window will be rolling aka from the moment that someone is trying to book this event. Otherwise it will be specified amount of days from the current date." } }, "required": [ - "status", - "data" + "type", + "value", + "rolling" ] }, - "EventTypeGroup": { + "CalendarDaysWindow_2024_06_14": { "type": "object", "properties": { - "eventTypes": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventTypeOutput" - } + "type": { + "type": "string", + "enum": [ + "businessDays", + "calendarDays", + "range" + ], + "description": "Whether the window should be business days, calendar days or a range of dates" + }, + "value": { + "type": "number", + "example": 5, + "description": "How many calendar days into the future can this event be booked" + }, + "rolling": { + "type": "boolean", + "example": true, + "description": "If true, the window will be rolling aka from the moment that someone is trying to book this event. Otherwise it will be specified amount of days from the current date." } }, "required": [ - "eventTypes" + "type", + "value", + "rolling" ] }, - "GetEventTypesData": { + "RangeWindow_2024_06_14": { "type": "object", "properties": { - "eventTypeGroups": { + "type": { + "type": "string", + "enum": [ + "businessDays", + "calendarDays", + "range" + ], + "description": "Whether the window should be business days, calendar days or a range of dates" + }, + "value": { + "example": [ + "2030-09-05", + "2030-09-09" + ], + "description": "Date range for when this event can be booked.", "type": "array", "items": { - "$ref": "#/components/schemas/EventTypeGroup" + "type": "string" } } }, "required": [ - "eventTypeGroups" + "type", + "value" ] }, - "GetEventTypesOutput": { + "BookingLimitsCount_2024_06_14": { "type": "object", "properties": { - "status": { - "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] + "day": { + "type": "number", + "description": "The number of bookings per day", + "example": 1 }, - "data": { - "$ref": "#/components/schemas/GetEventTypesData" + "week": { + "type": "number", + "description": "The number of bookings per week", + "example": 2 + }, + "month": { + "type": "number", + "description": "The number of bookings per month", + "example": 3 + }, + "year": { + "type": "number", + "description": "The number of bookings per year", + "example": 4 } }, "required": [ - "status", - "data" + "day", + "week", + "month", + "year" ] }, - "Location": { + "BookingLimitsDuration_2024_06_14": { "type": "object", "properties": { - "type": { - "type": "string" + "day": { + "type": "number", + "minimum": 15, + "description": "The duration of bookings per day (must be a multiple of 15)", + "example": 60 + }, + "week": { + "type": "number", + "minimum": 15, + "description": "The duration of bookings per week (must be a multiple of 15)", + "example": 120 + }, + "month": { + "type": "number", + "minimum": 15, + "description": "The duration of bookings per month (must be a multiple of 15)", + "example": 180 + }, + "year": { + "type": "number", + "minimum": 15, + "description": "The duration of bookings per year (must be a multiple of 15)", + "example": 240 } - }, - "required": [ - "type" - ] + } }, - "Source": { + "Recurrence_2024_06_14": { "type": "object", "properties": { - "id": { - "type": "string" + "interval": { + "type": "number", + "example": 10, + "description": "Repeats every {count} week | month | year" }, - "type": { - "type": "string" + "occurrences": { + "type": "number", + "example": 10, + "description": "Repeats for a maximum of {count} events" }, - "label": { - "type": "string" + "frequency": { + "type": "string", + "enum": [ + "yearly", + "monthly", + "weekly" + ] } }, "required": [ - "id", - "type", - "label" + "interval", + "occurrences", + "frequency" ] }, - "BookingField": { + "CreateEventTypeInput_2024_06_14": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "type": { - "type": "string" - }, - "defaultLabel": { - "type": "string" - }, - "label": { - "type": "string" - }, - "placeholder": { - "type": "string" - }, - "required": { - "type": "boolean" + "lengthInMinutes": { + "type": "number", + "example": 60 }, - "getOptionsAt": { - "type": "string" + "title": { + "type": "string", + "example": "Learn the secrets of masterchief!" }, - "hideWhenJustOneOption": { - "type": "boolean" + "slug": { + "type": "string", + "example": "learn-the-secrets-of-masterchief" }, - "editable": { - "type": "string" + "description": { + "type": "string", + "example": "Discover the culinary wonders of the Argentina by making the best flan ever!" }, - "sources": { + "locations": { "type": "array", + "description": "Locations where the event will take place. If not provided, cal video link will be used as the location.", "items": { - "$ref": "#/components/schemas/Source" + "oneOf": [ + { + "$ref": "#/components/schemas/AddressLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/LinkLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/IntegrationLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/PhoneLocation_2024_06_14" + } + ] } }, - "disableOnPrefill": { - "type": "boolean" - } - }, - "required": [ - "name", - "type" - ] - }, - "Organization": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "slug": { - "type": "string", - "nullable": true - }, - "name": { - "type": "string" + "bookingFields": { + "type": "array", + "description": "Custom fields that can be added to the booking form when the event is booked by someone. By default booking form has name and email field.", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/PhoneFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/AddressFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/TextFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/NumberFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/TextAreaFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/SelectFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/MultiSelectFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/MultiEmailFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/CheckboxGroupFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/RadioGroupFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/BooleanFieldInput_2024_06_14" + } + ] + } }, - "metadata": { - "type": "object" - } - }, - "required": [ - "id", - "name", - "metadata" - ] - }, - "Profile": { - "type": "object", - "properties": { - "username": { - "type": "string", - "nullable": true + "disableGuests": { + "type": "boolean", + "description": "If true, person booking this event't cant add guests via their emails." }, - "id": { + "slotInterval": { "type": "number", - "nullable": true + "description": "Number representing length of each slot when event is booked. By default it equal length of the event type.\n If event length is 60 minutes then we would have slots 9AM, 10AM, 11AM etc. but if it was changed to 30 minutes then\n we would have slots 9AM, 9:30AM, 10AM, 10:30AM etc. as the available times to book the 60 minute event." }, - "userId": { - "type": "number" + "minimumBookingNotice": { + "type": "number", + "description": "Minimum number of minutes before the event that a booking can be made." }, - "uid": { - "type": "string" + "beforeEventBuffer": { + "type": "number", + "description": "Time spaces that can be pre-pended before an event to give more time before it." }, - "name": { - "type": "string" + "afterEventBuffer": { + "type": "number", + "description": "Time spaces that can be appended after an event to give more time after it." }, - "organizationId": { + "scheduleId": { "type": "number", - "nullable": true + "description": "If you want that this event has different schedule than user's default one you can specify it here." }, - "organization": { - "nullable": true, + "bookingLimitsCount": { + "description": "Limit how many times this event can be booked", "allOf": [ { - "$ref": "#/components/schemas/Organization" + "$ref": "#/components/schemas/BookingLimitsCount_2024_06_14" } ] }, - "upId": { - "type": "string" - }, - "image": { - "type": "string" + "onlyShowFirstAvailableSlot": { + "type": "boolean", + "description": "This will limit your availability for this event type to one slot per day, scheduled at the earliest available time." }, - "brandColor": { - "type": "string" + "bookingLimitsDuration": { + "description": "Limit total amount of time that this event can be booked", + "allOf": [ + { + "$ref": "#/components/schemas/BookingLimitsDuration_2024_06_14" + } + ] }, - "darkBrandColor": { - "type": "string" + "bookingWindow": { + "type": "array", + "description": "Limit how far in the future this event can be booked", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/BusinessDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/CalendarDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/RangeWindow_2024_06_14" + } + ] + } }, - "theme": { - "type": "string" + "offsetStart": { + "type": "number", + "description": "Offset timeslots shown to bookers by a specified number of minutes" }, - "bookerLayouts": { - "type": "object" + "recurrence": { + "description": "Create a recurring event that can be booked once but will occur multiple times", + "allOf": [ + { + "$ref": "#/components/schemas/Recurrence_2024_06_14" + } + ] } }, "required": [ - "username", - "id", - "organizationId", - "upId" + "lengthInMinutes", + "title", + "slug", + "description", + "locations", + "bookingFields", + "disableGuests", + "slotInterval", + "minimumBookingNotice", + "beforeEventBuffer", + "afterEventBuffer", + "scheduleId", + "bookingLimitsCount", + "onlyShowFirstAvailableSlot", + "bookingLimitsDuration", + "bookingWindow", + "offsetStart", + "recurrence" ] }, - "Owner": { + "EmailDefaultFieldOutput_2024_06_14": { "type": "object", "properties": { - "id": { - "type": "number" - }, - "avatarUrl": { - "type": "string", - "nullable": true - }, - "username": { - "type": "string", - "nullable": true - }, - "name": { - "type": "string", - "nullable": true - }, - "weekStart": { - "type": "string" - }, - "brandColor": { - "type": "string", - "nullable": true - }, - "darkBrandColor": { - "type": "string", - "nullable": true + "isDefault": { + "type": "object", + "default": true, + "description": "This property is always true because it's a default field", + "example": true }, - "theme": { + "slug": { "type": "string", - "nullable": true - }, - "metadata": { - "type": "object" - }, - "defaultScheduleId": { - "type": "number", - "nullable": true + "default": "email" }, - "nonProfileUsername": { + "type": { "type": "string", - "nullable": true + "default": "email" }, - "profile": { - "$ref": "#/components/schemas/Profile" + "required": { + "type": "boolean" } }, "required": [ - "id", - "username", - "name", - "weekStart", - "metadata", - "nonProfileUsername", - "profile" + "isDefault", + "slug", + "type", + "required" ] }, - "Schedule": { + "NameDefaultFieldOutput_2024_06_14": { "type": "object", "properties": { - "id": { - "type": "number" + "isDefault": { + "type": "object", + "default": true, + "description": "This property is always true because it's a default field", + "example": true }, - "timeZone": { + "slug": { "type": "string", - "nullable": true + "default": "name" + }, + "type": { + "type": "string", + "default": "name" + }, + "required": { + "type": "boolean" } }, "required": [ - "id", - "timeZone" + "isDefault", + "slug", + "type", + "required" ] }, - "User": { + "LocationDefaultFieldOutput_2024_06_14": { "type": "object", "properties": { - "username": { - "type": "string", - "nullable": true + "isDefault": { + "type": "object", + "default": true, + "description": "This property is always true because it's a default field", + "example": true }, - "name": { + "slug": { "type": "string", - "nullable": true + "default": "location" }, - "weekStart": { - "type": "string" - }, - "organizationId": { - "type": "number" - }, - "avatarUrl": { + "type": { "type": "string", - "nullable": true - }, - "profile": { - "$ref": "#/components/schemas/Profile" + "default": "radioInput" }, - "bookerUrl": { - "type": "string" + "required": { + "type": "boolean" } }, "required": [ - "username", - "name", - "weekStart", - "profile", - "bookerUrl" + "isDefault", + "slug", + "type", + "required" ] }, - "PublicEventTypeOutput": { + "RescheduleReasonDefaultFieldOutput_2024_06_14": { "type": "object", "properties": { - "id": { - "type": "number" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "eventName": { - "type": "string", - "nullable": true - }, - "slug": { - "type": "string" - }, - "isInstantEvent": { - "type": "boolean" - }, - "aiPhoneCallConfig": { - "type": "object" - }, - "schedulingType": { - "type": "object" - }, - "length": { - "type": "number" - }, - "locations": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Location" - } - }, - "customInputs": { - "type": "array", - "items": { - "type": "object" - } - }, - "disableGuests": { - "type": "boolean" - }, - "metadata": { + "isDefault": { "type": "object", - "nullable": true - }, - "lockTimeZoneToggleOnBookingPage": { - "type": "boolean" - }, - "requiresConfirmation": { - "type": "boolean" - }, - "requiresBookerEmailVerification": { - "type": "boolean" - }, - "recurringEvent": { - "type": "object" - }, - "price": { - "type": "number" - }, - "currency": { - "type": "string" - }, - "seatsPerTimeSlot": { - "type": "number", - "nullable": true - }, - "seatsShowAvailabilityCount": { - "type": "boolean", - "nullable": true - }, - "bookingFields": { - "type": "array", - "items": { - "$ref": "#/components/schemas/BookingField" - } - }, - "team": { - "type": "object" + "default": true, + "description": "This property is always true because it's a default field", + "example": true }, - "successRedirectUrl": { + "slug": { "type": "string", - "nullable": true - }, - "workflows": { - "type": "array", - "items": { - "type": "object" - } - }, - "hosts": { - "type": "array", - "items": { - "type": "object" - } - }, - "owner": { - "nullable": true, - "allOf": [ - { - "$ref": "#/components/schemas/Owner" - } - ] - }, - "schedule": { - "nullable": true, - "allOf": [ - { - "$ref": "#/components/schemas/Schedule" - } - ] - }, - "hidden": { - "type": "boolean" - }, - "assignAllTeamMembers": { - "type": "boolean" - }, - "bookerLayouts": { - "type": "object" - }, - "users": { - "type": "array", - "items": { - "$ref": "#/components/schemas/User" - } + "default": "rescheduleReason" }, - "entity": { - "type": "object" + "type": { + "type": "string", + "default": "textarea" }, - "isDynamic": { + "required": { "type": "boolean" } }, "required": [ - "id", - "title", - "description", + "isDefault", "slug", - "isInstantEvent", - "length", - "locations", - "customInputs", - "disableGuests", - "metadata", - "lockTimeZoneToggleOnBookingPage", - "requiresConfirmation", - "requiresBookerEmailVerification", - "price", - "currency", - "seatsShowAvailabilityCount", - "bookingFields", - "workflows", - "hosts", - "owner", - "schedule", - "hidden", - "assignAllTeamMembers", - "users", - "entity", - "isDynamic" + "type", + "required" ] }, - "GetEventTypePublicOutput": { + "TitleDefaultFieldOutput_2024_06_14": { "type": "object", "properties": { - "status": { + "isDefault": { + "type": "object", + "default": true, + "description": "This property is always true because it's a default field", + "example": true + }, + "slug": { "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] + "default": "title" }, - "data": { - "nullable": true, - "allOf": [ - { - "$ref": "#/components/schemas/PublicEventTypeOutput" - } - ] + "type": { + "type": "string", + "default": "text" + }, + "required": { + "type": "boolean" } }, "required": [ - "status", - "data" + "isDefault", + "slug", + "type", + "required" ] }, - "PublicEventType": { + "NotesDefaultFieldOutput_2024_06_14": { "type": "object", "properties": { - "id": { - "type": "number", - "example": 1 - }, - "length": { - "type": "number", - "example": 60 + "isDefault": { + "type": "object", + "default": true, + "description": "This property is always true because it's a default field", + "example": true }, "slug": { "type": "string", - "example": "cooking-class" + "default": "notes" }, - "title": { + "type": { "type": "string", - "example": "Learn the secrets of masterchief!" + "default": "textarea" }, - "description": { - "type": "string", - "nullable": true + "required": { + "type": "boolean" } }, "required": [ - "id", - "length", + "isDefault", "slug", - "title" + "type", + "required" ] }, - "GetEventTypesPublicOutput": { + "GuestsDefaultFieldOutput_2024_06_14": { "type": "object", "properties": { - "status": { + "isDefault": { + "type": "object", + "default": true, + "description": "This property is always true because it's a default field", + "example": true + }, + "slug": { "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] + "default": "guests" }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicEventType" - } + "type": { + "type": "string", + "default": "multiemail" + }, + "required": { + "type": "boolean" } }, "required": [ - "status", - "data" + "isDefault", + "slug", + "type", + "required" ] }, - "Option": { + "AddressFieldOutput_2024_06_14": { "type": "object", "properties": { - "value": { - "type": "string" + "type": { + "type": "string", + "enum": [ + "phone", + "address", + "text", + "number", + "textarea", + "select", + "multiselect", + "multiemail", + "checkbox", + "radio", + "boolean" + ], + "example": "address", + "description": "only allowed value for type is `address`" + }, + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" }, "label": { - "type": "string" - } - }, - "required": [ - "value", - "label" - ] - }, - "VariantsConfig": { - "type": "object", - "properties": { - "variants": { - "type": "object" + "type": "string", + "example": "Please enter your address" + }, + "required": { + "type": "boolean" + }, + "placeholder": { + "type": "string", + "example": "e.g., 1234 Main St" + }, + "isDefault": { + "type": "object", + "default": false, + "description": "This property is always false because it's not default field but custom field", + "example": false } }, "required": [ - "variants" + "type", + "slug", + "label", + "required", + "isDefault" ] }, - "View": { + "BooleanFieldOutput_2024_06_14": { "type": "object", "properties": { - "id": { - "type": "string" + "type": { + "type": "string", + "enum": [ + "phone", + "address", + "text", + "number", + "textarea", + "select", + "multiselect", + "multiemail", + "checkbox", + "radio", + "boolean" + ], + "example": "boolean", + "description": "only allowed value for type is `boolean`" + }, + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" }, "label": { - "type": "string" + "type": "string", + "example": "Agree to terms?" }, - "description": { - "type": "string" + "required": { + "type": "boolean" + }, + "isDefault": { + "type": "object", + "default": false, + "description": "This property is always false because it's not default field but custom field", + "example": false } }, "required": [ - "id", - "label" + "type", + "slug", + "label", + "required", + "isDefault" ] }, - "BookingField_2024_04_15": { + "CheckboxGroupFieldOutput_2024_06_14": { "type": "object", "properties": { "type": { "type": "string", "enum": [ - "number", - "boolean", + "phone", "address", - "name", "text", + "number", "textarea", - "email", - "phone", - "multiemail", "select", "multiselect", + "multiemail", "checkbox", "radio", - "radioInput" - ] - }, - "name": { - "type": "string" + "boolean" + ], + "example": "checkbox", + "description": "only allowed value for type is `checkbox`" }, - "options": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Option" - } + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" }, "label": { - "type": "string" - }, - "labelAsSafeHtml": { - "type": "string" - }, - "defaultLabel": { - "type": "string" - }, - "placeholder": { - "type": "string" + "type": "string", + "example": "Select all that apply" }, "required": { "type": "boolean" }, - "getOptionsAt": { - "type": "string" - }, - "optionsInputs": { - "type": "object" - }, - "variant": { - "type": "string" - }, - "variantsConfig": { - "$ref": "#/components/schemas/VariantsConfig" - }, - "views": { + "options": { + "example": [ + "Checkbox 1", + "Checkbox 2" + ], "type": "array", "items": { - "$ref": "#/components/schemas/View" + "type": "string" } }, - "hideWhenJustOneOption": { - "type": "boolean" - }, - "hidden": { - "type": "boolean" - }, - "editable": { + "isDefault": { + "type": "object", + "default": false, + "description": "This property is always false because it's not default field but custom field", + "example": false + } + }, + "required": [ + "type", + "slug", + "label", + "required", + "options", + "isDefault" + ] + }, + "MultiEmailFieldOutput_2024_06_14": { + "type": "object", + "properties": { + "type": { "type": "string", "enum": [ - "system", - "system-but-optional", - "system-but-hidden", - "user", - "user-readonly" - ] + "phone", + "address", + "text", + "number", + "textarea", + "select", + "multiselect", + "multiemail", + "checkbox", + "radio", + "boolean" + ], + "example": "multiemail", + "description": "only allowed value for type is `multiemail`" }, - "sources": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Source" - } + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" + }, + "label": { + "type": "string", + "example": "Please enter multiple emails" }, - "disableOnPrefill": { + "required": { "type": "boolean" + }, + "placeholder": { + "type": "string", + "example": "e.g., example@example.com" + }, + "isDefault": { + "type": "object", + "default": false, + "description": "This property is always false because it's not default field but custom field", + "example": false } }, "required": [ "type", - "name" + "slug", + "label", + "required", + "isDefault" ] }, - "UpdateEventTypeInput_2024_04_15": { + "MultiSelectFieldOutput_2024_06_14": { "type": "object", "properties": { - "length": { - "type": "number", - "minimum": 1 + "type": { + "type": "string", + "enum": [ + "phone", + "address", + "text", + "number", + "textarea", + "select", + "multiselect", + "multiemail", + "checkbox", + "radio", + "boolean" + ], + "example": "multiselect", + "description": "only allowed value for type is `multiselect`" }, "slug": { - "type": "string" - }, - "title": { - "type": "string" + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" }, - "description": { - "type": "string" + "label": { + "type": "string", + "example": "Please select multiple options" }, - "hidden": { + "required": { "type": "boolean" }, - "locations": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventTypeLocation_2024_04_15" - } - }, - "bookingFields": { + "options": { + "example": [ + "Option 1", + "Option 2" + ], "type": "array", "items": { - "$ref": "#/components/schemas/BookingField_2024_04_15" + "type": "string" } }, - "disableGuests": { - "type": "boolean" - }, - "minimumBookingNotice": { - "type": "number", - "minimum": 0 - }, - "beforeEventBuffer": { - "type": "number", - "minimum": 0 - }, - "afterEventBuffer": { - "type": "number", - "minimum": 0 - }, - "slotInterval": { - "type": "number", - "minimum": 0 - } - } - }, - "UpdateEventTypeOutput": { - "type": "object", - "properties": { - "status": { - "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] - }, - "data": { - "$ref": "#/components/schemas/EventTypeOutput" + "isDefault": { + "type": "object", + "default": false, + "description": "This property is always false because it's not default field but custom field", + "example": false } }, "required": [ - "status", - "data" + "type", + "slug", + "label", + "required", + "options", + "isDefault" ] }, - "DeleteData": { + "NumberFieldOutput_2024_06_14": { "type": "object", "properties": { - "id": { - "type": "number", - "example": 1 - }, - "length": { - "type": "number", - "example": 60 + "type": { + "type": "string", + "enum": [ + "phone", + "address", + "text", + "number", + "textarea", + "select", + "multiselect", + "multiemail", + "checkbox", + "radio", + "boolean" + ], + "example": "number", + "description": "only allowed value for type is `number`" }, "slug": { "type": "string", - "example": "cooking-class" + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" }, - "title": { + "label": { "type": "string", - "example": "Learn the secrets of masterchief!" + "example": "Please enter a number" + }, + "required": { + "type": "boolean" + }, + "placeholder": { + "type": "string", + "example": "e.g., 100" + }, + "isDefault": { + "type": "object", + "default": false, + "description": "This property is always false because it's not default field but custom field", + "example": false } }, "required": [ - "id", - "length", + "type", "slug", - "title" + "label", + "required", + "isDefault" ] }, - "DeleteEventTypeOutput": { + "PhoneFieldOutput_2024_06_14": { "type": "object", "properties": { - "status": { + "type": { "type": "string", - "example": "success", "enum": [ - "success", - "error" - ] - }, - "data": { - "$ref": "#/components/schemas/DeleteData" - } - }, - "required": [ - "status", - "data" - ] - }, - "OrgTeamOutputDto": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "parentId": { - "type": "number" - }, - "name": { - "type": "string", - "minLength": 1 + "phone", + "address", + "text", + "number", + "textarea", + "select", + "multiselect", + "multiemail", + "checkbox", + "radio", + "boolean" + ], + "example": "phone", + "description": "only allowed value for type is `phone`" }, "slug": { - "type": "string" - }, - "logoUrl": { - "type": "string" - }, - "calVideoLogo": { - "type": "string" - }, - "appLogo": { - "type": "string" - }, - "appIconLogo": { - "type": "string" + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" }, - "bio": { + "label": { "type": "string" }, - "hideBranding": { - "type": "boolean" - }, - "isOrganization": { - "type": "boolean" - }, - "isPrivate": { + "required": { "type": "boolean" }, - "hideBookATeamMember": { - "type": "boolean", - "default": false - }, - "metadata": { - "type": "string" - }, - "theme": { - "type": "string" - }, - "brandColor": { - "type": "string" - }, - "darkBrandColor": { - "type": "string" - }, - "bannerUrl": { + "placeholder": { "type": "string" }, - "timeFormat": { - "type": "number" - }, - "timeZone": { - "type": "string", - "default": "Europe/London" - }, - "weekStart": { - "type": "string", - "default": "Sunday" + "isDefault": { + "type": "object", + "default": false, + "description": "This property is always false because it's not default field but custom field", + "example": false } }, "required": [ - "id", - "name" + "type", + "slug", + "label", + "required", + "isDefault" ] }, - "OrgTeamsOutputResponseDto": { + "RadioGroupFieldOutput_2024_06_14": { "type": "object", "properties": { - "status": { + "type": { "type": "string", - "example": "success", "enum": [ - "success", - "error" - ] + "phone", + "address", + "text", + "number", + "textarea", + "select", + "multiselect", + "multiemail", + "checkbox", + "radio", + "boolean" + ], + "example": "radio", + "description": "only allowed value for type is `radio`" }, - "data": { + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" + }, + "label": { + "type": "string", + "example": "Select one option" + }, + "required": { + "type": "boolean" + }, + "options": { + "example": [ + "Radio 1", + "Radio 2" + ], "type": "array", "items": { - "$ref": "#/components/schemas/OrgTeamOutputDto" + "type": "string" } + }, + "isDefault": { + "type": "object", + "default": false, + "description": "This property is always false because it's not default field but custom field", + "example": false } }, "required": [ - "status", - "data" + "type", + "slug", + "label", + "required", + "options", + "isDefault" ] }, - "OrgMeTeamsOutputResponseDto": { + "SelectFieldOutput_2024_06_14": { "type": "object", "properties": { - "status": { + "type": { "type": "string", - "example": "success", "enum": [ - "success", - "error" - ] + "phone", + "address", + "text", + "number", + "textarea", + "select", + "multiselect", + "multiemail", + "checkbox", + "radio", + "boolean" + ], + "example": "select", + "description": "only allowed value for type is `select`" }, - "data": { + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" + }, + "label": { + "type": "string", + "example": "Please select an option" + }, + "required": { + "type": "boolean" + }, + "placeholder": { + "type": "string", + "example": "Select..." + }, + "options": { + "example": [ + "Option 1", + "Option 2" + ], "type": "array", "items": { - "$ref": "#/components/schemas/OrgTeamOutputDto" + "type": "string" } + }, + "isDefault": { + "type": "object", + "default": false, + "description": "This property is always false because it's not default field but custom field", + "example": false } }, "required": [ - "status", - "data" + "type", + "slug", + "label", + "required", + "options", + "isDefault" ] }, - "OrgTeamOutputResponseDto": { + "TextAreaFieldOutput_2024_06_14": { "type": "object", "properties": { - "status": { + "type": { "type": "string", - "example": "success", "enum": [ - "success", - "error" - ] + "phone", + "address", + "text", + "number", + "textarea", + "select", + "multiselect", + "multiemail", + "checkbox", + "radio", + "boolean" + ], + "example": "textarea", + "description": "only allowed value for type is `textarea`" }, - "data": { - "$ref": "#/components/schemas/OrgTeamOutputDto" + "slug": { + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" + }, + "label": { + "type": "string", + "example": "Please enter detailed information" + }, + "required": { + "type": "boolean" + }, + "placeholder": { + "type": "string", + "example": "e.g., Detailed description here..." + }, + "isDefault": { + "type": "object", + "default": false, + "description": "This property is always false because it's not default field but custom field", + "example": false } }, "required": [ - "status", - "data" + "type", + "slug", + "label", + "required", + "isDefault" ] }, - "UpdateOrgTeamDto": { + "TextFieldOutput_2024_06_14": { "type": "object", "properties": { - "name": { + "type": { "type": "string", - "minLength": 1 + "enum": [ + "phone", + "address", + "text", + "number", + "textarea", + "select", + "multiselect", + "multiemail", + "checkbox", + "radio", + "boolean" + ], + "example": "text", + "description": "only allowed value for type is `text`" }, "slug": { - "type": "string" - }, - "logoUrl": { - "type": "string" + "type": "string", + "description": "Unique identifier for the field in format `some-slug`. It is used to access response to this booking field during the booking", + "example": "some-slug" }, - "calVideoLogo": { - "type": "string" + "label": { + "type": "string", + "example": "Please enter your text" }, - "appLogo": { - "type": "string" + "required": { + "type": "boolean" }, - "appIconLogo": { - "type": "string" + "placeholder": { + "type": "string", + "example": "e.g., Enter text here" }, - "bio": { - "type": "string" + "isDefault": { + "type": "object", + "default": false, + "description": "This property is always false because it's not default field but custom field", + "example": false + } + }, + "required": [ + "type", + "slug", + "label", + "required", + "isDefault" + ] + }, + "EventTypeOutput_2024_06_14": { + "type": "object", + "properties": { + "id": { + "type": "number", + "example": 1 }, - "hideBranding": { - "type": "boolean", - "default": false + "lengthInMinutes": { + "type": "number", + "example": 60 }, - "isPrivate": { - "type": "boolean" + "title": { + "type": "string", + "example": "Learn the secrets of masterchief!" }, - "hideBookATeamMember": { - "type": "boolean" + "slug": { + "type": "string", + "example": "learn-the-secrets-of-masterchief" }, - "metadata": { - "type": "string" + "description": { + "type": "string", + "example": "Discover the culinary wonders of the Argentina by making the best flan ever!" }, - "theme": { - "type": "string" + "locations": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/AddressLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/LinkLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/IntegrationLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/PhoneLocation_2024_06_14" + } + ] + } }, - "brandColor": { - "type": "string" + "bookingFields": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/NameDefaultFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/EmailDefaultFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/LocationDefaultFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/RescheduleReasonDefaultFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/TitleDefaultFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/NotesDefaultFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/GuestsDefaultFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/PhoneFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/AddressFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/TextFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/NumberFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/TextAreaFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/SelectFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/MultiSelectFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/MultiEmailFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/CheckboxGroupFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/RadioGroupFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/BooleanFieldOutput_2024_06_14" + } + ] + } }, - "darkBrandColor": { - "type": "string" + "disableGuests": { + "type": "boolean" }, - "bannerUrl": { - "type": "string" + "slotInterval": { + "type": "number", + "example": 60 }, - "timeFormat": { - "type": "number" + "minimumBookingNotice": { + "type": "number", + "example": 0 }, - "timeZone": { - "type": "string", - "default": "Europe/London" + "beforeEventBuffer": { + "type": "number", + "example": 0 }, - "weekStart": { - "type": "string", - "default": "Sunday" - } - } - }, - "CreateOrgTeamDto": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1 + "afterEventBuffer": { + "type": "number", + "example": 0 }, - "slug": { - "type": "string" + "recurrence": { + "type": "object" }, - "logoUrl": { - "type": "string" + "metadata": { + "type": "object" }, - "calVideoLogo": { - "type": "string" + "requiresConfirmation": { + "type": "boolean" }, - "appLogo": { - "type": "string" + "price": { + "type": "number" }, - "appIconLogo": { + "currency": { "type": "string" }, - "bio": { - "type": "string" + "lockTimeZoneToggleOnBookingPage": { + "type": "boolean" }, - "hideBranding": { - "type": "boolean", - "default": false + "seatsPerTimeSlot": { + "type": "object" }, - "isPrivate": { - "type": "boolean" + "forwardParamsSuccessRedirect": { + "type": "object" }, - "hideBookATeamMember": { - "type": "boolean" + "successRedirectUrl": { + "type": "object" }, - "metadata": { - "type": "string" + "seatsShowAvailabilityCount": { + "type": "object" }, - "theme": { - "type": "string" + "scheduleId": { + "type": "object" }, - "brandColor": { - "type": "string" + "bookingLimitsCount": { + "$ref": "#/components/schemas/BookingLimitsCount_2024_06_14" }, - "darkBrandColor": { - "type": "string" + "onlyShowFirstAvailableSlot": { + "type": "boolean" }, - "bannerUrl": { - "type": "string" + "bookingLimitsDuration": { + "$ref": "#/components/schemas/BookingLimitsDuration_2024_06_14" }, - "timeFormat": { - "type": "number" + "bookingWindow": { + "type": "array", + "description": "Limit how far in the future this event can be booked", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/BusinessDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/CalendarDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/RangeWindow_2024_06_14" + } + ] + } }, - "timeZone": { - "type": "string", - "default": "Europe/London" + "offsetStart": { + "type": "number" }, - "weekStart": { - "type": "string", - "default": "Sunday" + "ownerId": { + "type": "number", + "example": 10 }, - "autoAcceptCreator": { - "type": "boolean", - "default": true + "users": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "name" + "id", + "lengthInMinutes", + "title", + "slug", + "description", + "locations", + "bookingFields", + "disableGuests", + "slotInterval", + "minimumBookingNotice", + "beforeEventBuffer", + "afterEventBuffer", + "recurrence", + "metadata", + "requiresConfirmation", + "price", + "currency", + "lockTimeZoneToggleOnBookingPage", + "seatsPerTimeSlot", + "forwardParamsSuccessRedirect", + "successRedirectUrl", + "seatsShowAvailabilityCount", + "scheduleId", + "bookingLimitsCount", + "onlyShowFirstAvailableSlot", + "bookingLimitsDuration", + "bookingWindow", + "offsetStart", + "ownerId", + "users" ] }, - "ScheduleAvailabilityInput_2024_06_11": { + "CreateEventTypeOutput_2024_06_14": { "type": "object", "properties": { - "days": { - "example": [ - "Monday", - "Tuesday" - ], - "type": "array", - "items": { - "type": "object" - } - }, - "startTime": { + "status": { "type": "string", - "pattern": "TIME_FORMAT_HH_MM", - "example": "09:00" + "enum": [ + "success", + "error" + ], + "example": "success" }, - "endTime": { - "type": "string", - "pattern": "TIME_FORMAT_HH_MM", - "example": "10:00" + "data": { + "$ref": "#/components/schemas/EventTypeOutput_2024_06_14" } }, "required": [ - "days", - "startTime", - "endTime" + "status", + "data" ] }, - "ScheduleOverrideInput_2024_06_11": { + "GetEventTypeOutput_2024_06_14": { "type": "object", "properties": { - "date": { + "status": { "type": "string", - "example": "2024-05-20" + "enum": [ + "success", + "error" + ], + "example": "success" }, - "startTime": { - "type": "string", - "pattern": "TIME_FORMAT_HH_MM", - "example": "12:00" - }, - "endTime": { - "type": "string", - "pattern": "TIME_FORMAT_HH_MM", - "example": "13:00" - } - }, - "required": [ - "date", - "startTime", - "endTime" - ] - }, - "ScheduleOutput_2024_06_11": { - "type": "object", - "properties": { - "id": { - "type": "number", - "example": 254 - }, - "ownerId": { - "type": "number", - "example": 478 - }, - "name": { - "type": "string", - "example": "One-on-one coaching" - }, - "timeZone": { - "type": "string", - "example": "Europe/Rome" - }, - "availability": { - "example": [ - { - "days": [ - "Monday", - "Tuesday" - ], - "startTime": "09:00", - "endTime": "10:00" - } - ], - "type": "array", - "items": { - "$ref": "#/components/schemas/ScheduleAvailabilityInput_2024_06_11" - } - }, - "isDefault": { - "type": "boolean", - "example": true - }, - "overrides": { - "example": [ + "data": { + "nullable": true, + "allOf": [ { - "date": "2024-05-20", - "startTime": "12:00", - "endTime": "13:00" + "$ref": "#/components/schemas/EventTypeOutput_2024_06_14" } - ], - "type": "array", - "items": { - "$ref": "#/components/schemas/ScheduleOverrideInput_2024_06_11" - } + ] } }, "required": [ - "id", - "ownerId", - "name", - "timeZone", - "availability", - "isDefault", - "overrides" + "status", + "data" ] }, - "GetSchedulesOutput_2024_06_11": { + "GetEventTypesOutput_2024_06_14": { "type": "object", "properties": { "status": { "type": "string", - "example": "success", "enum": [ "success", "error" - ] + ], + "example": "success" }, "data": { "type": "array", "items": { - "$ref": "#/components/schemas/ScheduleOutput_2024_06_11" + "$ref": "#/components/schemas/EventTypeOutput_2024_06_14" } - }, - "error": { - "type": "object" } }, "required": [ @@ -6868,70 +7298,194 @@ "data" ] }, - "CreateScheduleInput_2024_06_11": { + "UpdateEventTypeInput_2024_06_14": { "type": "object", "properties": { - "name": { + "lengthInMinutes": { + "type": "number", + "example": 60 + }, + "title": { "type": "string", - "example": "One-on-one coaching" + "example": "Learn the secrets of masterchief!" }, - "timeZone": { + "slug": { "type": "string", - "example": "Europe/Rome" + "example": "learn-the-secrets-of-masterchief" }, - "availability": { - "example": [ - { - "days": [ - "Monday", - "Tuesday" - ], - "startTime": "09:00", - "endTime": "10:00" - } - ], + "description": { + "type": "string", + "example": "Discover the culinary wonders of the Argentina by making the best flan ever!" + }, + "locations": { "type": "array", + "description": "Locations where the event will take place. If not provided, cal video link will be used as the location.", "items": { - "$ref": "#/components/schemas/ScheduleAvailabilityInput_2024_06_11" + "oneOf": [ + { + "$ref": "#/components/schemas/AddressLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/LinkLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/IntegrationLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/PhoneLocation_2024_06_14" + } + ] } }, - "isDefault": { + "bookingFields": { + "type": "array", + "description": "Custom fields that can be added to the booking form when the event is booked by someone. By default booking form has name and email field.", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/PhoneFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/AddressFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/TextFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/NumberFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/TextAreaFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/SelectFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/MultiSelectFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/MultiEmailFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/CheckboxGroupFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/RadioGroupFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/BooleanFieldInput_2024_06_14" + } + ] + } + }, + "disableGuests": { "type": "boolean", - "example": true + "description": "If true, person booking this event't cant add guests via their emails." }, - "overrides": { - "example": [ + "slotInterval": { + "type": "number", + "description": "Number representing length of each slot when event is booked. By default it equal length of the event type.\n If event length is 60 minutes then we would have slots 9AM, 10AM, 11AM etc. but if it was changed to 30 minutes then\n we would have slots 9AM, 9:30AM, 10AM, 10:30AM etc. as the available times to book the 60 minute event." + }, + "minimumBookingNotice": { + "type": "number", + "description": "Minimum number of minutes before the event that a booking can be made." + }, + "beforeEventBuffer": { + "type": "number", + "description": "Time spaces that can be pre-pended before an event to give more time before it." + }, + "afterEventBuffer": { + "type": "number", + "description": "Time spaces that can be appended after an event to give more time after it." + }, + "scheduleId": { + "type": "number", + "description": "If you want that this event has different schedule than user's default one you can specify it here." + }, + "bookingLimitsCount": { + "description": "Limit how many times this event can be booked", + "allOf": [ { - "date": "2024-05-20", - "startTime": "12:00", - "endTime": "14:00" + "$ref": "#/components/schemas/BookingLimitsCount_2024_06_14" } - ], + ] + }, + "onlyShowFirstAvailableSlot": { + "type": "boolean", + "description": "This will limit your availability for this event type to one slot per day, scheduled at the earliest available time." + }, + "bookingLimitsDuration": { + "description": "Limit total amount of time that this event can be booked", + "allOf": [ + { + "$ref": "#/components/schemas/BookingLimitsDuration_2024_06_14" + } + ] + }, + "bookingWindow": { "type": "array", + "description": "Limit how far in the future this event can be booked", "items": { - "$ref": "#/components/schemas/ScheduleOverrideInput_2024_06_11" + "oneOf": [ + { + "$ref": "#/components/schemas/BusinessDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/CalendarDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/RangeWindow_2024_06_14" + } + ] } + }, + "offsetStart": { + "type": "number", + "description": "Offset timeslots shown to bookers by a specified number of minutes" + }, + "recurrence": { + "description": "Create a recurring event that can be booked once but will occur multiple times", + "allOf": [ + { + "$ref": "#/components/schemas/Recurrence_2024_06_14" + } + ] } }, "required": [ - "name", - "timeZone", - "isDefault" + "lengthInMinutes", + "title", + "slug", + "description", + "locations", + "bookingFields", + "disableGuests", + "slotInterval", + "minimumBookingNotice", + "beforeEventBuffer", + "afterEventBuffer", + "scheduleId", + "bookingLimitsCount", + "onlyShowFirstAvailableSlot", + "bookingLimitsDuration", + "bookingWindow", + "offsetStart", + "recurrence" ] }, - "CreateScheduleOutput_2024_06_11": { + "UpdateEventTypeOutput_2024_06_14": { "type": "object", "properties": { "status": { "type": "string", - "example": "success", "enum": [ "success", "error" - ] + ], + "example": "success" }, "data": { - "$ref": "#/components/schemas/ScheduleOutput_2024_06_11" + "$ref": "#/components/schemas/EventTypeOutput_2024_06_14" } }, "required": [ @@ -6939,27 +7493,45 @@ "data" ] }, - "GetScheduleOutput_2024_06_11": { + "DeleteData_2024_06_14": { + "type": "object", + "properties": { + "id": { + "type": "number", + "example": 1 + }, + "lengthInMinutes": { + "type": "number", + "example": 60 + }, + "title": { + "type": "string", + "example": "Learn the secrets of masterchief!" + }, + "slug": { + "type": "string" + } + }, + "required": [ + "id", + "lengthInMinutes", + "title", + "slug" + ] + }, + "DeleteEventTypeOutput_2024_06_14": { "type": "object", "properties": { "status": { "type": "string", - "example": "success", "enum": [ "success", "error" - ] + ], + "example": "success" }, "data": { - "nullable": true, - "allOf": [ - { - "$ref": "#/components/schemas/ScheduleOutput_2024_06_11" - } - ] - }, - "error": { - "type": "object" + "$ref": "#/components/schemas/DeleteData_2024_06_14" } }, "required": [ @@ -6967,76 +7539,50 @@ "data" ] }, - "UpdateScheduleInput_2024_06_11": { + "SelectedCalendarsInputDto": { "type": "object", "properties": { - "name": { - "type": "string", - "example": "One-on-one coaching" - }, - "timeZone": { - "type": "string", - "example": "Europe/Rome" - }, - "availability": { - "example": [ - { - "days": [ - "Monday", - "Tuesday" - ], - "startTime": "09:00", - "endTime": "10:00" - } - ], - "type": "array", - "items": { - "$ref": "#/components/schemas/ScheduleAvailabilityInput_2024_06_11" - } + "integration": { + "type": "string" }, - "isDefault": { - "type": "boolean", - "example": true + "externalId": { + "type": "string" }, - "overrides": { - "example": [ - { - "date": "2024-05-20", - "startTime": "12:00", - "endTime": "14:00" - } - ], - "type": "array", - "items": { - "$ref": "#/components/schemas/ScheduleOverrideInput_2024_06_11" - } + "credentialId": { + "type": "number" } - } + }, + "required": [ + "integration", + "externalId", + "credentialId" + ] }, - "UpdateScheduleOutput_2024_06_11": { + "SelectedCalendarOutputDto": { "type": "object", "properties": { - "status": { - "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] + "userId": { + "type": "number" }, - "data": { - "$ref": "#/components/schemas/ScheduleOutput_2024_06_11" + "integration": { + "type": "string" }, - "error": { - "type": "object" + "externalId": { + "type": "string" + }, + "credentialId": { + "type": "number", + "nullable": true } }, "required": [ - "status", - "data" + "userId", + "integration", + "externalId", + "credentialId" ] }, - "DeleteScheduleOutput_2024_06_11": { + "SelectedCalendarOutputResponseDto": { "type": "object", "properties": { "status": { @@ -7046,148 +7592,93 @@ "success", "error" ] + }, + "data": { + "$ref": "#/components/schemas/SelectedCalendarOutputDto" } }, "required": [ - "status" + "status", + "data" ] }, - "GetUserOutput": { + "OrgTeamOutputDto": { "type": "object", "properties": { "id": { - "type": "number", - "description": "The ID of the user", - "example": 1 + "type": "number" }, - "username": { - "type": "string", - "nullable": true, - "description": "The username of the user", - "example": "john_doe" + "parentId": { + "type": "number" }, "name": { "type": "string", - "nullable": true, - "description": "The name of the user", - "example": "John Doe" + "minLength": 1 }, - "email": { - "type": "string", - "description": "The email of the user", - "example": "john@example.com" + "slug": { + "type": "string" }, - "emailVerified": { - "format": "date-time", - "type": "string", - "nullable": true, - "description": "The date when the email was verified", - "example": "2022-01-01T00:00:00Z" + "logoUrl": { + "type": "string" }, - "bio": { - "type": "string", - "nullable": true, - "description": "The bio of the user", - "example": "I am a software developer" + "calVideoLogo": { + "type": "string" }, - "avatarUrl": { - "type": "string", - "nullable": true, - "description": "The URL of the user's avatar", - "example": "https://example.com/avatar.jpg" + "appLogo": { + "type": "string" }, - "timeZone": { - "type": "string", - "description": "The time zone of the user", - "example": "America/New_York" + "appIconLogo": { + "type": "string" }, - "weekStart": { - "type": "string", - "description": "The week start day of the user", - "example": "Monday" + "bio": { + "type": "string" }, - "appTheme": { - "type": "string", - "nullable": true, - "description": "The app theme of the user", - "example": "light" + "hideBranding": { + "type": "boolean" }, - "theme": { - "type": "string", - "nullable": true, - "description": "The theme of the user", - "example": "default" + "isOrganization": { + "type": "boolean" }, - "defaultScheduleId": { - "type": "number", - "nullable": true, - "description": "The ID of the default schedule for the user", - "example": 1 + "isPrivate": { + "type": "boolean" }, - "locale": { - "type": "string", - "nullable": true, - "description": "The locale of the user", - "example": "en-US" + "hideBookATeamMember": { + "type": "boolean", + "default": false }, - "timeFormat": { - "type": "number", - "nullable": true, - "description": "The time format of the user", - "example": 12 + "metadata": { + "type": "string" }, - "hideBranding": { - "type": "boolean", - "description": "Whether to hide branding for the user", - "example": false + "theme": { + "type": "string" }, "brandColor": { - "type": "string", - "nullable": true, - "description": "The brand color of the user", - "example": "#ffffff" + "type": "string" }, "darkBrandColor": { - "type": "string", - "nullable": true, - "description": "The dark brand color of the user", - "example": "#000000" + "type": "string" }, - "allowDynamicBooking": { - "type": "boolean", - "nullable": true, - "description": "Whether dynamic booking is allowed for the user", - "example": true + "bannerUrl": { + "type": "string" }, - "createdDate": { - "format": "date-time", - "type": "string", - "description": "The date when the user was created", - "example": "2022-01-01T00:00:00Z" + "timeFormat": { + "type": "number" }, - "verified": { - "type": "boolean", - "nullable": true, - "description": "Whether the user is verified", - "example": true + "timeZone": { + "type": "string", + "default": "Europe/London" }, - "invitedTo": { - "type": "number", - "nullable": true, - "description": "The ID of the user who invited this user", - "example": 1 + "weekStart": { + "type": "string", + "default": "Sunday" } }, "required": [ "id", - "email", - "timeZone", - "weekStart", - "hideBranding", - "createdDate" + "name" ] }, - "GetOrganizationUsersOutput": { + "OrgTeamsOutputResponseDto": { "type": "object", "properties": { "status": { @@ -7201,7 +7692,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/GetUserOutput" + "$ref": "#/components/schemas/OrgTeamOutputDto" } } }, @@ -7210,95 +7701,30 @@ "data" ] }, - "CreateOrganizationUserInput": { + "OrgMeTeamsOutputResponseDto": { "type": "object", "properties": { - "email": { - "type": "string", - "description": "User email address", - "example": "user@example.com" - }, - "username": { + "status": { "type": "string", - "description": "Username", - "example": "user123" + "example": "success", + "enum": [ + "success", + "error" + ] }, - "weekday": { - "type": "string", - "description": "Preferred weekday", - "example": "Monday" - }, - "brandColor": { - "type": "string", - "description": "Brand color in HEX format", - "example": "#FFFFFF" - }, - "darkBrandColor": { - "type": "string", - "description": "Dark brand color in HEX format", - "example": "#000000" - }, - "hideBranding": { - "type": "boolean", - "description": "Hide branding", - "example": false - }, - "timeZone": { - "type": "string", - "description": "Time zone", - "example": "America/New_York" - }, - "theme": { - "type": "string", - "nullable": true, - "description": "Theme", - "example": "dark" - }, - "appTheme": { - "type": "string", - "nullable": true, - "description": "Application theme", - "example": "light" - }, - "timeFormat": { - "type": "number", - "description": "Time format", - "example": 24 - }, - "defaultScheduleId": { - "type": "number", - "minimum": 0, - "description": "Default schedule ID", - "example": 1 - }, - "locale": { - "type": "string", - "nullable": true, - "default": "en", - "description": "Locale", - "example": "en" - }, - "avatarUrl": { - "type": "string", - "description": "Avatar URL", - "example": "https://example.com/avatar.jpg" - }, - "organizationRole": { - "type": "object", - "default": "MEMBER" - }, - "autoAccept": { - "type": "object", - "default": true + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OrgTeamOutputDto" + } } }, "required": [ - "email", - "organizationRole", - "autoAccept" + "status", + "data" ] }, - "GetOrganizationUserOutput": { + "OrgTeamOutputResponseDto": { "type": "object", "properties": { "status": { @@ -7310,7 +7736,7 @@ ] }, "data": { - "$ref": "#/components/schemas/GetUserOutput" + "$ref": "#/components/schemas/OrgTeamOutputDto" } }, "required": [ @@ -7318,177 +7744,276 @@ "data" ] }, - "UpdateOrganizationUserInput": { - "type": "object", - "properties": {} - }, - "OrgMembershipOutputDto": { + "UpdateOrgTeamDto": { "type": "object", "properties": { - "role": { + "name": { "type": "string", - "enum": [ - "MEMBER", - "OWNER", - "ADMIN" - ] + "minLength": 1 }, - "id": { - "type": "number" + "slug": { + "type": "string" }, - "userId": { - "type": "number" + "logoUrl": { + "type": "string" }, - "teamId": { - "type": "number" + "calVideoLogo": { + "type": "string" }, - "accepted": { + "appLogo": { + "type": "string" + }, + "appIconLogo": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "hideBranding": { + "type": "boolean", + "default": false + }, + "isPrivate": { "type": "boolean" }, - "disableImpersonation": { + "hideBookATeamMember": { "type": "boolean" - } - }, - "required": [ - "role", - "id", - "userId", - "teamId", - "accepted" - ] - }, - "GetAllOrgMemberships": { - "type": "object", - "properties": { - "status": { + }, + "metadata": { + "type": "string" + }, + "theme": { + "type": "string" + }, + "brandColor": { + "type": "string" + }, + "darkBrandColor": { + "type": "string" + }, + "bannerUrl": { + "type": "string" + }, + "timeFormat": { + "type": "number" + }, + "timeZone": { "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] + "default": "Europe/London" }, - "data": { - "$ref": "#/components/schemas/OrgMembershipOutputDto" + "weekStart": { + "type": "string", + "default": "Sunday" } - }, - "required": [ - "status", - "data" - ] + } }, - "CreateOrgMembershipDto": { + "CreateOrgTeamDto": { "type": "object", "properties": { - "role": { + "name": { "type": "string", - "default": "MEMBER", - "enum": [ - "MEMBER", - "OWNER", - "ADMIN" - ] + "minLength": 1 }, - "userId": { - "type": "number" + "slug": { + "type": "string" }, - "accepted": { + "logoUrl": { + "type": "string" + }, + "calVideoLogo": { + "type": "string" + }, + "appLogo": { + "type": "string" + }, + "appIconLogo": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "hideBranding": { "type": "boolean", "default": false }, - "disableImpersonation": { + "isPrivate": { + "type": "boolean" + }, + "hideBookATeamMember": { + "type": "boolean" + }, + "metadata": { + "type": "string" + }, + "theme": { + "type": "string" + }, + "brandColor": { + "type": "string" + }, + "darkBrandColor": { + "type": "string" + }, + "bannerUrl": { + "type": "string" + }, + "timeFormat": { + "type": "number" + }, + "timeZone": { + "type": "string", + "default": "Europe/London" + }, + "weekStart": { + "type": "string", + "default": "Sunday" + }, + "autoAcceptCreator": { "type": "boolean", - "default": false + "default": true } }, "required": [ - "role", - "userId" + "name" ] }, - "CreateOrgMembershipOutput": { + "ScheduleAvailabilityInput_2024_06_11": { "type": "object", "properties": { - "status": { + "days": { "type": "string", - "example": "success", "enum": [ - "success", - "error" - ] - }, - "data": { - "$ref": "#/components/schemas/OrgMembershipOutputDto" - } - }, - "required": [ - "status", - "data" - ] - }, - "GetOrgMembership": { - "type": "object", - "properties": { - "status": { + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday" + ], + "example": [ + "Monday", + "Tuesday" + ], + "description": "Array of days when schedule is active." + }, + "startTime": { "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] + "pattern": "TIME_FORMAT_HH_MM", + "example": "08:00", + "description": "startTime must be a valid time in format HH:MM e.g. 08:00" }, - "data": { - "$ref": "#/components/schemas/OrgMembershipOutputDto" + "endTime": { + "type": "string", + "pattern": "TIME_FORMAT_HH_MM", + "example": "15:00", + "description": "endTime must be a valid time in format HH:MM e.g. 15:00" } }, "required": [ - "status", - "data" + "days", + "startTime", + "endTime" ] }, - "DeleteOrgMembership": { + "ScheduleOverrideInput_2024_06_11": { "type": "object", "properties": { - "status": { + "date": { "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] + "example": "2024-05-20" }, - "data": { - "$ref": "#/components/schemas/OrgMembershipOutputDto" + "startTime": { + "type": "string", + "pattern": "TIME_FORMAT_HH_MM", + "example": "12:00", + "description": "startTime must be a valid time in format HH:MM e.g. 12:00" + }, + "endTime": { + "type": "string", + "pattern": "TIME_FORMAT_HH_MM", + "example": "13:00", + "description": "endTime must be a valid time in format HH:MM e.g. 13:00" } }, "required": [ - "status", - "data" + "date", + "startTime", + "endTime" ] }, - "UpdateOrgMembershipDto": { + "ScheduleOutput_2024_06_11": { "type": "object", "properties": { - "role": { + "id": { + "type": "number", + "example": 254 + }, + "ownerId": { + "type": "number", + "example": 478 + }, + "name": { "type": "string", - "default": "MEMBER", - "enum": [ - "MEMBER", - "OWNER", - "ADMIN" - ] + "example": "Catch up hours" }, - "accepted": { - "type": "boolean", - "default": false + "timeZone": { + "type": "string", + "example": "Europe/Rome" }, - "disableImpersonation": { + "availability": { + "example": [ + { + "days": [ + "Monday", + "Tuesday" + ], + "startTime": "17:00", + "endTime": "19:00" + }, + { + "days": [ + "Wednesday", + "Thursday" + ], + "startTime": "16:00", + "endTime": "20:00" + } + ], + "type": "array", + "items": { + "$ref": "#/components/schemas/ScheduleAvailabilityInput_2024_06_11" + } + }, + "isDefault": { "type": "boolean", - "default": false + "example": true + }, + "overrides": { + "example": [ + { + "date": "2024-05-20", + "startTime": "18:00", + "endTime": "21:00" + } + ], + "type": "array", + "items": { + "$ref": "#/components/schemas/ScheduleOverrideInput_2024_06_11" + } } - } + }, + "required": [ + "id", + "ownerId", + "name", + "timeZone", + "availability", + "isDefault", + "overrides" + ] }, - "UpdateOrgMembership": { + "GetSchedulesOutput_2024_06_11": { "type": "object", "properties": { "status": { @@ -7500,7 +8025,13 @@ ] }, "data": { - "$ref": "#/components/schemas/OrgMembershipOutputDto" + "type": "array", + "items": { + "$ref": "#/components/schemas/ScheduleOutput_2024_06_11" + } + }, + "error": { + "type": "object" } }, "required": [ @@ -7508,44 +8039,70 @@ "data" ] }, - "CreateTeamEventTypeInput_2024_06_14": { + "CreateScheduleInput_2024_06_11": { "type": "object", "properties": { - "lengthInMinutes": { - "type": "number", - "example": 60 - }, - "title": { + "name": { "type": "string", - "example": "Learn the secrets of masterchief!" + "example": "Catch up hours" }, - "description": { + "timeZone": { "type": "string", - "example": "Discover the culinary wonders of the Argentina by making the best flan ever!" - }, - "schedulingType": { - "type": "object" + "example": "Europe/Rome", + "description": "Timezone is used to calculate available times when an event using the schedule is booked." }, - "hosts": { + "availability": { + "description": "Each object contains days and times when the user is available. If not passed, the default availability is Monday to Friday from 09:00 to 17:00.", + "example": [ + { + "days": [ + "Monday", + "Tuesday" + ], + "startTime": "17:00", + "endTime": "19:00" + }, + { + "days": [ + "Wednesday", + "Thursday" + ], + "startTime": "16:00", + "endTime": "20:00" + } + ], "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/ScheduleAvailabilityInput_2024_06_11" } }, - "assignAllTeamMembers": { - "type": "boolean" + "isDefault": { + "type": "boolean", + "example": true, + "description": "Each user should have 1 default schedule. If you specified `timeZone` when creating managed user, then the default schedule will be created with that timezone.\n Default schedule means that if an event type is not tied to a specific schedule then the default schedule is used." + }, + "overrides": { + "description": "Need to change availability for a specific date? Add an override.", + "example": [ + { + "date": "2024-05-20", + "startTime": "18:00", + "endTime": "21:00" + } + ], + "type": "array", + "items": { + "$ref": "#/components/schemas/ScheduleOverrideInput_2024_06_11" + } } }, "required": [ - "lengthInMinutes", - "title", - "description", - "schedulingType", - "hosts", - "assignAllTeamMembers" + "name", + "timeZone", + "isDefault" ] }, - "CreateTeamEventTypeOutput": { + "CreateScheduleOutput_2024_06_11": { "type": "object", "properties": { "status": { @@ -7557,17 +8114,7 @@ ] }, "data": { - "oneOf": [ - { - "$ref": "#/components/schemas/TeamEventTypeOutput_2024_06_14" - }, - { - "type": "array", - "items": { - "$ref": "#/components/schemas/TeamEventTypeOutput_2024_06_14" - } - } - ] + "$ref": "#/components/schemas/ScheduleOutput_2024_06_11" } }, "required": [ @@ -7575,278 +8122,425 @@ "data" ] }, - "Recurrence_2024_06_14": { + "GetScheduleOutput_2024_06_11": { "type": "object", "properties": { - "interval": { - "type": "number", - "example": 10, - "description": "Repeats every {count} week | month | year" + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] }, - "occurrences": { - "type": "number", - "example": 10, - "description": "Repeats for a maximum of {count} events" + "data": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/ScheduleOutput_2024_06_11" + } + ] }, - "frequency": { - "enum": [ - "yearly", - "monthly", - "weekly" - ], - "type": "string" + "error": { + "type": "object" } }, "required": [ - "interval", - "occurrences", - "frequency" + "status", + "data" ] }, - "TeamEventTypeResponseHost": { + "UpdateScheduleInput_2024_06_11": { "type": "object", "properties": { "name": { - "type": "string" + "type": "string", + "example": "One-on-one coaching" }, - "userId": { - "type": "number" + "timeZone": { + "type": "string", + "example": "Europe/Rome" }, - "mandatory": { + "availability": { + "example": [ + { + "days": [ + "Monday", + "Tuesday" + ], + "startTime": "09:00", + "endTime": "10:00" + } + ], + "type": "array", + "items": { + "$ref": "#/components/schemas/ScheduleAvailabilityInput_2024_06_11" + } + }, + "isDefault": { "type": "boolean", - "default": false + "example": true }, - "priority": { - "type": "object", - "default": "medium" + "overrides": { + "example": [ + { + "date": "2024-05-20", + "startTime": "12:00", + "endTime": "14:00" + } + ], + "type": "array", + "items": { + "$ref": "#/components/schemas/ScheduleOverrideInput_2024_06_11" + } } - }, - "required": [ - "name", - "userId" - ] + } }, - "BookingLimitsCount_2024_06_14": { + "UpdateScheduleOutput_2024_06_11": { "type": "object", "properties": { - "day": { - "type": "number", - "minimum": 1, - "description": "The number of bookings per day", - "example": 1 - }, - "week": { - "type": "number", - "minimum": 1, - "description": "The number of bookings per week", - "example": 2 + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] }, - "month": { - "type": "number", - "minimum": 1, - "description": "The number of bookings per month", - "example": 3 + "data": { + "$ref": "#/components/schemas/ScheduleOutput_2024_06_11" }, - "year": { - "type": "number", - "minimum": 1, - "description": "The number of bookings per year", - "example": 4 + "error": { + "type": "object" } - } + }, + "required": [ + "status", + "data" + ] }, - "BookingLimitsDuration_2024_06_14": { + "DeleteScheduleOutput_2024_06_11": { "type": "object", "properties": { - "day": { - "type": "number", - "minimum": 15, - "description": "The duration of bookings per day (must be a multiple of 15)", - "example": 60 - }, - "week": { - "type": "number", - "minimum": 15, - "description": "The duration of bookings per week (must be a multiple of 15)", - "example": 120 - }, - "month": { - "type": "number", - "minimum": 15, - "description": "The duration of bookings per month (must be a multiple of 15)", - "example": 180 - }, - "year": { - "type": "number", - "minimum": 15, - "description": "The duration of bookings per year (must be a multiple of 15)", - "example": 240 + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] } - } + }, + "required": [ + "status" + ] }, - "TeamEventTypeOutput_2024_06_14": { + "GetUserOutput": { "type": "object", "properties": { "id": { "type": "number", + "description": "The ID of the user", "example": 1 }, - "lengthInMinutes": { - "type": "number", - "minimum": 1 + "username": { + "type": "string", + "nullable": true, + "description": "The username of the user", + "example": "john_doe" }, - "title": { - "type": "string" + "name": { + "type": "string", + "nullable": true, + "description": "The name of the user", + "example": "John Doe" }, - "slug": { - "type": "string" + "email": { + "type": "string", + "description": "The email of the user", + "example": "john@example.com" }, - "description": { - "type": "string" + "emailVerified": { + "format": "date-time", + "type": "string", + "nullable": true, + "description": "The date when the email was verified", + "example": "2022-01-01T00:00:00Z" }, - "locations": { - "type": "array", - "items": { - "type": "object" - } + "bio": { + "type": "string", + "nullable": true, + "description": "The bio of the user", + "example": "I am a software developer" }, - "bookingFields": { - "type": "array", - "items": { - "type": "object" - } + "avatarUrl": { + "type": "string", + "nullable": true, + "description": "The URL of the user's avatar", + "example": "https://example.com/avatar.jpg" }, - "disableGuests": { - "type": "boolean" + "timeZone": { + "type": "string", + "description": "The time zone of the user", + "example": "America/New_York" }, - "slotInterval": { + "weekStart": { + "type": "string", + "description": "The week start day of the user", + "example": "Monday" + }, + "appTheme": { + "type": "string", + "nullable": true, + "description": "The app theme of the user", + "example": "light" + }, + "theme": { + "type": "string", + "nullable": true, + "description": "The theme of the user", + "example": "default" + }, + "defaultScheduleId": { "type": "number", - "nullable": true + "nullable": true, + "description": "The ID of the default schedule for the user", + "example": 1 }, - "minimumBookingNotice": { + "locale": { + "type": "string", + "nullable": true, + "description": "The locale of the user", + "example": "en-US" + }, + "timeFormat": { "type": "number", - "minimum": 0 + "nullable": true, + "description": "The time format of the user", + "example": 12 }, - "beforeEventBuffer": { - "type": "number" + "hideBranding": { + "type": "boolean", + "description": "Whether to hide branding for the user", + "example": false }, - "afterEventBuffer": { - "type": "number" + "brandColor": { + "type": "string", + "nullable": true, + "description": "The brand color of the user", + "example": "#ffffff" }, - "schedulingType": { - "type": "object", - "nullable": true + "darkBrandColor": { + "type": "string", + "nullable": true, + "description": "The dark brand color of the user", + "example": "#000000" }, - "recurrence": { + "allowDynamicBooking": { + "type": "boolean", "nullable": true, - "allOf": [ - { - "$ref": "#/components/schemas/Recurrence_2024_06_14" - } - ] + "description": "Whether dynamic booking is allowed for the user", + "example": true }, - "metadata": { - "type": "object" + "createdDate": { + "format": "date-time", + "type": "string", + "description": "The date when the user was created", + "example": "2022-01-01T00:00:00Z" }, - "requiresConfirmation": { - "type": "boolean" + "verified": { + "type": "boolean", + "nullable": true, + "description": "Whether the user is verified", + "example": true }, - "price": { - "type": "number" + "invitedTo": { + "type": "number", + "nullable": true, + "description": "The ID of the user who invited this user", + "example": 1 + } + }, + "required": [ + "id", + "email", + "timeZone", + "weekStart", + "hideBranding", + "createdDate" + ] + }, + "GetOrganizationUsersOutput": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] }, - "currency": { - "type": "string" + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GetUserOutput" + } + } + }, + "required": [ + "status", + "data" + ] + }, + "CreateOrganizationUserInput": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "User email address", + "example": "user@example.com" }, - "lockTimeZoneToggleOnBookingPage": { - "type": "boolean" + "username": { + "type": "string", + "description": "Username", + "example": "user123" }, - "seatsPerTimeSlot": { - "type": "number", - "nullable": true + "weekday": { + "type": "string", + "description": "Preferred weekday", + "example": "Monday" }, - "forwardParamsSuccessRedirect": { - "type": "boolean", - "nullable": true + "brandColor": { + "type": "string", + "description": "Brand color in HEX format", + "example": "#FFFFFF" }, - "successRedirectUrl": { + "darkBrandColor": { "type": "string", - "nullable": true + "description": "Dark brand color in HEX format", + "example": "#000000" }, - "seatsShowAvailabilityCount": { + "hideBranding": { "type": "boolean", - "nullable": true + "description": "Hide branding", + "example": false }, - "isInstantEvent": { - "type": "boolean" + "timeZone": { + "type": "string", + "description": "Time zone", + "example": "America/New_York" }, - "scheduleId": { - "type": "number", - "nullable": true + "theme": { + "type": "string", + "nullable": true, + "description": "Theme", + "example": "dark" }, - "teamId": { - "type": "number", - "nullable": true + "appTheme": { + "type": "string", + "nullable": true, + "description": "Application theme", + "example": "light" }, - "ownerId": { + "timeFormat": { "type": "number", - "nullable": true + "description": "Time format", + "example": 24 }, - "parentEventTypeId": { + "defaultScheduleId": { "type": "number", - "nullable": true + "minimum": 0, + "description": "Default schedule ID", + "example": 1 }, - "hosts": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TeamEventTypeResponseHost" - } + "locale": { + "type": "string", + "nullable": true, + "default": "en", + "description": "Locale", + "example": "en" }, - "assignAllTeamMembers": { - "type": "boolean" + "avatarUrl": { + "type": "string", + "description": "Avatar URL", + "example": "https://example.com/avatar.jpg" + }, + "organizationRole": { + "type": "object", + "default": "MEMBER" + }, + "autoAccept": { + "type": "object", + "default": true + } + }, + "required": [ + "email", + "organizationRole", + "autoAccept" + ] + }, + "GetOrganizationUserOutput": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] + }, + "data": { + "$ref": "#/components/schemas/GetUserOutput" + } + }, + "required": [ + "status", + "data" + ] + }, + "UpdateOrganizationUserInput": { + "type": "object", + "properties": {} + }, + "OrgMembershipOutputDto": { + "type": "object", + "properties": { + "role": { + "type": "string", + "enum": [ + "MEMBER", + "OWNER", + "ADMIN" + ] }, - "bookingLimitsCount": { - "$ref": "#/components/schemas/BookingLimitsCount_2024_06_14" + "id": { + "type": "number" }, - "onlyShowFirstAvailableSlot": { - "type": "boolean" + "userId": { + "type": "number" }, - "bookingLimitsDuration": { - "$ref": "#/components/schemas/BookingLimitsDuration_2024_06_14" + "teamId": { + "type": "number" }, - "bookingWindow": { - "type": "object" + "accepted": { + "type": "boolean" }, - "offsetStart": { - "type": "number", - "minimum": 1 + "disableImpersonation": { + "type": "boolean" } }, "required": [ + "role", "id", - "lengthInMinutes", - "title", - "slug", - "description", - "locations", - "bookingFields", - "disableGuests", - "schedulingType", - "recurrence", - "metadata", - "requiresConfirmation", - "price", - "currency", - "lockTimeZoneToggleOnBookingPage", - "seatsPerTimeSlot", - "forwardParamsSuccessRedirect", - "successRedirectUrl", - "seatsShowAvailabilityCount", - "isInstantEvent", - "scheduleId", - "hosts" + "userId", + "teamId", + "accepted" ] }, - "GetTeamEventTypeOutput": { + "GetAllOrgMemberships": { "type": "object", "properties": { "status": { @@ -7858,7 +8552,7 @@ ] }, "data": { - "$ref": "#/components/schemas/TeamEventTypeOutput_2024_06_14" + "$ref": "#/components/schemas/OrgMembershipOutputDto" } }, "required": [ @@ -7866,71 +8560,76 @@ "data" ] }, - "CreatePhoneCallInput": { + "CreateOrgMembershipDto": { "type": "object", "properties": { - "yourPhoneNumber": { - "type": "string", - "pattern": "/^\\+[1-9]\\d{1,14}$/", - "description": "Your phone number" - }, - "numberToCall": { - "type": "string", - "pattern": "/^\\+[1-9]\\d{1,14}$/", - "description": "Number to call" - }, - "calApiKey": { + "role": { "type": "string", - "description": "CAL API Key" - }, - "enabled": { - "type": "object", - "default": true, - "description": "Enabled status" - }, - "templateType": { - "default": "CUSTOM_TEMPLATE", + "default": "MEMBER", "enum": [ - "CHECK_IN_APPOINTMENT", - "CUSTOM_TEMPLATE" - ], - "type": "string", - "description": "Template type" - }, - "schedulerName": { - "type": "string", - "description": "Scheduler name" + "MEMBER", + "OWNER", + "ADMIN" + ] }, - "guestName": { - "type": "string", - "description": "Guest name" + "userId": { + "type": "number" }, - "guestEmail": { - "type": "string", - "description": "Guest email" + "accepted": { + "type": "boolean", + "default": false }, - "guestCompany": { + "disableImpersonation": { + "type": "boolean", + "default": false + } + }, + "required": [ + "role", + "userId" + ] + }, + "CreateOrgMembershipOutput": { + "type": "object", + "properties": { + "status": { "type": "string", - "description": "Guest company" + "example": "success", + "enum": [ + "success", + "error" + ] }, - "beginMessage": { + "data": { + "$ref": "#/components/schemas/OrgMembershipOutputDto" + } + }, + "required": [ + "status", + "data" + ] + }, + "GetOrgMembership": { + "type": "object", + "properties": { + "status": { "type": "string", - "description": "Begin message" + "example": "success", + "enum": [ + "success", + "error" + ] }, - "generalPrompt": { - "type": "string", - "description": "General prompt" + "data": { + "$ref": "#/components/schemas/OrgMembershipOutputDto" } }, "required": [ - "yourPhoneNumber", - "numberToCall", - "calApiKey", - "enabled", - "templateType" + "status", + "data" ] }, - "CreatePhoneCallOutput": { + "DeleteOrgMembership": { "type": "object", "properties": { "status": { @@ -7942,7 +8641,7 @@ ] }, "data": { - "$ref": "#/components/schemas/Data" + "$ref": "#/components/schemas/OrgMembershipOutputDto" } }, "required": [ @@ -7950,7 +8649,29 @@ "data" ] }, - "GetTeamEventTypesOutput": { + "UpdateOrgMembershipDto": { + "type": "object", + "properties": { + "role": { + "type": "string", + "default": "MEMBER", + "enum": [ + "MEMBER", + "OWNER", + "ADMIN" + ] + }, + "accepted": { + "type": "boolean", + "default": false + }, + "disableImpersonation": { + "type": "boolean", + "default": false + } + } + }, + "UpdateOrgMembership": { "type": "object", "properties": { "status": { @@ -7962,10 +8683,7 @@ ] }, "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TeamEventTypeOutput_2024_06_14" - } + "$ref": "#/components/schemas/OrgMembershipOutputDto" } }, "required": [ @@ -7973,47 +8691,161 @@ "data" ] }, - "UpdateTeamEventTypeInput_2024_06_14": { + "CreateTeamEventTypeInput_2024_06_14": { "type": "object", "properties": { "lengthInMinutes": { - "type": "number" + "type": "number", + "example": 60 }, "title": { - "type": "string" + "type": "string", + "example": "Learn the secrets of masterchief!" }, "slug": { - "type": "string" + "type": "string", + "example": "learn-the-secrets-of-masterchief" }, "description": { - "type": "string" + "type": "string", + "example": "Discover the culinary wonders of the Argentina by making the best flan ever!" }, "locations": { "type": "array", + "description": "Locations where the event will take place. If not provided, cal video link will be used as the location.", "items": { - "type": "string" + "oneOf": [ + { + "$ref": "#/components/schemas/AddressLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/LinkLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/IntegrationLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/PhoneLocation_2024_06_14" + } + ] } }, "bookingFields": { "type": "array", + "description": "Custom fields that can be added to the booking form when the event is booked by someone. By default booking form has name and email field.", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/PhoneFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/AddressFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/TextFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/NumberFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/TextAreaFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/SelectFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/MultiSelectFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/MultiEmailFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/CheckboxGroupFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/RadioGroupFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/BooleanFieldInput_2024_06_14" + } + ] + } + }, + "disableGuests": { + "type": "boolean", + "description": "If true, person booking this event't cant add guests via their emails." + }, + "slotInterval": { + "type": "number", + "description": "Number representing length of each slot when event is booked. By default it equal length of the event type.\n If event length is 60 minutes then we would have slots 9AM, 10AM, 11AM etc. but if it was changed to 30 minutes then\n we would have slots 9AM, 9:30AM, 10AM, 10:30AM etc. as the available times to book the 60 minute event." + }, + "minimumBookingNotice": { + "type": "number", + "description": "Minimum number of minutes before the event that a booking can be made." + }, + "beforeEventBuffer": { + "type": "number", + "description": "Time spaces that can be pre-pended before an event to give more time before it." + }, + "afterEventBuffer": { + "type": "number", + "description": "Time spaces that can be appended after an event to give more time after it." + }, + "scheduleId": { + "type": "number", + "description": "If you want that this event has different schedule than user's default one you can specify it here." + }, + "bookingLimitsCount": { + "description": "Limit how many times this event can be booked", + "allOf": [ + { + "$ref": "#/components/schemas/BookingLimitsCount_2024_06_14" + } + ] + }, + "onlyShowFirstAvailableSlot": { + "type": "boolean", + "description": "This will limit your availability for this event type to one slot per day, scheduled at the earliest available time." + }, + "bookingLimitsDuration": { + "description": "Limit total amount of time that this event can be booked", + "allOf": [ + { + "$ref": "#/components/schemas/BookingLimitsDuration_2024_06_14" + } + ] + }, + "bookingWindow": { + "type": "array", + "description": "Limit how far in the future this event can be booked", "items": { - "type": "string" + "oneOf": [ + { + "$ref": "#/components/schemas/BusinessDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/CalendarDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/RangeWindow_2024_06_14" + } + ] } }, - "disableGuests": { - "type": "boolean" - }, - "slotInterval": { - "type": "number" - }, - "minimumBookingNotice": { - "type": "number" + "offsetStart": { + "type": "number", + "description": "Offset timeslots shown to bookers by a specified number of minutes" }, - "beforeEventBuffer": { - "type": "number" + "recurrence": { + "description": "Create a recurring event that can be booked once but will occur multiple times", + "allOf": [ + { + "$ref": "#/components/schemas/Recurrence_2024_06_14" + } + ] }, - "afterEventBuffer": { - "type": "number" + "schedulingType": { + "type": "object" }, "hosts": { "type": "array", @@ -8022,7 +8854,8 @@ } }, "assignAllTeamMembers": { - "type": "boolean" + "type": "boolean", + "description": "If true, all current and future team members will be assigned to this event type" } }, "required": [ @@ -8037,11 +8870,19 @@ "minimumBookingNotice", "beforeEventBuffer", "afterEventBuffer", + "scheduleId", + "bookingLimitsCount", + "onlyShowFirstAvailableSlot", + "bookingLimitsDuration", + "bookingWindow", + "offsetStart", + "recurrence", + "schedulingType", "hosts", "assignAllTeamMembers" ] }, - "UpdateTeamEventTypeOutput": { + "CreateTeamEventTypeOutput": { "type": "object", "properties": { "status": { @@ -8071,85 +8912,294 @@ "data" ] }, - "DeleteTeamEventTypeOutput": { + "TeamEventTypeResponseHost": { "type": "object", "properties": { - "status": { + "userId": { + "type": "number", + "description": "Which user is the host of this event" + }, + "mandatory": { + "type": "boolean", + "default": false, + "description": "Only relevant for round robin event types. If true then the user must attend round robin event always." + }, + "priority": { "type": "string", - "example": "success", + "default": "medium", "enum": [ - "success", - "error" + "lowest", + "low", + "medium", + "high", + "highest" ] }, - "data": { - "type": "object" + "name": { + "type": "string", + "example": "John Doe" } }, "required": [ - "status", - "data" + "userId", + "name" ] }, - "OrgTeamMembershipOutputDto": { + "TeamEventTypeOutput_2024_06_14": { "type": "object", "properties": { - "role": { + "id": { + "type": "number", + "example": 1 + }, + "lengthInMinutes": { + "type": "number", + "minimum": 1, + "example": 60 + }, + "title": { "type": "string", - "enum": [ - "MEMBER", - "OWNER", - "ADMIN" + "example": "Learn the secrets of masterchief!" + }, + "slug": { + "type": "string", + "example": "learn-the-secrets-of-masterchief" + }, + "description": { + "type": "string", + "example": "Discover the culinary wonders of the Argentina by making the best flan ever!" + }, + "locations": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/AddressLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/LinkLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/IntegrationLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/PhoneLocation_2024_06_14" + } + ] + } + }, + "bookingFields": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/NameDefaultFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/EmailDefaultFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/LocationDefaultFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/RescheduleReasonDefaultFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/TitleDefaultFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/NotesDefaultFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/GuestsDefaultFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/PhoneFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/AddressFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/TextFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/NumberFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/TextAreaFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/SelectFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/MultiSelectFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/MultiEmailFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/CheckboxGroupFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/RadioGroupFieldOutput_2024_06_14" + }, + { + "$ref": "#/components/schemas/BooleanFieldOutput_2024_06_14" + } + ] + } + }, + "disableGuests": { + "type": "boolean" + }, + "slotInterval": { + "type": "number", + "nullable": true, + "example": 60 + }, + "minimumBookingNotice": { + "type": "number", + "minimum": 0, + "example": 0 + }, + "beforeEventBuffer": { + "type": "number", + "example": 0 + }, + "afterEventBuffer": { + "type": "number", + "example": 0 + }, + "recurrence": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/Recurrence_2024_06_14" + } ] }, - "id": { - "type": "number" + "metadata": { + "type": "object" }, - "userId": { - "type": "number" + "requiresConfirmation": { + "type": "boolean" }, - "teamId": { + "price": { "type": "number" }, - "accepted": { + "currency": { + "type": "string" + }, + "lockTimeZoneToggleOnBookingPage": { "type": "boolean" }, - "disableImpersonation": { + "seatsPerTimeSlot": { + "type": "number", + "nullable": true + }, + "forwardParamsSuccessRedirect": { + "type": "boolean", + "nullable": true + }, + "successRedirectUrl": { + "type": "string", + "nullable": true + }, + "seatsShowAvailabilityCount": { + "type": "boolean", + "nullable": true + }, + "scheduleId": { + "type": "number", + "nullable": true + }, + "bookingLimitsCount": { + "$ref": "#/components/schemas/BookingLimitsCount_2024_06_14" + }, + "onlyShowFirstAvailableSlot": { "type": "boolean" - } - }, - "required": [ - "role", - "id", - "userId", - "teamId", - "accepted" - ] - }, - "OrgTeamMembershipsOutputResponseDto": { - "type": "object", - "properties": { - "status": { + }, + "bookingLimitsDuration": { + "$ref": "#/components/schemas/BookingLimitsDuration_2024_06_14" + }, + "bookingWindow": { + "type": "array", + "description": "Limit how far in the future this event can be booked", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/BusinessDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/CalendarDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/RangeWindow_2024_06_14" + } + ] + } + }, + "offsetStart": { + "type": "number", + "minimum": 1 + }, + "schedulingType": { "type": "string", - "example": "success", + "nullable": true, "enum": [ - "success", - "error" + "ROUND_ROBIN", + "COLLECTIVE", + "MANAGED" ] }, - "data": { + "teamId": { + "type": "number", + "nullable": true + }, + "ownerId": { + "type": "number", + "nullable": true + }, + "parentEventTypeId": { + "type": "number", + "nullable": true, + "description": "For managed event types parent event type is the event type that this event type is based on" + }, + "hosts": { "type": "array", "items": { - "$ref": "#/components/schemas/OrgTeamMembershipOutputDto" + "$ref": "#/components/schemas/TeamEventTypeResponseHost" } + }, + "assignAllTeamMembers": { + "type": "boolean" } }, "required": [ - "status", - "data" + "id", + "lengthInMinutes", + "title", + "slug", + "description", + "locations", + "bookingFields", + "disableGuests", + "recurrence", + "metadata", + "requiresConfirmation", + "price", + "currency", + "lockTimeZoneToggleOnBookingPage", + "seatsPerTimeSlot", + "forwardParamsSuccessRedirect", + "successRedirectUrl", + "seatsShowAvailabilityCount", + "scheduleId", + "schedulingType", + "hosts" ] }, - "OrgTeamMembershipOutputResponseDto": { + "GetTeamEventTypeOutput": { "type": "object", "properties": { "status": { @@ -8161,7 +9211,7 @@ ] }, "data": { - "$ref": "#/components/schemas/OrgTeamMembershipOutputDto" + "$ref": "#/components/schemas/TeamEventTypeOutput_2024_06_14" } }, "required": [ @@ -8169,111 +9219,86 @@ "data" ] }, - "UpdateOrgTeamMembershipDto": { + "CreatePhoneCallInput": { "type": "object", "properties": { - "role": { + "yourPhoneNumber": { "type": "string", - "default": "MEMBER", - "enum": [ - "MEMBER", - "OWNER", - "ADMIN" - ] + "pattern": "/^\\+[1-9]\\d{1,14}$/", + "description": "Your phone number" }, - "accepted": { - "type": "boolean", - "default": false + "numberToCall": { + "type": "string", + "pattern": "/^\\+[1-9]\\d{1,14}$/", + "description": "Number to call" }, - "disableImpersonation": { - "type": "boolean", - "default": false - } - } - }, - "CreateOrgTeamMembershipDto": { - "type": "object", - "properties": { - "role": { + "calApiKey": { "type": "string", - "default": "MEMBER", - "enum": [ - "MEMBER", - "OWNER", - "ADMIN" - ] + "description": "CAL API Key" }, - "userId": { - "type": "number" + "enabled": { + "type": "object", + "default": true, + "description": "Enabled status" }, - "accepted": { - "type": "boolean", - "default": false + "templateType": { + "default": "CUSTOM_TEMPLATE", + "enum": [ + "CHECK_IN_APPOINTMENT", + "CUSTOM_TEMPLATE" + ], + "type": "string", + "description": "Template type" }, - "disableImpersonation": { - "type": "boolean", - "default": false - } - }, - "required": [ - "role", - "userId" - ] - }, - "Attribute": { - "type": "object", - "properties": { - "id": { + "schedulerName": { "type": "string", - "description": "The ID of the attribute", - "example": "attr_123" + "description": "Scheduler name" }, - "teamId": { - "type": "number", - "description": "The team ID associated with the attribute", - "example": 1 + "guestName": { + "type": "string", + "description": "Guest name" }, - "type": { + "guestEmail": { "type": "string", - "description": "The type of the attribute", - "enum": [ - "TEXT", - "NUMBER", - "SINGLE_SELECT", - "MULTI_SELECT" - ] + "description": "Guest email" }, - "name": { + "guestCompany": { "type": "string", - "description": "The name of the attribute", - "example": "Attribute Name" + "description": "Guest company" }, - "slug": { + "beginMessage": { "type": "string", - "description": "The slug of the attribute", - "example": "attribute-name" + "description": "Begin message" }, - "enabled": { - "type": "boolean", - "description": "Whether the attribute is enabled and displayed on their profile", - "example": true + "generalPrompt": { + "type": "string", + "description": "General prompt" + } + }, + "required": [ + "yourPhoneNumber", + "numberToCall", + "calApiKey", + "enabled", + "templateType" + ] + }, + "Data": { + "type": "object", + "properties": { + "callId": { + "type": "string" }, - "usersCanEditRelation": { - "type": "boolean", - "description": "Whether users can edit the relation", - "example": true + "agentId": { + "type": "string" } }, "required": [ - "id", - "teamId", - "type", - "name", - "slug", - "enabled" + "callId", + "agentId" ] }, - "GetOrganizationAttributesOutput": { + "CreatePhoneCallOutput": { "type": "object", "properties": { "status": { @@ -8285,10 +9310,7 @@ ] }, "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Attribute" - } + "$ref": "#/components/schemas/Data" } }, "required": [ @@ -8296,7 +9318,7 @@ "data" ] }, - "GetSingleAttributeOutput": { + "GetTeamEventTypesOutput": { "type": "object", "properties": { "status": { @@ -8308,12 +9330,10 @@ ] }, "data": { - "nullable": true, - "allOf": [ - { - "$ref": "#/components/schemas/Attribute" - } - ] + "type": "array", + "items": { + "$ref": "#/components/schemas/TeamEventTypeOutput_2024_06_14" + } } }, "required": [ @@ -8321,51 +9341,194 @@ "data" ] }, - "CreateOrganizationAttributeOptionInput": { + "UpdateTeamEventTypeInput_2024_06_14": { "type": "object", "properties": { - "value": { - "type": "string" + "lengthInMinutes": { + "type": "number", + "example": 60 }, - "slug": { - "type": "string" - } - }, - "required": [ - "value", - "slug" - ] - }, - "CreateOrganizationAttributeInput": { - "type": "object", - "properties": { - "name": { - "type": "string" + "title": { + "type": "string", + "example": "Learn the secrets of masterchief!" }, "slug": { - "type": "string" + "type": "string", + "example": "learn-the-secrets-of-masterchief" + }, + "description": { + "type": "string", + "example": "Discover the culinary wonders of the Argentina by making the best flan ever!" + }, + "locations": { + "type": "array", + "description": "Locations where the event will take place. If not provided, cal video link will be used as the location.", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/AddressLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/LinkLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/IntegrationLocation_2024_06_14" + }, + { + "$ref": "#/components/schemas/PhoneLocation_2024_06_14" + } + ] + } + }, + "bookingFields": { + "type": "array", + "description": "Custom fields that can be added to the booking form when the event is booked by someone. By default booking form has name and email field.", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/PhoneFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/AddressFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/TextFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/NumberFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/TextAreaFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/SelectFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/MultiSelectFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/MultiEmailFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/CheckboxGroupFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/RadioGroupFieldInput_2024_06_14" + }, + { + "$ref": "#/components/schemas/BooleanFieldInput_2024_06_14" + } + ] + } + }, + "disableGuests": { + "type": "boolean", + "description": "If true, person booking this event't cant add guests via their emails." + }, + "slotInterval": { + "type": "number", + "description": "Number representing length of each slot when event is booked. By default it equal length of the event type.\n If event length is 60 minutes then we would have slots 9AM, 10AM, 11AM etc. but if it was changed to 30 minutes then\n we would have slots 9AM, 9:30AM, 10AM, 10:30AM etc. as the available times to book the 60 minute event." + }, + "minimumBookingNotice": { + "type": "number", + "description": "Minimum number of minutes before the event that a booking can be made." + }, + "beforeEventBuffer": { + "type": "number", + "description": "Time spaces that can be pre-pended before an event to give more time before it." + }, + "afterEventBuffer": { + "type": "number", + "description": "Time spaces that can be appended after an event to give more time after it." + }, + "scheduleId": { + "type": "number", + "description": "If you want that this event has different schedule than user's default one you can specify it here." + }, + "bookingLimitsCount": { + "description": "Limit how many times this event can be booked", + "allOf": [ + { + "$ref": "#/components/schemas/BookingLimitsCount_2024_06_14" + } + ] + }, + "onlyShowFirstAvailableSlot": { + "type": "boolean", + "description": "This will limit your availability for this event type to one slot per day, scheduled at the earliest available time." + }, + "bookingLimitsDuration": { + "description": "Limit total amount of time that this event can be booked", + "allOf": [ + { + "$ref": "#/components/schemas/BookingLimitsDuration_2024_06_14" + } + ] + }, + "bookingWindow": { + "type": "array", + "description": "Limit how far in the future this event can be booked", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/BusinessDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/CalendarDaysWindow_2024_06_14" + }, + { + "$ref": "#/components/schemas/RangeWindow_2024_06_14" + } + ] + } }, - "type": { - "type": "object" + "offsetStart": { + "type": "number", + "description": "Offset timeslots shown to bookers by a specified number of minutes" }, - "options": { + "recurrence": { + "description": "Create a recurring event that can be booked once but will occur multiple times", + "allOf": [ + { + "$ref": "#/components/schemas/Recurrence_2024_06_14" + } + ] + }, + "hosts": { "type": "array", "items": { - "$ref": "#/components/schemas/CreateOrganizationAttributeOptionInput" + "type": "string" } }, - "enabled": { - "type": "boolean" + "assignAllTeamMembers": { + "type": "boolean", + "description": "If true, all current and future team members will be assigned to this event type" } }, "required": [ - "name", + "lengthInMinutes", + "title", "slug", - "type", - "options" + "description", + "locations", + "bookingFields", + "disableGuests", + "slotInterval", + "minimumBookingNotice", + "beforeEventBuffer", + "afterEventBuffer", + "scheduleId", + "bookingLimitsCount", + "onlyShowFirstAvailableSlot", + "bookingLimitsDuration", + "bookingWindow", + "offsetStart", + "recurrence", + "hosts", + "assignAllTeamMembers" ] }, - "CreateOrganizationAttributesOutput": { + "UpdateTeamEventTypeOutput": { "type": "object", "properties": { "status": { @@ -8377,7 +9540,17 @@ ] }, "data": { - "$ref": "#/components/schemas/Attribute" + "oneOf": [ + { + "$ref": "#/components/schemas/TeamEventTypeOutput_2024_06_14" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/TeamEventTypeOutput_2024_06_14" + } + } + ] } }, "required": [ @@ -8385,24 +9558,7 @@ "data" ] }, - "UpdateOrganizationAttributeInput": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "slug": { - "type": "string" - }, - "type": { - "type": "object" - }, - "enabled": { - "type": "boolean" - } - } - }, - "UpdateOrganizationAttributesOutput": { + "DeleteTeamEventTypeOutput": { "type": "object", "properties": { "status": { @@ -8414,7 +9570,7 @@ ] }, "data": { - "$ref": "#/components/schemas/Attribute" + "type": "object" } }, "required": [ @@ -8422,58 +9578,42 @@ "data" ] }, - "DeleteOrganizationAttributesOutput": { + "OrgTeamMembershipOutputDto": { "type": "object", "properties": { - "status": { + "role": { "type": "string", - "example": "success", "enum": [ - "success", - "error" + "MEMBER", + "OWNER", + "ADMIN" ] }, - "data": { - "$ref": "#/components/schemas/Attribute" - } - }, - "required": [ - "status", - "data" - ] - }, - "OptionOutput": { - "type": "object", - "properties": { "id": { - "type": "string", - "description": "The ID of the option", - "example": "attr_option_id" + "type": "number" }, - "attributeId": { - "type": "string", - "description": "The ID of the attribute", - "example": "attr_id" + "userId": { + "type": "number" }, - "value": { - "type": "string", - "description": "The value of the option", - "example": "option_value" + "teamId": { + "type": "number" }, - "slug": { - "type": "string", - "description": "The slug of the option", - "example": "option-slug" + "accepted": { + "type": "boolean" + }, + "disableImpersonation": { + "type": "boolean" } }, "required": [ + "role", "id", - "attributeId", - "value", - "slug" + "userId", + "teamId", + "accepted" ] }, - "CreateAttributeOptionOutput": { + "OrgTeamMembershipsOutputResponseDto": { "type": "object", "properties": { "status": { @@ -8485,7 +9625,10 @@ ] }, "data": { - "$ref": "#/components/schemas/OptionOutput" + "type": "array", + "items": { + "$ref": "#/components/schemas/OrgTeamMembershipOutputDto" + } } }, "required": [ @@ -8493,7 +9636,7 @@ "data" ] }, - "DeleteAttributeOptionOutput": { + "OrgTeamMembershipOutputResponseDto": { "type": "object", "properties": { "status": { @@ -8505,7 +9648,7 @@ ] }, "data": { - "$ref": "#/components/schemas/OptionOutput" + "$ref": "#/components/schemas/OrgTeamMembershipOutputDto" } }, "required": [ @@ -8513,100 +9656,111 @@ "data" ] }, - "UpdateOrganizationAttributeOptionInput": { - "type": "object", - "properties": { - "value": { - "type": "string" - }, - "slug": { - "type": "string" - } - } - }, - "UpdateAttributeOptionOutput": { + "UpdateOrgTeamMembershipDto": { "type": "object", "properties": { - "status": { + "role": { "type": "string", - "example": "success", + "default": "MEMBER", "enum": [ - "success", - "error" + "MEMBER", + "OWNER", + "ADMIN" ] }, - "data": { - "$ref": "#/components/schemas/OptionOutput" + "accepted": { + "type": "boolean", + "default": false + }, + "disableImpersonation": { + "type": "boolean", + "default": false } - }, - "required": [ - "status", - "data" - ] + } }, - "GetAllAttributeOptionOutput": { + "CreateOrgTeamMembershipDto": { "type": "object", "properties": { - "status": { + "role": { "type": "string", - "example": "success", + "default": "MEMBER", "enum": [ - "success", - "error" + "MEMBER", + "OWNER", + "ADMIN" ] }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/OptionOutput" - } - } - }, - "required": [ - "status", - "data" - ] - }, - "AssignOrganizationAttributeOptionToUserInput": { - "type": "object", - "properties": { - "value": { - "type": "string" + "userId": { + "type": "number" }, - "attributeOptionId": { - "type": "string" + "accepted": { + "type": "boolean", + "default": false }, - "attributeId": { - "type": "string" + "disableImpersonation": { + "type": "boolean", + "default": false } }, "required": [ - "attributeId" + "role", + "userId" ] }, - "AssignOptionUserOutputData": { + "Attribute": { "type": "object", "properties": { "id": { "type": "string", - "description": "The ID of the option assigned to the user" + "description": "The ID of the attribute", + "example": "attr_123" }, - "memberId": { + "teamId": { "type": "number", - "description": "The ID form the org membership for the user" + "description": "The team ID associated with the attribute", + "example": 1 }, - "attributeOptionId": { + "type": { "type": "string", - "description": "The value of the option" + "description": "The type of the attribute", + "enum": [ + "TEXT", + "NUMBER", + "SINGLE_SELECT", + "MULTI_SELECT" + ] + }, + "name": { + "type": "string", + "description": "The name of the attribute", + "example": "Attribute Name" + }, + "slug": { + "type": "string", + "description": "The slug of the attribute", + "example": "attribute-name" + }, + "enabled": { + "type": "boolean", + "description": "Whether the attribute is enabled and displayed on their profile", + "example": true + }, + "usersCanEditRelation": { + "type": "boolean", + "description": "Whether users can edit the relation", + "example": true } }, "required": [ "id", - "memberId", - "attributeOptionId" + "teamId", + "type", + "name", + "slug", + "enabled" ] }, - "AssignOptionUserOutput": { + "GetOrganizationAttributesOutput": { "type": "object", "properties": { "status": { @@ -8618,7 +9772,10 @@ ] }, "data": { - "$ref": "#/components/schemas/AssignOptionUserOutputData" + "type": "array", + "items": { + "$ref": "#/components/schemas/Attribute" + } } }, "required": [ @@ -8626,7 +9783,7 @@ "data" ] }, - "UnassignOptionUserOutput": { + "GetSingleAttributeOutput": { "type": "object", "properties": { "status": { @@ -8638,7 +9795,12 @@ ] }, "data": { - "$ref": "#/components/schemas/AssignOptionUserOutputData" + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/Attribute" + } + ] } }, "required": [ @@ -8646,96 +9808,51 @@ "data" ] }, - "GetOptionUserOutputData": { + "CreateOrganizationAttributeOptionInput": { "type": "object", "properties": { - "id": { - "type": "string", - "description": "The ID of the option assigned to the user" - }, - "attributeId": { - "type": "string", - "description": "The ID of the attribute" - }, "value": { - "type": "string", - "description": "The value of the option" + "type": "string" }, "slug": { - "type": "string", - "description": "The slug of the option" + "type": "string" } }, "required": [ - "id", - "attributeId", "value", "slug" ] }, - "GetOptionUserOutput": { - "type": "object", - "properties": { - "status": { - "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GetOptionUserOutputData" - } - } - }, - "required": [ - "status", - "data" - ] - }, - "TeamWebhookOutputDto": { + "CreateOrganizationAttributeInput": { "type": "object", "properties": { - "payloadTemplate": { - "type": "string", - "description": "The template of the payload that will be sent to the subscriberUrl, check cal.com/docs/core-features/webhooks for more information", - "example": "{\"content\":\"A new event has been scheduled\",\"type\":\"{{type}}\",\"name\":\"{{title}}\",\"organizer\":\"{{organizer.name}}\",\"booker\":\"{{attendees.0.name}}\"}" + "name": { + "type": "string" }, - "teamId": { - "type": "number" + "slug": { + "type": "string" }, - "id": { - "type": "number" + "type": { + "type": "object" }, - "triggers": { + "options": { "type": "array", "items": { - "type": "object" + "$ref": "#/components/schemas/CreateOrganizationAttributeOptionInput" } }, - "subscriberUrl": { - "type": "string" - }, - "active": { + "enabled": { "type": "boolean" - }, - "secret": { - "type": "string" } }, "required": [ - "payloadTemplate", - "teamId", - "id", - "triggers", - "subscriberUrl", - "active" + "name", + "slug", + "type", + "options" ] }, - "TeamWebhooksOutputResponseDto": { + "CreateOrganizationAttributesOutput": { "type": "object", "properties": { "status": { @@ -8747,10 +9864,7 @@ ] }, "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TeamWebhookOutputDto" - } + "$ref": "#/components/schemas/Attribute" } }, "required": [ @@ -8758,130 +9872,24 @@ "data" ] }, - "CreateWebhookInputDto": { + "UpdateOrganizationAttributeInput": { "type": "object", "properties": { - "payloadTemplate": { - "type": "string", - "description": "The template of the payload that will be sent to the subscriberUrl, check cal.com/docs/core-features/webhooks for more information", - "example": "{\"content\":\"A new event has been scheduled\",\"type\":\"{{type}}\",\"name\":\"{{title}}\",\"organizer\":\"{{organizer.name}}\",\"booker\":\"{{attendees.0.name}}\"}" - }, - "triggers": { - "type": "string", - "example": [ - "BOOKING_CREATED", - "BOOKING_RESCHEDULED", - "BOOKING_CANCELLED", - "BOOKING_CONFIRMED", - "BOOKING_REJECTED", - "BOOKING_COMPLETED", - "BOOKING_NO_SHOW", - "BOOKING_REOPENED" - ], - "enum": [ - "BOOKING_CREATED", - "BOOKING_PAYMENT_INITIATED", - "BOOKING_PAID", - "BOOKING_RESCHEDULED", - "BOOKING_REQUESTED", - "BOOKING_CANCELLED", - "BOOKING_REJECTED", - "BOOKING_NO_SHOW_UPDATED", - "FORM_SUBMITTED", - "MEETING_ENDED", - "MEETING_STARTED", - "RECORDING_READY", - "INSTANT_MEETING", - "RECORDING_TRANSCRIPTION_GENERATED", - "OOO_CREATED" - ] - }, - "active": { - "type": "boolean" - }, - "subscriberUrl": { + "name": { "type": "string" }, - "secret": { + "slug": { "type": "string" - } - }, - "required": [ - "triggers", - "active", - "subscriberUrl" - ] - }, - "TeamWebhookOutputResponseDto": { - "type": "object", - "properties": { - "status": { - "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] - }, - "data": { - "$ref": "#/components/schemas/TeamWebhookOutputDto" - } - }, - "required": [ - "status", - "data" - ] - }, - "UpdateWebhookInputDto": { - "type": "object", - "properties": { - "payloadTemplate": { - "type": "string", - "description": "The template of the payload that will be sent to the subscriberUrl, check cal.com/docs/core-features/webhooks for more information", - "example": "{\"content\":\"A new event has been scheduled\",\"type\":\"{{type}}\",\"name\":\"{{title}}\",\"organizer\":\"{{organizer.name}}\",\"booker\":\"{{attendees.0.name}}\"}" }, - "triggers": { - "type": "string", - "example": [ - "BOOKING_CREATED", - "BOOKING_RESCHEDULED", - "BOOKING_CANCELLED", - "BOOKING_CONFIRMED", - "BOOKING_REJECTED", - "BOOKING_COMPLETED", - "BOOKING_NO_SHOW", - "BOOKING_REOPENED" - ], - "enum": [ - "BOOKING_CREATED", - "BOOKING_PAYMENT_INITIATED", - "BOOKING_PAID", - "BOOKING_RESCHEDULED", - "BOOKING_REQUESTED", - "BOOKING_CANCELLED", - "BOOKING_REJECTED", - "BOOKING_NO_SHOW_UPDATED", - "FORM_SUBMITTED", - "MEETING_ENDED", - "MEETING_STARTED", - "RECORDING_READY", - "INSTANT_MEETING", - "RECORDING_TRANSCRIPTION_GENERATED", - "OOO_CREATED" - ] + "type": { + "type": "object" }, - "active": { + "enabled": { "type": "boolean" - }, - "subscriberUrl": { - "type": "string" - }, - "secret": { - "type": "string" } } }, - "GetDefaultScheduleOutput_2024_06_11": { + "UpdateOrganizationAttributesOutput": { "type": "object", "properties": { "status": { @@ -8893,7 +9901,7 @@ ] }, "data": { - "$ref": "#/components/schemas/ScheduleOutput_2024_06_11" + "$ref": "#/components/schemas/Attribute" } }, "required": [ @@ -8901,219 +9909,109 @@ "data" ] }, - "CreateAvailabilityInput_2024_04_15": { - "type": "object", - "properties": { - "days": { - "example": [ - 1, - 2 - ], - "type": "array", - "items": { - "type": "number" - } - }, - "startTime": { - "format": "date-time", - "type": "string" - }, - "endTime": { - "format": "date-time", - "type": "string" - } - }, - "required": [ - "days", - "startTime", - "endTime" - ] - }, - "CreateScheduleInput_2024_04_15": { + "DeleteOrganizationAttributesOutput": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "timeZone": { - "type": "string" - }, - "availabilities": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CreateAvailabilityInput_2024_04_15" - } + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] }, - "isDefault": { - "type": "boolean" + "data": { + "$ref": "#/components/schemas/Attribute" } }, "required": [ - "name", - "timeZone", - "isDefault" + "status", + "data" ] }, - "WorkingHours": { + "OptionOutput": { "type": "object", "properties": { - "days": { - "type": "array", - "items": { - "type": "number" - } + "id": { + "type": "string", + "description": "The ID of the option", + "example": "attr_option_id" }, - "startTime": { - "type": "number" + "attributeId": { + "type": "string", + "description": "The ID of the attribute", + "example": "attr_id" }, - "endTime": { - "type": "number" + "value": { + "type": "string", + "description": "The value of the option", + "example": "option_value" }, - "userId": { - "type": "number", - "nullable": true + "slug": { + "type": "string", + "description": "The slug of the option", + "example": "option-slug" } }, "required": [ - "days", - "startTime", - "endTime" + "id", + "attributeId", + "value", + "slug" ] }, - "AvailabilityModel": { + "CreateAttributeOptionOutput": { "type": "object", "properties": { - "id": { - "type": "number" - }, - "userId": { - "type": "number", - "nullable": true - }, - "eventTypeId": { - "type": "number", - "nullable": true - }, - "days": { - "type": "array", - "items": { - "type": "number" - } - }, - "startTime": { - "format": "date-time", - "type": "string" - }, - "endTime": { - "format": "date-time", - "type": "string" - }, - "date": { - "format": "date-time", + "status": { "type": "string", - "nullable": true + "example": "success", + "enum": [ + "success", + "error" + ] }, - "scheduleId": { - "type": "number", - "nullable": true + "data": { + "$ref": "#/components/schemas/OptionOutput" } }, "required": [ - "id", - "days", - "startTime", - "endTime" + "status", + "data" ] }, - "TimeRange": { + "DeleteAttributeOptionOutput": { "type": "object", "properties": { - "userId": { - "type": "number", - "nullable": true - }, - "start": { - "format": "date-time", - "type": "string" + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] }, - "end": { - "format": "date-time", - "type": "string" + "data": { + "$ref": "#/components/schemas/OptionOutput" } }, "required": [ - "start", - "end" + "status", + "data" ] }, - "ScheduleOutput": { + "UpdateOrganizationAttributeOptionInput": { "type": "object", "properties": { - "id": { - "type": "number" - }, - "name": { + "value": { "type": "string" }, - "isManaged": { - "type": "boolean" - }, - "workingHours": { - "type": "array", - "items": { - "$ref": "#/components/schemas/WorkingHours" - } - }, - "schedule": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AvailabilityModel" - } - }, - "availability": { - "type": "array", - "items": { - "required": true, - "type": "array", - "items": { - "$ref": "#/components/schemas/TimeRange" - } - } - }, - "timeZone": { + "slug": { "type": "string" - }, - "dateOverrides": { - "type": "array", - "items": { - "type": "object" - } - }, - "isDefault": { - "type": "boolean" - }, - "isLastSchedule": { - "type": "boolean" - }, - "readOnly": { - "type": "boolean" } - }, - "required": [ - "id", - "name", - "isManaged", - "workingHours", - "schedule", - "availability", - "timeZone", - "dateOverrides", - "isDefault", - "isLastSchedule", - "readOnly" - ] + } }, - "CreateScheduleOutput_2024_04_15": { + "UpdateAttributeOptionOutput": { "type": "object", "properties": { "status": { @@ -9125,7 +10023,7 @@ ] }, "data": { - "$ref": "#/components/schemas/ScheduleOutput" + "$ref": "#/components/schemas/OptionOutput" } }, "required": [ @@ -9133,7 +10031,7 @@ "data" ] }, - "GetDefaultScheduleOutput_2024_04_15": { + "GetAllAttributeOptionOutput": { "type": "object", "properties": { "status": { @@ -9145,12 +10043,10 @@ ] }, "data": { - "nullable": true, - "allOf": [ - { - "$ref": "#/components/schemas/ScheduleOutput" - } - ] + "type": "array", + "items": { + "$ref": "#/components/schemas/OptionOutput" + } } }, "required": [ @@ -9158,7 +10054,46 @@ "data" ] }, - "GetScheduleOutput_2024_04_15": { + "AssignOrganizationAttributeOptionToUserInput": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "attributeOptionId": { + "type": "string" + }, + "attributeId": { + "type": "string" + } + }, + "required": [ + "attributeId" + ] + }, + "AssignOptionUserOutputData": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the option assigned to the user" + }, + "memberId": { + "type": "number", + "description": "The ID form the org membership for the user" + }, + "attributeOptionId": { + "type": "string", + "description": "The value of the option" + } + }, + "required": [ + "id", + "memberId", + "attributeOptionId" + ] + }, + "AssignOptionUserOutput": { "type": "object", "properties": { "status": { @@ -9170,7 +10105,7 @@ ] }, "data": { - "$ref": "#/components/schemas/ScheduleOutput" + "$ref": "#/components/schemas/AssignOptionUserOutputData" } }, "required": [ @@ -9178,7 +10113,7 @@ "data" ] }, - "GetSchedulesOutput_2024_04_15": { + "UnassignOptionUserOutput": { "type": "object", "properties": { "status": { @@ -9190,7 +10125,7 @@ ] }, "data": { - "$ref": "#/components/schemas/ScheduleOutput" + "$ref": "#/components/schemas/AssignOptionUserOutputData" } }, "required": [ @@ -9198,186 +10133,173 @@ "data" ] }, - "UpdateScheduleInput_2024_04_15": { + "GetOptionUserOutputData": { "type": "object", "properties": { - "timeZone": { - "type": "string" - }, - "name": { - "type": "string" + "id": { + "type": "string", + "description": "The ID of the option assigned to the user" }, - "isDefault": { - "type": "boolean" + "attributeId": { + "type": "string", + "description": "The ID of the attribute" }, - "schedule": { - "example": [ - [], - [ - { - "start": "2022-01-01T00:00:00.000Z", - "end": "2022-01-02T00:00:00.000Z" - } - ], - [], - [], - [], - [], - [] - ], - "items": { - "type": "array" - }, - "type": "array" + "value": { + "type": "string", + "description": "The value of the option" }, - "dateOverrides": { - "example": [ - [], - [ - { - "start": "2022-01-01T00:00:00.000Z", - "end": "2022-01-02T00:00:00.000Z" - } - ], - [], - [], - [], - [], - [] - ], - "items": { - "type": "array" - }, - "type": "array" + "slug": { + "type": "string", + "description": "The slug of the option" } }, "required": [ - "timeZone", - "name", - "isDefault", - "schedule" + "id", + "attributeId", + "value", + "slug" ] }, - "EventTypeModel_2024_04_15": { + "GetOptionUserOutput": { "type": "object", "properties": { - "id": { - "type": "number" - }, - "eventName": { + "status": { "type": "string", - "nullable": true + "example": "success", + "enum": [ + "success", + "error" + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GetOptionUserOutputData" + } } }, "required": [ - "id" + "status", + "data" ] }, - "AvailabilityModel_2024_04_15": { + "TeamWebhookOutputDto": { "type": "object", "properties": { - "id": { - "type": "number" - }, - "userId": { - "type": "number", - "nullable": true + "payloadTemplate": { + "type": "string", + "description": "The template of the payload that will be sent to the subscriberUrl, check cal.com/docs/core-features/webhooks for more information", + "example": "{\"content\":\"A new event has been scheduled\",\"type\":\"{{type}}\",\"name\":\"{{title}}\",\"organizer\":\"{{organizer.name}}\",\"booker\":\"{{attendees.0.name}}\"}" }, - "scheduleId": { - "type": "number", - "nullable": true + "teamId": { + "type": "number" }, - "eventTypeId": { - "type": "number", - "nullable": true + "id": { + "type": "number" }, - "days": { + "triggers": { "type": "array", "items": { - "type": "number" + "type": "object" } }, - "startTime": { - "format": "date-time", + "subscriberUrl": { "type": "string" }, - "endTime": { - "format": "date-time", - "type": "string" + "active": { + "type": "boolean" }, - "date": { - "format": "date-time", - "type": "string", - "nullable": true + "secret": { + "type": "string" } }, "required": [ + "payloadTemplate", + "teamId", "id", - "days" + "triggers", + "subscriberUrl", + "active" ] }, - "ScheduleModel_2024_04_15": { + "TeamWebhooksOutputResponseDto": { "type": "object", "properties": { - "id": { - "type": "number" - }, - "userId": { - "type": "number" - }, - "name": { - "type": "string" - }, - "timeZone": { + "status": { "type": "string", - "nullable": true - }, - "eventType": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventTypeModel_2024_04_15" - } + "example": "success", + "enum": [ + "success", + "error" + ] }, - "availability": { + "data": { "type": "array", "items": { - "$ref": "#/components/schemas/AvailabilityModel_2024_04_15" + "$ref": "#/components/schemas/TeamWebhookOutputDto" } } }, "required": [ - "id", - "userId", - "name" + "status", + "data" ] }, - "UpdatedScheduleOutput_2024_04_15": { + "CreateWebhookInputDto": { "type": "object", "properties": { - "schedule": { - "$ref": "#/components/schemas/ScheduleModel_2024_04_15" + "payloadTemplate": { + "type": "string", + "description": "The template of the payload that will be sent to the subscriberUrl, check cal.com/docs/core-features/webhooks for more information", + "example": "{\"content\":\"A new event has been scheduled\",\"type\":\"{{type}}\",\"name\":\"{{title}}\",\"organizer\":\"{{organizer.name}}\",\"booker\":\"{{attendees.0.name}}\"}" }, - "isDefault": { + "triggers": { + "type": "string", + "example": [ + "BOOKING_CREATED", + "BOOKING_RESCHEDULED", + "BOOKING_CANCELLED", + "BOOKING_CONFIRMED", + "BOOKING_REJECTED", + "BOOKING_COMPLETED", + "BOOKING_NO_SHOW", + "BOOKING_REOPENED" + ], + "enum": [ + "BOOKING_CREATED", + "BOOKING_PAYMENT_INITIATED", + "BOOKING_PAID", + "BOOKING_RESCHEDULED", + "BOOKING_REQUESTED", + "BOOKING_CANCELLED", + "BOOKING_REJECTED", + "BOOKING_NO_SHOW_UPDATED", + "FORM_SUBMITTED", + "MEETING_ENDED", + "MEETING_STARTED", + "RECORDING_READY", + "INSTANT_MEETING", + "RECORDING_TRANSCRIPTION_GENERATED", + "OOO_CREATED" + ] + }, + "active": { "type": "boolean" }, - "timeZone": { + "subscriberUrl": { "type": "string" }, - "prevDefaultId": { - "type": "number", - "nullable": true - }, - "currentDefaultId": { - "type": "number", - "nullable": true + "secret": { + "type": "string" } }, "required": [ - "schedule", - "isDefault" + "triggers", + "active", + "subscriberUrl" ] }, - "UpdateScheduleOutput_2024_04_15": { + "TeamWebhookOutputResponseDto": { "type": "object", "properties": { "status": { @@ -9389,7 +10311,7 @@ ] }, "data": { - "$ref": "#/components/schemas/UpdatedScheduleOutput_2024_04_15" + "$ref": "#/components/schemas/TeamWebhookOutputDto" } }, "required": [ @@ -9397,7 +10319,56 @@ "data" ] }, - "DeleteScheduleOutput_2024_04_15": { + "UpdateWebhookInputDto": { + "type": "object", + "properties": { + "payloadTemplate": { + "type": "string", + "description": "The template of the payload that will be sent to the subscriberUrl, check cal.com/docs/core-features/webhooks for more information", + "example": "{\"content\":\"A new event has been scheduled\",\"type\":\"{{type}}\",\"name\":\"{{title}}\",\"organizer\":\"{{organizer.name}}\",\"booker\":\"{{attendees.0.name}}\"}" + }, + "triggers": { + "type": "string", + "example": [ + "BOOKING_CREATED", + "BOOKING_RESCHEDULED", + "BOOKING_CANCELLED", + "BOOKING_CONFIRMED", + "BOOKING_REJECTED", + "BOOKING_COMPLETED", + "BOOKING_NO_SHOW", + "BOOKING_REOPENED" + ], + "enum": [ + "BOOKING_CREATED", + "BOOKING_PAYMENT_INITIATED", + "BOOKING_PAID", + "BOOKING_RESCHEDULED", + "BOOKING_REQUESTED", + "BOOKING_CANCELLED", + "BOOKING_REJECTED", + "BOOKING_NO_SHOW_UPDATED", + "FORM_SUBMITTED", + "MEETING_ENDED", + "MEETING_STARTED", + "RECORDING_READY", + "INSTANT_MEETING", + "RECORDING_TRANSCRIPTION_GENERATED", + "OOO_CREATED" + ] + }, + "active": { + "type": "boolean" + }, + "subscriberUrl": { + "type": "string" + }, + "secret": { + "type": "string" + } + } + }, + "GetDefaultScheduleOutput_2024_06_11": { "type": "object", "properties": { "status": { @@ -9407,10 +10378,14 @@ "success", "error" ] + }, + "data": { + "$ref": "#/components/schemas/ScheduleOutput_2024_06_11" } }, "required": [ - "status" + "status", + "data" ] }, "AuthUrlData": { @@ -10130,6 +11105,26 @@ "credentialId" ] }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "name": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "email" + ] + }, "GetBookingsDataEntry": { "type": "object", "properties": { @@ -10416,6 +11411,21 @@ "data" ] }, + "Location": { + "type": "object", + "properties": { + "optionValue": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "optionValue", + "value" + ] + }, "Response": { "type": "object", "properties": { diff --git a/apps/api/v2/test/setEnvVars.ts b/apps/api/v2/test/setEnvVars.ts index e7a2393878329e..102a16fc138dd6 100644 --- a/apps/api/v2/test/setEnvVars.ts +++ b/apps/api/v2/test/setEnvVars.ts @@ -22,4 +22,7 @@ const env: Partial> = { process.env = { ...env, ...process.env, + NEXT_PUBLIC_VAPID_PUBLIC_KEY: + "BIds0AQJ96xGBjTSMHTOqLBLutQE7Lu32KKdgSdy7A2cS4mKI2cgb3iGkhDJa5Siy-stezyuPm8qpbhmNxdNHMw", + VAPID_PRIVATE_KEY: "6cJtkASCar5sZWguIAW7OjvyixpBw9p8zL8WDDwk9Jk", }; diff --git a/apps/web/CHANGELOG.md b/apps/web/CHANGELOG.md index bfb263520b62ae..aff4d76b1e669f 100644 --- a/apps/web/CHANGELOG.md +++ b/apps/web/CHANGELOG.md @@ -1,5 +1,14 @@ # @calcom/web +## 4.5.2 + +### Patch Changes + +- Updated dependencies + - @calcom/embed-core@1.5.1 + - @calcom/embed-react@1.5.1 + - @calcom/embed-snippet@1.3.1 + ## 4.0.8 ### Patch Changes diff --git a/apps/web/abTest/middlewareFactory.ts b/apps/web/abTest/middlewareFactory.ts index 50e5e3d2aae937..c04f08fd15297e 100644 --- a/apps/web/abTest/middlewareFactory.ts +++ b/apps/web/abTest/middlewareFactory.ts @@ -28,7 +28,9 @@ const ROUTES: [URLPattern, boolean][] = [ ["/getting-started/:step", process.env.APP_ROUTER_GETTING_STARTED_STEP_ENABLED === "1"] as const, ["/apps", process.env.APP_ROUTER_APPS_ENABLED === "1"] as const, ["/bookings/:status", process.env.APP_ROUTER_BOOKINGS_STATUS_ENABLED === "1"] as const, + ["/booking/:path*", process.env.APP_ROUTER_BOOKING_ENABLED === "1"] as const, ["/video/:path*", process.env.APP_ROUTER_VIDEO_ENABLED === "1"] as const, + ["/team", process.env.APP_ROUTER_TEAM_ENABLED === "1"] as const, ["/teams", process.env.APP_ROUTER_TEAMS_ENABLED === "1"] as const, ["/more", process.env.APP_ROUTER_MORE_ENABLED === "1"] as const, ["/maintenance", process.env.APP_ROUTER_MAINTENANCE_ENABLED === "1"] as const, diff --git a/apps/web/app/_trpc/trpc-provider.tsx b/apps/web/app/_trpc/trpc-provider.tsx index 45100bd89f4c3f..6bb9e77a7185b2 100644 --- a/apps/web/app/_trpc/trpc-provider.tsx +++ b/apps/web/app/_trpc/trpc-provider.tsx @@ -55,7 +55,9 @@ export const TrpcProvider: React.FC<{ children: React.ReactNode; dehydratedState // adds pretty logs to your console in development and logs errors in production loggerLink({ enabled: (opts) => - !!process.env.NEXT_PUBLIC_DEBUG || (opts.direction === "down" && opts.result instanceof Error), + (typeof process.env.NEXT_PUBLIC_LOGGER_LEVEL === "number" && + process.env.NEXT_PUBLIC_LOGGER_LEVEL >= 0) || + (opts.direction === "down" && opts.result instanceof Error), }), splitLink({ // check for context property `skipBatch` diff --git a/apps/web/app/future/apps/[slug]/[...pages]/page.tsx b/apps/web/app/future/apps/[slug]/[...pages]/page.tsx index ad854995bdebfe..e67645d9fb8cf2 100644 --- a/apps/web/app/future/apps/[slug]/[...pages]/page.tsx +++ b/apps/web/app/future/apps/[slug]/[...pages]/page.tsx @@ -1,9 +1,8 @@ import { withAppDirSsr } from "app/WithAppDirSsr"; -import type { SearchParams } from "app/_types"; +import type { PageProps } from "app/_types"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; import type { GetServerSidePropsResult } from "next"; -import type { Params } from "next/dist/shared/lib/router/utils/route-matcher"; import { cookies, headers } from "next/headers"; import { notFound } from "next/navigation"; import z from "zod"; @@ -18,13 +17,7 @@ const paramsSchema = z.object({ pages: z.array(z.string()), }); -export const generateMetadata = async ({ - params, - searchParams, -}: { - params: Params; - searchParams: SearchParams; -}) => { +export const generateMetadata = async ({ params, searchParams }: PageProps) => { const p = paramsSchema.safeParse(params); if (!p.success) { @@ -34,7 +27,7 @@ export const generateMetadata = async ({ const legacyContext = buildLegacyCtx(headers(), cookies(), params, searchParams); const data = await getData(legacyContext); const form = "form" in data ? (data.form as { name?: string; description?: string }) : null; - const formName = form?.name ?? "Forms"; + const formName = form?.name ?? "Routing Forms"; const formDescription = form?.description ?? ""; return await _generateMetadata( diff --git a/apps/web/app/future/auth/setup/page.tsx b/apps/web/app/future/auth/setup/page.tsx index b33b9693ab55dc..5f4d478e455a63 100644 --- a/apps/web/app/future/auth/setup/page.tsx +++ b/apps/web/app/future/auth/setup/page.tsx @@ -1,12 +1,13 @@ -import Setup from "@pages/auth/setup"; import { withAppDirSsr } from "app/WithAppDirSsr"; import { WithLayout } from "app/layoutHOC"; -import type { InferGetServerSidePropsType } from "next"; import { getServerSideProps } from "@server/lib/setup/getServerSideProps"; +import Setup from "~/auth/setup-view"; +import type { PageProps } from "~/auth/setup-view"; + export default WithLayout({ getLayout: null, Page: Setup, - getData: withAppDirSsr>(getServerSideProps), + getData: withAppDirSsr(getServerSideProps), })<"P">; diff --git a/apps/web/app/future/auth/signin/page.tsx b/apps/web/app/future/auth/signin/page.tsx index bb19d2bc210d17..18354c4f9c7b9e 100644 --- a/apps/web/app/future/auth/signin/page.tsx +++ b/apps/web/app/future/auth/signin/page.tsx @@ -1,13 +1,14 @@ -import signin from "@pages/auth/signin"; import { withAppDirSsr } from "app/WithAppDirSsr"; import { WithLayout } from "app/layoutHOC"; -import type { InferGetServerSidePropsType } from "next"; import { getServerSideProps } from "@server/lib/auth/signin/getServerSideProps"; +import SignIn from "~/auth/signin-view"; +import type { PageProps } from "~/auth/signin-view"; + export default WithLayout({ getLayout: null, - Page: signin, + Page: SignIn, // @ts-expect-error TODO: fix this - getData: withAppDirSsr>(getServerSideProps), + getData: withAppDirSsr(getServerSideProps), })<"P">; diff --git a/apps/web/app/future/auth/sso/[provider]/page.tsx b/apps/web/app/future/auth/sso/[provider]/page.tsx index 2a59c87a8ac1c5..efac8c1f18d3a8 100644 --- a/apps/web/app/future/auth/sso/[provider]/page.tsx +++ b/apps/web/app/future/auth/sso/[provider]/page.tsx @@ -1,12 +1,13 @@ -import Provider from "@pages/auth/sso/[provider]"; import { withAppDirSsr } from "app/WithAppDirSsr"; import { WithLayout } from "app/layoutHOC"; -import type { InferGetServerSidePropsType } from "next"; import { getServerSideProps } from "@server/lib/auth/sso/[provider]/getServerSideProps"; +import type { SSOProviderPageProps } from "~/auth/sso/provider-view"; +import SSOProviderView from "~/auth/sso/provider-view"; + export default WithLayout({ getLayout: null, - Page: Provider, - getData: withAppDirSsr>(getServerSideProps), + Page: SSOProviderView, + getData: withAppDirSsr(getServerSideProps), })<"P">; diff --git a/apps/web/app/future/auth/sso/direct/page.tsx b/apps/web/app/future/auth/sso/direct/page.tsx index 0f69f324feaf8a..a920c02ba34f40 100644 --- a/apps/web/app/future/auth/sso/direct/page.tsx +++ b/apps/web/app/future/auth/sso/direct/page.tsx @@ -1,11 +1,13 @@ -import DirectSSOLogin from "@pages/auth/sso/direct"; import { withAppDirSsr } from "app/WithAppDirSsr"; import { WithLayout } from "app/layoutHOC"; import { getServerSideProps } from "@server/lib/auth/sso/direct/getServerSideProps"; +import type { SSODirectPageProps } from "~/auth/sso/direct-view"; +import SSODirectView from "~/auth/sso/direct-view"; + export default WithLayout({ getLayout: null, - Page: DirectSSOLogin, - getData: withAppDirSsr(getServerSideProps), + Page: SSODirectView, + getData: withAppDirSsr(getServerSideProps), })<"P">; diff --git a/apps/web/app/future/booking/[uid]/page.tsx b/apps/web/app/future/booking/[uid]/page.tsx index 238dd365eea6c5..e192d88b61211c 100644 --- a/apps/web/app/future/booking/[uid]/page.tsx +++ b/apps/web/app/future/booking/[uid]/page.tsx @@ -1,5 +1,5 @@ import { withAppDirSsr } from "app/WithAppDirSsr"; -import type { Params, SearchParams } from "app/_types"; +import type { PageProps as _PageProps } from "app/_types"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; import { cookies, headers } from "next/headers"; @@ -11,13 +11,7 @@ import { buildLegacyCtx } from "@lib/buildLegacyCtx"; import OldPage from "~/bookings/views/bookings-single-view"; import { getServerSideProps, type PageProps } from "~/bookings/views/bookings-single-view.getServerSideProps"; -export const generateMetadata = async ({ - params, - searchParams, -}: { - params: Params; - searchParams: SearchParams; -}) => { +export const generateMetadata = async ({ params, searchParams }: _PageProps) => { const { bookingInfo, eventType, recurringBookings } = await getData( buildLegacyCtx(headers(), cookies(), params, searchParams) ); diff --git a/apps/web/app/future/bookings/[status]/page.tsx b/apps/web/app/future/bookings/[status]/page.tsx index 15f5630fa0f11e..a4a5a0e058a85c 100644 --- a/apps/web/app/future/bookings/[status]/page.tsx +++ b/apps/web/app/future/bookings/[status]/page.tsx @@ -3,8 +3,6 @@ import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; import type { InferGetStaticPropsType } from "next"; -import { APP_NAME } from "@calcom/lib/constants"; - import { validStatuses } from "~/bookings/lib/validStatuses"; import Page from "~/bookings/views/bookings-listing-view"; import { getStaticProps } from "~/bookings/views/bookings-listing-view.getStaticProps"; @@ -14,8 +12,8 @@ const getData = withAppDirSsg(getStaticProps); export const generateMetadata = async () => await _generateMetadata( - (t) => `${APP_NAME} | ${t("bookings")}`, - () => "" + () => "Bookings", + () => "Create events to share for people to book on your calendar." ); export const generateStaticParams = async () => { diff --git a/apps/web/app/future/d/[link]/[slug]/page.tsx b/apps/web/app/future/d/[link]/[slug]/page.tsx index 03c250363f5953..0d194c685322de 100644 --- a/apps/web/app/future/d/[link]/[slug]/page.tsx +++ b/apps/web/app/future/d/[link]/[slug]/page.tsx @@ -1,6 +1,5 @@ -import LegacyPage from "@pages/d/[link]/[slug]"; import { withAppDirSsr } from "app/WithAppDirSsr"; -import type { Params, SearchParams } from "app/_types"; +import type { PageProps as _PageProps } from "app/_types"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; import { cookies, headers } from "next/headers"; @@ -10,14 +9,11 @@ import { EventRepository } from "@calcom/lib/server/repository/event"; import { buildLegacyCtx } from "@lib/buildLegacyCtx"; import { getServerSideProps } from "@lib/d/[link]/[slug]/getServerSideProps"; +import { type PageProps } from "@lib/d/[link]/[slug]/getServerSideProps"; -export const generateMetadata = async ({ - params, - searchParams, -}: { - params: Params; - searchParams: SearchParams; -}) => { +import Type from "~/d/[link]/d-type-view"; + +export const generateMetadata = async ({ params, searchParams }: _PageProps) => { const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams); const pageProps = await getData(legacyCtx); @@ -42,5 +38,5 @@ export const generateMetadata = async ({ ); }; -const getData = withAppDirSsr(getServerSideProps); -export default WithLayout({ getLayout: null, Page: LegacyPage, getData })<"P">; +const getData = withAppDirSsr(getServerSideProps); +export default WithLayout({ getLayout: null, Page: Type, getData })<"P">; diff --git a/apps/web/app/future/event-types/[type]/page.tsx b/apps/web/app/future/event-types/[type]/page.tsx index 6cc40aa59ce0e5..6bd794a2b1aac0 100644 --- a/apps/web/app/future/event-types/[type]/page.tsx +++ b/apps/web/app/future/event-types/[type]/page.tsx @@ -4,12 +4,12 @@ import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; import { cookies, headers } from "next/headers"; +import { EventType } from "@calcom/atoms/monorepo"; + import { buildLegacyCtx } from "@lib/buildLegacyCtx"; import { getServerSideProps } from "@lib/event-types/[type]/getServerSideProps"; import type { PageProps as EventTypePageProps } from "@lib/event-types/[type]/getServerSideProps"; -import EventTypePageWrapper from "~/event-types/views/event-types-single-view"; - export const generateMetadata = async ({ params, searchParams }: PageProps) => { const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams); const { eventType } = await getData(legacyCtx); @@ -21,5 +21,5 @@ export const generateMetadata = async ({ params, searchParams }: PageProps) => { }; const getData = withAppDirSsr(getServerSideProps); -const Page = (props: EventTypePageProps) => ; +const Page = ({ type, ...rest }: EventTypePageProps) => ; export default WithLayout({ getLayout: null, getData, Page })<"P">; diff --git a/apps/web/app/future/insights/page.tsx b/apps/web/app/future/insights/page.tsx index 24f94e4f8e5da3..1260490c07815b 100644 --- a/apps/web/app/future/insights/page.tsx +++ b/apps/web/app/future/insights/page.tsx @@ -1,10 +1,11 @@ -import LegacyPage from "@pages/insights/index"; import { withAppDirSsr } from "app/WithAppDirSsr"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; import { getServerSideProps } from "@lib/insights/getServerSideProps"; -import { type inferSSRProps } from "@lib/types/inferSSRProps"; + +import type { PageProps } from "~/insights/insights-view"; +import InsightsPage from "~/insights/insights-view"; export const generateMetadata = async () => await _generateMetadata( @@ -12,6 +13,6 @@ export const generateMetadata = async () => (t) => t("insights_subtitle") ); -const getData = withAppDirSsr>(getServerSideProps); +const getData = withAppDirSsr(getServerSideProps); -export default WithLayout({ getLayout: null, getData, Page: LegacyPage }); +export default WithLayout({ getLayout: null, getData, Page: InsightsPage }); diff --git a/apps/web/app/future/org/[orgSlug]/embed/page.tsx b/apps/web/app/future/org/[orgSlug]/embed/page.tsx index 499173cf980066..111865eeaf5403 100644 --- a/apps/web/app/future/org/[orgSlug]/embed/page.tsx +++ b/apps/web/app/future/org/[orgSlug]/embed/page.tsx @@ -1,6 +1,6 @@ import { withAppDirSsr } from "app/WithAppDirSsr"; import withEmbedSsrAppDir from "app/WithEmbedSSR"; -import type { Params, SearchParams } from "app/_types"; +import type { PageProps as _PageProps } from "app/_types"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; import { cookies, headers } from "next/headers"; @@ -13,13 +13,7 @@ import TeamPage from "~/team/team-view"; const getData = withAppDirSsr(getServerSideProps); -export const generateMetadata = async ({ - params, - searchParams, -}: { - params: Params; - searchParams: SearchParams; -}) => { +export const generateMetadata = async ({ params, searchParams }: _PageProps) => { const props = await getData(buildLegacyCtx(headers(), cookies(), params, searchParams)); const teamName = props.team.name || "Nameless Team"; diff --git a/apps/web/app/future/org/[orgSlug]/page.tsx b/apps/web/app/future/org/[orgSlug]/page.tsx index d6572bbb918fbd..b84f78b9679312 100644 --- a/apps/web/app/future/org/[orgSlug]/page.tsx +++ b/apps/web/app/future/org/[orgSlug]/page.tsx @@ -1,5 +1,5 @@ import { withAppDirSsr } from "app/WithAppDirSsr"; -import type { Params, SearchParams } from "app/_types"; +import type { PageProps as _PageProps } from "app/_types"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; import { cookies, headers } from "next/headers"; @@ -10,13 +10,7 @@ import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps"; import type { PageProps } from "~/team/team-view"; import TeamPage from "~/team/team-view"; -export const generateMetadata = async ({ - params, - searchParams, -}: { - params: Params; - searchParams: SearchParams; -}) => { +export const generateMetadata = async ({ params, searchParams }: _PageProps) => { const props = await getData(buildLegacyCtx(headers(), cookies(), params, searchParams)); const teamName = props.team.name || "Nameless Team"; diff --git a/apps/web/app/future/org/[orgSlug]/team/[slug]/[type]/page.tsx b/apps/web/app/future/org/[orgSlug]/team/[slug]/[type]/page.tsx index 22cb127b721915..ea51f5cd9b771b 100644 --- a/apps/web/app/future/org/[orgSlug]/team/[slug]/[type]/page.tsx +++ b/apps/web/app/future/org/[orgSlug]/team/[slug]/[type]/page.tsx @@ -1,5 +1,5 @@ import { withAppDirSsr } from "app/WithAppDirSsr"; -import type { Params, SearchParams } from "app/_types"; +import type { PageProps as _PageProps } from "app/_types"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; import { cookies, headers } from "next/headers"; @@ -13,13 +13,7 @@ import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps"; import type { PageProps } from "~/team/type-view"; import TypePage from "~/team/type-view"; -export const generateMetadata = async ({ - params, - searchParams, -}: { - params: Params; - searchParams: SearchParams; -}) => { +export const generateMetadata = async ({ params, searchParams }: _PageProps) => { const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams); const props = await getData(legacyCtx); const { user: username, slug: eventSlug, booking } = props; diff --git a/apps/web/app/future/org/[orgSlug]/team/[slug]/page.tsx b/apps/web/app/future/org/[orgSlug]/team/[slug]/page.tsx index d6572bbb918fbd..b84f78b9679312 100644 --- a/apps/web/app/future/org/[orgSlug]/team/[slug]/page.tsx +++ b/apps/web/app/future/org/[orgSlug]/team/[slug]/page.tsx @@ -1,5 +1,5 @@ import { withAppDirSsr } from "app/WithAppDirSsr"; -import type { Params, SearchParams } from "app/_types"; +import type { PageProps as _PageProps } from "app/_types"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; import { cookies, headers } from "next/headers"; @@ -10,13 +10,7 @@ import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps"; import type { PageProps } from "~/team/team-view"; import TeamPage from "~/team/team-view"; -export const generateMetadata = async ({ - params, - searchParams, -}: { - params: Params; - searchParams: SearchParams; -}) => { +export const generateMetadata = async ({ params, searchParams }: _PageProps) => { const props = await getData(buildLegacyCtx(headers(), cookies(), params, searchParams)); const teamName = props.team.name || "Nameless Team"; diff --git a/apps/web/app/future/page.tsx b/apps/web/app/future/page.tsx new file mode 100644 index 00000000000000..f82ff90851cf9c --- /dev/null +++ b/apps/web/app/future/page.tsx @@ -0,0 +1,13 @@ +import { redirect } from "next/navigation"; + +import { getServerSessionForAppDir } from "@calcom/features/auth/lib/get-server-session-for-app-dir"; + +const RedirectPage = async () => { + const session = await getServerSessionForAppDir(); + if (!session?.user?.id) { + redirect("/auth/login"); + } + redirect("/event-types"); +}; + +export default RedirectPage; diff --git a/apps/web/app/future/reschedule/[uid]/embed/page.tsx b/apps/web/app/future/reschedule/[uid]/embed/page.tsx index 4cc22aabf2903f..52f7f7e1d93e77 100644 --- a/apps/web/app/future/reschedule/[uid]/embed/page.tsx +++ b/apps/web/app/future/reschedule/[uid]/embed/page.tsx @@ -1,14 +1,15 @@ -import { getServerSideProps } from "@pages/reschedule/[uid]"; -import { withAppDirSsr } from "app/WithAppDirSsr"; +import { getServerSideProps as _getServerSideProps } from "@pages/reschedule/[uid]"; import type { PageProps } from "app/_types"; import { cookies, headers } from "next/headers"; import { buildLegacyCtx } from "@lib/buildLegacyCtx"; import withEmbedSsr from "@lib/withEmbedSsr"; +const getData = withEmbedSsr(_getServerSideProps); + const Page = async ({ params, searchParams }: PageProps) => { const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams); - await withAppDirSsr(withEmbedSsr(getServerSideProps))(legacyCtx); + await getData(legacyCtx); return null; }; diff --git a/apps/web/app/future/reschedule/[uid]/page.tsx b/apps/web/app/future/reschedule/[uid]/page.tsx index 921369891f3e1f..4f629a3ba0fd70 100644 --- a/apps/web/app/future/reschedule/[uid]/page.tsx +++ b/apps/web/app/future/reschedule/[uid]/page.tsx @@ -1,10 +1,10 @@ -import { getServerSideProps } from "@pages/reschedule/[uid]"; import { withAppDirSsr } from "app/WithAppDirSsr"; import type { PageProps } from "app/_types"; import { _generateMetadata } from "app/_utils"; import { headers, cookies } from "next/headers"; import { buildLegacyCtx } from "@lib/buildLegacyCtx"; +import { getServerSideProps } from "@lib/reschedule/[uid]/getServerSideProps"; export const generateMetadata = async () => await _generateMetadata( diff --git a/apps/web/app/future/routing-forms/[...pages]/page.tsx b/apps/web/app/future/routing-forms/[...pages]/page.tsx index 3ee4fbd90d2242..4ecc5a1aa0839f 100644 --- a/apps/web/app/future/routing-forms/[...pages]/page.tsx +++ b/apps/web/app/future/routing-forms/[...pages]/page.tsx @@ -1,12 +1,19 @@ +import type { PageProps } from "app/_types"; import { redirect } from "next/navigation"; +import z from "zod"; -const getPageProps = () => { - return redirect(`/apps/routing-forms/forms`); -}; -const Page = () => { - getPageProps(); +const paramsSchema = z + .object({ + pages: z.array(z.string()), + }) + .catch({ + pages: [], + }); + +const Page = ({ params, searchParams }: PageProps) => { + const { pages } = paramsSchema.parse({ ...params, ...searchParams }); - return null; + redirect(`/apps/routing-forms/${pages.length ? pages.join("/") : ""}`); }; export default Page; diff --git a/apps/web/app/future/routing-forms/page.tsx b/apps/web/app/future/routing-forms/page.tsx index f904f3eaf4b063..ba222ba47617af 100644 --- a/apps/web/app/future/routing-forms/page.tsx +++ b/apps/web/app/future/routing-forms/page.tsx @@ -1,30 +1,7 @@ -import type { PageProps } from "app/_types"; -import { type GetServerSidePropsContext } from "next"; -import { cookies, headers } from "next/headers"; import { redirect } from "next/navigation"; -import z from "zod"; -import { buildLegacyCtx } from "@lib/buildLegacyCtx"; - -const paramsSchema = z - .object({ - pages: z.array(z.string()), - }) - .catch({ - pages: [], - }); - -const getPageProps = async (context: GetServerSidePropsContext) => { - const { pages } = paramsSchema.parse(context.params); - - return redirect(`/apps/routing-forms/${pages.length ? pages.join("/") : ""}`); -}; - -const Page = async ({ params, searchParams }: PageProps) => { - const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams); - await getPageProps(legacyCtx); - - return null; +const Page = () => { + redirect("/apps/routing-forms/forms"); }; export default Page; diff --git a/apps/web/app/future/settings/(admin)/admin/page.tsx b/apps/web/app/future/settings/(admin)/admin/page.tsx index 77f09f99a3dba7..4efacc89bb0189 100644 --- a/apps/web/app/future/settings/(admin)/admin/page.tsx +++ b/apps/web/app/future/settings/(admin)/admin/page.tsx @@ -1,8 +1,4 @@ -import LegacyPage from "@pages/settings/admin/index"; import { _generateMetadata } from "app/_utils"; -import { WithLayout } from "app/layoutHOC"; - -import { getLayout } from "@components/auth/layouts/AdminLayoutAppDir"; export const generateMetadata = async () => await _generateMetadata( @@ -10,4 +6,5 @@ export const generateMetadata = async () => () => "admin_description" ); -export default WithLayout({ getServerLayout: getLayout, Page: LegacyPage })<"P">; +const Page = () =>

Admin index

; +export default Page; diff --git a/apps/web/app/future/settings/(settings)/organizations/appearance/layout.tsx b/apps/web/app/future/settings/(settings)/organizations/appearance/layout.tsx deleted file mode 100644 index 9da17cb2818d7b..00000000000000 --- a/apps/web/app/future/settings/(settings)/organizations/appearance/layout.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { WithLayout } from "app/layoutHOC"; - -import { getLayout } from "@calcom/features/settings/appDir/SettingsLayoutAppDir"; - -export default WithLayout({ getServerLayout: getLayout }); diff --git a/apps/web/app/future/settings/(settings)/organizations/billing/layout.tsx b/apps/web/app/future/settings/(settings)/organizations/billing/layout.tsx deleted file mode 100644 index 9da17cb2818d7b..00000000000000 --- a/apps/web/app/future/settings/(settings)/organizations/billing/layout.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { WithLayout } from "app/layoutHOC"; - -import { getLayout } from "@calcom/features/settings/appDir/SettingsLayoutAppDir"; - -export default WithLayout({ getServerLayout: getLayout }); diff --git a/apps/web/app/future/settings/(settings)/organizations/billing/page.tsx b/apps/web/app/future/settings/(settings)/organizations/billing/page.tsx index 52080e55f5b6c8..7993b194a42783 100644 --- a/apps/web/app/future/settings/(settings)/organizations/billing/page.tsx +++ b/apps/web/app/future/settings/(settings)/organizations/billing/page.tsx @@ -1,11 +1,5 @@ -import { _generateMetadata } from "app/_utils"; +import BillingPage, { generateMetadata } from "../../billing/page"; -import Page from "~/settings/billing/billing-view"; +export { generateMetadata }; -export const generateMetadata = async () => - await _generateMetadata( - (t) => t("billing"), - (t) => t("manage_billing_description") - ); - -export default Page; +export default BillingPage; diff --git a/apps/web/app/future/settings/(settings)/organizations/general/layout.tsx b/apps/web/app/future/settings/(settings)/organizations/general/layout.tsx deleted file mode 100644 index 9da17cb2818d7b..00000000000000 --- a/apps/web/app/future/settings/(settings)/organizations/general/layout.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { WithLayout } from "app/layoutHOC"; - -import { getLayout } from "@calcom/features/settings/appDir/SettingsLayoutAppDir"; - -export default WithLayout({ getServerLayout: getLayout }); diff --git a/apps/web/app/future/settings/(settings)/organizations/general/page.tsx b/apps/web/app/future/settings/(settings)/organizations/general/page.tsx index e55435094bf079..30416c6daa3c08 100644 --- a/apps/web/app/future/settings/(settings)/organizations/general/page.tsx +++ b/apps/web/app/future/settings/(settings)/organizations/general/page.tsx @@ -1,6 +1,8 @@ -import { _generateMetadata } from "app/_utils"; +import { _generateMetadata, getFixedT } from "app/_utils"; -import Page from "@calcom/features/ee/organizations/pages/settings/general"; +import { getServerSessionForAppDir } from "@calcom/features/auth/lib/get-server-session-for-app-dir"; +import LegacyPage from "@calcom/features/ee/organizations/pages/settings/general"; +import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; export const generateMetadata = async () => await _generateMetadata( @@ -8,4 +10,15 @@ export const generateMetadata = async () => (t) => t("general_description") ); +const Page = async () => { + const session = await getServerSessionForAppDir(); + const t = await getFixedT(session?.user.locale || "en"); + + return ( + + + + ); +}; + export default Page; diff --git a/apps/web/app/future/settings/(settings)/organizations/members/layout.tsx b/apps/web/app/future/settings/(settings)/organizations/members/layout.tsx deleted file mode 100644 index 9da17cb2818d7b..00000000000000 --- a/apps/web/app/future/settings/(settings)/organizations/members/layout.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { WithLayout } from "app/layoutHOC"; - -import { getLayout } from "@calcom/features/settings/appDir/SettingsLayoutAppDir"; - -export default WithLayout({ getServerLayout: getLayout }); diff --git a/apps/web/app/future/settings/(settings)/organizations/members/page.tsx b/apps/web/app/future/settings/(settings)/organizations/members/page.tsx index 5c90c6869eac7c..e4ee4b40d1400b 100644 --- a/apps/web/app/future/settings/(settings)/organizations/members/page.tsx +++ b/apps/web/app/future/settings/(settings)/organizations/members/page.tsx @@ -1,6 +1,8 @@ -import { _generateMetadata } from "app/_utils"; +import { _generateMetadata, getFixedT } from "app/_utils"; -import Page from "@calcom/features/ee/organizations/pages/settings/members"; +import { getServerSessionForAppDir } from "@calcom/features/auth/lib/get-server-session-for-app-dir"; +import LegacyPage from "@calcom/features/ee/organizations/pages/settings/members"; +import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; export const generateMetadata = async () => await _generateMetadata( @@ -8,4 +10,15 @@ export const generateMetadata = async () => (t) => t("organization_description") ); +const Page = async () => { + const session = await getServerSessionForAppDir(); + const t = await getFixedT(session?.user.locale || "en"); + + return ( + + + + ); +}; + export default Page; diff --git a/apps/web/app/future/settings/(settings)/organizations/profile/layout.tsx b/apps/web/app/future/settings/(settings)/organizations/profile/layout.tsx deleted file mode 100644 index 9da17cb2818d7b..00000000000000 --- a/apps/web/app/future/settings/(settings)/organizations/profile/layout.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { WithLayout } from "app/layoutHOC"; - -import { getLayout } from "@calcom/features/settings/appDir/SettingsLayoutAppDir"; - -export default WithLayout({ getServerLayout: getLayout }); diff --git a/apps/web/app/future/settings/(settings)/organizations/profile/page.tsx b/apps/web/app/future/settings/(settings)/organizations/profile/page.tsx index 3450c5abddde9b..a9e54e098b3c53 100644 --- a/apps/web/app/future/settings/(settings)/organizations/profile/page.tsx +++ b/apps/web/app/future/settings/(settings)/organizations/profile/page.tsx @@ -1,6 +1,8 @@ -import { _generateMetadata } from "app/_utils"; +import { _generateMetadata, getFixedT } from "app/_utils"; -import Page from "@calcom/features/ee/organizations/pages/settings/profile"; +import { getServerSessionForAppDir } from "@calcom/features/auth/lib/get-server-session-for-app-dir"; +import LegacyPage from "@calcom/features/ee/organizations/pages/settings/profile"; +import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; export const generateMetadata = async () => await _generateMetadata( @@ -8,4 +10,18 @@ export const generateMetadata = async () => (t) => t("profile_org_description") ); +const Page = async () => { + const session = await getServerSessionForAppDir(); + const t = await getFixedT(session?.user.locale || "en"); + + return ( + + + + ); +}; + export default Page; diff --git a/apps/web/app/future/settings/(settings)/organizations/sso/page.tsx b/apps/web/app/future/settings/(settings)/organizations/sso/page.tsx new file mode 100644 index 00000000000000..55c561e75baf31 --- /dev/null +++ b/apps/web/app/future/settings/(settings)/organizations/sso/page.tsx @@ -0,0 +1,24 @@ +import { _generateMetadata, getFixedT } from "app/_utils"; + +import { getServerSessionForAppDir } from "@calcom/features/auth/lib/get-server-session-for-app-dir"; +import OrgSSOView from "@calcom/features/ee/sso/page/orgs-sso-view"; +import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; + +export const generateMetadata = async () => + await _generateMetadata( + (t) => t("sso_configuration"), + (t) => t("sso_configuration_description_orgs") + ); + +const Page = async () => { + const session = await getServerSessionForAppDir(); + const t = await getFixedT(session?.user.locale || "en"); + + return ( + + + + ); +}; + +export default Page; diff --git a/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/appearance/layout.tsx b/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/appearance/layout.tsx deleted file mode 100644 index 9da17cb2818d7b..00000000000000 --- a/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/appearance/layout.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { WithLayout } from "app/layoutHOC"; - -import { getLayout } from "@calcom/features/settings/appDir/SettingsLayoutAppDir"; - -export default WithLayout({ getServerLayout: getLayout }); diff --git a/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/appearance/page.tsx b/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/appearance/page.tsx index ac76104d07a5f0..25f31d3027b6d4 100644 --- a/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/appearance/page.tsx +++ b/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/appearance/page.tsx @@ -1,6 +1,8 @@ -import { _generateMetadata } from "app/_utils"; +import { _generateMetadata, getFixedT } from "app/_utils"; -import Page from "@calcom/features/ee/teams/pages/team-appearance-view"; +import { getServerSessionForAppDir } from "@calcom/features/auth/lib/get-server-session-for-app-dir"; +import LegacyPage from "@calcom/features/ee/teams/pages/team-appearance-view"; +import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; export const generateMetadata = async () => await _generateMetadata( @@ -8,4 +10,18 @@ export const generateMetadata = async () => (t) => t("appearance_team_description") ); +const Page = async () => { + const session = await getServerSessionForAppDir(); + const t = await getFixedT(session?.user.locale || "en"); + + return ( + + + + ); +}; + export default Page; diff --git a/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/members/layout.tsx b/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/members/layout.tsx deleted file mode 100644 index 9da17cb2818d7b..00000000000000 --- a/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/members/layout.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { WithLayout } from "app/layoutHOC"; - -import { getLayout } from "@calcom/features/settings/appDir/SettingsLayoutAppDir"; - -export default WithLayout({ getServerLayout: getLayout }); diff --git a/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/members/page.tsx b/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/members/page.tsx index 9e5018ada3668e..1896558bec3d29 100644 --- a/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/members/page.tsx +++ b/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/members/page.tsx @@ -1,6 +1,8 @@ -import { _generateMetadata } from "app/_utils"; +import { _generateMetadata, getFixedT } from "app/_utils"; -import Page from "@calcom/features/ee/organizations/pages/settings/other-team-members-view"; +import { getServerSessionForAppDir } from "@calcom/features/auth/lib/get-server-session-for-app-dir"; +import LegacyPage from "@calcom/features/ee/organizations/pages/settings/other-team-members-view"; +import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; export const generateMetadata = async () => await _generateMetadata( @@ -8,4 +10,16 @@ export const generateMetadata = async () => (t) => t("members_team_description") ); +const Page = async () => { + const session = await getServerSessionForAppDir(); + const t = await getFixedT(session?.user.locale || "en"); + + return ( + // TODO: Add CTA Button + + + + ); +}; + export default Page; diff --git a/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/profile/layout.tsx b/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/profile/layout.tsx deleted file mode 100644 index 9da17cb2818d7b..00000000000000 --- a/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/profile/layout.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { WithLayout } from "app/layoutHOC"; - -import { getLayout } from "@calcom/features/settings/appDir/SettingsLayoutAppDir"; - -export default WithLayout({ getServerLayout: getLayout }); diff --git a/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/profile/page.tsx b/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/profile/page.tsx index 66cf714fbcc5b5..11a5626af0b206 100644 --- a/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/profile/page.tsx +++ b/apps/web/app/future/settings/(settings)/organizations/teams/other/[id]/profile/page.tsx @@ -1,6 +1,8 @@ -import { _generateMetadata } from "app/_utils"; +import { _generateMetadata, getFixedT } from "app/_utils"; -import Page from "@calcom/features/ee/organizations/pages/settings/other-team-profile-view"; +import { getServerSessionForAppDir } from "@calcom/features/auth/lib/get-server-session-for-app-dir"; +import LegacyPage from "@calcom/features/ee/organizations/pages/settings/other-team-profile-view"; +import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; export const generateMetadata = async () => await _generateMetadata( @@ -8,4 +10,15 @@ export const generateMetadata = async () => (t) => t("profile_team_description") ); +const Page = async () => { + const session = await getServerSessionForAppDir(); + const t = await getFixedT(session?.user.locale || "en"); + + return ( + + + + ); +}; + export default Page; diff --git a/apps/web/app/future/settings/(settings)/organizations/teams/other/layout.tsx b/apps/web/app/future/settings/(settings)/organizations/teams/other/layout.tsx deleted file mode 100644 index 9da17cb2818d7b..00000000000000 --- a/apps/web/app/future/settings/(settings)/organizations/teams/other/layout.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { WithLayout } from "app/layoutHOC"; - -import { getLayout } from "@calcom/features/settings/appDir/SettingsLayoutAppDir"; - -export default WithLayout({ getServerLayout: getLayout }); diff --git a/apps/web/app/future/settings/(settings)/organizations/teams/other/page.ts b/apps/web/app/future/settings/(settings)/organizations/teams/other/page.ts deleted file mode 100644 index ffaf5302cb166b..00000000000000 --- a/apps/web/app/future/settings/(settings)/organizations/teams/other/page.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { _generateMetadata } from "app/_utils"; - -import Page from "@calcom/features/ee/organizations/pages/settings/other-team-listing-view"; - -export const generateMetadata = async () => - await _generateMetadata( - (t) => t("org_admin_other_teams"), - (t) => t("org_admin_other_teams_description") - ); - -export default Page; diff --git a/apps/web/app/future/settings/(settings)/organizations/teams/other/page.tsx b/apps/web/app/future/settings/(settings)/organizations/teams/other/page.tsx new file mode 100644 index 00000000000000..b68b2199cd27cf --- /dev/null +++ b/apps/web/app/future/settings/(settings)/organizations/teams/other/page.tsx @@ -0,0 +1,24 @@ +import { _generateMetadata, getFixedT } from "app/_utils"; + +import { getServerSessionForAppDir } from "@calcom/features/auth/lib/get-server-session-for-app-dir"; +import LegacyPage from "@calcom/features/ee/organizations/pages/settings/other-team-listing-view"; +import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; + +export const generateMetadata = async () => + await _generateMetadata( + (t) => t("org_admin_other_teams"), + (t) => t("org_admin_other_teams_description") + ); + +const Page = async () => { + const session = await getServerSessionForAppDir(); + const t = await getFixedT(session?.user.locale || "en"); + + return ( + + + + ); +}; + +export default Page; diff --git a/apps/web/app/future/settings/(settings)/teams/[id]/appearance/page.tsx b/apps/web/app/future/settings/(settings)/teams/[id]/appearance/page.tsx index ac76104d07a5f0..25f31d3027b6d4 100644 --- a/apps/web/app/future/settings/(settings)/teams/[id]/appearance/page.tsx +++ b/apps/web/app/future/settings/(settings)/teams/[id]/appearance/page.tsx @@ -1,6 +1,8 @@ -import { _generateMetadata } from "app/_utils"; +import { _generateMetadata, getFixedT } from "app/_utils"; -import Page from "@calcom/features/ee/teams/pages/team-appearance-view"; +import { getServerSessionForAppDir } from "@calcom/features/auth/lib/get-server-session-for-app-dir"; +import LegacyPage from "@calcom/features/ee/teams/pages/team-appearance-view"; +import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; export const generateMetadata = async () => await _generateMetadata( @@ -8,4 +10,18 @@ export const generateMetadata = async () => (t) => t("appearance_team_description") ); +const Page = async () => { + const session = await getServerSessionForAppDir(); + const t = await getFixedT(session?.user.locale || "en"); + + return ( + + + + ); +}; + export default Page; diff --git a/apps/web/app/future/settings/(settings)/teams/[id]/billing/page.tsx b/apps/web/app/future/settings/(settings)/teams/[id]/billing/page.tsx index 34e7f004a4d0a0..e88787f0b75dd1 100644 --- a/apps/web/app/future/settings/(settings)/teams/[id]/billing/page.tsx +++ b/apps/web/app/future/settings/(settings)/teams/[id]/billing/page.tsx @@ -1,11 +1,4 @@ -import { _generateMetadata } from "app/_utils"; +import BillingPage, { generateMetadata } from "../../../billing/page"; -import Page from "~/settings/billing/billing-view"; - -export const generateMetadata = async () => - await _generateMetadata( - (t) => t("billing"), - (t) => t("team_billing_description") - ); - -export default Page; +export { generateMetadata }; +export default BillingPage; diff --git a/apps/web/app/future/settings/(settings)/teams/[id]/members/page.tsx b/apps/web/app/future/settings/(settings)/teams/[id]/members/page.tsx index 0f38c54f5975d2..711a118776de23 100644 --- a/apps/web/app/future/settings/(settings)/teams/[id]/members/page.tsx +++ b/apps/web/app/future/settings/(settings)/teams/[id]/members/page.tsx @@ -1,6 +1,8 @@ -import { _generateMetadata } from "app/_utils"; +import { _generateMetadata, getFixedT } from "app/_utils"; -import Page from "@calcom/features/ee/teams/pages/team-members-view"; +import { getServerSessionForAppDir } from "@calcom/features/auth/lib/get-server-session-for-app-dir"; +import LegacyPage from "@calcom/features/ee/teams/pages/team-members-view"; +import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; export const generateMetadata = async () => await _generateMetadata( @@ -8,4 +10,15 @@ export const generateMetadata = async () => (t) => t("members_team_description") ); +const Page = async () => { + const session = await getServerSessionForAppDir(); + const t = await getFixedT(session?.user.locale || "en"); + + return ( + + + + ); +}; + export default Page; diff --git a/apps/web/app/future/settings/(settings)/teams/[id]/profile/page.tsx b/apps/web/app/future/settings/(settings)/teams/[id]/profile/page.tsx index b2e02352ef39f2..b08a143563289d 100644 --- a/apps/web/app/future/settings/(settings)/teams/[id]/profile/page.tsx +++ b/apps/web/app/future/settings/(settings)/teams/[id]/profile/page.tsx @@ -1,6 +1,8 @@ -import { _generateMetadata } from "app/_utils"; +import { _generateMetadata, getFixedT } from "app/_utils"; -import Page from "@calcom/features/ee/teams/pages/team-profile-view"; +import { getServerSessionForAppDir } from "@calcom/features/auth/lib/get-server-session-for-app-dir"; +import LegacyPage from "@calcom/features/ee/teams/pages/team-profile-view"; +import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; export const generateMetadata = async () => await _generateMetadata( @@ -8,4 +10,18 @@ export const generateMetadata = async () => (t) => t("profile_team_description") ); +const Page = async () => { + const session = await getServerSessionForAppDir(); + const t = await getFixedT(session?.user.locale || "en"); + + return ( + + + + ); +}; + export default Page; diff --git a/apps/web/app/future/settings/(settings)/teams/page.ts b/apps/web/app/future/settings/(settings)/teams/page.ts deleted file mode 100644 index 6175f853ecda7e..00000000000000 --- a/apps/web/app/future/settings/(settings)/teams/page.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { _generateMetadata } from "app/_utils"; - -import Page from "@calcom/features/ee/teams/pages/team-listing-view"; - -export const generateMetadata = async () => - await _generateMetadata( - (t) => t("teams"), - (t) => t("create_manage_teams_collaborative") - ); - -export default Page; diff --git a/apps/web/app/future/settings/(settings)/teams/page.tsx b/apps/web/app/future/settings/(settings)/teams/page.tsx new file mode 100644 index 00000000000000..6aa5cd10734c4a --- /dev/null +++ b/apps/web/app/future/settings/(settings)/teams/page.tsx @@ -0,0 +1,24 @@ +import { _generateMetadata, getFixedT } from "app/_utils"; + +import { getServerSessionForAppDir } from "@calcom/features/auth/lib/get-server-session-for-app-dir"; +import LegacyPage from "@calcom/features/ee/teams/pages/team-listing-view"; +import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; + +export const generateMetadata = async () => + await _generateMetadata( + (t) => t("teams"), + (t) => t("create_manage_teams_collaborative") + ); + +const Page = async () => { + const session = await getServerSessionForAppDir(); + const t = await getFixedT(session?.user.locale || "en"); + + return ( + + + + ); +}; + +export default Page; diff --git a/apps/web/app/future/settings/organizations/[id]/about/page.tsx b/apps/web/app/future/settings/organizations/[id]/about/page.tsx index 42fca898e47d46..cb899d1e0b6a62 100644 --- a/apps/web/app/future/settings/organizations/[id]/about/page.tsx +++ b/apps/web/app/future/settings/organizations/[id]/about/page.tsx @@ -1,6 +1,9 @@ +import { withAppDirSsr } from "app/WithAppDirSsr"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; +import { getServerSideProps } from "@calcom/features/ee/organizations/pages/organization"; + import LegacyPage, { LayoutWrapper } from "~/settings/organizations/[id]/about-view"; export const generateMetadata = async () => @@ -9,7 +12,10 @@ export const generateMetadata = async () => (t) => t("about_your_organization_description") ); +const getData = withAppDirSsr(getServerSideProps); + export default WithLayout({ Page: LegacyPage, getLayout: LayoutWrapper, + getData, }); diff --git a/apps/web/app/future/settings/organizations/[id]/add-teams/page.tsx b/apps/web/app/future/settings/organizations/[id]/add-teams/page.tsx index 0fd0d5d11b8809..86c8f3296295a8 100644 --- a/apps/web/app/future/settings/organizations/[id]/add-teams/page.tsx +++ b/apps/web/app/future/settings/organizations/[id]/add-teams/page.tsx @@ -1,6 +1,9 @@ +import { withAppDirSsr } from "app/WithAppDirSsr"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; +import { getServerSideProps } from "@calcom/features/ee/organizations/pages/organization"; + import LegacyPage, { LayoutWrapper } from "~/settings/organizations/[id]/add-teams-view"; export const generateMetadata = async () => @@ -9,7 +12,10 @@ export const generateMetadata = async () => (t) => t("create_your_teams_description") ); +const getData = withAppDirSsr(getServerSideProps); + export default WithLayout({ Page: LegacyPage, getLayout: LayoutWrapper, + getData, }); diff --git a/apps/web/app/future/settings/organizations/[id]/onboard-members/page.tsx b/apps/web/app/future/settings/organizations/[id]/onboard-members/page.tsx index 113cd399bec7f2..56be6482e10470 100644 --- a/apps/web/app/future/settings/organizations/[id]/onboard-members/page.tsx +++ b/apps/web/app/future/settings/organizations/[id]/onboard-members/page.tsx @@ -1,6 +1,9 @@ +import { withAppDirSsr } from "app/WithAppDirSsr"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; +import { getServerSideProps } from "@calcom/features/ee/organizations/pages/organization"; + import LegacyPage, { LayoutWrapper } from "~/settings/organizations/[id]/onboard-members-view"; export const generateMetadata = async () => @@ -9,7 +12,10 @@ export const generateMetadata = async () => (t) => t("invite_organization_admins_description") ); +const getData = withAppDirSsr(getServerSideProps); + export default WithLayout({ Page: LegacyPage, getLayout: LayoutWrapper, + getData, }); diff --git a/apps/web/app/future/settings/(settings)/organizations/appearance/page.tsx b/apps/web/app/future/settings/organizations/appearance/page.tsx similarity index 77% rename from apps/web/app/future/settings/(settings)/organizations/appearance/page.tsx rename to apps/web/app/future/settings/organizations/appearance/page.tsx index 5f8b2547f0a43e..84acb6502b503c 100644 --- a/apps/web/app/future/settings/(settings)/organizations/appearance/page.tsx +++ b/apps/web/app/future/settings/organizations/appearance/page.tsx @@ -1,4 +1,5 @@ import { _generateMetadata } from "app/_utils"; +import { WithLayout } from "app/layoutHOC"; import Page from "@calcom/features/ee/organizations/pages/settings/appearance"; @@ -8,4 +9,4 @@ export const generateMetadata = async () => (t) => t("appearance_org_description") ); -export default Page; +export default WithLayout({ Page }); diff --git a/apps/web/app/future/settings/(settings)/teams/[id]/event-type/page.tsx b/apps/web/app/future/settings/teams/[id]/event-type/page.tsx similarity index 59% rename from apps/web/app/future/settings/(settings)/teams/[id]/event-type/page.tsx rename to apps/web/app/future/settings/teams/[id]/event-type/page.tsx index c741324ceb8596..47e5b8b8c12a6a 100644 --- a/apps/web/app/future/settings/(settings)/teams/[id]/event-type/page.tsx +++ b/apps/web/app/future/settings/teams/[id]/event-type/page.tsx @@ -1,11 +1,12 @@ -import LegacyPage, { GetLayout } from "@pages/settings/teams/[id]/event-type"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; +import CreateTeamEventType, { GetLayout } from "~/settings/teams/[id]/event-types-view"; + export const generateMetadata = async () => await _generateMetadata( (t) => t("add_new_team_event_type"), (t) => t("new_event_type_to_book_description") ); -export default WithLayout({ Page: LegacyPage, getLayout: GetLayout })<"P">; +export default WithLayout({ Page: CreateTeamEventType, getLayout: GetLayout })<"P">; diff --git a/apps/web/app/future/settings/(settings)/teams/[id]/onboard-members/page.tsx b/apps/web/app/future/settings/teams/[id]/onboard-members/page.tsx similarity index 58% rename from apps/web/app/future/settings/(settings)/teams/[id]/onboard-members/page.tsx rename to apps/web/app/future/settings/teams/[id]/onboard-members/page.tsx index adb33f8b63788d..cf2117b4ad0ef7 100644 --- a/apps/web/app/future/settings/(settings)/teams/[id]/onboard-members/page.tsx +++ b/apps/web/app/future/settings/teams/[id]/onboard-members/page.tsx @@ -1,11 +1,12 @@ -import LegacyPage, { GetLayout } from "@pages/settings/teams/[id]/onboard-members"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; +import AddNewTeamMembers, { GetLayout } from "~/settings/teams/[id]/onboard-members-view"; + export const generateMetadata = async () => await _generateMetadata( (t) => t("add_team_members"), (t) => t("add_team_members_description") ); -export default WithLayout({ Page: LegacyPage, getLayout: GetLayout })<"P">; +export default WithLayout({ Page: AddNewTeamMembers, getLayout: GetLayout })<"P">; diff --git a/apps/web/app/future/settings/(settings)/teams/new/page.tsx b/apps/web/app/future/settings/teams/new/page.tsx similarity index 57% rename from apps/web/app/future/settings/(settings)/teams/new/page.tsx rename to apps/web/app/future/settings/teams/new/page.tsx index 592517ab488227..160d6559d102c6 100644 --- a/apps/web/app/future/settings/(settings)/teams/new/page.tsx +++ b/apps/web/app/future/settings/teams/new/page.tsx @@ -1,11 +1,12 @@ -import LegacyPage, { LayoutWrapper } from "@pages/settings/teams/new/index"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; +import CreateNewTeamView, { LayoutWrapper } from "~/settings/teams/new/create-new-team-view"; + export const generateMetadata = async () => await _generateMetadata( (t) => t("create_new_team"), (t) => t("create_new_team_description") ); -export default WithLayout({ Page: LegacyPage, getLayout: LayoutWrapper })<"P">; +export default WithLayout({ Page: CreateNewTeamView, getLayout: LayoutWrapper })<"P">; diff --git a/apps/web/app/future/signup/page.tsx b/apps/web/app/future/signup/page.tsx index d24988d1d855bf..ff1a9cd80a78c8 100644 --- a/apps/web/app/future/signup/page.tsx +++ b/apps/web/app/future/signup/page.tsx @@ -1,10 +1,12 @@ -import LegacyPage, { type SignupProps } from "@pages/signup"; import { withAppDirSsr } from "app/WithAppDirSsr"; import { _generateMetadata } from "app/_utils"; import { WithLayout } from "app/layoutHOC"; import { getServerSideProps } from "@lib/signup/getServerSideProps"; +import type { SignupProps } from "~/signup-view"; +import Signup from "~/signup-view"; + export const generateMetadata = async () => await _generateMetadata( (t) => t("sign_up"), @@ -14,7 +16,7 @@ export const generateMetadata = async () => const getData = withAppDirSsr(getServerSideProps); export default WithLayout({ - Page: LegacyPage, + Page: Signup, getLayout: null, getData, })<"P">; diff --git a/apps/web/app/layoutHOC.tsx b/apps/web/app/layoutHOC.tsx index cd7070b270880c..8f71154e316424 100644 --- a/apps/web/app/layoutHOC.tsx +++ b/apps/web/app/layoutHOC.tsx @@ -51,6 +51,7 @@ export function WithLayout>({ requiresLicense={requiresLicense || !!(Page && "requiresLicense" in Page && Page.requiresLicense)} nonce={nonce} themeBasis={null} + isThemeSupported={!!(Page && "isThemeSupported" in Page && Page.isThemeSupported)} isBookingPage={isBookingPage || !!(Page && "isBookingPage" in Page && Page.isBookingPage)} {...props}> {pageWithServerLayout} diff --git a/apps/web/components/apps/AppPage.tsx b/apps/web/components/apps/AppPage.tsx index ac556f7045ca92..8294a0dff77118 100644 --- a/apps/web/components/apps/AppPage.tsx +++ b/apps/web/components/apps/AppPage.tsx @@ -177,7 +177,7 @@ export const AppPage = ({ typeof descriptionItem === "object" ? (
+ className="mr-4 max-h-full min-h-[315px] min-w-[90%] max-w-full snap-center overflow-hidden rounded-md last:mb-0 lg:mb-4 lg:mr-0 [&_iframe]:h-full [&_iframe]:min-h-[315px] [&_iframe]:w-full"> `, + }} + /> + + )} + + + ) : null} +
+
+ + {/* Left side */} +
+
+

+ {IS_CALCOM ? t("create_your_calcom_account") : t("create_your_account")} +

+ {IS_CALCOM ? ( +

{t("cal_signup_description")}

+ ) : ( +

+ {t("calcom_explained", { + appName: APP_NAME, + })} +

+ )} +
+ {/* Form Container */} +
+
{ + let updatedValues = values; + if (!formMethods.getValues().username && isOrgInviteByLink && orgAutoAcceptEmail) { + updatedValues = { + ...values, + username: getOrgUsernameFromEmail(values.email, orgAutoAcceptEmail), + }; + } + await signUp(updatedValues); + }}> + {/* Username */} + {!isOrgInviteByLink ? ( + setUsernameTaken(value)} + data-testid="signup-usernamefield" + setPremium={(value) => setPremiumUsername(value)} + addOnLeading={ + orgSlug + ? `${getOrgFullOrigin(orgSlug, { protocol: true }).replace(URL_PROTOCOL_REGEX, "")}/` + : `${process.env.NEXT_PUBLIC_WEBSITE_URL.replace(URL_PROTOCOL_REGEX, "")}/` + } + /> + ) : null} + {/* Email */} + + + {/* Password */} + + {/* Cloudflare Turnstile Captcha */} + {CLOUDFLARE_SITE_ID ? ( + { + formMethods.setValue("cfToken", token); + }} + /> + ) : null} + + handleConsentChange(COOKIE_CONSENT)} + description={t("cookie_consent_checkbox")} + /> + {errors.apiError && ( + + )} + + + {!isGoogleLoginEnabled && !isSAMLLoginEnabled ? null : ( +
+
+
+ + {t("or_continue_with")} + +
+
+
+ )} +
+ {isGoogleLoginEnabled ? ( + + ) : null} + {isSAMLLoginEnabled ? ( + + ) : null} +
+
+ {/* Already have an account & T&C */} +
+
+
+

{t("already_have_account")}

+ + {t("sign_in")} + +
+
+ + Terms + , + + Privacy Policy. + , + ]} + /> +
+
+
+
+
+ {IS_CALCOM && ( + <> +
+
+ Cal.com was Product of the Day at ProductHunt +
+
+ Cal.com was Product of the Week at ProductHunt +
+
+ Cal.com was Product of the Month at ProductHunt +
+
+
+
+ ProductHunt Rating of 5 Stars +
+
+ Google Reviews Rating of 4.7 Stars +
+
+ G2 Rating of 4.7 Stars +
+
+ + )} +
+ Cal.com Booking Page + Cal.com Booking Page +
+
+ {FEATURES.map((feature) => ( + <> +
+
+ + {t(feature.title)} +
+
+

+ {t( + feature.description, + feature.i18nOptions && { + ...feature.i18nOptions, + } + )} +

+
+
+ + ))} +
+
+
+ +
+ + ); +} diff --git a/apps/web/package.json b/apps/web/package.json index 2ea2345dd84a51..ea4583360ccbca 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@calcom/web", - "version": "4.5.0", + "version": "4.5.3", "private": true, "scripts": { "analyze": "ANALYZE=true next build", diff --git a/apps/web/pages/auth/setup/index.tsx b/apps/web/pages/auth/setup/index.tsx index 1b7b3826b93297..a611c1bd375861 100644 --- a/apps/web/pages/auth/setup/index.tsx +++ b/apps/web/pages/auth/setup/index.tsx @@ -1,152 +1,22 @@ "use client"; -import { usePathname, useRouter } from "next/navigation"; -import { useState } from "react"; - -import AdminAppsList from "@calcom/features/apps/AdminAppsList"; -import { APP_NAME } from "@calcom/lib/constants"; -import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useLocale } from "@calcom/lib/hooks/useLocale"; -import type { inferSSRProps } from "@calcom/types/inferSSRProps"; -import { Meta, WizardForm } from "@calcom/ui"; - -import PageWrapper from "@components/PageWrapper"; -import { AdminUserContainer as AdminUser } from "@components/setup/AdminUser"; -import ChooseLicense from "@components/setup/ChooseLicense"; -import EnterpriseLicense from "@components/setup/EnterpriseLicense"; +import { Meta } from "@calcom/ui"; import { getServerSideProps } from "@server/lib/setup/getServerSideProps"; -function useSetStep() { - const router = useRouter(); - const searchParams = useCompatSearchParams(); - const pathname = usePathname(); - const setStep = (newStep = 1) => { - const _searchParams = new URLSearchParams(searchParams ?? undefined); - _searchParams.set("step", newStep.toString()); - router.replace(`${pathname}?${_searchParams.toString()}`); - }; - return setStep; -} +import type { PageProps } from "~/auth/setup-view"; +import Setup from "~/auth/setup-view"; -export function Setup(props: inferSSRProps) { +const Page = (props: PageProps) => { const { t } = useLocale(); - const router = useRouter(); - const [value, setValue] = useState(props.isFreeLicense ? "FREE" : "EE"); - const isFreeLicense = value === "FREE"; - const [isEnabledEE, setIsEnabledEE] = useState(!props.isFreeLicense); - const setStep = useSetStep(); - - const steps: React.ComponentProps["steps"] = [ - { - title: t("administrator_user"), - description: t("lets_create_first_administrator_user"), - content: (setIsPending) => ( - { - setIsPending(true); - }} - onSuccess={() => { - setStep(2); - }} - onError={() => { - setIsPending(false); - }} - userCount={props.userCount} - /> - ), - }, - { - title: t("choose_a_license"), - description: t("choose_license_description"), - content: (setIsPending) => { - return ( - { - setIsPending(true); - setStep(3); - }} - /> - ); - }, - }, - ]; - - if (!isFreeLicense) { - steps.push({ - title: t("step_enterprise_license"), - description: t("step_enterprise_license_description"), - content: (setIsPending) => { - const currentStep = 3; - return ( - { - setIsPending(true); - }} - onSuccess={() => { - setStep(currentStep + 1); - }} - onSuccessValidate={() => { - setIsEnabledEE(true); - }} - /> - ); - }, - isEnabled: isEnabledEE, - }); - } - - steps.push({ - title: t("enable_apps"), - description: t("enable_apps_description", { appName: APP_NAME }), - contentClassname: "!pb-0 mb-[-1px]", - content: (setIsPending) => { - const currentStep = isFreeLicense ? 3 : 4; - return ( - { - setIsPending(true); - router.replace("/"); - }} - /> - ); - }, - }); - return ( <> -
- t("current_step_of_total", { currentStep, maxSteps })} - /> -
+ ); -} - -Setup.isThemeSupported = false; -Setup.PageWrapper = PageWrapper; -export default Setup; +}; +export default Page; export { getServerSideProps }; diff --git a/apps/web/pages/auth/signin.tsx b/apps/web/pages/auth/signin.tsx index 86a26a60cd34ab..7ce8e789e85cda 100644 --- a/apps/web/pages/auth/signin.tsx +++ b/apps/web/pages/auth/signin.tsx @@ -1,34 +1,14 @@ -"use client"; - -import type { getProviders } from "next-auth/react"; -import { signIn } from "next-auth/react"; - -import { Button } from "@calcom/ui"; - import PageWrapper from "@components/PageWrapper"; import { getServerSideProps } from "@server/lib/auth/signin/getServerSideProps"; -function signin({ providers }: { providers: Awaited> }) { - if (!providers) { - return null; - } +import type { PageProps } from "~/auth/signin-view"; +import SignIn from "~/auth/signin-view"; - return ( -
- {Object.values(providers).map((provider) => { - return ( -
- -
- ); - })} -
- ); -} +const Page = (props: PageProps) => ; -signin.PageWrapper = PageWrapper; +Page.PageWrapper = PageWrapper; -export default signin; +export default Page; export { getServerSideProps }; diff --git a/apps/web/pages/auth/sso/[provider].tsx b/apps/web/pages/auth/sso/[provider].tsx index 5ea01f8dc9b46f..8671d02292fde4 100644 --- a/apps/web/pages/auth/sso/[provider].tsx +++ b/apps/web/pages/auth/sso/[provider].tsx @@ -1,47 +1,12 @@ -"use client"; - -import { signIn } from "next-auth/react"; -import { useRouter } from "next/navigation"; -import { useEffect } from "react"; - -import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; - -import type { inferSSRProps } from "@lib/types/inferSSRProps"; - import PageWrapper from "@components/PageWrapper"; import { getServerSideProps } from "@server/lib/auth/sso/[provider]/getServerSideProps"; -export type SSOProviderPageProps = inferSSRProps; - -export default function Provider(props: SSOProviderPageProps) { - const searchParams = useCompatSearchParams(); - const router = useRouter(); - - useEffect(() => { - const email = searchParams?.get("email"); - if (props.provider === "saml") { - if (!email) { - router.push(`/auth/error?error=Email not provided`); - return; - } - - if (!props.isSAMLLoginEnabled) { - router.push(`/auth/error?error=SAML login not enabled`); - return; - } - - signIn("saml", {}, { tenant: props.tenant, product: props.product }); - } else if (props.provider === "google" && email) { - signIn("google", {}, { login_hint: email }); - } else { - signIn(props.provider); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - return null; -} +import type { SSOProviderPageProps } from "~/auth/sso/provider-view"; +import SSOProviderView from "~/auth/sso/provider-view"; -Provider.PageWrapper = PageWrapper; +const Page = (props: SSOProviderPageProps) => ; +Page.PageWrapper = PageWrapper; +export default Page; export { getServerSideProps }; diff --git a/apps/web/pages/auth/sso/direct.tsx b/apps/web/pages/auth/sso/direct.tsx index b616c592503c5b..c5b0f1aeecea77 100644 --- a/apps/web/pages/auth/sso/direct.tsx +++ b/apps/web/pages/auth/sso/direct.tsx @@ -1,44 +1,12 @@ -"use client"; - -import { signIn } from "next-auth/react"; -import { useRouter } from "next/navigation"; -import { useEffect } from "react"; - -import { HOSTED_CAL_FEATURES } from "@calcom/lib/constants"; - -import type { inferSSRProps } from "@lib/types/inferSSRProps"; - import PageWrapper from "@components/PageWrapper"; -import type { getServerSideProps } from "@server/lib/auth/sso/direct/getServerSideProps"; - -// This page is used to initiate the SAML authentication flow by redirecting to the SAML provider. -// Accessible only on self-hosted Cal.com instances. -export default function Page({ samlTenantID, samlProductID }: inferSSRProps) { - const router = useRouter(); +import { getServerSideProps } from "@server/lib/auth/sso/direct/getServerSideProps"; - useEffect(() => { - if (HOSTED_CAL_FEATURES) { - router.push("/auth/login"); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); +import type { SSODirectPageProps } from "~/auth/sso/direct-view"; +import SSODirectView from "~/auth/sso/direct-view"; - useEffect(() => { - // Initiate SAML authentication flow - signIn( - "saml", - { - callbackUrl: "/", - }, - { tenant: samlTenantID, product: samlProductID } - ); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return null; -} - -export { getServerSideProps }; +const Page = (props: SSODirectPageProps) => ; Page.PageWrapper = PageWrapper; +export default Page; +export { getServerSideProps }; diff --git a/apps/web/pages/d/[link]/[slug].tsx b/apps/web/pages/d/[link]/[slug].tsx index 20233baca17d87..1a9e3cf1369185 100644 --- a/apps/web/pages/d/[link]/[slug].tsx +++ b/apps/web/pages/d/[link]/[slug].tsx @@ -1,48 +1,11 @@ -"use client"; - -import { Booker } from "@calcom/atoms/monorepo"; -import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses"; -import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo"; - import { getServerSideProps, type PageProps } from "@lib/d/[link]/[slug]/getServerSideProps"; import PageWrapper from "@components/PageWrapper"; -export default function Type({ - slug, - isEmbed, - user, - booking, - isBrandingHidden, - isTeamEvent, - entity, - duration, - hashedLink, -}: PageProps) { - return ( -
- - -
- ); -} +import Type from "~/d/[link]/d-type-view"; +const Page = (props: PageProps) => ; export { getServerSideProps }; -Type.PageWrapper = PageWrapper; -Type.isBookingPage = true; +Page.PageWrapper = PageWrapper; +export default Page; diff --git a/apps/web/pages/insights/index.tsx b/apps/web/pages/insights/index.tsx index c98b0411e64c68..fa860836ac57b5 100644 --- a/apps/web/pages/insights/index.tsx +++ b/apps/web/pages/insights/index.tsx @@ -1,123 +1,13 @@ "use client"; -import { - AverageEventDurationChart, - BookingKPICards, - BookingStatusLineChart, - LeastBookedTeamMembersTable, - MostBookedTeamMembersTable, - PopularEventsTable, - HighestNoShowHostTable, - RecentFeedbackTable, - HighestRatedMembersTable, - LowestRatedMembersTable, -} from "@calcom/features/insights/components"; -import { FiltersProvider } from "@calcom/features/insights/context/FiltersProvider"; -import { Filters } from "@calcom/features/insights/filters"; -import Shell from "@calcom/features/shell/Shell"; -import { UpgradeTip } from "@calcom/features/tips"; -import { WEBAPP_URL } from "@calcom/lib/constants"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { trpc } from "@calcom/trpc"; -import { Button, ButtonGroup } from "@calcom/ui"; -import { Icon } from "@calcom/ui"; - import { getServerSideProps } from "@lib/insights/getServerSideProps"; import PageWrapper from "@components/PageWrapper"; -export default function InsightsPage() { - const { t } = useLocale(); - const { data: user } = trpc.viewer.me.useQuery(); - - const features = [ - { - icon: , - title: t("view_bookings_across"), - description: t("view_bookings_across_description"), - }, - { - icon: , - title: t("identify_booking_trends"), - description: t("identify_booking_trends_description"), - }, - { - icon: , - title: t("spot_popular_event_types"), - description: t("spot_popular_event_types_description"), - }, - ]; - - return ( -
- - - - - - -
- }> - {!user ? ( - <> - ) : ( - - - -
- - - - -
- - - -
-
- - -
- -
- - - -
- - {t("looking_for_more_insights")}{" "} - - {" "} - {t("contact_support")} - - -
-
- )} - - -
- ); -} - -InsightsPage.PageWrapper = PageWrapper; +import type { PageProps } from "~/insights/insights-view"; +import InsightsPage from "~/insights/insights-view"; +const Page = (props: PageProps) => ; +Page.PageWrapper = PageWrapper; +export default Page; export { getServerSideProps }; diff --git a/apps/web/pages/reschedule/[uid].tsx b/apps/web/pages/reschedule/[uid].tsx index 371afa924d3d36..1105adaa4cab41 100644 --- a/apps/web/pages/reschedule/[uid].tsx +++ b/apps/web/pages/reschedule/[uid].tsx @@ -1,185 +1,6 @@ -// page can be a server component -import type { GetServerSidePropsContext } from "next"; -import { URLSearchParams } from "url"; -import { z } from "zod"; - -import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; -import { buildEventUrlFromBooking } from "@calcom/lib/bookings/buildEventUrlFromBooking"; -import { getDefaultEvent } from "@calcom/lib/defaultEvents"; -import { maybeGetBookingUidFromSeat } from "@calcom/lib/server/maybeGetBookingUidFromSeat"; -import { UserRepository } from "@calcom/lib/server/repository/user"; -import prisma, { bookingMinimalSelect } from "@calcom/prisma"; -import { BookingStatus } from "@calcom/prisma/client"; - export default function Type() { // Just redirect to the schedule page to reschedule it. return null; } -const querySchema = z.object({ - uid: z.string(), - seatReferenceUid: z.string().optional(), - rescheduledBy: z.string().optional(), - allowRescheduleForCancelledBooking: z - .string() - .transform((value) => value === "true") - .optional(), -}); - -export async function getServerSideProps(context: GetServerSidePropsContext) { - const session = await getServerSession(context); - - const { - uid: bookingUid, - seatReferenceUid, - rescheduledBy, - /** - * This is for the case of request-reschedule where the booking is cancelled - */ - allowRescheduleForCancelledBooking, - } = querySchema.parse(context.query); - - const coepFlag = context.query["flag.coep"]; - const { uid, seatReferenceUid: maybeSeatReferenceUid } = await maybeGetBookingUidFromSeat( - prisma, - bookingUid - ); - - const booking = await prisma.booking.findUnique({ - where: { - uid, - }, - select: { - ...bookingMinimalSelect, - eventType: { - select: { - users: { - select: { - username: true, - }, - }, - slug: true, - team: { - select: { - parentId: true, - slug: true, - }, - }, - seatsPerTimeSlot: true, - userId: true, - owner: { - select: { - id: true, - }, - }, - hosts: { - select: { - user: { - select: { - id: true, - }, - }, - }, - }, - }, - }, - dynamicEventSlugRef: true, - dynamicGroupSlugRef: true, - user: true, - status: true, - }, - }); - const dynamicEventSlugRef = booking?.dynamicEventSlugRef || ""; - - if (!booking) { - return { - notFound: true, - } as const; - } - - // If booking is already CANCELLED or REJECTED, we can't reschedule this booking. Take the user to the booking page which would show it's correct status and other details. - // A booking that has been rescheduled to a new booking will also have a status of CANCELLED - if ( - !allowRescheduleForCancelledBooking && - (booking.status === BookingStatus.CANCELLED || booking.status === BookingStatus.REJECTED) - ) { - return { - redirect: { - destination: `/booking/${uid}`, - permanent: false, - }, - }; - } - - if (!booking?.eventType && !booking?.dynamicEventSlugRef) { - // TODO: Show something in UI to let user know that this booking is not rescheduleable - return { - notFound: true, - } as { - notFound: true; - }; - } - - // if booking event type is for a seated event and no seat reference uid is provided, throw not found - if (booking?.eventType?.seatsPerTimeSlot && !maybeSeatReferenceUid) { - const userId = session?.user?.id; - - if (!userId && !seatReferenceUid) { - return { - redirect: { - destination: `/auth/login?callbackUrl=/reschedule/${bookingUid}`, - permanent: false, - }, - }; - } - const userIsHost = booking?.eventType.hosts.find((host) => { - if (host.user.id === userId) return true; - }); - - const userIsOwnerOfEventType = booking?.eventType.owner?.id === userId; - - if (!userIsHost && !userIsOwnerOfEventType) { - return { - notFound: true, - } as { - notFound: true; - }; - } - } - - const eventType = booking.eventType ? booking.eventType : getDefaultEvent(dynamicEventSlugRef); - - const enrichedBookingUser = booking.user - ? await UserRepository.enrichUserWithItsProfile({ user: booking.user }) - : null; - - const eventUrl = await buildEventUrlFromBooking({ - eventType, - dynamicGroupSlugRef: booking.dynamicGroupSlugRef ?? null, - profileEnrichedBookingUser: enrichedBookingUser, - }); - - const destinationUrlSearchParams = new URLSearchParams(); - - destinationUrlSearchParams.set("rescheduleUid", seatReferenceUid || bookingUid); - - // TODO: I think we should just forward all the query params here including coep flag - if (coepFlag) { - destinationUrlSearchParams.set("flag.coep", coepFlag as string); - } - - const currentUserEmail = rescheduledBy ?? session?.user?.email; - - if (currentUserEmail) { - destinationUrlSearchParams.set("rescheduledBy", currentUserEmail); - } - - return { - redirect: { - destination: `${eventUrl}?${destinationUrlSearchParams.toString()}${ - eventType.seatsPerTimeSlot ? "&bookingUid=null" : "" - }`, - permanent: false, - }, - }; -} +export { getServerSideProps } from "@lib/reschedule/[uid]/getServerSideProps"; diff --git a/apps/web/pages/reschedule/[uid]/embed.tsx b/apps/web/pages/reschedule/[uid]/embed.tsx index 034b8ee719dc0d..5d6b405e57085f 100644 --- a/apps/web/pages/reschedule/[uid]/embed.tsx +++ b/apps/web/pages/reschedule/[uid]/embed.tsx @@ -1,5 +1,3 @@ -"use client"; - import withEmbedSsr from "@lib/withEmbedSsr"; import { getServerSideProps as _getServerSideProps } from "../[uid]"; diff --git a/apps/web/pages/settings/organizations/billing.tsx b/apps/web/pages/settings/organizations/billing.tsx index d830b3f3ac597b..d44849eea252a1 100644 --- a/apps/web/pages/settings/organizations/billing.tsx +++ b/apps/web/pages/settings/organizations/billing.tsx @@ -1,9 +1,3 @@ -import type { CalPageWrapper } from "@components/PageWrapper"; -import PageWrapper from "@components/PageWrapper"; - import BillingPage from "../../settings/billing/index"; -const Page = BillingPage as CalPageWrapper; -Page.PageWrapper = PageWrapper; - -export default Page; +export default BillingPage; diff --git a/apps/web/pages/settings/organizations/general.tsx b/apps/web/pages/settings/organizations/general.tsx index 308eecf72ff6a1..e5d5cefb108785 100644 --- a/apps/web/pages/settings/organizations/general.tsx +++ b/apps/web/pages/settings/organizations/general.tsx @@ -1,9 +1,11 @@ import OrgGeneralView from "@calcom/features/ee/organizations/pages/settings/general"; +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; -import type { CalPageWrapper } from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper"; -const Page = OrgGeneralView as CalPageWrapper; +const Page = () => ; + +Page.getLayout = getLayout; Page.PageWrapper = PageWrapper; export default Page; diff --git a/apps/web/pages/settings/organizations/members.tsx b/apps/web/pages/settings/organizations/members.tsx index a175173e5d1a85..34061d4a8c4331 100644 --- a/apps/web/pages/settings/organizations/members.tsx +++ b/apps/web/pages/settings/organizations/members.tsx @@ -1,6 +1,6 @@ import MembersView from "@calcom/features/ee/organizations/pages/settings/members"; +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; -import type { CalPageWrapper } from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper"; export { @@ -8,7 +8,9 @@ export { type PageProps, } from "@calcom/features/ee/organizations/pages/settings/getServerSidePropsMembers"; -const Page = MembersView as unknown as CalPageWrapper; +const Page = () => ; + +Page.getLayout = getLayout; Page.PageWrapper = PageWrapper; export default Page; diff --git a/apps/web/pages/settings/organizations/profile.tsx b/apps/web/pages/settings/organizations/profile.tsx index ef14c3b95711a6..e6bc8e8c1a0a95 100644 --- a/apps/web/pages/settings/organizations/profile.tsx +++ b/apps/web/pages/settings/organizations/profile.tsx @@ -1,9 +1,11 @@ import OrgProfileView from "@calcom/features/ee/organizations/pages/settings/profile"; +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; -import type { CalPageWrapper } from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper"; -const Page = OrgProfileView as CalPageWrapper; +const Page = () => ; + +Page.getLayout = getLayout; Page.PageWrapper = PageWrapper; export default Page; diff --git a/apps/web/pages/settings/organizations/sso.tsx b/apps/web/pages/settings/organizations/sso.tsx index 5b3b506c42e0b2..96f87e2077f5e9 100644 --- a/apps/web/pages/settings/organizations/sso.tsx +++ b/apps/web/pages/settings/organizations/sso.tsx @@ -1,9 +1,11 @@ import OrgSSOView from "@calcom/features/ee/sso/page/orgs-sso-view"; +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; -import type { CalPageWrapper } from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper"; -const Page = OrgSSOView as CalPageWrapper; +const Page = () => ; + +Page.getLayout = getLayout; Page.PageWrapper = PageWrapper; export default Page; diff --git a/apps/web/pages/settings/organizations/teams/other/[id]/appearance.tsx b/apps/web/pages/settings/organizations/teams/other/[id]/appearance.tsx index 6c6343e79198ad..ea0d5d2cc0fd83 100644 --- a/apps/web/pages/settings/organizations/teams/other/[id]/appearance.tsx +++ b/apps/web/pages/settings/organizations/teams/other/[id]/appearance.tsx @@ -1,9 +1,11 @@ import TeamAppearenceView from "@calcom/features/ee/teams/pages/team-appearance-view"; +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; -import type { CalPageWrapper } from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper"; -const Page = TeamAppearenceView as CalPageWrapper; +const Page = () => ; + +Page.getLayout = getLayout; Page.PageWrapper = PageWrapper; export default Page; diff --git a/apps/web/pages/settings/organizations/teams/other/[id]/members.tsx b/apps/web/pages/settings/organizations/teams/other/[id]/members.tsx index 14c0026a467879..fb4357175d1440 100644 --- a/apps/web/pages/settings/organizations/teams/other/[id]/members.tsx +++ b/apps/web/pages/settings/organizations/teams/other/[id]/members.tsx @@ -1,9 +1,11 @@ import TeamMembersView from "@calcom/features/ee/organizations/pages/settings/other-team-members-view"; +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; -import type { CalPageWrapper } from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper"; -const Page = TeamMembersView as CalPageWrapper; +const Page = () => ; + +Page.getLayout = getLayout; Page.PageWrapper = PageWrapper; export default Page; diff --git a/apps/web/pages/settings/organizations/teams/other/[id]/profile.tsx b/apps/web/pages/settings/organizations/teams/other/[id]/profile.tsx index ad4cba98e58a5a..c24a0104b0f9f6 100644 --- a/apps/web/pages/settings/organizations/teams/other/[id]/profile.tsx +++ b/apps/web/pages/settings/organizations/teams/other/[id]/profile.tsx @@ -1,9 +1,11 @@ import OtherTeamProfileView from "@calcom/features/ee/organizations/pages/settings/other-team-profile-view"; +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; -import type { CalPageWrapper } from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper"; -const Page = OtherTeamProfileView as CalPageWrapper; +const Page = () => ; + +Page.getLayout = getLayout; Page.PageWrapper = PageWrapper; export default Page; diff --git a/apps/web/pages/settings/organizations/teams/other/index.ts b/apps/web/pages/settings/organizations/teams/other/index.tsx similarity index 58% rename from apps/web/pages/settings/organizations/teams/other/index.ts rename to apps/web/pages/settings/organizations/teams/other/index.tsx index 06052c1f3e1fbc..84b951e9e86685 100644 --- a/apps/web/pages/settings/organizations/teams/other/index.ts +++ b/apps/web/pages/settings/organizations/teams/other/index.tsx @@ -1,9 +1,11 @@ import OtherTeamListView from "@calcom/features/ee/organizations/pages/settings/other-team-listing-view"; +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; import PageWrapper from "@components/PageWrapper"; -import type { CalPageWrapper } from "@components/PageWrapper"; -const Page = OtherTeamListView as CalPageWrapper; +const Page = () => ; + +Page.getLayout = getLayout; Page.PageWrapper = PageWrapper; export default Page; diff --git a/apps/web/pages/settings/teams/[id]/appearance.tsx b/apps/web/pages/settings/teams/[id]/appearance.tsx index 6c6343e79198ad..ea0d5d2cc0fd83 100644 --- a/apps/web/pages/settings/teams/[id]/appearance.tsx +++ b/apps/web/pages/settings/teams/[id]/appearance.tsx @@ -1,9 +1,11 @@ import TeamAppearenceView from "@calcom/features/ee/teams/pages/team-appearance-view"; +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; -import type { CalPageWrapper } from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper"; -const Page = TeamAppearenceView as CalPageWrapper; +const Page = () => ; + +Page.getLayout = getLayout; Page.PageWrapper = PageWrapper; export default Page; diff --git a/apps/web/pages/settings/teams/[id]/billing.tsx b/apps/web/pages/settings/teams/[id]/billing.tsx index baa9b4a4275683..ccd9d3c3693f48 100644 --- a/apps/web/pages/settings/teams/[id]/billing.tsx +++ b/apps/web/pages/settings/teams/[id]/billing.tsx @@ -1,9 +1,3 @@ -import type { CalPageWrapper } from "@components/PageWrapper"; -import PageWrapper from "@components/PageWrapper"; - import BillingPage from "../../billing"; -const Page = BillingPage as CalPageWrapper; -Page.PageWrapper = PageWrapper; - -export default Page; +export default BillingPage; diff --git a/apps/web/pages/settings/teams/[id]/event-type.tsx b/apps/web/pages/settings/teams/[id]/event-type.tsx index 101fa610423ddc..ec0a2b1c3bbc34 100644 --- a/apps/web/pages/settings/teams/[id]/event-type.tsx +++ b/apps/web/pages/settings/teams/[id]/event-type.tsx @@ -1,54 +1,12 @@ "use client"; import Head from "next/head"; -import { useRouter } from "next/navigation"; -import { TeamEventTypeForm } from "@calcom/features/ee/teams/components/TeamEventTypeForm"; -import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { WizardLayout } from "@calcom/ui"; -import { Button, showToast } from "@calcom/ui"; import PageWrapper from "@components/PageWrapper"; -export const CreateTeamEventType = () => { - const searchParams = useCompatSearchParams(); - const { t } = useLocale(); - const router = useRouter(); - const teamId = searchParams?.get("id") ? Number(searchParams.get("id")) : -1; - - const onSuccessMutation = () => { - router.push(`/settings/teams/${teamId}/profile`); - }; - - const onErrorMutation = (err: string) => { - showToast(err, "error"); - }; - - const SubmitButton = (isPending: boolean) => { - return ( - - ); - }; - - return ( - - ); -}; +import CreateTeamEventType, { GetLayout } from "~/settings/teams/[id]/event-types-view"; const TeamEventTypePage = () => { const { t } = useLocale(); @@ -63,23 +21,6 @@ const TeamEventTypePage = () => { ); }; -export const GetLayout = (page: React.ReactElement) => { - const router = useRouter(); - const searchParams = useCompatSearchParams(); - const teamId = searchParams?.get("id") ? Number(searchParams.get("id")) : -1; - - return ( - { - router.push(`/settings/teams/${teamId}/profile`); - }}> - {page} - - ); -}; - TeamEventTypePage.getLayout = GetLayout; TeamEventTypePage.PageWrapper = PageWrapper; diff --git a/apps/web/pages/settings/teams/[id]/members.tsx b/apps/web/pages/settings/teams/[id]/members.tsx index 5381265926a595..3a56a6025c8991 100644 --- a/apps/web/pages/settings/teams/[id]/members.tsx +++ b/apps/web/pages/settings/teams/[id]/members.tsx @@ -1,9 +1,11 @@ import TeamMembersView from "@calcom/features/ee/teams/pages/team-members-view"; +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; -import type { CalPageWrapper } from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper"; -const Page = TeamMembersView as CalPageWrapper; +const Page = () => ; + +Page.getLayout = getLayout; Page.PageWrapper = PageWrapper; export default Page; diff --git a/apps/web/pages/settings/teams/[id]/onboard-members.tsx b/apps/web/pages/settings/teams/[id]/onboard-members.tsx index aaf3dd9bfaf359..ca352381b85d9f 100644 --- a/apps/web/pages/settings/teams/[id]/onboard-members.tsx +++ b/apps/web/pages/settings/teams/[id]/onboard-members.tsx @@ -2,12 +2,12 @@ import Head from "next/head"; -import AddNewTeamMembers from "@calcom/features/ee/teams/components/AddNewTeamMembers"; import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { WizardLayout } from "@calcom/ui"; import PageWrapper from "@components/PageWrapper"; +import AddNewTeamMembers, { GetLayout } from "~/settings/teams/[id]/onboard-members-view"; + const OnboardTeamMembersPage = () => { const { t } = useLocale(); return ( @@ -21,12 +21,6 @@ const OnboardTeamMembersPage = () => { ); }; -export const GetLayout = (page: React.ReactElement) => ( - - {page} - -); - OnboardTeamMembersPage.getLayout = GetLayout; OnboardTeamMembersPage.PageWrapper = PageWrapper; diff --git a/apps/web/pages/settings/teams/[id]/profile.tsx b/apps/web/pages/settings/teams/[id]/profile.tsx index 9845bac378b0bc..d69987c83a1d52 100644 --- a/apps/web/pages/settings/teams/[id]/profile.tsx +++ b/apps/web/pages/settings/teams/[id]/profile.tsx @@ -1,9 +1,11 @@ import TeamProfileView from "@calcom/features/ee/teams/pages/team-profile-view"; +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; -import type { CalPageWrapper } from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper"; -const Page = TeamProfileView as CalPageWrapper; +const Page = () => ; + +Page.getLayout = getLayout; Page.PageWrapper = PageWrapper; export default Page; diff --git a/apps/web/pages/settings/teams/index.ts b/apps/web/pages/settings/teams/index.ts deleted file mode 100644 index 2f00167179c5f7..00000000000000 --- a/apps/web/pages/settings/teams/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import TeamListView from "@calcom/features/ee/teams/pages/team-listing-view"; - -import PageWrapper from "@components/PageWrapper"; -import type { CalPageWrapper } from "@components/PageWrapper"; - -const Page = TeamListView as CalPageWrapper; -Page.PageWrapper = PageWrapper; - -export default Page; diff --git a/apps/web/pages/settings/teams/index.tsx b/apps/web/pages/settings/teams/index.tsx new file mode 100644 index 00000000000000..ba0d9b85e23d5a --- /dev/null +++ b/apps/web/pages/settings/teams/index.tsx @@ -0,0 +1,11 @@ +import TeamListingView from "@calcom/features/ee/teams/pages/team-listing-view"; +import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; + +import PageWrapper from "@components/PageWrapper"; + +const Page = () => ; + +Page.getLayout = getLayout; +Page.PageWrapper = PageWrapper; + +export default Page; diff --git a/apps/web/pages/settings/teams/new/index.tsx b/apps/web/pages/settings/teams/new/index.tsx index a019cdfbbe2b8c..07cacbfee557a9 100644 --- a/apps/web/pages/settings/teams/new/index.tsx +++ b/apps/web/pages/settings/teams/new/index.tsx @@ -1,50 +1,15 @@ "use client"; import Head from "next/head"; -import { useRouter } from "next/navigation"; -import { z } from "zod"; -import { CreateANewTeamForm } from "@calcom/features/ee/teams/components"; -import { HOSTED_CAL_FEATURES } from "@calcom/lib/constants"; -import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl"; import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { useParamsWithFallback } from "@calcom/lib/hooks/useParamsWithFallback"; -import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; -import type { RouterOutputs } from "@calcom/trpc/react"; -import { WizardLayout } from "@calcom/ui"; import PageWrapper from "@components/PageWrapper"; -const querySchema = z.object({ - returnTo: z.string().optional(), - slug: z.string().optional(), -}); +import CreateNewTeamView, { LayoutWrapper } from "~/settings/teams/new/create-new-team-view"; const CreateNewTeamPage = () => { const { t } = useLocale(); - const params = useParamsWithFallback(); - const parsedQuery = querySchema.safeParse(params); - const router = useRouter(); - const telemetry = useTelemetry(); - - const isTeamBillingEnabledClient = !!process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY && HOSTED_CAL_FEATURES; - const flag = isTeamBillingEnabledClient - ? { - telemetryEvent: telemetryEventTypes.team_checkout_session_created, - submitLabel: "checkout", - } - : { - telemetryEvent: telemetryEventTypes.team_created, - submitLabel: "continue", - }; - - const returnToParam = - (parsedQuery.success ? getSafeRedirectUrl(parsedQuery.data.returnTo) : "/teams") || "/teams"; - - const onSuccess = (data: RouterOutputs["viewer"]["teams"]["create"]) => { - telemetry.event(flag.telemetryEvent); - router.push(data.url); - }; return ( <> @@ -52,22 +17,10 @@ const CreateNewTeamPage = () => { {t("create_new_team")} - router.push(returnToParam)} - submitLabel={flag.submitLabel} - onSuccess={onSuccess} - /> + ); }; -export const LayoutWrapper = (page: React.ReactElement) => { - return ( - - {page} - - ); -}; CreateNewTeamPage.getLayout = LayoutWrapper; CreateNewTeamPage.PageWrapper = PageWrapper; diff --git a/apps/web/pages/signup.tsx b/apps/web/pages/signup.tsx index 60c6d8eb02f75f..ecf0419ac38806 100644 --- a/apps/web/pages/signup.tsx +++ b/apps/web/pages/signup.tsx @@ -1,667 +1,12 @@ -"use client"; - -import { Analytics as DubAnalytics } from "@dub/analytics/react"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { signIn } from "next-auth/react"; -import { Trans } from "next-i18next"; -import dynamic from "next/dynamic"; -import Link from "next/link"; -import { useRouter } from "next/navigation"; -import Script from "next/script"; -import { useState, useEffect } from "react"; -import type { SubmitHandler } from "react-hook-form"; -import { useForm, useFormContext } from "react-hook-form"; -import { Toaster } from "react-hot-toast"; -import { z } from "zod"; - -import getStripe from "@calcom/app-store/stripepayment/lib/client"; -import { getPremiumPlanPriceValue } from "@calcom/app-store/stripepayment/lib/utils"; -import { getOrgUsernameFromEmail } from "@calcom/features/auth/signup/utils/getOrgUsernameFromEmail"; -import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains"; -import { classNames } from "@calcom/lib"; -import { - APP_NAME, - URL_PROTOCOL_REGEX, - IS_CALCOM, - IS_EUROPE, - WEBAPP_URL, - CLOUDFLARE_SITE_ID, - WEBSITE_PRIVACY_POLICY_URL, - WEBSITE_TERMS_URL, - WEBSITE_URL, -} from "@calcom/lib/constants"; -import { isENVDev } from "@calcom/lib/env"; -import { fetchUsername } from "@calcom/lib/fetchUsername"; -import { pushGTMEvent } from "@calcom/lib/gtm"; -import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; -import { useDebounce } from "@calcom/lib/hooks/useDebounce"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; -import { signupSchema as apiSignupSchema } from "@calcom/prisma/zod-utils"; -import type { inferSSRProps } from "@calcom/types/inferSSRProps"; -import { - Button, - HeadSeo, - PasswordField, - TextField, - Form, - Alert, - showToast, - CheckboxField, - Icon, -} from "@calcom/ui"; - import { getServerSideProps } from "@lib/signup/getServerSideProps"; import PageWrapper from "@components/PageWrapper"; -const signupSchema = apiSignupSchema.extend({ - apiError: z.string().optional(), // Needed to display API errors doesnt get passed to the API - cfToken: z.string().optional(), -}); - -const TurnstileCaptcha = dynamic(() => import("@components/auth/Turnstile"), { ssr: false }); - -type FormValues = z.infer; - -export type SignupProps = inferSSRProps; - -const FEATURES = [ - { - title: "connect_all_calendars", - description: "connect_all_calendars_description", - i18nOptions: { - appName: APP_NAME, - }, - icon: "calendar-heart" as const, - }, - { - title: "set_availability", - description: "set_availbility_description", - icon: "users" as const, - }, - { - title: "share_a_link_or_embed", - description: "share_a_link_or_embed_description", - icon: "link-2" as const, - i18nOptions: { - appName: APP_NAME, - }, - }, -]; - -function UsernameField({ - username, - setPremium, - premium, - setUsernameTaken, - orgSlug, - usernameTaken, - disabled, - ...props -}: React.ComponentProps & { - username: string; - setPremium: (value: boolean) => void; - premium: boolean; - usernameTaken: boolean; - orgSlug?: string; - setUsernameTaken: (value: boolean) => void; -}) { - const { t } = useLocale(); - const { register, formState } = useFormContext(); - const debouncedUsername = useDebounce(username, 600); - - useEffect(() => { - if (formState.isSubmitting || formState.isSubmitSuccessful) return; - - async function checkUsername() { - // If the username can't be changed, there is no point in doing the username availability check - if (disabled) return; - if (!debouncedUsername) { - setPremium(false); - setUsernameTaken(false); - return; - } - fetchUsername(debouncedUsername, orgSlug ?? null).then(({ data }) => { - setPremium(data.premium); - setUsernameTaken(!data.available); - }); - } - checkUsername(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [debouncedUsername, disabled, orgSlug, formState.isSubmitting, formState.isSubmitSuccessful]); - - return ( -
- - {(!formState.isSubmitting || !formState.isSubmitted) && ( -
-
- {usernameTaken ? ( -
- -

{t("already_in_use_error")}

-
- ) : premium ? ( -
- -

- {t("premium_username", { - price: getPremiumPlanPriceValue(), - })} -

-
- ) : null} -
-
- )} -
- ); -} - -function addOrUpdateQueryParam(url: string, key: string, value: string) { - const separator = url.includes("?") ? "&" : "?"; - const param = `${key}=${encodeURIComponent(value)}`; - return `${url}${separator}${param}`; -} - -export default function Signup({ - prepopulateFormValues, - token, - orgSlug, - isGoogleLoginEnabled, - isSAMLLoginEnabled, - orgAutoAcceptEmail, - redirectUrl, - emailVerificationEnabled, -}: SignupProps) { - const [premiumUsername, setPremiumUsername] = useState(false); - const [usernameTaken, setUsernameTaken] = useState(false); - const [isGoogleLoading, setIsGoogleLoading] = useState(false); - const searchParams = useCompatSearchParams(); - const telemetry = useTelemetry(); - const { t, i18n } = useLocale(); - const router = useRouter(); - const formMethods = useForm({ - resolver: zodResolver(signupSchema), - defaultValues: prepopulateFormValues satisfies FormValues, - mode: "onChange", - }); - const { - register, - watch, - formState: { isSubmitting, errors, isSubmitSuccessful }, - } = formMethods; - - useEffect(() => { - if (redirectUrl) { - localStorage.setItem("onBoardingRedirect", redirectUrl); - } - }, [redirectUrl]); - - const [COOKIE_CONSENT, setCOOKIE_CONSENT] = useState(false); - - function handleConsentChange(consent: boolean) { - setCOOKIE_CONSENT(!consent); - } - - const loadingSubmitState = isSubmitSuccessful || isSubmitting; - - const handleErrorsAndStripe = async (resp: Response) => { - if (!resp.ok) { - const err = await resp.json(); - if (err.checkoutSessionId) { - const stripe = await getStripe(); - if (stripe) { - console.log("Redirecting to stripe checkout"); - const { error } = await stripe.redirectToCheckout({ - sessionId: err.checkoutSessionId, - }); - console.warn(error.message); - } - } else { - throw new Error(err.message); - } - } - }; +import type { SignupProps } from "~/signup-view"; +import Signup from "~/signup-view"; - const isOrgInviteByLink = orgSlug && !prepopulateFormValues?.username; - const isPlatformUser = redirectUrl?.includes("platform") && redirectUrl?.includes("new"); - - const signUp: SubmitHandler = async (_data) => { - const { cfToken, ...data } = _data; - await fetch("/api/auth/signup", { - body: JSON.stringify({ - ...data, - language: i18n.language, - token, - }), - headers: { - "Content-Type": "application/json", - "cf-access-token": cfToken ?? "invalid-token", - }, - method: "POST", - }) - .then(handleErrorsAndStripe) - .then(async () => { - if (process.env.NEXT_PUBLIC_GTM_ID) - pushGTMEvent("create_account", { email: data.email, user: data.username, lang: data.language }); - - telemetry.event(telemetryEventTypes.signup, collectPageParameters()); - - const verifyOrGettingStarted = emailVerificationEnabled ? "auth/verify-email" : "getting-started"; - const gettingStartedWithPlatform = "settings/platform/new"; - - const constructCallBackIfUrlPresent = () => { - if (isOrgInviteByLink) { - return `${WEBAPP_URL}/${searchParams.get("callbackUrl")}`; - } - - return addOrUpdateQueryParam(`${WEBAPP_URL}/${searchParams.get("callbackUrl")}`, "from", "signup"); - }; - - const constructCallBackIfUrlNotPresent = () => { - if (!!isPlatformUser) { - return `${WEBAPP_URL}/${gettingStartedWithPlatform}?from=signup`; - } - - return `${WEBAPP_URL}/${verifyOrGettingStarted}?from=signup`; - }; - - const constructCallBackUrl = () => { - const callbackUrlSearchParams = searchParams?.get("callbackUrl"); - - return !!callbackUrlSearchParams - ? constructCallBackIfUrlPresent() - : constructCallBackIfUrlNotPresent(); - }; - - const callBackUrl = constructCallBackUrl(); - - await signIn<"credentials">("credentials", { - ...data, - callbackUrl: callBackUrl, - }); - }) - .catch((err) => { - formMethods.setError("apiError", { message: err.message }); - }); - }; - - return ( - <> - {IS_CALCOM && (!IS_EUROPE || COOKIE_CONSENT) ? ( - <> - {process.env.NEXT_PUBLIC_GTM_ID && ( - <> -