Feature: pre-fill name and email if user loggedIn in booking page (#2131)

* feat: pre-fill name and email if user loggedIn in booking page

* feat: add name to next-auth autoMergeIdentiteies response

* fix: Update booking page so if you're in your own booking, it doesn't prefill

Co-authored-by: Agusti Fernandez Pardo <git@agusti.me>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
Agusti Fernandez 2022-03-15 15:39:20 +01:00 committed by GitHub
parent 9b1031d009
commit 53b202790e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 40 additions and 41 deletions

@ -1 +1 @@
Subproject commit 378cbf8f3a67ea7877296f1da02edb2b6e3efbce Subproject commit 63e0ca33e95583a5e2aae69195af052fd0d9aef8

View file

@ -2,6 +2,7 @@ import { CalendarIcon, ClockIcon, CreditCardIcon, ExclamationIcon } from "@heroi
import { EventTypeCustomInputType } from "@prisma/client"; import { EventTypeCustomInputType } from "@prisma/client";
import { useContracts } from "contexts/contractsContext"; import { useContracts } from "contexts/contractsContext";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { useSession } from "next-auth/react";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import Head from "next/head"; import Head from "next/head";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
@ -51,12 +52,11 @@ type BookingFormValues = {
}; };
}; };
const BookingPage = (props: BookingPageProps) => { const BookingPage = ({ eventType, booking, profile }: BookingPageProps) => {
const { t, i18n } = useLocale(); const { t, i18n } = useLocale();
const router = useRouter(); const router = useRouter();
const { contracts } = useContracts(); const { contracts } = useContracts();
const { eventType } = props; const { data: session } = useSession();
useEffect(() => { useEffect(() => {
if (eventType.metadata.smartContractAddress) { if (eventType.metadata.smartContractAddress) {
const eventOwner = eventType.users[0]; const eventOwner = eventType.users[0];
@ -95,8 +95,8 @@ const BookingPage = (props: BookingPageProps) => {
pathname: "/success", pathname: "/success",
query: { query: {
date, date,
type: props.eventType.id, type: eventType.id,
user: props.profile.slug, user: profile.slug,
reschedule: !!rescheduleUid, reschedule: !!rescheduleUid,
name: attendees[0].name, name: attendees[0].name,
email: attendees[0].email, email: attendees[0].email,
@ -107,18 +107,18 @@ const BookingPage = (props: BookingPageProps) => {
}); });
const rescheduleUid = router.query.rescheduleUid as string; const rescheduleUid = router.query.rescheduleUid as string;
const { isReady, Theme } = useTheme(props.profile.theme); const { isReady, Theme } = useTheme(profile.theme);
const date = asStringOrNull(router.query.date); const date = asStringOrNull(router.query.date);
const [guestToggle, setGuestToggle] = useState(props.booking && props.booking.attendees.length > 1); const [guestToggle, setGuestToggle] = useState(booking && booking.attendees.length > 1);
const eventTypeDetail = { isWeb3Active: false, ...props.eventType }; const eventTypeDetail = { isWeb3Active: false, ...eventType };
type Location = { type: LocationType; address?: string }; type Location = { type: LocationType; address?: string };
// it would be nice if Prisma at some point in the future allowed for Json<Location>; as of now this is not the case. // it would be nice if Prisma at some point in the future allowed for Json<Location>; as of now this is not the case.
const locations: Location[] = useMemo( const locations: Location[] = useMemo(
() => (props.eventType.locations as Location[]) || [], () => (eventType.locations as Location[]) || [],
[props.eventType.locations] [eventType.locations]
); );
useEffect(() => { useEffect(() => {
@ -142,15 +142,15 @@ const BookingPage = (props: BookingPageProps) => {
[LocationType.Huddle01]: "Huddle01 Video", [LocationType.Huddle01]: "Huddle01 Video",
[LocationType.Tandem]: "Tandem Video", [LocationType.Tandem]: "Tandem Video",
}; };
const loggedInIsOwner = eventType.users[0].name === session?.user.name;
const defaultValues = () => { const defaultValues = () => {
if (!rescheduleUid) { if (!rescheduleUid) {
return { return {
name: (router.query.name as string) || "", name: loggedInIsOwner ? "" : session?.user?.name || (router.query.name as string) || "",
email: (router.query.email as string) || "", email: loggedInIsOwner ? "" : session?.user?.email || (router.query.email as string) || "",
notes: (router.query.notes as string) || "", notes: (router.query.notes as string) || "",
guests: ensureArray(router.query.guest) as string[], guests: ensureArray(router.query.guest) as string[],
customInputs: props.eventType.customInputs.reduce( customInputs: eventType.customInputs.reduce(
(customInputs, input) => ({ (customInputs, input) => ({
...customInputs, ...customInputs,
[input.id]: router.query[slugify(input.label)], [input.id]: router.query[slugify(input.label)],
@ -159,17 +159,17 @@ const BookingPage = (props: BookingPageProps) => {
), ),
}; };
} }
if (!props.booking || !props.booking.attendees.length) { if (!booking || !booking.attendees.length) {
return {}; return {};
} }
const primaryAttendee = props.booking.attendees[0]; const primaryAttendee = booking.attendees[0];
if (!primaryAttendee) { if (!primaryAttendee) {
return {}; return {};
} }
return { return {
name: primaryAttendee.name || "", name: primaryAttendee.name || "",
email: primaryAttendee.email || "", email: primaryAttendee.email || "",
guests: props.booking.attendees.slice(1).map((attendee) => attendee.email), guests: booking.attendees.slice(1).map((attendee) => attendee.email),
}; };
}; };
@ -245,8 +245,8 @@ const BookingPage = (props: BookingPageProps) => {
...booking, ...booking,
web3Details, web3Details,
start: dayjs(date).format(), start: dayjs(date).format(),
end: dayjs(date).add(props.eventType.length, "minute").format(), end: dayjs(date).add(eventType.length, "minute").format(),
eventTypeId: props.eventType.id, eventTypeId: eventType.id,
timeZone: timeZone(), timeZone: timeZone(),
language: i18n.language, language: i18n.language,
rescheduleUid, rescheduleUid,
@ -256,7 +256,7 @@ const BookingPage = (props: BookingPageProps) => {
), ),
metadata, metadata,
customInputs: Object.keys(booking.customInputs || {}).map((inputId) => ({ customInputs: Object.keys(booking.customInputs || {}).map((inputId) => ({
label: props.eventType.customInputs.find((input) => input.id === parseInt(inputId))!.label, label: eventType.customInputs.find((input) => input.id === parseInt(inputId))!.label,
value: booking.customInputs![inputId], value: booking.customInputs![inputId],
})), })),
}); });
@ -269,18 +269,18 @@ const BookingPage = (props: BookingPageProps) => {
<title> <title>
{rescheduleUid {rescheduleUid
? t("booking_reschedule_confirmation", { ? t("booking_reschedule_confirmation", {
eventTypeTitle: props.eventType.title, eventTypeTitle: eventType.title,
profileName: props.profile.name, profileName: profile.name,
}) })
: t("booking_confirmation", { : t("booking_confirmation", {
eventTypeTitle: props.eventType.title, eventTypeTitle: eventType.title,
profileName: props.profile.name, profileName: profile.name,
})}{" "} })}{" "}
| Cal.com | Cal.com
</title> </title>
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
</Head> </Head>
<CustomBranding lightVal={props.profile.brandColor} darkVal={props.profile.darkBrandColor} /> <CustomBranding lightVal={profile.brandColor} darkVal={profile.darkBrandColor} />
<main className="mx-auto my-0 max-w-3xl rounded-sm sm:my-24 sm:border sm:dark:border-gray-600"> <main className="mx-auto my-0 max-w-3xl rounded-sm sm:my-24 sm:border sm:dark:border-gray-600">
{isReady && ( {isReady && (
<div className="overflow-hidden border border-gray-200 bg-white dark:border-0 dark:bg-neutral-900 sm:rounded-sm"> <div className="overflow-hidden border border-gray-200 bg-white dark:border-0 dark:bg-neutral-900 sm:rounded-sm">
@ -289,33 +289,31 @@ const BookingPage = (props: BookingPageProps) => {
<AvatarGroup <AvatarGroup
border="border-2 border-white dark:border-gray-900" border="border-2 border-white dark:border-gray-900"
size={14} size={14}
items={[{ image: props.profile.image || "", alt: props.profile.name || "" }].concat( items={[{ image: profile.image || "", alt: profile.name || "" }].concat(
props.eventType.users eventType.users
.filter((user) => user.name !== props.profile.name) .filter((user) => user.name !== profile.name)
.map((user) => ({ .map((user) => ({
image: user.avatar || "", image: user.avatar || "",
alt: user.name || "", alt: user.name || "",
})) }))
)} )}
/> />
<h2 className="font-cal mt-2 font-medium text-gray-500 dark:text-gray-300"> <h2 className="font-cal mt-2 font-medium text-gray-500 dark:text-gray-300">{profile.name}</h2>
{props.profile.name}
</h2>
<h1 className="mb-4 text-3xl font-semibold text-gray-800 dark:text-white"> <h1 className="mb-4 text-3xl font-semibold text-gray-800 dark:text-white">
{props.eventType.title} {eventType.title}
</h1> </h1>
<p className="mb-2 text-gray-500"> <p className="mb-2 text-gray-500">
<ClockIcon className="mr-1 -mt-1 inline-block h-4 w-4" /> <ClockIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
{props.eventType.length} {t("minutes")} {eventType.length} {t("minutes")}
</p> </p>
{props.eventType.price > 0 && ( {eventType.price > 0 && (
<p className="mb-1 -ml-2 px-2 py-1 text-gray-500"> <p className="mb-1 -ml-2 px-2 py-1 text-gray-500">
<CreditCardIcon className="mr-1 -mt-1 inline-block h-4 w-4" /> <CreditCardIcon className="mr-1 -mt-1 inline-block h-4 w-4" />
<IntlProvider locale="en"> <IntlProvider locale="en">
<FormattedNumber <FormattedNumber
value={props.eventType.price / 100.0} value={eventType.price / 100.0}
style="currency" style="currency"
currency={props.eventType.currency.toUpperCase()} currency={eventType.currency.toUpperCase()}
/> />
</IntlProvider> </IntlProvider>
</p> </p>
@ -329,7 +327,7 @@ const BookingPage = (props: BookingPageProps) => {
{t("requires_ownership_of_a_token") + " " + eventType.metadata.smartContractAddress} {t("requires_ownership_of_a_token") + " " + eventType.metadata.smartContractAddress}
</p> </p>
)} )}
<p className="mb-8 text-gray-600 dark:text-white">{props.eventType.description}</p> <p className="mb-8 text-gray-600 dark:text-white">{eventType.description}</p>
</div> </div>
<div className="sm:w-1/2 sm:pl-8 sm:pr-4"> <div className="sm:w-1/2 sm:pl-8 sm:pr-4">
<Form form={bookingForm} handleSubmit={bookEvent}> <Form form={bookingForm} handleSubmit={bookEvent}>
@ -404,7 +402,7 @@ const BookingPage = (props: BookingPageProps) => {
</div> </div>
</div> </div>
)} )}
{props.eventType.customInputs {eventType.customInputs
.sort((a, b) => a.id - b.id) .sort((a, b) => a.id - b.id)
.map((input) => ( .map((input) => (
<div className="mb-4" key={input.id}> <div className="mb-4" key={input.id}>
@ -468,7 +466,7 @@ const BookingPage = (props: BookingPageProps) => {
)} )}
</div> </div>
))} ))}
{!props.eventType.disableGuests && ( {!eventType.disableGuests && (
<div className="mb-4"> <div className="mb-4">
{!guestToggle && ( {!guestToggle && (
<label <label

View file

@ -166,6 +166,7 @@ export default NextAuth({
return { return {
id: existingUser.id, id: existingUser.id,
username: existingUser.username, username: existingUser.username,
name: existingUser.name,
email: existingUser.email, email: existingUser.email,
}; };
} }

@ -1 +1 @@
Subproject commit 6545bb5cf0d5e38dcab9cce47853b7a7a4859033 Subproject commit 822263db61f0fc40034a20916fc4a21abbed8572