diff --git a/components/EmptyScreen.tsx b/components/EmptyScreen.tsx new file mode 100644 index 00000000..6fcbc532 --- /dev/null +++ b/components/EmptyScreen.tsx @@ -0,0 +1,26 @@ +import { SVGComponent } from "@lib/types/SVGComponent"; +import React from "react"; + +export default function EmptyScreen({ + Icon, + headline, + description, +}: { + Icon: SVGComponent; + headline: string; + description: string; +}) { + return ( + <> +
+
+ +
+
+

{headline}

+

{description}

+
+
+ + ); +} diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx index 4face4aa..752d18ab 100644 --- a/components/ui/Button.tsx +++ b/components/ui/Button.tsx @@ -1,9 +1,8 @@ import classNames from "@lib/classNames"; +import { SVGComponent } from "@lib/types/SVGComponent"; import Link, { LinkProps } from "next/link"; import React, { forwardRef } from "react"; -type SVGComponent = React.FunctionComponent>; - export type ButtonProps = { color?: "primary" | "secondary" | "minimal" | "warn"; size?: "base" | "sm" | "lg" | "fab"; diff --git a/lib/emails/EventAttendeeMail.ts b/lib/emails/EventAttendeeMail.ts index b03070f3..c3ebf7b5 100644 --- a/lib/emails/EventAttendeeMail.ts +++ b/lib/emails/EventAttendeeMail.ts @@ -59,7 +59,8 @@ export default class EventAttendeeMail extends EventMail { When - ${this.getInviteeStart().format("dddd, LL")}
${this.getInviteeStart().format("h:mma")} (${this.calEvent.attendees[0].timeZone + ${this.getInviteeStart().format("dddd, LL")}
${this.getInviteeStart().format("h:mma")} (${ + this.calEvent.attendees[0].timeZone }) @@ -141,8 +142,9 @@ export default class EventAttendeeMail extends EventMail { to: `${this.calEvent.attendees[0].name} <${this.calEvent.attendees[0].email}>`, from: `${this.calEvent.organizer.name} <${this.getMailerOptions().from}>`, replyTo: this.calEvent.organizer.email, - subject: `Confirmed: ${this.calEvent.type} with ${this.calEvent.team?.name || this.calEvent.organizer.name - } on ${this.getInviteeStart().format("LT dddd, LL")}`, + subject: `Confirmed: ${this.calEvent.type} with ${ + this.calEvent.team?.name || this.calEvent.organizer.name + } on ${this.getInviteeStart().format("LT dddd, LL")}`, html: this.getHtmlRepresentation(), text: this.getPlainTextRepresentation(), }; diff --git a/lib/emails/EventOrganizerMail.ts b/lib/emails/EventOrganizerMail.ts index 2a114348..28fd3738 100644 --- a/lib/emails/EventOrganizerMail.ts +++ b/lib/emails/EventOrganizerMail.ts @@ -51,8 +51,9 @@ export default class EventOrganizerMail extends EventMail { } protected getAdditionalFooter(): string { - return `

Need to make a change? Manage my bookings

`; + return `

Need to make a change? Manage my bookings

`; } protected getImage(): string { @@ -107,12 +108,14 @@ export default class EventOrganizerMail extends EventMail { When - ${this.getOrganizerStart().format("dddd, LL")}
${this.getOrganizerStart().format("h:mma")} (${this.calEvent.organizer.timeZone + ${this.getOrganizerStart().format("dddd, LL")}
${this.getOrganizerStart().format("h:mma")} (${ + this.calEvent.organizer.timeZone }) Who - ${this.calEvent.attendees[0].name}
${this.calEvent.attendees[0].email} diff --git a/lib/emails/EventRejectionMail.ts b/lib/emails/EventRejectionMail.ts index 17007b6e..1585bb94 100644 --- a/lib/emails/EventRejectionMail.ts +++ b/lib/emails/EventRejectionMail.ts @@ -68,8 +68,9 @@ export default class EventRejectionMail extends EventMail { to: `${this.calEvent.attendees[0].name} <${this.calEvent.attendees[0].email}>`, from: `${this.calEvent.organizer.name} <${this.getMailerOptions().from}>`, replyTo: this.calEvent.organizer.email, - subject: `Rejected: ${this.calEvent.type} with ${this.calEvent.organizer.name - } on ${this.getInviteeStart().format("dddd, LL")}`, + subject: `Rejected: ${this.calEvent.type} with ${ + this.calEvent.organizer.name + } on ${this.getInviteeStart().format("dddd, LL")}`, html: this.getHtmlRepresentation(), text: this.getPlainTextRepresentation(), }; diff --git a/lib/types/SVGComponent.ts b/lib/types/SVGComponent.ts new file mode 100644 index 00000000..98f38b8b --- /dev/null +++ b/lib/types/SVGComponent.ts @@ -0,0 +1 @@ +export type SVGComponent = React.FunctionComponent>; diff --git a/pages/auth/signup.tsx b/pages/auth/signup.tsx index a6413890..1b7038c2 100644 --- a/pages/auth/signup.tsx +++ b/pages/auth/signup.tsx @@ -115,9 +115,7 @@ export default function Signup(props) { className="btn btn-primary w-7/12 mr-2 inline-flex justify-center rounded-md border border-transparent cursor-pointer shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black sm:text-sm" /> - signIn("Cal.com", { callbackUrl: (router.query.callbackUrl || "") as string }) - } + onClick={() => signIn("Cal.com", { callbackUrl: (router.query.callbackUrl || "") as string })} className="w-5/12 inline-flex justify-center text-sm text-gray-500 font-medium border px-4 py-2 rounded btn cursor-pointer"> Login instead diff --git a/pages/bookings/index.tsx b/pages/bookings/index.tsx index d9dcdade..93451cf8 100644 --- a/pages/bookings/index.tsx +++ b/pages/bookings/index.tsx @@ -8,16 +8,19 @@ import { Fragment } from "react"; import { Menu, Transition } from "@headlessui/react"; import { DotsHorizontalIcon } from "@heroicons/react/solid"; import classNames from "@lib/classNames"; -import { ClockIcon, XIcon, CheckIcon, BanIcon } from "@heroicons/react/outline"; +import { ClockIcon, CalendarIcon, XIcon, CheckIcon, BanIcon } from "@heroicons/react/outline"; import Loader from "@components/Loader"; import { Button } from "@components/ui/Button"; import { getSession } from "@lib/auth"; import { BookingStatus, User } from "@prisma/client"; +import EmptyScreen from "@components/EmptyScreen"; export default function Bookings({ bookings }) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [session, loading] = useSession(); + const isEmpty = Object.keys(bookings).length === 0; + const router = useRouter(); if (loading) { @@ -43,227 +46,235 @@ export default function Bookings({ bookings }) {
-
- - - {bookings - .filter((booking) => booking.status !== BookingStatus.CANCELLED) - .map((booking) => ( - - + + ))} + +
- {!booking.confirmed && !booking.rejected && ( - - Unconfirmed - - )} -
- {booking.eventType?.team && {booking.eventType.team.name}: } - {booking.title} -
-
+ {isEmpty ? ( + + ) : ( +
+ + + {bookings + .filter((booking) => booking.status !== BookingStatus.CANCELLED) + .map((booking) => ( + + + - - + - - ))} - -
+ {!booking.confirmed && !booking.rejected && ( + + Unconfirmed + + )} +
+ {booking.eventType?.team && {booking.eventType.team.name}: } + {booking.title} +
+
+
+ {dayjs(booking.startTime).format("D MMMM YYYY")}:{" "} + + {dayjs(booking.startTime).format("HH:mm")} -{" "} + {dayjs(booking.endTime).format("HH:mm")} + +
+
+ {booking.attendees.length !== 0 && ( + + )} +
- {dayjs(booking.startTime).format("D MMMM YYYY")}:{" "} - - {dayjs(booking.startTime).format("HH:mm")} -{" "} - {dayjs(booking.endTime).format("HH:mm")} - + {dayjs(booking.startTime).format("D MMMM YYYY")}
- - {booking.attendees.length !== 0 && ( -
- - {booking.attendees[0].email} - +
+ {dayjs(booking.startTime).format("HH:mm")} -{" "} + {dayjs(booking.endTime).format("HH:mm")}
- )} -
-
- {dayjs(booking.startTime).format("D MMMM YYYY")} -
-
- {dayjs(booking.startTime).format("HH:mm")} -{" "} - {dayjs(booking.endTime).format("HH:mm")} -
-
- {!booking.confirmed && !booking.rejected && ( - <> -
- - -
- - {({ open }) => ( - <> -
- - Open options - -
- - -
- - {({ active }) => ( - confirmBookingHandler(booking, true)} - className={classNames( - active - ? "bg-neutral-100 text-neutral-900" - : "text-neutral-700", - "group flex items-center px-4 py-2 text-sm font-medium" - )}> - - )} - - - {({ active }) => ( - confirmBookingHandler(booking, false)} - className={classNames( - active - ? "bg-neutral-100 text-neutral-900" - : "text-neutral-700", - "group flex items-center px-4 py-2 text-sm w-full font-medium" - )}> - - )} - -
-
-
- - )} -
- - )} - {booking.confirmed && !booking.rejected && ( - <> -
- - -
- - {({ open }) => ( - <> -
- - Open options - -
+
+ {!booking.confirmed && !booking.rejected && ( + <> +
+ + +
+ + {({ open }) => ( + <> +
+ + Open options + +
+ + +
+ + {({ active }) => ( + confirmBookingHandler(booking, true)} + className={classNames( + active + ? "bg-neutral-100 text-neutral-900" + : "text-neutral-700", + "group flex items-center px-4 py-2 text-sm font-medium" + )}> + + )} + + + {({ active }) => ( + confirmBookingHandler(booking, false)} + className={classNames( + active + ? "bg-neutral-100 text-neutral-900" + : "text-neutral-700", + "group flex items-center px-4 py-2 text-sm w-full font-medium" + )}> + + )} + +
+
+
+ + )} +
+ + )} + {booking.confirmed && !booking.rejected && ( + <> +
+ + +
+ + {({ open }) => ( + <> +
+ + Open options + +
- - -
- - {({ active }) => ( - - - )} - - - {({ active }) => ( - - - )} - -
-
-
- - )} -
- - )} - {!booking.confirmed && booking.rejected && ( -
Rejected
- )} -
-
+ + +
+ + {({ active }) => ( + + + )} + + + {({ active }) => ( + + + )} + +
+
+
+ + )} + + + )} + {!booking.confirmed && booking.rejected && ( +
Rejected
+ )} +
+
+ )}
diff --git a/pages/event-types/index.tsx b/pages/event-types/index.tsx index ae4225fe..62451eb4 100644 --- a/pages/event-types/index.tsx +++ b/pages/event-types/index.tsx @@ -342,11 +342,11 @@ const CreateNewEventDialog = ({ profiles, canAddEvents }: { profiles: Profile[]; data-testid="new-event-type" {...(canAddEvents ? { - href: modalOpen.hrefOn, - } + href: modalOpen.hrefOn, + } : { - disabled: true, - })} + disabled: true, + })} StartIcon={PlusIcon}> New event type @@ -372,8 +372,8 @@ const CreateNewEventDialog = ({ profiles, canAddEvents }: { profiles: Profile[]; eventPage: profile.slug, ...(profile.teamId ? { - teamId: profile.teamId, - } + teamId: profile.teamId, + } : {}), }, }) @@ -681,13 +681,13 @@ export async function getServerSideProps(context) { eventTypes: user.eventTypes.concat(typesRaw).map((type, index) => user.plan === "FREE" && index > 0 ? { - ...type, - $disabled: true, - } + ...type, + $disabled: true, + } : { - ...type, - $disabled: false, - } + ...type, + $disabled: false, + } ), metadata: { membershipCount: 1, diff --git a/pages/success.tsx b/pages/success.tsx index cd3c2c22..3062cc52 100644 --- a/pages/success.tsx +++ b/pages/success.tsx @@ -135,9 +135,10 @@ export default function Success(props: inferSSRProps) `https://calendar.google.com/calendar/r/eventedit?dates=${date .utc() .format("YYYYMMDDTHHmmss[Z]")}/${date - .add(props.eventType.length, "minute") - .utc() - .format("YYYYMMDDTHHmmss[Z]")}&text=${eventName}&details=${props.eventType.description + .add(props.eventType.length, "minute") + .utc() + .format("YYYYMMDDTHHmmss[Z]")}&text=${eventName}&details=${ + props.eventType.description }` + (location ? "&location=" + encodeURIComponent(location) : "") }> @@ -155,13 +156,13 @@ export default function Success(props: inferSSRProps) href={ encodeURI( "https://outlook.live.com/calendar/0/deeplink/compose?body=" + - props.eventType.description + - "&enddt=" + - date.add(props.eventType.length, "minute").utc().format() + - "&path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&startdt=" + - date.utc().format() + - "&subject=" + - eventName + props.eventType.description + + "&enddt=" + + date.add(props.eventType.length, "minute").utc().format() + + "&path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&startdt=" + + date.utc().format() + + "&subject=" + + eventName ) + (location ? "&location=" + location : "") }> ) href={ encodeURI( "https://outlook.office.com/calendar/0/deeplink/compose?body=" + - props.eventType.description + - "&enddt=" + - date.add(props.eventType.length, "minute").utc().format() + - "&path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&startdt=" + - date.utc().format() + - "&subject=" + - eventName + props.eventType.description + + "&enddt=" + + date.add(props.eventType.length, "minute").utc().format() + + "&path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&startdt=" + + date.utc().format() + + "&subject=" + + eventName ) + (location ? "&location=" + location : "") }>