Fix/duplicate events on onboarding (#716)
parent
d4f29464f2
commit
a047177e72
|
@ -0,0 +1,10 @@
|
|||
import * as fetch from "@lib/core/http/fetch-wrapper";
|
||||
import { EventType } from "@prisma/client";
|
||||
|
||||
type GetEventsResponse = { message: string; data: EventType[] };
|
||||
const getEventTypes = async () => {
|
||||
const response = await fetch.get<GetEventsResponse>("/api/event-type");
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default getEventTypes;
|
|
@ -72,6 +72,7 @@
|
|||
"devDependencies": {
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/node": "^16.6.1",
|
||||
"@types/nodemailer": "^6.4.4",
|
||||
"@types/qrcode": "^1.4.1",
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { getSession } from "@lib/auth";
|
||||
import prisma from "@lib/prisma";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const session = await getSession({ req: req });
|
||||
|
||||
if (!session) {
|
||||
res.status(401).json({ message: "Not authenticated" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!session.user?.id) {
|
||||
console.error("Session is missing a user id");
|
||||
return res.status(500).json({ message: "Something went wrong" });
|
||||
}
|
||||
|
||||
if (req.method === "GET") {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: session.user.id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
eventTypes: {
|
||||
where: {
|
||||
team: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
description: true,
|
||||
length: true,
|
||||
schedulingType: true,
|
||||
slug: true,
|
||||
hidden: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Events.", data: user.eventTypes });
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
"theme",
|
||||
"completedOnboarding",
|
||||
]),
|
||||
bio: req.body.description,
|
||||
bio: req.body.description ?? req.body.data?.bio,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
|
|
|
@ -30,6 +30,9 @@ import classnames from "classnames";
|
|||
import { ArrowRightIcon } from "@heroicons/react/outline";
|
||||
import { getSession } from "@lib/auth";
|
||||
import Button from "@components/ui/Button";
|
||||
import debounce from "lodash.debounce";
|
||||
import Loader from "@components/Loader";
|
||||
import getEventTypes from "../lib/queries/event-types/get-event-types";
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
|
@ -63,6 +66,7 @@ type OnboardingProps = {
|
|||
export default function Onboarding(props: OnboardingProps) {
|
||||
const router = useRouter();
|
||||
|
||||
const [isSubmitting, setSubmitting] = React.useState(false);
|
||||
const [enteredName, setEnteredName] = React.useState();
|
||||
const Sess = useSession();
|
||||
const [ready, setReady] = useState(false);
|
||||
|
@ -116,7 +120,7 @@ export default function Onboarding(props: OnboardingProps) {
|
|||
return responseData.data;
|
||||
};
|
||||
|
||||
const integrationHandler = (type: string) => {
|
||||
const handleAddIntegration = (type: string) => {
|
||||
if (type === "caldav_calendar") {
|
||||
setAddCalDavError(null);
|
||||
setIsAddCalDavIntegrationDialogOpen(true);
|
||||
|
@ -137,7 +141,7 @@ export default function Onboarding(props: OnboardingProps) {
|
|||
}
|
||||
|
||||
return (
|
||||
<li onClick={() => integrationHandler(integration.type)} key={integration.type} className="flex py-4">
|
||||
<li onClick={() => handleAddIntegration(integration.type)} key={integration.type} className="flex py-4">
|
||||
<div className="w-1/12 mr-4 pt-2">
|
||||
<img className="h-8 w-8 mr-2" src={integration.imageSrc} alt={integration.title} />
|
||||
</div>
|
||||
|
@ -148,7 +152,7 @@ export default function Onboarding(props: OnboardingProps) {
|
|||
</Text>
|
||||
</div>
|
||||
<div className="w-2/12 text-right pt-2">
|
||||
<Button color="secondary" onClick={() => integrationHandler(integration.type)}>
|
||||
<Button color="secondary" onClick={() => handleAddIntegration(integration.type)}>
|
||||
Connect
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -280,6 +284,7 @@ export default function Onboarding(props: OnboardingProps) {
|
|||
|
||||
const handleConfirmStep = async () => {
|
||||
try {
|
||||
setSubmitting(true);
|
||||
if (
|
||||
steps[currentStep] &&
|
||||
steps[currentStep]?.onComplete &&
|
||||
|
@ -288,12 +293,16 @@ export default function Onboarding(props: OnboardingProps) {
|
|||
await steps[currentStep].onComplete();
|
||||
}
|
||||
incrementStep();
|
||||
setSubmitting(false);
|
||||
} catch (error) {
|
||||
console.log("handleConfirmStep", error);
|
||||
setSubmitting(false);
|
||||
setError(error);
|
||||
}
|
||||
};
|
||||
|
||||
const debouncedHandleConfirmStep = debounce(handleConfirmStep, 850);
|
||||
|
||||
const handleSkipStep = () => {
|
||||
incrementStep();
|
||||
};
|
||||
|
@ -331,17 +340,22 @@ export default function Onboarding(props: OnboardingProps) {
|
|||
* then the default availability is applied.
|
||||
*/
|
||||
const completeOnboarding = async () => {
|
||||
setSubmitting(true);
|
||||
if (!props.eventTypes || props.eventTypes.length === 0) {
|
||||
Promise.all(
|
||||
DEFAULT_EVENT_TYPES.map(async (event) => {
|
||||
return await createEventType(event);
|
||||
})
|
||||
);
|
||||
const eventTypes = await getEventTypes();
|
||||
if (eventTypes.length === 0) {
|
||||
Promise.all(
|
||||
DEFAULT_EVENT_TYPES.map(async (event) => {
|
||||
return await createEventType(event);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
await updateUser({
|
||||
completedOnboarding: true,
|
||||
});
|
||||
|
||||
setSubmitting(false);
|
||||
router.push("/event-types");
|
||||
};
|
||||
|
||||
|
@ -365,7 +379,7 @@ export default function Onboarding(props: OnboardingProps) {
|
|||
id="name"
|
||||
autoComplete="given-name"
|
||||
placeholder="Your name"
|
||||
defaultValue={props.user.name}
|
||||
defaultValue={props.user.name ?? enteredName}
|
||||
required
|
||||
className="mt-1 block w-full border border-gray-300 rounded-sm shadow-sm py-2 px-3 focus:outline-none focus:ring-neutral-500 focus:border-neutral-500 sm:text-sm"
|
||||
/>
|
||||
|
@ -397,13 +411,16 @@ export default function Onboarding(props: OnboardingProps) {
|
|||
cancelText: "Set up later",
|
||||
onComplete: async () => {
|
||||
try {
|
||||
setSubmitting(true);
|
||||
await updateUser({
|
||||
name: nameRef.current.value,
|
||||
timeZone: selectedTimeZone.value,
|
||||
});
|
||||
setEnteredName(nameRef.current.value);
|
||||
setSubmitting(true);
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
setSubmitting(false);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -435,10 +452,12 @@ export default function Onboarding(props: OnboardingProps) {
|
|||
<SchedulerForm
|
||||
onSubmit={async (data) => {
|
||||
try {
|
||||
setSubmitting(true);
|
||||
await createSchedule({
|
||||
freeBusyTimes: data,
|
||||
});
|
||||
handleConfirmStep();
|
||||
debouncedHandleConfirmStep();
|
||||
setSubmitting(false);
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
}
|
||||
|
@ -505,11 +524,15 @@ export default function Onboarding(props: OnboardingProps) {
|
|||
cancelText: "Set up later",
|
||||
onComplete: async () => {
|
||||
try {
|
||||
setSubmitting(true);
|
||||
console.log("updating");
|
||||
await updateUser({
|
||||
bio: bioRef.current.value,
|
||||
description: bioRef.current.value,
|
||||
});
|
||||
setSubmitting(false);
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
setSubmitting(false);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -532,8 +555,13 @@ export default function Onboarding(props: OnboardingProps) {
|
|||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
{isSubmitting && (
|
||||
<div className="fixed w-full h-full bg-white bg-opacity-25 flex flex-col justify-center items-center content-center z-10">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<div className="mx-auto py-24 px-4">
|
||||
<article>
|
||||
<article className="relative">
|
||||
<section className="sm:mx-auto sm:w-full sm:max-w-md space-y-4">
|
||||
<header className="">
|
||||
<Text className="text-white" variant="largetitle">
|
||||
|
@ -572,7 +600,11 @@ export default function Onboarding(props: OnboardingProps) {
|
|||
|
||||
{!steps[currentStep].hideConfirm && (
|
||||
<footer className="py-6 sm:mx-auto sm:w-full sm:max-w-md flex flex-col space-y-6 mt-8">
|
||||
<Button className="justify-center" onClick={handleConfirmStep} EndIcon={ArrowRightIcon}>
|
||||
<Button
|
||||
className="justify-center"
|
||||
disabled={isSubmitting}
|
||||
onClick={debouncedHandleConfirmStep}
|
||||
EndIcon={ArrowRightIcon}>
|
||||
{steps[currentStep].confirmText}
|
||||
</Button>
|
||||
</footer>
|
||||
|
@ -580,11 +612,11 @@ export default function Onboarding(props: OnboardingProps) {
|
|||
</section>
|
||||
<section className="py-6 mt-8 mx-auto max-w-xl">
|
||||
<div className="flex justify-between flex-row-reverse">
|
||||
<button onClick={handleSkipStep}>
|
||||
<button disabled={isSubmitting} onClick={handleSkipStep}>
|
||||
<Text variant="caption">Skip Step</Text>
|
||||
</button>
|
||||
{currentStep !== 0 && (
|
||||
<button onClick={decrementStep}>
|
||||
<button disabled={isSubmitting} onClick={decrementStep}>
|
||||
<Text variant="caption">Prev Step</Text>
|
||||
</button>
|
||||
)}
|
||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -1371,6 +1371,18 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
|
||||
integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
|
||||
|
||||
"@types/lodash.debounce@^4.0.6":
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.6.tgz#c5a2326cd3efc46566c47e4c0aa248dc0ee57d60"
|
||||
integrity sha512-4WTmnnhCfDvvuLMaF3KV4Qfki93KebocUF45msxhYyjMttZDQYzHkO639ohhk8+oco2cluAFL3t5+Jn4mleylQ==
|
||||
dependencies:
|
||||
"@types/lodash" "*"
|
||||
|
||||
"@types/lodash@*":
|
||||
version "4.14.173"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.173.tgz#9d3b674c67a26cf673756f6aca7b429f237f91ed"
|
||||
integrity sha512-vv0CAYoaEjCw/mLy96GBTnRoZrSxkGE0BKzKimdR8P3OzrNYNvBgtW7p055A+E8C31vXNUhWKoFCbhq7gbyhFg==
|
||||
|
||||
"@types/lodash@^4.14.165":
|
||||
version "4.14.172"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.172.tgz#aad774c28e7bfd7a67de25408e03ee5a8c3d028a"
|
||||
|
|
Loading…
Reference in New Issue