1
1
import { ArrowRightIcon } from "@heroicons/react/outline" ;
2
+ import { zodResolver } from "@hookform/resolvers/zod/dist/zod" ;
2
3
import { Prisma } from "@prisma/client" ;
3
4
import classnames from "classnames" ;
4
5
import dayjs from "dayjs" ;
@@ -14,6 +15,7 @@ import { useRouter } from "next/router";
14
15
import React , { useEffect , useRef , useState } from "react" ;
15
16
import { useForm } from "react-hook-form" ;
16
17
import TimezoneSelect from "react-timezone-select" ;
18
+ import * as z from "zod" ;
17
19
18
20
import { getSession } from "@lib/auth" ;
19
21
import { DEFAULT_SCHEDULE } from "@lib/availability" ;
@@ -71,6 +73,7 @@ export default function Onboarding(props: inferSSRProps<typeof getServerSideProp
71
73
const { status } = useSession ( ) ;
72
74
const loading = status === "loading" ;
73
75
const [ ready , setReady ] = useState ( false ) ;
76
+ const [ selectedImport , setSelectedImport ] = useState ( "" ) ;
74
77
const [ error , setError ] = useState < Error | null > ( null ) ;
75
78
76
79
const updateUser = async ( data : Prisma . UserUpdateInput ) => {
@@ -229,51 +232,125 @@ export default function Onboarding(props: inferSSRProps<typeof getServerSideProp
229
232
router . push ( "/event-types" ) ;
230
233
} ;
231
234
235
+ const schema = z . object ( {
236
+ token : z . string ( ) ,
237
+ } ) ;
238
+
239
+ const formMethods = useForm < {
240
+ token : string ;
241
+ } > ( { resolver : zodResolver ( schema ) , mode : "onSubmit" } ) ;
242
+
232
243
const availabilityForm = useForm ( { defaultValues : { schedule : DEFAULT_SCHEDULE } } ) ;
233
244
const steps = [
234
245
{
235
246
id : t ( "welcome" ) ,
236
247
title : t ( "welcome_to_calcom" ) ,
237
248
description : t ( "welcome_instructions" ) ,
238
249
Component : (
239
- < form className = "sm:mx-auto sm:w-full" >
240
- < section className = "space-y-8" >
241
- < fieldset >
242
- < label htmlFor = "name" className = "block text-sm font-medium text-gray-700" >
243
- { t ( "full_name" ) }
244
- </ label >
245
- < input
246
- ref = { nameRef }
247
- type = "text"
248
- name = "name"
249
- id = "name"
250
- autoComplete = "given-name"
251
- placeholder = { t ( "your_name" ) }
252
- defaultValue = { props . user . name ?? enteredName }
253
- required
254
- className = "block w-full px-3 py-2 mt-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-500 focus:border-neutral-500 sm:text-sm"
255
- />
256
- </ fieldset >
257
-
258
- < fieldset >
259
- < section className = "flex justify-between" >
260
- < label htmlFor = "timeZone" className = "block text-sm font-medium text-gray-700" >
261
- { t ( "timezone" ) }
250
+ < >
251
+ { selectedImport == "" && (
252
+ < div className = "grid grid-cols-2 mb-4 gap-x-4" >
253
+ < Button color = "secondary" onClick = { ( ) => setSelectedImport ( "calendly" ) } >
254
+ { t ( "import_from" ) } Calendly
255
+ </ Button >
256
+ < Button color = "secondary" onClick = { ( ) => setSelectedImport ( "savvycal" ) } >
257
+ { t ( "import_from" ) } SavvyCal
258
+ </ Button >
259
+ </ div >
260
+ ) }
261
+ { selectedImport && (
262
+ < div >
263
+ < h2 className = "text-2xl text-gray-900 font-cal" >
264
+ { t ( "import_from" ) } { selectedImport === "calendly" ? "Calendly" : "SavvyCal" }
265
+ </ h2 >
266
+ < p className = "mb-2 text-sm text-gray-500" > { t ( "you_will_need_to_generate" ) } </ p >
267
+ < form
268
+ className = "flex"
269
+ onSubmit = { formMethods . handleSubmit ( async ( values ) => {
270
+ setSubmitting ( true ) ;
271
+ const response = await fetch ( `/api/import/${ selectedImport } ` , {
272
+ method : "POST" ,
273
+ body : JSON . stringify ( {
274
+ token : values . token ,
275
+ } ) ,
276
+ headers : {
277
+ "Content-Type" : "application/json" ,
278
+ } ,
279
+ } ) ;
280
+ if ( response . status === 201 ) {
281
+ setSubmitting ( false ) ;
282
+ handleSkipStep ( ) ;
283
+ } else {
284
+ await response . json ( ) . catch ( ( e ) => {
285
+ console . log ( "Error: response.json invalid: " + e ) ;
286
+ setSubmitting ( false ) ;
287
+ } ) ;
288
+ }
289
+ } ) } >
290
+ < input
291
+ onChange = { async ( e ) => {
292
+ formMethods . setValue ( "token" , e . target . value ) ;
293
+ } }
294
+ type = "text"
295
+ name = "token"
296
+ id = "token"
297
+ placeholder = { t ( "access_token" ) }
298
+ required
299
+ className = "block w-full px-3 py-2 mt-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-500 focus:border-neutral-500 sm:text-sm"
300
+ />
301
+ < Button type = "submit" className = "h-10 mt-1 ml-4" >
302
+ { t ( "import" ) }
303
+ </ Button >
304
+ </ form >
305
+ </ div >
306
+ ) }
307
+ < div className = "relative my-4" >
308
+ < div className = "absolute inset-0 flex items-center" aria-hidden = "true" >
309
+ < div className = "w-full border-t border-gray-300" />
310
+ </ div >
311
+ < div className = "relative flex justify-center" >
312
+ < span className = "px-2 text-sm text-gray-500 bg-white" > or</ span >
313
+ </ div >
314
+ </ div >
315
+ < form className = "sm:mx-auto sm:w-full" >
316
+ < section className = "space-y-8" >
317
+ < fieldset >
318
+ < label htmlFor = "name" className = "block text-sm font-medium text-gray-700" >
319
+ { t ( "full_name" ) }
262
320
</ label >
263
- < Text variant = "caption" >
264
- { t ( "current_time" ) } :
265
- < span className = "text-black" > { dayjs ( ) . tz ( selectedTimeZone ) . format ( "LT" ) } </ span >
266
- </ Text >
267
- </ section >
268
- < TimezoneSelect
269
- id = "timeZone"
270
- value = { selectedTimeZone }
271
- onChange = { ( { value } ) => setSelectedTimeZone ( value ) }
272
- className = "block w-full mt-1 border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
273
- />
274
- </ fieldset >
275
- </ section >
276
- </ form >
321
+ < input
322
+ ref = { nameRef }
323
+ type = "text"
324
+ name = "name"
325
+ id = "name"
326
+ autoComplete = "given-name"
327
+ placeholder = { t ( "your_name" ) }
328
+ defaultValue = { props . user . name ?? enteredName }
329
+ required
330
+ className = "block w-full px-3 py-2 mt-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-500 focus:border-neutral-500 sm:text-sm"
331
+ />
332
+ </ fieldset >
333
+
334
+ < fieldset >
335
+ < section className = "flex justify-between" >
336
+ < label htmlFor = "timeZone" className = "block text-sm font-medium text-gray-700" >
337
+ { t ( "timezone" ) }
338
+ </ label >
339
+ < Text variant = "caption" >
340
+ { t ( "current_time" ) } :
341
+ < span className = "text-black" > { dayjs ( ) . tz ( selectedTimeZone ) . format ( "LT" ) } </ span >
342
+ </ Text >
343
+ </ section >
344
+ < TimezoneSelect
345
+ id = "timeZone"
346
+ value = { selectedTimeZone }
347
+ onChange = { ( { value } ) => setSelectedTimeZone ( value ) }
348
+ className = "block w-full mt-1 border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
349
+ />
350
+ </ fieldset >
351
+ </ section >
352
+ </ form >
353
+ </ >
277
354
) ,
278
355
hideConfirm : false ,
279
356
confirmText : t ( "continue" ) ,
0 commit comments