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:
Nico 2021-09-17 23:31:44 +02:00 committed by GitHub
parent 4c6bf96213
commit 961f297ba8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 41 additions and 39 deletions

View File

@ -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 }) {

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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;

View File

@ -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");

View File

@ -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";

View File

@ -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);
}

View File

@ -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) {

View File

@ -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";

View File

@ -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",

View File

@ -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";

View File

@ -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";

View File

@ -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