From 33694196e1730fe28310e54d441235fd3a02d993 Mon Sep 17 00:00:00 2001 From: Bailey Pumfleet Date: Sat, 15 Jan 2022 16:23:42 +0000 Subject: [PATCH] Calendly & SavvyCal import (#1512) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Calendly & SavvyCal import * added string keys to import * Update pages/api/import/savvycal.ts Co-authored-by: Omar López * Update pages/api/import/savvycal.ts Co-authored-by: Omar López * Update pages/getting-started.tsx Co-authored-by: Omar López * fixed string * prettier Co-authored-by: Peer Richelsen Co-authored-by: Omar López Co-authored-by: Peer Richelsen --- pages/api/import/calendly.ts | 78 ++++++++++++++ pages/api/import/savvycal.ts | 78 ++++++++++++++ pages/getting-started.tsx | 151 ++++++++++++++++++++------- public/static/locales/en/common.json | 4 + 4 files changed, 274 insertions(+), 37 deletions(-) create mode 100644 pages/api/import/calendly.ts create mode 100644 pages/api/import/savvycal.ts diff --git a/pages/api/import/calendly.ts b/pages/api/import/calendly.ts new file mode 100644 index 00000000000000..720c3e29ce2f1e --- /dev/null +++ b/pages/api/import/calendly.ts @@ -0,0 +1,78 @@ +import { PrismaClient } from "@prisma/client"; +import type { NextApiRequest, NextApiResponse } from "next"; + +import { getSession } from "@lib/auth"; + +const prisma = new PrismaClient(); + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const session = await getSession({ req }); + const authenticatedUser = await prisma.user.findFirst({ + rejectOnNotFound: true, + where: { + id: session?.user.id, + }, + select: { + id: true, + }, + }); + if (req.method == "POST") { + const userResult = await fetch("https://api.calendly.com/users/me", { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + req.body.token, + }, + }); + + if (userResult.status == 200) { + const userData = await userResult.json(); + + await prisma.user.update({ + where: { + id: authenticatedUser.id, + }, + data: { + name: userData.resource.name, + }, + }); + + const eventTypesResult = await fetch( + "https://api.calendly.com/event_types?user=" + userData.resource.uri, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + req.body.token, + }, + } + ); + + const eventTypesData = await eventTypesResult.json(); + + eventTypesData.collection.forEach(async (eventType: any) => { + await prisma.eventType.create({ + data: { + title: eventType.name, + slug: eventType.slug, + length: eventType.duration, + description: eventType.description_plain, + hidden: eventType.secret, + users: { + connect: { + id: authenticatedUser.id, + }, + }, + userId: authenticatedUser.id, + }, + }); + }); + + res.status(201).end(); + } else { + res.status(500).end(); + } + } else { + res.status(405).end(); + } +} diff --git a/pages/api/import/savvycal.ts b/pages/api/import/savvycal.ts new file mode 100644 index 00000000000000..4e5a809132e085 --- /dev/null +++ b/pages/api/import/savvycal.ts @@ -0,0 +1,78 @@ +import { PrismaClient } from "@prisma/client"; +import type { NextApiRequest, NextApiResponse } from "next"; + +import { getSession } from "@lib/auth"; + +const prisma = new PrismaClient(); + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const session = await getSession({ req }); + const authenticatedUser = await prisma.user.findFirst({ + rejectOnNotFound: true, + where: { + id: session?.user.id, + }, + select: { + id: true, + }, + }); + if (req.method === "POST") { + const userResult = await fetch("https://api.savvycal.com/v1/me", { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + req.body.token, + }, + }); + + if (userResult.status === 200) { + const userData = await userResult.json(); + + await prisma.user.update({ + where: { + id: authenticatedUser.id, + }, + data: { + name: userData.display_name, + timeZone: userData.time_zone, + weekStart: userData.first_day_of_week === 0 ? "Sunday" : "Monday", + avatar: userData.avatar_url, + }, + }); + + const eventTypesResult = await fetch("https://api.savvycal.com/v1/links?limit=100", { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + req.body.token, + }, + }); + + const eventTypesData = await eventTypesResult.json(); + + eventTypesData.entries.forEach(async (eventType: any) => { + await prisma.eventType.create({ + data: { + title: eventType.name, + slug: eventType.slug, + length: eventType.durations[0], + description: eventType.description.replace(/<[^>]*>?/gm, ""), + hidden: eventType.state === "active" ? true : false, + users: { + connect: { + id: authenticatedUser.id, + }, + }, + userId: authenticatedUser.id, + }, + }); + }); + + res.status(201).end(); + } else { + res.status(500).end(); + } + } else { + res.status(405).end(); + } +} diff --git a/pages/getting-started.tsx b/pages/getting-started.tsx index 1dddb4eeaf86b8..ce1597d5b0fe89 100644 --- a/pages/getting-started.tsx +++ b/pages/getting-started.tsx @@ -1,4 +1,5 @@ import { ArrowRightIcon } from "@heroicons/react/outline"; +import { zodResolver } from "@hookform/resolvers/zod/dist/zod"; import { Prisma } from "@prisma/client"; import classnames from "classnames"; import dayjs from "dayjs"; @@ -14,6 +15,7 @@ import { useRouter } from "next/router"; import React, { useEffect, useRef, useState } from "react"; import { useForm } from "react-hook-form"; import TimezoneSelect from "react-timezone-select"; +import * as z from "zod"; import { getSession } from "@lib/auth"; import { DEFAULT_SCHEDULE } from "@lib/availability"; @@ -71,6 +73,7 @@ export default function Onboarding(props: inferSSRProps(null); const updateUser = async (data: Prisma.UserUpdateInput) => { @@ -229,6 +232,14 @@ export default function Onboarding(props: inferSSRProps({ resolver: zodResolver(schema), mode: "onSubmit" }); + const availabilityForm = useForm({ defaultValues: { schedule: DEFAULT_SCHEDULE } }); const steps = [ { @@ -236,44 +247,110 @@ export default function Onboarding(props: inferSSRProps -
-
- - -
- -
-
-
-
- + + + +
+
+ + + {t("current_time")}:  + {dayjs().tz(selectedTimeZone).format("LT")} + +
+ setSelectedTimeZone(value)} + className="block w-full mt-1 border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" + /> +
+ + + ), hideConfirm: false, confirmText: t("continue"), diff --git a/public/static/locales/en/common.json b/public/static/locales/en/common.json index 6ffa158c437c70..a6463e47227f56 100644 --- a/public/static/locales/en/common.json +++ b/public/static/locales/en/common.json @@ -595,5 +595,9 @@ "saml_configuration_update_failed": "SAML configuration update failed", "saml_configuration_delete_failed": "SAML configuration delete failed", "saml_email_required": "Please enter an email so we can find your SAML Identity Provider", + "you_will_need_to_generate": "You will need to generate an access token from the integrations page.", + "import": "Import", + "import_from": "Import from", + "access_token": "Access token", "visit_roadmap": "Roadmap" }