No confirmation shall be needed when rescheduling events that need confirmation (#440)
* No reconfirmation needed when rescheduling * adapted success page * Parse query as string Co-authored-by: nicolas <privat@nicolasjessen.de> Co-authored-by: Bailey Pumfleet <pumfleet@hey.com> Co-authored-by: Peer_Rich <peeroke@gmail.com> Co-authored-by: Alex van Andel <me@alexvanandel.com>
This commit is contained in:
		
							parent
							
								
									4c6bf96213
								
							
						
					
					
						commit
						961f297ba8
					
				
					 14 changed files with 41 additions and 39 deletions
				
			
		|  | @ -1,5 +1,5 @@ | |||
| import Cropper from "react-easy-crop"; | ||||
| import { useState, useCallback, useRef } from "react"; | ||||
| import { useCallback, useRef, useState } from "react"; | ||||
| import Slider from "./Slider"; | ||||
| 
 | ||||
| export default function ImageUploader({ target, id, buttonMsg, handleAvatarChange, imageRef }) { | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import React, { useEffect, useState, useRef } from "react"; | ||||
| import React, { useEffect, useRef, useState } from "react"; | ||||
| import { ArrowLeftIcon, PlusIcon, TrashIcon } from "@heroicons/react/outline"; | ||||
| import ErrorAlert from "@components/ui/alerts/Error"; | ||||
| import { UsernameInput } from "@components/ui/UsernameInput"; | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| import { | ||||
|   TrashIcon, | ||||
|   DotsHorizontalIcon, | ||||
|   ExternalLinkIcon, | ||||
|   LinkIcon, | ||||
|   PencilAltIcon, | ||||
|   ExternalLinkIcon, | ||||
|   TrashIcon, | ||||
| } from "@heroicons/react/outline"; | ||||
| import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../ui/Dropdown"; | ||||
| import { useState } from "react"; | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import { XCircleIcon, InformationCircleIcon, CheckCircleIcon } from "@heroicons/react/solid"; | ||||
| import { CheckCircleIcon, InformationCircleIcon, XCircleIcon } from "@heroicons/react/solid"; | ||||
| import classNames from "classnames"; | ||||
| import { ReactNode } from "react"; | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import Text from "@components/ui/Text"; | |||
| import { PlusIcon, TrashIcon } from "@heroicons/react/outline"; | ||||
| import dayjs, { Dayjs } from "dayjs"; | ||||
| import classnames from "classnames"; | ||||
| 
 | ||||
| export const SCHEDULE_FORM_ID = "SCHEDULE_FORM_ID"; | ||||
| export const toCalendsoAvailabilityFormat = (schedule: Schedule) => { | ||||
|   return schedule; | ||||
|  |  | |||
|  | @ -5,11 +5,11 @@ import { Credential } from "@prisma/client"; | |||
| import CalEventParser from "./CalEventParser"; | ||||
| import { EventResult } from "@lib/events/EventManager"; | ||||
| import logger from "@lib/logger"; | ||||
| 
 | ||||
| const log = logger.getChildLogger({ prefix: ["[lib] calendarClient"] }); | ||||
| import { CalDavCalendar } from "./integrations/CalDav/CalDavCalendarAdapter"; | ||||
| import { AppleCalendar } from "./integrations/Apple/AppleCalendarAdapter"; | ||||
| 
 | ||||
| const log = logger.getChildLogger({ prefix: ["[lib] calendarClient"] }); | ||||
| 
 | ||||
| // eslint-disable-next-line @typescript-eslint/no-var-requires
 | ||||
| const { google } = require("googleapis"); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,17 +1,17 @@ | |||
| import { IntegrationCalendar, CalendarApiAdapter, CalendarEvent } from "../../calendarClient"; | ||||
| import { CalendarApiAdapter, CalendarEvent, IntegrationCalendar } from "../../calendarClient"; | ||||
| import { symmetricDecrypt } from "@lib/crypto"; | ||||
| import { | ||||
|   createAccount, | ||||
|   fetchCalendars, | ||||
|   fetchCalendarObjects, | ||||
|   getBasicAuthHeaders, | ||||
|   createCalendarObject, | ||||
|   updateCalendarObject, | ||||
|   deleteCalendarObject, | ||||
|   fetchCalendarObjects, | ||||
|   fetchCalendars, | ||||
|   getBasicAuthHeaders, | ||||
|   updateCalendarObject, | ||||
| } from "tsdav"; | ||||
| import { Credential } from "@prisma/client"; | ||||
| import ICAL from "ical.js"; | ||||
| import { createEvent, DurationObject, Attendee, Person } from "ics"; | ||||
| import { Attendee, createEvent, DurationObject, Person } from "ics"; | ||||
| import dayjs from "dayjs"; | ||||
| import { v4 as uuidv4 } from "uuid"; | ||||
| import { stripHtml } from "../../emails/helpers"; | ||||
|  |  | |||
|  | @ -242,13 +242,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) | |||
|     }; // used for invitee emails
 | ||||
|   } | ||||
| 
 | ||||
|   // Initialize EventManager with credentials
 | ||||
|   const rescheduleUid = req.body.rescheduleUid; | ||||
| 
 | ||||
|   const bookingCreateInput: Prisma.BookingCreateInput = { | ||||
|     uid, | ||||
|     title: evt.title, | ||||
|     startTime: dayjs(evt.startTime).toDate(), | ||||
|     endTime: dayjs(evt.endTime).toDate(), | ||||
|     description: evt.description, | ||||
|     confirmed: !eventType.requiresConfirmation, | ||||
|     confirmed: !eventType.requiresConfirmation || !!rescheduleUid, | ||||
|     location: evt.location, | ||||
|     eventType: { | ||||
|       connect: { | ||||
|  | @ -375,9 +378,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Initialize EventManager with credentials
 | ||||
|   const eventManager = new EventManager(user.credentials); | ||||
|   const rescheduleUid = req.body.rescheduleUid; | ||||
| 
 | ||||
|   if (rescheduleUid) { | ||||
|     // Use EventManager to conditionally use all needed integrations.
 | ||||
|     const updateResults: CreateUpdateResult = await eventManager.update(evt, rescheduleUid); | ||||
|  | @ -410,7 +412,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (eventType.requiresConfirmation) { | ||||
|   if (eventType.requiresConfirmation && !rescheduleUid) { | ||||
|     await new EventOrganizerRequestMail(evt, uid).sendEmail(); | ||||
|   } | ||||
| 
 | ||||
|  | @ -429,6 +431,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) | |||
|     }, | ||||
|   }); | ||||
| 
 | ||||
|   // booking succesfull
 | ||||
|   // booking successful
 | ||||
|   return res.status(201).json(booking); | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| import type { NextApiRequest, NextApiResponse } from "next"; | ||||
| import { getSession } from "@lib/auth"; | ||||
| import prisma from "../../../../lib/prisma"; | ||||
| 
 | ||||
| const scopes = ["offline_access", "Calendars.Read", "Calendars.ReadWrite"]; | ||||
| 
 | ||||
| export default async function handler(req: NextApiRequest, res: NextApiResponse) { | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ import Modal from "@components/Modal"; | |||
| import React, { useEffect, useRef, useState } from "react"; | ||||
| import Select, { OptionTypeBase } from "react-select"; | ||||
| import prisma from "@lib/prisma"; | ||||
| import { EventTypeCustomInput, EventTypeCustomInputType, SchedulingType } from "@prisma/client"; | ||||
| import { Availability, EventTypeCustomInput, EventTypeCustomInputType, SchedulingType } from "@prisma/client"; | ||||
| import { LocationType } from "@lib/location"; | ||||
| import Shell from "@components/Shell"; | ||||
| import { getSession } from "@lib/auth"; | ||||
|  | @ -28,7 +28,6 @@ import { | |||
| import dayjs from "dayjs"; | ||||
| import utc from "dayjs/plugin/utc"; | ||||
| import timezone from "dayjs/plugin/timezone"; | ||||
| import { Availability } from "@prisma/client"; | ||||
| import { validJson } from "@lib/jsonUtils"; | ||||
| import throttle from "lodash.throttle"; | ||||
| import "react-dates/initialize"; | ||||
|  |  | |||
|  | @ -2,15 +2,15 @@ import Head from "next/head"; | |||
| import prisma from "@lib/prisma"; | ||||
| import { useSession } from "next-auth/client"; | ||||
| import { | ||||
|   EventType, | ||||
|   EventTypeCreateInput, | ||||
|   Schedule, | ||||
|   ScheduleCreateInput, | ||||
|   User, | ||||
|   UserUpdateInput, | ||||
|   EventType, | ||||
|   Schedule, | ||||
| } from "@prisma/client"; | ||||
| import { NextPageContext } from "next"; | ||||
| import React, { useState, useEffect, useRef } from "react"; | ||||
| import React, { useEffect, useRef, useState } from "react"; | ||||
| import { validJson } from "@lib/jsonUtils"; | ||||
| import TimezoneSelect from "react-timezone-select"; | ||||
| import Text from "@components/ui/Text"; | ||||
|  | @ -18,8 +18,6 @@ import ErrorAlert from "@components/ui/alerts/Error"; | |||
| import dayjs from "dayjs"; | ||||
| import utc from "dayjs/plugin/utc"; | ||||
| import timezone from "dayjs/plugin/timezone"; | ||||
| dayjs.extend(utc); | ||||
| dayjs.extend(timezone); | ||||
| import AddCalDavIntegration, { | ||||
|   ADD_CALDAV_INTEGRATION_FORM_TITLE, | ||||
| } from "@lib/integrations/CalDav/components/AddCalDavIntegration"; | ||||
|  | @ -33,6 +31,9 @@ import { ArrowRightIcon } from "@heroicons/react/outline"; | |||
| import { getSession } from "@lib/auth"; | ||||
| import Button from "@components/ui/Button"; | ||||
| 
 | ||||
| dayjs.extend(utc); | ||||
| dayjs.extend(timezone); | ||||
| 
 | ||||
| const DEFAULT_EVENT_TYPES = [ | ||||
|   { | ||||
|     title: "15 Min Meeting", | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import Link from "next/link"; | ||||
| import prisma from "@lib/prisma"; | ||||
| import Shell from "@components/Shell"; | ||||
| import { useEffect, useState, useRef, useCallback } from "react"; | ||||
| import { useCallback, useEffect, useRef, useState } from "react"; | ||||
| import { useSession } from "next-auth/client"; | ||||
| import { CheckCircleIcon, ChevronRightIcon, PlusIcon, XCircleIcon } from "@heroicons/react/solid"; | ||||
| import { InformationCircleIcon } from "@heroicons/react/outline"; | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import { GetServerSideProps } from "next"; | ||||
| import Shell from "@components/Shell"; | ||||
| import SettingsShell from "@components/Settings"; | ||||
| import { useEffect, useState, useRef } from "react"; | ||||
| import { useEffect, useRef, useState } from "react"; | ||||
| import type { Session } from "next-auth"; | ||||
| import { useSession } from "next-auth/client"; | ||||
| import { UsersIcon } from "@heroicons/react/outline"; | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ dayjs.extend(timezone); | |||
| 
 | ||||
| export default function Success(props: inferSSRProps<typeof getServerSideProps>) { | ||||
|   const router = useRouter(); | ||||
|   const { location, name } = router.query; | ||||
|   const { location, name, reschedule } = router.query; | ||||
| 
 | ||||
|   const [is24h, setIs24h] = useState(false); | ||||
|   const [date, setDate] = useState(dayjs.utc(asStringOrNull(router.query.date))); | ||||
|  | @ -62,12 +62,14 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>) | |||
|     return encodeURIComponent(event.value); | ||||
|   } | ||||
| 
 | ||||
|   const needsConfirmation = props.eventType.requiresConfirmation && reschedule != "true"; | ||||
| 
 | ||||
|   return ( | ||||
|     isReady && ( | ||||
|       <div className="bg-neutral-50 dark:bg-neutral-900 h-screen"> | ||||
|         <HeadSeo | ||||
|           title={`Booking ${props.eventType.requiresConfirmation ? "Submitted" : "Confirmed"}`} | ||||
|           description={`Booking ${props.eventType.requiresConfirmation ? "Submitted" : "Confirmed"}`} | ||||
|           title={`Booking ${needsConfirmation ? "Submitted" : "Confirmed"}`} | ||||
|           description={`Booking ${needsConfirmation ? "Submitted" : "Confirmed"}`} | ||||
|         /> | ||||
|         <main className="max-w-3xl mx-auto py-24"> | ||||
|           <div className="fixed z-50 inset-0 overflow-y-auto"> | ||||
|  | @ -83,22 +85,18 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>) | |||
|                   aria-labelledby="modal-headline"> | ||||
|                   <div> | ||||
|                     <div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100"> | ||||
|                       {!props.eventType.requiresConfirmation && ( | ||||
|                         <CheckIcon className="h-8 w-8 text-green-600" /> | ||||
|                       )} | ||||
|                       {props.eventType.requiresConfirmation && ( | ||||
|                         <ClockIcon className="h-8 w-8 text-green-600" /> | ||||
|                       )} | ||||
|                       {!needsConfirmation && <CheckIcon className="h-8 w-8 text-green-600" />} | ||||
|                       {needsConfirmation && <ClockIcon className="h-8 w-8 text-green-600" />} | ||||
|                     </div> | ||||
|                     <div className="mt-3 text-center sm:mt-5"> | ||||
|                       <h3 | ||||
|                         className="text-2xl leading-6 font-semibold dark:text-white text-neutral-900" | ||||
|                         id="modal-headline"> | ||||
|                         {props.eventType.requiresConfirmation ? "Submitted" : "This meeting is scheduled"} | ||||
|                         {needsConfirmation ? "Submitted" : "This meeting is scheduled"} | ||||
|                       </h3> | ||||
|                       <div className="mt-3"> | ||||
|                         <p className="text-sm text-neutral-600 dark:text-gray-300"> | ||||
|                           {props.eventType.requiresConfirmation | ||||
|                           {needsConfirmation | ||||
|                             ? props.profile.name !== null | ||||
|                               ? `${props.profile.name} still needs to confirm or reject the booking.` | ||||
|                               : "Your booking still needs to be confirmed or rejected." | ||||
|  | @ -126,7 +124,7 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>) | |||
|                       </div> | ||||
|                     </div> | ||||
|                   </div> | ||||
|                   {!props.eventType.requiresConfirmation && ( | ||||
|                   {!needsConfirmation && ( | ||||
|                     <div className="mt-5 sm:mt-0 sm:pt-4 pt-2 text-center flex"> | ||||
|                       <span className="font-medium text-gray-700 dark:text-gray-50 flex self-center mr-6"> | ||||
|                         Add to calendar | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Nico
						Nico